Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F157666379
D43106.id131560.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
64 KB
Referenced Files
None
Subscribers
None
D43106.id131560.diff
View Options
diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile
--- a/sbin/ifconfig/Makefile
+++ b/sbin/ifconfig/Makefile
@@ -48,6 +48,7 @@
SRCS+= ifgroup.c # ...
.if ${MK_PF} != "no"
SRCS+= ifpfsync.c # pfsync(4) support
+SRCS+= ifpflow.c # pflow(4) support
.endif
SRCS+= ifbridge.c # bridge support
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd November 08, 2023
+.Dd December 06, 2023
.Dt IFCONFIG 8
.Os
.Sh NAME
@@ -2863,6 +2863,36 @@
.Xr gre 4 will always accept GRE packets with invalid or absent keys.
This command will result in a four byte MTU reduction on the interface.
.El
+.El
+.Ss Packet Filter Flow Information Parameters
+The following parameters are specific to
+.Xr pflow 4
+interfaces:
+.Bl -tag -width indent
+.Bl -tag -width Ds
+.It Cm flowdst Ar addr : Ns Ar port
+Set the receiver address and the port for
+.Xr pflow 4
+packets.
+Both must be defined to export pflow data.
+.Ar addr
+is the IP address and
+.Ar port
+is the port number of the flow collector.
+Pflow data will be sent to this address/port.
+.It Cm -flowdst
+Unset the receiver address and stop sending pflow data.
+.It Cm flowsrc Ar addr Ns Oo : Ns Ar port Oc
+Set the source IP address for pflow packets.
+.Ar addr
+is the IP address used as sender of the UDP packets and may be used to
+identify the source of the data on the pflow collector.
+.It Cm -flowsrc
+Unset the source address.
+.It Cm pflowproto Ar n
+Set the protocol version.
+The default is version 5.
+
.Ss Packet Filter State Table Sychronisation Parameters
The following parameters are specific to
.Xr pfsync 4
@@ -3281,6 +3311,7 @@
.Xr carp 4 ,
.Xr gif 4 ,
.Xr netintro 4 ,
+.Xr pflow 4,
.Xr pfsync 4 ,
.Xr polling 4 ,
.Xr vlan 4 ,
diff --git a/sbin/ifconfig/ifpflow.c b/sbin/ifconfig/ifpflow.c
new file mode 100644
--- /dev/null
+++ b/sbin/ifconfig/ifpflow.c
@@ -0,0 +1,283 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Rubicon Communications, LLC (Netgate)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+#include <net/if_pflow.h>
+
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ifconfig.h"
+
+static void
+pflow_status(if_ctx *ctx)
+{
+ struct pflowreq preq = {};
+ struct ifreq ifr = {};
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int error;
+ char buf[INET6_ADDRSTRLEN];
+
+ ifr.ifr_data = (caddr_t)&preq;
+
+ if (ioctl_ctx_ifr(ctx, SIOCGETPFLOW, &ifr) == -1)
+ return;
+
+ if (preq.flowsrc.ss_family == AF_INET ||
+ preq.flowsrc.ss_family == AF_INET6) {
+ error = getnameinfo((struct sockaddr *)&preq.flowsrc,
+ preq.flowsrc.ss_len, buf, sizeof(buf), NULL, 0,
+ NI_NUMERICHOST);
+ if (error)
+ err(1, "sender: %s", gai_strerror(error));
+ }
+
+ printf("\tpflow: ");
+ switch (preq.flowsrc.ss_family) {
+ case AF_INET:
+ sin = (struct sockaddr_in *)&preq.flowsrc;
+ if (sin->sin_addr.s_addr != INADDR_ANY) {
+ printf("sender: %s", buf);
+ if (sin->sin_port != 0)
+ printf(":%u", ntohs(sin->sin_port));
+ printf(" ");
+ }
+ break;
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *)&preq.flowsrc;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+ printf("sender: [%s]", buf);
+ if (sin6->sin6_port != 0)
+ printf(":%u", ntohs(sin6->sin6_port));
+ printf(" ");
+ }
+ default:
+ break;
+ }
+ if (preq.flowdst.ss_family == AF_INET ||
+ preq.flowdst.ss_family == AF_INET6) {
+ error = getnameinfo((struct sockaddr *)&preq.flowdst,
+ preq.flowdst.ss_len, buf, sizeof(buf), NULL, 0,
+ NI_NUMERICHOST);
+ if (error)
+ err(1, "receiver: %s", gai_strerror(error));
+ }
+ switch (preq.flowdst.ss_family) {
+ case AF_INET:
+ sin = (struct sockaddr_in *)&preq.flowdst;
+ printf("receiver: %s:",
+ sin->sin_addr.s_addr != INADDR_ANY ? buf : "INVALID");
+ if (sin->sin_port == 0)
+ printf("%s ", "INVALID");
+ else
+ printf("%u ", ntohs(sin->sin_port));
+ break;
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *)&preq.flowdst;
+ printf("receiver: [%s]:",
+ !IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ? buf :
+ "INVALID");
+ if (sin6->sin6_port == 0)
+ printf("%s ", "INVALID");
+ else
+ printf("%u ", ntohs(sin6->sin6_port));
+ break;
+ default:
+ printf("receiver: INVALID:INVALID ");
+ break;
+ }
+ printf("version: %d\n", preq.version);
+}
+
+static void
+pflow_addr(const char *val, struct sockaddr_storage *ss)
+{
+ struct addrinfo hints, *res0;
+ int error, flag;
+ char *cp, *ip, *port;
+ char buf[sysconf(_SC_HOST_NAME_MAX) + 1 + sizeof(":65535")];
+
+ if (strlcpy(buf, val, sizeof(buf)) >= sizeof(buf))
+ errx(1, "%s bad value", val);
+
+ port = NULL;
+ cp = buf;
+ if (*cp == '[')
+ flag = 1;
+ else
+ flag = 0;
+
+ for (; *cp; ++cp) {
+ if (*cp == ']' && *(cp + 1) == ':' && flag) {
+ *cp = '\0';
+ *(cp + 1) = '\0';
+ port = cp + 2;
+ break;
+ }
+ if (*cp == ']' && *(cp + 1) == '\0' && flag) {
+ *cp = '\0';
+ port = NULL;
+ break;
+ }
+ if (*cp == ':' && !flag) {
+ *cp = '\0';
+ port = cp + 1;
+ break;
+ }
+ }
+
+ ip = buf;
+ if (flag)
+ ip++;
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+
+ if ((error = getaddrinfo(ip, port, &hints, &res0)) != 0)
+ errx(1, "error in parsing address string: %s",
+ gai_strerror(error));
+
+ memcpy(ss, res0->ai_addr, res0->ai_addr->sa_len);
+ freeaddrinfo(res0);
+}
+
+static void
+setpflow_sender(if_ctx *ctx, const char *val, int d __unused)
+{
+ struct pflowreq preq = {};
+ struct ifreq ifr = {};
+
+ ifr.ifr_data = (caddr_t)&preq;
+ preq.addrmask |= PFLOW_MASK_SRCIP;
+ pflow_addr(val, &preq.flowsrc);
+
+ if (ioctl_ctx_ifr(ctx, SIOCSETPFLOW, &ifr) == -1)
+ err(1, "SIOCSETPFLOW");
+}
+
+static void
+unsetpflow_sender(if_ctx *ctx, const char *val __unused, int d __unused)
+{
+ struct pflowreq preq = {};
+ struct ifreq ifr = {};
+
+ preq.addrmask |= PFLOW_MASK_SRCIP;
+ ifr.ifr_data = (caddr_t)&preq;
+
+ if (ioctl_ctx_ifr(ctx, SIOCSETPFLOW, &ifr) == -1)
+ err(1, "SIOCSETPFLOW");
+}
+
+static void
+setpflow_receiver(if_ctx *ctx, const char *val, int d __unused)
+{
+ struct pflowreq preq = {};
+ struct ifreq ifr = {};
+
+ ifr.ifr_data = (caddr_t)&preq;
+ preq.addrmask |= PFLOW_MASK_DSTIP;
+ pflow_addr(val, &preq.flowdst);
+
+ if (ioctl_ctx_ifr(ctx, SIOCSETPFLOW, &ifr) == -1)
+ err(1, "SIOCSETPFLOW");
+}
+
+static void
+unsetpflow_receiver(if_ctx *ctx, const char *val __unused, int d __unused)
+{
+ struct pflowreq preq = {};
+ struct ifreq ifr = {};
+
+ ifr.ifr_data = (caddr_t)&preq;
+ preq.addrmask |= PFLOW_MASK_DSTIP;
+
+ if (ioctl_ctx_ifr(ctx, SIOCSETPFLOW, &ifr) == -1)
+ err(1, "SIOCSETPFLOW");
+}
+
+static void
+setpflow_proto(if_ctx *ctx, const char *val, int d __unused)
+{
+ struct pflow_protos ppr[] = PFLOW_PROTOS;
+ struct pflowreq preq = {};
+ unsigned int i;
+
+ struct ifreq ifr = {};
+
+ ifr.ifr_data = (caddr_t)&preq;
+
+ preq.version = PFLOW_PROTO_MAX;
+
+ for (i = 0; i < (sizeof(ppr) / sizeof(ppr[0])); i++) {
+ if (strcmp(val, ppr[i].ppr_name) == 0) {
+ preq.version = ppr[i].ppr_proto;
+ break;
+ }
+ }
+ if (preq.version == PFLOW_PROTO_MAX)
+ errx(1, "Invalid pflow protocol: %s", val);
+
+ preq.addrmask |= PFLOW_MASK_VERSION;
+
+ if (ioctl_ctx_ifr(ctx, SIOCSETPFLOW, &ifr) == -1)
+ err(1, "SIOCSETPFLOW");
+}
+
+static struct cmd pflow_cmds[] = {
+ DEF_CMD_ARG("flowsrc", setpflow_sender),
+ DEF_CMD("-flowsrc", 1, unsetpflow_sender),
+ DEF_CMD_ARG("flowdst", setpflow_receiver),
+ DEF_CMD("-flowdst", 1, unsetpflow_receiver),
+ DEF_CMD_ARG("pflowproto", setpflow_proto),
+};
+static struct afswtch af_pflow = {
+ .af_name = "af_pflow",
+ .af_af = AF_UNSPEC,
+ .af_other_status = pflow_status,
+};
+
+static __constructor void
+pflow_ctor(void)
+{
+ for (size_t i = 0; i < nitems(pflow_cmds); i++)
+ cmd_register(&pflow_cmds[i]);
+ af_register(&af_pflow);
+}
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -434,6 +434,7 @@
pcm.4 \
${_pf.4} \
${_pflog.4} \
+ ${_pflow.4} \
${_pfsync.4} \
pim.4 \
pms.4 \
@@ -969,6 +970,7 @@
.if ${MK_PF} != "no"
_pf.4= pf.4
_pflog.4= pflog.4
+_pflow.4= pflow.4
_pfsync.4= pfsync.4
.endif
diff --git a/share/man/man4/pflow.4 b/share/man/man4/pflow.4
new file mode 100644
--- /dev/null
+++ b/share/man/man4/pflow.4
@@ -0,0 +1,136 @@
+.\" $OpenBSD: pflow.4,v 1.19 2014/03/29 11:26:03 florian Exp $
+.\"
+.\" Copyright (c) 2008 Henning Brauer <henning@openbsd.org>
+.\" Copyright (c) 2008 Joerg Goltermann <jg@osn.de>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: March 29 2014 $
+.Dt PFLOW 4
+.Os
+.Sh NAME
+.Nm pflow
+.Nd kernel interface for pflow data export
+.Sh SYNOPSIS
+.Cd "pseudo-device pflow"
+.Sh DESCRIPTION
+The
+.Nm
+interface is a pseudo-device which exports
+.Nm
+accounting data from the kernel using
+.Xr udp 4
+packets.
+.Nm
+is compatible with netflow version 5 and IPFIX (10).
+The data is extracted from the
+.Xr pf 4
+state table.
+.Pp
+Multiple
+.Nm
+interfaces can be created at runtime using the
+.Ic ifconfig pflow Ns Ar N Ic create
+command.
+Each interface must be configured with a flow receiver IP address
+and a flow receiver port number.
+.Pp
+Only states created by a rule marked with the
+.Ar pflow
+keyword are exported by the
+.Nm
+interface.
+.Pp
+The
+.Nm
+interface will attempt to export multiple
+.Nm
+records in one
+UDP packet, but will not hold a record for longer than 30 seconds.
+The packet size and thus the maximum number of flows is controlled by the
+.Cm mtu
+parameter of
+.Xr ifconfig 8 .
+.Pp
+Each packet seen on this interface has one header and a variable number of
+flows.
+The header indicates the version of the protocol, number of
+flows in the packet, a unique sequence number, system time, and an engine
+ID and type.
+Header and flow structs are defined in
+.In net/if_pflow.h .
+.Pp
+There is a one-to-one correspondence between packets seen by
+.Xr bpf 4
+on the
+.Nm
+interface and packets sent out to the flow receiver.
+That is, a packet with 30 flows on
+.Nm
+means that the same 30 flows were sent out to the receiver.
+.Pp
+The
+.Nm
+source and destination addresses are controlled by
+.Xr ifconfig 8 .
+.Cm flowsrc
+is the sender IP address of the UDP packet which can be used
+to identify the source of the data on the
+.Nm
+collector.
+.Cm flowdst
+defines the collector IP address and the port.
+The
+.Cm flowdst
+IP address and port must be defined to enable the export of flows.
+.Pp
+For example, the following command sets 10.0.0.1 as the source
+and 10.0.0.2:1234 as destination:
+.Bd -literal -offset indent
+# ifconfig pflow0 flowsrc 10.0.0.1 flowdst 10.0.0.2:1234
+.Ed
+.Pp
+The protocol is set to IPFIX with the following command:
+.Bd -literal -offset indent
+# ifconfig pflow0 pflowproto 10
+.Ed
+.Sh SEE ALSO
+.Xr netintro 4 ,
+.Xr pf 4 ,
+.Xr udp 4 ,
+.Xr pf.conf 5 ,
+.Xr ifconfig 8 ,
+.Xr tcpdump 8
+.Sh STANDARDS
+.Rs
+.%A B. Claise
+.%D January 2008
+.%R RFC 5101
+.%T "Specification of the IP Flow Information Export (IPFIX) Protocol for the Exchange of IP Traffic Flow Information"
+.Re
+.Sh HISTORY
+The
+.Nm
+device first appeared in
+.Ox 4.5 .
+.Sh BUGS
+A state created by
+.Xr pfsync 4
+can have a creation or expiration time before the machine came up.
+In this case,
+.Nm
+pretends such flows were created or expired when the machine came up.
+.Pp
+The IPFIX implementation is incomplete:
+The required transport protocol SCTP is not supported.
+Transport over TCP and DTLS protected flow export is also not supported.
diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -4502,6 +4502,7 @@
netpfil/ipfw/pmod/tcpmod.c optional inet ipfirewall_pmod
netpfil/pf/if_pflog.c optional pflog pf inet
netpfil/pf/if_pfsync.c optional pfsync pf inet
+netpfil/pf/if_pflow.c optional pflow pf inet
netpfil/pf/pf.c optional pf inet
netpfil/pf/pf_if.c optional pf inet
netpfil/pf/pf_ioctl.c optional pf inet
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -306,6 +306,7 @@
${_pcfclock} \
${_pf} \
${_pflog} \
+ ${_pflow} \
${_pfsync} \
plip \
${_pms} \
@@ -611,6 +612,7 @@
${MK_INET6_SUPPORT} != "no")) || defined(ALL_MODULES)
_pf= pf
_pflog= pflog
+_pflow= pflow
.if ${MK_INET_SUPPORT} != "no"
_pfsync= pfsync
.endif
diff --git a/sys/modules/pflow/Makefile b/sys/modules/pflow/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/pflow/Makefile
@@ -0,0 +1,16 @@
+.PATH: ${SRCTOP}/sys/netpfil/pf
+
+KMOD= pflow
+SRCS= if_pflow.c \
+ opt_pf.h opt_inet.h opt_inet6.h opt_global.h
+SRCS+= bus_if.h device_if.h
+
+.if !defined(KERNBUILDDIR)
+.if defined(VIMAGE)
+opt_global.h:
+ echo "#define VIMAGE 1" >> ${.TARGET}
+CFLAGS+= -include opt_global.h
+.endif
+.endif
+
+.include <bsd.kmod.mk>
diff --git a/sys/net/if_pflow.h b/sys/net/if_pflow.h
new file mode 100644
--- /dev/null
+++ b/sys/net/if_pflow.h
@@ -0,0 +1,295 @@
+/* $OpenBSD: if_pflow.h,v 1.19 2022/11/23 15:12:27 mvs Exp $ */
+
+/*
+ * Copyright (c) 2008 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2008 Joerg Goltermann <jg@osn.de>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _NET_IF_PFLOW_H_
+#define _NET_IF_PFLOW_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/rmlock.h>
+#include <sys/interrupt.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_private.h>
+#include <net/pfvar.h>
+
+#include <netinet/ip.h>
+#endif
+
+#define PFLOW_ID_LEN sizeof(u_int64_t)
+
+#define PFLOW_MAXFLOWS 30
+#define PFLOW_ENGINE_TYPE 42
+#define PFLOW_ENGINE_ID 42
+#define PFLOW_MAXBYTES 0xffffffff
+#define PFLOW_TIMEOUT 30
+#define PFLOW_TMPL_TIMEOUT 30 /* rfc 5101 10.3.6 (p.40) recommends 600 */
+
+#define PFLOW_IPFIX_TMPL_SET_ID 2
+
+/* RFC 5102 Information Element Identifiers */
+
+#define PFIX_IE_octetDeltaCount 1
+#define PFIX_IE_packetDeltaCount 2
+#define PFIX_IE_protocolIdentifier 4
+#define PFIX_IE_ipClassOfService 5
+#define PFIX_IE_sourceTransportPort 7
+#define PFIX_IE_sourceIPv4Address 8
+#define PFIX_IE_ingressInterface 10
+#define PFIX_IE_destinationTransportPort 11
+#define PFIX_IE_destinationIPv4Address 12
+#define PFIX_IE_egressInterface 14
+#define PFIX_IE_flowEndSysUpTime 21
+#define PFIX_IE_flowStartSysUpTime 22
+#define PFIX_IE_sourceIPv6Address 27
+#define PFIX_IE_destinationIPv6Address 28
+#define PFIX_IE_flowStartMilliseconds 152
+#define PFIX_IE_flowEndMilliseconds 153
+
+struct pflow_flow {
+ u_int32_t src_ip;
+ u_int32_t dest_ip;
+ u_int32_t nexthop_ip;
+ u_int16_t if_index_in;
+ u_int16_t if_index_out;
+ u_int32_t flow_packets;
+ u_int32_t flow_octets;
+ u_int32_t flow_start;
+ u_int32_t flow_finish;
+ u_int16_t src_port;
+ u_int16_t dest_port;
+ u_int8_t pad1;
+ u_int8_t tcp_flags;
+ u_int8_t protocol;
+ u_int8_t tos;
+ u_int16_t src_as;
+ u_int16_t dest_as;
+ u_int8_t src_mask;
+ u_int8_t dest_mask;
+ u_int16_t pad2;
+} __packed;
+
+struct pflow_set_header {
+ u_int16_t set_id;
+ u_int16_t set_length; /* total length of the set,
+ in octets, including the set header */
+} __packed;
+
+#define PFLOW_SET_HDRLEN sizeof(struct pflow_set_header)
+
+struct pflow_tmpl_hdr {
+ u_int16_t tmpl_id;
+ u_int16_t field_count;
+} __packed;
+
+struct pflow_tmpl_fspec {
+ u_int16_t field_id;
+ u_int16_t len;
+} __packed;
+
+/* update pflow_clone_create() when changing pflow_ipfix_tmpl_ipv4 */
+struct pflow_ipfix_tmpl_ipv4 {
+ struct pflow_tmpl_hdr h;
+ struct pflow_tmpl_fspec src_ip;
+ struct pflow_tmpl_fspec dest_ip;
+ struct pflow_tmpl_fspec if_index_in;
+ struct pflow_tmpl_fspec if_index_out;
+ struct pflow_tmpl_fspec packets;
+ struct pflow_tmpl_fspec octets;
+ struct pflow_tmpl_fspec start;
+ struct pflow_tmpl_fspec finish;
+ struct pflow_tmpl_fspec src_port;
+ struct pflow_tmpl_fspec dest_port;
+ struct pflow_tmpl_fspec tos;
+ struct pflow_tmpl_fspec protocol;
+#define PFLOW_IPFIX_TMPL_IPV4_FIELD_COUNT 12
+#define PFLOW_IPFIX_TMPL_IPV4_ID 256
+} __packed;
+
+/* update pflow_clone_create() when changing pflow_ipfix_tmpl_v6 */
+struct pflow_ipfix_tmpl_ipv6 {
+ struct pflow_tmpl_hdr h;
+ struct pflow_tmpl_fspec src_ip;
+ struct pflow_tmpl_fspec dest_ip;
+ struct pflow_tmpl_fspec if_index_in;
+ struct pflow_tmpl_fspec if_index_out;
+ struct pflow_tmpl_fspec packets;
+ struct pflow_tmpl_fspec octets;
+ struct pflow_tmpl_fspec start;
+ struct pflow_tmpl_fspec finish;
+ struct pflow_tmpl_fspec src_port;
+ struct pflow_tmpl_fspec dest_port;
+ struct pflow_tmpl_fspec tos;
+ struct pflow_tmpl_fspec protocol;
+#define PFLOW_IPFIX_TMPL_IPV6_FIELD_COUNT 12
+#define PFLOW_IPFIX_TMPL_IPV6_ID 257
+} __packed;
+
+struct pflow_ipfix_tmpl {
+ struct pflow_set_header set_header;
+ struct pflow_ipfix_tmpl_ipv4 ipv4_tmpl;
+ struct pflow_ipfix_tmpl_ipv6 ipv6_tmpl;
+} __packed;
+
+struct pflow_ipfix_flow4 {
+ u_int32_t src_ip; /* sourceIPv4Address*/
+ u_int32_t dest_ip; /* destinationIPv4Address */
+ u_int32_t if_index_in; /* ingressInterface */
+ u_int32_t if_index_out; /* egressInterface */
+ u_int64_t flow_packets; /* packetDeltaCount */
+ u_int64_t flow_octets; /* octetDeltaCount */
+ int64_t flow_start; /* flowStartMilliseconds */
+ int64_t flow_finish; /* flowEndMilliseconds */
+ u_int16_t src_port; /* sourceTransportPort */
+ u_int16_t dest_port; /* destinationTransportPort */
+ u_int8_t tos; /* ipClassOfService */
+ u_int8_t protocol; /* protocolIdentifier */
+ /* XXX padding needed? */
+} __packed;
+
+struct pflow_ipfix_flow6 {
+ struct in6_addr src_ip; /* sourceIPv6Address */
+ struct in6_addr dest_ip; /* destinationIPv6Address */
+ u_int32_t if_index_in; /* ingressInterface */
+ u_int32_t if_index_out; /* egressInterface */
+ u_int64_t flow_packets; /* packetDeltaCount */
+ u_int64_t flow_octets; /* octetDeltaCount */
+ int64_t flow_start; /* flowStartMilliseconds */
+ int64_t flow_finish; /* flowEndMilliseconds */
+ u_int16_t src_port; /* sourceTransportPort */
+ u_int16_t dest_port; /* destinationTransportPort */
+ u_int8_t tos; /* ipClassOfService */
+ u_int8_t protocol; /* protocolIdentifier */
+ /* XXX padding needed? */
+} __packed;
+
+#ifdef _KERNEL
+
+struct pflow_softc {
+ struct mtx sc_lock;
+
+ int sc_dying; /* [N] */
+ struct ifnet *sc_if;
+
+ unsigned int sc_count;
+ unsigned int sc_count4;
+ unsigned int sc_count6;
+ unsigned int sc_maxcount;
+ unsigned int sc_maxcount4;
+ unsigned int sc_maxcount6;
+ u_int64_t sc_gcounter;
+ u_int32_t sc_sequence;
+ struct callout sc_tmo;
+ struct callout sc_tmo6;
+ struct callout sc_tmo_tmpl;
+ struct intr_event *sc_swi_ie;
+ void *sc_swi_cookie;
+ struct mbufq sc_outputqueue;
+ struct task sc_outputtask;
+ struct socket *so; /* [p] */
+ struct sockaddr *sc_flowsrc;
+ struct sockaddr *sc_flowdst;
+ struct pflow_ipfix_tmpl sc_tmpl_ipfix;
+ u_int8_t sc_version;
+ struct mbuf *sc_mbuf; /* current cumulative mbuf */
+ struct mbuf *sc_mbuf6; /* current cumulative mbuf */
+ CK_LIST_ENTRY(pflow_softc) sc_next;
+ struct epoch_context sc_epoch_ctx;
+};
+
+#endif /* _KERNEL */
+
+struct pflow_header {
+ u_int16_t version;
+ u_int16_t count;
+ u_int32_t uptime_ms;
+ u_int32_t time_sec;
+ u_int32_t time_nanosec;
+ u_int32_t flow_sequence;
+ u_int8_t engine_type;
+ u_int8_t engine_id;
+ u_int8_t reserved1;
+ u_int8_t reserved2;
+} __packed;
+
+#define PFLOW_HDRLEN sizeof(struct pflow_header)
+
+struct pflow_v10_header {
+ u_int16_t version;
+ u_int16_t length;
+ u_int32_t time_sec;
+ u_int32_t flow_sequence;
+ u_int32_t observation_dom;
+} __packed;
+
+#define PFLOW_IPFIX_HDRLEN sizeof(struct pflow_v10_header)
+
+struct pflowstats {
+ u_int64_t pflow_flows;
+ u_int64_t pflow_packets;
+ u_int64_t pflow_onomem;
+ u_int64_t pflow_oerrors;
+};
+
+/* Supported flow protocols */
+#define PFLOW_PROTO_5 5 /* original pflow */
+#define PFLOW_PROTO_10 10 /* ipfix */
+#define PFLOW_PROTO_MAX 11
+
+#define PFLOW_PROTO_DEFAULT PFLOW_PROTO_5
+
+struct pflow_protos {
+ const char *ppr_name;
+ u_int8_t ppr_proto;
+};
+
+#define PFLOW_PROTOS { \
+ { "5", PFLOW_PROTO_5 }, \
+ { "10", PFLOW_PROTO_10 }, \
+}
+
+/*
+ * Configuration structure for SIOCSETPFLOW SIOCGETPFLOW
+ */
+struct pflowreq {
+ struct sockaddr_storage flowsrc;
+ struct sockaddr_storage flowdst;
+ u_int16_t addrmask;
+ u_int8_t version;
+#define PFLOW_MASK_SRCIP 0x01
+#define PFLOW_MASK_DSTIP 0x02
+#define PFLOW_MASK_VERSION 0x04
+};
+
+#define SIOCSETPFLOW _IOW('i', 253, struct ifreq)
+#define SIOCGETPFLOW _IOWR('i', 254, struct ifreq)
+
+#ifdef _KERNEL
+int export_pflow(struct pf_kstate *);
+int pflow_sysctl(int *, u_int, void *, size_t *, void *, size_t);
+#endif /* _KERNEL */
+
+#endif /* _NET_IF_PFLOW_H_ */
diff --git a/sys/net/if_types.h b/sys/net/if_types.h
--- a/sys/net/if_types.h
+++ b/sys/net/if_types.h
@@ -254,6 +254,7 @@
IFT_PFLOG = 0xf6, /* PF packet filter logging */
IFT_PFSYNC = 0xf7, /* PF packet filter synchronization */
IFT_WIREGUARD = 0xf8, /* WireGuard tunnel */
+ IFT_PFLOW = 0xf9, /* PF packet filter logging */
} ifType;
/*
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1067,12 +1067,14 @@
struct pf_rule_actions act;
u_int16_t tag;
u_int8_t rt;
+ u_int16_t if_index_in;
+ u_int16_t if_index_out;
};
/*
* Size <= fits 11 objects per page on LP64. Try to not grow the struct beyond that.
*/
-_Static_assert(sizeof(struct pf_kstate) <= 368, "pf_kstate size crosses 368 bytes");
+_Static_assert(sizeof(struct pf_kstate) <= 372, "pf_kstate size crosses 372 bytes");
#endif
/*
diff --git a/sys/netpfil/pf/if_pflow.c b/sys/netpfil/pf/if_pflow.c
new file mode 100644
--- /dev/null
+++ b/sys/netpfil/pf/if_pflow.c
@@ -0,0 +1,1346 @@
+/* $OpenBSD: if_pflow.c,v 1.100 2023/11/09 08:53:20 mvs Exp $ */
+
+/*
+ * Copyright (c) 2023 Rubicon Communications, LLC (Netgate)
+ * Copyright (c) 2011 Florian Obser <florian@narrans.de>
+ * Copyright (c) 2011 Sebastian Benoit <benoit-lists@fb12.de>
+ * Copyright (c) 2008 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2008 Joerg Goltermann <jg@osn.de>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/callout.h>
+#include <sys/endian.h>
+#include <sys/interrupt.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/priv.h>
+
+#include <net/if.h>
+#include <net/if_clone.h>
+#include <net/if_types.h>
+#include <net/bpf.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/tcp.h>
+
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <netinet/in_pcb.h>
+
+#include <net/pfvar.h>
+#include <net/if_pflow.h>
+#include "net/if_var.h"
+
+#define PFLOW_MINMTU \
+ (sizeof(struct pflow_header) + sizeof(struct pflow_flow))
+
+#ifdef PFLOWDEBUG
+#define DPRINTF(x) do { printf x ; } while (0)
+#else
+#define DPRINTF(x)
+#endif
+
+int pflow_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
+ struct route *rt);
+void pflow_output_process(void *);
+int pflow_clone_create(struct if_clone *, int, caddr_t);
+void pflow_clone_destroy(struct ifnet *);
+int pflow_set(struct pflow_softc *, struct pflowreq *);
+int pflow_calc_mtu(struct pflow_softc *, int, int);
+void pflow_setmtu(struct pflow_softc *, int);
+int pflowvalidsockaddr(const struct sockaddr *, int);
+int pflowioctl(struct ifnet *, u_long, caddr_t);
+
+struct mbuf *pflow_get_mbuf(struct pflow_softc *, u_int16_t);
+void pflow_flush(struct pflow_softc *);
+int pflow_sendout_v5(struct pflow_softc *);
+int pflow_sendout_ipfix(struct pflow_softc *, sa_family_t);
+int pflow_sendout_ipfix_tmpl(struct pflow_softc *);
+int pflow_sendout_mbuf(struct pflow_softc *, struct mbuf *);
+void pflow_timeout(void *);
+void pflow_timeout6(void *);
+void pflow_timeout_tmpl(void *);
+void copy_flow_data(struct pflow_flow *, struct pflow_flow *,
+ struct pf_kstate *, struct pf_state_key *, int, int);
+void copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 *,
+ struct pflow_ipfix_flow4 *, struct pf_kstate *, struct pf_state_key *,
+ struct pflow_softc *, int, int);
+void copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *,
+ struct pflow_ipfix_flow6 *, struct pf_kstate *, struct pf_state_key *,
+ struct pflow_softc *, int, int);
+int pflow_pack_flow(struct pf_kstate *, struct pf_state_key *,
+ struct pflow_softc *);
+int pflow_pack_flow_ipfix(struct pf_kstate *, struct pf_state_key *,
+ struct pflow_softc *);
+int export_pflow_if(struct pf_kstate*, struct pf_state_key *,
+ struct pflow_softc *);
+int copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc);
+int copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow,
+ struct pflow_softc *sc);
+int copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow,
+ struct pflow_softc *sc);
+
+static const char pflowname[] = "pflow";
+
+/**
+ * Locking concept
+ *
+ * The list of pflow devices (V_pflowif_list) is managed through epoch.
+ * It is safe to read the list without locking (while in NET_EPOCH).
+ * There may only be one simultaneous modifier, hence we need V_pflow_list_mtx
+ * on every add/delete.
+ *
+ * Each pflow interface protects its own data with the sc_lock mutex.
+ *
+ * We do not require any pf locks, and in fact expect to be called without
+ * hashrow locks held.
+ **/
+
+VNET_DEFINE(struct if_clone *, pflow_cloner);
+#define V_pflow_cloner VNET(pflow_cloner)
+VNET_DEFINE(CK_LIST_HEAD(, pflow_softc), pflowif_list);
+#define V_pflowif_list VNET(pflowif_list)
+VNET_DEFINE(struct mtx, pflowif_list_mtx);
+#define V_pflowif_list_mtx VNET(pflowif_list_mtx)
+VNET_DEFINE(struct pflowstats, pflowstats);
+#define V_pflowstats VNET(pflowstats)
+
+#define PFLOW_LOCK(_sc) mtx_lock(&(_sc)->sc_lock)
+#define PFLOW_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_lock)
+#define PFLOW_ASSERT(_sc) mtx_assert(&(_sc)->sc_lock, MA_OWNED)
+
+static void
+vnet_pflowattach(void)
+{
+ CK_LIST_INIT(&V_pflowif_list);
+ mtx_init(&V_pflowif_list_mtx, "pflow interface list mtx", NULL, MTX_DEF);
+ V_pflow_cloner = if_clone_simple(pflowname, pflow_clone_create,
+ pflow_clone_destroy, 0);
+}
+VNET_SYSINIT(vnet_pflowattach, SI_SUB_PROTO_FIREWALL, SI_ORDER_ANY,
+ vnet_pflowattach, NULL);
+
+static void
+vnet_pflowdetach(void)
+{
+ if_clone_detach(V_pflow_cloner);
+
+ MPASS(CK_LIST_EMPTY(&V_pflowif_list));
+ mtx_destroy(&V_pflowif_list_mtx);
+}
+VNET_SYSUNINIT(vnet_pflowdetach, SI_SUB_PROTO_FIREWALL, SI_ORDER_FOURTH,
+ vnet_pflowdetach, NULL);
+
+static void
+vnet_pflow_finalise(void)
+{
+ /*
+ * Ensure we've freed all interfaces, and do not have pending
+ * epoch cleanup calls.
+ */
+ NET_EPOCH_DRAIN_CALLBACKS();
+}
+VNET_SYSUNINIT(vnet_pflow_finalise, SI_SUB_PROTO_FIREWALL, SI_ORDER_THIRD,
+ vnet_pflow_finalise, NULL);
+
+int
+pflow_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
+ struct route *rt)
+{
+ m_freem(m); /* drop packet */
+ return (EAFNOSUPPORT);
+}
+
+void
+pflow_output_process(void *arg)
+{
+ struct mbufq ml;
+ struct pflow_softc *sc = arg;
+ struct mbuf *m;
+
+ mbufq_init(&ml, 0);
+
+ PFLOW_LOCK(sc);
+ mbufq_concat(&ml, &sc->sc_outputqueue);
+ PFLOW_UNLOCK(sc);
+
+ CURVNET_SET(sc->sc_if->if_vnet);
+ while ((m = mbufq_dequeue(&ml)) != NULL) {
+ pflow_sendout_mbuf(sc, m);
+ }
+ CURVNET_RESTORE();
+}
+
+int
+pflow_clone_create(struct if_clone *ifc, int unit, caddr_t param)
+{
+ struct ifnet *ifp;
+ struct pflow_softc *pflowif;
+ int error;
+
+ ifp = if_alloc(IFT_PFLOW);
+ if (ifp == NULL)
+ return (ENOSPC);
+
+ pflowif = malloc(sizeof(*pflowif), M_DEVBUF, M_WAITOK|M_ZERO);
+ mtx_init(&pflowif->sc_lock, "pflowlk", NULL, MTX_DEF);
+ pflowif->sc_version = PFLOW_PROTO_DEFAULT;
+
+ /* ipfix template init */
+ bzero(&pflowif->sc_tmpl_ipfix,sizeof(pflowif->sc_tmpl_ipfix));
+ pflowif->sc_tmpl_ipfix.set_header.set_id =
+ htons(PFLOW_IPFIX_TMPL_SET_ID);
+ pflowif->sc_tmpl_ipfix.set_header.set_length =
+ htons(sizeof(struct pflow_ipfix_tmpl));
+
+ /* ipfix IPv4 template */
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.h.tmpl_id =
+ htons(PFLOW_IPFIX_TMPL_IPV4_ID);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.h.field_count
+ = htons(PFLOW_IPFIX_TMPL_IPV4_FIELD_COUNT);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_ip.field_id =
+ htons(PFIX_IE_sourceIPv4Address);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_ip.len = htons(4);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_ip.field_id =
+ htons(PFIX_IE_destinationIPv4Address);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_ip.len = htons(4);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_in.field_id =
+ htons(PFIX_IE_ingressInterface);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_in.len = htons(4);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_out.field_id =
+ htons(PFIX_IE_egressInterface);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.if_index_out.len = htons(4);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.packets.field_id =
+ htons(PFIX_IE_packetDeltaCount);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.packets.len = htons(8);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.octets.field_id =
+ htons(PFIX_IE_octetDeltaCount);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.octets.len = htons(8);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.start.field_id =
+ htons(PFIX_IE_flowStartMilliseconds);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.start.len = htons(8);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.finish.field_id =
+ htons(PFIX_IE_flowEndMilliseconds);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.finish.len = htons(8);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_port.field_id =
+ htons(PFIX_IE_sourceTransportPort);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.src_port.len = htons(2);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_port.field_id =
+ htons(PFIX_IE_destinationTransportPort);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.dest_port.len = htons(2);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.tos.field_id =
+ htons(PFIX_IE_ipClassOfService);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.tos.len = htons(1);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.protocol.field_id =
+ htons(PFIX_IE_protocolIdentifier);
+ pflowif->sc_tmpl_ipfix.ipv4_tmpl.protocol.len = htons(1);
+
+ /* ipfix IPv6 template */
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.h.tmpl_id =
+ htons(PFLOW_IPFIX_TMPL_IPV6_ID);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.h.field_count =
+ htons(PFLOW_IPFIX_TMPL_IPV6_FIELD_COUNT);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_ip.field_id =
+ htons(PFIX_IE_sourceIPv6Address);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_ip.len = htons(16);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_ip.field_id =
+ htons(PFIX_IE_destinationIPv6Address);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_ip.len = htons(16);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_in.field_id =
+ htons(PFIX_IE_ingressInterface);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_in.len = htons(4);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_out.field_id =
+ htons(PFIX_IE_egressInterface);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.if_index_out.len = htons(4);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.packets.field_id =
+ htons(PFIX_IE_packetDeltaCount);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.packets.len = htons(8);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.octets.field_id =
+ htons(PFIX_IE_octetDeltaCount);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.octets.len = htons(8);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.start.field_id =
+ htons(PFIX_IE_flowStartMilliseconds);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.start.len = htons(8);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.finish.field_id =
+ htons(PFIX_IE_flowEndMilliseconds);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.finish.len = htons(8);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_port.field_id =
+ htons(PFIX_IE_sourceTransportPort);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.src_port.len = htons(2);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_port.field_id =
+ htons(PFIX_IE_destinationTransportPort);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.dest_port.len = htons(2);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.tos.field_id =
+ htons(PFIX_IE_ipClassOfService);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.tos.len = htons(1);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.protocol.field_id =
+ htons(PFIX_IE_protocolIdentifier);
+ pflowif->sc_tmpl_ipfix.ipv6_tmpl.protocol.len = htons(1);
+
+ ifp->if_softc = pflowif;
+ pflowif->sc_if = ifp;
+
+ if_initname(ifp, pflowname, unit);
+ ifp->if_softc = pflowif;
+ ifp->if_ioctl = pflowioctl;
+ ifp->if_output = pflow_output;
+ ifp->if_start = NULL;
+ ifp->if_hdrlen = PFLOW_HDRLEN;
+ ifp->if_flags = IFF_UP;
+ mbufq_init(&pflowif->sc_outputqueue, 8192);
+ pflow_setmtu(pflowif, ETHERMTU);
+
+ callout_init_mtx(&pflowif->sc_tmo, &pflowif->sc_lock, 0);
+ callout_init_mtx(&pflowif->sc_tmo6, &pflowif->sc_lock, 0);
+ callout_init_mtx(&pflowif->sc_tmo_tmpl, &pflowif->sc_lock, 0);
+
+ error = swi_add(&pflowif->sc_swi_ie, pflowname, pflow_output_process,
+ pflowif, SWI_NET, INTR_MPSAFE, &pflowif->sc_swi_cookie);
+ if (error) {
+ free(pflowif, M_DEVBUF);
+ if_free(ifp);
+ return (error);
+ }
+
+ if_attach(ifp);
+
+ /* Insert into list of pflows */
+ mtx_lock(&V_pflowif_list_mtx);
+ CK_LIST_INSERT_HEAD(&V_pflowif_list, pflowif, sc_next);
+ mtx_unlock(&V_pflowif_list_mtx);
+
+ return (0);
+}
+
+static void
+pflow_clone_free_cb(struct epoch_context *ctx)
+{
+ struct pflow_softc *sc;
+
+ sc = __containerof(ctx, struct pflow_softc, sc_epoch_ctx);
+
+ free(sc, M_DEVBUF);
+}
+
+void
+pflow_clone_destroy(struct ifnet *ifp)
+{
+ struct pflow_softc *sc = ifp->if_softc;
+ int error __diagused;
+
+ sc->sc_dying = 1;
+
+ mtx_lock(&V_pflowif_list_mtx);
+ CK_LIST_REMOVE(sc, sc_next);
+ mtx_unlock(&V_pflowif_list_mtx);
+
+ /* Let's be sure no one is using this interface any more. */
+ NET_EPOCH_DRAIN_CALLBACKS();
+
+ error = swi_remove(sc->sc_swi_cookie);
+ MPASS(error == 0);
+ error = intr_event_destroy(sc->sc_swi_ie);
+ MPASS(error == 0);
+
+ callout_drain(&sc->sc_tmo);
+ callout_drain(&sc->sc_tmo6);
+ callout_drain(&sc->sc_tmo_tmpl);
+
+ m_freem(sc->sc_mbuf);
+ m_freem(sc->sc_mbuf6);
+
+ PFLOW_LOCK(sc);
+ mbufq_drain(&sc->sc_outputqueue);
+ if (sc->so != NULL) {
+ soclose(sc->so);
+ sc->so = NULL;
+ }
+ if (sc->sc_flowdst != NULL)
+ free(sc->sc_flowdst, M_DEVBUF);
+ if (sc->sc_flowsrc != NULL)
+ free(sc->sc_flowsrc, M_DEVBUF);
+ PFLOW_UNLOCK(sc);
+
+ if_detach(ifp);
+ if_free(ifp);
+
+ mtx_destroy(&sc->sc_lock);
+
+ NET_EPOCH_CALL(pflow_clone_free_cb, &sc->sc_epoch_ctx);
+}
+
+int
+pflowvalidsockaddr(const struct sockaddr *sa, int ignore_port)
+{
+ const struct sockaddr_in6 *sin6;
+ const struct sockaddr_in *sin;
+
+ if (sa == NULL)
+ return (0);
+ switch(sa->sa_family) {
+ case AF_INET:
+ sin = (const struct sockaddr_in *)sa;
+ return (sin->sin_addr.s_addr != INADDR_ANY &&
+ (ignore_port || sin->sin_port != 0));
+ case AF_INET6:
+ sin6 = (const struct sockaddr_in6 *)sa;
+ return (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
+ (ignore_port || sin6->sin6_port != 0));
+ default:
+ return (0);
+ }
+}
+
+int
+pflow_set(struct pflow_softc *sc, struct pflowreq *pflowr)
+{
+ struct thread *td;
+ struct socket *so;
+ int error = 0;
+
+ td = curthread;
+
+ PFLOW_ASSERT(sc);
+
+ if (pflowr->addrmask & PFLOW_MASK_VERSION) {
+ switch(pflowr->version) {
+ case PFLOW_PROTO_5:
+ case PFLOW_PROTO_10:
+ break;
+ default:
+ return(EINVAL);
+ }
+ }
+
+ pflow_flush(sc);
+
+ if (pflowr->addrmask & PFLOW_MASK_DSTIP) {
+ if (sc->sc_flowdst != NULL &&
+ sc->sc_flowdst->sa_family != pflowr->flowdst.ss_family) {
+ free(sc->sc_flowdst, M_DEVBUF);
+ sc->sc_flowdst = NULL;
+ if (sc->so != NULL) {
+ soclose(sc->so);
+ sc->so = NULL;
+ }
+ }
+
+ switch (pflowr->flowdst.ss_family) {
+ case AF_INET:
+ if (sc->sc_flowdst == NULL) {
+ if ((sc->sc_flowdst = malloc(
+ sizeof(struct sockaddr_in),
+ M_DEVBUF, M_NOWAIT)) == NULL)
+ return (ENOMEM);
+ }
+ memcpy(sc->sc_flowdst, &pflowr->flowdst,
+ sizeof(struct sockaddr_in));
+ sc->sc_flowdst->sa_len = sizeof(struct
+ sockaddr_in);
+ break;
+ case AF_INET6:
+ if (sc->sc_flowdst == NULL) {
+ if ((sc->sc_flowdst = malloc(
+ sizeof(struct sockaddr_in6),
+ M_DEVBUF, M_NOWAIT)) == NULL)
+ return (ENOMEM);
+ }
+ memcpy(sc->sc_flowdst, &pflowr->flowdst,
+ sizeof(struct sockaddr_in6));
+ sc->sc_flowdst->sa_len = sizeof(struct
+ sockaddr_in6);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (pflowr->addrmask & PFLOW_MASK_SRCIP) {
+ if (sc->sc_flowsrc != NULL)
+ free(sc->sc_flowsrc, M_DEVBUF);
+ sc->sc_flowsrc = NULL;
+ if (sc->so != NULL) {
+ soclose(sc->so);
+ sc->so = NULL;
+ }
+ switch(pflowr->flowsrc.ss_family) {
+ case AF_INET:
+ if ((sc->sc_flowsrc = malloc(
+ sizeof(struct sockaddr_in),
+ M_DEVBUF, M_NOWAIT)) == NULL)
+ return (ENOMEM);
+ memcpy(sc->sc_flowsrc, &pflowr->flowsrc,
+ sizeof(struct sockaddr_in));
+ sc->sc_flowsrc->sa_len = sizeof(struct
+ sockaddr_in);
+ break;
+ case AF_INET6:
+ if ((sc->sc_flowsrc = malloc(
+ sizeof(struct sockaddr_in6),
+ M_DEVBUF, M_NOWAIT)) == NULL)
+ return (ENOMEM);
+ memcpy(sc->sc_flowsrc, &pflowr->flowsrc,
+ sizeof(struct sockaddr_in6));
+ sc->sc_flowsrc->sa_len = sizeof(struct
+ sockaddr_in6);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (sc->so == NULL) {
+ if (pflowvalidsockaddr(sc->sc_flowdst, 0)) {
+ error = socreate(sc->sc_flowdst->sa_family,
+ &so, SOCK_DGRAM, IPPROTO_UDP, td->td_ucred, td);
+ if (error)
+ return (error);
+ if (pflowvalidsockaddr(sc->sc_flowsrc, 1)) {
+ error = sobind(so, sc->sc_flowsrc, td);
+ if (error) {
+ soclose(so);
+ return (error);
+ }
+ }
+ sc->so = so;
+ }
+ } else if (!pflowvalidsockaddr(sc->sc_flowdst, 0)) {
+ soclose(sc->so);
+ sc->so = NULL;
+ }
+
+ /* error check is above */
+ if (pflowr->addrmask & PFLOW_MASK_VERSION)
+ sc->sc_version = pflowr->version;
+
+ pflow_setmtu(sc, ETHERMTU);
+
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_5:
+ callout_stop(&sc->sc_tmo6);
+ callout_stop(&sc->sc_tmo_tmpl);
+ break;
+ case PFLOW_PROTO_10:
+ callout_reset(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT * hz,
+ pflow_timeout_tmpl, sc);
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
+
+ return (0);
+}
+
+int
+pflowioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct pflow_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct pflowreq pflowr;
+ int error;
+
+ if (sc->sc_dying)
+ return ENXIO;
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCSIFFLAGS:
+ PFLOW_LOCK(sc);
+ if ((ifp->if_flags & IFF_UP) && sc->so != NULL) {
+ ifp->if_flags |= IFF_UP;
+ sc->sc_gcounter = V_pflowstats.pflow_flows;
+ /* send templates on startup */
+ if (sc->sc_version == PFLOW_PROTO_10)
+ pflow_sendout_ipfix_tmpl(sc);
+ } else
+ ifp->if_flags &= ~IFF_UP;
+ PFLOW_UNLOCK(sc);
+ break;
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu < PFLOW_MINMTU)
+ return (EINVAL);
+ if (ifr->ifr_mtu > MCLBYTES)
+ ifr->ifr_mtu = MCLBYTES;
+ PFLOW_LOCK(sc);
+ if (ifr->ifr_mtu < ifp->if_mtu)
+ pflow_flush(sc);
+ pflow_setmtu(sc, ifr->ifr_mtu);
+ PFLOW_UNLOCK(sc);
+ break;
+
+ case SIOCGETPFLOW:
+ PFLOW_LOCK(sc);
+ bzero(&pflowr, sizeof(pflowr));
+
+ if (sc->sc_flowsrc != NULL)
+ memcpy(&pflowr.flowsrc, sc->sc_flowsrc,
+ sc->sc_flowsrc->sa_len);
+ if (sc->sc_flowdst != NULL)
+ memcpy(&pflowr.flowdst, sc->sc_flowdst,
+ sc->sc_flowdst->sa_len);
+ pflowr.version = sc->sc_version;
+ PFLOW_UNLOCK(sc);
+
+ if ((error = copyout(&pflowr, ifr_data_get_ptr(ifr),
+ sizeof(pflowr))))
+ return (error);
+ break;
+
+ case SIOCSETPFLOW:
+ if ((error = priv_check(curthread, PRIV_NETINET_PF)) != 0)
+ return (error);
+ if ((error = copyin(ifr_data_get_ptr(ifr), &pflowr,
+ sizeof(pflowr))))
+ return (error);
+
+ PFLOW_LOCK(sc);
+ error = pflow_set(sc, &pflowr);
+ if (error != 0) {
+ PFLOW_UNLOCK(sc);
+ return (error);
+ }
+
+ if ((ifp->if_flags & IFF_UP) && sc->so != NULL) {
+ ifp->if_flags |= IFF_UP;
+ sc->sc_gcounter = V_pflowstats.pflow_flows;
+ if (sc->sc_version == PFLOW_PROTO_10)
+ pflow_sendout_ipfix_tmpl(sc);
+ } else
+ ifp->if_flags &= ~IFF_UP;
+ PFLOW_UNLOCK(sc);
+
+ break;
+
+ default:
+ return (ENOTTY);
+ }
+ return (0);
+}
+
+int
+pflow_calc_mtu(struct pflow_softc *sc, int mtu, int hdrsz)
+{
+
+ sc->sc_maxcount4 = (mtu - hdrsz -
+ sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_flow4);
+ sc->sc_maxcount6 = (mtu - hdrsz -
+ sizeof(struct udpiphdr)) / sizeof(struct pflow_ipfix_flow6);
+ if (sc->sc_maxcount4 > PFLOW_MAXFLOWS)
+ sc->sc_maxcount4 = PFLOW_MAXFLOWS;
+ if (sc->sc_maxcount6 > PFLOW_MAXFLOWS)
+ sc->sc_maxcount6 = PFLOW_MAXFLOWS;
+ return (hdrsz + sizeof(struct udpiphdr) +
+ MIN(sc->sc_maxcount4 * sizeof(struct pflow_ipfix_flow4),
+ sc->sc_maxcount6 * sizeof(struct pflow_ipfix_flow6)));
+}
+
+void
+pflow_setmtu(struct pflow_softc *sc, int mtu_req)
+{
+ int mtu;
+
+ mtu = mtu_req;
+
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_5:
+ sc->sc_maxcount = (mtu - sizeof(struct pflow_header) -
+ sizeof(struct udpiphdr)) / sizeof(struct pflow_flow);
+ if (sc->sc_maxcount > PFLOW_MAXFLOWS)
+ sc->sc_maxcount = PFLOW_MAXFLOWS;
+ sc->sc_if->if_mtu = sizeof(struct pflow_header) +
+ sizeof(struct udpiphdr) +
+ sc->sc_maxcount * sizeof(struct pflow_flow);
+ break;
+ case PFLOW_PROTO_10:
+ sc->sc_if->if_mtu =
+ pflow_calc_mtu(sc, mtu, sizeof(struct pflow_v10_header));
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
+}
+
+struct mbuf *
+pflow_get_mbuf(struct pflow_softc *sc, u_int16_t set_id)
+{
+ struct pflow_set_header set_hdr;
+ struct pflow_header h;
+ struct mbuf *m;
+
+ MGETHDR(m, M_NOWAIT, MT_DATA);
+ if (m == NULL) {
+ V_pflowstats.pflow_onomem++;
+ return (NULL);
+ }
+
+ MCLGET(m, M_NOWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ V_pflowstats.pflow_onomem++;
+ return (NULL);
+ }
+
+ m->m_len = m->m_pkthdr.len = 0;
+
+ if (sc == NULL) /* get only a new empty mbuf */
+ return (m);
+
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_5:
+ /* populate pflow_header */
+ h.reserved1 = 0;
+ h.reserved2 = 0;
+ h.count = 0;
+ h.version = htons(PFLOW_PROTO_5);
+ h.flow_sequence = htonl(sc->sc_gcounter);
+ h.engine_type = PFLOW_ENGINE_TYPE;
+ h.engine_id = PFLOW_ENGINE_ID;
+ m_copyback(m, 0, PFLOW_HDRLEN, (caddr_t)&h);
+
+ sc->sc_count = 0;
+ callout_reset(&sc->sc_tmo, PFLOW_TIMEOUT * hz,
+ pflow_timeout, sc);
+ break;
+ case PFLOW_PROTO_10:
+ /* populate pflow_set_header */
+ set_hdr.set_length = 0;
+ set_hdr.set_id = htons(set_id);
+ m_copyback(m, 0, PFLOW_SET_HDRLEN, (caddr_t)&set_hdr);
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
+
+ return (m);
+}
+
+void
+copy_flow_data(struct pflow_flow *flow1, struct pflow_flow *flow2,
+ struct pf_kstate *st, struct pf_state_key *sk, int src, int dst)
+{
+ flow1->src_ip = flow2->dest_ip = sk->addr[src].v4.s_addr;
+ flow1->src_port = flow2->dest_port = sk->port[src];
+ flow1->dest_ip = flow2->src_ip = sk->addr[dst].v4.s_addr;
+ flow1->dest_port = flow2->src_port = sk->port[dst];
+
+ flow1->dest_as = flow2->src_as =
+ flow1->src_as = flow2->dest_as = 0;
+ flow1->if_index_in = htons(st->if_index_in);
+ flow1->if_index_out = htons(st->if_index_out);
+ flow2->if_index_in = htons(st->if_index_out);
+ flow2->if_index_out = htons(st->if_index_in);
+ flow1->dest_mask = flow2->src_mask =
+ flow1->src_mask = flow2->dest_mask = 0;
+
+ flow1->flow_packets = htonl(st->packets[0]);
+ flow2->flow_packets = htonl(st->packets[1]);
+ flow1->flow_octets = htonl(st->bytes[0]);
+ flow2->flow_octets = htonl(st->bytes[1]);
+
+ /*
+ * Pretend the flow was created or expired when the machine came up
+ * when creation is in the future of the last time a package was seen
+ * or was created / expired before this machine came up due to pfsync.
+ */
+ flow1->flow_start = flow2->flow_start = st->creation < 0 ||
+ st->creation > st->expire ? htonl(0) : htonl(st->creation * 1000);
+ flow1->flow_finish = flow2->flow_finish = st->expire < 0 ? htonl(0) :
+ htonl(st->expire * 1000);
+ flow1->tcp_flags = flow2->tcp_flags = 0;
+ flow1->protocol = flow2->protocol = sk->proto;
+ flow1->tos = flow2->tos = st->rule.ptr->tos;
+}
+
+void
+copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 *flow1,
+ struct pflow_ipfix_flow4 *flow2, struct pf_kstate *st,
+ struct pf_state_key *sk, struct pflow_softc *sc, int src, int dst)
+{
+ flow1->src_ip = flow2->dest_ip = sk->addr[src].v4.s_addr;
+ flow1->src_port = flow2->dest_port = sk->port[src];
+ flow1->dest_ip = flow2->src_ip = sk->addr[dst].v4.s_addr;
+ flow1->dest_port = flow2->src_port = sk->port[dst];
+
+ flow1->if_index_in = htonl(st->if_index_in);
+ flow1->if_index_out = htonl(st->if_index_out);
+ flow2->if_index_in = htonl(st->if_index_out);
+ flow2->if_index_out = htonl(st->if_index_in);
+
+ flow1->flow_packets = htobe64(st->packets[0]);
+ flow2->flow_packets = htobe64(st->packets[1]);
+ flow1->flow_octets = htobe64(st->bytes[0]);
+ flow2->flow_octets = htobe64(st->bytes[1]);
+
+ /*
+ * Pretend the flow was created when the machine came up when creation
+ * is in the future of the last time a package was seen due to pfsync.
+ */
+ if (st->creation > st->expire)
+ flow1->flow_start = flow2->flow_start = htobe64((time_second -
+ time_uptime)*1000);
+ else
+ flow1->flow_start = flow2->flow_start = htobe64((time_second -
+ (time_uptime - st->creation))*1000);
+ flow1->flow_finish = flow2->flow_finish = htobe64((time_second -
+ (time_uptime - st->expire))*1000);
+
+ flow1->protocol = flow2->protocol = sk->proto;
+ flow1->tos = flow2->tos = st->rule.ptr->tos;
+}
+
+void
+copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *flow1,
+ struct pflow_ipfix_flow6 *flow2, struct pf_kstate *st,
+ struct pf_state_key *sk, struct pflow_softc *sc, int src, int dst)
+{
+ bcopy(&sk->addr[src].v6, &flow1->src_ip, sizeof(flow1->src_ip));
+ bcopy(&sk->addr[src].v6, &flow2->dest_ip, sizeof(flow2->dest_ip));
+ flow1->src_port = flow2->dest_port = sk->port[src];
+ bcopy(&sk->addr[dst].v6, &flow1->dest_ip, sizeof(flow1->dest_ip));
+ bcopy(&sk->addr[dst].v6, &flow2->src_ip, sizeof(flow2->src_ip));
+ flow1->dest_port = flow2->src_port = sk->port[dst];
+
+ flow1->if_index_in = htonl(st->if_index_in);
+ flow1->if_index_out = htonl(st->if_index_out);
+ flow2->if_index_in = htonl(st->if_index_out);
+ flow2->if_index_out = htonl(st->if_index_in);
+
+ flow1->flow_packets = htobe64(st->packets[0]);
+ flow2->flow_packets = htobe64(st->packets[1]);
+ flow1->flow_octets = htobe64(st->bytes[0]);
+ flow2->flow_octets = htobe64(st->bytes[1]);
+
+ /*
+ * Pretend the flow was created when the machine came up when creation
+ * is in the future of the last time a package was seen due to pfsync.
+ */
+ if (st->creation > st->expire)
+ flow1->flow_start = flow2->flow_start = htobe64((time_second -
+ time_uptime)*1000);
+ else
+ flow1->flow_start = flow2->flow_start = htobe64((time_second -
+ (time_uptime - st->creation))*1000);
+ flow1->flow_finish = flow2->flow_finish = htobe64((time_second -
+ (time_uptime - st->expire))*1000);
+
+ flow1->protocol = flow2->protocol = sk->proto;
+ flow1->tos = flow2->tos = st->rule.ptr->tos;
+}
+
+int
+export_pflow(struct pf_kstate *st)
+{
+ struct pflow_softc *sc = NULL;
+ struct pf_state_key *sk;
+
+ NET_EPOCH_ASSERT();
+
+ sk = st->key[st->direction == PF_IN ? PF_SK_WIRE : PF_SK_STACK];
+
+ CK_LIST_FOREACH(sc, &V_pflowif_list, sc_next) {
+ PFLOW_LOCK(sc);
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_5:
+ if (sk->af == AF_INET)
+ export_pflow_if(st, sk, sc);
+ break;
+ case PFLOW_PROTO_10:
+ if (sk->af == AF_INET || sk->af == AF_INET6)
+ export_pflow_if(st, sk, sc);
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
+ PFLOW_UNLOCK(sc);
+ }
+
+ return (0);
+}
+
+int
+export_pflow_if(struct pf_kstate *st, struct pf_state_key *sk,
+ struct pflow_softc *sc)
+{
+ struct pf_kstate pfs_copy;
+ struct ifnet *ifp = sc->sc_if;
+ u_int64_t bytes[2];
+ int ret = 0;
+
+ if (!(ifp->if_flags & IFF_UP))
+ return (0);
+
+ if (sc->sc_version == PFLOW_PROTO_10)
+ return (pflow_pack_flow_ipfix(st, sk, sc));
+
+ /* PFLOW_PROTO_5 */
+ if ((st->bytes[0] < (u_int64_t)PFLOW_MAXBYTES)
+ && (st->bytes[1] < (u_int64_t)PFLOW_MAXBYTES))
+ return (pflow_pack_flow(st, sk, sc));
+
+ /* flow > PFLOW_MAXBYTES need special handling */
+ bcopy(st, &pfs_copy, sizeof(pfs_copy));
+ bytes[0] = pfs_copy.bytes[0];
+ bytes[1] = pfs_copy.bytes[1];
+
+ while (bytes[0] > PFLOW_MAXBYTES) {
+ pfs_copy.bytes[0] = PFLOW_MAXBYTES;
+ pfs_copy.bytes[1] = 0;
+
+ if ((ret = pflow_pack_flow(&pfs_copy, sk, sc)) != 0)
+ return (ret);
+ if ((bytes[0] - PFLOW_MAXBYTES) > 0)
+ bytes[0] -= PFLOW_MAXBYTES;
+ }
+
+ while (bytes[1] > (u_int64_t)PFLOW_MAXBYTES) {
+ pfs_copy.bytes[1] = PFLOW_MAXBYTES;
+ pfs_copy.bytes[0] = 0;
+
+ if ((ret = pflow_pack_flow(&pfs_copy, sk, sc)) != 0)
+ return (ret);
+ if ((bytes[1] - PFLOW_MAXBYTES) > 0)
+ bytes[1] -= PFLOW_MAXBYTES;
+ }
+
+ pfs_copy.bytes[0] = bytes[0];
+ pfs_copy.bytes[1] = bytes[1];
+
+ return (pflow_pack_flow(&pfs_copy, sk, sc));
+}
+
+int
+copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc)
+{
+ int ret = 0;
+
+ PFLOW_ASSERT(sc);
+
+ if (sc->sc_mbuf == NULL) {
+ if ((sc->sc_mbuf = pflow_get_mbuf(sc, 0)) == NULL)
+ return (ENOBUFS);
+ }
+ m_copyback(sc->sc_mbuf, PFLOW_HDRLEN +
+ (sc->sc_count * sizeof(struct pflow_flow)),
+ sizeof(struct pflow_flow), (caddr_t)flow);
+
+ if (V_pflowstats.pflow_flows == sc->sc_gcounter)
+ V_pflowstats.pflow_flows++;
+ sc->sc_gcounter++;
+ sc->sc_count++;
+
+ if (sc->sc_count >= sc->sc_maxcount)
+ ret = pflow_sendout_v5(sc);
+
+ return(ret);
+}
+
+int
+copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow, struct pflow_softc *sc)
+{
+ int ret = 0;
+
+ PFLOW_ASSERT(sc);
+
+ if (sc->sc_mbuf == NULL) {
+ if ((sc->sc_mbuf =
+ pflow_get_mbuf(sc, PFLOW_IPFIX_TMPL_IPV4_ID)) == NULL) {
+ return (ENOBUFS);
+ }
+ sc->sc_count4 = 0;
+ callout_reset(&sc->sc_tmo, PFLOW_TIMEOUT * hz,
+ pflow_timeout, sc);
+ }
+ m_copyback(sc->sc_mbuf, PFLOW_SET_HDRLEN +
+ (sc->sc_count4 * sizeof(struct pflow_ipfix_flow4)),
+ sizeof(struct pflow_ipfix_flow4), (caddr_t)flow);
+
+ if (V_pflowstats.pflow_flows == sc->sc_gcounter)
+ V_pflowstats.pflow_flows++;
+ sc->sc_gcounter++;
+ sc->sc_count4++;
+
+ if (sc->sc_count4 >= sc->sc_maxcount4)
+ ret = pflow_sendout_ipfix(sc, AF_INET);
+ return(ret);
+}
+
+int
+copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow, struct pflow_softc *sc)
+{
+ int ret = 0;
+
+ PFLOW_ASSERT(sc);
+
+ if (sc->sc_mbuf6 == NULL) {
+ if ((sc->sc_mbuf6 =
+ pflow_get_mbuf(sc, PFLOW_IPFIX_TMPL_IPV6_ID)) == NULL) {
+ return (ENOBUFS);
+ }
+ sc->sc_count6 = 0;
+ callout_reset(&sc->sc_tmo6, PFLOW_TIMEOUT * hz,
+ pflow_timeout6, sc);
+ }
+ m_copyback(sc->sc_mbuf6, PFLOW_SET_HDRLEN +
+ (sc->sc_count6 * sizeof(struct pflow_ipfix_flow6)),
+ sizeof(struct pflow_ipfix_flow6), (caddr_t)flow);
+
+ if (V_pflowstats.pflow_flows == sc->sc_gcounter)
+ V_pflowstats.pflow_flows++;
+ sc->sc_gcounter++;
+ sc->sc_count6++;
+
+ if (sc->sc_count6 >= sc->sc_maxcount6)
+ ret = pflow_sendout_ipfix(sc, AF_INET6);
+
+ return(ret);
+}
+
+int
+pflow_pack_flow(struct pf_kstate *st, struct pf_state_key *sk,
+ struct pflow_softc *sc)
+{
+ struct pflow_flow flow1;
+ struct pflow_flow flow2;
+ int ret = 0;
+
+ bzero(&flow1, sizeof(flow1));
+ bzero(&flow2, sizeof(flow2));
+
+ if (st->direction == PF_OUT)
+ copy_flow_data(&flow1, &flow2, st, sk, 1, 0);
+ else
+ copy_flow_data(&flow1, &flow2, st, sk, 0, 1);
+
+ if (st->bytes[0] != 0) /* first flow from state */
+ ret = copy_flow_to_m(&flow1, sc);
+
+ if (st->bytes[1] != 0) /* second flow from state */
+ ret = copy_flow_to_m(&flow2, sc);
+
+ return (ret);
+}
+
+int
+pflow_pack_flow_ipfix(struct pf_kstate *st, struct pf_state_key *sk,
+ struct pflow_softc *sc)
+{
+ struct pflow_ipfix_flow4 flow4_1, flow4_2;
+ struct pflow_ipfix_flow6 flow6_1, flow6_2;
+ int ret = 0;
+ if (sk->af == AF_INET) {
+ bzero(&flow4_1, sizeof(flow4_1));
+ bzero(&flow4_2, sizeof(flow4_2));
+
+ if (st->direction == PF_OUT)
+ copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc,
+ 1, 0);
+ else
+ copy_flow_ipfix_4_data(&flow4_1, &flow4_2, st, sk, sc,
+ 0, 1);
+
+ if (st->bytes[0] != 0) /* first flow from state */
+ ret = copy_flow_ipfix_4_to_m(&flow4_1, sc);
+
+ if (st->bytes[1] != 0) /* second flow from state */
+ ret = copy_flow_ipfix_4_to_m(&flow4_2, sc);
+ } else if (sk->af == AF_INET6) {
+ bzero(&flow6_1, sizeof(flow6_1));
+ bzero(&flow6_2, sizeof(flow6_2));
+
+ if (st->direction == PF_OUT)
+ copy_flow_ipfix_6_data(&flow6_1, &flow6_2, st, sk, sc,
+ 1, 0);
+ else
+ copy_flow_ipfix_6_data(&flow6_1, &flow6_2, st, sk, sc,
+ 0, 1);
+
+ if (st->bytes[0] != 0) /* first flow from state */
+ ret = copy_flow_ipfix_6_to_m(&flow6_1, sc);
+
+ if (st->bytes[1] != 0) /* second flow from state */
+ ret = copy_flow_ipfix_6_to_m(&flow6_2, sc);
+ }
+ return (ret);
+}
+
+void
+pflow_timeout(void *v)
+{
+ struct pflow_softc *sc = v;
+
+ PFLOW_ASSERT(sc);
+ CURVNET_SET(sc->sc_if->if_vnet);
+
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_5:
+ pflow_sendout_v5(sc);
+ break;
+ case PFLOW_PROTO_10:
+ pflow_sendout_ipfix(sc, AF_INET);
+ break;
+ default: /* NOTREACHED */
+ panic("Unsupported version %d", sc->sc_version);
+ break;
+ }
+
+ CURVNET_RESTORE();
+}
+
+void
+pflow_timeout6(void *v)
+{
+ struct pflow_softc *sc = v;
+
+ PFLOW_ASSERT(sc);
+
+ if (sc->sc_version != PFLOW_PROTO_10)
+ return;
+
+ CURVNET_SET(sc->sc_if->if_vnet);
+ pflow_sendout_ipfix(sc, AF_INET6);
+ CURVNET_RESTORE();
+}
+
+void
+pflow_timeout_tmpl(void *v)
+{
+ struct pflow_softc *sc = v;
+
+ PFLOW_ASSERT(sc);
+
+ if (sc->sc_version != PFLOW_PROTO_10)
+ return;
+
+ CURVNET_SET(sc->sc_if->if_vnet);
+ pflow_sendout_ipfix_tmpl(sc);
+ CURVNET_RESTORE();
+}
+
+void
+pflow_flush(struct pflow_softc *sc)
+{
+ PFLOW_ASSERT(sc);
+
+ switch (sc->sc_version) {
+ case PFLOW_PROTO_5:
+ pflow_sendout_v5(sc);
+ break;
+ case PFLOW_PROTO_10:
+ pflow_sendout_ipfix(sc, AF_INET);
+ pflow_sendout_ipfix(sc, AF_INET6);
+ break;
+ default: /* NOTREACHED */
+ break;
+ }
+}
+
+int
+pflow_sendout_v5(struct pflow_softc *sc)
+{
+ struct mbuf *m = sc->sc_mbuf;
+ struct pflow_header *h;
+ struct ifnet *ifp = sc->sc_if;
+ struct timespec tv;
+
+ PFLOW_ASSERT(sc);
+
+ if (m == NULL)
+ return (0);
+
+ sc->sc_mbuf = NULL;
+ if (!(ifp->if_flags & IFF_UP)) {
+ m_freem(m);
+ return (0);
+ }
+
+ V_pflowstats.pflow_packets++;
+ h = mtod(m, struct pflow_header *);
+ h->count = htons(sc->sc_count);
+
+ /* populate pflow_header */
+ h->uptime_ms = htonl(time_uptime * 1000);
+
+ getnanotime(&tv);
+ h->time_sec = htonl(tv.tv_sec); /* XXX 2038 */
+ h->time_nanosec = htonl(tv.tv_nsec);
+ if (mbufq_enqueue(&sc->sc_outputqueue, m) == 0)
+ swi_sched(sc->sc_swi_cookie, 0);
+
+ return (0);
+}
+
+int
+pflow_sendout_ipfix(struct pflow_softc *sc, sa_family_t af)
+{
+ struct mbuf *m;
+ struct pflow_v10_header *h10;
+ struct pflow_set_header *set_hdr;
+ struct ifnet *ifp = sc->sc_if;
+ u_int32_t count;
+ int set_length;
+
+ PFLOW_ASSERT(sc);
+
+ switch (af) {
+ case AF_INET:
+ m = sc->sc_mbuf;
+ callout_stop(&sc->sc_tmo);
+ if (m == NULL)
+ return (0);
+ sc->sc_mbuf = NULL;
+ count = sc->sc_count4;
+ set_length = sizeof(struct pflow_set_header)
+ + sc->sc_count4 * sizeof(struct pflow_ipfix_flow4);
+ break;
+ case AF_INET6:
+ m = sc->sc_mbuf6;
+ callout_stop(&sc->sc_tmo6);
+ if (m == NULL)
+ return (0);
+ sc->sc_mbuf6 = NULL;
+ count = sc->sc_count6;
+ set_length = sizeof(struct pflow_set_header)
+ + sc->sc_count6 * sizeof(struct pflow_ipfix_flow6);
+ break;
+ default:
+ panic("Unsupported AF %d", af);
+ }
+
+ if (!(ifp->if_flags & IFF_UP)) {
+ m_freem(m);
+ return (0);
+ }
+
+ V_pflowstats.pflow_packets++;
+ set_hdr = mtod(m, struct pflow_set_header *);
+ set_hdr->set_length = htons(set_length);
+
+ /* populate pflow_header */
+ M_PREPEND(m, sizeof(struct pflow_v10_header), M_NOWAIT);
+ if (m == NULL) {
+ V_pflowstats.pflow_onomem++;
+ return (ENOBUFS);
+ }
+ h10 = mtod(m, struct pflow_v10_header *);
+ h10->version = htons(PFLOW_PROTO_10);
+ h10->length = htons(PFLOW_IPFIX_HDRLEN + set_length);
+ h10->time_sec = htonl(time_second); /* XXX 2038 */
+ h10->flow_sequence = htonl(sc->sc_sequence);
+ sc->sc_sequence += count;
+ h10->observation_dom = htonl(PFLOW_ENGINE_TYPE);
+ if (mbufq_enqueue(&sc->sc_outputqueue, m) == 0)
+ swi_sched(sc->sc_swi_cookie, 0);
+
+ return (0);
+}
+
+int
+pflow_sendout_ipfix_tmpl(struct pflow_softc *sc)
+{
+ struct mbuf *m;
+ struct pflow_v10_header *h10;
+ struct ifnet *ifp = sc->sc_if;
+
+ PFLOW_ASSERT(sc);
+
+ if (!(ifp->if_flags & IFF_UP)) {
+ return (0);
+ }
+ m = pflow_get_mbuf(sc, 0);
+ if (m == NULL)
+ return (0);
+ m_copyback(m, 0, sizeof(struct pflow_ipfix_tmpl),
+ (caddr_t)&sc->sc_tmpl_ipfix);
+
+ V_pflowstats.pflow_packets++;
+
+ /* populate pflow_header */
+ M_PREPEND(m, sizeof(struct pflow_v10_header), M_NOWAIT);
+ if (m == NULL) {
+ V_pflowstats.pflow_onomem++;
+ return (ENOBUFS);
+ }
+ h10 = mtod(m, struct pflow_v10_header *);
+ h10->version = htons(PFLOW_PROTO_10);
+ h10->length = htons(PFLOW_IPFIX_HDRLEN + sizeof(struct
+ pflow_ipfix_tmpl));
+ h10->time_sec = htonl(time_second); /* XXX 2038 */
+ h10->flow_sequence = htonl(sc->sc_sequence);
+ h10->observation_dom = htonl(PFLOW_ENGINE_TYPE);
+
+ callout_reset(&sc->sc_tmo_tmpl, PFLOW_TMPL_TIMEOUT * hz,
+ pflow_timeout_tmpl, sc);
+ if (mbufq_enqueue(&sc->sc_outputqueue, m) == 0)
+ swi_sched(sc->sc_swi_cookie, 0);
+ return (0);
+}
+
+int
+pflow_sendout_mbuf(struct pflow_softc *sc, struct mbuf *m)
+{
+ if_inc_counter(sc->sc_if, IFCOUNTER_OPACKETS, 1);
+ if_inc_counter(sc->sc_if, IFCOUNTER_OBYTES, m->m_pkthdr.len);
+
+ if (sc->so == NULL) {
+ m_freem(m);
+ return (EINVAL);
+ }
+ return (sosend(sc->so, sc->sc_flowdst, NULL, m, NULL, 0, curthread));
+}
+
+static int
+pflow_modevent(module_t mod, int type, void *data)
+{
+ int error = 0;
+
+ /*switch (type) {
+ case MOD_LOAD:
+ error = pflow_init();
+ break;
+ case MOD_UNLOAD:
+ pflow_uninit();
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }*/
+
+ return (error);
+}
+
+static moduledata_t pflow_mod = {
+ pflowname,
+ pflow_modevent,
+ 0
+};
+
+DECLARE_MODULE(pflow, pflow_mod, SI_SUB_PROTO_FIREWALL, SI_ORDER_ANY);
+MODULE_VERSION(pflow, 1);
+MODULE_DEPEND(pflow, pf, PF_MODVER, PF_MODVER, PF_MODVER);
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -1226,6 +1226,7 @@
struct pf_kstate *si, *olds = NULL;
int idx;
+ NET_EPOCH_ASSERT();
KASSERT(s->refs == 0, ("%s: state not pristine", __func__));
KASSERT(s->key[PF_SK_WIRE] == NULL, ("%s: state has key", __func__));
KASSERT(s->key[PF_SK_STACK] == NULL, ("%s: state has key", __func__));
@@ -1392,6 +1393,8 @@
struct pf_state_key *sks = s->key[PF_SK_STACK];
struct pf_keyhash *kh;
+ NET_EPOCH_ASSERT();
+
pf_sctp_multihome_detach_addr(s);
if (sks != NULL) {
@@ -1491,6 +1494,8 @@
struct pf_kstate *cur;
int error;
+ NET_EPOCH_ASSERT();
+
KASSERT(TAILQ_EMPTY(&sks->states[0]) && TAILQ_EMPTY(&sks->states[1]),
("%s: sks not pristine", __func__));
KASSERT(TAILQ_EMPTY(&skw->states[0]) && TAILQ_EMPTY(&skw->states[1]),
@@ -1915,6 +1920,8 @@
void
pf_purge_thread(void *unused __unused)
{
+ struct epoch_tracker et;
+
VNET_ITERATOR_DECL(vnet_iter);
sx_xlock(&pf_end_lock);
@@ -1922,6 +1929,7 @@
sx_sleep(pf_purge_thread, &pf_end_lock, 0, "pftm", pf_purge_thread_period);
VNET_LIST_RLOCK();
+ NET_EPOCH_ENTER(et);
VNET_FOREACH(vnet_iter) {
CURVNET_SET(vnet_iter);
@@ -1958,6 +1966,7 @@
}
CURVNET_RESTORE();
}
+ NET_EPOCH_EXIT(et);
VNET_LIST_RUNLOCK();
}
@@ -2097,6 +2106,7 @@
{
struct pf_idhash *ih = &V_pf_idhash[PF_IDHASH(s)];
+ NET_EPOCH_ASSERT();
PF_HASHROW_ASSERT(ih);
if (s->timeout == PFTM_UNLINKED) {
@@ -8432,6 +8442,13 @@
SDT_PROBE4(pf, ip, test, done, action, reason, r, s);
+ if (s && action != PF_DROP) {
+ if (!s->if_index_in && dir == PF_IN)
+ s->if_index_in = ifp->if_index;
+ else if (!s->if_index_out && dir == PF_OUT)
+ s->if_index_out = ifp->if_index;
+ }
+
if (s)
PF_STATE_UNLOCK(s);
@@ -8982,6 +8999,13 @@
break;
}
+ if (s && action != PF_DROP) {
+ if (!s->if_index_in && dir == PF_IN)
+ s->if_index_in = ifp->if_index;
+ else if (!s->if_index_out && dir == PF_OUT)
+ s->if_index_out = ifp->if_index;
+ }
+
if (s)
PF_STATE_UNLOCK(s);
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -5784,9 +5784,11 @@
static void
pf_clear_all_states(void)
{
+ struct epoch_tracker et;
struct pf_kstate *s;
u_int i;
+ NET_EPOCH_ENTER(et);
for (i = 0; i <= pf_hashmask; i++) {
struct pf_idhash *ih = &V_pf_idhash[i];
relock:
@@ -5800,6 +5802,7 @@
}
PF_HASHROW_UNLOCK(ih);
}
+ NET_EPOCH_EXIT(et);
}
static int
@@ -5943,6 +5946,8 @@
int idx;
unsigned int killed = 0, dir;
+ NET_EPOCH_ASSERT();
+
for (unsigned int i = 0; i <= pf_hashmask; i++) {
struct pf_idhash *ih = &V_pf_idhash[i];
@@ -6004,8 +6009,10 @@
static void
pf_killstates(struct pf_kstate_kill *kill, unsigned int *killed)
{
+ struct epoch_tracker et;
struct pf_kstate *s;
+ NET_EPOCH_ENTER(et);
if (kill->psk_pfcmp.id) {
if (kill->psk_pfcmp.creatorid == 0)
kill->psk_pfcmp.creatorid = V_pf_status.hostid;
@@ -6014,13 +6021,14 @@
pf_unlink_state(s);
*killed = 1;
}
+ NET_EPOCH_EXIT(et);
return;
}
for (unsigned int i = 0; i <= pf_hashmask; i++)
*killed += pf_killstates_row(kill, &V_pf_idhash[i]);
- return;
+ NET_EPOCH_EXIT(et);
}
static int
@@ -6085,6 +6093,7 @@
pf_clearstates_nv(struct pfioc_nv *nv)
{
struct pf_kstate_kill kill;
+ struct epoch_tracker et;
nvlist_t *nvl = NULL;
void *nvlpacked = NULL;
int error = 0;
@@ -6111,7 +6120,9 @@
if (error)
ERROUT(error);
+ NET_EPOCH_ENTER(et);
killed = pf_clear_states(&kill);
+ NET_EPOCH_EXIT(et);
free(nvlpacked, M_NVLIST);
nvlpacked = NULL;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, May 24, 9:17 PM (4 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33487279
Default Alt Text
D43106.id131560.diff (64 KB)
Attached To
Mode
D43106: pflow: import from OpenBSD
Attached
Detach File
Event Timeline
Log In to Comment