Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netgraph/netflow/netflow_v9.c
34672 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2010 Alexander V. Chernikov <[email protected]>
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
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
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
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
free(t, M_NETFLOW_GENERAL);
231
232
return (error);
233
}
234
235
/* Add V9 record to dgram. */
236
int
237
export9_add(item_p item, struct netflow_v9_packet_opt *t, struct flow_entry *fle)
238
{
239
size_t len = 0;
240
struct netflow_v9_flowset_header fsh;
241
struct netflow_v9_record_general rg;
242
struct mbuf *m = NGI_M(item);
243
uint16_t flow_type;
244
struct flow_entry_data *fed;
245
#ifdef INET6
246
struct flow6_entry_data *fed6;
247
#endif
248
if (t == NULL) {
249
CTR0(KTR_NET, "ng_netflow: V9 export packet without tag!");
250
return (0);
251
}
252
253
/* Prepare flow record */
254
fed = (struct flow_entry_data *)&fle->f;
255
#ifdef INET6
256
fed6 = (struct flow6_entry_data *)&fle->f;
257
#endif
258
/* We can use flow_type field since fle6 offset is equal to fle */
259
flow_type = fed->r.flow_type;
260
261
switch (flow_type) {
262
case NETFLOW_V9_FLOW_V4_L4:
263
{
264
/* IPv4 TCP/UDP/[SCTP] */
265
struct netflow_v9_record_ipv4_tcp *rec = &rg.rec.v4_tcp;
266
267
rec->src_addr = fed->r.r_src.s_addr;
268
rec->dst_addr = fed->r.r_dst.s_addr;
269
rec->next_hop = fed->next_hop.s_addr;
270
rec->i_ifx = htons(fed->fle_i_ifx);
271
rec->o_ifx = htons(fed->fle_o_ifx);
272
rec->i_packets = htonl(fed->packets);
273
rec->i_octets = htonl(fed->bytes);
274
rec->o_packets = htonl(0);
275
rec->o_octets = htonl(0);
276
rec->first = htonl(MILLIUPTIME(fed->first));
277
rec->last = htonl(MILLIUPTIME(fed->last));
278
rec->s_port = fed->r.r_sport;
279
rec->d_port = fed->r.r_dport;
280
rec->flags = fed->tcp_flags;
281
rec->prot = fed->r.r_ip_p;
282
rec->tos = fed->r.r_tos;
283
rec->dst_mask = fed->dst_mask;
284
rec->src_mask = fed->src_mask;
285
286
/* Not supported fields. */
287
rec->src_as = rec->dst_as = 0;
288
289
len = sizeof(struct netflow_v9_record_ipv4_tcp);
290
break;
291
}
292
#ifdef INET6
293
case NETFLOW_V9_FLOW_V6_L4:
294
{
295
/* IPv6 TCP/UDP/[SCTP] */
296
struct netflow_v9_record_ipv6_tcp *rec = &rg.rec.v6_tcp;
297
298
rec->src_addr = fed6->r.src.r_src6;
299
rec->dst_addr = fed6->r.dst.r_dst6;
300
rec->next_hop = fed6->n.next_hop6;
301
rec->i_ifx = htons(fed6->fle_i_ifx);
302
rec->o_ifx = htons(fed6->fle_o_ifx);
303
rec->i_packets = htonl(fed6->packets);
304
rec->i_octets = htonl(fed6->bytes);
305
rec->o_packets = htonl(0);
306
rec->o_octets = htonl(0);
307
rec->first = htonl(MILLIUPTIME(fed6->first));
308
rec->last = htonl(MILLIUPTIME(fed6->last));
309
rec->s_port = fed6->r.r_sport;
310
rec->d_port = fed6->r.r_dport;
311
rec->flags = fed6->tcp_flags;
312
rec->prot = fed6->r.r_ip_p;
313
rec->tos = fed6->r.r_tos;
314
rec->dst_mask = fed6->dst_mask;
315
rec->src_mask = fed6->src_mask;
316
317
/* Not supported fields. */
318
rec->src_as = rec->dst_as = 0;
319
320
len = sizeof(struct netflow_v9_record_ipv6_tcp);
321
break;
322
}
323
#endif
324
default:
325
{
326
CTR1(KTR_NET, "export9_add(): Don't know what to do with %d flow type!", flow_type);
327
return (0);
328
}
329
}
330
331
/* Check if new records has the same template */
332
if (flow_type != t->flow_type) {
333
/* close old flowset */
334
if (t->flow_type != 0)
335
close_flowset(m, t);
336
337
t->flow_type = flow_type;
338
t->flow_header = m_pktlen(m);
339
340
/* Generate data flowset ID */
341
fsh.id = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + flow_type);
342
fsh.length = 0;
343
344
/* m_append should not fail since all data is already allocated */
345
if (m_append(m, sizeof(fsh), (void *)&fsh) != 1)
346
panic("ng_netflow: m_append() failed");
347
348
}
349
350
if (m_append(m, len, (void *)&rg.rec) != 1)
351
panic("ng_netflow: m_append() failed");
352
353
t->count++;
354
355
if (m_pktlen(m) + sizeof(struct netflow_v9_record_general) + sizeof(struct netflow_v9_flowset_header) >= _NETFLOW_V9_MAX_SIZE(t->mtu))
356
return (1); /* end of datagram */
357
return (0);
358
}
359
360
/*
361
* Detach export datagram from fib instance, if there is any.
362
* If there is no, allocate a new one.
363
*/
364
item_p
365
get_export9_dgram(priv_p priv, fib_export_p fe, struct netflow_v9_packet_opt **tt)
366
{
367
item_p item = NULL;
368
struct netflow_v9_packet_opt *t = NULL;
369
370
mtx_lock(&fe->export9_mtx);
371
if (fe->exp.item9 != NULL) {
372
item = fe->exp.item9;
373
fe->exp.item9 = NULL;
374
t = fe->exp.item9_opt;
375
fe->exp.item9_opt = NULL;
376
}
377
mtx_unlock(&fe->export9_mtx);
378
379
if (item == NULL) {
380
struct netflow_v9_export_dgram *dgram;
381
struct mbuf *m;
382
uint16_t mtu = priv->mtu;
383
384
/* Allocate entire packet at once, allowing easy m_append() calls */
385
m = m_getm(NULL, mtu, M_NOWAIT, MT_DATA);
386
if (m == NULL)
387
return (NULL);
388
389
t = malloc(sizeof(struct netflow_v9_packet_opt), M_NETFLOW_GENERAL, M_NOWAIT | M_ZERO);
390
if (t == NULL) {
391
m_free(m);
392
return (NULL);
393
}
394
395
item = ng_package_data(m, NG_NOFLAGS);
396
if (item == NULL) {
397
free(t, M_NETFLOW_GENERAL);
398
return (NULL);
399
}
400
401
dgram = mtod(m, struct netflow_v9_export_dgram *);
402
dgram->header.count = 0;
403
dgram->header.version = htons(NETFLOW_V9);
404
/* Set mbuf current data length */
405
m->m_len = m->m_pkthdr.len = sizeof(struct netflow_v9_header);
406
407
t->count = 0;
408
t->mtu = mtu;
409
t->flow_header = m->m_len;
410
411
/*
412
* Check if we need to insert templates into packet
413
*/
414
415
struct netflow_v9_flowset_header *fl;
416
417
if ((time_uptime >= priv->templ_time + fe->templ_last_ts) ||
418
(fe->sent_packets >= priv->templ_packets + fe->templ_last_pkt)) {
419
fe->templ_last_ts = time_uptime;
420
fe->templ_last_pkt = fe->sent_packets;
421
422
fl = priv->v9_flowsets[0];
423
m_append(m, ntohs(fl->length), (void *)fl);
424
t->flow_header = m->m_len;
425
t->count += priv->flowset_records[0];
426
}
427
}
428
429
*tt = t;
430
return (item);
431
}
432
433
/*
434
* Re-attach incomplete datagram back to fib instance.
435
* If there is already another one, then send incomplete.
436
*/
437
void
438
return_export9_dgram(priv_p priv, fib_export_p fe, item_p item, struct netflow_v9_packet_opt *t, int flags)
439
{
440
/*
441
* It may happen on SMP, that some thread has already
442
* put its item there, in this case we bail out and
443
* send what we have to collector.
444
*/
445
mtx_lock(&fe->export9_mtx);
446
if (fe->exp.item9 == NULL) {
447
fe->exp.item9 = item;
448
fe->exp.item9_opt = t;
449
mtx_unlock(&fe->export9_mtx);
450
} else {
451
mtx_unlock(&fe->export9_mtx);
452
export9_send(priv, fe, item, t, flags);
453
}
454
}
455
456
/* Allocate memory and set up flow cache */
457
void
458
ng_netflow_v9_cache_init(priv_p priv)
459
{
460
generate_v9_templates(priv);
461
462
priv->templ_time = NETFLOW_V9_MAX_TIME_TEMPL;
463
priv->templ_packets = NETFLOW_V9_MAX_PACKETS_TEMPL;
464
priv->mtu = BASE_MTU;
465
}
466
467
/* Free all flow cache memory. Called from ng_netflow_cache_flush() */
468
void
469
ng_netflow_v9_cache_flush(priv_p priv)
470
{
471
int i;
472
473
/* Free flowsets*/
474
for (i = 0; i < priv->flowsets_count; i++)
475
free(priv->v9_flowsets[i], M_NETFLOW_GENERAL);
476
}
477
478
/* Get a snapshot of NetFlow v9 settings */
479
void
480
ng_netflow_copyv9info(priv_p priv, struct ng_netflow_v9info *i)
481
{
482
483
i->templ_time = priv->templ_time;
484
i->templ_packets = priv->templ_packets;
485
i->mtu = priv->mtu;
486
}
487
488