1219182Sglebius/*-
2219182Sglebius * Copyright (c) 2010 Alexander V. Chernikov <melifaro@ipfw.ru>
3219182Sglebius * All rights reserved.
4219182Sglebius *
5219182Sglebius * Redistribution and use in source and binary forms, with or without
6219182Sglebius * modification, are permitted provided that the following conditions
7219182Sglebius * are met:
8219182Sglebius * 1. Redistributions of source code must retain the above copyright
9219182Sglebius *    notice, this list of conditions and the following disclaimer.
10219182Sglebius * 2. Redistributions in binary form must reproduce the above copyright
11219182Sglebius *    notice, this list of conditions and the following disclaimer in the
12219182Sglebius *    documentation and/or other materials provided with the distribution.
13219182Sglebius *
14219182Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15219182Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16219182Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17219182Sglebius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18219182Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19219182Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20219182Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21219182Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22219182Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23219182Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24219182Sglebius * SUCH DAMAGE.
25219182Sglebius *
26219182Sglebius * 	$FreeBSD$
27219182Sglebius */
28219182Sglebius
29260278Sdim#include <sys/cdefs.h>
30260278Sdim__FBSDID("$FreeBSD$");
31219182Sglebius
32219182Sglebius#include "opt_inet6.h"
33219182Sglebius#include "opt_route.h"
34219182Sglebius#include <sys/param.h>
35219182Sglebius#include <sys/kernel.h>
36219182Sglebius#include <sys/limits.h>
37219182Sglebius#include <sys/mbuf.h>
38219182Sglebius#include <sys/syslog.h>
39219182Sglebius#include <sys/systm.h>
40219182Sglebius#include <sys/socket.h>
41219182Sglebius#include <sys/endian.h>
42219182Sglebius
43219182Sglebius#include <machine/atomic.h>
44219182Sglebius#include <machine/stdarg.h>
45219182Sglebius
46219182Sglebius#include <net/if.h>
47219182Sglebius#include <net/route.h>
48219182Sglebius#include <net/ethernet.h>
49219182Sglebius#include <netinet/in.h>
50219182Sglebius#include <netinet/in_systm.h>
51219182Sglebius#include <netinet/ip.h>
52219182Sglebius#include <netinet/ip6.h>
53219182Sglebius#include <netinet/tcp.h>
54219182Sglebius#include <netinet/udp.h>
55219182Sglebius
56219182Sglebius#include <netgraph/ng_message.h>
57219182Sglebius#include <netgraph/netgraph.h>
58219182Sglebius
59219182Sglebius#include <netgraph/netflow/netflow.h>
60219182Sglebius#include <netgraph/netflow/ng_netflow.h>
61219182Sglebius#include <netgraph/netflow/netflow_v9.h>
62219182Sglebius
63219182SglebiusMALLOC_DECLARE(M_NETFLOW_GENERAL);
64219182SglebiusMALLOC_DEFINE(M_NETFLOW_GENERAL, "netflow_general", "plog, V9 templates data");
65219182Sglebius
66219182Sglebius/*
67219182Sglebius * Base V9 templates for L4+ IPv4/IPv6 protocols
68219182Sglebius */
69219182Sglebiusstruct netflow_v9_template _netflow_v9_record_ipv4_tcp[] =
70219182Sglebius{
71219182Sglebius	{ NETFLOW_V9_FIELD_IPV4_SRC_ADDR, 4},
72219182Sglebius	{ NETFLOW_V9_FIELD_IPV4_DST_ADDR, 4},
73219182Sglebius	{ NETFLOW_V9_FIELD_IPV4_NEXT_HOP, 4},
74219182Sglebius	{ NETFLOW_V9_FIELD_INPUT_SNMP, 2},
75219182Sglebius	{ NETFLOW_V9_FIELD_OUTPUT_SNMP, 2},
76219182Sglebius	{ NETFLOW_V9_FIELD_IN_PKTS, sizeof(CNTR)},
77219182Sglebius	{ NETFLOW_V9_FIELD_IN_BYTES, sizeof(CNTR)},
78219182Sglebius	{ NETFLOW_V9_FIELD_OUT_PKTS, sizeof(CNTR)},
79219182Sglebius	{ NETFLOW_V9_FIELD_OUT_BYTES, sizeof(CNTR)},
80219182Sglebius	{ NETFLOW_V9_FIELD_FIRST_SWITCHED, 4},
81219182Sglebius	{ NETFLOW_V9_FIELD_LAST_SWITCHED, 4},
82219182Sglebius	{ NETFLOW_V9_FIELD_L4_SRC_PORT, 2},
83219182Sglebius	{ NETFLOW_V9_FIELD_L4_DST_PORT, 2},
84219182Sglebius	{ NETFLOW_V9_FIELD_TCP_FLAGS, 1},
85219182Sglebius	{ NETFLOW_V9_FIELD_PROTOCOL, 1},
86219182Sglebius	{ NETFLOW_V9_FIELD_TOS, 1},
87219182Sglebius	{ NETFLOW_V9_FIELD_SRC_AS, 4},
88219182Sglebius	{ NETFLOW_V9_FIELD_DST_AS, 4},
89219182Sglebius	{ NETFLOW_V9_FIELD_SRC_MASK, 1},
90219182Sglebius	{ NETFLOW_V9_FIELD_DST_MASK, 1},
91219182Sglebius	{0, 0}
92219182Sglebius};
93219182Sglebius
94219182Sglebiusstruct netflow_v9_template _netflow_v9_record_ipv6_tcp[] =
95219182Sglebius{
96219182Sglebius	{ NETFLOW_V9_FIELD_IPV6_SRC_ADDR, 16},
97219182Sglebius	{ NETFLOW_V9_FIELD_IPV6_DST_ADDR, 16},
98219182Sglebius	{ NETFLOW_V9_FIELD_IPV6_NEXT_HOP, 16},
99219182Sglebius	{ NETFLOW_V9_FIELD_INPUT_SNMP, 2},
100219182Sglebius	{ NETFLOW_V9_FIELD_OUTPUT_SNMP, 2},
101219182Sglebius	{ NETFLOW_V9_FIELD_IN_PKTS, sizeof(CNTR)},
102219182Sglebius	{ NETFLOW_V9_FIELD_IN_BYTES, sizeof(CNTR)},
103219182Sglebius	{ NETFLOW_V9_FIELD_OUT_PKTS, sizeof(CNTR)},
104219182Sglebius	{ NETFLOW_V9_FIELD_OUT_BYTES, sizeof(CNTR)},
105219182Sglebius	{ NETFLOW_V9_FIELD_FIRST_SWITCHED, 4},
106219182Sglebius	{ NETFLOW_V9_FIELD_LAST_SWITCHED, 4},
107219182Sglebius	{ NETFLOW_V9_FIELD_L4_SRC_PORT, 2},
108219182Sglebius	{ NETFLOW_V9_FIELD_L4_DST_PORT, 2},
109219182Sglebius	{ NETFLOW_V9_FIELD_TCP_FLAGS, 1},
110219182Sglebius	{ NETFLOW_V9_FIELD_PROTOCOL, 1},
111219182Sglebius	{ NETFLOW_V9_FIELD_TOS, 1},
112219182Sglebius	{ NETFLOW_V9_FIELD_SRC_AS, 4},
113219182Sglebius	{ NETFLOW_V9_FIELD_DST_AS, 4},
114219182Sglebius	{ NETFLOW_V9_FIELD_SRC_MASK, 1},
115219182Sglebius	{ NETFLOW_V9_FIELD_DST_MASK, 1},
116219182Sglebius	{0, 0}
117219182Sglebius};
118219182Sglebius
119219182Sglebius/*
120219182Sglebius * Pre-compiles flow exporter for all possible FlowSets
121219182Sglebius * so we can add flowset to packet via simple memcpy()
122219182Sglebius */
123219182Sglebiusstatic void
124219182Sglebiusgenerate_v9_templates(priv_p priv)
125219182Sglebius{
126219182Sglebius	uint16_t *p, *template_fields_cnt;
127219182Sglebius	int cnt;
128219182Sglebius
129219182Sglebius	int flowset_size = sizeof(struct netflow_v9_flowset_header) +
130219182Sglebius		_NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv4_tcp) + /* netflow_v9_record_ipv4_tcp */
131219182Sglebius		_NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv6_tcp); /* netflow_v9_record_ipv6_tcp */
132219182Sglebius
133219182Sglebius	priv->v9_flowsets[0] = malloc(flowset_size, M_NETFLOW_GENERAL, M_WAITOK | M_ZERO);
134219182Sglebius
135219182Sglebius	if (flowset_size % 4)
136219182Sglebius		flowset_size += 4 - (flowset_size % 4); /* Padding to 4-byte boundary */
137219182Sglebius
138219182Sglebius	priv->flowsets_count = 1;
139219182Sglebius	p = (uint16_t *)priv->v9_flowsets[0];
140219182Sglebius	*p++ = 0; /* Flowset ID, 0 is reserved for Template FlowSets  */
141219182Sglebius	*p++ = htons(flowset_size); /* Total FlowSet length */
142219182Sglebius
143219182Sglebius	/*
144219182Sglebius	 * Most common TCP/UDP IPv4 template, ID = 256
145219182Sglebius	 */
146219182Sglebius	*p++ = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + NETFLOW_V9_FLOW_V4_L4);
147219182Sglebius	template_fields_cnt = p++;
148219182Sglebius	for (cnt = 0; _netflow_v9_record_ipv4_tcp[cnt].field_id != 0; cnt++) {
149219182Sglebius		*p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_id);
150219182Sglebius		*p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_length);
151219182Sglebius	}
152219182Sglebius	*template_fields_cnt = htons(cnt);
153219182Sglebius
154219182Sglebius	/*
155219182Sglebius	 * TCP/UDP IPv6 template, ID = 257
156219182Sglebius	 */
157219182Sglebius	*p++ = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + NETFLOW_V9_FLOW_V6_L4);
158219182Sglebius	template_fields_cnt = p++;
159219182Sglebius	for (cnt = 0; _netflow_v9_record_ipv6_tcp[cnt].field_id != 0; cnt++) {
160219182Sglebius		*p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_id);
161219182Sglebius		*p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_length);
162219182Sglebius	}
163219182Sglebius	*template_fields_cnt = htons(cnt);
164219182Sglebius
165219182Sglebius	priv->flowset_records[0] = 2;
166219182Sglebius}
167219182Sglebius
168219182Sglebius/* Closes current data flowset */
169219182Sglebiusstatic void inline
170219182Sglebiusclose_flowset(struct mbuf *m, struct netflow_v9_packet_opt *t)
171219182Sglebius{
172219182Sglebius	struct mbuf *m_old;
173219182Sglebius	uint32_t zero = 0;
174219182Sglebius	int offset = 0;
175219182Sglebius	uint16_t *flowset_length, len;
176219182Sglebius
177219182Sglebius	/* Hack to ensure we are not crossing mbuf boundary, length is uint16_t  */
178219182Sglebius	m_old = m_getptr(m, t->flow_header + offsetof(struct netflow_v9_flowset_header, length), &offset);
179219182Sglebius	flowset_length = (uint16_t *)(mtod(m_old, char *) + offset);
180219182Sglebius
181219182Sglebius	len = (uint16_t)(m_pktlen(m) - t->flow_header);
182219182Sglebius	/* Align on 4-byte boundary (RFC 3954, Clause 5.3) */
183219182Sglebius	if (len % 4) {
184219182Sglebius		if (m_append(m, 4 - (len % 4), (void *)&zero) != 1)
185219182Sglebius			panic("ng_netflow: m_append() failed!");
186219182Sglebius
187219182Sglebius		len += 4 - (len % 4);
188219182Sglebius	}
189219182Sglebius
190219182Sglebius	*flowset_length = htons(len);
191219182Sglebius}
192219182Sglebius
193219182Sglebius/*
194219182Sglebius * Non-static functions called from ng_netflow.c
195219182Sglebius */
196219182Sglebius
197219182Sglebius/* We have full datagram in fib data. Send it to export hook. */
198219182Sglebiusint
199219182Sglebiusexport9_send(priv_p priv, fib_export_p fe, item_p item, struct netflow_v9_packet_opt *t, int flags)
200219182Sglebius{
201219182Sglebius	struct mbuf *m = NGI_M(item);
202219182Sglebius	struct netflow_v9_export_dgram *dgram = mtod(m,
203219182Sglebius					struct netflow_v9_export_dgram *);
204219182Sglebius	struct netflow_v9_header *header = &dgram->header;
205219182Sglebius	struct timespec ts;
206219182Sglebius	int error = 0;
207219182Sglebius
208219182Sglebius	if (t == NULL) {
209219182Sglebius		CTR0(KTR_NET, "export9_send(): V9 export packet without tag");
210219182Sglebius		NG_FREE_ITEM(item);
211219182Sglebius		return (0);
212219182Sglebius	}
213219182Sglebius
214219182Sglebius	/* Close flowset if not closed already */
215219182Sglebius	if (m_pktlen(m) != t->flow_header)
216219182Sglebius		close_flowset(m, t);
217219182Sglebius
218219182Sglebius	/* Fill export header. */
219219182Sglebius	header->count = t->count;
220219182Sglebius	header->sys_uptime = htonl(MILLIUPTIME(time_uptime));
221219182Sglebius	getnanotime(&ts);
222219182Sglebius	header->unix_secs  = htonl(ts.tv_sec);
223219182Sglebius	header->seq_num = htonl(atomic_fetchadd_32(&fe->flow9_seq, 1));
224219182Sglebius	header->count = htons(t->count);
225238295Smelifaro	header->source_id = htonl(fe->domain_id);
226219182Sglebius
227219182Sglebius	if (priv->export9 != NULL)
228219182Sglebius		NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export9, flags);
229219182Sglebius	else
230219182Sglebius		NG_FREE_ITEM(item);
231219182Sglebius
232219182Sglebius	free(t, M_NETFLOW_GENERAL);
233219182Sglebius
234219182Sglebius	return (error);
235219182Sglebius}
236219182Sglebius
237219182Sglebius
238219182Sglebius
239219182Sglebius/* Add V9 record to dgram. */
240219182Sglebiusint
241219182Sglebiusexport9_add(item_p item, struct netflow_v9_packet_opt *t, struct flow_entry *fle)
242219182Sglebius{
243219182Sglebius	size_t len = 0;
244219182Sglebius	struct netflow_v9_flowset_header fsh;
245219182Sglebius	struct netflow_v9_record_general rg;
246219182Sglebius	struct mbuf *m = NGI_M(item);
247219182Sglebius	uint16_t flow_type;
248219182Sglebius	struct flow_entry_data *fed;
249219182Sglebius#ifdef INET6
250219182Sglebius	struct flow6_entry_data *fed6;
251219182Sglebius#endif
252219182Sglebius	if (t == NULL) {
253219182Sglebius		CTR0(KTR_NET, "ng_netflow: V9 export packet without tag!");
254219182Sglebius		return (0);
255219182Sglebius	}
256219182Sglebius
257219182Sglebius	/* Prepare flow record */
258219182Sglebius	fed = (struct flow_entry_data *)&fle->f;
259219229Sbz#ifdef INET6
260219182Sglebius	fed6 = (struct flow6_entry_data *)&fle->f;
261219229Sbz#endif
262219182Sglebius	/* We can use flow_type field since fle6 offset is equal to fle */
263219182Sglebius	flow_type = fed->r.flow_type;
264219182Sglebius
265219182Sglebius	switch (flow_type) {
266219182Sglebius	case NETFLOW_V9_FLOW_V4_L4:
267219182Sglebius	{
268219182Sglebius		/* IPv4 TCP/UDP/[SCTP] */
269219182Sglebius		struct netflow_v9_record_ipv4_tcp *rec = &rg.rec.v4_tcp;
270219182Sglebius
271219182Sglebius		rec->src_addr = fed->r.r_src.s_addr;
272219182Sglebius		rec->dst_addr = fed->r.r_dst.s_addr;
273219182Sglebius		rec->next_hop = fed->next_hop.s_addr;
274219182Sglebius		rec->i_ifx    = htons(fed->fle_i_ifx);
275219182Sglebius		rec->o_ifx    = htons(fed->fle_o_ifx);
276219182Sglebius		rec->i_packets  = htonl(fed->packets);
277219182Sglebius		rec->i_octets   = htonl(fed->bytes);
278219182Sglebius		rec->o_packets  = htonl(0);
279219182Sglebius		rec->o_octets   = htonl(0);
280219182Sglebius		rec->first    = htonl(MILLIUPTIME(fed->first));
281219182Sglebius		rec->last     = htonl(MILLIUPTIME(fed->last));
282219182Sglebius		rec->s_port   = fed->r.r_sport;
283219182Sglebius		rec->d_port   = fed->r.r_dport;
284219182Sglebius		rec->flags    = fed->tcp_flags;
285219182Sglebius		rec->prot     = fed->r.r_ip_p;
286219182Sglebius		rec->tos      = fed->r.r_tos;
287219182Sglebius		rec->dst_mask = fed->dst_mask;
288219182Sglebius		rec->src_mask = fed->src_mask;
289219182Sglebius
290219182Sglebius		/* Not supported fields. */
291219182Sglebius		rec->src_as = rec->dst_as = 0;
292219182Sglebius
293219182Sglebius		len = sizeof(struct netflow_v9_record_ipv4_tcp);
294219182Sglebius		break;
295219182Sglebius	}
296219182Sglebius#ifdef INET6
297219182Sglebius	case NETFLOW_V9_FLOW_V6_L4:
298219182Sglebius	{
299219182Sglebius		/* IPv6 TCP/UDP/[SCTP] */
300219182Sglebius		struct netflow_v9_record_ipv6_tcp *rec = &rg.rec.v6_tcp;
301219182Sglebius
302219182Sglebius		rec->src_addr = fed6->r.src.r_src6;
303219182Sglebius		rec->dst_addr = fed6->r.dst.r_dst6;
304219182Sglebius		rec->next_hop = fed6->n.next_hop6;
305219182Sglebius		rec->i_ifx    = htons(fed6->fle_i_ifx);
306219182Sglebius		rec->o_ifx    = htons(fed6->fle_o_ifx);
307219182Sglebius		rec->i_packets  = htonl(fed6->packets);
308219182Sglebius		rec->i_octets   = htonl(fed6->bytes);
309219182Sglebius		rec->o_packets  = htonl(0);
310219182Sglebius		rec->o_octets   = htonl(0);
311219182Sglebius		rec->first    = htonl(MILLIUPTIME(fed6->first));
312219182Sglebius		rec->last     = htonl(MILLIUPTIME(fed6->last));
313219182Sglebius		rec->s_port   = fed6->r.r_sport;
314219182Sglebius		rec->d_port   = fed6->r.r_dport;
315219182Sglebius		rec->flags    = fed6->tcp_flags;
316219182Sglebius		rec->prot     = fed6->r.r_ip_p;
317219182Sglebius		rec->tos      = fed6->r.r_tos;
318219182Sglebius		rec->dst_mask = fed6->dst_mask;
319219182Sglebius		rec->src_mask = fed6->src_mask;
320219182Sglebius
321219182Sglebius		/* Not supported fields. */
322219182Sglebius		rec->src_as = rec->dst_as = 0;
323219182Sglebius
324219182Sglebius		len = sizeof(struct netflow_v9_record_ipv6_tcp);
325219182Sglebius		break;
326219182Sglebius	}
327219182Sglebius#endif
328219182Sglebius	default:
329219182Sglebius	{
330219182Sglebius		CTR1(KTR_NET, "export9_add(): Don't know what to do with %d flow type!", flow_type);
331219182Sglebius		return (0);
332219182Sglebius	}
333219182Sglebius	}
334219182Sglebius
335219182Sglebius	/* Check if new records has the same template */
336219182Sglebius	if (flow_type != t->flow_type) {
337219182Sglebius		/* close old flowset */
338219182Sglebius		if (t->flow_type != 0)
339219182Sglebius			close_flowset(m, t);
340219182Sglebius
341219182Sglebius		t->flow_type = flow_type;
342219182Sglebius		t->flow_header = m_pktlen(m);
343219182Sglebius
344219182Sglebius		/* Generate data flowset ID */
345219182Sglebius		fsh.id = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + flow_type);
346219182Sglebius		fsh.length = 0;
347219182Sglebius
348219182Sglebius		/* m_append should not fail since all data is already allocated */
349219182Sglebius		if (m_append(m, sizeof(fsh), (void *)&fsh) != 1)
350219182Sglebius			panic("ng_netflow: m_append() failed");
351219182Sglebius
352219182Sglebius	}
353219182Sglebius
354219182Sglebius	if (m_append(m, len, (void *)&rg.rec) != 1)
355219182Sglebius		panic("ng_netflow: m_append() failed");
356219182Sglebius
357219182Sglebius	t->count++;
358219182Sglebius
359219182Sglebius	if (m_pktlen(m) + sizeof(struct netflow_v9_record_general) + sizeof(struct netflow_v9_flowset_header) >= _NETFLOW_V9_MAX_SIZE(t->mtu))
360219182Sglebius		return (1); /* end of datagram */
361219182Sglebius	return (0);
362219182Sglebius}
363219182Sglebius
364219182Sglebius/*
365219182Sglebius * Detach export datagram from fib instance, if there is any.
366219182Sglebius * If there is no, allocate a new one.
367219182Sglebius */
368219182Sglebiusitem_p
369219182Sglebiusget_export9_dgram(priv_p priv, fib_export_p fe, struct netflow_v9_packet_opt **tt)
370219182Sglebius{
371219182Sglebius	item_p	item = NULL;
372219182Sglebius	struct netflow_v9_packet_opt *t = NULL;
373219182Sglebius
374219182Sglebius	mtx_lock(&fe->export9_mtx);
375219182Sglebius	if (fe->exp.item9 != NULL) {
376219182Sglebius		item = fe->exp.item9;
377219182Sglebius		fe->exp.item9 = NULL;
378219182Sglebius		t = fe->exp.item9_opt;
379219182Sglebius		fe->exp.item9_opt = NULL;
380219182Sglebius	}
381219182Sglebius	mtx_unlock(&fe->export9_mtx);
382219182Sglebius
383219182Sglebius	if (item == NULL) {
384219182Sglebius		struct netflow_v9_export_dgram *dgram;
385219182Sglebius		struct mbuf *m;
386219182Sglebius		uint16_t mtu = priv->mtu;
387219182Sglebius
388219182Sglebius		/* Allocate entire packet at once, allowing easy m_append() calls */
389219182Sglebius		m = m_getm(NULL, mtu, M_DONTWAIT, MT_DATA);
390219182Sglebius		if (m == NULL)
391219182Sglebius			return (NULL);
392219182Sglebius
393219182Sglebius		t = malloc(sizeof(struct netflow_v9_packet_opt), M_NETFLOW_GENERAL, M_NOWAIT | M_ZERO);
394219182Sglebius		if (t == NULL) {
395219182Sglebius			m_free(m);
396219182Sglebius			return (NULL);
397219182Sglebius		}
398219182Sglebius
399219182Sglebius		item = ng_package_data(m, NG_NOFLAGS);
400219182Sglebius		if (item == NULL) {
401219182Sglebius			free(t, M_NETFLOW_GENERAL);
402219182Sglebius			return (NULL);
403219182Sglebius		}
404219182Sglebius
405219182Sglebius		dgram = mtod(m, struct netflow_v9_export_dgram *);
406219182Sglebius		dgram->header.count = 0;
407219182Sglebius		dgram->header.version = htons(NETFLOW_V9);
408219182Sglebius		/* Set mbuf current data length */
409219182Sglebius		m->m_len = m->m_pkthdr.len = sizeof(struct netflow_v9_header);
410219182Sglebius
411219182Sglebius		t->count = 0;
412219182Sglebius		t->mtu = mtu;
413219182Sglebius		t->flow_header = m->m_len;
414219182Sglebius
415219182Sglebius		/*
416219182Sglebius		 * Check if we need to insert templates into packet
417219182Sglebius		 */
418219182Sglebius
419219182Sglebius		struct netflow_v9_flowset_header	*fl;
420219182Sglebius
421238295Smelifaro		if ((time_uptime >= priv->templ_time + fe->templ_last_ts) ||
422219182Sglebius				(fe->sent_packets >= priv->templ_packets + fe->templ_last_pkt)) {
423219182Sglebius
424238295Smelifaro			fe->templ_last_ts = time_uptime;
425238295Smelifaro			fe->templ_last_pkt = fe->sent_packets;
426238295Smelifaro
427219182Sglebius			fl = priv->v9_flowsets[0];
428219182Sglebius			m_append(m, ntohs(fl->length), (void *)fl);
429219182Sglebius			t->flow_header = m->m_len;
430219182Sglebius			t->count += priv->flowset_records[0];
431219182Sglebius		}
432219182Sglebius
433219182Sglebius	}
434219182Sglebius
435219182Sglebius	*tt = t;
436219182Sglebius	return (item);
437219182Sglebius}
438219182Sglebius
439219182Sglebius/*
440219182Sglebius * Re-attach incomplete datagram back to fib instance.
441219182Sglebius * If there is already another one, then send incomplete.
442219182Sglebius */
443219182Sglebiusvoid
444219182Sglebiusreturn_export9_dgram(priv_p priv, fib_export_p fe, item_p item, struct netflow_v9_packet_opt *t, int flags)
445219182Sglebius{
446219182Sglebius	/*
447219182Sglebius	 * It may happen on SMP, that some thread has already
448219182Sglebius	 * put its item there, in this case we bail out and
449219182Sglebius	 * send what we have to collector.
450219182Sglebius	 */
451219182Sglebius	mtx_lock(&fe->export9_mtx);
452219182Sglebius	if (fe->exp.item9 == NULL) {
453219182Sglebius		fe->exp.item9 = item;
454219182Sglebius		fe->exp.item9_opt = t;
455219182Sglebius		mtx_unlock(&fe->export9_mtx);
456219182Sglebius	} else {
457219182Sglebius		mtx_unlock(&fe->export9_mtx);
458219182Sglebius		export9_send(priv, fe, item, t, flags);
459219182Sglebius	}
460219182Sglebius}
461219182Sglebius
462219182Sglebius/* Allocate memory and set up flow cache */
463219182Sglebiusvoid
464219182Sglebiusng_netflow_v9_cache_init(priv_p priv)
465219182Sglebius{
466219182Sglebius	generate_v9_templates(priv);
467219182Sglebius
468219182Sglebius	priv->templ_time = NETFLOW_V9_MAX_TIME_TEMPL;
469219182Sglebius	priv->templ_packets = NETFLOW_V9_MAX_PACKETS_TEMPL;
470219182Sglebius	priv->mtu = BASE_MTU;
471219182Sglebius}
472219182Sglebius
473219182Sglebius/* Free all flow cache memory. Called from ng_netflow_cache_flush() */
474219182Sglebiusvoid
475219182Sglebiusng_netflow_v9_cache_flush(priv_p priv)
476219182Sglebius{
477219182Sglebius	int i;
478219182Sglebius
479219182Sglebius	/* Free flowsets*/
480219182Sglebius	for (i = 0; i < priv->flowsets_count; i++)
481219182Sglebius		free(priv->v9_flowsets[i], M_NETFLOW_GENERAL);
482219182Sglebius}
483243983Smelifaro
484243983Smelifaro/* Get a snapshot of NetFlow v9 settings */
485243983Smelifarovoid
486243983Smelifarong_netflow_copyv9info(priv_p priv, struct ng_netflow_v9info *i)
487243983Smelifaro{
488243983Smelifaro
489243983Smelifaro	i->templ_time = priv->templ_time;
490243983Smelifaro	i->templ_packets = priv->templ_packets;
491243983Smelifaro	i->mtu = priv->mtu;
492243983Smelifaro}
493243983Smelifaro
494