1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2010 Alexander V. Chernikov <melifaro@ipfw.ru>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 #include "opt_inet6.h"
31 #include "opt_route.h"
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/counter.h>
35 #include <sys/kernel.h>
36 #include <sys/ktr.h>
37 #include <sys/limits.h>
38 #include <sys/malloc.h>
39 #include <sys/mbuf.h>
40 #include <sys/syslog.h>
41 #include <sys/socket.h>
42 #include <vm/uma.h>
43
44 #include <net/if.h>
45 #include <net/route.h>
46 #include <net/ethernet.h>
47 #include <netinet/in.h>
48 #include <netinet/in_systm.h>
49 #include <netinet/ip.h>
50 #include <netinet/ip6.h>
51 #include <netinet/tcp.h>
52 #include <netinet/udp.h>
53
54 #include <netgraph/ng_message.h>
55 #include <netgraph/netgraph.h>
56
57 #include <netgraph/netflow/netflow.h>
58 #include <netgraph/netflow/ng_netflow.h>
59 #include <netgraph/netflow/netflow_v9.h>
60
61 MALLOC_DECLARE(M_NETFLOW_GENERAL);
62 MALLOC_DEFINE(M_NETFLOW_GENERAL, "netflow_general", "plog, V9 templates data");
63
64 /*
65 * Base V9 templates for L4+ IPv4/IPv6 protocols
66 */
67 struct netflow_v9_template _netflow_v9_record_ipv4_tcp[] =
68 {
69 { NETFLOW_V9_FIELD_IPV4_SRC_ADDR, 4},
70 { NETFLOW_V9_FIELD_IPV4_DST_ADDR, 4},
71 { NETFLOW_V9_FIELD_IPV4_NEXT_HOP, 4},
72 { NETFLOW_V9_FIELD_INPUT_SNMP, 2},
73 { NETFLOW_V9_FIELD_OUTPUT_SNMP, 2},
74 { NETFLOW_V9_FIELD_IN_PKTS, sizeof(CNTR)},
75 { NETFLOW_V9_FIELD_IN_BYTES, sizeof(CNTR)},
76 { NETFLOW_V9_FIELD_OUT_PKTS, sizeof(CNTR)},
77 { NETFLOW_V9_FIELD_OUT_BYTES, sizeof(CNTR)},
78 { NETFLOW_V9_FIELD_FIRST_SWITCHED, 4},
79 { NETFLOW_V9_FIELD_LAST_SWITCHED, 4},
80 { NETFLOW_V9_FIELD_L4_SRC_PORT, 2},
81 { NETFLOW_V9_FIELD_L4_DST_PORT, 2},
82 { NETFLOW_V9_FIELD_TCP_FLAGS, 1},
83 { NETFLOW_V9_FIELD_PROTOCOL, 1},
84 { NETFLOW_V9_FIELD_TOS, 1},
85 { NETFLOW_V9_FIELD_SRC_AS, 4},
86 { NETFLOW_V9_FIELD_DST_AS, 4},
87 { NETFLOW_V9_FIELD_SRC_MASK, 1},
88 { NETFLOW_V9_FIELD_DST_MASK, 1},
89 {0, 0}
90 };
91
92 struct netflow_v9_template _netflow_v9_record_ipv6_tcp[] =
93 {
94 { NETFLOW_V9_FIELD_IPV6_SRC_ADDR, 16},
95 { NETFLOW_V9_FIELD_IPV6_DST_ADDR, 16},
96 { NETFLOW_V9_FIELD_IPV6_NEXT_HOP, 16},
97 { NETFLOW_V9_FIELD_INPUT_SNMP, 2},
98 { NETFLOW_V9_FIELD_OUTPUT_SNMP, 2},
99 { NETFLOW_V9_FIELD_IN_PKTS, sizeof(CNTR)},
100 { NETFLOW_V9_FIELD_IN_BYTES, sizeof(CNTR)},
101 { NETFLOW_V9_FIELD_OUT_PKTS, sizeof(CNTR)},
102 { NETFLOW_V9_FIELD_OUT_BYTES, sizeof(CNTR)},
103 { NETFLOW_V9_FIELD_FIRST_SWITCHED, 4},
104 { NETFLOW_V9_FIELD_LAST_SWITCHED, 4},
105 { NETFLOW_V9_FIELD_L4_SRC_PORT, 2},
106 { NETFLOW_V9_FIELD_L4_DST_PORT, 2},
107 { NETFLOW_V9_FIELD_TCP_FLAGS, 1},
108 { NETFLOW_V9_FIELD_PROTOCOL, 1},
109 { NETFLOW_V9_FIELD_TOS, 1},
110 { NETFLOW_V9_FIELD_SRC_AS, 4},
111 { NETFLOW_V9_FIELD_DST_AS, 4},
112 { NETFLOW_V9_FIELD_SRC_MASK, 1},
113 { NETFLOW_V9_FIELD_DST_MASK, 1},
114 {0, 0}
115 };
116
117 /*
118 * Pre-compiles flow exporter for all possible FlowSets
119 * so we can add flowset to packet via simple memcpy()
120 */
121 static void
generate_v9_templates(priv_p priv)122 generate_v9_templates(priv_p priv)
123 {
124 uint16_t *p, *template_fields_cnt;
125 int cnt;
126
127 int flowset_size = sizeof(struct netflow_v9_flowset_header) +
128 _NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv4_tcp) + /* netflow_v9_record_ipv4_tcp */
129 _NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv6_tcp); /* netflow_v9_record_ipv6_tcp */
130
131 priv->v9_flowsets[0] = malloc(flowset_size, M_NETFLOW_GENERAL, M_WAITOK | M_ZERO);
132
133 if (flowset_size % 4)
134 flowset_size += 4 - (flowset_size % 4); /* Padding to 4-byte boundary */
135
136 priv->flowsets_count = 1;
137 p = (uint16_t *)priv->v9_flowsets[0];
138 *p++ = 0; /* Flowset ID, 0 is reserved for Template FlowSets */
139 *p++ = htons(flowset_size); /* Total FlowSet length */
140
141 /*
142 * Most common TCP/UDP IPv4 template, ID = 256
143 */
144 *p++ = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + NETFLOW_V9_FLOW_V4_L4);
145 template_fields_cnt = p++;
146 for (cnt = 0; _netflow_v9_record_ipv4_tcp[cnt].field_id != 0; cnt++) {
147 *p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_id);
148 *p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_length);
149 }
150 *template_fields_cnt = htons(cnt);
151
152 /*
153 * TCP/UDP IPv6 template, ID = 257
154 */
155 *p++ = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + NETFLOW_V9_FLOW_V6_L4);
156 template_fields_cnt = p++;
157 for (cnt = 0; _netflow_v9_record_ipv6_tcp[cnt].field_id != 0; cnt++) {
158 *p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_id);
159 *p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_length);
160 }
161 *template_fields_cnt = htons(cnt);
162
163 priv->flowset_records[0] = 2;
164 }
165
166 /* Closes current data flowset */
167 static void inline
close_flowset(struct mbuf * m,struct netflow_v9_packet_opt * t)168 close_flowset(struct mbuf *m, struct netflow_v9_packet_opt *t)
169 {
170 struct mbuf *m_old;
171 uint32_t zero = 0;
172 int offset = 0;
173 uint16_t *flowset_length, len;
174
175 /* Hack to ensure we are not crossing mbuf boundary, length is uint16_t */
176 m_old = m_getptr(m, t->flow_header + offsetof(struct netflow_v9_flowset_header, length), &offset);
177 flowset_length = (uint16_t *)(mtod(m_old, char *) + offset);
178
179 len = (uint16_t)(m_pktlen(m) - t->flow_header);
180 /* Align on 4-byte boundary (RFC 3954, Clause 5.3) */
181 if (len % 4) {
182 if (m_append(m, 4 - (len % 4), (void *)&zero) != 1)
183 panic("ng_netflow: m_append() failed!");
184
185 len += 4 - (len % 4);
186 }
187
188 *flowset_length = htons(len);
189 }
190
191 /*
192 * Non-static functions called from ng_netflow.c
193 */
194
195 /* We have full datagram in fib data. Send it to export hook. */
196 int
export9_send(priv_p priv,fib_export_p fe,item_p item,struct netflow_v9_packet_opt * t,int flags)197 export9_send(priv_p priv, fib_export_p fe, item_p item, struct netflow_v9_packet_opt *t, int flags)
198 {
199 struct mbuf *m = NGI_M(item);
200 struct netflow_v9_export_dgram *dgram = mtod(m,
201 struct netflow_v9_export_dgram *);
202 struct netflow_v9_header *header = &dgram->header;
203 struct timespec ts;
204 int error = 0;
205
206 if (t == NULL) {
207 CTR0(KTR_NET, "export9_send(): V9 export packet without tag");
208 NG_FREE_ITEM(item);
209 return (0);
210 }
211
212 /* Close flowset if not closed already */
213 if (m_pktlen(m) != t->flow_header)
214 close_flowset(m, t);
215
216 /* Fill export header. */
217 header->count = t->count;
218 header->sys_uptime = htonl(MILLIUPTIME(time_uptime));
219 getnanotime(&ts);
220 header->unix_secs = htonl(ts.tv_sec);
221 header->seq_num = htonl(atomic_fetchadd_32(&fe->flow9_seq, 1));
222 header->count = htons(t->count);
223 header->source_id = htonl(fe->domain_id);
224
225 if (priv->export9 != NULL)
226 NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export9, flags);
227 else
228 NG_FREE_ITEM(item);
229
230 fe->sent_packets++;
231 free(t, M_NETFLOW_GENERAL);
232
233 return (error);
234 }
235
236 /* Add V9 record to dgram. */
237 int
export9_add(item_p item,struct netflow_v9_packet_opt * t,struct flow_entry * fle)238 export9_add(item_p item, struct netflow_v9_packet_opt *t, struct flow_entry *fle)
239 {
240 size_t len = 0;
241 struct netflow_v9_flowset_header fsh;
242 struct netflow_v9_record_general rg;
243 struct mbuf *m = NGI_M(item);
244 uint16_t flow_type;
245 struct flow_entry_data *fed;
246 #ifdef INET6
247 struct flow6_entry_data *fed6;
248 #endif
249 if (t == NULL) {
250 CTR0(KTR_NET, "ng_netflow: V9 export packet without tag!");
251 return (0);
252 }
253
254 /* Prepare flow record */
255 fed = (struct flow_entry_data *)&fle->f;
256 #ifdef INET6
257 fed6 = (struct flow6_entry_data *)&fle->f;
258 #endif
259 /* We can use flow_type field since fle6 offset is equal to fle */
260 flow_type = fed->r.flow_type;
261
262 switch (flow_type) {
263 case NETFLOW_V9_FLOW_V4_L4:
264 {
265 /* IPv4 TCP/UDP/[SCTP] */
266 struct netflow_v9_record_ipv4_tcp *rec = &rg.rec.v4_tcp;
267
268 rec->src_addr = fed->r.r_src.s_addr;
269 rec->dst_addr = fed->r.r_dst.s_addr;
270 rec->next_hop = fed->next_hop.s_addr;
271 rec->i_ifx = htons(fed->fle_i_ifx);
272 rec->o_ifx = htons(fed->fle_o_ifx);
273 rec->i_packets = htonl(fed->packets);
274 rec->i_octets = htonl(fed->bytes);
275 rec->o_packets = htonl(0);
276 rec->o_octets = htonl(0);
277 rec->first = htonl(MILLIUPTIME(fed->first));
278 rec->last = htonl(MILLIUPTIME(fed->last));
279 rec->s_port = fed->r.r_sport;
280 rec->d_port = fed->r.r_dport;
281 rec->flags = fed->tcp_flags;
282 rec->prot = fed->r.r_ip_p;
283 rec->tos = fed->r.r_tos;
284 rec->dst_mask = fed->dst_mask;
285 rec->src_mask = fed->src_mask;
286
287 /* Not supported fields. */
288 rec->src_as = rec->dst_as = 0;
289
290 len = sizeof(struct netflow_v9_record_ipv4_tcp);
291 break;
292 }
293 #ifdef INET6
294 case NETFLOW_V9_FLOW_V6_L4:
295 {
296 /* IPv6 TCP/UDP/[SCTP] */
297 struct netflow_v9_record_ipv6_tcp *rec = &rg.rec.v6_tcp;
298
299 rec->src_addr = fed6->r.src.r_src6;
300 rec->dst_addr = fed6->r.dst.r_dst6;
301 rec->next_hop = fed6->n.next_hop6;
302 rec->i_ifx = htons(fed6->fle_i_ifx);
303 rec->o_ifx = htons(fed6->fle_o_ifx);
304 rec->i_packets = htonl(fed6->packets);
305 rec->i_octets = htonl(fed6->bytes);
306 rec->o_packets = htonl(0);
307 rec->o_octets = htonl(0);
308 rec->first = htonl(MILLIUPTIME(fed6->first));
309 rec->last = htonl(MILLIUPTIME(fed6->last));
310 rec->s_port = fed6->r.r_sport;
311 rec->d_port = fed6->r.r_dport;
312 rec->flags = fed6->tcp_flags;
313 rec->prot = fed6->r.r_ip_p;
314 rec->tos = fed6->r.r_tos;
315 rec->dst_mask = fed6->dst_mask;
316 rec->src_mask = fed6->src_mask;
317
318 /* Not supported fields. */
319 rec->src_as = rec->dst_as = 0;
320
321 len = sizeof(struct netflow_v9_record_ipv6_tcp);
322 break;
323 }
324 #endif
325 default:
326 {
327 CTR1(KTR_NET, "export9_add(): Don't know what to do with %d flow type!", flow_type);
328 return (0);
329 }
330 }
331
332 /* Check if new records has the same template */
333 if (flow_type != t->flow_type) {
334 /* close old flowset */
335 if (t->flow_type != 0)
336 close_flowset(m, t);
337
338 t->flow_type = flow_type;
339 t->flow_header = m_pktlen(m);
340
341 /* Generate data flowset ID */
342 fsh.id = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + flow_type);
343 fsh.length = 0;
344
345 /* m_append should not fail since all data is already allocated */
346 if (m_append(m, sizeof(fsh), (void *)&fsh) != 1)
347 panic("ng_netflow: m_append() failed");
348
349 }
350
351 if (m_append(m, len, (void *)&rg.rec) != 1)
352 panic("ng_netflow: m_append() failed");
353
354 t->count++;
355
356 if (m_pktlen(m) + sizeof(struct netflow_v9_record_general) + sizeof(struct netflow_v9_flowset_header) >= _NETFLOW_V9_MAX_SIZE(t->mtu))
357 return (1); /* end of datagram */
358 return (0);
359 }
360
361 /*
362 * Detach export datagram from fib instance, if there is any.
363 * If there is no, allocate a new one.
364 */
365 item_p
get_export9_dgram(priv_p priv,fib_export_p fe,struct netflow_v9_packet_opt ** tt)366 get_export9_dgram(priv_p priv, fib_export_p fe, struct netflow_v9_packet_opt **tt)
367 {
368 item_p item = NULL;
369 struct netflow_v9_packet_opt *t = NULL;
370
371 mtx_lock(&fe->export9_mtx);
372 if (fe->exp.item9 != NULL) {
373 item = fe->exp.item9;
374 fe->exp.item9 = NULL;
375 t = fe->exp.item9_opt;
376 fe->exp.item9_opt = NULL;
377 }
378 mtx_unlock(&fe->export9_mtx);
379
380 if (item == NULL) {
381 struct netflow_v9_export_dgram *dgram;
382 struct mbuf *m;
383 uint16_t mtu = priv->mtu;
384
385 /* Allocate entire packet at once, allowing easy m_append() calls */
386 m = m_getm(NULL, mtu, M_NOWAIT, MT_DATA);
387 if (m == NULL)
388 return (NULL);
389
390 t = malloc(sizeof(struct netflow_v9_packet_opt), M_NETFLOW_GENERAL, M_NOWAIT | M_ZERO);
391 if (t == NULL) {
392 m_free(m);
393 return (NULL);
394 }
395
396 item = ng_package_data(m, NG_NOFLAGS);
397 if (item == NULL) {
398 free(t, M_NETFLOW_GENERAL);
399 return (NULL);
400 }
401
402 dgram = mtod(m, struct netflow_v9_export_dgram *);
403 dgram->header.count = 0;
404 dgram->header.version = htons(NETFLOW_V9);
405 /* Set mbuf current data length */
406 m->m_len = m->m_pkthdr.len = sizeof(struct netflow_v9_header);
407
408 t->count = 0;
409 t->mtu = mtu;
410 t->flow_header = m->m_len;
411
412 /*
413 * Check if we need to insert templates into packet
414 */
415
416 struct netflow_v9_flowset_header *fl;
417
418 if ((time_uptime >= priv->templ_time + fe->templ_last_ts) ||
419 (fe->sent_packets >= priv->templ_packets + fe->templ_last_pkt)) {
420 fe->templ_last_ts = time_uptime;
421 fe->templ_last_pkt = fe->sent_packets;
422
423 fl = priv->v9_flowsets[0];
424 m_append(m, ntohs(fl->length), (void *)fl);
425 t->flow_header = m->m_len;
426 t->count += priv->flowset_records[0];
427 }
428 }
429
430 *tt = t;
431 return (item);
432 }
433
434 /*
435 * Re-attach incomplete datagram back to fib instance.
436 * If there is already another one, then send incomplete.
437 */
438 void
return_export9_dgram(priv_p priv,fib_export_p fe,item_p item,struct netflow_v9_packet_opt * t,int flags)439 return_export9_dgram(priv_p priv, fib_export_p fe, item_p item, struct netflow_v9_packet_opt *t, int flags)
440 {
441 /*
442 * It may happen on SMP, that some thread has already
443 * put its item there, in this case we bail out and
444 * send what we have to collector.
445 */
446 mtx_lock(&fe->export9_mtx);
447 if (fe->exp.item9 == NULL) {
448 fe->exp.item9 = item;
449 fe->exp.item9_opt = t;
450 mtx_unlock(&fe->export9_mtx);
451 } else {
452 mtx_unlock(&fe->export9_mtx);
453 export9_send(priv, fe, item, t, flags);
454 }
455 }
456
457 /* Allocate memory and set up flow cache */
458 void
ng_netflow_v9_cache_init(priv_p priv)459 ng_netflow_v9_cache_init(priv_p priv)
460 {
461 generate_v9_templates(priv);
462
463 priv->templ_time = NETFLOW_V9_MAX_TIME_TEMPL;
464 priv->templ_packets = NETFLOW_V9_MAX_PACKETS_TEMPL;
465 priv->mtu = BASE_MTU;
466 }
467
468 /* Free all flow cache memory. Called from ng_netflow_cache_flush() */
469 void
ng_netflow_v9_cache_flush(priv_p priv)470 ng_netflow_v9_cache_flush(priv_p priv)
471 {
472 int i;
473
474 /* Free flowsets*/
475 for (i = 0; i < priv->flowsets_count; i++)
476 free(priv->v9_flowsets[i], M_NETFLOW_GENERAL);
477 }
478
479 /* Get a snapshot of NetFlow v9 settings */
480 void
ng_netflow_copyv9info(priv_p priv,struct ng_netflow_v9info * i)481 ng_netflow_copyv9info(priv_p priv, struct ng_netflow_v9info *i)
482 {
483
484 i->templ_time = priv->templ_time;
485 i->templ_packets = priv->templ_packets;
486 i->mtu = priv->mtu;
487 }
488