xref: /freebsd/sys/netpfil/pf/pflow.c (revision 221d459fbc67e0c0565d6c6ea52fe8bbc5466fc7)
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 
753*221d459fSKristof Provost 	/* e.g. if pf_state_key_attach() fails. */
754*221d459fSKristof Provost 	if (st->key[PF_SK_STACK] == NULL || st->key[PF_SK_WIRE] == NULL)
755*221d459fSKristof Provost 		return;
756*221d459fSKristof Provost 
757f92d9b1aSKristof Provost 	sk = st->key[st->direction == PF_IN ? PF_SK_WIRE : PF_SK_STACK];
758f92d9b1aSKristof Provost 
759f92d9b1aSKristof Provost 	CK_LIST_FOREACH(sc, &V_pflowif_list, sc_next) {
760f92d9b1aSKristof Provost 		PFLOW_LOCK(sc);
761f92d9b1aSKristof Provost 		switch (sc->sc_version) {
762f92d9b1aSKristof Provost 		case PFLOW_PROTO_5:
763f92d9b1aSKristof Provost 			if (sk->af == AF_INET)
764f92d9b1aSKristof Provost 				export_pflow_if(st, sk, sc);
765f92d9b1aSKristof Provost 			break;
766f92d9b1aSKristof Provost 		case PFLOW_PROTO_10:
767f92d9b1aSKristof Provost 			if (sk->af == AF_INET || sk->af == AF_INET6)
768f92d9b1aSKristof Provost 				export_pflow_if(st, sk, sc);
769f92d9b1aSKristof Provost 			break;
770f92d9b1aSKristof Provost 		default: /* NOTREACHED */
771f92d9b1aSKristof Provost 			break;
772f92d9b1aSKristof Provost 		}
773f92d9b1aSKristof Provost 		PFLOW_UNLOCK(sc);
774f92d9b1aSKristof Provost 	}
775f92d9b1aSKristof Provost }
776f92d9b1aSKristof Provost 
777f92d9b1aSKristof Provost static int
778baf9b6d0SKristof Provost export_pflow_if(const struct pf_kstate *st, struct pf_state_key *sk,
779f92d9b1aSKristof Provost     struct pflow_softc *sc)
780f92d9b1aSKristof Provost {
781f92d9b1aSKristof Provost 	struct pf_kstate	 pfs_copy;
782f92d9b1aSKristof Provost 	u_int64_t		 bytes[2];
783f92d9b1aSKristof Provost 	int			 ret = 0;
784f92d9b1aSKristof Provost 
785f92d9b1aSKristof Provost 	if (sc->sc_version == PFLOW_PROTO_10)
786f92d9b1aSKristof Provost 		return (pflow_pack_flow_ipfix(st, sk, sc));
787f92d9b1aSKristof Provost 
788f92d9b1aSKristof Provost 	/* PFLOW_PROTO_5 */
789f92d9b1aSKristof Provost 	if ((st->bytes[0] < (u_int64_t)PFLOW_MAXBYTES)
790f92d9b1aSKristof Provost 	    && (st->bytes[1] < (u_int64_t)PFLOW_MAXBYTES))
791f92d9b1aSKristof Provost 		return (pflow_pack_flow(st, sk, sc));
792f92d9b1aSKristof Provost 
793f92d9b1aSKristof Provost 	/* flow > PFLOW_MAXBYTES need special handling */
794f92d9b1aSKristof Provost 	bcopy(st, &pfs_copy, sizeof(pfs_copy));
795f92d9b1aSKristof Provost 	bytes[0] = pfs_copy.bytes[0];
796f92d9b1aSKristof Provost 	bytes[1] = pfs_copy.bytes[1];
797f92d9b1aSKristof Provost 
798f92d9b1aSKristof Provost 	while (bytes[0] > PFLOW_MAXBYTES) {
799f92d9b1aSKristof Provost 		pfs_copy.bytes[0] = PFLOW_MAXBYTES;
800f92d9b1aSKristof Provost 		pfs_copy.bytes[1] = 0;
801f92d9b1aSKristof Provost 
802f92d9b1aSKristof Provost 		if ((ret = pflow_pack_flow(&pfs_copy, sk, sc)) != 0)
803f92d9b1aSKristof Provost 			return (ret);
804f92d9b1aSKristof Provost 		if ((bytes[0] - PFLOW_MAXBYTES) > 0)
805f92d9b1aSKristof Provost 			bytes[0] -= PFLOW_MAXBYTES;
806f92d9b1aSKristof Provost 	}
807f92d9b1aSKristof Provost 
808f92d9b1aSKristof Provost 	while (bytes[1] > (u_int64_t)PFLOW_MAXBYTES) {
809f92d9b1aSKristof Provost 		pfs_copy.bytes[1] = PFLOW_MAXBYTES;
810f92d9b1aSKristof Provost 		pfs_copy.bytes[0] = 0;
811f92d9b1aSKristof Provost 
812f92d9b1aSKristof Provost 		if ((ret = pflow_pack_flow(&pfs_copy, sk, sc)) != 0)
813f92d9b1aSKristof Provost 			return (ret);
814f92d9b1aSKristof Provost 		if ((bytes[1] - PFLOW_MAXBYTES) > 0)
815f92d9b1aSKristof Provost 			bytes[1] -= PFLOW_MAXBYTES;
816f92d9b1aSKristof Provost 	}
817f92d9b1aSKristof Provost 
818f92d9b1aSKristof Provost 	pfs_copy.bytes[0] = bytes[0];
819f92d9b1aSKristof Provost 	pfs_copy.bytes[1] = bytes[1];
820f92d9b1aSKristof Provost 
821f92d9b1aSKristof Provost 	return (pflow_pack_flow(&pfs_copy, sk, sc));
822f92d9b1aSKristof Provost }
823f92d9b1aSKristof Provost 
824f92d9b1aSKristof Provost static int
825f92d9b1aSKristof Provost copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc)
826f92d9b1aSKristof Provost {
827f92d9b1aSKristof Provost 	int		ret = 0;
828f92d9b1aSKristof Provost 
829f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
830f92d9b1aSKristof Provost 
831f92d9b1aSKristof Provost 	if (sc->sc_mbuf == NULL) {
832f92d9b1aSKristof Provost 		if ((sc->sc_mbuf = pflow_get_mbuf(sc, 0)) == NULL)
833f92d9b1aSKristof Provost 			return (ENOBUFS);
834f92d9b1aSKristof Provost 	}
835f92d9b1aSKristof Provost 	m_copyback(sc->sc_mbuf, PFLOW_HDRLEN +
836f92d9b1aSKristof Provost 	    (sc->sc_count * sizeof(struct pflow_flow)),
837f92d9b1aSKristof Provost 	    sizeof(struct pflow_flow), (caddr_t)flow);
838f92d9b1aSKristof Provost 
8392be6f757SKristof Provost 	pflowstat_inc(pflow_flows);
840f92d9b1aSKristof Provost 	sc->sc_gcounter++;
841f92d9b1aSKristof Provost 	sc->sc_count++;
842f92d9b1aSKristof Provost 
843f92d9b1aSKristof Provost 	if (sc->sc_count >= sc->sc_maxcount)
844f92d9b1aSKristof Provost 		ret = pflow_sendout_v5(sc);
845f92d9b1aSKristof Provost 
846f92d9b1aSKristof Provost 	return(ret);
847f92d9b1aSKristof Provost }
848f92d9b1aSKristof Provost 
849f92d9b1aSKristof Provost static int
850f92d9b1aSKristof Provost copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow, struct pflow_softc *sc)
851f92d9b1aSKristof Provost {
852f92d9b1aSKristof Provost 	int		ret = 0;
853f92d9b1aSKristof Provost 
854f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
855f92d9b1aSKristof Provost 
856f92d9b1aSKristof Provost 	if (sc->sc_mbuf == NULL) {
857f92d9b1aSKristof Provost 		if ((sc->sc_mbuf =
858f92d9b1aSKristof Provost 		    pflow_get_mbuf(sc, PFLOW_IPFIX_TMPL_IPV4_ID)) == NULL) {
859f92d9b1aSKristof Provost 			return (ENOBUFS);
860f92d9b1aSKristof Provost 		}
861f92d9b1aSKristof Provost 		sc->sc_count4 = 0;
862f92d9b1aSKristof Provost 		callout_reset(&sc->sc_tmo, PFLOW_TIMEOUT * hz,
863f92d9b1aSKristof Provost 		    pflow_timeout, sc);
864f92d9b1aSKristof Provost 	}
865f92d9b1aSKristof Provost 	m_copyback(sc->sc_mbuf, PFLOW_SET_HDRLEN +
866f92d9b1aSKristof Provost 	    (sc->sc_count4 * sizeof(struct pflow_ipfix_flow4)),
867f92d9b1aSKristof Provost 	    sizeof(struct pflow_ipfix_flow4), (caddr_t)flow);
868f92d9b1aSKristof Provost 
8692be6f757SKristof Provost 	pflowstat_inc(pflow_flows);
870f92d9b1aSKristof Provost 	sc->sc_gcounter++;
871f92d9b1aSKristof Provost 	sc->sc_count4++;
872f92d9b1aSKristof Provost 
873f92d9b1aSKristof Provost 	if (sc->sc_count4 >= sc->sc_maxcount4)
874fc6e5069SKristof Provost 		ret = pflow_sendout_ipfix(sc, PFLOW_INET);
875f92d9b1aSKristof Provost 	return(ret);
876f92d9b1aSKristof Provost }
877f92d9b1aSKristof Provost 
878f92d9b1aSKristof Provost static int
879f92d9b1aSKristof Provost copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow, struct pflow_softc *sc)
880f92d9b1aSKristof Provost {
881f92d9b1aSKristof Provost 	int		ret = 0;
882f92d9b1aSKristof Provost 
883f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
884f92d9b1aSKristof Provost 
885f92d9b1aSKristof Provost 	if (sc->sc_mbuf6 == NULL) {
886f92d9b1aSKristof Provost 		if ((sc->sc_mbuf6 =
887f92d9b1aSKristof Provost 		    pflow_get_mbuf(sc, PFLOW_IPFIX_TMPL_IPV6_ID)) == NULL) {
888f92d9b1aSKristof Provost 			return (ENOBUFS);
889f92d9b1aSKristof Provost 		}
890f92d9b1aSKristof Provost 		sc->sc_count6 = 0;
891f92d9b1aSKristof Provost 		callout_reset(&sc->sc_tmo6, PFLOW_TIMEOUT * hz,
892f92d9b1aSKristof Provost 		    pflow_timeout6, sc);
893f92d9b1aSKristof Provost 	}
894f92d9b1aSKristof Provost 	m_copyback(sc->sc_mbuf6, PFLOW_SET_HDRLEN +
895f92d9b1aSKristof Provost 	    (sc->sc_count6 * sizeof(struct pflow_ipfix_flow6)),
896f92d9b1aSKristof Provost 	    sizeof(struct pflow_ipfix_flow6), (caddr_t)flow);
897f92d9b1aSKristof Provost 
8982be6f757SKristof Provost 	pflowstat_inc(pflow_flows);
899f92d9b1aSKristof Provost 	sc->sc_gcounter++;
900f92d9b1aSKristof Provost 	sc->sc_count6++;
901f92d9b1aSKristof Provost 
902f92d9b1aSKristof Provost 	if (sc->sc_count6 >= sc->sc_maxcount6)
903fc6e5069SKristof Provost 		ret = pflow_sendout_ipfix(sc, PFLOW_INET6);
904fc6e5069SKristof Provost 
905fc6e5069SKristof Provost 	return(ret);
906fc6e5069SKristof Provost }
907fc6e5069SKristof Provost 
908fc6e5069SKristof Provost int
909fc6e5069SKristof Provost copy_nat_ipfix_4_to_m(struct pflow_ipfix_nat4 *nat, const struct pf_kstate *st,
910fc6e5069SKristof Provost     struct pflow_softc *sc, uint8_t event, uint64_t timestamp)
911fc6e5069SKristof Provost {
912fc6e5069SKristof Provost 	int		ret = 0;
913fc6e5069SKristof Provost 
914fc6e5069SKristof Provost 	PFLOW_ASSERT(sc);
915fc6e5069SKristof Provost 
916fc6e5069SKristof Provost 	if (sc->sc_mbuf_nat4 == NULL) {
917fc6e5069SKristof Provost 		if ((sc->sc_mbuf_nat4 =
918fc6e5069SKristof Provost 		    pflow_get_mbuf(sc, PFLOW_IPFIX_TMPL_NAT44_ID)) == NULL) {
919fc6e5069SKristof Provost 			return (ENOBUFS);
920fc6e5069SKristof Provost 		}
921fc6e5069SKristof Provost 		sc->sc_count_nat4 = 0;
922fc6e5069SKristof Provost 		callout_reset(&sc->sc_tmo, PFLOW_TIMEOUT * hz,
923fc6e5069SKristof Provost 		    pflow_timeout_nat4, sc);
924fc6e5069SKristof Provost 	}
925fc6e5069SKristof Provost 
926fc6e5069SKristof Provost 	nat->nat_event = event;
927fc6e5069SKristof Provost 	nat->timestamp = htobe64(pf_get_time() - (pf_get_uptime() - timestamp));
928fc6e5069SKristof Provost 	m_copyback(sc->sc_mbuf_nat4, PFLOW_SET_HDRLEN +
929fc6e5069SKristof Provost 	    (sc->sc_count_nat4 * sizeof(struct pflow_ipfix_nat4)),
930fc6e5069SKristof Provost 	    sizeof(struct pflow_ipfix_nat4), (caddr_t)nat);
931fc6e5069SKristof Provost 	sc->sc_count_nat4++;
932fc6e5069SKristof Provost 
9332be6f757SKristof Provost 	pflowstat_inc(pflow_flows);
934fc6e5069SKristof Provost 	sc->sc_gcounter++;
9352be6f757SKristof Provost 
936fc6e5069SKristof Provost 	if (sc->sc_count_nat4 >= sc->sc_maxcount_nat4)
937fc6e5069SKristof Provost 		ret = pflow_sendout_ipfix(sc, PFLOW_NAT4);
938f92d9b1aSKristof Provost 
939f92d9b1aSKristof Provost 	return (ret);
940f92d9b1aSKristof Provost }
941f92d9b1aSKristof Provost 
942f92d9b1aSKristof Provost static int
943baf9b6d0SKristof Provost pflow_pack_flow(const struct pf_kstate *st, struct pf_state_key *sk,
944f92d9b1aSKristof Provost     struct pflow_softc *sc)
945f92d9b1aSKristof Provost {
946f92d9b1aSKristof Provost 	struct pflow_flow	 flow1;
947f92d9b1aSKristof Provost 	struct pflow_flow	 flow2;
948f92d9b1aSKristof Provost 	int			 ret = 0;
949f92d9b1aSKristof Provost 
950f92d9b1aSKristof Provost 	bzero(&flow1, sizeof(flow1));
951f92d9b1aSKristof Provost 	bzero(&flow2, sizeof(flow2));
952f92d9b1aSKristof Provost 
953f92d9b1aSKristof Provost 	if (st->direction == PF_OUT)
954f92d9b1aSKristof Provost 		copy_flow_data(&flow1, &flow2, st, sk, 1, 0);
955f92d9b1aSKristof Provost 	else
956f92d9b1aSKristof Provost 		copy_flow_data(&flow1, &flow2, st, sk, 0, 1);
957f92d9b1aSKristof Provost 
958f92d9b1aSKristof Provost 	if (st->bytes[0] != 0) /* first flow from state */
959f92d9b1aSKristof Provost 		ret = copy_flow_to_m(&flow1, sc);
960f92d9b1aSKristof Provost 
961f92d9b1aSKristof Provost 	if (st->bytes[1] != 0) /* second flow from state */
962f92d9b1aSKristof Provost 		ret = copy_flow_to_m(&flow2, sc);
963f92d9b1aSKristof Provost 
964f92d9b1aSKristof Provost 	return (ret);
965f92d9b1aSKristof Provost }
966f92d9b1aSKristof Provost 
967fc6e5069SKristof Provost static bool
968fc6e5069SKristof Provost pflow_is_natd(const struct pf_kstate *st)
969fc6e5069SKristof Provost {
970fc6e5069SKristof Provost 	/* If ports or addresses are different we've been NAT-ed. */
971fc6e5069SKristof Provost 	return (memcmp(st->key[PF_SK_WIRE], st->key[PF_SK_STACK],
972fc6e5069SKristof Provost 	    sizeof(struct pf_addr) * 2 + sizeof(uint16_t) * 2) != 0);
973fc6e5069SKristof Provost }
974fc6e5069SKristof Provost 
975f92d9b1aSKristof Provost static int
976baf9b6d0SKristof Provost pflow_pack_flow_ipfix(const struct pf_kstate *st, struct pf_state_key *sk,
977f92d9b1aSKristof Provost     struct pflow_softc *sc)
978f92d9b1aSKristof Provost {
979f92d9b1aSKristof Provost 	struct pflow_ipfix_flow4	 flow4_1, flow4_2;
980fc6e5069SKristof Provost 	struct pflow_ipfix_nat4		 nat4_1, nat4_2;
981f92d9b1aSKristof Provost 	struct pflow_ipfix_flow6	 flow6_1, flow6_2;
982f92d9b1aSKristof Provost 	int				 ret = 0;
983fc6e5069SKristof Provost 	bool				 nat = false;
984fc6e5069SKristof Provost 
985f92d9b1aSKristof Provost 	if (sk->af == AF_INET) {
986f92d9b1aSKristof Provost 		bzero(&flow4_1, sizeof(flow4_1));
987f92d9b1aSKristof Provost 		bzero(&flow4_2, sizeof(flow4_2));
988f92d9b1aSKristof Provost 
989fc6e5069SKristof Provost 		nat = pflow_is_natd(st);
990fc6e5069SKristof Provost 
991f92d9b1aSKristof Provost 		if (st->direction == PF_OUT)
992f92d9b1aSKristof Provost 			copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc,
993f92d9b1aSKristof Provost 			    1, 0);
994f92d9b1aSKristof Provost 		else
995f92d9b1aSKristof Provost 			copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc,
996f92d9b1aSKristof Provost 			    0, 1);
997f92d9b1aSKristof Provost 
998fc6e5069SKristof Provost 		if (nat)
999fc6e5069SKristof Provost 			copy_nat_ipfix_4_data(&nat4_1, &nat4_2, st, sk, sc, 1, 0);
1000fc6e5069SKristof Provost 
1001fc6e5069SKristof Provost 		if (st->bytes[0] != 0) /* first flow from state */ {
1002f92d9b1aSKristof Provost 			ret = copy_flow_ipfix_4_to_m(&flow4_1, sc);
1003f92d9b1aSKristof Provost 
1004fc6e5069SKristof Provost 			if (ret == 0 && nat) {
1005fc6e5069SKristof Provost 				ret = copy_nat_ipfix_4_to_m(&nat4_1, st, sc,
1006fc6e5069SKristof Provost 				    PFIX_NAT_EVENT_SESSION_CREATE, st->creation);
1007fc6e5069SKristof Provost 				ret |= copy_nat_ipfix_4_to_m(&nat4_1, st, sc,
1008fc6e5069SKristof Provost 				    PFIX_NAT_EVENT_SESSION_DELETE, st->expire);
1009fc6e5069SKristof Provost 			}
1010fc6e5069SKristof Provost 		}
1011fc6e5069SKristof Provost 
1012fc6e5069SKristof Provost 		if (st->bytes[1] != 0) /* second flow from state */ {
1013f92d9b1aSKristof Provost 			ret = copy_flow_ipfix_4_to_m(&flow4_2, sc);
1014fc6e5069SKristof Provost 
1015fc6e5069SKristof Provost 			if (ret == 0 && nat) {
1016fc6e5069SKristof Provost 				ret = copy_nat_ipfix_4_to_m(&nat4_2, st, sc,
1017fc6e5069SKristof Provost 				    PFIX_NAT_EVENT_SESSION_CREATE, st->creation);
1018fc6e5069SKristof Provost 				ret |= copy_nat_ipfix_4_to_m(&nat4_2, st, sc,
1019fc6e5069SKristof Provost 				    PFIX_NAT_EVENT_SESSION_DELETE, st->expire);
1020fc6e5069SKristof Provost 			}
1021fc6e5069SKristof Provost 		}
1022f92d9b1aSKristof Provost 	} else if (sk->af == AF_INET6) {
1023f92d9b1aSKristof Provost 		bzero(&flow6_1, sizeof(flow6_1));
1024f92d9b1aSKristof Provost 		bzero(&flow6_2, sizeof(flow6_2));
1025f92d9b1aSKristof Provost 
1026f92d9b1aSKristof Provost 		if (st->direction == PF_OUT)
1027f92d9b1aSKristof Provost 			copy_flow_ipfix_6_data(&flow6_1, &flow6_2, st, sk, sc,
1028f92d9b1aSKristof Provost 			    1, 0);
1029f92d9b1aSKristof Provost 		else
1030f92d9b1aSKristof Provost 			copy_flow_ipfix_6_data(&flow6_1, &flow6_2, st, sk, sc,
1031f92d9b1aSKristof Provost 			    0, 1);
1032f92d9b1aSKristof Provost 
1033f92d9b1aSKristof Provost 		if (st->bytes[0] != 0) /* first flow from state */
1034f92d9b1aSKristof Provost 			ret = copy_flow_ipfix_6_to_m(&flow6_1, sc);
1035f92d9b1aSKristof Provost 
1036f92d9b1aSKristof Provost 		if (st->bytes[1] != 0) /* second flow from state */
1037f92d9b1aSKristof Provost 			ret = copy_flow_ipfix_6_to_m(&flow6_2, sc);
1038f92d9b1aSKristof Provost 	}
1039f92d9b1aSKristof Provost 	return (ret);
1040f92d9b1aSKristof Provost }
1041f92d9b1aSKristof Provost 
1042f92d9b1aSKristof Provost static void
1043f92d9b1aSKristof Provost pflow_timeout(void *v)
1044f92d9b1aSKristof Provost {
1045f92d9b1aSKristof Provost 	struct pflow_softc	*sc = v;
1046f92d9b1aSKristof Provost 
1047f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
1048f92d9b1aSKristof Provost 	CURVNET_SET(sc->sc_vnet);
1049f92d9b1aSKristof Provost 
1050f92d9b1aSKristof Provost 	switch (sc->sc_version) {
1051f92d9b1aSKristof Provost 	case PFLOW_PROTO_5:
1052f92d9b1aSKristof Provost 		pflow_sendout_v5(sc);
1053f92d9b1aSKristof Provost 		break;
1054f92d9b1aSKristof Provost 	case PFLOW_PROTO_10:
1055fc6e5069SKristof Provost 		pflow_sendout_ipfix(sc, PFLOW_INET);
1056f92d9b1aSKristof Provost 		break;
1057f92d9b1aSKristof Provost 	default: /* NOTREACHED */
1058f92d9b1aSKristof Provost 		panic("Unsupported version %d", sc->sc_version);
1059f92d9b1aSKristof Provost 		break;
1060f92d9b1aSKristof Provost 	}
1061f92d9b1aSKristof Provost 
1062f92d9b1aSKristof Provost 	CURVNET_RESTORE();
1063f92d9b1aSKristof Provost }
1064f92d9b1aSKristof Provost 
1065f92d9b1aSKristof Provost static void
1066f92d9b1aSKristof Provost pflow_timeout6(void *v)
1067f92d9b1aSKristof Provost {
1068f92d9b1aSKristof Provost 	struct pflow_softc	*sc = v;
1069f92d9b1aSKristof Provost 
1070f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
1071f92d9b1aSKristof Provost 
1072f92d9b1aSKristof Provost 	if (sc->sc_version != PFLOW_PROTO_10)
1073f92d9b1aSKristof Provost 		return;
1074f92d9b1aSKristof Provost 
1075f92d9b1aSKristof Provost 	CURVNET_SET(sc->sc_vnet);
1076fc6e5069SKristof Provost 	pflow_sendout_ipfix(sc, PFLOW_INET6);
1077f92d9b1aSKristof Provost 	CURVNET_RESTORE();
1078f92d9b1aSKristof Provost }
1079f92d9b1aSKristof Provost 
1080f92d9b1aSKristof Provost static void
1081f92d9b1aSKristof Provost pflow_timeout_tmpl(void *v)
1082f92d9b1aSKristof Provost {
1083f92d9b1aSKristof Provost 	struct pflow_softc	*sc = v;
1084f92d9b1aSKristof Provost 
1085f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
1086f92d9b1aSKristof Provost 
1087f92d9b1aSKristof Provost 	if (sc->sc_version != PFLOW_PROTO_10)
1088f92d9b1aSKristof Provost 		return;
1089f92d9b1aSKristof Provost 
1090f92d9b1aSKristof Provost 	CURVNET_SET(sc->sc_vnet);
1091f92d9b1aSKristof Provost 	pflow_sendout_ipfix_tmpl(sc);
1092f92d9b1aSKristof Provost 	CURVNET_RESTORE();
1093f92d9b1aSKristof Provost }
1094f92d9b1aSKristof Provost 
1095f92d9b1aSKristof Provost static void
1096fc6e5069SKristof Provost pflow_timeout_nat4(void *v)
1097fc6e5069SKristof Provost {
1098fc6e5069SKristof Provost 	struct pflow_softc	*sc = v;
1099fc6e5069SKristof Provost 
1100fc6e5069SKristof Provost 	PFLOW_ASSERT(sc);
1101fc6e5069SKristof Provost 
1102fc6e5069SKristof Provost 	if (sc->sc_version != PFLOW_PROTO_10)
1103fc6e5069SKristof Provost 		return;
1104fc6e5069SKristof Provost 
1105fc6e5069SKristof Provost 	CURVNET_SET(sc->sc_vnet);
1106fc6e5069SKristof Provost 	pflow_sendout_ipfix(sc, PFLOW_NAT4);
1107fc6e5069SKristof Provost 	CURVNET_RESTORE();
1108fc6e5069SKristof Provost }
1109fc6e5069SKristof Provost 
1110fc6e5069SKristof Provost static void
1111f92d9b1aSKristof Provost pflow_flush(struct pflow_softc *sc)
1112f92d9b1aSKristof Provost {
1113f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
1114f92d9b1aSKristof Provost 
1115f92d9b1aSKristof Provost 	switch (sc->sc_version) {
1116f92d9b1aSKristof Provost 	case PFLOW_PROTO_5:
1117f92d9b1aSKristof Provost 		pflow_sendout_v5(sc);
1118f92d9b1aSKristof Provost 		break;
1119f92d9b1aSKristof Provost 	case PFLOW_PROTO_10:
1120fc6e5069SKristof Provost 		pflow_sendout_ipfix(sc, PFLOW_INET);
1121fc6e5069SKristof Provost 		pflow_sendout_ipfix(sc, PFLOW_INET6);
1122fc6e5069SKristof Provost 		pflow_sendout_ipfix(sc, PFLOW_NAT4);
1123f92d9b1aSKristof Provost 		break;
1124f92d9b1aSKristof Provost 	default: /* NOTREACHED */
1125f92d9b1aSKristof Provost 		break;
1126f92d9b1aSKristof Provost 	}
1127f92d9b1aSKristof Provost }
1128f92d9b1aSKristof Provost 
1129f92d9b1aSKristof Provost static int
1130f92d9b1aSKristof Provost pflow_sendout_v5(struct pflow_softc *sc)
1131f92d9b1aSKristof Provost {
1132f92d9b1aSKristof Provost 	struct mbuf		*m = sc->sc_mbuf;
1133f92d9b1aSKristof Provost 	struct pflow_header	*h;
1134f92d9b1aSKristof Provost 	struct timespec		tv;
1135f92d9b1aSKristof Provost 
1136f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
1137f92d9b1aSKristof Provost 
1138f92d9b1aSKristof Provost 	if (m == NULL)
1139f92d9b1aSKristof Provost 		return (0);
1140f92d9b1aSKristof Provost 
1141f92d9b1aSKristof Provost 	sc->sc_mbuf = NULL;
1142f92d9b1aSKristof Provost 
11432be6f757SKristof Provost 	pflowstat_inc(pflow_packets);
1144f92d9b1aSKristof Provost 	h = mtod(m, struct pflow_header *);
1145f92d9b1aSKristof Provost 	h->count = htons(sc->sc_count);
1146f92d9b1aSKristof Provost 
1147f92d9b1aSKristof Provost 	/* populate pflow_header */
1148f92d9b1aSKristof Provost 	h->uptime_ms = htonl(time_uptime * 1000);
1149f92d9b1aSKristof Provost 
1150f92d9b1aSKristof Provost 	getnanotime(&tv);
1151f92d9b1aSKristof Provost 	h->time_sec = htonl(tv.tv_sec);			/* XXX 2038 */
1152f92d9b1aSKristof Provost 	h->time_nanosec = htonl(tv.tv_nsec);
1153f92d9b1aSKristof Provost 	if (mbufq_enqueue(&sc->sc_outputqueue, m) == 0)
1154f92d9b1aSKristof Provost 		swi_sched(sc->sc_swi_cookie, 0);
1155f92d9b1aSKristof Provost 
1156f92d9b1aSKristof Provost 	return (0);
1157f92d9b1aSKristof Provost }
1158f92d9b1aSKristof Provost 
1159f92d9b1aSKristof Provost static int
1160fc6e5069SKristof Provost pflow_sendout_ipfix(struct pflow_softc *sc, enum pflow_family_t af)
1161f92d9b1aSKristof Provost {
1162f92d9b1aSKristof Provost 	struct mbuf			*m;
1163f92d9b1aSKristof Provost 	struct pflow_v10_header		*h10;
1164f92d9b1aSKristof Provost 	struct pflow_set_header		*set_hdr;
1165f92d9b1aSKristof Provost 	u_int32_t			 count;
1166f92d9b1aSKristof Provost 	int				 set_length;
1167f92d9b1aSKristof Provost 
1168f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
1169f92d9b1aSKristof Provost 
1170f92d9b1aSKristof Provost 	switch (af) {
1171fc6e5069SKristof Provost 	case PFLOW_INET:
1172f92d9b1aSKristof Provost 		m = sc->sc_mbuf;
1173f92d9b1aSKristof Provost 		callout_stop(&sc->sc_tmo);
1174f92d9b1aSKristof Provost 		if (m == NULL)
1175f92d9b1aSKristof Provost 			return (0);
1176f92d9b1aSKristof Provost 		sc->sc_mbuf = NULL;
1177f92d9b1aSKristof Provost 		count = sc->sc_count4;
1178f92d9b1aSKristof Provost 		set_length = sizeof(struct pflow_set_header)
1179f92d9b1aSKristof Provost 		    + sc->sc_count4 * sizeof(struct pflow_ipfix_flow4);
1180f92d9b1aSKristof Provost 		break;
1181fc6e5069SKristof Provost 	case PFLOW_INET6:
1182f92d9b1aSKristof Provost 		m = sc->sc_mbuf6;
1183f92d9b1aSKristof Provost 		callout_stop(&sc->sc_tmo6);
1184f92d9b1aSKristof Provost 		if (m == NULL)
1185f92d9b1aSKristof Provost 			return (0);
1186f92d9b1aSKristof Provost 		sc->sc_mbuf6 = NULL;
1187f92d9b1aSKristof Provost 		count = sc->sc_count6;
1188f92d9b1aSKristof Provost 		set_length = sizeof(struct pflow_set_header)
1189f92d9b1aSKristof Provost 		    + sc->sc_count6 * sizeof(struct pflow_ipfix_flow6);
1190f92d9b1aSKristof Provost 		break;
1191fc6e5069SKristof Provost 	case PFLOW_NAT4:
1192fc6e5069SKristof Provost 		m = sc->sc_mbuf_nat4;
1193fc6e5069SKristof Provost 		callout_stop(&sc->sc_tmo_nat4);
1194fc6e5069SKristof Provost 		if (m == NULL)
1195fc6e5069SKristof Provost 			return (0);
1196fc6e5069SKristof Provost 		sc->sc_mbuf_nat4 = NULL;
1197fc6e5069SKristof Provost 		count = sc->sc_count_nat4;
1198fc6e5069SKristof Provost 		set_length = sizeof(struct pflow_set_header)
1199fc6e5069SKristof Provost 		    + sc->sc_count_nat4 * sizeof(struct pflow_ipfix_nat4);
1200fc6e5069SKristof Provost 		break;
1201f92d9b1aSKristof Provost 	default:
1202f92d9b1aSKristof Provost 		panic("Unsupported AF %d", af);
1203f92d9b1aSKristof Provost 	}
1204f92d9b1aSKristof Provost 
12052be6f757SKristof Provost 	pflowstat_inc(pflow_packets);
12062be6f757SKristof Provost 
1207f92d9b1aSKristof Provost 	set_hdr = mtod(m, struct pflow_set_header *);
1208f92d9b1aSKristof Provost 	set_hdr->set_length = htons(set_length);
1209f92d9b1aSKristof Provost 
1210f92d9b1aSKristof Provost 	/* populate pflow_header */
1211f92d9b1aSKristof Provost 	M_PREPEND(m, sizeof(struct pflow_v10_header), M_NOWAIT);
1212f92d9b1aSKristof Provost 	if (m == NULL) {
12132be6f757SKristof Provost 		pflowstat_inc(pflow_onomem);
1214f92d9b1aSKristof Provost 		return (ENOBUFS);
1215f92d9b1aSKristof Provost 	}
1216f92d9b1aSKristof Provost 	h10 = mtod(m, struct pflow_v10_header *);
1217f92d9b1aSKristof Provost 	h10->version = htons(PFLOW_PROTO_10);
1218f92d9b1aSKristof Provost 	h10->length = htons(PFLOW_IPFIX_HDRLEN + set_length);
1219f92d9b1aSKristof Provost 	h10->time_sec = htonl(time_second);		/* XXX 2038 */
1220f92d9b1aSKristof Provost 	h10->flow_sequence = htonl(sc->sc_sequence);
1221f92d9b1aSKristof Provost 	sc->sc_sequence += count;
122285b71dcfSKristof Provost 	h10->observation_dom = htonl(sc->sc_observation_dom);
1223f92d9b1aSKristof Provost 	if (mbufq_enqueue(&sc->sc_outputqueue, m) == 0)
1224f92d9b1aSKristof Provost 		swi_sched(sc->sc_swi_cookie, 0);
1225f92d9b1aSKristof Provost 
1226f92d9b1aSKristof Provost 	return (0);
1227f92d9b1aSKristof Provost }
1228f92d9b1aSKristof Provost 
1229f92d9b1aSKristof Provost static int
1230f92d9b1aSKristof Provost pflow_sendout_ipfix_tmpl(struct pflow_softc *sc)
1231f92d9b1aSKristof Provost {
1232f92d9b1aSKristof Provost 	struct mbuf			*m;
1233f92d9b1aSKristof Provost 	struct pflow_v10_header		*h10;
1234f92d9b1aSKristof Provost 
1235f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
1236f92d9b1aSKristof Provost 
1237f92d9b1aSKristof Provost 	m = pflow_get_mbuf(sc, 0);
1238f92d9b1aSKristof Provost 	if (m == NULL)
1239f92d9b1aSKristof Provost 		return (0);
1240f92d9b1aSKristof Provost 	m_copyback(m, 0, sizeof(struct pflow_ipfix_tmpl),
1241f92d9b1aSKristof Provost 	    (caddr_t)&sc->sc_tmpl_ipfix);
1242f92d9b1aSKristof Provost 
12432be6f757SKristof Provost 	pflowstat_inc(pflow_packets);
1244f92d9b1aSKristof Provost 
1245f92d9b1aSKristof Provost 	/* populate pflow_header */
1246f92d9b1aSKristof Provost 	M_PREPEND(m, sizeof(struct pflow_v10_header), M_NOWAIT);
1247f92d9b1aSKristof Provost 	if (m == NULL) {
12482be6f757SKristof Provost 		pflowstat_inc(pflow_onomem);
1249f92d9b1aSKristof Provost 		return (ENOBUFS);
1250f92d9b1aSKristof Provost 	}
1251f92d9b1aSKristof Provost 	h10 = mtod(m, struct pflow_v10_header *);
1252f92d9b1aSKristof Provost 	h10->version = htons(PFLOW_PROTO_10);
1253f92d9b1aSKristof Provost 	h10->length = htons(PFLOW_IPFIX_HDRLEN + sizeof(struct
1254f92d9b1aSKristof Provost 	    pflow_ipfix_tmpl));
1255f92d9b1aSKristof Provost 	h10->time_sec = htonl(time_second);		/* XXX 2038 */
1256f92d9b1aSKristof Provost 	h10->flow_sequence = htonl(sc->sc_sequence);
125785b71dcfSKristof Provost 	h10->observation_dom = htonl(sc->sc_observation_dom);
1258f92d9b1aSKristof Provost 
1259f92d9b1aSKristof Provost 	callout_reset(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT * hz,
1260f92d9b1aSKristof Provost 	    pflow_timeout_tmpl, sc);
1261f92d9b1aSKristof Provost 	if (mbufq_enqueue(&sc->sc_outputqueue, m) == 0)
1262f92d9b1aSKristof Provost 		swi_sched(sc->sc_swi_cookie, 0);
1263f92d9b1aSKristof Provost 
1264f92d9b1aSKristof Provost 	return (0);
1265f92d9b1aSKristof Provost }
1266f92d9b1aSKristof Provost 
1267f92d9b1aSKristof Provost static int
1268f92d9b1aSKristof Provost pflow_sendout_mbuf(struct pflow_softc *sc, struct mbuf *m)
1269f92d9b1aSKristof Provost {
1270f92d9b1aSKristof Provost 	if (sc->so == NULL) {
1271f92d9b1aSKristof Provost 		m_freem(m);
1272f92d9b1aSKristof Provost 		return (EINVAL);
1273f92d9b1aSKristof Provost 	}
1274f92d9b1aSKristof Provost 	return (sosend(sc->so, sc->sc_flowdst, NULL, m, NULL, 0, curthread));
1275f92d9b1aSKristof Provost }
1276f92d9b1aSKristof Provost 
1277f92d9b1aSKristof Provost static int
12782be6f757SKristof Provost sysctl_pflowstats(SYSCTL_HANDLER_ARGS)
12792be6f757SKristof Provost {
12802be6f757SKristof Provost 	struct pflowstats pflowstats;
12812be6f757SKristof Provost 
12822be6f757SKristof Provost 	pflowstats.pflow_flows =
12832be6f757SKristof Provost 	    counter_u64_fetch(V_pflowstats.c[pflow_flows]);
12842be6f757SKristof Provost 	pflowstats.pflow_packets =
12852be6f757SKristof Provost 	    counter_u64_fetch(V_pflowstats.c[pflow_packets]);
12862be6f757SKristof Provost 	pflowstats.pflow_onomem =
12872be6f757SKristof Provost 	    counter_u64_fetch(V_pflowstats.c[pflow_onomem]);
12882be6f757SKristof Provost 	pflowstats.pflow_oerrors =
12892be6f757SKristof Provost 	    counter_u64_fetch(V_pflowstats.c[pflow_oerrors]);
12902be6f757SKristof Provost 
12912be6f757SKristof Provost 	return (sysctl_handle_opaque(oidp, &pflowstats, sizeof(pflowstats), req));
12922be6f757SKristof Provost }
12932be6f757SKristof Provost 
12942be6f757SKristof Provost static int
1295f92d9b1aSKristof Provost pflow_nl_list(struct nlmsghdr *hdr, struct nl_pstate *npt)
1296f92d9b1aSKristof Provost {
1297f92d9b1aSKristof Provost 	struct epoch_tracker	 et;
1298f92d9b1aSKristof Provost 	struct pflow_softc	*sc = NULL;
1299f92d9b1aSKristof Provost 	struct nl_writer	 *nw = npt->nw;
1300f92d9b1aSKristof Provost 	int			 error = 0;
1301f92d9b1aSKristof Provost 
1302f92d9b1aSKristof Provost 	hdr->nlmsg_flags |= NLM_F_MULTI;
1303f92d9b1aSKristof Provost 
1304f92d9b1aSKristof Provost 	NET_EPOCH_ENTER(et);
1305f92d9b1aSKristof Provost 	CK_LIST_FOREACH(sc, &V_pflowif_list, sc_next) {
1306f92d9b1aSKristof Provost 		if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
1307f92d9b1aSKristof Provost 			error = ENOMEM;
1308f92d9b1aSKristof Provost 			goto out;
1309f92d9b1aSKristof Provost 		}
1310f92d9b1aSKristof Provost 
1311f92d9b1aSKristof Provost 		struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1312f92d9b1aSKristof Provost 		ghdr_new->cmd = PFLOWNL_CMD_LIST;
1313f92d9b1aSKristof Provost 		ghdr_new->version = 0;
1314f92d9b1aSKristof Provost 		ghdr_new->reserved = 0;
1315f92d9b1aSKristof Provost 
1316f92d9b1aSKristof Provost 		nlattr_add_u32(nw, PFLOWNL_L_ID, sc->sc_id);
1317f92d9b1aSKristof Provost 
1318f92d9b1aSKristof Provost 		if (! nlmsg_end(nw)) {
1319f92d9b1aSKristof Provost 			error = ENOMEM;
1320f92d9b1aSKristof Provost 			goto out;
1321f92d9b1aSKristof Provost 		}
1322f92d9b1aSKristof Provost 	}
1323f92d9b1aSKristof Provost 
1324f92d9b1aSKristof Provost out:
1325f92d9b1aSKristof Provost 	NET_EPOCH_EXIT(et);
1326f92d9b1aSKristof Provost 
1327f92d9b1aSKristof Provost 	if (error != 0)
1328f92d9b1aSKristof Provost 		nlmsg_abort(nw);
1329f92d9b1aSKristof Provost 
1330f92d9b1aSKristof Provost 	return (error);
1331f92d9b1aSKristof Provost }
1332f92d9b1aSKristof Provost 
1333f92d9b1aSKristof Provost static int
1334f92d9b1aSKristof Provost pflow_nl_create(struct nlmsghdr *hdr, struct nl_pstate *npt)
1335f92d9b1aSKristof Provost {
1336f92d9b1aSKristof Provost 	struct nl_writer	 *nw = npt->nw;
1337f92d9b1aSKristof Provost 	int			 error = 0;
1338f92d9b1aSKristof Provost 	int			 unit;
1339f92d9b1aSKristof Provost 
1340f92d9b1aSKristof Provost 	if (! nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
1341f92d9b1aSKristof Provost 		return (ENOMEM);
1342f92d9b1aSKristof Provost 	}
1343f92d9b1aSKristof Provost 
1344f92d9b1aSKristof Provost 	struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1345f92d9b1aSKristof Provost 	ghdr_new->cmd = PFLOWNL_CMD_CREATE;
1346f92d9b1aSKristof Provost 	ghdr_new->version = 0;
1347f92d9b1aSKristof Provost 	ghdr_new->reserved = 0;
1348f92d9b1aSKristof Provost 
1349f92d9b1aSKristof Provost 	unit = alloc_unr(V_pflow_unr);
135063a5fe83SKristof Provost 	if (unit == -1) {
135163a5fe83SKristof Provost 		nlmsg_abort(nw);
135263a5fe83SKristof Provost 		return (ENOMEM);
135363a5fe83SKristof Provost 	}
1354f92d9b1aSKristof Provost 
1355f92d9b1aSKristof Provost 	error = pflow_create(unit);
1356f92d9b1aSKristof Provost 	if (error != 0) {
1357f92d9b1aSKristof Provost 		free_unr(V_pflow_unr, unit);
1358f92d9b1aSKristof Provost 		nlmsg_abort(nw);
1359f92d9b1aSKristof Provost 		return (error);
1360f92d9b1aSKristof Provost 	}
1361f92d9b1aSKristof Provost 
1362f92d9b1aSKristof Provost 	nlattr_add_s32(nw, PFLOWNL_CREATE_ID, unit);
1363f92d9b1aSKristof Provost 
1364f92d9b1aSKristof Provost 	if (! nlmsg_end(nw)) {
1365f92d9b1aSKristof Provost 		pflow_destroy(unit, true);
1366f92d9b1aSKristof Provost 		return (ENOMEM);
1367f92d9b1aSKristof Provost 	}
1368f92d9b1aSKristof Provost 
1369f92d9b1aSKristof Provost 	return (0);
1370f92d9b1aSKristof Provost }
1371f92d9b1aSKristof Provost 
1372f92d9b1aSKristof Provost struct pflow_parsed_del {
1373f92d9b1aSKristof Provost 	int id;
1374f92d9b1aSKristof Provost };
1375f92d9b1aSKristof Provost #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1376f92d9b1aSKristof Provost #define	_OUT(_field)	offsetof(struct pflow_parsed_del, _field)
1377f92d9b1aSKristof Provost static const struct nlattr_parser nla_p_del[] = {
1378f92d9b1aSKristof Provost 	{ .type = PFLOWNL_DEL_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
1379f92d9b1aSKristof Provost };
1380f92d9b1aSKristof Provost static const struct nlfield_parser nlf_p_del[] = {};
1381f92d9b1aSKristof Provost #undef _IN
1382f92d9b1aSKristof Provost #undef _OUT
1383f92d9b1aSKristof Provost NL_DECLARE_PARSER(del_parser, struct genlmsghdr, nlf_p_del, nla_p_del);
1384f92d9b1aSKristof Provost 
1385f92d9b1aSKristof Provost static int
1386f92d9b1aSKristof Provost pflow_nl_del(struct nlmsghdr *hdr, struct nl_pstate *npt)
1387f92d9b1aSKristof Provost {
1388f92d9b1aSKristof Provost 	struct pflow_parsed_del d = {};
1389f92d9b1aSKristof Provost 	int error;
1390f92d9b1aSKristof Provost 
1391f92d9b1aSKristof Provost 	error = nl_parse_nlmsg(hdr, &del_parser, npt, &d);
1392f92d9b1aSKristof Provost 	if (error != 0)
1393f92d9b1aSKristof Provost 		return (error);
1394f92d9b1aSKristof Provost 
1395f92d9b1aSKristof Provost 	error = pflow_destroy(d.id, true);
1396f92d9b1aSKristof Provost 
1397f92d9b1aSKristof Provost 	return (error);
1398f92d9b1aSKristof Provost }
1399f92d9b1aSKristof Provost 
1400f92d9b1aSKristof Provost struct pflow_parsed_get {
1401f92d9b1aSKristof Provost 	int id;
1402f92d9b1aSKristof Provost };
1403f92d9b1aSKristof Provost #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1404f92d9b1aSKristof Provost #define	_OUT(_field)	offsetof(struct pflow_parsed_get, _field)
1405f92d9b1aSKristof Provost static const struct nlattr_parser nla_p_get[] = {
1406f92d9b1aSKristof Provost 	{ .type = PFLOWNL_GET_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
1407f92d9b1aSKristof Provost };
1408f92d9b1aSKristof Provost static const struct nlfield_parser nlf_p_get[] = {};
1409f92d9b1aSKristof Provost #undef _IN
1410f92d9b1aSKristof Provost #undef _OUT
1411f92d9b1aSKristof Provost NL_DECLARE_PARSER(get_parser, struct genlmsghdr, nlf_p_get, nla_p_get);
1412f92d9b1aSKristof Provost 
1413f92d9b1aSKristof Provost static bool
1414f92d9b1aSKristof Provost nlattr_add_sockaddr(struct nl_writer *nw, int attr, const struct sockaddr *s)
1415f92d9b1aSKristof Provost {
1416f92d9b1aSKristof Provost 	int off = nlattr_add_nested(nw, attr);
1417f92d9b1aSKristof Provost 	if (off == 0)
1418f92d9b1aSKristof Provost 		return (false);
1419f92d9b1aSKristof Provost 
1420f92d9b1aSKristof Provost 	nlattr_add_u8(nw, PFLOWNL_ADDR_FAMILY, s->sa_family);
1421f92d9b1aSKristof Provost 
1422f92d9b1aSKristof Provost 	switch (s->sa_family) {
1423f92d9b1aSKristof Provost 	case AF_INET: {
1424f92d9b1aSKristof Provost 		const struct sockaddr_in *in = (const struct sockaddr_in *)s;
1425f92d9b1aSKristof Provost 		nlattr_add_u16(nw, PFLOWNL_ADDR_PORT, in->sin_port);
1426f92d9b1aSKristof Provost 		nlattr_add_in_addr(nw, PFLOWNL_ADDR_IP, &in->sin_addr);
1427f92d9b1aSKristof Provost 		break;
1428f92d9b1aSKristof Provost 	}
1429f92d9b1aSKristof Provost 	case AF_INET6: {
1430f92d9b1aSKristof Provost 		const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *)s;
1431f92d9b1aSKristof Provost 		nlattr_add_u16(nw, PFLOWNL_ADDR_PORT, in6->sin6_port);
1432f92d9b1aSKristof Provost 		nlattr_add_in6_addr(nw, PFLOWNL_ADDR_IP6, &in6->sin6_addr);
1433f92d9b1aSKristof Provost 		break;
1434f92d9b1aSKristof Provost 	}
1435f92d9b1aSKristof Provost 	default:
1436f92d9b1aSKristof Provost 		panic("Unknown address family %d", s->sa_family);
1437f92d9b1aSKristof Provost 	}
1438f92d9b1aSKristof Provost 
1439f92d9b1aSKristof Provost 	nlattr_set_len(nw, off);
1440f92d9b1aSKristof Provost 	return (true);
1441f92d9b1aSKristof Provost }
1442f92d9b1aSKristof Provost 
1443f92d9b1aSKristof Provost static int
1444f92d9b1aSKristof Provost pflow_nl_get(struct nlmsghdr *hdr, struct nl_pstate *npt)
1445f92d9b1aSKristof Provost {
1446f92d9b1aSKristof Provost 	struct epoch_tracker et;
1447f92d9b1aSKristof Provost 	struct pflow_parsed_get g = {};
1448f92d9b1aSKristof Provost 	struct pflow_softc *sc = NULL;
1449f92d9b1aSKristof Provost 	struct nl_writer *nw = npt->nw;
1450f92d9b1aSKristof Provost 	struct genlmsghdr *ghdr_new;
1451f92d9b1aSKristof Provost 	int error;
1452f92d9b1aSKristof Provost 
1453f92d9b1aSKristof Provost 	error = nl_parse_nlmsg(hdr, &get_parser, npt, &g);
1454f92d9b1aSKristof Provost 	if (error != 0)
1455f92d9b1aSKristof Provost 		return (error);
1456f92d9b1aSKristof Provost 
1457f92d9b1aSKristof Provost 	NET_EPOCH_ENTER(et);
1458f92d9b1aSKristof Provost 	CK_LIST_FOREACH(sc, &V_pflowif_list, sc_next) {
1459f92d9b1aSKristof Provost 		if (sc->sc_id == g.id)
1460f92d9b1aSKristof Provost 			break;
1461f92d9b1aSKristof Provost 	}
1462f92d9b1aSKristof Provost 	if (sc == NULL) {
1463f92d9b1aSKristof Provost 		error = ENOENT;
1464f92d9b1aSKristof Provost 		goto out;
1465f92d9b1aSKristof Provost 	}
1466f92d9b1aSKristof Provost 
1467f92d9b1aSKristof Provost 	if (! nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
1468f92d9b1aSKristof Provost 		nlmsg_abort(nw);
1469f92d9b1aSKristof Provost 		error = ENOMEM;
1470f92d9b1aSKristof Provost 		goto out;
1471f92d9b1aSKristof Provost 	}
1472f92d9b1aSKristof Provost 
1473f92d9b1aSKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1474f92d9b1aSKristof Provost 	if (ghdr_new == NULL) {
1475f92d9b1aSKristof Provost 		nlmsg_abort(nw);
1476f92d9b1aSKristof Provost 		error = ENOMEM;
1477f92d9b1aSKristof Provost 		goto out;
1478f92d9b1aSKristof Provost 	}
1479f92d9b1aSKristof Provost 
1480f92d9b1aSKristof Provost 	ghdr_new->cmd = PFLOWNL_CMD_GET;
1481f92d9b1aSKristof Provost 	ghdr_new->version = 0;
1482f92d9b1aSKristof Provost 	ghdr_new->reserved = 0;
1483f92d9b1aSKristof Provost 
1484f92d9b1aSKristof Provost 	nlattr_add_u32(nw, PFLOWNL_GET_ID, sc->sc_id);
1485f92d9b1aSKristof Provost 	nlattr_add_u16(nw, PFLOWNL_GET_VERSION, sc->sc_version);
1486f92d9b1aSKristof Provost 	if (sc->sc_flowsrc)
1487f92d9b1aSKristof Provost 		nlattr_add_sockaddr(nw, PFLOWNL_GET_SRC, sc->sc_flowsrc);
1488f92d9b1aSKristof Provost 	if (sc->sc_flowdst)
1489f92d9b1aSKristof Provost 		nlattr_add_sockaddr(nw, PFLOWNL_GET_DST, sc->sc_flowdst);
149085b71dcfSKristof Provost 	nlattr_add_u32(nw, PFLOWNL_GET_OBSERVATION_DOMAIN,
149185b71dcfSKristof Provost 	    sc->sc_observation_dom);
1492e95025edSKristof Provost 	nlattr_add_u8(nw, PFLOWNL_GET_SOCKET_STATUS, sc->so != NULL);
1493f92d9b1aSKristof Provost 
1494f92d9b1aSKristof Provost 	if (! nlmsg_end(nw)) {
1495f92d9b1aSKristof Provost 		nlmsg_abort(nw);
1496f92d9b1aSKristof Provost 		error = ENOMEM;
1497f92d9b1aSKristof Provost 	}
1498f92d9b1aSKristof Provost 
1499f92d9b1aSKristof Provost out:
1500f92d9b1aSKristof Provost 	NET_EPOCH_EXIT(et);
1501f92d9b1aSKristof Provost 
1502f92d9b1aSKristof Provost 	return (error);
1503f92d9b1aSKristof Provost }
1504f92d9b1aSKristof Provost 
1505f92d9b1aSKristof Provost struct pflow_sockaddr {
1506f92d9b1aSKristof Provost 	union {
1507f92d9b1aSKristof Provost 		struct sockaddr_in in;
1508f92d9b1aSKristof Provost 		struct sockaddr_in6 in6;
1509f92d9b1aSKristof Provost 		struct sockaddr_storage storage;
1510f92d9b1aSKristof Provost 	};
1511f92d9b1aSKristof Provost };
1512f92d9b1aSKristof Provost static bool
1513f92d9b1aSKristof Provost pflow_postparse_sockaddr(void *parsed_args, struct nl_pstate *npt __unused)
1514f92d9b1aSKristof Provost {
1515f92d9b1aSKristof Provost 	struct pflow_sockaddr *s = (struct pflow_sockaddr *)parsed_args;
1516f92d9b1aSKristof Provost 
1517f92d9b1aSKristof Provost 	if (s->storage.ss_family == AF_INET)
1518f92d9b1aSKristof Provost 		s->storage.ss_len = sizeof(struct sockaddr_in);
1519f92d9b1aSKristof Provost 	else if (s->storage.ss_family == AF_INET6)
1520f92d9b1aSKristof Provost 		s->storage.ss_len = sizeof(struct sockaddr_in6);
1521f92d9b1aSKristof Provost 	else
1522f92d9b1aSKristof Provost 		return (false);
1523f92d9b1aSKristof Provost 
1524f92d9b1aSKristof Provost 	return (true);
1525f92d9b1aSKristof Provost }
1526f92d9b1aSKristof Provost 
1527f92d9b1aSKristof Provost #define	_OUT(_field)	offsetof(struct pflow_sockaddr, _field)
1528f92d9b1aSKristof Provost static struct nlattr_parser nla_p_sockaddr[] = {
1529f92d9b1aSKristof Provost 	{ .type = PFLOWNL_ADDR_FAMILY, .off = _OUT(in.sin_family), .cb = nlattr_get_uint8 },
1530f92d9b1aSKristof Provost 	{ .type = PFLOWNL_ADDR_PORT, .off = _OUT(in.sin_port), .cb = nlattr_get_uint16 },
1531f92d9b1aSKristof Provost 	{ .type = PFLOWNL_ADDR_IP, .off = _OUT(in.sin_addr), .cb = nlattr_get_in_addr },
1532f92d9b1aSKristof Provost 	{ .type = PFLOWNL_ADDR_IP6, .off = _OUT(in6.sin6_addr), .cb = nlattr_get_in6_addr },
1533f92d9b1aSKristof Provost };
1534f92d9b1aSKristof Provost NL_DECLARE_ATTR_PARSER_EXT(addr_parser, nla_p_sockaddr, pflow_postparse_sockaddr);
1535f92d9b1aSKristof Provost #undef _OUT
1536f92d9b1aSKristof Provost 
1537f92d9b1aSKristof Provost struct pflow_parsed_set {
1538f92d9b1aSKristof Provost 	int id;
1539f92d9b1aSKristof Provost 	uint16_t version;
1540f92d9b1aSKristof Provost 	struct sockaddr_storage src;
1541f92d9b1aSKristof Provost 	struct sockaddr_storage dst;
154285b71dcfSKristof Provost 	uint32_t observation_dom;
1543f92d9b1aSKristof Provost };
1544f92d9b1aSKristof Provost #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1545f92d9b1aSKristof Provost #define	_OUT(_field)	offsetof(struct pflow_parsed_set, _field)
1546f92d9b1aSKristof Provost static const struct nlattr_parser nla_p_set[] = {
1547f92d9b1aSKristof Provost 	{ .type = PFLOWNL_SET_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
1548f92d9b1aSKristof Provost 	{ .type = PFLOWNL_SET_VERSION, .off = _OUT(version), .cb = nlattr_get_uint16 },
1549f92d9b1aSKristof Provost 	{ .type = PFLOWNL_SET_SRC, .off = _OUT(src), .arg = &addr_parser, .cb = nlattr_get_nested },
1550f92d9b1aSKristof Provost 	{ .type = PFLOWNL_SET_DST, .off = _OUT(dst), .arg = &addr_parser, .cb = nlattr_get_nested },
155185b71dcfSKristof Provost 	{ .type = PFLOWNL_SET_OBSERVATION_DOMAIN, .off = _OUT(observation_dom), .cb = nlattr_get_uint32 },
1552f92d9b1aSKristof Provost };
1553f92d9b1aSKristof Provost static const struct nlfield_parser nlf_p_set[] = {};
1554f92d9b1aSKristof Provost #undef _IN
1555f92d9b1aSKristof Provost #undef _OUT
1556f92d9b1aSKristof Provost NL_DECLARE_PARSER(set_parser, struct genlmsghdr, nlf_p_set, nla_p_set);
1557f92d9b1aSKristof Provost 
1558f92d9b1aSKristof Provost static int
1559f92d9b1aSKristof Provost pflow_set(struct pflow_softc *sc, const struct pflow_parsed_set *pflowr, struct ucred *cred)
1560f92d9b1aSKristof Provost {
1561f92d9b1aSKristof Provost 	struct thread		*td;
1562f92d9b1aSKristof Provost 	struct socket		*so;
1563f92d9b1aSKristof Provost 	int			 error = 0;
1564f92d9b1aSKristof Provost 
1565f92d9b1aSKristof Provost 	td = curthread;
1566f92d9b1aSKristof Provost 
1567f92d9b1aSKristof Provost 	PFLOW_ASSERT(sc);
1568f92d9b1aSKristof Provost 
1569f92d9b1aSKristof Provost 	if (pflowr->version != 0) {
1570f92d9b1aSKristof Provost 		switch(pflowr->version) {
1571f92d9b1aSKristof Provost 		case PFLOW_PROTO_5:
1572f92d9b1aSKristof Provost 		case PFLOW_PROTO_10:
1573f92d9b1aSKristof Provost 			break;
1574f92d9b1aSKristof Provost 		default:
1575f92d9b1aSKristof Provost 			return(EINVAL);
1576f92d9b1aSKristof Provost 		}
1577f92d9b1aSKristof Provost 	}
1578f92d9b1aSKristof Provost 
1579f92d9b1aSKristof Provost 	pflow_flush(sc);
1580f92d9b1aSKristof Provost 
1581f92d9b1aSKristof Provost 	if (pflowr->dst.ss_len != 0) {
1582f92d9b1aSKristof Provost 		if (sc->sc_flowdst != NULL &&
1583f92d9b1aSKristof Provost 		    sc->sc_flowdst->sa_family != pflowr->dst.ss_family) {
1584f92d9b1aSKristof Provost 			free(sc->sc_flowdst, M_DEVBUF);
1585f92d9b1aSKristof Provost 			sc->sc_flowdst = NULL;
1586f92d9b1aSKristof Provost 			if (sc->so != NULL) {
1587f92d9b1aSKristof Provost 				soclose(sc->so);
1588f92d9b1aSKristof Provost 				sc->so = NULL;
1589f92d9b1aSKristof Provost 			}
1590f92d9b1aSKristof Provost 		}
1591f92d9b1aSKristof Provost 
1592f92d9b1aSKristof Provost 		switch (pflowr->dst.ss_family) {
1593f92d9b1aSKristof Provost 		case AF_INET:
1594f92d9b1aSKristof Provost 			if (sc->sc_flowdst == NULL) {
1595f92d9b1aSKristof Provost 				if ((sc->sc_flowdst = malloc(
1596f92d9b1aSKristof Provost 				    sizeof(struct sockaddr_in),
1597f92d9b1aSKristof Provost 				    M_DEVBUF,  M_NOWAIT)) == NULL)
1598f92d9b1aSKristof Provost 					return (ENOMEM);
1599f92d9b1aSKristof Provost 			}
1600f92d9b1aSKristof Provost 			memcpy(sc->sc_flowdst, &pflowr->dst,
1601f92d9b1aSKristof Provost 			    sizeof(struct sockaddr_in));
1602f92d9b1aSKristof Provost 			sc->sc_flowdst->sa_len = sizeof(struct
1603f92d9b1aSKristof Provost 			    sockaddr_in);
1604f92d9b1aSKristof Provost 			break;
1605f92d9b1aSKristof Provost 		case AF_INET6:
1606f92d9b1aSKristof Provost 			if (sc->sc_flowdst == NULL) {
1607f92d9b1aSKristof Provost 				if ((sc->sc_flowdst = malloc(
1608f92d9b1aSKristof Provost 				    sizeof(struct sockaddr_in6),
1609f92d9b1aSKristof Provost 				    M_DEVBUF, M_NOWAIT)) == NULL)
1610f92d9b1aSKristof Provost 					return (ENOMEM);
1611f92d9b1aSKristof Provost 			}
1612f92d9b1aSKristof Provost 			memcpy(sc->sc_flowdst, &pflowr->dst,
1613f92d9b1aSKristof Provost 			    sizeof(struct sockaddr_in6));
1614f92d9b1aSKristof Provost 			sc->sc_flowdst->sa_len = sizeof(struct
1615f92d9b1aSKristof Provost 			    sockaddr_in6);
1616f92d9b1aSKristof Provost 			break;
1617f92d9b1aSKristof Provost 		default:
1618f92d9b1aSKristof Provost 			break;
1619f92d9b1aSKristof Provost 		}
1620f92d9b1aSKristof Provost 	}
1621f92d9b1aSKristof Provost 
1622f92d9b1aSKristof Provost 	if (pflowr->src.ss_len != 0) {
1623f92d9b1aSKristof Provost 		if (sc->sc_flowsrc != NULL)
1624f92d9b1aSKristof Provost 			free(sc->sc_flowsrc, M_DEVBUF);
1625f92d9b1aSKristof Provost 		sc->sc_flowsrc = NULL;
1626f92d9b1aSKristof Provost 		if (sc->so != NULL) {
1627f92d9b1aSKristof Provost 			soclose(sc->so);
1628f92d9b1aSKristof Provost 			sc->so = NULL;
1629f92d9b1aSKristof Provost 		}
1630f92d9b1aSKristof Provost 		switch(pflowr->src.ss_family) {
1631f92d9b1aSKristof Provost 		case AF_INET:
1632f92d9b1aSKristof Provost 			if ((sc->sc_flowsrc = malloc(
1633f92d9b1aSKristof Provost 			    sizeof(struct sockaddr_in),
1634f92d9b1aSKristof Provost 			    M_DEVBUF, M_NOWAIT)) == NULL)
1635f92d9b1aSKristof Provost 				return (ENOMEM);
1636f92d9b1aSKristof Provost 			memcpy(sc->sc_flowsrc, &pflowr->src,
1637f92d9b1aSKristof Provost 			    sizeof(struct sockaddr_in));
1638f92d9b1aSKristof Provost 			sc->sc_flowsrc->sa_len = sizeof(struct
1639f92d9b1aSKristof Provost 			    sockaddr_in);
1640f92d9b1aSKristof Provost 			break;
1641f92d9b1aSKristof Provost 		case AF_INET6:
1642f92d9b1aSKristof Provost 			if ((sc->sc_flowsrc = malloc(
1643f92d9b1aSKristof Provost 			    sizeof(struct sockaddr_in6),
1644f92d9b1aSKristof Provost 			    M_DEVBUF, M_NOWAIT)) == NULL)
1645f92d9b1aSKristof Provost 				return (ENOMEM);
1646f92d9b1aSKristof Provost 			memcpy(sc->sc_flowsrc, &pflowr->src,
1647f92d9b1aSKristof Provost 			    sizeof(struct sockaddr_in6));
1648f92d9b1aSKristof Provost 			sc->sc_flowsrc->sa_len = sizeof(struct
1649f92d9b1aSKristof Provost 			    sockaddr_in6);
1650f92d9b1aSKristof Provost 			break;
1651f92d9b1aSKristof Provost 		default:
1652f92d9b1aSKristof Provost 			break;
1653f92d9b1aSKristof Provost 		}
1654f92d9b1aSKristof Provost 	}
1655f92d9b1aSKristof Provost 
1656f92d9b1aSKristof Provost 	if (sc->so == NULL) {
1657f92d9b1aSKristof Provost 		if (pflowvalidsockaddr(sc->sc_flowdst, 0)) {
1658f92d9b1aSKristof Provost 			error = socreate(sc->sc_flowdst->sa_family,
1659f92d9b1aSKristof Provost 			    &so, SOCK_DGRAM, IPPROTO_UDP, cred, td);
1660f92d9b1aSKristof Provost 			if (error)
1661f92d9b1aSKristof Provost 				return (error);
1662f92d9b1aSKristof Provost 			if (pflowvalidsockaddr(sc->sc_flowsrc, 1)) {
1663f92d9b1aSKristof Provost 				error = sobind(so, sc->sc_flowsrc, td);
1664f92d9b1aSKristof Provost 				if (error) {
1665f92d9b1aSKristof Provost 					soclose(so);
1666f92d9b1aSKristof Provost 					return (error);
1667f92d9b1aSKristof Provost 				}
1668f92d9b1aSKristof Provost 			}
1669f92d9b1aSKristof Provost 			sc->so = so;
1670f92d9b1aSKristof Provost 		}
1671f92d9b1aSKristof Provost 	} else if (!pflowvalidsockaddr(sc->sc_flowdst, 0)) {
1672f92d9b1aSKristof Provost 		soclose(sc->so);
1673f92d9b1aSKristof Provost 		sc->so = NULL;
1674f92d9b1aSKristof Provost 	}
1675f92d9b1aSKristof Provost 
167685b71dcfSKristof Provost 	if (pflowr->observation_dom != 0)
167785b71dcfSKristof Provost 		sc->sc_observation_dom = pflowr->observation_dom;
167885b71dcfSKristof Provost 
1679f92d9b1aSKristof Provost 	/* error check is above */
1680f92d9b1aSKristof Provost 	if (pflowr->version != 0)
1681f92d9b1aSKristof Provost 		sc->sc_version = pflowr->version;
1682f92d9b1aSKristof Provost 
1683f92d9b1aSKristof Provost 	pflow_setmtu(sc, ETHERMTU);
1684f92d9b1aSKristof Provost 
1685f92d9b1aSKristof Provost 	switch (sc->sc_version) {
1686f92d9b1aSKristof Provost 	case PFLOW_PROTO_5:
1687f92d9b1aSKristof Provost 		callout_stop(&sc->sc_tmo6);
1688f92d9b1aSKristof Provost 		callout_stop(&sc->sc_tmo_tmpl);
1689f92d9b1aSKristof Provost 		break;
1690f92d9b1aSKristof Provost 	case PFLOW_PROTO_10:
1691f92d9b1aSKristof Provost 		callout_reset(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT * hz,
1692f92d9b1aSKristof Provost 		    pflow_timeout_tmpl, sc);
1693f92d9b1aSKristof Provost 		break;
1694f92d9b1aSKristof Provost 	default: /* NOTREACHED */
1695f92d9b1aSKristof Provost 		break;
1696f92d9b1aSKristof Provost 	}
1697f92d9b1aSKristof Provost 
1698f92d9b1aSKristof Provost 	return (0);
1699f92d9b1aSKristof Provost }
1700f92d9b1aSKristof Provost 
1701f92d9b1aSKristof Provost static int
1702f92d9b1aSKristof Provost pflow_nl_set(struct nlmsghdr *hdr, struct nl_pstate *npt)
1703f92d9b1aSKristof Provost {
1704f92d9b1aSKristof Provost 	struct epoch_tracker et;
1705f92d9b1aSKristof Provost 	struct pflow_parsed_set s = {};
1706f92d9b1aSKristof Provost 	struct pflow_softc *sc = NULL;
1707f92d9b1aSKristof Provost 	int error;
1708f92d9b1aSKristof Provost 
1709f92d9b1aSKristof Provost 	error = nl_parse_nlmsg(hdr, &set_parser, npt, &s);
1710f92d9b1aSKristof Provost 	if (error != 0)
1711f92d9b1aSKristof Provost 		return (error);
1712f92d9b1aSKristof Provost 
1713f92d9b1aSKristof Provost 	NET_EPOCH_ENTER(et);
1714f92d9b1aSKristof Provost 	CK_LIST_FOREACH(sc, &V_pflowif_list, sc_next) {
1715f92d9b1aSKristof Provost 		if (sc->sc_id == s.id)
1716f92d9b1aSKristof Provost 			break;
1717f92d9b1aSKristof Provost 	}
1718f92d9b1aSKristof Provost 	if (sc == NULL) {
1719f92d9b1aSKristof Provost 		error = ENOENT;
1720f92d9b1aSKristof Provost 		goto out;
1721f92d9b1aSKristof Provost 	}
1722f92d9b1aSKristof Provost 
1723f92d9b1aSKristof Provost 	PFLOW_LOCK(sc);
1724f92d9b1aSKristof Provost 	error = pflow_set(sc, &s, nlp_get_cred(npt->nlp));
1725f92d9b1aSKristof Provost 	PFLOW_UNLOCK(sc);
1726f92d9b1aSKristof Provost 
1727f92d9b1aSKristof Provost out:
1728f92d9b1aSKristof Provost 	NET_EPOCH_EXIT(et);
1729f92d9b1aSKristof Provost 	return (error);
1730f92d9b1aSKristof Provost }
1731f92d9b1aSKristof Provost 
1732f92d9b1aSKristof Provost static const struct genl_cmd pflow_cmds[] = {
1733f92d9b1aSKristof Provost 	{
1734f92d9b1aSKristof Provost 		.cmd_num = PFLOWNL_CMD_LIST,
1735f92d9b1aSKristof Provost 		.cmd_name = "LIST",
1736f92d9b1aSKristof Provost 		.cmd_cb = pflow_nl_list,
1737f92d9b1aSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1738f92d9b1aSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1739f92d9b1aSKristof Provost 	},
1740f92d9b1aSKristof Provost 	{
1741f92d9b1aSKristof Provost 		.cmd_num = PFLOWNL_CMD_CREATE,
1742f92d9b1aSKristof Provost 		.cmd_name = "CREATE",
1743f92d9b1aSKristof Provost 		.cmd_cb = pflow_nl_create,
1744f92d9b1aSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1745f92d9b1aSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1746f92d9b1aSKristof Provost 	},
1747f92d9b1aSKristof Provost 	{
1748f92d9b1aSKristof Provost 		.cmd_num = PFLOWNL_CMD_DEL,
1749f92d9b1aSKristof Provost 		.cmd_name = "DEL",
1750f92d9b1aSKristof Provost 		.cmd_cb = pflow_nl_del,
1751f92d9b1aSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1752f92d9b1aSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1753f92d9b1aSKristof Provost 	},
1754f92d9b1aSKristof Provost 	{
1755f92d9b1aSKristof Provost 		.cmd_num = PFLOWNL_CMD_GET,
1756f92d9b1aSKristof Provost 		.cmd_name = "GET",
1757f92d9b1aSKristof Provost 		.cmd_cb = pflow_nl_get,
1758f92d9b1aSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1759f92d9b1aSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1760f92d9b1aSKristof Provost 	},
1761f92d9b1aSKristof Provost 	{
1762f92d9b1aSKristof Provost 		.cmd_num = PFLOWNL_CMD_SET,
1763f92d9b1aSKristof Provost 		.cmd_name = "SET",
1764f92d9b1aSKristof Provost 		.cmd_cb = pflow_nl_set,
1765f92d9b1aSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1766f92d9b1aSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1767f92d9b1aSKristof Provost 	},
1768f92d9b1aSKristof Provost };
1769f92d9b1aSKristof Provost 
1770f92d9b1aSKristof Provost static const struct nlhdr_parser *all_parsers[] = {
1771f92d9b1aSKristof Provost 	&del_parser,
1772f92d9b1aSKristof Provost 	&get_parser,
1773f92d9b1aSKristof Provost 	&set_parser,
1774f92d9b1aSKristof Provost };
1775f92d9b1aSKristof Provost 
1776f92d9b1aSKristof Provost static int
1777f92d9b1aSKristof Provost pflow_init(void)
1778f92d9b1aSKristof Provost {
1779f92d9b1aSKristof Provost 	bool ret;
1780f92d9b1aSKristof Provost 	int family_id __diagused;
1781f92d9b1aSKristof Provost 
1782f92d9b1aSKristof Provost 	NL_VERIFY_PARSERS(all_parsers);
1783f92d9b1aSKristof Provost 
1784f92d9b1aSKristof Provost 	family_id = genl_register_family(PFLOWNL_FAMILY_NAME, 0, 2, PFLOWNL_CMD_MAX);
1785f92d9b1aSKristof Provost 	MPASS(family_id != 0);
1786f92d9b1aSKristof Provost 	ret = genl_register_cmds(PFLOWNL_FAMILY_NAME, pflow_cmds, NL_ARRAY_LEN(pflow_cmds));
1787f92d9b1aSKristof Provost 
1788f92d9b1aSKristof Provost 	return (ret ? 0 : ENODEV);
1789f92d9b1aSKristof Provost }
1790f92d9b1aSKristof Provost 
1791f92d9b1aSKristof Provost static void
1792f92d9b1aSKristof Provost pflow_uninit(void)
1793f92d9b1aSKristof Provost {
1794f92d9b1aSKristof Provost 	genl_unregister_family(PFLOWNL_FAMILY_NAME);
1795f92d9b1aSKristof Provost }
1796f92d9b1aSKristof Provost 
1797f92d9b1aSKristof Provost static int
1798f92d9b1aSKristof Provost pflow_modevent(module_t mod, int type, void *data)
1799f92d9b1aSKristof Provost {
1800f92d9b1aSKristof Provost 	int error = 0;
1801f92d9b1aSKristof Provost 
1802f92d9b1aSKristof Provost 	switch (type) {
1803f92d9b1aSKristof Provost 	case MOD_LOAD:
1804f92d9b1aSKristof Provost 		error = pflow_init();
1805f92d9b1aSKristof Provost 		break;
1806f92d9b1aSKristof Provost 	case MOD_UNLOAD:
1807f92d9b1aSKristof Provost 		pflow_uninit();
1808f92d9b1aSKristof Provost 		break;
1809f92d9b1aSKristof Provost 	default:
1810f92d9b1aSKristof Provost 		error = EINVAL;
1811f92d9b1aSKristof Provost 		break;
1812f92d9b1aSKristof Provost 	}
1813f92d9b1aSKristof Provost 
1814f92d9b1aSKristof Provost 	return (error);
1815f92d9b1aSKristof Provost }
1816f92d9b1aSKristof Provost 
1817f92d9b1aSKristof Provost static moduledata_t pflow_mod = {
1818f92d9b1aSKristof Provost 	pflowname,
1819f92d9b1aSKristof Provost 	pflow_modevent,
1820f92d9b1aSKristof Provost 	0
1821f92d9b1aSKristof Provost };
1822f92d9b1aSKristof Provost 
1823f92d9b1aSKristof Provost DECLARE_MODULE(pflow, pflow_mod, SI_SUB_PROTO_FIREWALL, SI_ORDER_ANY);
1824f92d9b1aSKristof Provost MODULE_VERSION(pflow, 1);
1825f92d9b1aSKristof Provost MODULE_DEPEND(pflow, pf, PF_MODVER, PF_MODVER, PF_MODVER);
1826