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