xref: /freebsd/sys/netpfil/pf/pflow.c (revision baf9b6d04205e02e6248377b1e13b33059a35fc9)
1f92d9b1aSKristof Provost /*	$OpenBSD: if_pflow.c,v 1.100 2023/11/09 08:53:20 mvs Exp $	*/
2f92d9b1aSKristof Provost 
3f92d9b1aSKristof Provost /*
4f92d9b1aSKristof Provost  * Copyright (c) 2023 Rubicon Communications, LLC (Netgate)
5f92d9b1aSKristof Provost  * Copyright (c) 2011 Florian Obser <florian@narrans.de>
6f92d9b1aSKristof Provost  * Copyright (c) 2011 Sebastian Benoit <benoit-lists@fb12.de>
7f92d9b1aSKristof Provost  * Copyright (c) 2008 Henning Brauer <henning@openbsd.org>
8f92d9b1aSKristof Provost  * Copyright (c) 2008 Joerg Goltermann <jg@osn.de>
9f92d9b1aSKristof Provost  *
10f92d9b1aSKristof Provost  * Permission to use, copy, modify, and distribute this software for any
11f92d9b1aSKristof Provost  * purpose with or without fee is hereby granted, provided that the above
12f92d9b1aSKristof Provost  * copyright notice and this permission notice appear in all copies.
13f92d9b1aSKristof Provost  *
14f92d9b1aSKristof Provost  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15f92d9b1aSKristof Provost  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16f92d9b1aSKristof Provost  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17f92d9b1aSKristof Provost  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18f92d9b1aSKristof Provost  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
19f92d9b1aSKristof Provost  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20f92d9b1aSKristof Provost  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21f92d9b1aSKristof Provost  */
22f92d9b1aSKristof Provost 
23f92d9b1aSKristof Provost #include <sys/cdefs.h>
24f92d9b1aSKristof Provost #include <sys/param.h>
25f92d9b1aSKristof Provost #include <sys/bus.h>
26f92d9b1aSKristof Provost #include <sys/callout.h>
27f92d9b1aSKristof Provost #include <sys/endian.h>
28f92d9b1aSKristof Provost #include <sys/interrupt.h>
29f92d9b1aSKristof Provost #include <sys/kernel.h>
30f92d9b1aSKristof Provost #include <sys/malloc.h>
31f92d9b1aSKristof Provost #include <sys/module.h>
32f92d9b1aSKristof Provost #include <sys/mbuf.h>
33f92d9b1aSKristof Provost #include <sys/socket.h>
34f92d9b1aSKristof Provost #include <sys/socketvar.h>
35f92d9b1aSKristof Provost #include <sys/sockio.h>
36f92d9b1aSKristof Provost #include <sys/sysctl.h>
37f92d9b1aSKristof Provost #include <sys/systm.h>
38f92d9b1aSKristof Provost #include <sys/priv.h>
39f92d9b1aSKristof Provost 
40f92d9b1aSKristof Provost #include <net/if.h>
41f92d9b1aSKristof Provost #include <net/if_types.h>
42f92d9b1aSKristof Provost #include <net/bpf.h>
43f92d9b1aSKristof Provost #include <net/route.h>
44f92d9b1aSKristof Provost #include <netinet/in.h>
45f92d9b1aSKristof Provost #include <netinet/if_ether.h>
46f92d9b1aSKristof Provost #include <netinet/tcp.h>
47f92d9b1aSKristof Provost 
48f92d9b1aSKristof Provost #include <netinet/ip.h>
49f92d9b1aSKristof Provost #include <netinet/ip_icmp.h>
50f92d9b1aSKristof Provost #include <netinet/ip_var.h>
51f92d9b1aSKristof Provost #include <netinet/udp.h>
52f92d9b1aSKristof Provost #include <netinet/udp_var.h>
53f92d9b1aSKristof Provost #include <netinet/in_pcb.h>
54f92d9b1aSKristof Provost 
55f92d9b1aSKristof Provost #include <netlink/netlink.h>
56f92d9b1aSKristof Provost #include <netlink/netlink_ctl.h>
57f92d9b1aSKristof Provost #include <netlink/netlink_generic.h>
58f92d9b1aSKristof Provost #include <netlink/netlink_message_writer.h>
59f92d9b1aSKristof Provost 
60f92d9b1aSKristof Provost #include <net/pfvar.h>
61f92d9b1aSKristof Provost #include <net/pflow.h>
62f92d9b1aSKristof Provost #include "net/if_var.h"
63f92d9b1aSKristof Provost 
64f92d9b1aSKristof Provost #define PFLOW_MINMTU	\
65f92d9b1aSKristof Provost     (sizeof(struct pflow_header) + sizeof(struct pflow_flow))
66f92d9b1aSKristof Provost 
67f92d9b1aSKristof Provost #ifdef PFLOWDEBUG
68f92d9b1aSKristof Provost #define DPRINTF(x)	do { printf x ; } while (0)
69f92d9b1aSKristof Provost #else
70f92d9b1aSKristof Provost #define DPRINTF(x)
71f92d9b1aSKristof Provost #endif
72f92d9b1aSKristof Provost 
73f92d9b1aSKristof Provost static void	pflow_output_process(void *);
74f92d9b1aSKristof Provost static int	pflow_create(int);
75f92d9b1aSKristof Provost static int	pflow_destroy(int, bool);
76f92d9b1aSKristof Provost static int	pflow_calc_mtu(struct pflow_softc *, int, int);
77f92d9b1aSKristof Provost static void	pflow_setmtu(struct pflow_softc *, int);
78f92d9b1aSKristof Provost static int	pflowvalidsockaddr(const struct sockaddr *, int);
79f92d9b1aSKristof Provost 
80f92d9b1aSKristof Provost static struct mbuf	*pflow_get_mbuf(struct pflow_softc *, u_int16_t);
81f92d9b1aSKristof Provost static void	pflow_flush(struct pflow_softc *);
82f92d9b1aSKristof Provost static int	pflow_sendout_v5(struct pflow_softc *);
83f92d9b1aSKristof Provost static int	pflow_sendout_ipfix(struct pflow_softc *, sa_family_t);
84f92d9b1aSKristof Provost static int	pflow_sendout_ipfix_tmpl(struct pflow_softc *);
85f92d9b1aSKristof Provost static int	pflow_sendout_mbuf(struct pflow_softc *, struct mbuf *);
86f92d9b1aSKristof Provost static void	pflow_timeout(void *);
87f92d9b1aSKristof Provost static void	pflow_timeout6(void *);
88f92d9b1aSKristof Provost static void	pflow_timeout_tmpl(void *);
89f92d9b1aSKristof Provost static void	copy_flow_data(struct pflow_flow *, struct pflow_flow *,
90*baf9b6d0SKristof Provost 	const struct pf_kstate *, struct pf_state_key *, int, int);
91f92d9b1aSKristof Provost static void	copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 *,
92*baf9b6d0SKristof Provost 	struct pflow_ipfix_flow4 *, const struct pf_kstate *, struct pf_state_key *,
93f92d9b1aSKristof Provost 	struct pflow_softc *, int, int);
94f92d9b1aSKristof Provost static void	copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *,
95*baf9b6d0SKristof Provost 	struct pflow_ipfix_flow6 *, const struct pf_kstate *, struct pf_state_key *,
96f92d9b1aSKristof Provost 	struct pflow_softc *, int, int);
97*baf9b6d0SKristof Provost static int	pflow_pack_flow(const struct pf_kstate *, struct pf_state_key *,
98f92d9b1aSKristof Provost 	struct pflow_softc *);
99*baf9b6d0SKristof Provost static int	pflow_pack_flow_ipfix(const struct pf_kstate *, struct pf_state_key *,
100f92d9b1aSKristof Provost 	struct pflow_softc *);
101*baf9b6d0SKristof Provost static void	export_pflow(const struct pf_kstate *);
102*baf9b6d0SKristof Provost static int	export_pflow_if(const struct pf_kstate*, struct pf_state_key *,
103f92d9b1aSKristof Provost 	struct pflow_softc *);
104f92d9b1aSKristof Provost static int	copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc);
105f92d9b1aSKristof Provost static int	copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow,
106f92d9b1aSKristof Provost 	struct pflow_softc *sc);
107f92d9b1aSKristof Provost static int	copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow,
108f92d9b1aSKristof Provost 	struct pflow_softc *sc);
109f92d9b1aSKristof Provost 
110f92d9b1aSKristof Provost static const char pflowname[] = "pflow";
111f92d9b1aSKristof Provost 
112f92d9b1aSKristof Provost /**
113f92d9b1aSKristof Provost  * Locking concept
114f92d9b1aSKristof Provost  *
115f92d9b1aSKristof Provost  * The list of pflow devices (V_pflowif_list) is managed through epoch.
116f92d9b1aSKristof Provost  * It is safe to read the list without locking (while in NET_EPOCH).
117f92d9b1aSKristof Provost  * There may only be one simultaneous modifier, hence we need V_pflow_list_mtx
118f92d9b1aSKristof Provost  * on every add/delete.
119f92d9b1aSKristof Provost  *
120f92d9b1aSKristof Provost  * Each pflow interface protects its own data with the sc_lock mutex.
121f92d9b1aSKristof Provost  *
122f92d9b1aSKristof Provost  * We do not require any pf locks, and in fact expect to be called without
123f92d9b1aSKristof Provost  * hashrow locks held.
124f92d9b1aSKristof Provost  **/
125f92d9b1aSKristof Provost 
126f92d9b1aSKristof Provost VNET_DEFINE(struct unrhdr *,	pflow_unr);
127f92d9b1aSKristof Provost #define	V_pflow_unr	VNET(pflow_unr)
128f92d9b1aSKristof Provost VNET_DEFINE(CK_LIST_HEAD(, pflow_softc), pflowif_list);
129f92d9b1aSKristof Provost #define	V_pflowif_list	VNET(pflowif_list)
130f92d9b1aSKristof Provost VNET_DEFINE(struct mtx, pflowif_list_mtx);
131f92d9b1aSKristof Provost #define	V_pflowif_list_mtx	VNET(pflowif_list_mtx)
1325dea523bSKristof Provost VNET_DEFINE(struct pflowstats,	 pflowstat);
1335dea523bSKristof Provost #define	V_pflowstats	VNET(pflowstat)
134f92d9b1aSKristof Provost 
135f92d9b1aSKristof Provost #define	PFLOW_LOCK(_sc)		mtx_lock(&(_sc)->sc_lock)
136f92d9b1aSKristof Provost #define	PFLOW_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_lock)
137f92d9b1aSKristof Provost #define	PFLOW_ASSERT(_sc)	mtx_assert(&(_sc)->sc_lock, MA_OWNED)
138f92d9b1aSKristof Provost 
1395dea523bSKristof Provost SYSCTL_NODE(_net, OID_AUTO, pflow, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
1405dea523bSKristof Provost     "PFLOW");
1415dea523bSKristof Provost SYSCTL_STRUCT(_net_pflow, OID_AUTO, stats, CTLFLAG_VNET | CTLFLAG_RW,
1425dea523bSKristof Provost     &VNET_NAME(pflowstat), pflowstats,
1435dea523bSKristof Provost     "PFLOW statistics (struct pflowstats, net/if_pflow.h)");
1445dea523bSKristof Provost 
145f92d9b1aSKristof Provost static void
146f92d9b1aSKristof Provost vnet_pflowattach(void)
147f92d9b1aSKristof Provost {
148f92d9b1aSKristof Provost 	CK_LIST_INIT(&V_pflowif_list);
149f92d9b1aSKristof Provost 	mtx_init(&V_pflowif_list_mtx, "pflow interface list mtx", NULL, MTX_DEF);
150f92d9b1aSKristof Provost 
151f92d9b1aSKristof Provost 	V_pflow_unr = new_unrhdr(0, INT_MAX, &V_pflowif_list_mtx);
152f92d9b1aSKristof Provost }
153f92d9b1aSKristof Provost VNET_SYSINIT(vnet_pflowattach, SI_SUB_PROTO_FIREWALL, SI_ORDER_ANY,
154f92d9b1aSKristof Provost     vnet_pflowattach, NULL);
155f92d9b1aSKristof Provost 
156f92d9b1aSKristof Provost static void
157f92d9b1aSKristof Provost vnet_pflowdetach(void)
158f92d9b1aSKristof Provost {
159f92d9b1aSKristof Provost 	struct pflow_softc	*sc;
160f92d9b1aSKristof Provost 
161f92d9b1aSKristof Provost 	CK_LIST_FOREACH(sc, &V_pflowif_list, sc_next) {
162f92d9b1aSKristof Provost 		pflow_destroy(sc->sc_id, false);
163f92d9b1aSKristof Provost 	}
164f92d9b1aSKristof Provost 
165f92d9b1aSKristof Provost 	MPASS(CK_LIST_EMPTY(&V_pflowif_list));
166f92d9b1aSKristof Provost 	delete_unrhdr(V_pflow_unr);
167f92d9b1aSKristof Provost 	mtx_destroy(&V_pflowif_list_mtx);
168f92d9b1aSKristof Provost }
169f92d9b1aSKristof Provost VNET_SYSUNINIT(vnet_pflowdetach, SI_SUB_PROTO_FIREWALL, SI_ORDER_FOURTH,
170f92d9b1aSKristof Provost     vnet_pflowdetach, NULL);
171f92d9b1aSKristof Provost 
172f92d9b1aSKristof Provost static void
173f92d9b1aSKristof Provost vnet_pflow_finalise(void)
174f92d9b1aSKristof Provost {
175f92d9b1aSKristof Provost 	/*
176f92d9b1aSKristof Provost 	 * Ensure we've freed all interfaces, and do not have pending
177f92d9b1aSKristof Provost 	 * epoch cleanup calls.
178f92d9b1aSKristof Provost 	 */
179f92d9b1aSKristof Provost 	NET_EPOCH_DRAIN_CALLBACKS();
180f92d9b1aSKristof Provost }
181f92d9b1aSKristof Provost VNET_SYSUNINIT(vnet_pflow_finalise, SI_SUB_PROTO_FIREWALL, SI_ORDER_THIRD,
182f92d9b1aSKristof Provost     vnet_pflow_finalise, NULL);
183f92d9b1aSKristof Provost 
184f92d9b1aSKristof Provost static void
185f92d9b1aSKristof Provost pflow_output_process(void *arg)
186f92d9b1aSKristof Provost {
187f92d9b1aSKristof Provost 	struct mbufq ml;
188f92d9b1aSKristof Provost 	struct pflow_softc *sc = arg;
189f92d9b1aSKristof Provost 	struct mbuf *m;
190f92d9b1aSKristof Provost 
191f92d9b1aSKristof Provost 	mbufq_init(&ml, 0);
192f92d9b1aSKristof Provost 
193f92d9b1aSKristof Provost 	PFLOW_LOCK(sc);
194f92d9b1aSKristof Provost 	mbufq_concat(&ml, &sc->sc_outputqueue);
195f92d9b1aSKristof Provost 	PFLOW_UNLOCK(sc);
196f92d9b1aSKristof Provost 
197f92d9b1aSKristof Provost 	CURVNET_SET(sc->sc_vnet);
198f92d9b1aSKristof Provost 	while ((m = mbufq_dequeue(&ml)) != NULL) {
199f92d9b1aSKristof Provost 		pflow_sendout_mbuf(sc, m);
200f92d9b1aSKristof Provost 	}
201f92d9b1aSKristof Provost 	CURVNET_RESTORE();
202f92d9b1aSKristof Provost }
203f92d9b1aSKristof Provost 
204f92d9b1aSKristof Provost static int
205f92d9b1aSKristof Provost pflow_create(int unit)
206f92d9b1aSKristof Provost {
207f92d9b1aSKristof Provost 	struct pflow_softc	*pflowif;
208f92d9b1aSKristof Provost 	int			 error;
209f92d9b1aSKristof Provost 
210f92d9b1aSKristof Provost 	pflowif = malloc(sizeof(*pflowif), M_DEVBUF, M_WAITOK|M_ZERO);
211f92d9b1aSKristof Provost 	mtx_init(&pflowif->sc_lock, "pflowlk", NULL, MTX_DEF);
212f92d9b1aSKristof Provost 	pflowif->sc_version = PFLOW_PROTO_DEFAULT;
213f92d9b1aSKristof Provost 
214f92d9b1aSKristof Provost 	/* ipfix template init */
215f92d9b1aSKristof Provost 	bzero(&pflowif->sc_tmpl_ipfix,sizeof(pflowif->sc_tmpl_ipfix));
216f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.set_header.set_id =
217f92d9b1aSKristof Provost 	    htons(PFLOW_IPFIX_TMPL_SET_ID);
218f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.set_header.set_length =
219f92d9b1aSKristof Provost 	    htons(sizeof(struct pflow_ipfix_tmpl));
220f92d9b1aSKristof Provost 
221f92d9b1aSKristof Provost 	/* ipfix IPv4 template */
222f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.h.tmpl_id =
223f92d9b1aSKristof Provost 	    htons(PFLOW_IPFIX_TMPL_IPV4_ID);
224f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.h.field_count
225f92d9b1aSKristof Provost 	    = htons(PFLOW_IPFIX_TMPL_IPV4_FIELD_COUNT);
226f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_ip.field_id =
227f92d9b1aSKristof Provost 	    htons(PFIX_IE_sourceIPv4Address);
228f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_ip.len = htons(4);
229f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_ip.field_id =
230f92d9b1aSKristof Provost 	    htons(PFIX_IE_destinationIPv4Address);
231f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_ip.len = htons(4);
232f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_in.field_id =
233f92d9b1aSKristof Provost 	    htons(PFIX_IE_ingressInterface);
234f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_in.len = htons(4);
235f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_out.field_id =
236f92d9b1aSKristof Provost 	    htons(PFIX_IE_egressInterface);
237f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_out.len = htons(4);
238f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.packets.field_id =
239f92d9b1aSKristof Provost 	    htons(PFIX_IE_packetDeltaCount);
240f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.packets.len = htons(8);
241f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.octets.field_id =
242f92d9b1aSKristof Provost 	    htons(PFIX_IE_octetDeltaCount);
243f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.octets.len = htons(8);
244f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.start.field_id =
245f92d9b1aSKristof Provost 	    htons(PFIX_IE_flowStartMilliseconds);
246f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.start.len = htons(8);
247f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.finish.field_id =
248f92d9b1aSKristof Provost 	    htons(PFIX_IE_flowEndMilliseconds);
249f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.finish.len = htons(8);
250f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_port.field_id =
251f92d9b1aSKristof Provost 	    htons(PFIX_IE_sourceTransportPort);
252f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_port.len = htons(2);
253f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_port.field_id =
254f92d9b1aSKristof Provost 	    htons(PFIX_IE_destinationTransportPort);
255f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_port.len = htons(2);
256f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.tos.field_id =
257f92d9b1aSKristof Provost 	    htons(PFIX_IE_ipClassOfService);
258f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.tos.len = htons(1);
259f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.protocol.field_id =
260f92d9b1aSKristof Provost 	    htons(PFIX_IE_protocolIdentifier);
261f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv4_tmpl.protocol.len = htons(1);
262f92d9b1aSKristof Provost 
263f92d9b1aSKristof Provost 	/* ipfix IPv6 template */
264f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.h.tmpl_id =
265f92d9b1aSKristof Provost 	    htons(PFLOW_IPFIX_TMPL_IPV6_ID);
266f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.h.field_count =
267f92d9b1aSKristof Provost 	    htons(PFLOW_IPFIX_TMPL_IPV6_FIELD_COUNT);
268f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_ip.field_id =
269f92d9b1aSKristof Provost 	    htons(PFIX_IE_sourceIPv6Address);
270f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_ip.len = htons(16);
271f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_ip.field_id =
272f92d9b1aSKristof Provost 	    htons(PFIX_IE_destinationIPv6Address);
273f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_ip.len = htons(16);
274f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_in.field_id =
275f92d9b1aSKristof Provost 	    htons(PFIX_IE_ingressInterface);
276f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_in.len = htons(4);
277f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_out.field_id =
278f92d9b1aSKristof Provost 	    htons(PFIX_IE_egressInterface);
279f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_out.len = htons(4);
280f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.packets.field_id =
281f92d9b1aSKristof Provost 	    htons(PFIX_IE_packetDeltaCount);
282f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.packets.len = htons(8);
283f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.octets.field_id =
284f92d9b1aSKristof Provost 	    htons(PFIX_IE_octetDeltaCount);
285f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.octets.len = htons(8);
286f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.start.field_id =
287f92d9b1aSKristof Provost 	    htons(PFIX_IE_flowStartMilliseconds);
288f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.start.len = htons(8);
289f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.finish.field_id =
290f92d9b1aSKristof Provost 	    htons(PFIX_IE_flowEndMilliseconds);
291f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.finish.len = htons(8);
292f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_port.field_id =
293f92d9b1aSKristof Provost 	    htons(PFIX_IE_sourceTransportPort);
294f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_port.len = htons(2);
295f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_port.field_id =
296f92d9b1aSKristof Provost 	    htons(PFIX_IE_destinationTransportPort);
297f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_port.len = htons(2);
298f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.tos.field_id =
299f92d9b1aSKristof Provost 	    htons(PFIX_IE_ipClassOfService);
300f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.tos.len = htons(1);
301f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.protocol.field_id =
302f92d9b1aSKristof Provost 	    htons(PFIX_IE_protocolIdentifier);
303f92d9b1aSKristof Provost 	pflowif->sc_tmpl_ipfix.ipv6_tmpl.protocol.len = htons(1);
304f92d9b1aSKristof Provost 
305f92d9b1aSKristof Provost 	pflowif->sc_id = unit;
306f92d9b1aSKristof Provost 	pflowif->sc_vnet = curvnet;
307f92d9b1aSKristof Provost 
308f92d9b1aSKristof Provost 	mbufq_init(&pflowif->sc_outputqueue, 8192);
309f92d9b1aSKristof Provost 	pflow_setmtu(pflowif, ETHERMTU);
310f92d9b1aSKristof Provost 
311f92d9b1aSKristof Provost 	callout_init_mtx(&pflowif->sc_tmo, &pflowif->sc_lock, 0);
312f92d9b1aSKristof Provost 	callout_init_mtx(&pflowif->sc_tmo6, &pflowif->sc_lock, 0);
313f92d9b1aSKristof Provost 	callout_init_mtx(&pflowif->sc_tmo_tmpl, &pflowif->sc_lock, 0);
314f92d9b1aSKristof Provost 
315f92d9b1aSKristof Provost 	error = swi_add(&pflowif->sc_swi_ie, pflowname, pflow_output_process,
316f92d9b1aSKristof Provost 	    pflowif, SWI_NET, INTR_MPSAFE, &pflowif->sc_swi_cookie);
317f92d9b1aSKristof Provost 	if (error) {
318f92d9b1aSKristof Provost 		free(pflowif, M_DEVBUF);
319f92d9b1aSKristof Provost 		return (error);
320f92d9b1aSKristof Provost 	}
321f92d9b1aSKristof Provost 
322f92d9b1aSKristof Provost 	/* Insert into list of pflows */
323f92d9b1aSKristof Provost 	mtx_lock(&V_pflowif_list_mtx);
324f92d9b1aSKristof Provost 	CK_LIST_INSERT_HEAD(&V_pflowif_list, pflowif, sc_next);
325f92d9b1aSKristof Provost 	mtx_unlock(&V_pflowif_list_mtx);
326f92d9b1aSKristof Provost 
327*baf9b6d0SKristof Provost 	V_pflow_export_state_ptr = export_pflow;
328*baf9b6d0SKristof Provost 
329f92d9b1aSKristof Provost 	return (0);
330f92d9b1aSKristof Provost }
331f92d9b1aSKristof Provost 
332f92d9b1aSKristof Provost static void
333f92d9b1aSKristof Provost pflow_free_cb(struct epoch_context *ctx)
334f92d9b1aSKristof Provost {
335f92d9b1aSKristof Provost 	struct pflow_softc *sc;
336f92d9b1aSKristof Provost 
337f92d9b1aSKristof Provost 	sc = __containerof(ctx, struct pflow_softc, sc_epoch_ctx);
338f92d9b1aSKristof Provost 
339f92d9b1aSKristof Provost 	free(sc, M_DEVBUF);
340f92d9b1aSKristof Provost }
341f92d9b1aSKristof Provost 
342f92d9b1aSKristof Provost static int
343f92d9b1aSKristof Provost pflow_destroy(int unit, bool drain)
344f92d9b1aSKristof Provost {
345f92d9b1aSKristof Provost 	struct pflow_softc	*sc;
346f92d9b1aSKristof Provost 	int			 error __diagused;
347f92d9b1aSKristof Provost 
348f92d9b1aSKristof Provost 	mtx_lock(&V_pflowif_list_mtx);
349f92d9b1aSKristof Provost 	CK_LIST_FOREACH(sc, &V_pflowif_list, sc_next) {
350f92d9b1aSKristof Provost 		if (sc->sc_id == unit)
351f92d9b1aSKristof Provost 			break;
352f92d9b1aSKristof Provost 	}
353f92d9b1aSKristof Provost 	if (sc == NULL) {
354f92d9b1aSKristof Provost 		mtx_unlock(&V_pflowif_list_mtx);
355f92d9b1aSKristof Provost 		return (ENOENT);
356f92d9b1aSKristof Provost 	}
357f92d9b1aSKristof Provost 	CK_LIST_REMOVE(sc, sc_next);
358*baf9b6d0SKristof Provost 	if (CK_LIST_EMPTY(&V_pflowif_list))
359*baf9b6d0SKristof Provost 		V_pflow_export_state_ptr = NULL;
360f92d9b1aSKristof Provost 	mtx_unlock(&V_pflowif_list_mtx);
361f92d9b1aSKristof Provost 
362f92d9b1aSKristof Provost 	sc->sc_dying = 1;
363f92d9b1aSKristof Provost 
364f92d9b1aSKristof Provost 	if (drain) {
365f92d9b1aSKristof Provost 		/* Let's be sure no one is using this interface any more. */
366f92d9b1aSKristof Provost 		NET_EPOCH_DRAIN_CALLBACKS();
367f92d9b1aSKristof Provost 	}
368f92d9b1aSKristof Provost 
369f92d9b1aSKristof Provost 	error = swi_remove(sc->sc_swi_cookie);
370f92d9b1aSKristof Provost 	MPASS(error == 0);
371f92d9b1aSKristof Provost 	error = intr_event_destroy(sc->sc_swi_ie);
372f92d9b1aSKristof Provost 	MPASS(error == 0);
373f92d9b1aSKristof Provost 
374f92d9b1aSKristof Provost 	callout_drain(&sc->sc_tmo);
375f92d9b1aSKristof Provost 	callout_drain(&sc->sc_tmo6);
376f92d9b1aSKristof Provost 	callout_drain(&sc->sc_tmo_tmpl);
377f92d9b1aSKristof Provost 
378f92d9b1aSKristof Provost 	m_freem(sc->sc_mbuf);
379f92d9b1aSKristof Provost 	m_freem(sc->sc_mbuf6);
380f92d9b1aSKristof Provost 
381f92d9b1aSKristof Provost 	PFLOW_LOCK(sc);
382f92d9b1aSKristof Provost 	mbufq_drain(&sc->sc_outputqueue);
383f92d9b1aSKristof Provost 	if (sc->so != NULL) {
384f92d9b1aSKristof Provost 		soclose(sc->so);
385f92d9b1aSKristof Provost 		sc->so = NULL;
386f92d9b1aSKristof Provost 	}
387f92d9b1aSKristof Provost 	if (sc->sc_flowdst != NULL)
388f92d9b1aSKristof Provost 		free(sc->sc_flowdst, M_DEVBUF);
389f92d9b1aSKristof Provost 	if (sc->sc_flowsrc != NULL)
390f92d9b1aSKristof Provost 		free(sc->sc_flowsrc, M_DEVBUF);
391f92d9b1aSKristof Provost 	PFLOW_UNLOCK(sc);
392f92d9b1aSKristof Provost 
393f92d9b1aSKristof Provost 	mtx_destroy(&sc->sc_lock);
394f92d9b1aSKristof Provost 
395f92d9b1aSKristof Provost 	free_unr(V_pflow_unr, unit);
396f92d9b1aSKristof Provost 
397f92d9b1aSKristof Provost 	NET_EPOCH_CALL(pflow_free_cb, &sc->sc_epoch_ctx);
398f92d9b1aSKristof Provost 
399f92d9b1aSKristof Provost 	return (0);
400f92d9b1aSKristof Provost }
401f92d9b1aSKristof Provost 
402f92d9b1aSKristof Provost static int
403f92d9b1aSKristof Provost pflowvalidsockaddr(const struct sockaddr *sa, int ignore_port)
404f92d9b1aSKristof Provost {
405f92d9b1aSKristof Provost 	const struct sockaddr_in6	*sin6;
406f92d9b1aSKristof Provost 	const struct sockaddr_in	*sin;
407f92d9b1aSKristof Provost 
408f92d9b1aSKristof Provost 	if (sa == NULL)
409f92d9b1aSKristof Provost 		return (0);
410f92d9b1aSKristof Provost 	switch(sa->sa_family) {
411f92d9b1aSKristof Provost 	case AF_INET:
412f92d9b1aSKristof Provost 		sin = (const struct sockaddr_in *)sa;
413f92d9b1aSKristof Provost 		return (sin->sin_addr.s_addr != INADDR_ANY &&
414f92d9b1aSKristof Provost 		    (ignore_port || sin->sin_port != 0));
415f92d9b1aSKristof Provost 	case AF_INET6:
416f92d9b1aSKristof Provost 		sin6 = (const struct sockaddr_in6 *)sa;
417f92d9b1aSKristof Provost 		return (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
418f92d9b1aSKristof Provost 		    (ignore_port || sin6->sin6_port != 0));
419f92d9b1aSKristof Provost 	default:
420f92d9b1aSKristof Provost 		return (0);
421f92d9b1aSKristof Provost 	}
422f92d9b1aSKristof Provost }
423f92d9b1aSKristof Provost 
424f92d9b1aSKristof Provost static int
425f92d9b1aSKristof Provost pflow_calc_mtu(struct pflow_softc *sc, int mtu, int hdrsz)
426f92d9b1aSKristof Provost {
427f92d9b1aSKristof Provost 
428f92d9b1aSKristof Provost 	sc->sc_maxcount4 = (mtu - hdrsz -
429f92d9b1aSKristof Provost 	    sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_flow4);
430f92d9b1aSKristof Provost 	sc->sc_maxcount6 = (mtu - hdrsz -
431f92d9b1aSKristof Provost 	    sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_flow6);
432f92d9b1aSKristof Provost 	if (sc->sc_maxcount4 > PFLOW_MAXFLOWS)
433f92d9b1aSKristof Provost 		sc->sc_maxcount4 = PFLOW_MAXFLOWS;
434f92d9b1aSKristof Provost 	if (sc->sc_maxcount6 > PFLOW_MAXFLOWS)
435f92d9b1aSKristof Provost 		sc->sc_maxcount6 = PFLOW_MAXFLOWS;
436f92d9b1aSKristof Provost 	return (hdrsz + sizeof(struct udpiphdr) +
437f92d9b1aSKristof Provost 	    MIN(sc->sc_maxcount4 * sizeof(struct pflow_ipfix_flow4),
438f92d9b1aSKristof Provost 	    sc->sc_maxcount6 * sizeof(struct pflow_ipfix_flow6)));
439f92d9b1aSKristof Provost }
440f92d9b1aSKristof Provost 
441f92d9b1aSKristof Provost static void
442f92d9b1aSKristof Provost pflow_setmtu(struct pflow_softc *sc, int mtu_req)
443f92d9b1aSKristof Provost {
444f92d9b1aSKristof Provost 	int	mtu;
445f92d9b1aSKristof Provost 
446f92d9b1aSKristof Provost 	mtu = mtu_req;
447f92d9b1aSKristof Provost 
448f92d9b1aSKristof Provost 	switch (sc->sc_version) {
449f92d9b1aSKristof Provost 	case PFLOW_PROTO_5:
450f92d9b1aSKristof Provost 		sc->sc_maxcount = (mtu - sizeof(struct pflow_header) -
451f92d9b1aSKristof Provost 		    sizeof(struct udpiphdr)) / sizeof(struct pflow_flow);
452f92d9b1aSKristof Provost 		if (sc->sc_maxcount > PFLOW_MAXFLOWS)
453f92d9b1aSKristof Provost 		    sc->sc_maxcount = PFLOW_MAXFLOWS;
454f92d9b1aSKristof Provost 		break;
455f92d9b1aSKristof Provost 	case PFLOW_PROTO_10:
456f92d9b1aSKristof Provost 		pflow_calc_mtu(sc, mtu, sizeof(struct pflow_v10_header));
457f92d9b1aSKristof Provost 		break;
458f92d9b1aSKristof Provost 	default: /* NOTREACHED */
459f92d9b1aSKristof Provost 		break;
460f92d9b1aSKristof Provost 	}
461f92d9b1aSKristof Provost }
462f92d9b1aSKristof Provost 
463f92d9b1aSKristof Provost static struct mbuf *
464f92d9b1aSKristof Provost pflow_get_mbuf(struct pflow_softc *sc, u_int16_t set_id)
465f92d9b1aSKristof Provost {
466f92d9b1aSKristof Provost 	struct pflow_set_header	 set_hdr;
467f92d9b1aSKristof Provost 	struct pflow_header	 h;
468f92d9b1aSKristof Provost 	struct mbuf		*m;
469f92d9b1aSKristof Provost 
470f92d9b1aSKristof Provost 	MGETHDR(m, M_NOWAIT, MT_DATA);
471f92d9b1aSKristof Provost 	if (m == NULL) {
472f92d9b1aSKristof Provost 		V_pflowstats.pflow_onomem++;
473f92d9b1aSKristof Provost 		return (NULL);
474f92d9b1aSKristof Provost 	}
475f92d9b1aSKristof Provost 
476f92d9b1aSKristof Provost 	MCLGET(m, M_NOWAIT);
477f92d9b1aSKristof Provost 	if ((m->m_flags & M_EXT) == 0) {
478f92d9b1aSKristof Provost 		m_free(m);
479f92d9b1aSKristof Provost 		V_pflowstats.pflow_onomem++;
480f92d9b1aSKristof Provost 		return (NULL);
481f92d9b1aSKristof Provost 	}
482f92d9b1aSKristof Provost 
483f92d9b1aSKristof Provost 	m->m_len = m->m_pkthdr.len = 0;
484f92d9b1aSKristof Provost 
485f92d9b1aSKristof Provost 	if (sc == NULL)		/* get only a new empty mbuf */
486f92d9b1aSKristof Provost 		return (m);
487f92d9b1aSKristof Provost 
488f92d9b1aSKristof Provost 	switch (sc->sc_version) {
489f92d9b1aSKristof Provost 	case PFLOW_PROTO_5:
490f92d9b1aSKristof Provost 		/* populate pflow_header */
491f92d9b1aSKristof Provost 		h.reserved1 = 0;
492f92d9b1aSKristof Provost 		h.reserved2 = 0;
493f92d9b1aSKristof Provost 		h.count = 0;
494f92d9b1aSKristof Provost 		h.version = htons(PFLOW_PROTO_5);
495f92d9b1aSKristof Provost 		h.flow_sequence = htonl(sc->sc_gcounter);
496f92d9b1aSKristof Provost 		h.engine_type = PFLOW_ENGINE_TYPE;
497f92d9b1aSKristof Provost 		h.engine_id = PFLOW_ENGINE_ID;
498f92d9b1aSKristof Provost 		m_copyback(m, 0, PFLOW_HDRLEN, (caddr_t)&h);
499f92d9b1aSKristof Provost 
500f92d9b1aSKristof Provost 		sc->sc_count = 0;
501f92d9b1aSKristof Provost 		callout_reset(&sc->sc_tmo, PFLOW_TIMEOUT * hz,
502f92d9b1aSKristof Provost 		    pflow_timeout, sc);
503f92d9b1aSKristof Provost 		break;
504f92d9b1aSKristof Provost 	case PFLOW_PROTO_10:
505f92d9b1aSKristof Provost 		/* populate pflow_set_header */
506f92d9b1aSKristof Provost 		set_hdr.set_length = 0;
507f92d9b1aSKristof Provost 		set_hdr.set_id = htons(set_id);
508f92d9b1aSKristof Provost 		m_copyback(m, 0, PFLOW_SET_HDRLEN, (caddr_t)&set_hdr);
509f92d9b1aSKristof Provost 		break;
510f92d9b1aSKristof Provost 	default: /* NOTREACHED */
511f92d9b1aSKristof Provost 		break;
512f92d9b1aSKristof Provost 	}
513f92d9b1aSKristof Provost 
514f92d9b1aSKristof Provost 	return (m);
515f92d9b1aSKristof Provost }
516f92d9b1aSKristof Provost 
517f92d9b1aSKristof Provost static void
518f92d9b1aSKristof Provost copy_flow_data(struct pflow_flow *flow1, struct pflow_flow *flow2,
519*baf9b6d0SKristof Provost     const struct pf_kstate *st, struct pf_state_key *sk, int src, int dst)
520f92d9b1aSKristof Provost {
521f92d9b1aSKristof Provost 	flow1->src_ip = flow2->dest_ip = sk->addr[src].v4.s_addr;
522f92d9b1aSKristof Provost 	flow1->src_port = flow2->dest_port = sk->port[src];
523f92d9b1aSKristof Provost 	flow1->dest_ip = flow2->src_ip = sk->addr[dst].v4.s_addr;
524f92d9b1aSKristof Provost 	flow1->dest_port = flow2->src_port = sk->port[dst];
525f92d9b1aSKristof Provost 
526f92d9b1aSKristof Provost 	flow1->dest_as = flow2->src_as =
527f92d9b1aSKristof Provost 	    flow1->src_as = flow2->dest_as = 0;
528f92d9b1aSKristof Provost 	flow1->if_index_in = htons(st->if_index_in);
529f92d9b1aSKristof Provost 	flow1->if_index_out = htons(st->if_index_out);
530f92d9b1aSKristof Provost 	flow2->if_index_in = htons(st->if_index_out);
531f92d9b1aSKristof Provost 	flow2->if_index_out = htons(st->if_index_in);
532f92d9b1aSKristof Provost 	flow1->dest_mask = flow2->src_mask =
533f92d9b1aSKristof Provost 	    flow1->src_mask = flow2->dest_mask = 0;
534f92d9b1aSKristof Provost 
535f92d9b1aSKristof Provost 	flow1->flow_packets = htonl(st->packets[0]);
536f92d9b1aSKristof Provost 	flow2->flow_packets = htonl(st->packets[1]);
537f92d9b1aSKristof Provost 	flow1->flow_octets = htonl(st->bytes[0]);
538f92d9b1aSKristof Provost 	flow2->flow_octets = htonl(st->bytes[1]);
539f92d9b1aSKristof Provost 
540f92d9b1aSKristof Provost 	/*
541f92d9b1aSKristof Provost 	 * Pretend the flow was created or expired when the machine came up
542f92d9b1aSKristof Provost 	 * when creation is in the future of the last time a package was seen
543f92d9b1aSKristof Provost 	 * or was created / expired before this machine came up due to pfsync.
544f92d9b1aSKristof Provost 	 */
545f92d9b1aSKristof Provost 	flow1->flow_start = flow2->flow_start = st->creation < 0 ||
546f92d9b1aSKristof Provost 	    st->creation > st->expire ? htonl(0) : htonl(st->creation * 1000);
547f92d9b1aSKristof Provost 	flow1->flow_finish = flow2->flow_finish = st->expire < 0 ? htonl(0) :
548f92d9b1aSKristof Provost 	    htonl(st->expire * 1000);
549f92d9b1aSKristof Provost 	flow1->tcp_flags = flow2->tcp_flags = 0;
550f92d9b1aSKristof Provost 	flow1->protocol = flow2->protocol = sk->proto;
551f92d9b1aSKristof Provost 	flow1->tos = flow2->tos = st->rule.ptr->tos;
552f92d9b1aSKristof Provost }
553f92d9b1aSKristof Provost 
554f92d9b1aSKristof Provost static void
555f92d9b1aSKristof Provost copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 *flow1,
556*baf9b6d0SKristof Provost     struct pflow_ipfix_flow4 *flow2, const struct pf_kstate *st,
557f92d9b1aSKristof Provost     struct pf_state_key *sk, struct pflow_softc *sc, int src, int dst)
558f92d9b1aSKristof Provost {
559f92d9b1aSKristof Provost 	flow1->src_ip = flow2->dest_ip = sk->addr[src].v4.s_addr;
560f92d9b1aSKristof Provost 	flow1->src_port = flow2->dest_port = sk->port[src];
561f92d9b1aSKristof Provost 	flow1->dest_ip = flow2->src_ip = sk->addr[dst].v4.s_addr;
562f92d9b1aSKristof Provost 	flow1->dest_port = flow2->src_port = sk->port[dst];
563f92d9b1aSKristof Provost 
564f92d9b1aSKristof Provost 	flow1->if_index_in = htonl(st->if_index_in);
565f92d9b1aSKristof Provost 	flow1->if_index_out = htonl(st->if_index_out);
566f92d9b1aSKristof Provost 	flow2->if_index_in = htonl(st->if_index_out);
567f92d9b1aSKristof Provost 	flow2->if_index_out = htonl(st->if_index_in);
568f92d9b1aSKristof Provost 
569f92d9b1aSKristof Provost 	flow1->flow_packets = htobe64(st->packets[0]);
570f92d9b1aSKristof Provost 	flow2->flow_packets = htobe64(st->packets[1]);
571f92d9b1aSKristof Provost 	flow1->flow_octets = htobe64(st->bytes[0]);
572f92d9b1aSKristof Provost 	flow2->flow_octets = htobe64(st->bytes[1]);
573f92d9b1aSKristof Provost 
574f92d9b1aSKristof Provost 	/*
575f92d9b1aSKristof Provost 	 * Pretend the flow was created when the machine came up when creation
576f92d9b1aSKristof Provost 	 * is in the future of the last time a package was seen due to pfsync.
577f92d9b1aSKristof Provost 	 */
578f92d9b1aSKristof Provost 	if (st->creation > st->expire)
579f92d9b1aSKristof Provost 		flow1->flow_start = flow2->flow_start = htobe64((time_second -
580f92d9b1aSKristof Provost 		    time_uptime)*1000);
581f92d9b1aSKristof Provost 	else
582f92d9b1aSKristof Provost 		flow1->flow_start = flow2->flow_start = htobe64((time_second -
583f92d9b1aSKristof Provost 		    (time_uptime - st->creation))*1000);
584f92d9b1aSKristof Provost 	flow1->flow_finish = flow2->flow_finish = htobe64((time_second -
585f92d9b1aSKristof Provost 	    (time_uptime - st->expire))*1000);
586f92d9b1aSKristof Provost 
587f92d9b1aSKristof Provost 	flow1->protocol = flow2->protocol = sk->proto;
588f92d9b1aSKristof Provost 	flow1->tos = flow2->tos = st->rule.ptr->tos;
589f92d9b1aSKristof Provost }
590f92d9b1aSKristof Provost 
591f92d9b1aSKristof Provost static void
592f92d9b1aSKristof Provost copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *flow1,
593*baf9b6d0SKristof Provost     struct pflow_ipfix_flow6 *flow2, const struct pf_kstate *st,
594f92d9b1aSKristof Provost     struct pf_state_key *sk, struct pflow_softc *sc, int src, int dst)
595f92d9b1aSKristof Provost {
596f92d9b1aSKristof Provost 	bcopy(&sk->addr[src].v6, &flow1->src_ip, sizeof(flow1->src_ip));
597f92d9b1aSKristof Provost 	bcopy(&sk->addr[src].v6, &flow2->dest_ip, sizeof(flow2->dest_ip));
598f92d9b1aSKristof Provost 	flow1->src_port = flow2->dest_port = sk->port[src];
599f92d9b1aSKristof Provost 	bcopy(&sk->addr[dst].v6, &flow1->dest_ip, sizeof(flow1->dest_ip));
600f92d9b1aSKristof Provost 	bcopy(&sk->addr[dst].v6, &flow2->src_ip, sizeof(flow2->src_ip));
601f92d9b1aSKristof Provost 	flow1->dest_port = flow2->src_port = sk->port[dst];
602f92d9b1aSKristof Provost 
603f92d9b1aSKristof Provost 	flow1->if_index_in = htonl(st->if_index_in);
604f92d9b1aSKristof Provost 	flow1->if_index_out = htonl(st->if_index_out);
605f92d9b1aSKristof Provost 	flow2->if_index_in = htonl(st->if_index_out);
606f92d9b1aSKristof Provost 	flow2->if_index_out = htonl(st->if_index_in);
607f92d9b1aSKristof Provost 
608f92d9b1aSKristof Provost 	flow1->flow_packets = htobe64(st->packets[0]);
609f92d9b1aSKristof Provost 	flow2->flow_packets = htobe64(st->packets[1]);
610f92d9b1aSKristof Provost 	flow1->flow_octets = htobe64(st->bytes[0]);
611f92d9b1aSKristof Provost 	flow2->flow_octets = htobe64(st->bytes[1]);
612f92d9b1aSKristof Provost 
613f92d9b1aSKristof Provost 	/*
614f92d9b1aSKristof Provost 	 * Pretend the flow was created when the machine came up when creation
615f92d9b1aSKristof Provost 	 * is in the future of the last time a package was seen due to pfsync.
616f92d9b1aSKristof Provost 	 */
617f92d9b1aSKristof Provost 	if (st->creation > st->expire)
618f92d9b1aSKristof Provost 		flow1->flow_start = flow2->flow_start = htobe64((time_second -
619f92d9b1aSKristof Provost 		    time_uptime)*1000);
620f92d9b1aSKristof Provost 	else
621f92d9b1aSKristof Provost 		flow1->flow_start = flow2->flow_start = htobe64((time_second -
622f92d9b1aSKristof Provost 		    (time_uptime - st->creation))*1000);
623f92d9b1aSKristof Provost 	flow1->flow_finish = flow2->flow_finish = htobe64((time_second -
624f92d9b1aSKristof Provost 	    (time_uptime - st->expire))*1000);
625f92d9b1aSKristof Provost 
626f92d9b1aSKristof Provost 	flow1->protocol = flow2->protocol = sk->proto;
627f92d9b1aSKristof Provost 	flow1->tos = flow2->tos = st->rule.ptr->tos;
628f92d9b1aSKristof Provost }
629f92d9b1aSKristof Provost 
630*baf9b6d0SKristof Provost static void
631*baf9b6d0SKristof Provost export_pflow(const struct pf_kstate *st)
632f92d9b1aSKristof Provost {
633f92d9b1aSKristof Provost 	struct pflow_softc	*sc = NULL;
634f92d9b1aSKristof Provost 	struct pf_state_key	*sk;
635f92d9b1aSKristof Provost 
636f92d9b1aSKristof Provost 	NET_EPOCH_ASSERT();
637f92d9b1aSKristof Provost 
638f92d9b1aSKristof Provost 	sk = st->key[st->direction == PF_IN ? PF_SK_WIRE : PF_SK_STACK];
639f92d9b1aSKristof Provost 
640f92d9b1aSKristof Provost 	CK_LIST_FOREACH(sc, &V_pflowif_list, sc_next) {
641f92d9b1aSKristof Provost 		PFLOW_LOCK(sc);
642f92d9b1aSKristof Provost 		switch (sc->sc_version) {
643f92d9b1aSKristof Provost 		case PFLOW_PROTO_5:
644f92d9b1aSKristof Provost 			if (sk->af == AF_INET)
645f92d9b1aSKristof Provost 				export_pflow_if(st, sk, sc);
646f92d9b1aSKristof Provost 			break;
647f92d9b1aSKristof Provost 		case PFLOW_PROTO_10:
648f92d9b1aSKristof Provost 			if (sk->af == AF_INET || sk->af == AF_INET6)
649f92d9b1aSKristof Provost 				export_pflow_if(st, sk, sc);
650f92d9b1aSKristof Provost 			break;
651f92d9b1aSKristof Provost 		default: /* NOTREACHED */
652f92d9b1aSKristof Provost 			break;
653f92d9b1aSKristof Provost 		}
654f92d9b1aSKristof Provost 		PFLOW_UNLOCK(sc);
655f92d9b1aSKristof Provost 	}
656f92d9b1aSKristof Provost }
657f92d9b1aSKristof Provost 
658f92d9b1aSKristof Provost static int
659*baf9b6d0SKristof Provost export_pflow_if(const struct pf_kstate *st, struct pf_state_key *sk,
660f92d9b1aSKristof Provost     struct pflow_softc *sc)
661f92d9b1aSKristof Provost {
662f92d9b1aSKristof Provost 	struct pf_kstate	 pfs_copy;
663f92d9b1aSKristof Provost 	u_int64_t		 bytes[2];
664f92d9b1aSKristof Provost 	int			 ret = 0;
665f92d9b1aSKristof Provost 
666f92d9b1aSKristof Provost 	if (sc->sc_version == PFLOW_PROTO_10)
667f92d9b1aSKristof Provost 		return (pflow_pack_flow_ipfix(st, sk, sc));
668f92d9b1aSKristof Provost 
669f92d9b1aSKristof Provost 	/* PFLOW_PROTO_5 */
670f92d9b1aSKristof Provost 	if ((st->bytes[0] < (u_int64_t)PFLOW_MAXBYTES)
671f92d9b1aSKristof Provost 	    && (st->bytes[1] < (u_int64_t)PFLOW_MAXBYTES))
672f92d9b1aSKristof Provost 		return (pflow_pack_flow(st, sk, sc));
673f92d9b1aSKristof Provost 
674f92d9b1aSKristof Provost 	/* flow > PFLOW_MAXBYTES need special handling */
675f92d9b1aSKristof Provost 	bcopy(st, &pfs_copy, sizeof(pfs_copy));
676f92d9b1aSKristof Provost 	bytes[0] = pfs_copy.bytes[0];
677f92d9b1aSKristof Provost 	bytes[1] = pfs_copy.bytes[1];
678f92d9b1aSKristof Provost 
679f92d9b1aSKristof Provost 	while (bytes[0] > PFLOW_MAXBYTES) {
680f92d9b1aSKristof Provost 		pfs_copy.bytes[0] = PFLOW_MAXBYTES;
681f92d9b1aSKristof Provost 		pfs_copy.bytes[1] = 0;
682f92d9b1aSKristof Provost 
683f92d9b1aSKristof Provost 		if ((ret = pflow_pack_flow(&pfs_copy, sk, sc)) != 0)
684f92d9b1aSKristof Provost 			return (ret);
685f92d9b1aSKristof Provost 		if ((bytes[0] - PFLOW_MAXBYTES) > 0)
686f92d9b1aSKristof Provost 			bytes[0] -= PFLOW_MAXBYTES;
687f92d9b1aSKristof Provost 	}
688f92d9b1aSKristof Provost 
689f92d9b1aSKristof Provost 	while (bytes[1] > (u_int64_t)PFLOW_MAXBYTES) {
690f92d9b1aSKristof Provost 		pfs_copy.bytes[1] = PFLOW_MAXBYTES;
691f92d9b1aSKristof Provost 		pfs_copy.bytes[0] = 0;
692f92d9b1aSKristof Provost 
693f92d9b1aSKristof Provost 		if ((ret = pflow_pack_flow(&pfs_copy, sk, sc)) != 0)
694f92d9b1aSKristof Provost 			return (ret);
695f92d9b1aSKristof Provost 		if ((bytes[1] - PFLOW_MAXBYTES) > 0)
696f92d9b1aSKristof Provost 			bytes[1] -= PFLOW_MAXBYTES;
697f92d9b1aSKristof Provost 	}
698f92d9b1aSKristof Provost 
699f92d9b1aSKristof Provost 	pfs_copy.bytes[0] = bytes[0];
700f92d9b1aSKristof Provost 	pfs_copy.bytes[1] = bytes[1];
701f92d9b1aSKristof Provost 
702f92d9b1aSKristof Provost 	return (pflow_pack_flow(&pfs_copy, sk, sc));
703f92d9b1aSKristof Provost }
704f92d9b1aSKristof Provost 
705f92d9b1aSKristof Provost static int
706f92d9b1aSKristof Provost copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc)
707f92d9b1aSKristof Provost {
708f92d9b1aSKristof Provost 	int		ret = 0;
709f92d9b1aSKristof Provost 
710f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
711f92d9b1aSKristof Provost 
712f92d9b1aSKristof Provost 	if (sc->sc_mbuf == NULL) {
713f92d9b1aSKristof Provost 		if ((sc->sc_mbuf = pflow_get_mbuf(sc, 0)) == NULL)
714f92d9b1aSKristof Provost 			return (ENOBUFS);
715f92d9b1aSKristof Provost 	}
716f92d9b1aSKristof Provost 	m_copyback(sc->sc_mbuf, PFLOW_HDRLEN +
717f92d9b1aSKristof Provost 	    (sc->sc_count * sizeof(struct pflow_flow)),
718f92d9b1aSKristof Provost 	    sizeof(struct pflow_flow), (caddr_t)flow);
719f92d9b1aSKristof Provost 
720f92d9b1aSKristof Provost 	if (V_pflowstats.pflow_flows == sc->sc_gcounter)
721f92d9b1aSKristof Provost 		V_pflowstats.pflow_flows++;
722f92d9b1aSKristof Provost 	sc->sc_gcounter++;
723f92d9b1aSKristof Provost 	sc->sc_count++;
724f92d9b1aSKristof Provost 
725f92d9b1aSKristof Provost 	if (sc->sc_count >= sc->sc_maxcount)
726f92d9b1aSKristof Provost 		ret = pflow_sendout_v5(sc);
727f92d9b1aSKristof Provost 
728f92d9b1aSKristof Provost 	return(ret);
729f92d9b1aSKristof Provost }
730f92d9b1aSKristof Provost 
731f92d9b1aSKristof Provost static int
732f92d9b1aSKristof Provost copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow, struct pflow_softc *sc)
733f92d9b1aSKristof Provost {
734f92d9b1aSKristof Provost 	int		ret = 0;
735f92d9b1aSKristof Provost 
736f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
737f92d9b1aSKristof Provost 
738f92d9b1aSKristof Provost 	if (sc->sc_mbuf == NULL) {
739f92d9b1aSKristof Provost 		if ((sc->sc_mbuf =
740f92d9b1aSKristof Provost 		    pflow_get_mbuf(sc, PFLOW_IPFIX_TMPL_IPV4_ID)) == NULL) {
741f92d9b1aSKristof Provost 			return (ENOBUFS);
742f92d9b1aSKristof Provost 		}
743f92d9b1aSKristof Provost 		sc->sc_count4 = 0;
744f92d9b1aSKristof Provost 		callout_reset(&sc->sc_tmo, PFLOW_TIMEOUT * hz,
745f92d9b1aSKristof Provost 		    pflow_timeout, sc);
746f92d9b1aSKristof Provost 	}
747f92d9b1aSKristof Provost 	m_copyback(sc->sc_mbuf, PFLOW_SET_HDRLEN +
748f92d9b1aSKristof Provost 	    (sc->sc_count4 * sizeof(struct pflow_ipfix_flow4)),
749f92d9b1aSKristof Provost 	    sizeof(struct pflow_ipfix_flow4), (caddr_t)flow);
750f92d9b1aSKristof Provost 
751f92d9b1aSKristof Provost 	if (V_pflowstats.pflow_flows == sc->sc_gcounter)
752f92d9b1aSKristof Provost 		V_pflowstats.pflow_flows++;
753f92d9b1aSKristof Provost 	sc->sc_gcounter++;
754f92d9b1aSKristof Provost 	sc->sc_count4++;
755f92d9b1aSKristof Provost 
756f92d9b1aSKristof Provost 	if (sc->sc_count4 >= sc->sc_maxcount4)
757f92d9b1aSKristof Provost 		ret = pflow_sendout_ipfix(sc, AF_INET);
758f92d9b1aSKristof Provost 	return(ret);
759f92d9b1aSKristof Provost }
760f92d9b1aSKristof Provost 
761f92d9b1aSKristof Provost static int
762f92d9b1aSKristof Provost copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow, struct pflow_softc *sc)
763f92d9b1aSKristof Provost {
764f92d9b1aSKristof Provost 	int		ret = 0;
765f92d9b1aSKristof Provost 
766f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
767f92d9b1aSKristof Provost 
768f92d9b1aSKristof Provost 	if (sc->sc_mbuf6 == NULL) {
769f92d9b1aSKristof Provost 		if ((sc->sc_mbuf6 =
770f92d9b1aSKristof Provost 		    pflow_get_mbuf(sc, PFLOW_IPFIX_TMPL_IPV6_ID)) == NULL) {
771f92d9b1aSKristof Provost 			return (ENOBUFS);
772f92d9b1aSKristof Provost 		}
773f92d9b1aSKristof Provost 		sc->sc_count6 = 0;
774f92d9b1aSKristof Provost 		callout_reset(&sc->sc_tmo6, PFLOW_TIMEOUT * hz,
775f92d9b1aSKristof Provost 		    pflow_timeout6, sc);
776f92d9b1aSKristof Provost 	}
777f92d9b1aSKristof Provost 	m_copyback(sc->sc_mbuf6, PFLOW_SET_HDRLEN +
778f92d9b1aSKristof Provost 	    (sc->sc_count6 * sizeof(struct pflow_ipfix_flow6)),
779f92d9b1aSKristof Provost 	    sizeof(struct pflow_ipfix_flow6), (caddr_t)flow);
780f92d9b1aSKristof Provost 
781f92d9b1aSKristof Provost 	if (V_pflowstats.pflow_flows == sc->sc_gcounter)
782f92d9b1aSKristof Provost 		V_pflowstats.pflow_flows++;
783f92d9b1aSKristof Provost 	sc->sc_gcounter++;
784f92d9b1aSKristof Provost 	sc->sc_count6++;
785f92d9b1aSKristof Provost 
786f92d9b1aSKristof Provost 	if (sc->sc_count6 >= sc->sc_maxcount6)
787f92d9b1aSKristof Provost 		ret = pflow_sendout_ipfix(sc, AF_INET6);
788f92d9b1aSKristof Provost 
789f92d9b1aSKristof Provost 	return(ret);
790f92d9b1aSKristof Provost }
791f92d9b1aSKristof Provost 
792f92d9b1aSKristof Provost static int
793*baf9b6d0SKristof Provost pflow_pack_flow(const struct pf_kstate *st, struct pf_state_key *sk,
794f92d9b1aSKristof Provost     struct pflow_softc *sc)
795f92d9b1aSKristof Provost {
796f92d9b1aSKristof Provost 	struct pflow_flow	 flow1;
797f92d9b1aSKristof Provost 	struct pflow_flow	 flow2;
798f92d9b1aSKristof Provost 	int			 ret = 0;
799f92d9b1aSKristof Provost 
800f92d9b1aSKristof Provost 	bzero(&flow1, sizeof(flow1));
801f92d9b1aSKristof Provost 	bzero(&flow2, sizeof(flow2));
802f92d9b1aSKristof Provost 
803f92d9b1aSKristof Provost 	if (st->direction == PF_OUT)
804f92d9b1aSKristof Provost 		copy_flow_data(&flow1, &flow2, st, sk, 1, 0);
805f92d9b1aSKristof Provost 	else
806f92d9b1aSKristof Provost 		copy_flow_data(&flow1, &flow2, st, sk, 0, 1);
807f92d9b1aSKristof Provost 
808f92d9b1aSKristof Provost 	if (st->bytes[0] != 0) /* first flow from state */
809f92d9b1aSKristof Provost 		ret = copy_flow_to_m(&flow1, sc);
810f92d9b1aSKristof Provost 
811f92d9b1aSKristof Provost 	if (st->bytes[1] != 0) /* second flow from state */
812f92d9b1aSKristof Provost 		ret = copy_flow_to_m(&flow2, sc);
813f92d9b1aSKristof Provost 
814f92d9b1aSKristof Provost 	return (ret);
815f92d9b1aSKristof Provost }
816f92d9b1aSKristof Provost 
817f92d9b1aSKristof Provost static int
818*baf9b6d0SKristof Provost pflow_pack_flow_ipfix(const struct pf_kstate *st, struct pf_state_key *sk,
819f92d9b1aSKristof Provost     struct pflow_softc *sc)
820f92d9b1aSKristof Provost {
821f92d9b1aSKristof Provost 	struct pflow_ipfix_flow4	 flow4_1, flow4_2;
822f92d9b1aSKristof Provost 	struct pflow_ipfix_flow6	 flow6_1, flow6_2;
823f92d9b1aSKristof Provost 	int				 ret = 0;
824f92d9b1aSKristof Provost 	if (sk->af == AF_INET) {
825f92d9b1aSKristof Provost 		bzero(&flow4_1, sizeof(flow4_1));
826f92d9b1aSKristof Provost 		bzero(&flow4_2, sizeof(flow4_2));
827f92d9b1aSKristof Provost 
828f92d9b1aSKristof Provost 		if (st->direction == PF_OUT)
829f92d9b1aSKristof Provost 			copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc,
830f92d9b1aSKristof Provost 			    1, 0);
831f92d9b1aSKristof Provost 		else
832f92d9b1aSKristof Provost 			copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc,
833f92d9b1aSKristof Provost 			    0, 1);
834f92d9b1aSKristof Provost 
835f92d9b1aSKristof Provost 		if (st->bytes[0] != 0) /* first flow from state */
836f92d9b1aSKristof Provost 			ret = copy_flow_ipfix_4_to_m(&flow4_1, sc);
837f92d9b1aSKristof Provost 
838f92d9b1aSKristof Provost 		if (st->bytes[1] != 0) /* second flow from state */
839f92d9b1aSKristof Provost 			ret = copy_flow_ipfix_4_to_m(&flow4_2, sc);
840f92d9b1aSKristof Provost 	} else if (sk->af == AF_INET6) {
841f92d9b1aSKristof Provost 		bzero(&flow6_1, sizeof(flow6_1));
842f92d9b1aSKristof Provost 		bzero(&flow6_2, sizeof(flow6_2));
843f92d9b1aSKristof Provost 
844f92d9b1aSKristof Provost 		if (st->direction == PF_OUT)
845f92d9b1aSKristof Provost 			copy_flow_ipfix_6_data(&flow6_1, &flow6_2, st, sk, sc,
846f92d9b1aSKristof Provost 			    1, 0);
847f92d9b1aSKristof Provost 		else
848f92d9b1aSKristof Provost 			copy_flow_ipfix_6_data(&flow6_1, &flow6_2, st, sk, sc,
849f92d9b1aSKristof Provost 			    0, 1);
850f92d9b1aSKristof Provost 
851f92d9b1aSKristof Provost 		if (st->bytes[0] != 0) /* first flow from state */
852f92d9b1aSKristof Provost 			ret = copy_flow_ipfix_6_to_m(&flow6_1, sc);
853f92d9b1aSKristof Provost 
854f92d9b1aSKristof Provost 		if (st->bytes[1] != 0) /* second flow from state */
855f92d9b1aSKristof Provost 			ret = copy_flow_ipfix_6_to_m(&flow6_2, sc);
856f92d9b1aSKristof Provost 	}
857f92d9b1aSKristof Provost 	return (ret);
858f92d9b1aSKristof Provost }
859f92d9b1aSKristof Provost 
860f92d9b1aSKristof Provost static void
861f92d9b1aSKristof Provost pflow_timeout(void *v)
862f92d9b1aSKristof Provost {
863f92d9b1aSKristof Provost 	struct pflow_softc	*sc = v;
864f92d9b1aSKristof Provost 
865f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
866f92d9b1aSKristof Provost 	CURVNET_SET(sc->sc_vnet);
867f92d9b1aSKristof Provost 
868f92d9b1aSKristof Provost 	switch (sc->sc_version) {
869f92d9b1aSKristof Provost 	case PFLOW_PROTO_5:
870f92d9b1aSKristof Provost 		pflow_sendout_v5(sc);
871f92d9b1aSKristof Provost 		break;
872f92d9b1aSKristof Provost 	case PFLOW_PROTO_10:
873f92d9b1aSKristof Provost 		pflow_sendout_ipfix(sc, AF_INET);
874f92d9b1aSKristof Provost 		break;
875f92d9b1aSKristof Provost 	default: /* NOTREACHED */
876f92d9b1aSKristof Provost 		panic("Unsupported version %d", sc->sc_version);
877f92d9b1aSKristof Provost 		break;
878f92d9b1aSKristof Provost 	}
879f92d9b1aSKristof Provost 
880f92d9b1aSKristof Provost 	CURVNET_RESTORE();
881f92d9b1aSKristof Provost }
882f92d9b1aSKristof Provost 
883f92d9b1aSKristof Provost static void
884f92d9b1aSKristof Provost pflow_timeout6(void *v)
885f92d9b1aSKristof Provost {
886f92d9b1aSKristof Provost 	struct pflow_softc	*sc = v;
887f92d9b1aSKristof Provost 
888f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
889f92d9b1aSKristof Provost 
890f92d9b1aSKristof Provost 	if (sc->sc_version != PFLOW_PROTO_10)
891f92d9b1aSKristof Provost 		return;
892f92d9b1aSKristof Provost 
893f92d9b1aSKristof Provost 	CURVNET_SET(sc->sc_vnet);
894f92d9b1aSKristof Provost 	pflow_sendout_ipfix(sc, AF_INET6);
895f92d9b1aSKristof Provost 	CURVNET_RESTORE();
896f92d9b1aSKristof Provost }
897f92d9b1aSKristof Provost 
898f92d9b1aSKristof Provost static void
899f92d9b1aSKristof Provost pflow_timeout_tmpl(void *v)
900f92d9b1aSKristof Provost {
901f92d9b1aSKristof Provost 	struct pflow_softc	*sc = v;
902f92d9b1aSKristof Provost 
903f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
904f92d9b1aSKristof Provost 
905f92d9b1aSKristof Provost 	if (sc->sc_version != PFLOW_PROTO_10)
906f92d9b1aSKristof Provost 		return;
907f92d9b1aSKristof Provost 
908f92d9b1aSKristof Provost 	CURVNET_SET(sc->sc_vnet);
909f92d9b1aSKristof Provost 	pflow_sendout_ipfix_tmpl(sc);
910f92d9b1aSKristof Provost 	CURVNET_RESTORE();
911f92d9b1aSKristof Provost }
912f92d9b1aSKristof Provost 
913f92d9b1aSKristof Provost static void
914f92d9b1aSKristof Provost pflow_flush(struct pflow_softc *sc)
915f92d9b1aSKristof Provost {
916f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
917f92d9b1aSKristof Provost 
918f92d9b1aSKristof Provost 	switch (sc->sc_version) {
919f92d9b1aSKristof Provost 	case PFLOW_PROTO_5:
920f92d9b1aSKristof Provost 		pflow_sendout_v5(sc);
921f92d9b1aSKristof Provost 		break;
922f92d9b1aSKristof Provost 	case PFLOW_PROTO_10:
923f92d9b1aSKristof Provost 		pflow_sendout_ipfix(sc, AF_INET);
924f92d9b1aSKristof Provost 		pflow_sendout_ipfix(sc, AF_INET6);
925f92d9b1aSKristof Provost 		break;
926f92d9b1aSKristof Provost 	default: /* NOTREACHED */
927f92d9b1aSKristof Provost 		break;
928f92d9b1aSKristof Provost 	}
929f92d9b1aSKristof Provost }
930f92d9b1aSKristof Provost 
931f92d9b1aSKristof Provost static int
932f92d9b1aSKristof Provost pflow_sendout_v5(struct pflow_softc *sc)
933f92d9b1aSKristof Provost {
934f92d9b1aSKristof Provost 	struct mbuf		*m = sc->sc_mbuf;
935f92d9b1aSKristof Provost 	struct pflow_header	*h;
936f92d9b1aSKristof Provost 	struct timespec		tv;
937f92d9b1aSKristof Provost 
938f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
939f92d9b1aSKristof Provost 
940f92d9b1aSKristof Provost 	if (m == NULL)
941f92d9b1aSKristof Provost 		return (0);
942f92d9b1aSKristof Provost 
943f92d9b1aSKristof Provost 	sc->sc_mbuf = NULL;
944f92d9b1aSKristof Provost 
945f92d9b1aSKristof Provost 	V_pflowstats.pflow_packets++;
946f92d9b1aSKristof Provost 	h = mtod(m, struct pflow_header *);
947f92d9b1aSKristof Provost 	h->count = htons(sc->sc_count);
948f92d9b1aSKristof Provost 
949f92d9b1aSKristof Provost 	/* populate pflow_header */
950f92d9b1aSKristof Provost 	h->uptime_ms = htonl(time_uptime * 1000);
951f92d9b1aSKristof Provost 
952f92d9b1aSKristof Provost 	getnanotime(&tv);
953f92d9b1aSKristof Provost 	h->time_sec = htonl(tv.tv_sec);			/* XXX 2038 */
954f92d9b1aSKristof Provost 	h->time_nanosec = htonl(tv.tv_nsec);
955f92d9b1aSKristof Provost 	if (mbufq_enqueue(&sc->sc_outputqueue, m) == 0)
956f92d9b1aSKristof Provost 		swi_sched(sc->sc_swi_cookie, 0);
957f92d9b1aSKristof Provost 
958f92d9b1aSKristof Provost 	return (0);
959f92d9b1aSKristof Provost }
960f92d9b1aSKristof Provost 
961f92d9b1aSKristof Provost static int
962f92d9b1aSKristof Provost pflow_sendout_ipfix(struct pflow_softc *sc, sa_family_t af)
963f92d9b1aSKristof Provost {
964f92d9b1aSKristof Provost 	struct mbuf			*m;
965f92d9b1aSKristof Provost 	struct pflow_v10_header		*h10;
966f92d9b1aSKristof Provost 	struct pflow_set_header		*set_hdr;
967f92d9b1aSKristof Provost 	u_int32_t			 count;
968f92d9b1aSKristof Provost 	int				 set_length;
969f92d9b1aSKristof Provost 
970f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
971f92d9b1aSKristof Provost 
972f92d9b1aSKristof Provost 	switch (af) {
973f92d9b1aSKristof Provost 	case AF_INET:
974f92d9b1aSKristof Provost 		m = sc->sc_mbuf;
975f92d9b1aSKristof Provost 		callout_stop(&sc->sc_tmo);
976f92d9b1aSKristof Provost 		if (m == NULL)
977f92d9b1aSKristof Provost 			return (0);
978f92d9b1aSKristof Provost 		sc->sc_mbuf = NULL;
979f92d9b1aSKristof Provost 		count = sc->sc_count4;
980f92d9b1aSKristof Provost 		set_length = sizeof(struct pflow_set_header)
981f92d9b1aSKristof Provost 		    + sc->sc_count4 * sizeof(struct pflow_ipfix_flow4);
982f92d9b1aSKristof Provost 		break;
983f92d9b1aSKristof Provost 	case AF_INET6:
984f92d9b1aSKristof Provost 		m = sc->sc_mbuf6;
985f92d9b1aSKristof Provost 		callout_stop(&sc->sc_tmo6);
986f92d9b1aSKristof Provost 		if (m == NULL)
987f92d9b1aSKristof Provost 			return (0);
988f92d9b1aSKristof Provost 		sc->sc_mbuf6 = NULL;
989f92d9b1aSKristof Provost 		count = sc->sc_count6;
990f92d9b1aSKristof Provost 		set_length = sizeof(struct pflow_set_header)
991f92d9b1aSKristof Provost 		    + sc->sc_count6 * sizeof(struct pflow_ipfix_flow6);
992f92d9b1aSKristof Provost 		break;
993f92d9b1aSKristof Provost 	default:
994f92d9b1aSKristof Provost 		panic("Unsupported AF %d", af);
995f92d9b1aSKristof Provost 	}
996f92d9b1aSKristof Provost 
997f92d9b1aSKristof Provost 	V_pflowstats.pflow_packets++;
998f92d9b1aSKristof Provost 	set_hdr = mtod(m, struct pflow_set_header *);
999f92d9b1aSKristof Provost 	set_hdr->set_length = htons(set_length);
1000f92d9b1aSKristof Provost 
1001f92d9b1aSKristof Provost 	/* populate pflow_header */
1002f92d9b1aSKristof Provost 	M_PREPEND(m, sizeof(struct pflow_v10_header), M_NOWAIT);
1003f92d9b1aSKristof Provost 	if (m == NULL) {
1004f92d9b1aSKristof Provost 		V_pflowstats.pflow_onomem++;
1005f92d9b1aSKristof Provost 		return (ENOBUFS);
1006f92d9b1aSKristof Provost 	}
1007f92d9b1aSKristof Provost 	h10 = mtod(m, struct pflow_v10_header *);
1008f92d9b1aSKristof Provost 	h10->version = htons(PFLOW_PROTO_10);
1009f92d9b1aSKristof Provost 	h10->length = htons(PFLOW_IPFIX_HDRLEN + set_length);
1010f92d9b1aSKristof Provost 	h10->time_sec = htonl(time_second);		/* XXX 2038 */
1011f92d9b1aSKristof Provost 	h10->flow_sequence = htonl(sc->sc_sequence);
1012f92d9b1aSKristof Provost 	sc->sc_sequence += count;
1013f92d9b1aSKristof Provost 	h10->observation_dom = htonl(PFLOW_ENGINE_TYPE);
1014f92d9b1aSKristof Provost 	if (mbufq_enqueue(&sc->sc_outputqueue, m) == 0)
1015f92d9b1aSKristof Provost 		swi_sched(sc->sc_swi_cookie, 0);
1016f92d9b1aSKristof Provost 
1017f92d9b1aSKristof Provost 	return (0);
1018f92d9b1aSKristof Provost }
1019f92d9b1aSKristof Provost 
1020f92d9b1aSKristof Provost static int
1021f92d9b1aSKristof Provost pflow_sendout_ipfix_tmpl(struct pflow_softc *sc)
1022f92d9b1aSKristof Provost {
1023f92d9b1aSKristof Provost 	struct mbuf			*m;
1024f92d9b1aSKristof Provost 	struct pflow_v10_header		*h10;
1025f92d9b1aSKristof Provost 
1026f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
1027f92d9b1aSKristof Provost 
1028f92d9b1aSKristof Provost 	m = pflow_get_mbuf(sc, 0);
1029f92d9b1aSKristof Provost 	if (m == NULL)
1030f92d9b1aSKristof Provost 		return (0);
1031f92d9b1aSKristof Provost 	m_copyback(m, 0, sizeof(struct pflow_ipfix_tmpl),
1032f92d9b1aSKristof Provost 	    (caddr_t)&sc->sc_tmpl_ipfix);
1033f92d9b1aSKristof Provost 
1034f92d9b1aSKristof Provost 	V_pflowstats.pflow_packets++;
1035f92d9b1aSKristof Provost 
1036f92d9b1aSKristof Provost 	/* populate pflow_header */
1037f92d9b1aSKristof Provost 	M_PREPEND(m, sizeof(struct pflow_v10_header), M_NOWAIT);
1038f92d9b1aSKristof Provost 	if (m == NULL) {
1039f92d9b1aSKristof Provost 		V_pflowstats.pflow_onomem++;
1040f92d9b1aSKristof Provost 		return (ENOBUFS);
1041f92d9b1aSKristof Provost 	}
1042f92d9b1aSKristof Provost 	h10 = mtod(m, struct pflow_v10_header *);
1043f92d9b1aSKristof Provost 	h10->version = htons(PFLOW_PROTO_10);
1044f92d9b1aSKristof Provost 	h10->length = htons(PFLOW_IPFIX_HDRLEN + sizeof(struct
1045f92d9b1aSKristof Provost 	    pflow_ipfix_tmpl));
1046f92d9b1aSKristof Provost 	h10->time_sec = htonl(time_second);		/* XXX 2038 */
1047f92d9b1aSKristof Provost 	h10->flow_sequence = htonl(sc->sc_sequence);
1048f92d9b1aSKristof Provost 	h10->observation_dom = htonl(PFLOW_ENGINE_TYPE);
1049f92d9b1aSKristof Provost 
1050f92d9b1aSKristof Provost 	callout_reset(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT * hz,
1051f92d9b1aSKristof Provost 	    pflow_timeout_tmpl, sc);
1052f92d9b1aSKristof Provost 	if (mbufq_enqueue(&sc->sc_outputqueue, m) == 0)
1053f92d9b1aSKristof Provost 		swi_sched(sc->sc_swi_cookie, 0);
1054f92d9b1aSKristof Provost 
1055f92d9b1aSKristof Provost 	return (0);
1056f92d9b1aSKristof Provost }
1057f92d9b1aSKristof Provost 
1058f92d9b1aSKristof Provost static int
1059f92d9b1aSKristof Provost pflow_sendout_mbuf(struct pflow_softc *sc, struct mbuf *m)
1060f92d9b1aSKristof Provost {
1061f92d9b1aSKristof Provost 	if (sc->so == NULL) {
1062f92d9b1aSKristof Provost 		m_freem(m);
1063f92d9b1aSKristof Provost 		return (EINVAL);
1064f92d9b1aSKristof Provost 	}
1065f92d9b1aSKristof Provost 	return (sosend(sc->so, sc->sc_flowdst, NULL, m, NULL, 0, curthread));
1066f92d9b1aSKristof Provost }
1067f92d9b1aSKristof Provost 
1068f92d9b1aSKristof Provost static int
1069f92d9b1aSKristof Provost pflow_nl_list(struct nlmsghdr *hdr, struct nl_pstate *npt)
1070f92d9b1aSKristof Provost {
1071f92d9b1aSKristof Provost 	struct epoch_tracker	 et;
1072f92d9b1aSKristof Provost 	struct pflow_softc	*sc = NULL;
1073f92d9b1aSKristof Provost 	struct nl_writer	 *nw = npt->nw;
1074f92d9b1aSKristof Provost 	int			 error = 0;
1075f92d9b1aSKristof Provost 
1076f92d9b1aSKristof Provost 	hdr->nlmsg_flags |= NLM_F_MULTI;
1077f92d9b1aSKristof Provost 
1078f92d9b1aSKristof Provost 	NET_EPOCH_ENTER(et);
1079f92d9b1aSKristof Provost 	CK_LIST_FOREACH(sc, &V_pflowif_list, sc_next) {
1080f92d9b1aSKristof Provost 		if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
1081f92d9b1aSKristof Provost 			error = ENOMEM;
1082f92d9b1aSKristof Provost 			goto out;
1083f92d9b1aSKristof Provost 		}
1084f92d9b1aSKristof Provost 
1085f92d9b1aSKristof Provost 		struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1086f92d9b1aSKristof Provost 		ghdr_new->cmd = PFLOWNL_CMD_LIST;
1087f92d9b1aSKristof Provost 		ghdr_new->version = 0;
1088f92d9b1aSKristof Provost 		ghdr_new->reserved = 0;
1089f92d9b1aSKristof Provost 
1090f92d9b1aSKristof Provost 		nlattr_add_u32(nw, PFLOWNL_L_ID, sc->sc_id);
1091f92d9b1aSKristof Provost 
1092f92d9b1aSKristof Provost 		if (! nlmsg_end(nw)) {
1093f92d9b1aSKristof Provost 			error = ENOMEM;
1094f92d9b1aSKristof Provost 			goto out;
1095f92d9b1aSKristof Provost 		}
1096f92d9b1aSKristof Provost 	}
1097f92d9b1aSKristof Provost 
1098f92d9b1aSKristof Provost out:
1099f92d9b1aSKristof Provost 	NET_EPOCH_EXIT(et);
1100f92d9b1aSKristof Provost 
1101f92d9b1aSKristof Provost 	if (error != 0)
1102f92d9b1aSKristof Provost 		nlmsg_abort(nw);
1103f92d9b1aSKristof Provost 
1104f92d9b1aSKristof Provost 	return (error);
1105f92d9b1aSKristof Provost }
1106f92d9b1aSKristof Provost 
1107f92d9b1aSKristof Provost static int
1108f92d9b1aSKristof Provost pflow_nl_create(struct nlmsghdr *hdr, struct nl_pstate *npt)
1109f92d9b1aSKristof Provost {
1110f92d9b1aSKristof Provost 	struct nl_writer	 *nw = npt->nw;
1111f92d9b1aSKristof Provost 	int			 error = 0;
1112f92d9b1aSKristof Provost 	int			 unit;
1113f92d9b1aSKristof Provost 
1114f92d9b1aSKristof Provost 	if (! nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
1115f92d9b1aSKristof Provost 		return (ENOMEM);
1116f92d9b1aSKristof Provost 	}
1117f92d9b1aSKristof Provost 
1118f92d9b1aSKristof Provost 	struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1119f92d9b1aSKristof Provost 	ghdr_new->cmd = PFLOWNL_CMD_CREATE;
1120f92d9b1aSKristof Provost 	ghdr_new->version = 0;
1121f92d9b1aSKristof Provost 	ghdr_new->reserved = 0;
1122f92d9b1aSKristof Provost 
1123f92d9b1aSKristof Provost 	unit = alloc_unr(V_pflow_unr);
1124f92d9b1aSKristof Provost 
1125f92d9b1aSKristof Provost 	error = pflow_create(unit);
1126f92d9b1aSKristof Provost 	if (error != 0) {
1127f92d9b1aSKristof Provost 		free_unr(V_pflow_unr, unit);
1128f92d9b1aSKristof Provost 		nlmsg_abort(nw);
1129f92d9b1aSKristof Provost 		return (error);
1130f92d9b1aSKristof Provost 	}
1131f92d9b1aSKristof Provost 
1132f92d9b1aSKristof Provost 	nlattr_add_s32(nw, PFLOWNL_CREATE_ID, unit);
1133f92d9b1aSKristof Provost 
1134f92d9b1aSKristof Provost 	if (! nlmsg_end(nw)) {
1135f92d9b1aSKristof Provost 		pflow_destroy(unit, true);
1136f92d9b1aSKristof Provost 		return (ENOMEM);
1137f92d9b1aSKristof Provost 	}
1138f92d9b1aSKristof Provost 
1139f92d9b1aSKristof Provost 	return (0);
1140f92d9b1aSKristof Provost }
1141f92d9b1aSKristof Provost 
1142f92d9b1aSKristof Provost struct pflow_parsed_del {
1143f92d9b1aSKristof Provost 	int id;
1144f92d9b1aSKristof Provost };
1145f92d9b1aSKristof Provost #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1146f92d9b1aSKristof Provost #define	_OUT(_field)	offsetof(struct pflow_parsed_del, _field)
1147f92d9b1aSKristof Provost static const struct nlattr_parser nla_p_del[] = {
1148f92d9b1aSKristof Provost 	{ .type = PFLOWNL_DEL_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
1149f92d9b1aSKristof Provost };
1150f92d9b1aSKristof Provost static const struct nlfield_parser nlf_p_del[] = {};
1151f92d9b1aSKristof Provost #undef _IN
1152f92d9b1aSKristof Provost #undef _OUT
1153f92d9b1aSKristof Provost NL_DECLARE_PARSER(del_parser, struct genlmsghdr, nlf_p_del, nla_p_del);
1154f92d9b1aSKristof Provost 
1155f92d9b1aSKristof Provost static int
1156f92d9b1aSKristof Provost pflow_nl_del(struct nlmsghdr *hdr, struct nl_pstate *npt)
1157f92d9b1aSKristof Provost {
1158f92d9b1aSKristof Provost 	struct pflow_parsed_del d = {};
1159f92d9b1aSKristof Provost 	int error;
1160f92d9b1aSKristof Provost 
1161f92d9b1aSKristof Provost 	error = nl_parse_nlmsg(hdr, &del_parser, npt, &d);
1162f92d9b1aSKristof Provost 	if (error != 0)
1163f92d9b1aSKristof Provost 		return (error);
1164f92d9b1aSKristof Provost 
1165f92d9b1aSKristof Provost 	error = pflow_destroy(d.id, true);
1166f92d9b1aSKristof Provost 
1167f92d9b1aSKristof Provost 	return (error);
1168f92d9b1aSKristof Provost }
1169f92d9b1aSKristof Provost 
1170f92d9b1aSKristof Provost struct pflow_parsed_get {
1171f92d9b1aSKristof Provost 	int id;
1172f92d9b1aSKristof Provost };
1173f92d9b1aSKristof Provost #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1174f92d9b1aSKristof Provost #define	_OUT(_field)	offsetof(struct pflow_parsed_get, _field)
1175f92d9b1aSKristof Provost static const struct nlattr_parser nla_p_get[] = {
1176f92d9b1aSKristof Provost 	{ .type = PFLOWNL_GET_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
1177f92d9b1aSKristof Provost };
1178f92d9b1aSKristof Provost static const struct nlfield_parser nlf_p_get[] = {};
1179f92d9b1aSKristof Provost #undef _IN
1180f92d9b1aSKristof Provost #undef _OUT
1181f92d9b1aSKristof Provost NL_DECLARE_PARSER(get_parser, struct genlmsghdr, nlf_p_get, nla_p_get);
1182f92d9b1aSKristof Provost 
1183f92d9b1aSKristof Provost static bool
1184f92d9b1aSKristof Provost nlattr_add_sockaddr(struct nl_writer *nw, int attr, const struct sockaddr *s)
1185f92d9b1aSKristof Provost {
1186f92d9b1aSKristof Provost 	int off = nlattr_add_nested(nw, attr);
1187f92d9b1aSKristof Provost 	if (off == 0)
1188f92d9b1aSKristof Provost 		return (false);
1189f92d9b1aSKristof Provost 
1190f92d9b1aSKristof Provost 	nlattr_add_u8(nw, PFLOWNL_ADDR_FAMILY, s->sa_family);
1191f92d9b1aSKristof Provost 
1192f92d9b1aSKristof Provost 	switch (s->sa_family) {
1193f92d9b1aSKristof Provost 	case AF_INET: {
1194f92d9b1aSKristof Provost 		const struct sockaddr_in *in = (const struct sockaddr_in *)s;
1195f92d9b1aSKristof Provost 		nlattr_add_u16(nw, PFLOWNL_ADDR_PORT, in->sin_port);
1196f92d9b1aSKristof Provost 		nlattr_add_in_addr(nw, PFLOWNL_ADDR_IP, &in->sin_addr);
1197f92d9b1aSKristof Provost 		break;
1198f92d9b1aSKristof Provost 	}
1199f92d9b1aSKristof Provost 	case AF_INET6: {
1200f92d9b1aSKristof Provost 		const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *)s;
1201f92d9b1aSKristof Provost 		nlattr_add_u16(nw, PFLOWNL_ADDR_PORT, in6->sin6_port);
1202f92d9b1aSKristof Provost 		nlattr_add_in6_addr(nw, PFLOWNL_ADDR_IP6, &in6->sin6_addr);
1203f92d9b1aSKristof Provost 		break;
1204f92d9b1aSKristof Provost 	}
1205f92d9b1aSKristof Provost 	default:
1206f92d9b1aSKristof Provost 		panic("Unknown address family %d", s->sa_family);
1207f92d9b1aSKristof Provost 	}
1208f92d9b1aSKristof Provost 
1209f92d9b1aSKristof Provost 	nlattr_set_len(nw, off);
1210f92d9b1aSKristof Provost 	return (true);
1211f92d9b1aSKristof Provost }
1212f92d9b1aSKristof Provost 
1213f92d9b1aSKristof Provost static int
1214f92d9b1aSKristof Provost pflow_nl_get(struct nlmsghdr *hdr, struct nl_pstate *npt)
1215f92d9b1aSKristof Provost {
1216f92d9b1aSKristof Provost 	struct epoch_tracker et;
1217f92d9b1aSKristof Provost 	struct pflow_parsed_get g = {};
1218f92d9b1aSKristof Provost 	struct pflow_softc *sc = NULL;
1219f92d9b1aSKristof Provost 	struct nl_writer *nw = npt->nw;
1220f92d9b1aSKristof Provost 	struct genlmsghdr *ghdr_new;
1221f92d9b1aSKristof Provost 	int error;
1222f92d9b1aSKristof Provost 
1223f92d9b1aSKristof Provost 	error = nl_parse_nlmsg(hdr, &get_parser, npt, &g);
1224f92d9b1aSKristof Provost 	if (error != 0)
1225f92d9b1aSKristof Provost 		return (error);
1226f92d9b1aSKristof Provost 
1227f92d9b1aSKristof Provost 	NET_EPOCH_ENTER(et);
1228f92d9b1aSKristof Provost 	CK_LIST_FOREACH(sc, &V_pflowif_list, sc_next) {
1229f92d9b1aSKristof Provost 		if (sc->sc_id == g.id)
1230f92d9b1aSKristof Provost 			break;
1231f92d9b1aSKristof Provost 	}
1232f92d9b1aSKristof Provost 	if (sc == NULL) {
1233f92d9b1aSKristof Provost 		error = ENOENT;
1234f92d9b1aSKristof Provost 		goto out;
1235f92d9b1aSKristof Provost 	}
1236f92d9b1aSKristof Provost 
1237f92d9b1aSKristof Provost 	if (! nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
1238f92d9b1aSKristof Provost 		nlmsg_abort(nw);
1239f92d9b1aSKristof Provost 		error = ENOMEM;
1240f92d9b1aSKristof Provost 		goto out;
1241f92d9b1aSKristof Provost 	}
1242f92d9b1aSKristof Provost 
1243f92d9b1aSKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1244f92d9b1aSKristof Provost 	if (ghdr_new == NULL) {
1245f92d9b1aSKristof Provost 		nlmsg_abort(nw);
1246f92d9b1aSKristof Provost 		error = ENOMEM;
1247f92d9b1aSKristof Provost 		goto out;
1248f92d9b1aSKristof Provost 	}
1249f92d9b1aSKristof Provost 
1250f92d9b1aSKristof Provost 	ghdr_new->cmd = PFLOWNL_CMD_GET;
1251f92d9b1aSKristof Provost 	ghdr_new->version = 0;
1252f92d9b1aSKristof Provost 	ghdr_new->reserved = 0;
1253f92d9b1aSKristof Provost 
1254f92d9b1aSKristof Provost 	nlattr_add_u32(nw, PFLOWNL_GET_ID, sc->sc_id);
1255f92d9b1aSKristof Provost 	nlattr_add_u16(nw, PFLOWNL_GET_VERSION, sc->sc_version);
1256f92d9b1aSKristof Provost 	if (sc->sc_flowsrc)
1257f92d9b1aSKristof Provost 		nlattr_add_sockaddr(nw, PFLOWNL_GET_SRC, sc->sc_flowsrc);
1258f92d9b1aSKristof Provost 	if (sc->sc_flowdst)
1259f92d9b1aSKristof Provost 		nlattr_add_sockaddr(nw, PFLOWNL_GET_DST, sc->sc_flowdst);
1260f92d9b1aSKristof Provost 
1261f92d9b1aSKristof Provost 	if (! nlmsg_end(nw)) {
1262f92d9b1aSKristof Provost 		nlmsg_abort(nw);
1263f92d9b1aSKristof Provost 		error = ENOMEM;
1264f92d9b1aSKristof Provost 	}
1265f92d9b1aSKristof Provost 
1266f92d9b1aSKristof Provost out:
1267f92d9b1aSKristof Provost 	NET_EPOCH_EXIT(et);
1268f92d9b1aSKristof Provost 
1269f92d9b1aSKristof Provost 	return (error);
1270f92d9b1aSKristof Provost }
1271f92d9b1aSKristof Provost 
1272f92d9b1aSKristof Provost struct pflow_sockaddr {
1273f92d9b1aSKristof Provost 	union {
1274f92d9b1aSKristof Provost 		struct sockaddr_in in;
1275f92d9b1aSKristof Provost 		struct sockaddr_in6 in6;
1276f92d9b1aSKristof Provost 		struct sockaddr_storage storage;
1277f92d9b1aSKristof Provost 	};
1278f92d9b1aSKristof Provost };
1279f92d9b1aSKristof Provost static bool
1280f92d9b1aSKristof Provost pflow_postparse_sockaddr(void *parsed_args, struct nl_pstate *npt __unused)
1281f92d9b1aSKristof Provost {
1282f92d9b1aSKristof Provost 	struct pflow_sockaddr *s = (struct pflow_sockaddr *)parsed_args;
1283f92d9b1aSKristof Provost 
1284f92d9b1aSKristof Provost 	if (s->storage.ss_family == AF_INET)
1285f92d9b1aSKristof Provost 		s->storage.ss_len = sizeof(struct sockaddr_in);
1286f92d9b1aSKristof Provost 	else if (s->storage.ss_family == AF_INET6)
1287f92d9b1aSKristof Provost 		s->storage.ss_len = sizeof(struct sockaddr_in6);
1288f92d9b1aSKristof Provost 	else
1289f92d9b1aSKristof Provost 		return (false);
1290f92d9b1aSKristof Provost 
1291f92d9b1aSKristof Provost 	return (true);
1292f92d9b1aSKristof Provost }
1293f92d9b1aSKristof Provost 
1294f92d9b1aSKristof Provost #define	_OUT(_field)	offsetof(struct pflow_sockaddr, _field)
1295f92d9b1aSKristof Provost static struct nlattr_parser nla_p_sockaddr[] = {
1296f92d9b1aSKristof Provost 	{ .type = PFLOWNL_ADDR_FAMILY, .off = _OUT(in.sin_family), .cb = nlattr_get_uint8 },
1297f92d9b1aSKristof Provost 	{ .type = PFLOWNL_ADDR_PORT, .off = _OUT(in.sin_port), .cb = nlattr_get_uint16 },
1298f92d9b1aSKristof Provost 	{ .type = PFLOWNL_ADDR_IP, .off = _OUT(in.sin_addr), .cb = nlattr_get_in_addr },
1299f92d9b1aSKristof Provost 	{ .type = PFLOWNL_ADDR_IP6, .off = _OUT(in6.sin6_addr), .cb = nlattr_get_in6_addr },
1300f92d9b1aSKristof Provost };
1301f92d9b1aSKristof Provost NL_DECLARE_ATTR_PARSER_EXT(addr_parser, nla_p_sockaddr, pflow_postparse_sockaddr);
1302f92d9b1aSKristof Provost #undef _OUT
1303f92d9b1aSKristof Provost 
1304f92d9b1aSKristof Provost struct pflow_parsed_set {
1305f92d9b1aSKristof Provost 	int id;
1306f92d9b1aSKristof Provost 	uint16_t version;
1307f92d9b1aSKristof Provost 	struct sockaddr_storage src;
1308f92d9b1aSKristof Provost 	struct sockaddr_storage dst;
1309f92d9b1aSKristof Provost };
1310f92d9b1aSKristof Provost #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1311f92d9b1aSKristof Provost #define	_OUT(_field)	offsetof(struct pflow_parsed_set, _field)
1312f92d9b1aSKristof Provost static const struct nlattr_parser nla_p_set[] = {
1313f92d9b1aSKristof Provost 	{ .type = PFLOWNL_SET_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
1314f92d9b1aSKristof Provost 	{ .type = PFLOWNL_SET_VERSION, .off = _OUT(version), .cb = nlattr_get_uint16 },
1315f92d9b1aSKristof Provost 	{ .type = PFLOWNL_SET_SRC, .off = _OUT(src), .arg = &addr_parser, .cb = nlattr_get_nested },
1316f92d9b1aSKristof Provost 	{ .type = PFLOWNL_SET_DST, .off = _OUT(dst), .arg = &addr_parser, .cb = nlattr_get_nested },
1317f92d9b1aSKristof Provost };
1318f92d9b1aSKristof Provost static const struct nlfield_parser nlf_p_set[] = {};
1319f92d9b1aSKristof Provost #undef _IN
1320f92d9b1aSKristof Provost #undef _OUT
1321f92d9b1aSKristof Provost NL_DECLARE_PARSER(set_parser, struct genlmsghdr, nlf_p_set, nla_p_set);
1322f92d9b1aSKristof Provost 
1323f92d9b1aSKristof Provost static int
1324f92d9b1aSKristof Provost pflow_set(struct pflow_softc *sc, const struct pflow_parsed_set *pflowr, struct ucred *cred)
1325f92d9b1aSKristof Provost {
1326f92d9b1aSKristof Provost 	struct thread		*td;
1327f92d9b1aSKristof Provost 	struct socket		*so;
1328f92d9b1aSKristof Provost 	int			 error = 0;
1329f92d9b1aSKristof Provost 
1330f92d9b1aSKristof Provost 	td = curthread;
1331f92d9b1aSKristof Provost 
1332f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
1333f92d9b1aSKristof Provost 
1334f92d9b1aSKristof Provost 	if (pflowr->version != 0) {
1335f92d9b1aSKristof Provost 		switch(pflowr->version) {
1336f92d9b1aSKristof Provost 		case PFLOW_PROTO_5:
1337f92d9b1aSKristof Provost 		case PFLOW_PROTO_10:
1338f92d9b1aSKristof Provost 			break;
1339f92d9b1aSKristof Provost 		default:
1340f92d9b1aSKristof Provost 			return(EINVAL);
1341f92d9b1aSKristof Provost 		}
1342f92d9b1aSKristof Provost 	}
1343f92d9b1aSKristof Provost 
1344f92d9b1aSKristof Provost 	pflow_flush(sc);
1345f92d9b1aSKristof Provost 
1346f92d9b1aSKristof Provost 	if (pflowr->dst.ss_len != 0) {
1347f92d9b1aSKristof Provost 		if (sc->sc_flowdst != NULL &&
1348f92d9b1aSKristof Provost 		    sc->sc_flowdst->sa_family != pflowr->dst.ss_family) {
1349f92d9b1aSKristof Provost 			free(sc->sc_flowdst, M_DEVBUF);
1350f92d9b1aSKristof Provost 			sc->sc_flowdst = NULL;
1351f92d9b1aSKristof Provost 			if (sc->so != NULL) {
1352f92d9b1aSKristof Provost 				soclose(sc->so);
1353f92d9b1aSKristof Provost 				sc->so = NULL;
1354f92d9b1aSKristof Provost 			}
1355f92d9b1aSKristof Provost 		}
1356f92d9b1aSKristof Provost 
1357f92d9b1aSKristof Provost 		switch (pflowr->dst.ss_family) {
1358f92d9b1aSKristof Provost 		case AF_INET:
1359f92d9b1aSKristof Provost 			if (sc->sc_flowdst == NULL) {
1360f92d9b1aSKristof Provost 				if ((sc->sc_flowdst = malloc(
1361f92d9b1aSKristof Provost 				    sizeof(struct sockaddr_in),
1362f92d9b1aSKristof Provost 				    M_DEVBUF,  M_NOWAIT)) == NULL)
1363f92d9b1aSKristof Provost 					return (ENOMEM);
1364f92d9b1aSKristof Provost 			}
1365f92d9b1aSKristof Provost 			memcpy(sc->sc_flowdst, &pflowr->dst,
1366f92d9b1aSKristof Provost 			    sizeof(struct sockaddr_in));
1367f92d9b1aSKristof Provost 			sc->sc_flowdst->sa_len = sizeof(struct
1368f92d9b1aSKristof Provost 			    sockaddr_in);
1369f92d9b1aSKristof Provost 			break;
1370f92d9b1aSKristof Provost 		case AF_INET6:
1371f92d9b1aSKristof Provost 			if (sc->sc_flowdst == NULL) {
1372f92d9b1aSKristof Provost 				if ((sc->sc_flowdst = malloc(
1373f92d9b1aSKristof Provost 				    sizeof(struct sockaddr_in6),
1374f92d9b1aSKristof Provost 				    M_DEVBUF, M_NOWAIT)) == NULL)
1375f92d9b1aSKristof Provost 					return (ENOMEM);
1376f92d9b1aSKristof Provost 			}
1377f92d9b1aSKristof Provost 			memcpy(sc->sc_flowdst, &pflowr->dst,
1378f92d9b1aSKristof Provost 			    sizeof(struct sockaddr_in6));
1379f92d9b1aSKristof Provost 			sc->sc_flowdst->sa_len = sizeof(struct
1380f92d9b1aSKristof Provost 			    sockaddr_in6);
1381f92d9b1aSKristof Provost 			break;
1382f92d9b1aSKristof Provost 		default:
1383f92d9b1aSKristof Provost 			break;
1384f92d9b1aSKristof Provost 		}
1385f92d9b1aSKristof Provost 	}
1386f92d9b1aSKristof Provost 
1387f92d9b1aSKristof Provost 	if (pflowr->src.ss_len != 0) {
1388f92d9b1aSKristof Provost 		if (sc->sc_flowsrc != NULL)
1389f92d9b1aSKristof Provost 			free(sc->sc_flowsrc, M_DEVBUF);
1390f92d9b1aSKristof Provost 		sc->sc_flowsrc = NULL;
1391f92d9b1aSKristof Provost 		if (sc->so != NULL) {
1392f92d9b1aSKristof Provost 			soclose(sc->so);
1393f92d9b1aSKristof Provost 			sc->so = NULL;
1394f92d9b1aSKristof Provost 		}
1395f92d9b1aSKristof Provost 		switch(pflowr->src.ss_family) {
1396f92d9b1aSKristof Provost 		case AF_INET:
1397f92d9b1aSKristof Provost 			if ((sc->sc_flowsrc = malloc(
1398f92d9b1aSKristof Provost 			    sizeof(struct sockaddr_in),
1399f92d9b1aSKristof Provost 			    M_DEVBUF, M_NOWAIT)) == NULL)
1400f92d9b1aSKristof Provost 				return (ENOMEM);
1401f92d9b1aSKristof Provost 			memcpy(sc->sc_flowsrc, &pflowr->src,
1402f92d9b1aSKristof Provost 			    sizeof(struct sockaddr_in));
1403f92d9b1aSKristof Provost 			sc->sc_flowsrc->sa_len = sizeof(struct
1404f92d9b1aSKristof Provost 			    sockaddr_in);
1405f92d9b1aSKristof Provost 			break;
1406f92d9b1aSKristof Provost 		case AF_INET6:
1407f92d9b1aSKristof Provost 			if ((sc->sc_flowsrc = malloc(
1408f92d9b1aSKristof Provost 			    sizeof(struct sockaddr_in6),
1409f92d9b1aSKristof Provost 			    M_DEVBUF, M_NOWAIT)) == NULL)
1410f92d9b1aSKristof Provost 				return (ENOMEM);
1411f92d9b1aSKristof Provost 			memcpy(sc->sc_flowsrc, &pflowr->src,
1412f92d9b1aSKristof Provost 			    sizeof(struct sockaddr_in6));
1413f92d9b1aSKristof Provost 			sc->sc_flowsrc->sa_len = sizeof(struct
1414f92d9b1aSKristof Provost 			    sockaddr_in6);
1415f92d9b1aSKristof Provost 			break;
1416f92d9b1aSKristof Provost 		default:
1417f92d9b1aSKristof Provost 			break;
1418f92d9b1aSKristof Provost 		}
1419f92d9b1aSKristof Provost 	}
1420f92d9b1aSKristof Provost 
1421f92d9b1aSKristof Provost 	if (sc->so == NULL) {
1422f92d9b1aSKristof Provost 		if (pflowvalidsockaddr(sc->sc_flowdst, 0)) {
1423f92d9b1aSKristof Provost 			error = socreate(sc->sc_flowdst->sa_family,
1424f92d9b1aSKristof Provost 			    &so, SOCK_DGRAM, IPPROTO_UDP, cred, td);
1425f92d9b1aSKristof Provost 			if (error)
1426f92d9b1aSKristof Provost 				return (error);
1427f92d9b1aSKristof Provost 			if (pflowvalidsockaddr(sc->sc_flowsrc, 1)) {
1428f92d9b1aSKristof Provost 				error = sobind(so, sc->sc_flowsrc, td);
1429f92d9b1aSKristof Provost 				if (error) {
1430f92d9b1aSKristof Provost 					soclose(so);
1431f92d9b1aSKristof Provost 					return (error);
1432f92d9b1aSKristof Provost 				}
1433f92d9b1aSKristof Provost 			}
1434f92d9b1aSKristof Provost 			sc->so = so;
1435f92d9b1aSKristof Provost 		}
1436f92d9b1aSKristof Provost 	} else if (!pflowvalidsockaddr(sc->sc_flowdst, 0)) {
1437f92d9b1aSKristof Provost 		soclose(sc->so);
1438f92d9b1aSKristof Provost 		sc->so = NULL;
1439f92d9b1aSKristof Provost 	}
1440f92d9b1aSKristof Provost 
1441f92d9b1aSKristof Provost 	/* error check is above */
1442f92d9b1aSKristof Provost 	if (pflowr->version != 0)
1443f92d9b1aSKristof Provost 		sc->sc_version = pflowr->version;
1444f92d9b1aSKristof Provost 
1445f92d9b1aSKristof Provost 	pflow_setmtu(sc, ETHERMTU);
1446f92d9b1aSKristof Provost 
1447f92d9b1aSKristof Provost 	switch (sc->sc_version) {
1448f92d9b1aSKristof Provost 	case PFLOW_PROTO_5:
1449f92d9b1aSKristof Provost 		callout_stop(&sc->sc_tmo6);
1450f92d9b1aSKristof Provost 		callout_stop(&sc->sc_tmo_tmpl);
1451f92d9b1aSKristof Provost 		break;
1452f92d9b1aSKristof Provost 	case PFLOW_PROTO_10:
1453f92d9b1aSKristof Provost 		callout_reset(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT * hz,
1454f92d9b1aSKristof Provost 		    pflow_timeout_tmpl, sc);
1455f92d9b1aSKristof Provost 		break;
1456f92d9b1aSKristof Provost 	default: /* NOTREACHED */
1457f92d9b1aSKristof Provost 		break;
1458f92d9b1aSKristof Provost 	}
1459f92d9b1aSKristof Provost 
1460f92d9b1aSKristof Provost 	return (0);
1461f92d9b1aSKristof Provost }
1462f92d9b1aSKristof Provost 
1463f92d9b1aSKristof Provost static int
1464f92d9b1aSKristof Provost pflow_nl_set(struct nlmsghdr *hdr, struct nl_pstate *npt)
1465f92d9b1aSKristof Provost {
1466f92d9b1aSKristof Provost 	struct epoch_tracker et;
1467f92d9b1aSKristof Provost 	struct pflow_parsed_set s = {};
1468f92d9b1aSKristof Provost 	struct pflow_softc *sc = NULL;
1469f92d9b1aSKristof Provost 	int error;
1470f92d9b1aSKristof Provost 
1471f92d9b1aSKristof Provost 	error = nl_parse_nlmsg(hdr, &set_parser, npt, &s);
1472f92d9b1aSKristof Provost 	if (error != 0)
1473f92d9b1aSKristof Provost 		return (error);
1474f92d9b1aSKristof Provost 
1475f92d9b1aSKristof Provost 	NET_EPOCH_ENTER(et);
1476f92d9b1aSKristof Provost 	CK_LIST_FOREACH(sc, &V_pflowif_list, sc_next) {
1477f92d9b1aSKristof Provost 		if (sc->sc_id == s.id)
1478f92d9b1aSKristof Provost 			break;
1479f92d9b1aSKristof Provost 	}
1480f92d9b1aSKristof Provost 	if (sc == NULL) {
1481f92d9b1aSKristof Provost 		error = ENOENT;
1482f92d9b1aSKristof Provost 		goto out;
1483f92d9b1aSKristof Provost 	}
1484f92d9b1aSKristof Provost 
1485f92d9b1aSKristof Provost 	PFLOW_LOCK(sc);
1486f92d9b1aSKristof Provost 	error = pflow_set(sc, &s, nlp_get_cred(npt->nlp));
1487f92d9b1aSKristof Provost 	PFLOW_UNLOCK(sc);
1488f92d9b1aSKristof Provost 
1489f92d9b1aSKristof Provost out:
1490f92d9b1aSKristof Provost 	NET_EPOCH_EXIT(et);
1491f92d9b1aSKristof Provost 	return (error);
1492f92d9b1aSKristof Provost }
1493f92d9b1aSKristof Provost 
1494f92d9b1aSKristof Provost static const struct genl_cmd pflow_cmds[] = {
1495f92d9b1aSKristof Provost 	{
1496f92d9b1aSKristof Provost 		.cmd_num = PFLOWNL_CMD_LIST,
1497f92d9b1aSKristof Provost 		.cmd_name = "LIST",
1498f92d9b1aSKristof Provost 		.cmd_cb = pflow_nl_list,
1499f92d9b1aSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1500f92d9b1aSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1501f92d9b1aSKristof Provost 	},
1502f92d9b1aSKristof Provost 	{
1503f92d9b1aSKristof Provost 		.cmd_num = PFLOWNL_CMD_CREATE,
1504f92d9b1aSKristof Provost 		.cmd_name = "CREATE",
1505f92d9b1aSKristof Provost 		.cmd_cb = pflow_nl_create,
1506f92d9b1aSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1507f92d9b1aSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1508f92d9b1aSKristof Provost 	},
1509f92d9b1aSKristof Provost 	{
1510f92d9b1aSKristof Provost 		.cmd_num = PFLOWNL_CMD_DEL,
1511f92d9b1aSKristof Provost 		.cmd_name = "DEL",
1512f92d9b1aSKristof Provost 		.cmd_cb = pflow_nl_del,
1513f92d9b1aSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1514f92d9b1aSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1515f92d9b1aSKristof Provost 	},
1516f92d9b1aSKristof Provost 	{
1517f92d9b1aSKristof Provost 		.cmd_num = PFLOWNL_CMD_GET,
1518f92d9b1aSKristof Provost 		.cmd_name = "GET",
1519f92d9b1aSKristof Provost 		.cmd_cb = pflow_nl_get,
1520f92d9b1aSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1521f92d9b1aSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1522f92d9b1aSKristof Provost 	},
1523f92d9b1aSKristof Provost 	{
1524f92d9b1aSKristof Provost 		.cmd_num = PFLOWNL_CMD_SET,
1525f92d9b1aSKristof Provost 		.cmd_name = "SET",
1526f92d9b1aSKristof Provost 		.cmd_cb = pflow_nl_set,
1527f92d9b1aSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1528f92d9b1aSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1529f92d9b1aSKristof Provost 	},
1530f92d9b1aSKristof Provost };
1531f92d9b1aSKristof Provost 
1532f92d9b1aSKristof Provost static const struct nlhdr_parser *all_parsers[] = {
1533f92d9b1aSKristof Provost 	&del_parser,
1534f92d9b1aSKristof Provost 	&get_parser,
1535f92d9b1aSKristof Provost 	&set_parser,
1536f92d9b1aSKristof Provost };
1537f92d9b1aSKristof Provost 
1538f92d9b1aSKristof Provost static int
1539f92d9b1aSKristof Provost pflow_init(void)
1540f92d9b1aSKristof Provost {
1541f92d9b1aSKristof Provost 	bool ret;
1542f92d9b1aSKristof Provost 	int family_id __diagused;
1543f92d9b1aSKristof Provost 
1544f92d9b1aSKristof Provost 	NL_VERIFY_PARSERS(all_parsers);
1545f92d9b1aSKristof Provost 
1546f92d9b1aSKristof Provost 	family_id = genl_register_family(PFLOWNL_FAMILY_NAME, 0, 2, PFLOWNL_CMD_MAX);
1547f92d9b1aSKristof Provost 	MPASS(family_id != 0);
1548f92d9b1aSKristof Provost 	ret = genl_register_cmds(PFLOWNL_FAMILY_NAME, pflow_cmds, NL_ARRAY_LEN(pflow_cmds));
1549f92d9b1aSKristof Provost 
1550f92d9b1aSKristof Provost 	return (ret ? 0 : ENODEV);
1551f92d9b1aSKristof Provost }
1552f92d9b1aSKristof Provost 
1553f92d9b1aSKristof Provost static void
1554f92d9b1aSKristof Provost pflow_uninit(void)
1555f92d9b1aSKristof Provost {
1556f92d9b1aSKristof Provost 	genl_unregister_family(PFLOWNL_FAMILY_NAME);
1557f92d9b1aSKristof Provost }
1558f92d9b1aSKristof Provost 
1559f92d9b1aSKristof Provost static int
1560f92d9b1aSKristof Provost pflow_modevent(module_t mod, int type, void *data)
1561f92d9b1aSKristof Provost {
1562f92d9b1aSKristof Provost 	int error = 0;
1563f92d9b1aSKristof Provost 
1564f92d9b1aSKristof Provost 	switch (type) {
1565f92d9b1aSKristof Provost 	case MOD_LOAD:
1566f92d9b1aSKristof Provost 		error = pflow_init();
1567f92d9b1aSKristof Provost 		break;
1568f92d9b1aSKristof Provost 	case MOD_UNLOAD:
1569f92d9b1aSKristof Provost 		pflow_uninit();
1570f92d9b1aSKristof Provost 		break;
1571f92d9b1aSKristof Provost 	default:
1572f92d9b1aSKristof Provost 		error = EINVAL;
1573f92d9b1aSKristof Provost 		break;
1574f92d9b1aSKristof Provost 	}
1575f92d9b1aSKristof Provost 
1576f92d9b1aSKristof Provost 	return (error);
1577f92d9b1aSKristof Provost }
1578f92d9b1aSKristof Provost 
1579f92d9b1aSKristof Provost static moduledata_t pflow_mod = {
1580f92d9b1aSKristof Provost 	pflowname,
1581f92d9b1aSKristof Provost 	pflow_modevent,
1582f92d9b1aSKristof Provost 	0
1583f92d9b1aSKristof Provost };
1584f92d9b1aSKristof Provost 
1585f92d9b1aSKristof Provost DECLARE_MODULE(pflow, pflow_mod, SI_SUB_PROTO_FIREWALL, SI_ORDER_ANY);
1586f92d9b1aSKristof Provost MODULE_VERSION(pflow, 1);
1587f92d9b1aSKristof Provost MODULE_DEPEND(pflow, pf, PF_MODVER, PF_MODVER, PF_MODVER);
1588