xref: /openbsd-src/sys/net/if_pflow.c (revision 938ff1ae9de381d8345ce36468f0e90c37e6e5a3)
1*938ff1aeSbluhm /*	$OpenBSD: if_pflow.c,v 1.109 2023/12/23 10:52:54 bluhm Exp $	*/
218ae10f7Shenning 
318ae10f7Shenning /*
4fa1d79b8Sbenno  * Copyright (c) 2011 Florian Obser <florian@narrans.de>
5fa1d79b8Sbenno  * Copyright (c) 2011 Sebastian Benoit <benoit-lists@fb12.de>
618ae10f7Shenning  * Copyright (c) 2008 Henning Brauer <henning@openbsd.org>
718ae10f7Shenning  * Copyright (c) 2008 Joerg Goltermann <jg@osn.de>
818ae10f7Shenning  *
918ae10f7Shenning  * Permission to use, copy, modify, and distribute this software for any
1018ae10f7Shenning  * purpose with or without fee is hereby granted, provided that the above
1118ae10f7Shenning  * copyright notice and this permission notice appear in all copies.
1218ae10f7Shenning  *
1318ae10f7Shenning  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1418ae10f7Shenning  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1518ae10f7Shenning  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1618ae10f7Shenning  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1718ae10f7Shenning  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
1818ae10f7Shenning  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
1918ae10f7Shenning  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2018ae10f7Shenning  */
2118ae10f7Shenning 
2218ae10f7Shenning #include <sys/param.h>
23b27348b2Sderaadt #include <sys/malloc.h>
249b074ffaStedu #include <sys/systm.h>
2518ae10f7Shenning #include <sys/mbuf.h>
2618ae10f7Shenning #include <sys/socket.h>
27638c25e3Stedu #include <sys/timeout.h>
2818ae10f7Shenning #include <sys/ioctl.h>
2918ae10f7Shenning #include <sys/kernel.h>
30416aa928Sflorian #include <sys/socketvar.h>
3118ae10f7Shenning #include <sys/sysctl.h>
32339bbf62Smvs #include <sys/mutex.h>
3318ae10f7Shenning 
3418ae10f7Shenning #include <net/if.h>
3518ae10f7Shenning #include <net/if_types.h>
3618ae10f7Shenning #include <net/bpf.h>
3718ae10f7Shenning #include <net/route.h>
3818ae10f7Shenning #include <netinet/in.h>
3918ae10f7Shenning #include <netinet/if_ether.h>
4018ae10f7Shenning #include <netinet/tcp.h>
4118ae10f7Shenning 
4218ae10f7Shenning #include <netinet/ip.h>
4333e54543Sdlg #include <netinet/ip_icmp.h>
4418ae10f7Shenning #include <netinet/ip_var.h>
4518ae10f7Shenning #include <netinet/udp.h>
4618ae10f7Shenning #include <netinet/udp_var.h>
4718ae10f7Shenning #include <netinet/in_pcb.h>
4818ae10f7Shenning 
4918ae10f7Shenning #include <net/pfvar.h>
5033e54543Sdlg #include <net/pfvar_priv.h>
5118ae10f7Shenning #include <net/if_pflow.h>
5218ae10f7Shenning 
5318ae10f7Shenning #include "bpfilter.h"
5418ae10f7Shenning #include "pflow.h"
5518ae10f7Shenning 
5618ae10f7Shenning #define PFLOW_MINMTU	\
5718ae10f7Shenning     (sizeof(struct pflow_header) + sizeof(struct pflow_flow))
5818ae10f7Shenning 
5918ae10f7Shenning #ifdef PFLOWDEBUG
6018ae10f7Shenning #define DPRINTF(x)	do { printf x ; } while (0)
6118ae10f7Shenning #else
6218ae10f7Shenning #define DPRINTF(x)
6318ae10f7Shenning #endif
6418ae10f7Shenning 
6563a0cdecSmvs SMR_SLIST_HEAD(, pflow_softc) pflowif_list;
66ccf5da69Smvs 
67ccf5da69Smvs enum pflowstat_counters {
68ccf5da69Smvs 	pflow_flows,
69ccf5da69Smvs 	pflow_packets,
70ccf5da69Smvs 	pflow_onomem,
71ccf5da69Smvs 	pflow_oerrors,
72ccf5da69Smvs 	pflow_ncounters,
73ccf5da69Smvs };
74ccf5da69Smvs 
75ccf5da69Smvs struct cpumem *pflow_counters;
76ccf5da69Smvs 
77ccf5da69Smvs static inline void
pflowstat_inc(enum pflowstat_counters c)78ccf5da69Smvs pflowstat_inc(enum pflowstat_counters c)
79ccf5da69Smvs {
80ccf5da69Smvs 	counters_inc(pflow_counters, c);
81ccf5da69Smvs }
8218ae10f7Shenning 
8318ae10f7Shenning void	pflowattach(int);
8460368739Skrw int	pflow_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
8560368739Skrw 	struct rtentry *rt);
868818ef4eSbenno void	pflow_output_process(void *);
8718ae10f7Shenning int	pflow_clone_create(struct if_clone *, int);
8818ae10f7Shenning int	pflow_clone_destroy(struct ifnet *);
894da5ea9aSmpi int	pflow_set(struct pflow_softc *, struct pflowreq *);
90fa1d79b8Sbenno int	pflow_calc_mtu(struct pflow_softc *, int, int);
9118ae10f7Shenning void	pflow_setmtu(struct pflow_softc *, int);
923baed77aSflorian int	pflowvalidsockaddr(const struct sockaddr *, int);
9318ae10f7Shenning int	pflowioctl(struct ifnet *, u_long, caddr_t);
9418ae10f7Shenning 
95fa1d79b8Sbenno struct mbuf	*pflow_get_mbuf(struct pflow_softc *, u_int16_t);
96fa1d79b8Sbenno void	pflow_flush(struct pflow_softc *);
97fa1d79b8Sbenno int	pflow_sendout_v5(struct pflow_softc *);
98fa1d79b8Sbenno int	pflow_sendout_ipfix(struct pflow_softc *, sa_family_t);
99fa1d79b8Sbenno int	pflow_sendout_ipfix_tmpl(struct pflow_softc *);
10018ae10f7Shenning int	pflow_sendout_mbuf(struct pflow_softc *, struct mbuf *);
10118ae10f7Shenning void	pflow_timeout(void *);
102fa1d79b8Sbenno void	pflow_timeout6(void *);
103fa1d79b8Sbenno void	pflow_timeout_tmpl(void *);
10418ae10f7Shenning void	copy_flow_data(struct pflow_flow *, struct pflow_flow *,
105c07294cfSflorian 	struct pf_state *, struct pf_state_key *, int, int);
10658354163Sflorian void	copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 *,
10758354163Sflorian 	struct pflow_ipfix_flow4 *, struct pf_state *, struct pf_state_key *,
10858354163Sflorian 	struct pflow_softc *, int, int);
10958354163Sflorian void	copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *,
11058354163Sflorian 	struct pflow_ipfix_flow6 *, struct pf_state *, struct pf_state_key *,
11158354163Sflorian 	struct pflow_softc *, int, int);
112c07294cfSflorian int	pflow_pack_flow(struct pf_state *, struct pf_state_key *,
113c07294cfSflorian 	struct pflow_softc *);
114c07294cfSflorian int	pflow_pack_flow_ipfix(struct pf_state *, struct pf_state_key *,
115c07294cfSflorian 	struct pflow_softc *);
116c07294cfSflorian int	export_pflow_if(struct pf_state*, struct pf_state_key *,
117c07294cfSflorian 	struct pflow_softc *);
11888e5d322Sgollo int	copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc);
11958354163Sflorian int	copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow,
12058354163Sflorian 	struct pflow_softc *sc);
12158354163Sflorian int	copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow,
12258354163Sflorian 	struct pflow_softc *sc);
12318ae10f7Shenning 
12418ae10f7Shenning struct if_clone	pflow_cloner =
12518ae10f7Shenning     IF_CLONE_INITIALIZER("pflow", pflow_clone_create,
12618ae10f7Shenning     pflow_clone_destroy);
12718ae10f7Shenning 
12818ae10f7Shenning void
pflowattach(int npflow)12918ae10f7Shenning pflowattach(int npflow)
13018ae10f7Shenning {
13163a0cdecSmvs 	SMR_SLIST_INIT(&pflowif_list);
132ccf5da69Smvs 	pflow_counters = counters_alloc(pflow_ncounters);
13318ae10f7Shenning 	if_clone_attach(&pflow_cloner);
13418ae10f7Shenning }
13518ae10f7Shenning 
13618ae10f7Shenning int
pflow_output(struct ifnet * ifp,struct mbuf * m,struct sockaddr * dst,struct rtentry * rt)13760368739Skrw pflow_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
13860368739Skrw 	struct rtentry *rt)
13960368739Skrw {
14060368739Skrw 	m_freem(m);	/* drop packet */
14160368739Skrw 	return (EAFNOSUPPORT);
14260368739Skrw }
14360368739Skrw 
1448818ef4eSbenno void
pflow_output_process(void * arg)1458818ef4eSbenno pflow_output_process(void *arg)
1468818ef4eSbenno {
147b52009cbSvisa 	struct mbuf_list ml;
1488818ef4eSbenno 	struct pflow_softc *sc = arg;
1498818ef4eSbenno 	struct mbuf *m;
1508818ef4eSbenno 
151b52009cbSvisa 	mq_delist(&sc->sc_outputqueue, &ml);
152f0d16c93Smvs 	rw_enter_read(&sc->sc_lock);
153b52009cbSvisa 	while ((m = ml_dequeue(&ml)) != NULL) {
1548818ef4eSbenno 		pflow_sendout_mbuf(sc, m);
1558818ef4eSbenno 	}
156f0d16c93Smvs 	rw_exit_read(&sc->sc_lock);
1578818ef4eSbenno }
1588818ef4eSbenno 
15960368739Skrw int
pflow_clone_create(struct if_clone * ifc,int unit)16018ae10f7Shenning pflow_clone_create(struct if_clone *ifc, int unit)
16118ae10f7Shenning {
16218ae10f7Shenning 	struct ifnet		*ifp;
163d9bf6ab7Sgollo 	struct pflow_softc	*pflowif;
16418ae10f7Shenning 
165809d3a3eSbluhm 	pflowif = malloc(sizeof(*pflowif), M_DEVBUF, M_WAITOK|M_ZERO);
166f0d16c93Smvs 	rw_init(&pflowif->sc_lock, "pflowlk");
167339bbf62Smvs 	mtx_init(&pflowif->sc_mtx, IPL_MPFLOOR);
168416aa928Sflorian 	MGET(pflowif->send_nam, M_WAIT, MT_SONAME);
169fa1d79b8Sbenno 	pflowif->sc_version = PFLOW_PROTO_DEFAULT;
170fa1d79b8Sbenno 
17158354163Sflorian 	/* ipfix template init */
17258354163Sflorian 	bzero(&pflowif->sc_tmpl_ipfix,sizeof(pflowif->sc_tmpl_ipfix));
17358354163Sflorian 	pflowif->sc_tmpl_ipfix.set_header.set_id =
17458354163Sflorian 	    htons(PFLOW_IPFIX_TMPL_SET_ID);
17558354163Sflorian 	pflowif->sc_tmpl_ipfix.set_header.set_length =
17658354163Sflorian 	    htons(sizeof(struct pflow_ipfix_tmpl));
17758354163Sflorian 
17858354163Sflorian 	/* ipfix IPv4 template */
17958354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.h.tmpl_id =
18058354163Sflorian 	    htons(PFLOW_IPFIX_TMPL_IPV4_ID);
18158354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.h.field_count
18258354163Sflorian 	    = htons(PFLOW_IPFIX_TMPL_IPV4_FIELD_COUNT);
18358354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_ip.field_id =
18458354163Sflorian 	    htons(PFIX_IE_sourceIPv4Address);
18558354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_ip.len = htons(4);
18658354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_ip.field_id =
18758354163Sflorian 	    htons(PFIX_IE_destinationIPv4Address);
18858354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_ip.len = htons(4);
18958354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_in.field_id =
19058354163Sflorian 	    htons(PFIX_IE_ingressInterface);
19158354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_in.len = htons(4);
19258354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_out.field_id =
19358354163Sflorian 	    htons(PFIX_IE_egressInterface);
19458354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_out.len = htons(4);
19558354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.packets.field_id =
19658354163Sflorian 	    htons(PFIX_IE_packetDeltaCount);
19758354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.packets.len = htons(8);
19858354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.octets.field_id =
19958354163Sflorian 	    htons(PFIX_IE_octetDeltaCount);
20058354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.octets.len = htons(8);
20158354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.start.field_id =
20258354163Sflorian 	    htons(PFIX_IE_flowStartMilliseconds);
20358354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.start.len = htons(8);
20458354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.finish.field_id =
20558354163Sflorian 	    htons(PFIX_IE_flowEndMilliseconds);
20658354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.finish.len = htons(8);
20758354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_port.field_id =
20858354163Sflorian 	    htons(PFIX_IE_sourceTransportPort);
20958354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_port.len = htons(2);
21058354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_port.field_id =
21158354163Sflorian 	    htons(PFIX_IE_destinationTransportPort);
21258354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_port.len = htons(2);
21358354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.tos.field_id =
21458354163Sflorian 	    htons(PFIX_IE_ipClassOfService);
21558354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.tos.len = htons(1);
21658354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.protocol.field_id =
21758354163Sflorian 	    htons(PFIX_IE_protocolIdentifier);
21858354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.protocol.len = htons(1);
21958354163Sflorian 
22058354163Sflorian 	/* ipfix IPv6 template */
22158354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.h.tmpl_id =
22258354163Sflorian 	    htons(PFLOW_IPFIX_TMPL_IPV6_ID);
22358354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.h.field_count =
22458354163Sflorian 	    htons(PFLOW_IPFIX_TMPL_IPV6_FIELD_COUNT);
22558354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_ip.field_id =
22658354163Sflorian 	    htons(PFIX_IE_sourceIPv6Address);
22758354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_ip.len = htons(16);
22858354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_ip.field_id =
22958354163Sflorian 	    htons(PFIX_IE_destinationIPv6Address);
23058354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_ip.len = htons(16);
23158354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_in.field_id =
23258354163Sflorian 	    htons(PFIX_IE_ingressInterface);
23358354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_in.len = htons(4);
23458354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_out.field_id =
23558354163Sflorian 	    htons(PFIX_IE_egressInterface);
23658354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_out.len = htons(4);
23758354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.packets.field_id =
23858354163Sflorian 	    htons(PFIX_IE_packetDeltaCount);
23958354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.packets.len = htons(8);
24058354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.octets.field_id =
24158354163Sflorian 	    htons(PFIX_IE_octetDeltaCount);
24258354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.octets.len = htons(8);
24358354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.start.field_id =
24458354163Sflorian 	    htons(PFIX_IE_flowStartMilliseconds);
24558354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.start.len = htons(8);
24658354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.finish.field_id =
24758354163Sflorian 	    htons(PFIX_IE_flowEndMilliseconds);
24858354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.finish.len = htons(8);
24958354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_port.field_id =
25058354163Sflorian 	    htons(PFIX_IE_sourceTransportPort);
25158354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_port.len = htons(2);
25258354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_port.field_id =
25358354163Sflorian 	    htons(PFIX_IE_destinationTransportPort);
25458354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_port.len = htons(2);
25558354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.tos.field_id =
25658354163Sflorian 	    htons(PFIX_IE_ipClassOfService);
25758354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.tos.len = htons(1);
25858354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.protocol.field_id =
25958354163Sflorian 	    htons(PFIX_IE_protocolIdentifier);
26058354163Sflorian 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.protocol.len = htons(1);
261fa1d79b8Sbenno 
26218ae10f7Shenning 	ifp = &pflowif->sc_if;
26318ae10f7Shenning 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflow%d", unit);
26418ae10f7Shenning 	ifp->if_softc = pflowif;
26518ae10f7Shenning 	ifp->if_ioctl = pflowioctl;
26660368739Skrw 	ifp->if_output = pflow_output;
267416aa928Sflorian 	ifp->if_start = NULL;
26899bf4b5fSmpi 	ifp->if_xflags = IFXF_CLONED;
26918ae10f7Shenning 	ifp->if_type = IFT_PFLOW;
27018ae10f7Shenning 	ifp->if_hdrlen = PFLOW_HDRLEN;
27118ae10f7Shenning 	ifp->if_flags = IFF_UP;
27218ae10f7Shenning 	ifp->if_flags &= ~IFF_RUNNING;	/* not running, need receiver */
273b52009cbSvisa 	mq_init(&pflowif->sc_outputqueue, 8192, IPL_SOFTNET);
27418ae10f7Shenning 	pflow_setmtu(pflowif, ETHERMTU);
275415db4d1Smvs 
276415db4d1Smvs 	timeout_set_proc(&pflowif->sc_tmo, pflow_timeout, pflowif);
277415db4d1Smvs 	timeout_set_proc(&pflowif->sc_tmo6, pflow_timeout6, pflowif);
278415db4d1Smvs 	timeout_set_proc(&pflowif->sc_tmo_tmpl, pflow_timeout_tmpl, pflowif);
279415db4d1Smvs 
280f69555e6Smvs 	task_set(&pflowif->sc_outputtask, pflow_output_process, pflowif);
281f69555e6Smvs 
282*938ff1aeSbluhm 	if_counters_alloc(ifp);
28318ae10f7Shenning 	if_attach(ifp);
28418ae10f7Shenning 	if_alloc_sadl(ifp);
28518ae10f7Shenning 
286d9bf6ab7Sgollo 	/* Insert into list of pflows */
28763a0cdecSmvs 	KERNEL_ASSERT_LOCKED();
28863a0cdecSmvs 	SMR_SLIST_INSERT_HEAD_LOCKED(&pflowif_list, pflowif, sc_next);
28918ae10f7Shenning 	return (0);
29018ae10f7Shenning }
29118ae10f7Shenning 
29218ae10f7Shenning int
pflow_clone_destroy(struct ifnet * ifp)29318ae10f7Shenning pflow_clone_destroy(struct ifnet *ifp)
29418ae10f7Shenning {
29518ae10f7Shenning 	struct pflow_softc	*sc = ifp->if_softc;
296aa28b9a6Smpi 	int			 error;
297416aa928Sflorian 
298416aa928Sflorian 	error = 0;
29918ae10f7Shenning 
3000b9ea278Smvs 	rw_enter_write(&sc->sc_lock);
301f0d16c93Smvs 	sc->sc_dying = 1;
3020b9ea278Smvs 	rw_exit_write(&sc->sc_lock);
303b170e74aSmvs 
30463a0cdecSmvs 	KERNEL_ASSERT_LOCKED();
30563a0cdecSmvs 	SMR_SLIST_REMOVE_LOCKED(&pflowif_list, sc, pflow_softc, sc_next);
30663a0cdecSmvs 	smr_barrier();
30763a0cdecSmvs 
308ea893a00Sflorian 	timeout_del(&sc->sc_tmo);
309ea893a00Sflorian 	timeout_del(&sc->sc_tmo6);
310ea893a00Sflorian 	timeout_del(&sc->sc_tmo_tmpl);
311415db4d1Smvs 
312fa1d79b8Sbenno 	pflow_flush(sc);
31393865884Ssashan 	task_del(net_tq(ifp->if_index), &sc->sc_outputtask);
314b170e74aSmvs 	taskq_barrier(net_tq(ifp->if_index));
315b52009cbSvisa 	mq_purge(&sc->sc_outputqueue);
316928053bcSflorian 	m_freem(sc->send_nam);
317416aa928Sflorian 	if (sc->so != NULL) {
3184a4dc3eaSmpi 		error = soclose(sc->so, MSG_DONTWAIT);
319416aa928Sflorian 		sc->so = NULL;
320416aa928Sflorian 	}
3213baed77aSflorian 	if (sc->sc_flowdst != NULL)
3223baed77aSflorian 		free(sc->sc_flowdst, M_DEVBUF, sc->sc_flowdst->sa_len);
3233baed77aSflorian 	if (sc->sc_flowsrc != NULL)
3243baed77aSflorian 		free(sc->sc_flowsrc, M_DEVBUF, sc->sc_flowsrc->sa_len);
32518ae10f7Shenning 	if_detach(ifp);
326fa157596Sflorian 	free(sc, M_DEVBUF, sizeof(*sc));
327416aa928Sflorian 	return (error);
32818ae10f7Shenning }
32918ae10f7Shenning 
33018ae10f7Shenning int
pflowvalidsockaddr(const struct sockaddr * sa,int ignore_port)3313baed77aSflorian pflowvalidsockaddr(const struct sockaddr *sa, int ignore_port)
3323baed77aSflorian {
3333baed77aSflorian 	struct sockaddr_in6	*sin6;
3343baed77aSflorian 	struct sockaddr_in	*sin;
3353baed77aSflorian 
3363baed77aSflorian 	if (sa == NULL)
3373baed77aSflorian 		return (0);
3383baed77aSflorian 	switch(sa->sa_family) {
3393baed77aSflorian 	case AF_INET:
3403baed77aSflorian 		sin = (struct sockaddr_in*) sa;
3413baed77aSflorian 		return (sin->sin_addr.s_addr != INADDR_ANY &&
3423baed77aSflorian 		    (ignore_port || sin->sin_port != 0));
3433baed77aSflorian 	case AF_INET6:
3443baed77aSflorian 		sin6 = (struct sockaddr_in6*) sa;
3453baed77aSflorian 		return (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
3463baed77aSflorian 		    (ignore_port || sin6->sin6_port != 0));
3473baed77aSflorian 	default:
3483baed77aSflorian 		return (0);
3493baed77aSflorian 	}
3503baed77aSflorian }
3514da5ea9aSmpi 
3524da5ea9aSmpi int
pflow_set(struct pflow_softc * sc,struct pflowreq * pflowr)3534da5ea9aSmpi pflow_set(struct pflow_softc *sc, struct pflowreq *pflowr)
3544da5ea9aSmpi {
3554da5ea9aSmpi 	struct proc		*p = curproc;
3564da5ea9aSmpi 	struct socket		*so;
3574da5ea9aSmpi 	struct sockaddr		*sa;
3584da5ea9aSmpi 	int			 error = 0;
3594da5ea9aSmpi 
3604da5ea9aSmpi 	if (pflowr->addrmask & PFLOW_MASK_VERSION) {
3614da5ea9aSmpi 		switch(pflowr->version) {
3624da5ea9aSmpi 		case PFLOW_PROTO_5:
3634da5ea9aSmpi 		case PFLOW_PROTO_10:
3644da5ea9aSmpi 			break;
3654da5ea9aSmpi 		default:
3664da5ea9aSmpi 			return(EINVAL);
3674da5ea9aSmpi 		}
3684da5ea9aSmpi 	}
3694da5ea9aSmpi 
370339bbf62Smvs 	rw_assert_wrlock(&sc->sc_lock);
371339bbf62Smvs 
3724da5ea9aSmpi 	pflow_flush(sc);
3734da5ea9aSmpi 
3744da5ea9aSmpi 	if (pflowr->addrmask & PFLOW_MASK_DSTIP) {
3754da5ea9aSmpi 		if (sc->sc_flowdst != NULL &&
3764da5ea9aSmpi 		    sc->sc_flowdst->sa_family != pflowr->flowdst.ss_family) {
3774da5ea9aSmpi 			free(sc->sc_flowdst, M_DEVBUF, sc->sc_flowdst->sa_len);
3784da5ea9aSmpi 			sc->sc_flowdst = NULL;
3794da5ea9aSmpi 			if (sc->so != NULL) {
3804a4dc3eaSmpi 				soclose(sc->so, MSG_DONTWAIT);
3814da5ea9aSmpi 				sc->so = NULL;
3824da5ea9aSmpi 			}
3834da5ea9aSmpi 		}
3844da5ea9aSmpi 
3854da5ea9aSmpi 		switch (pflowr->flowdst.ss_family) {
3864da5ea9aSmpi 		case AF_INET:
387085e702cSflorian 			if (sc->sc_flowdst == NULL) {
3884da5ea9aSmpi 				if ((sc->sc_flowdst = malloc(
3894da5ea9aSmpi 				    sizeof(struct sockaddr_in),
3904da5ea9aSmpi 				    M_DEVBUF,  M_NOWAIT)) == NULL)
3914da5ea9aSmpi 					return (ENOMEM);
392085e702cSflorian 			}
3934da5ea9aSmpi 			memcpy(sc->sc_flowdst, &pflowr->flowdst,
3944da5ea9aSmpi 			    sizeof(struct sockaddr_in));
3954da5ea9aSmpi 			sc->sc_flowdst->sa_len = sizeof(struct
3964da5ea9aSmpi 			    sockaddr_in);
3974da5ea9aSmpi 			break;
3984da5ea9aSmpi 		case AF_INET6:
399085e702cSflorian 			if (sc->sc_flowdst == NULL) {
4004da5ea9aSmpi 				if ((sc->sc_flowdst = malloc(
4014da5ea9aSmpi 				    sizeof(struct sockaddr_in6),
4024da5ea9aSmpi 				    M_DEVBUF, M_NOWAIT)) == NULL)
4034da5ea9aSmpi 					return (ENOMEM);
404085e702cSflorian 			}
4054da5ea9aSmpi 			memcpy(sc->sc_flowdst, &pflowr->flowdst,
4064da5ea9aSmpi 			    sizeof(struct sockaddr_in6));
4074da5ea9aSmpi 			sc->sc_flowdst->sa_len = sizeof(struct
4084da5ea9aSmpi 			    sockaddr_in6);
4094da5ea9aSmpi 			break;
4104da5ea9aSmpi 		default:
4114da5ea9aSmpi 			break;
4124da5ea9aSmpi 		}
413085e702cSflorian 
4144da5ea9aSmpi 		if (sc->sc_flowdst != NULL) {
4154da5ea9aSmpi 			sc->send_nam->m_len = sc->sc_flowdst->sa_len;
4164da5ea9aSmpi 			sa = mtod(sc->send_nam, struct sockaddr *);
4174da5ea9aSmpi 			memcpy(sa, sc->sc_flowdst, sc->sc_flowdst->sa_len);
4184da5ea9aSmpi 		}
4194da5ea9aSmpi 	}
4204da5ea9aSmpi 
4214da5ea9aSmpi 	if (pflowr->addrmask & PFLOW_MASK_SRCIP) {
422dcb6b858Sflorian 		if (sc->sc_flowsrc != NULL)
4234da5ea9aSmpi 			free(sc->sc_flowsrc, M_DEVBUF, sc->sc_flowsrc->sa_len);
4244da5ea9aSmpi 		sc->sc_flowsrc = NULL;
4254da5ea9aSmpi 		if (sc->so != NULL) {
4264a4dc3eaSmpi 			soclose(sc->so, MSG_DONTWAIT);
4274da5ea9aSmpi 			sc->so = NULL;
4284da5ea9aSmpi 		}
4294da5ea9aSmpi 		switch(pflowr->flowsrc.ss_family) {
4304da5ea9aSmpi 		case AF_INET:
4314da5ea9aSmpi 			if ((sc->sc_flowsrc = malloc(
4324da5ea9aSmpi 			    sizeof(struct sockaddr_in),
4334da5ea9aSmpi 			    M_DEVBUF, M_NOWAIT)) == NULL)
4344da5ea9aSmpi 				return (ENOMEM);
4354da5ea9aSmpi 			memcpy(sc->sc_flowsrc, &pflowr->flowsrc,
4364da5ea9aSmpi 			    sizeof(struct sockaddr_in));
4374da5ea9aSmpi 			sc->sc_flowsrc->sa_len = sizeof(struct
4384da5ea9aSmpi 			    sockaddr_in);
4394da5ea9aSmpi 			break;
4404da5ea9aSmpi 		case AF_INET6:
4414da5ea9aSmpi 			if ((sc->sc_flowsrc = malloc(
4424da5ea9aSmpi 			    sizeof(struct sockaddr_in6),
4434da5ea9aSmpi 			    M_DEVBUF, M_NOWAIT)) == NULL)
4444da5ea9aSmpi 				return (ENOMEM);
4454da5ea9aSmpi 			memcpy(sc->sc_flowsrc, &pflowr->flowsrc,
4464da5ea9aSmpi 			    sizeof(struct sockaddr_in6));
4474da5ea9aSmpi 			sc->sc_flowsrc->sa_len = sizeof(struct
4484da5ea9aSmpi 			    sockaddr_in6);
4494da5ea9aSmpi 			break;
4504da5ea9aSmpi 		default:
4514da5ea9aSmpi 			break;
4524da5ea9aSmpi 		}
4534da5ea9aSmpi 	}
4544da5ea9aSmpi 
4554da5ea9aSmpi 	if (sc->so == NULL) {
4564da5ea9aSmpi 		if (pflowvalidsockaddr(sc->sc_flowdst, 0)) {
4574da5ea9aSmpi 			error = socreate(sc->sc_flowdst->sa_family,
4584da5ea9aSmpi 			    &so, SOCK_DGRAM, 0);
4594da5ea9aSmpi 			if (error)
4604da5ea9aSmpi 				return (error);
4614da5ea9aSmpi 			if (pflowvalidsockaddr(sc->sc_flowsrc, 1)) {
4624da5ea9aSmpi 				struct mbuf *m;
4634da5ea9aSmpi 
4644da5ea9aSmpi 				MGET(m, M_WAIT, MT_SONAME);
4654da5ea9aSmpi 				m->m_len = sc->sc_flowsrc->sa_len;
4664da5ea9aSmpi 				sa = mtod(m, struct sockaddr *);
4674da5ea9aSmpi 				memcpy(sa, sc->sc_flowsrc,
4684da5ea9aSmpi 				    sc->sc_flowsrc->sa_len);
4694da5ea9aSmpi 
47084245c07Sclaudio 				solock(so);
4714da5ea9aSmpi 				error = sobind(so, m, p);
47284245c07Sclaudio 				sounlock(so);
4734da5ea9aSmpi 				m_freem(m);
4744da5ea9aSmpi 				if (error) {
4754a4dc3eaSmpi 					soclose(so, MSG_DONTWAIT);
4764da5ea9aSmpi 					return (error);
4774da5ea9aSmpi 				}
4784da5ea9aSmpi 			}
4794da5ea9aSmpi 			sc->so = so;
4804da5ea9aSmpi 		}
4814da5ea9aSmpi 	} else if (!pflowvalidsockaddr(sc->sc_flowdst, 0)) {
4824a4dc3eaSmpi 		soclose(sc->so, MSG_DONTWAIT);
4834da5ea9aSmpi 		sc->so = NULL;
4844da5ea9aSmpi 	}
4854da5ea9aSmpi 
4860b9ea278Smvs 	NET_LOCK();
487339bbf62Smvs 	mtx_enter(&sc->sc_mtx);
488339bbf62Smvs 
4894da5ea9aSmpi 	/* error check is above */
4904da5ea9aSmpi 	if (pflowr->addrmask & PFLOW_MASK_VERSION)
4914da5ea9aSmpi 		sc->sc_version = pflowr->version;
4924da5ea9aSmpi 
4934da5ea9aSmpi 	pflow_setmtu(sc, ETHERMTU);
494415db4d1Smvs 
495415db4d1Smvs 	switch (sc->sc_version) {
496415db4d1Smvs 	case PFLOW_PROTO_5:
497415db4d1Smvs 		timeout_del(&sc->sc_tmo6);
498415db4d1Smvs 		timeout_del(&sc->sc_tmo_tmpl);
499415db4d1Smvs 		break;
500415db4d1Smvs 	case PFLOW_PROTO_10:
501415db4d1Smvs 		timeout_add_sec(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT);
502415db4d1Smvs 		break;
503415db4d1Smvs 	default: /* NOTREACHED */
504415db4d1Smvs 		break;
505415db4d1Smvs 	}
5064da5ea9aSmpi 
507339bbf62Smvs 	mtx_leave(&sc->sc_mtx);
5080b9ea278Smvs 	NET_UNLOCK();
509339bbf62Smvs 
5104da5ea9aSmpi 	return (0);
5114da5ea9aSmpi }
5124da5ea9aSmpi 
5133baed77aSflorian int
pflowioctl(struct ifnet * ifp,u_long cmd,caddr_t data)51418ae10f7Shenning pflowioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
51518ae10f7Shenning {
51618ae10f7Shenning 	struct proc		*p = curproc;
51718ae10f7Shenning 	struct pflow_softc	*sc = ifp->if_softc;
51818ae10f7Shenning 	struct ifreq		*ifr = (struct ifreq *)data;
51918ae10f7Shenning 	struct pflowreq		 pflowr;
5200b9ea278Smvs 	int			 error = 0;
521f0d16c93Smvs 
52218ae10f7Shenning 	switch (cmd) {
52318ae10f7Shenning 	case SIOCSIFADDR:
52418ae10f7Shenning 	case SIOCSIFDSTADDR:
52518ae10f7Shenning 	case SIOCSIFFLAGS:
5260b9ea278Smvs 	case SIOCSIFMTU:
5270b9ea278Smvs 	case SIOCGETPFLOW:
5280b9ea278Smvs 	case SIOCSETPFLOW:
5290b9ea278Smvs 		break;
5300b9ea278Smvs 	default:
5310b9ea278Smvs 		return (ENOTTY);
5320b9ea278Smvs 	}
5330b9ea278Smvs 
534f0d16c93Smvs 	/* XXXSMP: enforce lock order */
535f0d16c93Smvs 	NET_UNLOCK();
5360b9ea278Smvs 	rw_enter_write(&sc->sc_lock);
5370b9ea278Smvs 
5380b9ea278Smvs 	if (sc->sc_dying) {
5390b9ea278Smvs 		error = ENXIO;
5400b9ea278Smvs 		goto out;
5410b9ea278Smvs 	}
5420b9ea278Smvs 
5430b9ea278Smvs 	switch (cmd) {
5440b9ea278Smvs 	case SIOCSIFADDR:
5450b9ea278Smvs 	case SIOCSIFDSTADDR:
5460b9ea278Smvs 	case SIOCSIFFLAGS:
547f0d16c93Smvs 		NET_LOCK();
548c97a905aSflorian 		if ((ifp->if_flags & IFF_UP) && sc->so != NULL) {
54918ae10f7Shenning 			ifp->if_flags |= IFF_RUNNING;
550339bbf62Smvs 			mtx_enter(&sc->sc_mtx);
551fa1d79b8Sbenno 			/* send templates on startup */
5528818ef4eSbenno 			if (sc->sc_version == PFLOW_PROTO_10)
553fa1d79b8Sbenno 				pflow_sendout_ipfix_tmpl(sc);
554339bbf62Smvs 			mtx_leave(&sc->sc_mtx);
55541cbda0dSgollo 		} else
55618ae10f7Shenning 			ifp->if_flags &= ~IFF_RUNNING;
5570b9ea278Smvs 		NET_UNLOCK();
55818ae10f7Shenning 		break;
5590b9ea278Smvs 
56018ae10f7Shenning 	case SIOCSIFMTU:
5610b9ea278Smvs 		if (ifr->ifr_mtu < PFLOW_MINMTU) {
5620b9ea278Smvs 			error = EINVAL;
5630b9ea278Smvs 			goto out;
5640b9ea278Smvs 		}
56518ae10f7Shenning 		if (ifr->ifr_mtu > MCLBYTES)
56618ae10f7Shenning 			ifr->ifr_mtu = MCLBYTES;
5670b9ea278Smvs 		NET_LOCK();
56818ae10f7Shenning 		if (ifr->ifr_mtu < ifp->if_mtu)
569fa1d79b8Sbenno 			pflow_flush(sc);
570339bbf62Smvs 		mtx_enter(&sc->sc_mtx);
57118ae10f7Shenning 		pflow_setmtu(sc, ifr->ifr_mtu);
572339bbf62Smvs 		mtx_leave(&sc->sc_mtx);
5730b9ea278Smvs 		NET_UNLOCK();
57418ae10f7Shenning 		break;
57518ae10f7Shenning 
57618ae10f7Shenning 	case SIOCGETPFLOW:
57718ae10f7Shenning 		bzero(&pflowr, sizeof(pflowr));
57818ae10f7Shenning 
5793baed77aSflorian 		if (sc->sc_flowsrc != NULL)
5803baed77aSflorian 			memcpy(&pflowr.flowsrc, sc->sc_flowsrc,
5813baed77aSflorian 			    sc->sc_flowsrc->sa_len);
5823baed77aSflorian 		if (sc->sc_flowdst != NULL)
5833baed77aSflorian 			memcpy(&pflowr.flowdst, sc->sc_flowdst,
5843baed77aSflorian 			    sc->sc_flowdst->sa_len);
585339bbf62Smvs 		mtx_enter(&sc->sc_mtx);
586fa1d79b8Sbenno 		pflowr.version = sc->sc_version;
587339bbf62Smvs 		mtx_leave(&sc->sc_mtx);
58818ae10f7Shenning 
5890b9ea278Smvs 		if ((error = copyout(&pflowr, ifr->ifr_data, sizeof(pflowr))))
5900b9ea278Smvs 			goto out;
59118ae10f7Shenning 		break;
59218ae10f7Shenning 
59318ae10f7Shenning 	case SIOCSETPFLOW:
5943e676399Smpi 		if ((error = suser(p)) != 0)
5950b9ea278Smvs 			goto out;
5960b9ea278Smvs 		if ((error = copyin(ifr->ifr_data, &pflowr, sizeof(pflowr))))
5970b9ea278Smvs 			goto out;
598fa1d79b8Sbenno 
5994da5ea9aSmpi 		error = pflow_set(sc, &pflowr);
6000b9ea278Smvs 		if (error != 0)
6010b9ea278Smvs 			goto out;
60218ae10f7Shenning 
6030b9ea278Smvs 		NET_LOCK();
604c97a905aSflorian 		if ((ifp->if_flags & IFF_UP) && sc->so != NULL) {
60518ae10f7Shenning 			ifp->if_flags |= IFF_RUNNING;
606339bbf62Smvs 			mtx_enter(&sc->sc_mtx);
6078818ef4eSbenno 			if (sc->sc_version == PFLOW_PROTO_10)
6084d8db378Sflorian 				pflow_sendout_ipfix_tmpl(sc);
609339bbf62Smvs 			mtx_leave(&sc->sc_mtx);
61041cbda0dSgollo 		} else
61118ae10f7Shenning 			ifp->if_flags &= ~IFF_RUNNING;
6120b9ea278Smvs 		NET_UNLOCK();
61318ae10f7Shenning 
61418ae10f7Shenning 		break;
61518ae10f7Shenning 	}
6160b9ea278Smvs 
6170b9ea278Smvs out:
6180b9ea278Smvs 	rw_exit_write(&sc->sc_lock);
6190b9ea278Smvs 	NET_LOCK();
6200b9ea278Smvs 
6210b9ea278Smvs 	return (error);
62218ae10f7Shenning }
62318ae10f7Shenning 
624fa1d79b8Sbenno int
pflow_calc_mtu(struct pflow_softc * sc,int mtu,int hdrsz)625fa1d79b8Sbenno pflow_calc_mtu(struct pflow_softc *sc, int mtu, int hdrsz)
626fa1d79b8Sbenno {
62758354163Sflorian 	sc->sc_maxcount4 = (mtu - hdrsz -
62858354163Sflorian 	    sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_flow4);
62958354163Sflorian 	sc->sc_maxcount6 = (mtu - hdrsz -
63058354163Sflorian 	    sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_flow6);
631fa1d79b8Sbenno 	if (sc->sc_maxcount4 > PFLOW_MAXFLOWS)
632fa1d79b8Sbenno 		sc->sc_maxcount4 = PFLOW_MAXFLOWS;
633fa1d79b8Sbenno 	if (sc->sc_maxcount6 > PFLOW_MAXFLOWS)
634fa1d79b8Sbenno 		sc->sc_maxcount6 = PFLOW_MAXFLOWS;
63558354163Sflorian 	return (hdrsz + sizeof(struct udpiphdr) +
63658354163Sflorian 	    MIN(sc->sc_maxcount4 * sizeof(struct pflow_ipfix_flow4),
63758354163Sflorian 	    sc->sc_maxcount6 * sizeof(struct pflow_ipfix_flow6)));
638fa1d79b8Sbenno }
639fa1d79b8Sbenno 
640fa1d79b8Sbenno void
pflow_setmtu(struct pflow_softc * sc,int mtu_req)64118ae10f7Shenning pflow_setmtu(struct pflow_softc *sc, int mtu_req)
64218ae10f7Shenning {
64318ae10f7Shenning 	int	mtu;
64418ae10f7Shenning 
64518ae10f7Shenning 	mtu = mtu_req;
64618ae10f7Shenning 
647fa1d79b8Sbenno 	switch (sc->sc_version) {
648fa1d79b8Sbenno 	case PFLOW_PROTO_5:
64988e5d322Sgollo 		sc->sc_maxcount = (mtu - sizeof(struct pflow_header) -
65088e5d322Sgollo 		    sizeof(struct udpiphdr)) / sizeof(struct pflow_flow);
65118ae10f7Shenning 		if (sc->sc_maxcount > PFLOW_MAXFLOWS)
65218ae10f7Shenning 		    sc->sc_maxcount = PFLOW_MAXFLOWS;
65318ae10f7Shenning 		sc->sc_if.if_mtu = sizeof(struct pflow_header) +
65488e5d322Sgollo 		    sizeof(struct udpiphdr) +
65518ae10f7Shenning 		    sc->sc_maxcount * sizeof(struct pflow_flow);
656fa1d79b8Sbenno 		break;
657fa1d79b8Sbenno 	case PFLOW_PROTO_10:
658fa1d79b8Sbenno 		sc->sc_if.if_mtu =
659fa1d79b8Sbenno 		    pflow_calc_mtu(sc, mtu, sizeof(struct pflow_v10_header));
660fa1d79b8Sbenno 		break;
661fa1d79b8Sbenno 	default: /* NOTREACHED */
662fa1d79b8Sbenno 		break;
663fa1d79b8Sbenno 	}
66418ae10f7Shenning }
66518ae10f7Shenning 
66618ae10f7Shenning struct mbuf *
pflow_get_mbuf(struct pflow_softc * sc,u_int16_t set_id)667fa1d79b8Sbenno pflow_get_mbuf(struct pflow_softc *sc, u_int16_t set_id)
66818ae10f7Shenning {
669fa1d79b8Sbenno 	struct pflow_set_header	 set_hdr;
67088e5d322Sgollo 	struct pflow_header	 h;
67188e5d322Sgollo 	struct mbuf		*m;
67218ae10f7Shenning 
673339bbf62Smvs 	MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
674339bbf62Smvs 
67518ae10f7Shenning 	MGETHDR(m, M_DONTWAIT, MT_DATA);
67618ae10f7Shenning 	if (m == NULL) {
677ccf5da69Smvs 		pflowstat_inc(pflow_onomem);
67818ae10f7Shenning 		return (NULL);
67918ae10f7Shenning 	}
68018ae10f7Shenning 
68118ae10f7Shenning 	MCLGET(m, M_DONTWAIT);
68288e5d322Sgollo 	if ((m->m_flags & M_EXT) == 0) {
68318ae10f7Shenning 		m_free(m);
684ccf5da69Smvs 		pflowstat_inc(pflow_onomem);
68518ae10f7Shenning 		return (NULL);
68618ae10f7Shenning 	}
68718ae10f7Shenning 
68888e5d322Sgollo 	m->m_len = m->m_pkthdr.len = 0;
689fb492c37Smpi 	m->m_pkthdr.ph_ifidx = 0;
69018ae10f7Shenning 
691fa1d79b8Sbenno 	if (sc == NULL)		/* get only a new empty mbuf */
692fa1d79b8Sbenno 		return (m);
693fa1d79b8Sbenno 
69410989718Sjasper 	switch (sc->sc_version) {
69510989718Sjasper 	case PFLOW_PROTO_5:
69618ae10f7Shenning 		/* populate pflow_header */
69788e5d322Sgollo 		h.reserved1 = 0;
69888e5d322Sgollo 		h.reserved2 = 0;
69988e5d322Sgollo 		h.count = 0;
700fa1d79b8Sbenno 		h.version = htons(PFLOW_PROTO_5);
70188e5d322Sgollo 		h.flow_sequence = htonl(sc->sc_gcounter);
70288e5d322Sgollo 		h.engine_type = PFLOW_ENGINE_TYPE;
70388e5d322Sgollo 		h.engine_id = PFLOW_ENGINE_ID;
70441b18b7eSblambert 		m_copyback(m, 0, PFLOW_HDRLEN, &h, M_NOWAIT);
70518ae10f7Shenning 
70618ae10f7Shenning 		sc->sc_count = 0;
70718ae10f7Shenning 		timeout_add_sec(&sc->sc_tmo, PFLOW_TIMEOUT);
70810989718Sjasper 		break;
70910989718Sjasper 	case PFLOW_PROTO_10:
710fa1d79b8Sbenno 		/* populate pflow_set_header */
711fa1d79b8Sbenno 		set_hdr.set_length = 0;
712fa1d79b8Sbenno 		set_hdr.set_id = htons(set_id);
713fa1d79b8Sbenno 		m_copyback(m, 0, PFLOW_SET_HDRLEN, &set_hdr, M_NOWAIT);
71410989718Sjasper 		break;
71510989718Sjasper 	default: /* NOTREACHED */
71610989718Sjasper 		break;
717fa1d79b8Sbenno 	}
718fa1d79b8Sbenno 
71988e5d322Sgollo 	return (m);
72018ae10f7Shenning }
72118ae10f7Shenning 
72218ae10f7Shenning void
copy_flow_data(struct pflow_flow * flow1,struct pflow_flow * flow2,struct pf_state * st,struct pf_state_key * sk,int src,int dst)72318ae10f7Shenning copy_flow_data(struct pflow_flow *flow1, struct pflow_flow *flow2,
724c07294cfSflorian     struct pf_state *st, struct pf_state_key *sk, int src, int dst)
72518ae10f7Shenning {
72688e5d322Sgollo 	flow1->src_ip = flow2->dest_ip = sk->addr[src].v4.s_addr;
72788e5d322Sgollo 	flow1->src_port = flow2->dest_port = sk->port[src];
72888e5d322Sgollo 	flow1->dest_ip = flow2->src_ip = sk->addr[dst].v4.s_addr;
72988e5d322Sgollo 	flow1->dest_port = flow2->src_port = sk->port[dst];
73018ae10f7Shenning 
73118ae10f7Shenning 	flow1->dest_as = flow2->src_as =
73218ae10f7Shenning 	    flow1->src_as = flow2->dest_as = 0;
7336e55bf81Sflorian 	flow1->if_index_in = htons(st->if_index_in);
7346e55bf81Sflorian 	flow1->if_index_out = htons(st->if_index_out);
7356e55bf81Sflorian 	flow2->if_index_in = htons(st->if_index_out);
7366e55bf81Sflorian 	flow2->if_index_out = htons(st->if_index_in);
73718ae10f7Shenning 	flow1->dest_mask = flow2->src_mask =
73818ae10f7Shenning 	    flow1->src_mask = flow2->dest_mask = 0;
73918ae10f7Shenning 
74018ae10f7Shenning 	flow1->flow_packets = htonl(st->packets[0]);
74118ae10f7Shenning 	flow2->flow_packets = htonl(st->packets[1]);
74218ae10f7Shenning 	flow1->flow_octets = htonl(st->bytes[0]);
74318ae10f7Shenning 	flow2->flow_octets = htonl(st->bytes[1]);
74418ae10f7Shenning 
7455e6d0011Sflorian 	/*
7465e6d0011Sflorian 	 * Pretend the flow was created or expired when the machine came up
7475e6d0011Sflorian 	 * when creation is in the future of the last time a package was seen
7485e6d0011Sflorian 	 * or was created / expired before this machine came up due to pfsync.
7495e6d0011Sflorian 	 */
7505e6d0011Sflorian 	flow1->flow_start = flow2->flow_start = st->creation < 0 ||
7515e6d0011Sflorian 	    st->creation > st->expire ? htonl(0) : htonl(st->creation * 1000);
7525e6d0011Sflorian 	flow1->flow_finish = flow2->flow_finish = st->expire < 0 ? htonl(0) :
7535e6d0011Sflorian 	    htonl(st->expire * 1000);
75418ae10f7Shenning 	flow1->tcp_flags = flow2->tcp_flags = 0;
75518ae10f7Shenning 	flow1->protocol = flow2->protocol = sk->proto;
75618ae10f7Shenning 	flow1->tos = flow2->tos = st->rule.ptr->tos;
75718ae10f7Shenning }
75818ae10f7Shenning 
759fa1d79b8Sbenno void
copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 * flow1,struct pflow_ipfix_flow4 * flow2,struct pf_state * st,struct pf_state_key * sk,struct pflow_softc * sc,int src,int dst)76058354163Sflorian copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 *flow1,
76158354163Sflorian     struct pflow_ipfix_flow4 *flow2, struct pf_state *st,
76258354163Sflorian     struct pf_state_key *sk, struct pflow_softc *sc, int src, int dst)
76358354163Sflorian {
76458354163Sflorian 	flow1->src_ip = flow2->dest_ip = sk->addr[src].v4.s_addr;
76558354163Sflorian 	flow1->src_port = flow2->dest_port = sk->port[src];
76658354163Sflorian 	flow1->dest_ip = flow2->src_ip = sk->addr[dst].v4.s_addr;
76758354163Sflorian 	flow1->dest_port = flow2->src_port = sk->port[dst];
76858354163Sflorian 
76958354163Sflorian 	flow1->if_index_in = htonl(st->if_index_in);
77058354163Sflorian 	flow1->if_index_out = htonl(st->if_index_out);
77158354163Sflorian 	flow2->if_index_in = htonl(st->if_index_out);
77258354163Sflorian 	flow2->if_index_out = htonl(st->if_index_in);
77358354163Sflorian 
77458354163Sflorian 	flow1->flow_packets = htobe64(st->packets[0]);
77558354163Sflorian 	flow2->flow_packets = htobe64(st->packets[1]);
77658354163Sflorian 	flow1->flow_octets = htobe64(st->bytes[0]);
77758354163Sflorian 	flow2->flow_octets = htobe64(st->bytes[1]);
77858354163Sflorian 
77958354163Sflorian 	/*
78058354163Sflorian 	 * Pretend the flow was created when the machine came up when creation
78158354163Sflorian 	 * is in the future of the last time a package was seen due to pfsync.
78258354163Sflorian 	 */
78358354163Sflorian 	if (st->creation > st->expire)
7843209772dScheloha 		flow1->flow_start = flow2->flow_start = htobe64((gettime() -
7853209772dScheloha 		    getuptime())*1000);
78658354163Sflorian 	else
7873209772dScheloha 		flow1->flow_start = flow2->flow_start = htobe64((gettime() -
7883209772dScheloha 		    (getuptime() - st->creation))*1000);
7893209772dScheloha 	flow1->flow_finish = flow2->flow_finish = htobe64((gettime() -
7903209772dScheloha 	    (getuptime() - st->expire))*1000);
79158354163Sflorian 
79258354163Sflorian 	flow1->protocol = flow2->protocol = sk->proto;
79358354163Sflorian 	flow1->tos = flow2->tos = st->rule.ptr->tos;
79458354163Sflorian }
79558354163Sflorian 
79658354163Sflorian void
copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 * flow1,struct pflow_ipfix_flow6 * flow2,struct pf_state * st,struct pf_state_key * sk,struct pflow_softc * sc,int src,int dst)79758354163Sflorian copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *flow1,
79858354163Sflorian     struct pflow_ipfix_flow6 *flow2, struct pf_state *st,
79958354163Sflorian     struct pf_state_key *sk, struct pflow_softc *sc, int src, int dst)
80058354163Sflorian {
80158354163Sflorian 	bcopy(&sk->addr[src].v6, &flow1->src_ip, sizeof(flow1->src_ip));
80258354163Sflorian 	bcopy(&sk->addr[src].v6, &flow2->dest_ip, sizeof(flow2->dest_ip));
80358354163Sflorian 	flow1->src_port = flow2->dest_port = sk->port[src];
80458354163Sflorian 	bcopy(&sk->addr[dst].v6, &flow1->dest_ip, sizeof(flow1->dest_ip));
80558354163Sflorian 	bcopy(&sk->addr[dst].v6, &flow2->src_ip, sizeof(flow2->src_ip));
80658354163Sflorian 	flow1->dest_port = flow2->src_port = sk->port[dst];
80758354163Sflorian 
80858354163Sflorian 	flow1->if_index_in = htonl(st->if_index_in);
80958354163Sflorian 	flow1->if_index_out = htonl(st->if_index_out);
81058354163Sflorian 	flow2->if_index_in = htonl(st->if_index_out);
81158354163Sflorian 	flow2->if_index_out = htonl(st->if_index_in);
81258354163Sflorian 
81358354163Sflorian 	flow1->flow_packets = htobe64(st->packets[0]);
81458354163Sflorian 	flow2->flow_packets = htobe64(st->packets[1]);
81558354163Sflorian 	flow1->flow_octets = htobe64(st->bytes[0]);
81658354163Sflorian 	flow2->flow_octets = htobe64(st->bytes[1]);
81758354163Sflorian 
81858354163Sflorian 	/*
81958354163Sflorian 	 * Pretend the flow was created when the machine came up when creation
82058354163Sflorian 	 * is in the future of the last time a package was seen due to pfsync.
82158354163Sflorian 	 */
82258354163Sflorian 	if (st->creation > st->expire)
8233209772dScheloha 		flow1->flow_start = flow2->flow_start = htobe64((gettime() -
8243209772dScheloha 		    getuptime())*1000);
82558354163Sflorian 	else
8263209772dScheloha 		flow1->flow_start = flow2->flow_start = htobe64((gettime() -
8273209772dScheloha 		    (getuptime() - st->creation))*1000);
8283209772dScheloha 	flow1->flow_finish = flow2->flow_finish = htobe64((gettime() -
8293209772dScheloha 	    (getuptime() - st->expire))*1000);
83058354163Sflorian 
831fa1d79b8Sbenno 	flow1->protocol = flow2->protocol = sk->proto;
832fa1d79b8Sbenno 	flow1->tos = flow2->tos = st->rule.ptr->tos;
833fa1d79b8Sbenno }
834fa1d79b8Sbenno 
83518ae10f7Shenning int
export_pflow(struct pf_state * st)83618ae10f7Shenning export_pflow(struct pf_state *st)
83718ae10f7Shenning {
838d9bf6ab7Sgollo 	struct pflow_softc	*sc = NULL;
839c07294cfSflorian 	struct pf_state_key	*sk;
840c07294cfSflorian 
841c07294cfSflorian 	sk = st->key[st->direction == PF_IN ? PF_SK_WIRE : PF_SK_STACK];
84288e5d322Sgollo 
84363a0cdecSmvs 	SMR_SLIST_FOREACH(sc, &pflowif_list, sc_next) {
844339bbf62Smvs 		mtx_enter(&sc->sc_mtx);
845fa1d79b8Sbenno 		switch (sc->sc_version) {
846fa1d79b8Sbenno 		case PFLOW_PROTO_5:
847fa1d79b8Sbenno 			if (sk->af == AF_INET)
848c07294cfSflorian 				export_pflow_if(st, sk, sc);
849fa1d79b8Sbenno 			break;
850ab60931cSflorian 		case PFLOW_PROTO_10:
851fa1d79b8Sbenno 			if (sk->af == AF_INET || sk->af == AF_INET6)
852c07294cfSflorian 				export_pflow_if(st, sk, sc);
853fa1d79b8Sbenno 			break;
854fa1d79b8Sbenno 		default: /* NOTREACHED */
855fa1d79b8Sbenno 			break;
856fa1d79b8Sbenno 		}
857339bbf62Smvs 		mtx_leave(&sc->sc_mtx);
858d9bf6ab7Sgollo 	}
859d9bf6ab7Sgollo 
860d9bf6ab7Sgollo 	return (0);
861d9bf6ab7Sgollo }
862d9bf6ab7Sgollo 
863d9bf6ab7Sgollo int
export_pflow_if(struct pf_state * st,struct pf_state_key * sk,struct pflow_softc * sc)864c07294cfSflorian export_pflow_if(struct pf_state *st, struct pf_state_key *sk,
865c07294cfSflorian     struct pflow_softc *sc)
866d9bf6ab7Sgollo {
86718ae10f7Shenning 	struct pf_state		 pfs_copy;
86888e5d322Sgollo 	struct ifnet		*ifp = &sc->sc_if;
86918ae10f7Shenning 	u_int64_t		 bytes[2];
87018ae10f7Shenning 	int			 ret = 0;
87118ae10f7Shenning 
872d9bf6ab7Sgollo 	if (!(ifp->if_flags & IFF_RUNNING))
87318ae10f7Shenning 		return (0);
87418ae10f7Shenning 
87558354163Sflorian 	if (sc->sc_version == PFLOW_PROTO_10)
876c07294cfSflorian 		return (pflow_pack_flow_ipfix(st, sk, sc));
877fa1d79b8Sbenno 
878fa1d79b8Sbenno 	/* PFLOW_PROTO_5 */
87918ae10f7Shenning 	if ((st->bytes[0] < (u_int64_t)PFLOW_MAXBYTES)
88018ae10f7Shenning 	    && (st->bytes[1] < (u_int64_t)PFLOW_MAXBYTES))
881c07294cfSflorian 		return (pflow_pack_flow(st, sk, sc));
88218ae10f7Shenning 
88318ae10f7Shenning 	/* flow > PFLOW_MAXBYTES need special handling */
88418ae10f7Shenning 	bcopy(st, &pfs_copy, sizeof(pfs_copy));
88518ae10f7Shenning 	bytes[0] = pfs_copy.bytes[0];
88618ae10f7Shenning 	bytes[1] = pfs_copy.bytes[1];
88718ae10f7Shenning 
88818ae10f7Shenning 	while (bytes[0] > PFLOW_MAXBYTES) {
88918ae10f7Shenning 		pfs_copy.bytes[0] = PFLOW_MAXBYTES;
89018ae10f7Shenning 		pfs_copy.bytes[1] = 0;
89118ae10f7Shenning 
892c07294cfSflorian 		if ((ret = pflow_pack_flow(&pfs_copy, sk, sc)) != 0)
89318ae10f7Shenning 			return (ret);
89418ae10f7Shenning 		if ((bytes[0] - PFLOW_MAXBYTES) > 0)
89518ae10f7Shenning 			bytes[0] -= PFLOW_MAXBYTES;
89618ae10f7Shenning 	}
89718ae10f7Shenning 
89818ae10f7Shenning 	while (bytes[1] > (u_int64_t)PFLOW_MAXBYTES) {
89918ae10f7Shenning 		pfs_copy.bytes[1] = PFLOW_MAXBYTES;
90018ae10f7Shenning 		pfs_copy.bytes[0] = 0;
90118ae10f7Shenning 
902c07294cfSflorian 		if ((ret = pflow_pack_flow(&pfs_copy, sk, sc)) != 0)
90318ae10f7Shenning 			return (ret);
90418ae10f7Shenning 		if ((bytes[1] - PFLOW_MAXBYTES) > 0)
90518ae10f7Shenning 			bytes[1] -= PFLOW_MAXBYTES;
90618ae10f7Shenning 	}
90718ae10f7Shenning 
90818ae10f7Shenning 	pfs_copy.bytes[0] = bytes[0];
90918ae10f7Shenning 	pfs_copy.bytes[1] = bytes[1];
91018ae10f7Shenning 
911c07294cfSflorian 	return (pflow_pack_flow(&pfs_copy, sk, sc));
91218ae10f7Shenning }
91318ae10f7Shenning 
91418ae10f7Shenning int
copy_flow_to_m(struct pflow_flow * flow,struct pflow_softc * sc)91588e5d322Sgollo copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc)
91618ae10f7Shenning {
9178818ef4eSbenno 	int		ret = 0;
91818ae10f7Shenning 
919339bbf62Smvs 	MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
920339bbf62Smvs 
92118ae10f7Shenning 	if (sc->sc_mbuf == NULL) {
9228818ef4eSbenno 		if ((sc->sc_mbuf = pflow_get_mbuf(sc, 0)) == NULL)
92388e5d322Sgollo 			return (ENOBUFS);
92418ae10f7Shenning 	}
92588e5d322Sgollo 	m_copyback(sc->sc_mbuf, PFLOW_HDRLEN +
92688e5d322Sgollo 	    (sc->sc_count * sizeof(struct pflow_flow)),
92741b18b7eSblambert 	    sizeof(struct pflow_flow), flow, M_NOWAIT);
92818ae10f7Shenning 
929ccf5da69Smvs 	pflowstat_inc(pflow_flows);
93018ae10f7Shenning 	sc->sc_gcounter++;
93118ae10f7Shenning 	sc->sc_count++;
93218ae10f7Shenning 
93388e5d322Sgollo 	if (sc->sc_count >= sc->sc_maxcount)
934fa1d79b8Sbenno 		ret = pflow_sendout_v5(sc);
935fa1d79b8Sbenno 
936fa1d79b8Sbenno 	return(ret);
937fa1d79b8Sbenno }
938fa1d79b8Sbenno 
939fa1d79b8Sbenno int
copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 * flow,struct pflow_softc * sc)94058354163Sflorian copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow, struct pflow_softc *sc)
94158354163Sflorian {
9428818ef4eSbenno 	int		ret = 0;
94358354163Sflorian 
944339bbf62Smvs 	MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
945339bbf62Smvs 
94658354163Sflorian 	if (sc->sc_mbuf == NULL) {
94758354163Sflorian 		if ((sc->sc_mbuf =
94858354163Sflorian 		    pflow_get_mbuf(sc, PFLOW_IPFIX_TMPL_IPV4_ID)) == NULL) {
94958354163Sflorian 			return (ENOBUFS);
95058354163Sflorian 		}
95158354163Sflorian 		sc->sc_count4 = 0;
95258354163Sflorian 		timeout_add_sec(&sc->sc_tmo, PFLOW_TIMEOUT);
95358354163Sflorian 	}
95458354163Sflorian 	m_copyback(sc->sc_mbuf, PFLOW_SET_HDRLEN +
95558354163Sflorian 	    (sc->sc_count4 * sizeof(struct pflow_ipfix_flow4)),
95658354163Sflorian 	    sizeof(struct pflow_ipfix_flow4), flow, M_NOWAIT);
957fa1d79b8Sbenno 
958ccf5da69Smvs 	pflowstat_inc(pflow_flows);
959fa1d79b8Sbenno 	sc->sc_gcounter++;
960fa1d79b8Sbenno 	sc->sc_count4++;
961fa1d79b8Sbenno 
962fa1d79b8Sbenno 	if (sc->sc_count4 >= sc->sc_maxcount4)
963fa1d79b8Sbenno 		ret = pflow_sendout_ipfix(sc, AF_INET);
964fa1d79b8Sbenno 	return(ret);
965fa1d79b8Sbenno }
966fa1d79b8Sbenno 
967fa1d79b8Sbenno int
copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 * flow,struct pflow_softc * sc)96858354163Sflorian copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow, struct pflow_softc *sc)
969fa1d79b8Sbenno {
9708818ef4eSbenno 	int		ret = 0;
971fa1d79b8Sbenno 
972339bbf62Smvs 	MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
973339bbf62Smvs 
974fa1d79b8Sbenno 	if (sc->sc_mbuf6 == NULL) {
975fa1d79b8Sbenno 		if ((sc->sc_mbuf6 =
97658354163Sflorian 		    pflow_get_mbuf(sc, PFLOW_IPFIX_TMPL_IPV6_ID)) == NULL) {
977fa1d79b8Sbenno 			return (ENOBUFS);
978fa1d79b8Sbenno 		}
979fa1d79b8Sbenno 		sc->sc_count6 = 0;
980fa1d79b8Sbenno 		timeout_add_sec(&sc->sc_tmo6, PFLOW_TIMEOUT);
981fa1d79b8Sbenno 	}
982fa1d79b8Sbenno 	m_copyback(sc->sc_mbuf6, PFLOW_SET_HDRLEN +
98358354163Sflorian 	    (sc->sc_count6 * sizeof(struct pflow_ipfix_flow6)),
98458354163Sflorian 	    sizeof(struct pflow_ipfix_flow6), flow, M_NOWAIT);
985fa1d79b8Sbenno 
986ccf5da69Smvs 	pflowstat_inc(pflow_flows);
987fa1d79b8Sbenno 	sc->sc_gcounter++;
988fa1d79b8Sbenno 	sc->sc_count6++;
989fa1d79b8Sbenno 
990fa1d79b8Sbenno 	if (sc->sc_count6 >= sc->sc_maxcount6)
991fa1d79b8Sbenno 		ret = pflow_sendout_ipfix(sc, AF_INET6);
99288e5d322Sgollo 
99388e5d322Sgollo 	return(ret);
99488e5d322Sgollo }
99588e5d322Sgollo 
99688e5d322Sgollo int
pflow_pack_flow(struct pf_state * st,struct pf_state_key * sk,struct pflow_softc * sc)997c07294cfSflorian pflow_pack_flow(struct pf_state *st, struct pf_state_key *sk,
998c07294cfSflorian     struct pflow_softc *sc)
99988e5d322Sgollo {
100088e5d322Sgollo 	struct pflow_flow	 flow1;
100188e5d322Sgollo 	struct pflow_flow	 flow2;
100288e5d322Sgollo 	int			 ret = 0;
100388e5d322Sgollo 
100488e5d322Sgollo 	bzero(&flow1, sizeof(flow1));
100518ae10f7Shenning 	bzero(&flow2, sizeof(flow2));
100618ae10f7Shenning 
100718ae10f7Shenning 	if (st->direction == PF_OUT)
1008c07294cfSflorian 		copy_flow_data(&flow1, &flow2, st, sk, 1, 0);
100918ae10f7Shenning 	else
1010c07294cfSflorian 		copy_flow_data(&flow1, &flow2, st, sk, 0, 1);
101118ae10f7Shenning 
101288e5d322Sgollo 	if (st->bytes[0] != 0) /* first flow from state */
101388e5d322Sgollo 		ret = copy_flow_to_m(&flow1, sc);
101418ae10f7Shenning 
101588e5d322Sgollo 	if (st->bytes[1] != 0) /* second flow from state */
101688e5d322Sgollo 		ret = copy_flow_to_m(&flow2, sc);
101718ae10f7Shenning 
101818ae10f7Shenning 	return (ret);
101918ae10f7Shenning }
102018ae10f7Shenning 
1021fa1d79b8Sbenno int
pflow_pack_flow_ipfix(struct pf_state * st,struct pf_state_key * sk,struct pflow_softc * sc)102258354163Sflorian pflow_pack_flow_ipfix(struct pf_state *st, struct pf_state_key *sk,
102358354163Sflorian     struct pflow_softc *sc)
102458354163Sflorian {
102558354163Sflorian 	struct pflow_ipfix_flow4	 flow4_1, flow4_2;
102658354163Sflorian 	struct pflow_ipfix_flow6	 flow6_1, flow6_2;
102758354163Sflorian 	int				 ret = 0;
102858354163Sflorian 	if (sk->af == AF_INET) {
102958354163Sflorian 		bzero(&flow4_1, sizeof(flow4_1));
103058354163Sflorian 		bzero(&flow4_2, sizeof(flow4_2));
103158354163Sflorian 
103258354163Sflorian 		if (st->direction == PF_OUT)
103358354163Sflorian 			copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc,
103458354163Sflorian 			    1, 0);
103558354163Sflorian 		else
103658354163Sflorian 			copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc,
103758354163Sflorian 			    0, 1);
103858354163Sflorian 
103958354163Sflorian 		if (st->bytes[0] != 0) /* first flow from state */
104058354163Sflorian 			ret = copy_flow_ipfix_4_to_m(&flow4_1, sc);
104158354163Sflorian 
104258354163Sflorian 		if (st->bytes[1] != 0) /* second flow from state */
104358354163Sflorian 			ret = copy_flow_ipfix_4_to_m(&flow4_2, sc);
104458354163Sflorian 	} else if (sk->af == AF_INET6) {
104558354163Sflorian 		bzero(&flow6_1, sizeof(flow6_1));
104658354163Sflorian 		bzero(&flow6_2, sizeof(flow6_2));
104758354163Sflorian 
104858354163Sflorian 		if (st->direction == PF_OUT)
104958354163Sflorian 			copy_flow_ipfix_6_data(&flow6_1, &flow6_2, st, sk, sc,
105058354163Sflorian 			    1, 0);
105158354163Sflorian 		else
105258354163Sflorian 			copy_flow_ipfix_6_data(&flow6_1, &flow6_2, st, sk, sc,
105358354163Sflorian 			    0, 1);
105458354163Sflorian 
105558354163Sflorian 		if (st->bytes[0] != 0) /* first flow from state */
105658354163Sflorian 			ret = copy_flow_ipfix_6_to_m(&flow6_1, sc);
105758354163Sflorian 
105858354163Sflorian 		if (st->bytes[1] != 0) /* second flow from state */
105958354163Sflorian 			ret = copy_flow_ipfix_6_to_m(&flow6_2, sc);
1060fa1d79b8Sbenno 	}
1061fa1d79b8Sbenno 	return (ret);
1062fa1d79b8Sbenno }
1063fa1d79b8Sbenno 
106418ae10f7Shenning void
pflow_timeout(void * v)106518ae10f7Shenning pflow_timeout(void *v)
106618ae10f7Shenning {
106718ae10f7Shenning 	struct pflow_softc	*sc = v;
106818ae10f7Shenning 
1069339bbf62Smvs 	mtx_enter(&sc->sc_mtx);
1070fa1d79b8Sbenno 	switch (sc->sc_version) {
1071fa1d79b8Sbenno 	case PFLOW_PROTO_5:
1072fa1d79b8Sbenno 		pflow_sendout_v5(sc);
1073fa1d79b8Sbenno 		break;
1074fa1d79b8Sbenno 	case PFLOW_PROTO_10:
1075fa1d79b8Sbenno 		pflow_sendout_ipfix(sc, AF_INET);
107658354163Sflorian 		break;
1077fa1d79b8Sbenno 	default: /* NOTREACHED */
1078fa1d79b8Sbenno 		break;
1079fa1d79b8Sbenno 	}
1080339bbf62Smvs 	mtx_leave(&sc->sc_mtx);
1081fa1d79b8Sbenno }
1082fa1d79b8Sbenno 
1083fa1d79b8Sbenno void
pflow_timeout6(void * v)1084fa1d79b8Sbenno pflow_timeout6(void *v)
1085fa1d79b8Sbenno {
1086fa1d79b8Sbenno 	struct pflow_softc	*sc = v;
1087fa1d79b8Sbenno 
1088339bbf62Smvs 	mtx_enter(&sc->sc_mtx);
1089fa1d79b8Sbenno 	pflow_sendout_ipfix(sc, AF_INET6);
1090339bbf62Smvs 	mtx_leave(&sc->sc_mtx);
1091fa1d79b8Sbenno }
1092fa1d79b8Sbenno 
1093fa1d79b8Sbenno void
pflow_timeout_tmpl(void * v)1094fa1d79b8Sbenno pflow_timeout_tmpl(void *v)
1095fa1d79b8Sbenno {
1096fa1d79b8Sbenno 	struct pflow_softc	*sc = v;
1097fa1d79b8Sbenno 
1098339bbf62Smvs 	mtx_enter(&sc->sc_mtx);
1099fa1d79b8Sbenno 	pflow_sendout_ipfix_tmpl(sc);
1100339bbf62Smvs 	mtx_leave(&sc->sc_mtx);
110118ae10f7Shenning }
110218ae10f7Shenning 
1103fa1d79b8Sbenno void
pflow_flush(struct pflow_softc * sc)1104fa1d79b8Sbenno pflow_flush(struct pflow_softc *sc)
1105fa1d79b8Sbenno {
1106339bbf62Smvs 	mtx_enter(&sc->sc_mtx);
1107fa1d79b8Sbenno 	switch (sc->sc_version) {
1108fa1d79b8Sbenno 	case PFLOW_PROTO_5:
1109fa1d79b8Sbenno 		pflow_sendout_v5(sc);
1110fa1d79b8Sbenno 		break;
1111fa1d79b8Sbenno 	case PFLOW_PROTO_10:
1112fa1d79b8Sbenno 		pflow_sendout_ipfix(sc, AF_INET);
1113fa1d79b8Sbenno 		pflow_sendout_ipfix(sc, AF_INET6);
1114fa1d79b8Sbenno 		break;
1115fa1d79b8Sbenno 	default: /* NOTREACHED */
1116fa1d79b8Sbenno 		break;
1117fa1d79b8Sbenno 	}
1118339bbf62Smvs 	mtx_leave(&sc->sc_mtx);
1119fa1d79b8Sbenno }
1120fa1d79b8Sbenno 
112118ae10f7Shenning int
pflow_sendout_v5(struct pflow_softc * sc)1122fa1d79b8Sbenno pflow_sendout_v5(struct pflow_softc *sc)
112318ae10f7Shenning {
11249e200726Sgollo 	struct mbuf		*m = sc->sc_mbuf;
112518ae10f7Shenning 	struct pflow_header	*h;
112618ae10f7Shenning 	struct ifnet		*ifp = &sc->sc_if;
11277ea7180bSdlg 	struct timespec		tv;
112818ae10f7Shenning 
1129339bbf62Smvs 	MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
1130339bbf62Smvs 
113118ae10f7Shenning 	timeout_del(&sc->sc_tmo);
113218ae10f7Shenning 
11339e200726Sgollo 	if (m == NULL)
113418ae10f7Shenning 		return (0);
113518ae10f7Shenning 
11369e200726Sgollo 	sc->sc_mbuf = NULL;
113718ae10f7Shenning 	if (!(ifp->if_flags & IFF_RUNNING)) {
113818ae10f7Shenning 		m_freem(m);
113918ae10f7Shenning 		return (0);
114018ae10f7Shenning 	}
114118ae10f7Shenning 
1142ccf5da69Smvs 	pflowstat_inc(pflow_packets);
114318ae10f7Shenning 	h = mtod(m, struct pflow_header *);
114418ae10f7Shenning 	h->count = htons(sc->sc_count);
114518ae10f7Shenning 
114618ae10f7Shenning 	/* populate pflow_header */
11473209772dScheloha 	h->uptime_ms = htonl(getuptime() * 1000);
11487ea7180bSdlg 
11497ea7180bSdlg 	getnanotime(&tv);
115058354163Sflorian 	h->time_sec = htonl(tv.tv_sec);			/* XXX 2038 */
11517ea7180bSdlg 	h->time_nanosec = htonl(tv.tv_nsec);
1152b52009cbSvisa 	if (mq_enqueue(&sc->sc_outputqueue, m) == 0)
115393865884Ssashan 		task_add(net_tq(ifp->if_index), &sc->sc_outputtask);
11548818ef4eSbenno 	return (0);
115518ae10f7Shenning }
115618ae10f7Shenning 
1157fa1d79b8Sbenno int
pflow_sendout_ipfix(struct pflow_softc * sc,sa_family_t af)115858354163Sflorian pflow_sendout_ipfix(struct pflow_softc *sc, sa_family_t af)
115958354163Sflorian {
116058354163Sflorian 	struct mbuf			*m;
116158354163Sflorian 	struct pflow_v10_header		*h10;
116258354163Sflorian 	struct pflow_set_header		*set_hdr;
116358354163Sflorian 	struct ifnet			*ifp = &sc->sc_if;
1164990339f0Sblambert 	u_int32_t			 count;
116558354163Sflorian 	int				 set_length;
116658354163Sflorian 
1167339bbf62Smvs 	MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
1168339bbf62Smvs 
116958354163Sflorian 	switch (af) {
117058354163Sflorian 	case AF_INET:
117158354163Sflorian 		m = sc->sc_mbuf;
117258354163Sflorian 		timeout_del(&sc->sc_tmo);
117358354163Sflorian 		if (m == NULL)
117458354163Sflorian 			return (0);
117558354163Sflorian 		sc->sc_mbuf = NULL;
1176990339f0Sblambert 		count = sc->sc_count4;
117758354163Sflorian 		set_length = sizeof(struct pflow_set_header)
117858354163Sflorian 		    + sc->sc_count4 * sizeof(struct pflow_ipfix_flow4);
1179fa1d79b8Sbenno 		break;
118058354163Sflorian 	case AF_INET6:
118158354163Sflorian 		m = sc->sc_mbuf6;
118258354163Sflorian 		timeout_del(&sc->sc_tmo6);
118358354163Sflorian 		if (m == NULL)
118458354163Sflorian 			return (0);
118558354163Sflorian 		sc->sc_mbuf6 = NULL;
1186990339f0Sblambert 		count = sc->sc_count6;
118758354163Sflorian 		set_length = sizeof(struct pflow_set_header)
118858354163Sflorian 		    + sc->sc_count6 * sizeof(struct pflow_ipfix_flow6);
118958354163Sflorian 		break;
1190ba413839Sjsg 	default:
1191ba413839Sjsg 		unhandled_af(af);
119258354163Sflorian 	}
119358354163Sflorian 
119458354163Sflorian 	if (!(ifp->if_flags & IFF_RUNNING)) {
119558354163Sflorian 		m_freem(m);
119658354163Sflorian 		return (0);
119758354163Sflorian 	}
119858354163Sflorian 
1199ccf5da69Smvs 	pflowstat_inc(pflow_packets);
120058354163Sflorian 	set_hdr = mtod(m, struct pflow_set_header *);
120158354163Sflorian 	set_hdr->set_length = htons(set_length);
120258354163Sflorian 
1203fa1d79b8Sbenno 	/* populate pflow_header */
1204fa1d79b8Sbenno 	M_PREPEND(m, sizeof(struct pflow_v10_header), M_DONTWAIT);
1205fa1d79b8Sbenno 	if (m == NULL) {
1206ccf5da69Smvs 		pflowstat_inc(pflow_onomem);
1207fa1d79b8Sbenno 		return (ENOBUFS);
1208fa1d79b8Sbenno 	}
1209fa1d79b8Sbenno 	h10 = mtod(m, struct pflow_v10_header *);
1210fa1d79b8Sbenno 	h10->version = htons(PFLOW_PROTO_10);
121158354163Sflorian 	h10->length = htons(PFLOW_IPFIX_HDRLEN + set_length);
12123209772dScheloha 	h10->time_sec = htonl(gettime());		/* XXX 2038 */
1213990339f0Sblambert 	h10->flow_sequence = htonl(sc->sc_sequence);
1214990339f0Sblambert 	sc->sc_sequence += count;
1215fa1d79b8Sbenno 	h10->observation_dom = htonl(PFLOW_ENGINE_TYPE);
1216b52009cbSvisa 	if (mq_enqueue(&sc->sc_outputqueue, m) == 0)
121793865884Ssashan 		task_add(net_tq(ifp->if_index), &sc->sc_outputtask);
12188818ef4eSbenno 	return (0);
1219fa1d79b8Sbenno }
122058354163Sflorian 
122158354163Sflorian int
pflow_sendout_ipfix_tmpl(struct pflow_softc * sc)1222fa1d79b8Sbenno pflow_sendout_ipfix_tmpl(struct pflow_softc *sc)
1223fa1d79b8Sbenno {
1224fa1d79b8Sbenno 	struct mbuf			*m;
1225fa1d79b8Sbenno 	struct pflow_v10_header		*h10;
1226fa1d79b8Sbenno 	struct ifnet			*ifp = &sc->sc_if;
1227fa1d79b8Sbenno 
1228339bbf62Smvs 	MUTEX_ASSERT_LOCKED(&sc->sc_mtx);
1229339bbf62Smvs 
1230fa1d79b8Sbenno 	timeout_del(&sc->sc_tmo_tmpl);
1231fa1d79b8Sbenno 
1232fa1d79b8Sbenno 	if (!(ifp->if_flags & IFF_RUNNING)) {
1233fa1d79b8Sbenno 		return (0);
1234fa1d79b8Sbenno 	}
1235339bbf62Smvs 	m = pflow_get_mbuf(sc, 0);
1236fa1d79b8Sbenno 	if (m == NULL)
1237fa1d79b8Sbenno 		return (0);
123858354163Sflorian 	if (m_copyback(m, 0, sizeof(struct pflow_ipfix_tmpl),
123958354163Sflorian 	    &sc->sc_tmpl_ipfix, M_NOWAIT)) {
1240fa1d79b8Sbenno 		m_freem(m);
1241fa1d79b8Sbenno 		return (0);
1242fa1d79b8Sbenno 	}
1243ccf5da69Smvs 	pflowstat_inc(pflow_packets);
124458354163Sflorian 
1245fa1d79b8Sbenno 	/* populate pflow_header */
1246fa1d79b8Sbenno 	M_PREPEND(m, sizeof(struct pflow_v10_header), M_DONTWAIT);
1247fa1d79b8Sbenno 	if (m == NULL) {
1248ccf5da69Smvs 		pflowstat_inc(pflow_onomem);
1249fa1d79b8Sbenno 		return (ENOBUFS);
1250fa1d79b8Sbenno 	}
1251fa1d79b8Sbenno 	h10 = mtod(m, struct pflow_v10_header *);
1252fa1d79b8Sbenno 	h10->version = htons(PFLOW_PROTO_10);
125358354163Sflorian 	h10->length = htons(PFLOW_IPFIX_HDRLEN + sizeof(struct
125458354163Sflorian 	    pflow_ipfix_tmpl));
12553209772dScheloha 	h10->time_sec = htonl(gettime());		/* XXX 2038 */
1256990339f0Sblambert 	h10->flow_sequence = htonl(sc->sc_sequence);
1257fa1d79b8Sbenno 	h10->observation_dom = htonl(PFLOW_ENGINE_TYPE);
125858354163Sflorian 
1259fa1d79b8Sbenno 	timeout_add_sec(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT);
1260b52009cbSvisa 	if (mq_enqueue(&sc->sc_outputqueue, m) == 0)
126193865884Ssashan 		task_add(net_tq(ifp->if_index), &sc->sc_outputtask);
12628818ef4eSbenno 	return (0);
1263fa1d79b8Sbenno }
1264fa1d79b8Sbenno 
126518ae10f7Shenning int
pflow_sendout_mbuf(struct pflow_softc * sc,struct mbuf * m)126618ae10f7Shenning pflow_sendout_mbuf(struct pflow_softc *sc, struct mbuf *m)
126718ae10f7Shenning {
1268339bbf62Smvs 	rw_assert_anylock(&sc->sc_lock);
1269339bbf62Smvs 
1270f0d16c93Smvs 	counters_pkt(sc->sc_if.if_counters,
1271f0d16c93Smvs 	            ifc_opackets, ifc_obytes, m->m_pkthdr.len);
127288e5d322Sgollo 
1273416aa928Sflorian 	if (sc->so == NULL) {
1274416aa928Sflorian 		m_freem(m);
1275416aa928Sflorian 		return (EINVAL);
127688e5d322Sgollo 	}
1277416aa928Sflorian 	return (sosend(sc->so, sc->send_nam, NULL, m, NULL, 0));
127818ae10f7Shenning }
12790aaca726Sgollo 
12800aaca726Sgollo int
pflow_sysctl(int * name,u_int namelen,void * oldp,size_t * oldlenp,void * newp,size_t newlen)12810aaca726Sgollo pflow_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
12820aaca726Sgollo     void *newp, size_t newlen)
12830aaca726Sgollo {
12840aaca726Sgollo 	if (namelen != 1)
12850aaca726Sgollo 		return (ENOTDIR);
12860aaca726Sgollo 
12870aaca726Sgollo 	switch (name[0]) {
1288ccf5da69Smvs 	case NET_PFLOW_STATS: {
1289ccf5da69Smvs 		uint64_t counters[pflow_ncounters];
1290ccf5da69Smvs 		struct pflowstats pflowstats;
1291ccf5da69Smvs 
12920aaca726Sgollo 		if (newp != NULL)
12930aaca726Sgollo 			return (EPERM);
1294ccf5da69Smvs 
1295ccf5da69Smvs 		counters_read(pflow_counters, counters, pflow_ncounters, NULL);
1296ccf5da69Smvs 
1297ccf5da69Smvs 		pflowstats.pflow_flows = counters[pflow_flows];
1298ccf5da69Smvs 		pflowstats.pflow_packets = counters[pflow_packets];
1299ccf5da69Smvs 		pflowstats.pflow_onomem = counters[pflow_onomem];
1300ccf5da69Smvs 		pflowstats.pflow_oerrors = counters[pflow_oerrors];
1301ccf5da69Smvs 
13020aaca726Sgollo 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
13030aaca726Sgollo 		    &pflowstats, sizeof(pflowstats)));
1304ccf5da69Smvs 	}
13050aaca726Sgollo 	default:
13060aaca726Sgollo 		return (EOPNOTSUPP);
13070aaca726Sgollo 	}
13080aaca726Sgollo 	return (0);
13090aaca726Sgollo }
1310