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