netflow.c revision 143923
1135332Sglebius/*-
2143923Sglebius * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org>
3135332Sglebius * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net>
4135332Sglebius * All rights reserved.
5135332Sglebius *
6135332Sglebius * Redistribution and use in source and binary forms, with or without
7135332Sglebius * modification, are permitted provided that the following conditions
8135332Sglebius * are met:
9135332Sglebius * 1. Redistributions of source code must retain the above copyright
10135332Sglebius *    notice, this list of conditions and the following disclaimer.
11135332Sglebius * 2. Redistributions in binary form must reproduce the above copyright
12135332Sglebius *    notice, this list of conditions and the following disclaimer in the
13135332Sglebius *    documentation and/or other materials provided with the distribution.
14135332Sglebius *
15135332Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16135332Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17135332Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18135332Sglebius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19135332Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20135332Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21135332Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22135332Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23135332Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24135332Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25135332Sglebius * SUCH DAMAGE.
26135332Sglebius *
27135332Sglebius * $SourceForge: netflow.c,v 1.41 2004/09/05 11:41:10 glebius Exp $
28135332Sglebius */
29135332Sglebius
30135332Sglebiusstatic const char rcs_id[] =
31135332Sglebius    "@(#) $FreeBSD: head/sys/netgraph/netflow/netflow.c 143923 2005-03-21 15:34:03Z glebius $";
32135332Sglebius
33135332Sglebius#include <sys/param.h>
34135332Sglebius#include <sys/kernel.h>
35135332Sglebius#include <sys/limits.h>
36135332Sglebius#include <sys/mbuf.h>
37140511Sglebius#include <sys/syslog.h>
38135332Sglebius#include <sys/systm.h>
39135332Sglebius#include <sys/socket.h>
40135332Sglebius
41135332Sglebius#include <net/if.h>
42135332Sglebius#include <net/if_var.h>
43135332Sglebius#include <net/if_dl.h>
44135332Sglebius#include <net/route.h>
45135332Sglebius#include <netinet/in.h>
46135332Sglebius#include <netinet/in_systm.h>
47135332Sglebius#include <netinet/ip.h>
48135332Sglebius#include <netinet/tcp.h>
49135332Sglebius#include <netinet/udp.h>
50135332Sglebius
51135332Sglebius#include <netgraph/ng_message.h>
52135332Sglebius#include <netgraph/netgraph.h>
53135332Sglebius
54135332Sglebius#include <netgraph/netflow/netflow.h>
55135332Sglebius#include <netgraph/netflow/ng_netflow.h>
56135332Sglebius
57135332Sglebius#define	NBUCKETS	(4096)	/* must be power of 2 */
58135332Sglebius
59135332Sglebius/* This hash is for TCP or UDP packets */
60135332Sglebius#define FULL_HASH(addr1,addr2,port1,port2)\
61135332Sglebius	(((addr1 >> 16) ^		\
62135332Sglebius	  (addr2 & 0x00FF) ^		\
63135332Sglebius	  ((port1 ^ port2) << 8) )&	\
64135332Sglebius	 (NBUCKETS - 1))
65135332Sglebius
66135332Sglebius/* This hash for all other IP packets */
67135332Sglebius#define ADDR_HASH(addr1,addr2)\
68135332Sglebius	(((addr1 >> 16) ^		\
69135332Sglebius	  (addr2 & 0x00FF) )&		\
70135332Sglebius	 (NBUCKETS - 1))
71135332Sglebius
72135332Sglebius/* Macros to shorten logical constructions */
73135332Sglebius/* XXX: priv must exist in namespace */
74135332Sglebius#define	INACTIVE(fle)	(time_uptime - fle->f.last > priv->info.nfinfo_inact_t)
75135332Sglebius#define	AGED(fle)	(time_uptime - fle->f.first > priv->info.nfinfo_act_t)
76135332Sglebius#define	ISFREE(fle)	(fle->f.packets == 0)
77135332Sglebius
78135332Sglebius/*
79135332Sglebius * 4 is a magical number: statistically number of 4-packet flows is
80135332Sglebius * bigger than 5,6,7...-packet flows by an order of magnitude. Most UDP/ICMP
81135332Sglebius * scans are 1 packet (~ 90% of flow cache). TCP scans are 2-packet in case
82135332Sglebius * of reachable host and 4-packet otherwise.
83135332Sglebius */
84135332Sglebius#define	SMALL(fle)	(fle->f.packets <= 4)
85143103Sglebius
86143103Sglebius/*
87143103Sglebius * Cisco uses milliseconds for uptime. Bad idea, since it overflows
88143103Sglebius * every 48+ days. But we will do same to keep compatibility. This macro
89143103Sglebius * does overflowable multiplication to 1000.
90143103Sglebius */
91143103Sglebius#define	MILLIUPTIME(t)	(((t) << 9) +	/* 512 */	\
92143103Sglebius			 ((t) << 8) +	/* 256 */	\
93143103Sglebius			 ((t) << 7) +	/* 128 */	\
94143103Sglebius			 ((t) << 6) +	/* 64  */	\
95143103Sglebius			 ((t) << 5) +	/* 32  */	\
96143103Sglebius			 ((t) << 3))	/* 8   */
97143103Sglebius
98135332SglebiusMALLOC_DECLARE(M_NETFLOW);
99135332SglebiusMALLOC_DEFINE(M_NETFLOW, "NetFlow", "flow cache");
100135332Sglebius
101135332Sglebiusstatic int export_add(priv_p , struct flow_entry *);
102135332Sglebiusstatic int export_send(priv_p );
103135332Sglebius
104135332Sglebius/* Generate hash for a given flow record */
105135332Sglebiusstatic __inline uint32_t
106135332Sglebiusip_hash(struct flow_rec *r)
107135332Sglebius{
108135332Sglebius	switch (r->r_ip_p) {
109135332Sglebius	case IPPROTO_TCP:
110135332Sglebius	case IPPROTO_UDP:
111135332Sglebius		return FULL_HASH(r->r_src.s_addr, r->r_dst.s_addr,
112135332Sglebius		    r->r_sport, r->r_dport);
113135332Sglebius	default:
114135332Sglebius		return ADDR_HASH(r->r_src.s_addr, r->r_dst.s_addr);
115135332Sglebius	}
116135332Sglebius}
117135332Sglebius
118135332Sglebius/* Lookup for record in given slot */
119135332Sglebiusstatic __inline struct flow_entry *
120135332Sglebiushash_lookup(struct flow_hash_entry *h, int slot, struct flow_rec *r)
121135332Sglebius{
122135332Sglebius	struct flow_entry *fle;
123135332Sglebius
124135332Sglebius	LIST_FOREACH(fle, &(h[slot].head), fle_hash)
125135332Sglebius		if (bcmp(r, &fle->f.r, sizeof(struct flow_rec)) == 0)
126135332Sglebius			return (fle);
127135332Sglebius
128135332Sglebius	return (NULL);
129135332Sglebius}
130135332Sglebius
131135332Sglebius/* Get a flow entry from free list */
132135332Sglebiusstatic __inline struct flow_entry *
133135332Sglebiusalloc_flow(priv_p priv, int *flows)
134135332Sglebius{
135135332Sglebius	register struct flow_entry	*fle;
136135332Sglebius
137135332Sglebius	mtx_lock(&priv->free_mtx);
138135332Sglebius
139135332Sglebius	if (SLIST_EMPTY(&priv->free_list)) {
140135332Sglebius		mtx_unlock(&priv->free_mtx);
141135332Sglebius		return(NULL);
142135332Sglebius	}
143135332Sglebius
144135332Sglebius	fle = SLIST_FIRST(&priv->free_list);
145135332Sglebius	SLIST_REMOVE_HEAD(&priv->free_list, fle_free);
146135332Sglebius
147135332Sglebius	priv->info.nfinfo_used++;
148135332Sglebius	priv->info.nfinfo_free--;
149135332Sglebius
150135332Sglebius	if (flows != NULL)
151135332Sglebius		*flows = priv->info.nfinfo_used;
152135332Sglebius
153135332Sglebius	mtx_unlock(&priv->free_mtx);
154135332Sglebius
155135332Sglebius	return (fle);
156135332Sglebius}
157135332Sglebius
158135332Sglebius/* Insert flow entry into a free list. */
159135332Sglebiusstatic __inline int
160135332Sglebiusfree_flow(priv_p priv, struct flow_entry *fle)
161135332Sglebius{
162135332Sglebius	int flows;
163135332Sglebius
164135332Sglebius	mtx_lock(&priv->free_mtx);
165135332Sglebius	fle->f.packets = 0;
166135332Sglebius	SLIST_INSERT_HEAD(&priv->free_list, fle, fle_free);
167135332Sglebius	flows = priv->info.nfinfo_used--;
168135332Sglebius	priv->info.nfinfo_free++;
169135332Sglebius	mtx_unlock(&priv->free_mtx);
170135332Sglebius
171135332Sglebius	return flows;
172135332Sglebius}
173135332Sglebius
174135332Sglebius#define	NGNF_GETUSED(priv, rval)	do {	\
175135332Sglebius	mtx_lock(&priv->free_mtx);		\
176135332Sglebius	rval = priv->info.nfinfo_used;		\
177135332Sglebius	mtx_unlock(&priv->free_mtx);		\
178135332Sglebius	} while (0)
179135332Sglebius
180135332Sglebius/* Insert flow entry into expire list. */
181135332Sglebius/* XXX: Flow must be detached from work queue, but not from cache */
182135332Sglebiusstatic __inline void
183135332Sglebiusexpire_flow(priv_p priv, struct flow_entry *fle)
184135332Sglebius{
185135332Sglebius	mtx_assert(&priv->work_mtx, MA_OWNED);
186135332Sglebius	LIST_REMOVE(fle, fle_hash);
187135332Sglebius
188135332Sglebius	mtx_lock(&priv->expire_mtx);
189135332Sglebius	SLIST_INSERT_HEAD(&priv->expire_list, fle, fle_free);
190135332Sglebius	mtx_unlock(&priv->expire_mtx);
191135332Sglebius}
192135332Sglebius
193135332Sglebius/* Get a snapshot of node statistics */
194135332Sglebiusvoid
195135332Sglebiusng_netflow_copyinfo(priv_p priv, struct ng_netflow_info *i)
196135332Sglebius{
197135332Sglebius	mtx_lock(&priv->free_mtx);
198135332Sglebius	memcpy((void *)i, (void *)&priv->info, sizeof(priv->info));
199135332Sglebius	mtx_unlock(&priv->free_mtx);
200135332Sglebius}
201135332Sglebius
202135332Sglebius/* Calculate number of bits in netmask */
203135332Sglebius#define	g21	0x55555555ul	/* = 0101_0101_0101_0101_0101_0101_0101_0101 */
204135332Sglebius#define	g22	0x33333333ul	/* = 0011_0011_0011_0011_0011_0011_0011_0011 */
205135332Sglebius#define	g23	0x0f0f0f0ful	/* = 0000_1111_0000_1111_0000_1111_0000_1111 */
206135332Sglebiusstatic __inline u_char
207135332Sglebiusbit_count(uint32_t v)
208135332Sglebius{
209135332Sglebius	v = (v & g21) + ((v >> 1) & g21);
210135332Sglebius	v = (v & g22) + ((v >> 2) & g22);
211135332Sglebius	v = (v + (v >> 4)) & g23;
212135332Sglebius	return (v + (v >> 8) + (v >> 16) + (v >> 24)) & 0x3f;
213135332Sglebius}
214135332Sglebius
215135332Sglebius/*
216135332Sglebius * Insert a record into defined slot.
217135332Sglebius *
218135332Sglebius * First we get for us a free flow entry, then fill in all
219135332Sglebius * possible fields in it. Then obtain lock on flow cache
220135332Sglebius * and insert flow entry.
221135332Sglebius */
222135332Sglebiusstatic __inline int
223143890Sglebiushash_insert(priv_p priv, int slot, struct flow_rec *r, int plen,
224143890Sglebius	uint8_t tcp_flags)
225135332Sglebius{
226135332Sglebius	struct flow_hash_entry	*h = priv->hash;
227135332Sglebius	struct flow_entry	*fle;
228135332Sglebius	struct route ro;
229135332Sglebius	struct sockaddr_in *sin;
230135332Sglebius
231135332Sglebius	fle = alloc_flow(priv, NULL);
232135332Sglebius	if (fle == NULL)
233135332Sglebius		return (ENOMEM);
234135332Sglebius
235135332Sglebius	/*
236135332Sglebius	 * Now fle is totally ours. It is detached from all lists,
237135332Sglebius	 * we can safely edit it.
238135332Sglebius	 */
239135332Sglebius
240135332Sglebius	bcopy(r, &fle->f.r, sizeof(struct flow_rec));
241135332Sglebius	fle->f.bytes = plen;
242135332Sglebius	fle->f.packets = 1;
243143890Sglebius	fle->f.tcp_flags = tcp_flags;
244135332Sglebius
245135332Sglebius	fle->f.first = fle->f.last = time_uptime;
246135332Sglebius
247135332Sglebius	/*
248135332Sglebius	 * First we do route table lookup on destination address. So we can
249135332Sglebius	 * fill in out_ifx, dst_mask, nexthop, and dst_as in future releases.
250135332Sglebius	 */
251135332Sglebius	bzero((caddr_t)&ro, sizeof(ro));
252135332Sglebius	sin = (struct sockaddr_in *)&ro.ro_dst;
253135332Sglebius	sin->sin_len = sizeof(*sin);
254135332Sglebius	sin->sin_family = AF_INET;
255135332Sglebius	sin->sin_addr = fle->f.r.r_dst;
256135332Sglebius	rtalloc_ign(&ro, RTF_CLONING);
257135332Sglebius	if (ro.ro_rt != NULL) {
258135332Sglebius		struct rtentry *rt = ro.ro_rt;
259135332Sglebius
260135332Sglebius		fle->f.fle_o_ifx = rt->rt_ifp->if_index;
261135332Sglebius
262135332Sglebius		if (rt->rt_flags & RTF_GATEWAY &&
263135332Sglebius		    rt->rt_gateway->sa_family == AF_INET)
264135332Sglebius			fle->f.next_hop =
265135332Sglebius			    ((struct sockaddr_in *)(rt->rt_gateway))->sin_addr;
266135332Sglebius
267135332Sglebius		if (rt_mask(rt))
268135332Sglebius			fle->f.dst_mask =
269135332Sglebius			    bit_count(((struct sockaddr_in *)rt_mask(rt))->sin_addr.s_addr);
270135332Sglebius		else if (rt->rt_flags & RTF_HOST)
271135332Sglebius			/* Give up. We can't determine mask :( */
272135332Sglebius			fle->f.dst_mask = 32;
273135332Sglebius
274135332Sglebius		RTFREE(ro.ro_rt);
275135332Sglebius	}
276135332Sglebius
277135332Sglebius	/* Do route lookup on source address, to fill in src_mask. */
278135332Sglebius
279135332Sglebius	bzero((caddr_t)&ro, sizeof(ro));
280135332Sglebius	sin = (struct sockaddr_in *)&ro.ro_dst;
281135332Sglebius	sin->sin_len = sizeof(*sin);
282135332Sglebius	sin->sin_family = AF_INET;
283135332Sglebius	sin->sin_addr = fle->f.r.r_src;
284135332Sglebius	rtalloc_ign(&ro, RTF_CLONING);
285135332Sglebius	if (ro.ro_rt != NULL) {
286135332Sglebius		struct rtentry *rt = ro.ro_rt;
287135332Sglebius
288135332Sglebius		if (rt_mask(rt))
289135332Sglebius			fle->f.src_mask =
290135332Sglebius			    bit_count(((struct sockaddr_in *)rt_mask(rt))->sin_addr.s_addr);
291135332Sglebius		else if (rt->rt_flags & RTF_HOST)
292135332Sglebius			/* Give up. We can't determine mask :( */
293135332Sglebius			fle->f.src_mask = 32;
294135332Sglebius
295135332Sglebius		RTFREE(ro.ro_rt);
296135332Sglebius	}
297135332Sglebius
298135332Sglebius	/* Push new flow entry into flow cache */
299135332Sglebius	mtx_lock(&priv->work_mtx);
300135332Sglebius	LIST_INSERT_HEAD(&(h[slot].head), fle, fle_hash);
301135332Sglebius	TAILQ_INSERT_TAIL(&priv->work_queue, fle, fle_work);
302135332Sglebius	mtx_unlock(&priv->work_mtx);
303135332Sglebius
304135332Sglebius	return (0);
305135332Sglebius}
306135332Sglebius
307135332Sglebius
308135332Sglebius/*
309135332Sglebius * Non-static functions called from ng_netflow.c
310135332Sglebius */
311135332Sglebius
312135332Sglebius/* Allocate memory and set up flow cache */
313135332Sglebiusint
314135332Sglebiusng_netflow_cache_init(priv_p priv)
315135332Sglebius{
316135332Sglebius	struct flow_entry *fle;
317135332Sglebius	int i;
318135332Sglebius
319135332Sglebius	/* allocate cache */
320135332Sglebius	MALLOC(priv->cache, struct flow_entry *,
321135332Sglebius	    CACHESIZE * sizeof(struct flow_entry),
322135332Sglebius	    M_NETFLOW, M_WAITOK | M_ZERO);
323135332Sglebius
324135332Sglebius	if (priv->cache == NULL)
325135332Sglebius		return (ENOMEM);
326135332Sglebius
327135332Sglebius	/* allocate hash */
328135332Sglebius	MALLOC(priv->hash, struct flow_hash_entry *,
329135332Sglebius	    NBUCKETS * sizeof(struct flow_hash_entry),
330135332Sglebius	    M_NETFLOW, M_WAITOK | M_ZERO);
331135332Sglebius
332139374Sglebius	if (priv->hash == NULL) {
333139374Sglebius		FREE(priv->cache, M_NETFLOW);
334135332Sglebius		return (ENOMEM);
335139374Sglebius	}
336135332Sglebius
337135332Sglebius	TAILQ_INIT(&priv->work_queue);
338135332Sglebius	SLIST_INIT(&priv->free_list);
339135332Sglebius	SLIST_INIT(&priv->expire_list);
340135332Sglebius
341135332Sglebius	mtx_init(&priv->work_mtx, "ng_netflow cache mutex", NULL, MTX_DEF);
342135332Sglebius	mtx_init(&priv->free_mtx, "ng_netflow free mutex", NULL, MTX_DEF);
343135332Sglebius	mtx_init(&priv->expire_mtx, "ng_netflow expire mutex", NULL, MTX_DEF);
344135332Sglebius
345135332Sglebius	/* build free list */
346135332Sglebius	for (i = 0, fle = priv->cache; i < CACHESIZE; i++, fle++)
347135332Sglebius		SLIST_INSERT_HEAD(&priv->free_list, fle, fle_free);
348135332Sglebius
349135332Sglebius	priv->info.nfinfo_free = CACHESIZE;
350135332Sglebius
351135332Sglebius	return (0);
352135332Sglebius}
353135332Sglebius
354135332Sglebius/* Free all flow cache memory. Called from node close method. */
355135332Sglebiusvoid
356135332Sglebiusng_netflow_cache_flush(priv_p priv)
357135332Sglebius{
358135332Sglebius	register struct flow_entry	*fle;
359135332Sglebius	int i;
360135332Sglebius
361135332Sglebius	/*
362135332Sglebius	 * We are going to free probably billable data.
363135332Sglebius	 * Expire everything before freeing it.
364135332Sglebius	 * No locking is required since callout is already drained.
365135332Sglebius	 */
366135332Sglebius
367135332Sglebius	for (i = 0, fle = priv->cache; i < CACHESIZE; i++, fle++)
368135332Sglebius		if (!ISFREE(fle))
369135332Sglebius			/* ignore errors now */
370135332Sglebius			(void )export_add(priv, fle);
371135332Sglebius
372135332Sglebius	mtx_destroy(&priv->work_mtx);
373135332Sglebius	mtx_destroy(&priv->free_mtx);
374135332Sglebius	mtx_destroy(&priv->expire_mtx);
375135332Sglebius
376135332Sglebius	/* free hash memory */
377135332Sglebius	if (priv->hash)
378135332Sglebius		FREE(priv->hash, M_NETFLOW);
379135332Sglebius
380135332Sglebius	/* free flow cache */
381135332Sglebius	if (priv->cache)
382135332Sglebius		FREE(priv->cache, M_NETFLOW);
383135332Sglebius
384135332Sglebius}
385135332Sglebius
386135332Sglebius/* Insert packet from &m into flow cache. */
387135332Sglebiusint
388143923Sglebiusng_netflow_flow_add(priv_p priv, struct ip *ip, iface_p iface,
389143923Sglebius	struct ifnet *ifp)
390135332Sglebius{
391135332Sglebius	struct flow_hash_entry		*h = priv->hash;
392135332Sglebius	register struct flow_entry	*fle;
393135332Sglebius	struct flow_rec		r;
394143923Sglebius	int			hlen, plen;
395135332Sglebius	uint32_t		slot;
396135332Sglebius	uint8_t			tcp_flags = 0;
397135332Sglebius
398143923Sglebius	/* Try to fill flow_rec r */
399135332Sglebius	bzero(&r, sizeof(r));
400143923Sglebius	/* check version */
401143923Sglebius	if (ip->ip_v != IPVERSION)
402143923Sglebius		return (EINVAL);
403135332Sglebius
404143923Sglebius	/* verify min header length */
405143923Sglebius	hlen = ip->ip_hl << 2;
406143923Sglebius
407143923Sglebius	if (hlen < sizeof(struct ip))
408143923Sglebius		return (EINVAL);
409143923Sglebius
410143923Sglebius	r.r_src = ip->ip_src;
411143923Sglebius	r.r_dst = ip->ip_dst;
412143923Sglebius
413143923Sglebius	/* save packet length */
414143923Sglebius	plen = ntohs(ip->ip_len);
415143923Sglebius
416143923Sglebius	r.r_ip_p = ip->ip_p;
417143923Sglebius	r.r_tos = ip->ip_tos;
418143923Sglebius
419143923Sglebius	/* Configured in_ifx overrides mbuf's */
420143923Sglebius	if (iface->info.ifinfo_index == 0) {
421143923Sglebius		if (ifp != NULL)
422143923Sglebius			r.r_i_ifx = ifp->if_index;
423143923Sglebius	} else
424143923Sglebius		r.r_i_ifx = iface->info.ifinfo_index;
425143923Sglebius
426143923Sglebius	/*
427143923Sglebius	 * XXX NOTE: only first fragment of fragmented TCP, UDP and
428143923Sglebius	 * ICMP packet will be recorded with proper s_port and d_port.
429143923Sglebius	 * Following fragments will be recorded simply as IP packet with
430143923Sglebius	 * ip_proto = ip->ip_p and s_port, d_port set to zero.
431143923Sglebius	 * I know, it looks like bug. But I don't want to re-implement
432143923Sglebius	 * ip packet assebmling here. Anyway, (in)famous trafd works this way -
433143923Sglebius	 * and nobody complains yet :)
434143923Sglebius	 */
435143923Sglebius	if(ip->ip_off & htons(IP_OFFMASK))
436143923Sglebius		goto flow_rec_done;
437143923Sglebius
438143923Sglebius	switch(r.r_ip_p) {
439143923Sglebius	case IPPROTO_TCP:
440143923Sglebius	{
441143923Sglebius		register struct tcphdr *tcp;
442143923Sglebius
443143923Sglebius		tcp = (struct tcphdr *)((caddr_t )ip + hlen);
444143923Sglebius		r.r_sport = tcp->th_sport;
445143923Sglebius		r.r_dport = tcp->th_dport;
446143923Sglebius		tcp_flags = tcp->th_flags;
447143923Sglebius		break;
448143923Sglebius	}
449143923Sglebius	case IPPROTO_UDP:
450143923Sglebius		r.r_ports = *(uint32_t *)((caddr_t )ip + hlen);
451143923Sglebius		break;
452143923Sglebius	}
453143923Sglebius
454143923Sglebiusflow_rec_done:
455143923Sglebius
456135332Sglebius	slot = ip_hash(&r);
457135332Sglebius
458135332Sglebius	mtx_lock(&priv->work_mtx);
459139374Sglebius
460139374Sglebius	/* Update node statistics. */
461139374Sglebius	priv->info.nfinfo_packets ++;
462139374Sglebius	priv->info.nfinfo_bytes += plen;
463139374Sglebius
464135332Sglebius	fle = hash_lookup(h, slot, &r); /* New flow entry or existent? */
465135332Sglebius
466135332Sglebius	if (fle) {	/* an existent entry */
467135332Sglebius
468135332Sglebius		TAILQ_REMOVE(&priv->work_queue, fle, fle_work);
469135332Sglebius
470135332Sglebius		fle->f.bytes += plen;
471135332Sglebius		fle->f.packets ++;
472135332Sglebius		fle->f.tcp_flags |= tcp_flags;
473135332Sglebius		fle->f.last = time_uptime;
474135332Sglebius
475135332Sglebius		/*
476135332Sglebius		 * We have the following reasons to expire flow in active way:
477135332Sglebius		 * - it hit active timeout
478135332Sglebius		 * - a TCP connection closed
479135332Sglebius		 * - it is going to overflow counter
480135332Sglebius		 */
481135332Sglebius		if (tcp_flags & TH_FIN || tcp_flags & TH_RST || AGED(fle) ||
482135332Sglebius		    (fle->f.bytes >= (UINT_MAX - IF_MAXMTU)) )
483135332Sglebius			expire_flow(priv, fle);
484135332Sglebius		else
485135332Sglebius			TAILQ_INSERT_TAIL(&priv->work_queue, fle, fle_work);
486135332Sglebius
487135332Sglebius		mtx_unlock(&priv->work_mtx);
488135332Sglebius
489135332Sglebius	} else {	/* a new flow entry */
490135332Sglebius
491135332Sglebius		mtx_unlock(&priv->work_mtx);
492143890Sglebius		return hash_insert(priv, slot, &r, plen, tcp_flags);
493135332Sglebius
494135332Sglebius	}
495135332Sglebius
496135332Sglebius	mtx_assert(&priv->work_mtx, MA_NOTOWNED);
497135332Sglebius	mtx_assert(&priv->expire_mtx, MA_NOTOWNED);
498135332Sglebius	mtx_assert(&priv->free_mtx, MA_NOTOWNED);
499139374Sglebius
500135332Sglebius	return (0);
501135332Sglebius}
502135332Sglebius
503135332Sglebius/*
504135332Sglebius * Return records from cache. netgraph(4) guarantees us that we
505135332Sglebius * are locked against ng_netflow_rcvdata(). However we can
506135332Sglebius * work with ng_netflow_expire() in parrallel. XXX: Is it dangerous?
507135332Sglebius *
508135332Sglebius * TODO: matching particular IP should be done in kernel, here.
509135332Sglebius */
510135332Sglebiusint
511135332Sglebiusng_netflow_flow_show(priv_p priv, uint32_t last, struct ng_mesg *resp)
512135332Sglebius{
513135332Sglebius	struct flow_entry *fle;
514135332Sglebius	struct ngnf_flows *data;
515135332Sglebius
516135332Sglebius	data = (struct ngnf_flows *)resp->data;
517135332Sglebius	data->last = 0;
518135332Sglebius	data->nentries = 0;
519135332Sglebius
520135332Sglebius	/* Check if this is a first run */
521135332Sglebius	if (last == 0)
522135332Sglebius		fle = priv->cache;
523135332Sglebius	else {
524135332Sglebius		if (last > CACHESIZE-1)
525135332Sglebius			return (EINVAL);
526135332Sglebius		fle = priv->cache + last;
527135332Sglebius	}
528135332Sglebius
529135332Sglebius	/*
530135332Sglebius	 * We will transfer not more than NREC_AT_ONCE. More data
531135332Sglebius	 * will come in next message.
532135332Sglebius	 * We send current stop point to userland, and userland should return
533135332Sglebius	 * it back to us.
534135332Sglebius	 */
535135332Sglebius	for (; last < CACHESIZE; fle++, last++) {
536135332Sglebius		if (ISFREE(fle))
537135332Sglebius			continue;
538141343Sglebius		bcopy(&fle->f, &(data->entries[data->nentries]),
539141343Sglebius		    sizeof(fle->f));
540135332Sglebius		data->nentries ++;
541135332Sglebius		if (data->nentries == NREC_AT_ONCE) {
542135332Sglebius			if (++last < CACHESIZE)
543135332Sglebius				data->last = (++fle - priv->cache);
544135332Sglebius			return (0);
545135332Sglebius		}
546135332Sglebius     	}
547135332Sglebius
548135332Sglebius	return (0);
549135332Sglebius}
550135332Sglebius
551135332Sglebius/* We have full datagram in privdata. Send it to export hook. */
552135332Sglebiusstatic int
553135332Sglebiusexport_send(priv_p priv)
554135332Sglebius{
555135332Sglebius	struct netflow_v5_header *header = &priv->dgram.header;
556135332Sglebius	struct timespec ts;
557135332Sglebius	struct mbuf *m;
558135332Sglebius	int error = 0;
559135332Sglebius	int mlen;
560135332Sglebius
561143103Sglebius	header->sys_uptime = htonl(MILLIUPTIME(time_uptime));
562135332Sglebius
563135332Sglebius	getnanotime(&ts);
564135332Sglebius	header->unix_secs  = htonl(ts.tv_sec);
565135332Sglebius	header->unix_nsecs = htonl(ts.tv_nsec);
566135332Sglebius
567135332Sglebius	/* Flow sequence contains number of first record */
568135332Sglebius	header->flow_seq = htonl(priv->flow_seq - header->count);
569135332Sglebius
570135332Sglebius	mlen = sizeof(struct netflow_v5_header) +
571135332Sglebius	    sizeof(struct netflow_v5_record) * header->count;
572135332Sglebius
573135332Sglebius	header->count = htons(header->count);
574135332Sglebius	if ((m = m_devget((caddr_t)header, mlen, 0, NULL, NULL)) == NULL) {
575140511Sglebius		log(LOG_CRIT, "ng_netflow: m_devget() failed, losing export "
576140511Sglebius		    "dgram\n");
577135332Sglebius		header->count = 0;
578135332Sglebius		return(ENOBUFS);
579135332Sglebius	}
580135332Sglebius
581135332Sglebius	header->count = 0;
582135332Sglebius
583135332Sglebius	/* Giant is required in sosend() at this moment. */
584135332Sglebius	NET_LOCK_GIANT();
585135332Sglebius	NG_SEND_DATA_ONLY(error, priv->export, m);
586135332Sglebius	NET_UNLOCK_GIANT();
587135332Sglebius
588135332Sglebius	if (error)
589135332Sglebius		NG_FREE_M(m);
590135332Sglebius
591135332Sglebius	return (error);
592135332Sglebius}
593135332Sglebius
594135332Sglebius
595135332Sglebius/* Create export datagram. */
596135332Sglebiusstatic int
597135332Sglebiusexport_add(priv_p priv, struct flow_entry *fle)
598135332Sglebius{
599135332Sglebius	struct netflow_v5_header *header = &priv->dgram.header;
600135332Sglebius	struct netflow_v5_record *rec;
601135332Sglebius
602135332Sglebius	if (header->count == 0 ) {	/* first record */
603135332Sglebius		rec = &priv->dgram.r[0];
604135332Sglebius		header->count = 1;
605135332Sglebius	} else {			/* continue filling datagram */
606135332Sglebius		rec = &priv->dgram.r[header->count];
607135332Sglebius		header->count ++;
608135332Sglebius	}
609135332Sglebius
610135332Sglebius	/* Fill in export record */
611135332Sglebius	rec->src_addr = fle->f.r.r_src.s_addr;
612135332Sglebius	rec->dst_addr = fle->f.r.r_dst.s_addr;
613135332Sglebius	rec->next_hop = fle->f.next_hop.s_addr;
614135332Sglebius	rec->i_ifx    = htons(fle->f.fle_i_ifx);
615135332Sglebius	rec->o_ifx    = htons(fle->f.fle_o_ifx);
616135332Sglebius	rec->packets  = htonl(fle->f.packets);
617135332Sglebius	rec->octets   = htonl(fle->f.bytes);
618143103Sglebius	rec->first    = htonl(MILLIUPTIME(fle->f.first));
619143103Sglebius	rec->last     = htonl(MILLIUPTIME(fle->f.last));
620135332Sglebius	rec->s_port   = fle->f.r.r_sport;
621135332Sglebius	rec->d_port   = fle->f.r.r_dport;
622135332Sglebius	rec->flags    = fle->f.tcp_flags;
623135332Sglebius	rec->prot     = fle->f.r.r_ip_p;
624135332Sglebius	rec->tos      = fle->f.r.r_tos;
625135332Sglebius	rec->dst_mask = fle->f.dst_mask;
626135332Sglebius	rec->src_mask = fle->f.src_mask;
627135332Sglebius
628135332Sglebius	priv->flow_seq++;
629135332Sglebius
630135332Sglebius	if (header->count == NETFLOW_V5_MAX_RECORDS) /* end of datagram */
631135332Sglebius		return export_send(priv);
632135332Sglebius
633135332Sglebius	return (0);
634135332Sglebius}
635135332Sglebius
636135332Sglebius/* Periodic flow expiry run. */
637135332Sglebiusvoid
638135332Sglebiusng_netflow_expire(void *arg)
639135332Sglebius{
640135332Sglebius	register struct flow_entry	*fle, *fle1;
641135332Sglebius	priv_p priv = (priv_p )arg;
642135332Sglebius	uint32_t used;
643135332Sglebius	int error = 0;
644135332Sglebius
645135332Sglebius	/* First pack actively expired entries */
646135332Sglebius	mtx_lock(&priv->expire_mtx);
647135332Sglebius	while (!SLIST_EMPTY(&(priv->expire_list))) {
648135332Sglebius		fle = SLIST_FIRST(&(priv->expire_list));
649135332Sglebius		SLIST_REMOVE_HEAD(&(priv->expire_list), fle_free);
650135332Sglebius		mtx_unlock(&priv->expire_mtx);
651135332Sglebius
652135332Sglebius		/*
653135332Sglebius		 * While we have dropped the lock, expire_flow() may
654135332Sglebius		 * insert another flow into top of the list.
655135332Sglebius		 * This is not harmful for us, since we have already
656135332Sglebius		 * detached our own.
657135332Sglebius		 */
658135332Sglebius
659135332Sglebius		if ((error = export_add(priv, fle)) != 0)
660140511Sglebius			log(LOG_CRIT, "ng_netflow: export_add() failed: %u\n",
661140511Sglebius			    error);
662135332Sglebius		(void )free_flow(priv, fle);
663135332Sglebius
664135332Sglebius		mtx_lock(&priv->expire_mtx);
665135332Sglebius	}
666135332Sglebius	mtx_unlock(&priv->expire_mtx);
667135332Sglebius
668135332Sglebius	NGNF_GETUSED(priv, used);
669135332Sglebius	mtx_lock(&priv->work_mtx);
670135332Sglebius	TAILQ_FOREACH_SAFE(fle, &(priv->work_queue), fle_work, fle1) {
671135332Sglebius		/*
672141343Sglebius		 * When cache size has not reached CACHELOWAT yet, we keep
673141343Sglebius		 * both inactive and active flows in cache. Doing this, we
674141343Sglebius		 * reduce number of exports, since many inactive flows may
675141343Sglebius		 * wake up and continue their life. However, we make an
676141343Sglebius		 * exclusion for scans. It is very rare situation that
677141343Sglebius		 * inactive 1-packet flow will wake up.
678141343Sglebius		 * When cache has reached CACHELOWAT, we expire all inactive
679141343Sglebius		 * flows, until cache gets to a sane size.
680135332Sglebius		 */
681135332Sglebius		if (used <= CACHELOWAT && !INACTIVE(fle))
682135332Sglebius			goto finish;
683135332Sglebius
684141348Sglebius		if ((INACTIVE(fle) && (SMALL(fle) || (used > CACHELOWAT))) ||
685141348Sglebius		    AGED(fle)) {
686135332Sglebius
687135332Sglebius			/* Detach flow entry from cache */
688135332Sglebius			LIST_REMOVE(fle, fle_hash);
689135332Sglebius			TAILQ_REMOVE(&priv->work_queue, fle, fle_work);
690135332Sglebius
691135332Sglebius			/*
692135332Sglebius			 * While we are sending to collector, unlock cache.
693135332Sglebius			 * XXX: it can happen, however with a small probability,
694141343Sglebius			 * that item, we are holding now, can be moved to the
695141343Sglebius			 * top of flow cache by node thread. In this case our
696141343Sglebius			 * expire thread stops checking. Since this is not
697141343Sglebius			 * fatal we will just ignore it now.
698135332Sglebius			 */
699135332Sglebius			mtx_unlock(&priv->work_mtx);
700135332Sglebius
701135332Sglebius			if ((error = export_add(priv, fle)) != 0)
702140511Sglebius				log(LOG_CRIT, "ng_netflow: export_add() "
703140511Sglebius				    "failed: %u\n", error);
704135332Sglebius
705135332Sglebius			used = free_flow(priv, fle);
706135332Sglebius
707135332Sglebius			mtx_lock(&priv->work_mtx);
708135332Sglebius		}
709135332Sglebius     	}
710135332Sglebius
711135332Sglebiusfinish:
712135332Sglebius	mtx_unlock(&priv->work_mtx);
713135332Sglebius
714135332Sglebius	mtx_assert(&priv->expire_mtx, MA_NOTOWNED);
715135332Sglebius	mtx_assert(&priv->free_mtx, MA_NOTOWNED);
716135332Sglebius
717135332Sglebius	/* schedule next expire */
718135332Sglebius	callout_reset(&priv->exp_callout, (1*hz), &ng_netflow_expire,
719135332Sglebius	    (void *)priv);
720135332Sglebius
721135332Sglebius}
722