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