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