summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenning Brauer <henning@cvs.openbsd.org>2008-09-09 13:56:40 +0000
committerHenning Brauer <henning@cvs.openbsd.org>2008-09-09 13:56:40 +0000
commiteafa0437596580a038262a0363fdd2115f6f1726 (patch)
tree508e9ede5b29e41966fa0aa869df62cbd54600a3
parent456e9ebf40a66cc20f4da77dbd097479607938d9 (diff)
welcome pflow(4), a netflow v5 compatible flow export interface.
flows export data gathered from pf states. initial implementation by Joerg Goltermann <jg@osn.de>, guidance and many changes by me. 'put it in' theo
-rw-r--r--sbin/ifconfig/ifconfig.c136
-rw-r--r--sbin/pfctl/parse.y25
-rw-r--r--sbin/pfctl/pf_print_state.c4
-rw-r--r--sbin/pfctl/pfctl_parser.c8
-rw-r--r--share/man/man4/pflow.487
-rw-r--r--sys/conf/files5
-rw-r--r--sys/net/if_pflow.c622
-rw-r--r--sys/net/if_pflow.h123
-rw-r--r--sys/net/if_types.h3
-rw-r--r--sys/net/pf.c10
-rw-r--r--sys/net/pfvar.h10
-rw-r--r--sys/sys/sockio.h5
12 files changed, 1024 insertions, 14 deletions
diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c
index 03b40456482..72103b1c197 100644
--- a/sbin/ifconfig/ifconfig.c
+++ b/sbin/ifconfig/ifconfig.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ifconfig.c,v 1.203 2008/09/07 02:22:34 deraadt Exp $ */
+/* $OpenBSD: ifconfig.c,v 1.204 2008/09/09 13:56:38 henning Exp $ */
/* $NetBSD: ifconfig.c,v 1.40 1997/10/01 02:19:43 enami Exp $ */
/*
@@ -79,6 +79,7 @@
#include <net80211/ieee80211_ioctl.h>
#include <net/pfvar.h>
#include <net/if_pfsync.h>
+#include <net/if_pflow.h>
#include <net/if_pppoe.h>
#include <net/if_trunk.h>
#include <net/if_sppp.h>
@@ -233,6 +234,14 @@ void trunk_status(void);
int main(int, char *[]);
int prefix(void *val, int);
+#ifndef SMALL
+void pflow_status(void);
+void setpflow_sender(const char *, int);
+void unsetpflow_sender(const char *, int);
+void setpflow_receiver(const char *, int);
+void unsetpflow_receiver(const char *, int);
+#endif
+
/*
* Media stuff. Whenever a media command is first performed, the
* currently select media is grabbed for this interface. If `media'
@@ -395,6 +404,12 @@ const struct cmd {
{ "descr", NEXTARG, 0, setifdesc },
{ "-description", 1, 0, unsetifdesc },
{ "-descr", 1, 0, unsetifdesc },
+#ifndef SMALL
+ { "flowsrc", NEXTARG, 0, setpflow_sender },
+ { "-flowsrc", 1, 0, unsetpflow_sender },
+ { "flowdst", NEXTARG, 0, setpflow_receiver },
+ { "-flowdst", 1, 0, unsetpflow_receiver },
+#endif
{ NULL, /*src*/ 0, 0, setifaddr },
{ NULL, /*dst*/ 0, 0, setifdstaddr },
{ NULL, /*illegal*/0, 0, NULL },
@@ -2592,6 +2607,7 @@ status(int link, struct sockaddr_dl *sdl)
sppp_status();
trunk_status();
mpe_status();
+ pflow_status();
#endif
getifgroups();
@@ -3674,6 +3690,124 @@ pfsync_status(void)
}
}
+#ifndef SMALL
+void
+pflow_status(void)
+{
+ struct pflowreq preq;
+
+ bzero((char *)&preq, sizeof(struct pflowreq));
+ ifr.ifr_data = (caddr_t)&preq;
+
+ if (ioctl(s, SIOCGETPFLOW, (caddr_t)&ifr) == -1)
+ return;
+
+ printf("\tpflow: sender: %s ", inet_ntoa(preq.sender_ip));
+ printf("receiver: %s:%u\n", inet_ntoa(preq.receiver_ip),
+ ntohs(preq.receiver_port));
+}
+
+/* ARGSUSED */
+void
+setpflow_sender(const char *val, int d)
+{
+ struct pflowreq preq;
+ struct addrinfo hints, *sender;
+ int ecode;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+
+ if ((ecode = getaddrinfo(val, NULL, &hints, &sender)) != 0)
+ errx(1, "error in parsing address string: %s",
+ gai_strerror(ecode));
+
+ if (sender->ai_addr->sa_family != AF_INET)
+ errx(1, "only IPv4 addresses supported for the sender");
+
+ bzero((char *)&preq, sizeof(struct pflowreq));
+ ifr.ifr_data = (caddr_t)&preq;
+ preq.addrmask |= PFLOW_MASK_SRCIP;
+ preq.sender_ip.s_addr = ((struct sockaddr_in *)
+ sender->ai_addr)->sin_addr.s_addr;
+
+
+ if (ioctl(s, SIOCSETPFLOW, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSETPFLOW");
+
+ freeaddrinfo(sender);
+}
+
+void
+unsetpflow_sender(const char *val, int d)
+{
+ struct pflowreq preq;
+
+ bzero((char *)&preq, sizeof(struct pflowreq));
+ preq.addrmask |= PFLOW_MASK_SRCIP;
+ ifr.ifr_data = (caddr_t)&preq;
+ if (ioctl(s, SIOCSETPFLOW, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSETPFLOW");
+}
+
+/* ARGSUSED */
+void
+setpflow_receiver(const char *val, int d)
+{
+ const char *errmsg = NULL;
+ struct pflowreq preq;
+ struct addrinfo hints, *receiver;
+ int ecode;
+ char *ip, *port, buf[MAXHOSTNAMELEN+sizeof (":65535")];
+
+ if (strchr (val, ':') == NULL)
+ errx(1, "%s bad value", val);
+
+ if (strlcpy(buf, val, sizeof(buf)) >= sizeof(buf))
+ errx(1, "%s bad value", val);
+ port = strchr(buf, ':');
+ *port++ = '\0';
+ ip = buf;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+
+ if ((ecode = getaddrinfo(ip, port, &hints, &receiver)) != 0)
+ errx(1, "error in parsing address string: %s",
+ gai_strerror(ecode));
+
+ if (receiver->ai_addr->sa_family != AF_INET)
+ errx(1, "only IPv4 addresses supported for the receiver");
+
+ bzero((char *)&preq, sizeof(struct pflowreq));
+ ifr.ifr_data = (caddr_t)&preq;
+ preq.addrmask |= PFLOW_MASK_DSTIP | PFLOW_MASK_DSTPRT;
+ preq.receiver_ip.s_addr = ((struct sockaddr_in *)
+ receiver->ai_addr)->sin_addr.s_addr;
+ preq.receiver_port = (u_int16_t) ((struct sockaddr_in *)
+ receiver->ai_addr)->sin_port;
+
+ if (ioctl(s, SIOCSETPFLOW, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSETPFLOW");
+
+ freeaddrinfo(receiver);
+}
+
+void
+unsetpflow_receiver(const char *val, int d)
+{
+ struct pflowreq preq;
+
+ bzero((char *)&preq, sizeof(struct pflowreq));
+ ifr.ifr_data = (caddr_t)&preq;
+ preq.addrmask |= PFLOW_MASK_DSTIP | PFLOW_MASK_DSTPRT;
+ if (ioctl(s, SIOCSETPFLOW, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSETPFLOW");
+}
+#endif /* SMALL */
+
void
pppoe_status(void)
{
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index c9ccec6f2c3..fc075f9b0ae 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.550 2008/08/07 18:29:32 henning Exp $ */
+/* $OpenBSD: parse.y,v 1.551 2008/09/09 13:56:38 henning Exp $ */
/*
* Copyright (c) 2001 Markus Friedl. All rights reserved.
@@ -153,7 +153,8 @@ enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK,
PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN,
PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES,
PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK,
- PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY };
+ PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY,
+ PF_STATE_OPT_PFLOW };
enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE };
@@ -442,7 +443,7 @@ int parseport(char *, struct range *r, int);
%token QUEUE PRIORITY QLIMIT RTABLE
%token LOAD RULESET_OPTIMIZATION
%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
-%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY
+%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW
%token TAGGED TAG IFBOUND FLOATING STATEPOLICY ROUTE SETTOS
%token DIVERTTO DIVERTREPLY
%token <v.string> STRING
@@ -2061,6 +2062,15 @@ pfrule : action dir logquick interface route af proto fromto
}
r.rule_flag |= PFRULE_STATESLOPPY;
break;
+ case PF_STATE_OPT_PFLOW:
+ if (r.rule_flag & PFRULE_PFLOW) {
+ yyerror("state pflow "
+ "option: multiple "
+ "definitions");
+ YYERROR;
+ }
+ r.rule_flag |= PFRULE_PFLOW;
+ break;
case PF_STATE_OPT_TIMEOUT:
if (o->data.timeout.number ==
PFTM_ADAPTIVE_START ||
@@ -3541,6 +3551,14 @@ state_opt_item : MAXIMUM NUMBER {
$$->next = NULL;
$$->tail = $$;
}
+ | PFLOW {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_PFLOW;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
| STRING NUMBER {
int i;
@@ -5256,6 +5274,7 @@ lookup(char *s)
{ "out", OUT},
{ "overload", OVERLOAD},
{ "pass", PASS},
+ { "pflow", PFLOW},
{ "port", PORT},
{ "priority", PRIORITY},
{ "priq", PRIQ},
diff --git a/sbin/pfctl/pf_print_state.c b/sbin/pfctl/pf_print_state.c
index 73eef1199c7..b20fc941c16 100644
--- a/sbin/pfctl/pf_print_state.c
+++ b/sbin/pfctl/pf_print_state.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf_print_state.c,v 1.52 2008/08/12 16:40:18 david Exp $ */
+/* $OpenBSD: pf_print_state.c,v 1.53 2008/09/09 13:56:38 henning Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -325,6 +325,8 @@ print_state(struct pfsync_state *s, int opts)
printf(", rule %u", ntohl(s->rule));
if (s->state_flags & PFSTATE_SLOPPY)
printf(", sloppy");
+ if (s->state_flags & PFSTATE_PFLOW)
+ printf(", pflow");
if (s->sync_flags & PFSYNC_FLAG_SRCNODE)
printf(", source-track");
if (s->sync_flags & PFSYNC_FLAG_NATSRCNODE)
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index 7368dbe7d3c..86c5f90ffeb 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfctl_parser.c,v 1.240 2008/06/10 20:55:02 mcbride Exp $ */
+/* $OpenBSD: pfctl_parser.c,v 1.241 2008/09/09 13:56:38 henning Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -934,6 +934,12 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
printf("sloppy");
opts = 0;
}
+ if (r->rule_flag & PFRULE_PFLOW) {
+ if (!opts)
+ printf(", ");
+ printf("pflow");
+ opts = 0;
+ }
for (i = 0; i < PFTM_MAX; ++i)
if (r->timeout[i]) {
int j;
diff --git a/share/man/man4/pflow.4 b/share/man/man4/pflow.4
new file mode 100644
index 00000000000..46e9666fcca
--- /dev/null
+++ b/share/man/man4/pflow.4
@@ -0,0 +1,87 @@
+.\" $OpenBSD: pflow.4,v 1.1 2008/09/09 13:56:38 henning 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 ALLWARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BELIABLE 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, ARISINGOUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: September 9 2008 $
+.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 pflow
+accounting data from the kernel using
+.Xr udp 4
+packets.
+pflow is compatible with netflow v5.
+The data is extracted from the
+.Xr pf 4
+state table.
+.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 pflow records in one
+UDP packet, but will not hold a record for longer than 30 seconds.
+The maximum number of flows is controlled by the
+.Ar mtu
+parameter of ifconfig
+(see the
+.Xr ifconfig 8
+.Cm mtu
+parameter).
+.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
+.Aq Pa net/if_pflow.h .
+.Pp
+The pflow source and destination addresses are controlled by
+.Xr ifconfig 8 .
+flowsrc is the sender IP address of the UDP packet which can be used
+to identify the source of the data on the pflow collector.
+flowdst defines the collector IP address and the port.
+The 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
+.Sh SEE ALSO
+.Xr netintro 4 ,
+.Xr pf 4 ,
+.Xr udp 4 ,
+.Xr pf.conf 5 ,
+.Xr ifconfig 8 ,
+.Xr tcpdump 8
+.Sh HISTORY
+The
+.Nm
+device first appeared in
+.Ox 4.5 .
diff --git a/sys/conf/files b/sys/conf/files
index 1443adf21d2..3644ee064f1 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.441 2008/08/15 19:42:03 miod Exp $
+# $OpenBSD: files,v 1.442 2008/09/09 13:56:39 henning Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -501,6 +501,9 @@ file net/if_pflog.c pflog needs-flag
pseudo-device pfsync: ifnet
file net/if_pfsync.c pfsync needs-flag
+pseudo-device pflow: ifnet
+file net/if_pflow.c pflow needs-flag
+
pseudo-device bio
file dev/bio.c bio needs-flag
diff --git a/sys/net/if_pflow.c b/sys/net/if_pflow.c
new file mode 100644
index 00000000000..87e0a094ab0
--- /dev/null
+++ b/sys/net/if_pflow.c
@@ -0,0 +1,622 @@
+/* $OpenBSD: if_pflow.c,v 1.1 2008/09/09 13:56:39 henning 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.
+ */
+
+#include <sys/types.h>
+#include <sys/malloc.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <dev/rndvar.h>
+
+#include <net/if.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>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <netinet/in_pcb.h>
+#endif /* INET */
+
+#include <net/pfvar.h>
+#include <net/if_pflow.h>
+
+#include "bpfilter.h"
+#include "pflow.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
+
+struct pflow_softc *pflowif = NULL;
+struct pflowstats pflowstats;
+
+void pflowattach(int);
+int pflow_clone_create(struct if_clone *, int);
+int pflow_clone_destroy(struct ifnet *);
+void pflow_setmtu(struct pflow_softc *, int);
+int pflowoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
+ struct rtentry *);
+int pflowioctl(struct ifnet *, u_long, caddr_t);
+void pflowstart(struct ifnet *);
+
+struct mbuf *pflow_get_mbuf(struct pflow_softc *, void **);
+int pflow_sendout(struct pflow_softc *);
+int pflow_sendout_mbuf(struct pflow_softc *, struct mbuf *);
+void pflow_timeout(void *);
+void copy_flow_data(struct pflow_flow *, struct pflow_flow *,
+ struct pf_state *, int, int);
+int pflow_pack_flow(struct pf_state *);
+int pflow_get_dynport(void);
+
+struct if_clone pflow_cloner =
+ IF_CLONE_INITIALIZER("pflow", pflow_clone_create,
+ pflow_clone_destroy);
+
+/* from in_pcb.c */
+extern int ipport_hifirstauto;
+extern int ipport_hilastauto;
+
+void
+pflowattach(int npflow)
+{
+ if_clone_attach(&pflow_cloner);
+}
+
+int
+pflow_clone_create(struct if_clone *ifc, int unit)
+{
+ struct ifnet *ifp;
+
+ if (unit != 0)
+ return (EINVAL);
+
+ if ((pflowif = malloc(sizeof(*pflowif),
+ M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
+ return (ENOMEM);
+
+ pflowif->sc_sender_ip.s_addr = INADDR_ANY;
+ pflowif->sc_sender_port = pflow_get_dynport();
+
+ pflowif->sc_imo.imo_membership = malloc(
+ (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_IPMOPTS,
+ M_WAITOK|M_ZERO);
+ pflowif->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS;
+ pflowif->sc_receiver_ip.s_addr = 0;
+ pflowif->sc_receiver_port = 0;
+ pflowif->sc_sender_ip.s_addr = INADDR_ANY;
+ pflowif->sc_sender_port = pflow_get_dynport();
+ ifp = &pflowif->sc_if;
+ snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflow%d", unit);
+ ifp->if_softc = pflowif;
+ ifp->if_ioctl = pflowioctl;
+ ifp->if_output = pflowoutput;
+ ifp->if_start = pflowstart;
+ ifp->if_type = IFT_PFLOW;
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+ ifp->if_hdrlen = PFLOW_HDRLEN;
+ ifp->if_flags = IFF_UP;
+ ifp->if_flags &= ~IFF_RUNNING; /* not running, need receiver */
+ pflow_setmtu(pflowif, ETHERMTU);
+ timeout_set(&pflowif->sc_tmo, pflow_timeout, pflowif);
+ if_attach(ifp);
+ if_alloc_sadl(ifp);
+
+ return (0);
+}
+
+int
+pflow_clone_destroy(struct ifnet *ifp)
+{
+ struct pflow_softc *sc = ifp->if_softc;
+
+ timeout_del(&sc->sc_tmo);
+
+#if NBPFILTER > 0
+ bpfdetach(ifp);
+#endif
+ if_detach(ifp);
+ free(pflowif->sc_imo.imo_membership, M_IPMOPTS);
+ free(pflowif, M_DEVBUF);
+ pflowif = NULL;
+ return (0);
+}
+
+/*
+ * Start output on the pflow interface.
+ */
+void
+pflowstart(struct ifnet *ifp)
+{
+ struct mbuf *m;
+ int s;
+
+ for (;;) {
+ s = splnet();
+ IF_DROP(&ifp->if_snd);
+ IF_DEQUEUE(&ifp->if_snd, m);
+ splx(s);
+
+ if (m == NULL)
+ return;
+ m_freem(m);
+ }
+}
+
+int
+pflowoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
+ struct rtentry *rt)
+{
+ m_freem(m);
+ return (0);
+}
+
+/* ARGSUSED */
+int
+pflowioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct proc *p = curproc;
+ struct pflow_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct pflowreq pflowr;
+ int s, error;
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ case SIOCAIFADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCSIFFLAGS:
+ if ((ifp->if_flags & IFF_UP) &&
+ sc->sc_receiver_ip.s_addr != 0 &&
+ sc->sc_receiver_port != 0)
+ ifp->if_flags |= IFF_RUNNING;
+ else
+ ifp->if_flags &= ~IFF_RUNNING;
+ break;
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu < PFLOW_MINMTU)
+ return (EINVAL);
+ if (ifr->ifr_mtu > MCLBYTES)
+ ifr->ifr_mtu = MCLBYTES;
+ s = splnet();
+ if (ifr->ifr_mtu < ifp->if_mtu)
+ pflow_sendout(sc);
+ pflow_setmtu(sc, ifr->ifr_mtu);
+ splx(s);
+ break;
+
+ case SIOCGETPFLOW:
+ bzero(&pflowr, sizeof(pflowr));
+
+ pflowr.sender_ip = sc->sc_sender_ip;
+ pflowr.receiver_ip = sc->sc_receiver_ip;
+ pflowr.receiver_port = sc->sc_receiver_port;
+
+ if ((error = copyout(&pflowr, ifr->ifr_data,
+ sizeof(pflowr))))
+ return (error);
+ break;
+
+ case SIOCSETPFLOW:
+ if ((error = suser(p, p->p_acflag)) != 0)
+ return (error);
+ if ((error = copyin(ifr->ifr_data, &pflowr,
+ sizeof(pflowr))))
+ return (error);
+
+ if ((ifp->if_flags & IFF_UP) && sc->sc_receiver_ip.s_addr != 0
+ && sc->sc_receiver_port != 0) {
+ s = splnet();
+ pflow_sendout(sc);
+ splx(s);
+ }
+
+ if (pflowr.addrmask & PFLOW_MASK_DSTIP)
+ sc->sc_receiver_ip = pflowr.receiver_ip;
+ if (pflowr.addrmask & PFLOW_MASK_DSTPRT)
+ sc->sc_receiver_port = pflowr.receiver_port;
+ if (pflowr.addrmask & PFLOW_MASK_SRCIP)
+ sc->sc_sender_ip.s_addr = pflowr.sender_ip.s_addr;
+
+ if ((ifp->if_flags & IFF_UP) &&
+ sc->sc_receiver_ip.s_addr != 0 &&
+ sc->sc_receiver_port != 0)
+ ifp->if_flags |= IFF_RUNNING;
+ else
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ break;
+
+ default:
+ return (ENOTTY);
+ }
+ return (0);
+}
+
+void
+pflow_setmtu(struct pflow_softc *sc, int mtu_req)
+{
+ int mtu;
+
+ if (sc->sc_pflow_ifp && sc->sc_pflow_ifp->if_mtu < mtu_req)
+ mtu = sc->sc_pflow_ifp->if_mtu;
+ else
+ mtu = mtu_req;
+
+ sc->sc_maxcount = (mtu - sizeof(struct pflow_header)) /
+ sizeof(struct pflow_flow);
+ if (sc->sc_maxcount > PFLOW_MAXFLOWS)
+ sc->sc_maxcount = PFLOW_MAXFLOWS;
+ sc->sc_if.if_mtu = sizeof(struct pflow_header) +
+ sc->sc_maxcount * sizeof(struct pflow_flow);
+}
+
+struct mbuf *
+pflow_get_mbuf(struct pflow_softc *sc, void **sp)
+{
+ struct pflow_header *h;
+ struct mbuf *m, *top = NULL, **mp = &top;
+ int len, totlen;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ sc->sc_if.if_oerrors++;
+ return (NULL);
+ }
+
+ len = MHLEN;
+ totlen = (sc->sc_maxcount * sizeof(struct pflow_flow)) +
+ sizeof(struct pflow_header);
+
+ while (totlen > 0) {
+ if (top) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0) {
+ m_freem(top);
+ sc->sc_if.if_oerrors++;
+ return (NULL);
+ }
+ len = MLEN;
+ }
+ if (totlen >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if (m->m_flags & M_EXT)
+ len = MCLBYTES;
+ else {
+ m_free(m);
+ m_freem(top);
+ sc->sc_if.if_oerrors++;
+ return (NULL);
+ }
+ }
+ m->m_len = len = min(totlen, len);
+ totlen -= len;
+ *mp = m;
+ mp = &m->m_next;
+ }
+
+ top->m_pkthdr.rcvif = NULL;
+ top->m_len = m->m_pkthdr.len = sizeof(struct pflow_header);
+
+ /* populate pflow_header */
+ h = mtod(top, struct pflow_header *);
+ h->reserved1 = 0;
+ h->reserved2 = 0;
+ h->count = 0;
+ h->version = htons(PFLOW_VERSION);
+ h->flow_sequence = htonl(sc->sc_gcounter);
+ h->engine_type = PFLOW_ENGINE_TYPE;
+ h->engine_id = PFLOW_ENGINE_ID;
+
+ sc->sc_count = 0;
+ *sp = (void *)((char *)h + PFLOW_HDRLEN);
+ timeout_add_sec(&sc->sc_tmo, PFLOW_TIMEOUT);
+ return (top);
+}
+
+void
+copy_flow_data(struct pflow_flow *flow1, struct pflow_flow *flow2,
+ struct pf_state *st, int src, int dst)
+{
+ struct pf_state_key *sk = st->key[PF_SK_WIRE];
+
+ flow1->src_ip = flow2->dest_ip =
+ st->key[PF_SK_WIRE]->addr[src].v4.s_addr;
+ flow1->src_port = flow2->dest_port = st->key[PF_SK_WIRE]->port[src];
+ flow1->dest_ip = flow2->src_ip =
+ st->key[PF_SK_WIRE]->addr[dst].v4.s_addr;
+ flow1->dest_port = flow2->src_port = st->key[PF_SK_WIRE]->port[dst];
+
+ flow1->dest_as = flow2->src_as =
+ flow1->src_as = flow2->dest_as = 0;
+ flow1->if_index_out = flow2->if_index_in =
+ flow1->if_index_in = flow2->if_index_out = 0;
+ 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]);
+
+ flow1->flow_start = flow2->flow_start = htonl(st->creation * 1000);
+ flow1->flow_finish = flow2->flow_finish = htonl(time_second * 1000);
+ flow1->tcp_flags = flow2->tcp_flags = 0;
+ flow1->protocol = flow2->protocol = sk->proto;
+ flow1->tos = flow2->tos = st->rule.ptr->tos;
+}
+
+int
+export_pflow(struct pf_state *st)
+{
+ struct pf_state pfs_copy;
+ struct pflow_softc *sc = pflowif;
+ struct ifnet *ifp = NULL;
+ u_int64_t bytes[2];
+ int ret = 0;
+
+ if (sc == NULL)
+ return (0);
+
+ ifp = &sc->sc_if;
+ if (!(ifp->if_flags & IFF_UP))
+ return (0);
+
+ if ((st->bytes[0] < (u_int64_t)PFLOW_MAXBYTES)
+ && (st->bytes[1] < (u_int64_t)PFLOW_MAXBYTES))
+ return pflow_pack_flow(st);
+
+ /* 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)) != 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)) != 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));
+}
+
+int
+pflow_pack_flow(struct pf_state *st)
+{
+ struct pflow_softc *sc = pflowif;
+ struct pflow_flow *flow1 = NULL;
+ struct pflow_flow flow2;
+ struct pf_state_key *sk = st->key[PF_SK_WIRE];
+ int s, ret = 0;
+
+ if (sk->af != AF_INET)
+ return (0);
+
+ s = splnet();
+
+ if (sc->sc_mbuf == NULL) {
+ if ((sc->sc_mbuf = pflow_get_mbuf(sc,
+ (void **)&sc->sc_flowp.s)) == NULL) {
+ splx(s);
+ return (ENOMEM);
+ }
+ }
+
+ pflowstats.pflow_flows++;
+ sc->sc_gcounter++;
+ sc->sc_count++;
+
+ flow1 = sc->sc_flowp.s++;
+ sc->sc_mbuf->m_pkthdr.len =
+ sc->sc_mbuf->m_len += sizeof(struct pflow_flow);
+ bzero(flow1, sizeof(*flow1));
+ bzero(&flow2, sizeof(flow2));
+
+ if (st->direction == PF_OUT)
+ copy_flow_data(flow1, &flow2, st, 1, 0);
+ else
+ copy_flow_data(flow1, &flow2, st, 0, 1);
+
+ if (st->bytes[0] != 0) { /* first flow from state */
+ if (sc->sc_count >= sc->sc_maxcount)
+ ret = pflow_sendout(sc);
+
+ if (st->bytes[1] != 0) {
+ /* one more flow, second part from state */
+ if (sc->sc_mbuf == NULL) {
+ if ((sc->sc_mbuf = pflow_get_mbuf(sc,
+ (void **)&sc->sc_flowp.s)) == NULL) {
+ splx(s);
+ return (ENOMEM);
+ }
+ }
+
+ pflowstats.pflow_flows++;
+ sc->sc_gcounter++;
+ sc->sc_count++;
+
+ flow1 = sc->sc_flowp.s++;
+ sc->sc_mbuf->m_pkthdr.len =
+ sc->sc_mbuf->m_len += sizeof(struct pflow_flow);
+ bzero(flow1, sizeof(*flow1));
+ }
+ }
+
+ if (st->bytes[1] != 0) { /* second flow from state */
+ bcopy(&flow2, flow1, sizeof(*flow1));
+ if (sc->sc_count >= sc->sc_maxcount)
+ ret = pflow_sendout(sc);
+ }
+
+ splx(s);
+ return (ret);
+}
+
+void
+pflow_timeout(void *v)
+{
+ struct pflow_softc *sc = v;
+ int s;
+
+ s = splnet();
+ pflow_sendout(sc);
+ splx(s);
+}
+
+/* This must be called in splnet() */
+int
+pflow_sendout(struct pflow_softc *sc)
+{
+ struct mbuf *m;
+ struct pflow_header *h;
+#if NBPFILTER > 0
+ struct ifnet *ifp = &sc->sc_if;
+#endif
+
+ timeout_del(&sc->sc_tmo);
+
+ if (sc->sc_mbuf == NULL)
+ return (0);
+
+ pflowstats.pflow_packets++;
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ m_freem(m);
+ return (0);
+ }
+
+ m = sc->sc_mbuf;
+ sc->sc_mbuf = NULL;
+ sc->sc_flowp.s = NULL;
+ h = mtod(m, struct pflow_header *);
+ h->count = htons(sc->sc_count);
+
+ /* populate pflow_header */
+ h->uptime_ms = htonl(time_uptime * 1000);
+ h->time_sec = htonl(time_second);
+ h->time_nanosec = htonl(ticks);
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
+#endif
+
+ return (pflow_sendout_mbuf(sc, m));
+}
+
+int
+pflow_sendout_mbuf(struct pflow_softc *sc, struct mbuf *m)
+{
+ struct udpiphdr *ui;
+ int len = m->m_pkthdr.len;
+
+ /* UDP Header*/
+ M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
+ if (m == NULL) {
+ pflowstats.pflow_onomem++;
+ return (0);
+ }
+
+ ui = mtod(m, struct udpiphdr *);
+ ui->ui_pr = IPPROTO_UDP;
+ ui->ui_len = htons((u_int16_t) len + sizeof (struct udphdr));
+ ui->ui_src = sc->sc_sender_ip;
+ ui->ui_sport = sc->sc_sender_port;
+ ui->ui_dst = sc->sc_receiver_ip;
+ ui->ui_dport = sc->sc_receiver_port;
+ ui->ui_ulen = ui->ui_len;
+
+ ((struct ip *)ui)->ip_v = IPVERSION;
+ ((struct ip *)ui)->ip_hl = sizeof(struct ip) >> 2;
+ ((struct ip *)ui)->ip_id = htons(ip_randomid());
+ ((struct ip *)ui)->ip_off = htons(IP_DF);
+ ((struct ip *)ui)->ip_tos = IPTOS_LOWDELAY;
+ ((struct ip *)ui)->ip_ttl = IPDEFTTL;
+ ((struct ip *)ui)->ip_len = htons(sizeof (struct udpiphdr) + len);
+
+ /*
+ * Compute the pseudo-header checksum; defer further checksumming
+ * until ip_output() or hardware (if it exists).
+ */
+ m->m_pkthdr.csum_flags |= M_UDPV4_CSUM_OUT;
+ ui->ui_sum = in_cksum_phdr(ui->ui_src.s_addr,
+ ui->ui_dst.s_addr, htons((u_int16_t)len +
+ sizeof(struct udphdr) + IPPROTO_UDP));
+
+ if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL))
+ pflowstats.pflow_oerrors++;
+ return (0);
+}
+
+int
+pflow_get_dynport(void)
+{
+ u_int16_t tmp, low, high, cut;
+
+ low = ipport_hifirstauto; /* sysctl */
+ high = ipport_hilastauto;
+
+ cut = arc4random_uniform(1 + high - low) + low;
+
+ for (tmp = cut; tmp <= high; ++(tmp)) {
+ if (!in_baddynamic(tmp, IPPROTO_UDP))
+ return (htons(tmp));
+ }
+
+ for (tmp = cut - 1; tmp >= low; --(tmp)) {
+ if (!in_baddynamic(tmp, IPPROTO_UDP))
+ return (htons(tmp));
+ }
+
+ return (htons(ipport_hilastauto)); /* XXX */
+}
diff --git a/sys/net/if_pflow.h b/sys/net/if_pflow.h
new file mode 100644
index 00000000000..cd9d2dcc145
--- /dev/null
+++ b/sys/net/if_pflow.h
@@ -0,0 +1,123 @@
+/* $OpenBSD: if_pflow.h,v 1.1 2008/09/09 13:56:39 henning 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_
+
+#define PFLOW_ID_LEN sizeof(u_int64_t)
+
+#define PFLOW_MAXFLOWS 30
+#define PFLOW_VERSION 5
+#define PFLOW_ENGINE_TYPE 42
+#define PFLOW_ENGINE_ID 42
+#define PFLOW_MAXBYTES 0xffffffff
+#define PFLOW_TIMEOUT 30
+
+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;
+
+#ifdef _KERNEL
+
+extern int pflow_ok;
+
+union sc_flowp {
+ struct pflow_flow *s;
+};
+
+struct pflow_softc {
+ struct ifnet sc_if;
+ struct ifnet *sc_pflow_ifp;
+
+ unsigned int sc_count;
+ unsigned int sc_maxcount;
+ u_int32_t sc_gcounter;
+ struct ip_moptions sc_imo;
+ struct timeout sc_tmo;
+ struct in_addr sc_sender_ip;
+ u_int16_t sc_sender_port;
+ struct in_addr sc_receiver_ip;
+ u_int16_t sc_receiver_port;
+ union sc_flowp sc_flowp;
+ struct mbuf *sc_mbuf; /* current cumulative mbuf */
+};
+
+extern struct pflow_softc *pflowif;
+
+#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 pflowstats {
+ u_int64_t pflow_flows;
+ u_int64_t pflow_packets;
+ u_int64_t pflow_onomem;
+ u_int64_t pflow_oerrors;
+};
+
+/*
+ * Configuration structure for SIOCSETPFLOW SIOCGETPFLOW
+ */
+struct pflowreq {
+ struct in_addr sender_ip;
+ struct in_addr receiver_ip;
+ u_int16_t receiver_port;
+ u_int16_t addrmask;
+#define PFLOW_MASK_SRCIP 0x01
+#define PFLOW_MASK_DSTIP 0x02
+#define PFLOW_MASK_DSTPRT 0x04
+};
+
+#ifdef _KERNEL
+int export_pflow(struct pf_state *);
+#endif /* _KERNEL */
+
+#endif /* _NET_IF_PFLOW_H_ */
diff --git a/sys/net/if_types.h b/sys/net/if_types.h
index bac91ccfc73..2de5f6b47aa 100644
--- a/sys/net/if_types.h
+++ b/sys/net/if_types.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_types.h,v 1.18 2005/01/14 12:04:02 grange Exp $ */
+/* $OpenBSD: if_types.h,v 1.19 2008/09/09 13:56:39 henning Exp $ */
/* $NetBSD: if_types.h,v 1.17 2000/10/26 06:51:31 onoe Exp $ */
/*
@@ -267,5 +267,6 @@
#define IFT_PFSYNC 0xf6 /* Packet filter state syncing */
#define IFT_CARP 0xf7 /* Common Address Redundancy Protocol */
#define IFT_BLUETOOTH 0xf8 /* Bluetooth */
+#define IFT_PFLOW 0xf9 /* pflow */
#endif /* _NET_IF_TYPES_H_ */
diff --git a/sys/net/pf.c b/sys/net/pf.c
index ed0831a698f..61f63b837a3 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf.c,v 1.618 2008/09/03 12:57:19 henning Exp $ */
+/* $OpenBSD: pf.c,v 1.619 2008/09/09 13:56:39 henning Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -38,6 +38,7 @@
#include "bpfilter.h"
#include "pflog.h"
#include "pfsync.h"
+#include "pflow.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -78,6 +79,7 @@
#include <dev/rndvar.h>
#include <net/pfvar.h>
#include <net/if_pflog.h>
+#include <net/if_pflow.h>
#if NPFSYNC > 0
#include <net/if_pfsync.h>
@@ -1090,6 +1092,10 @@ pf_unlink_state(struct pf_state *cur)
TH_RST|TH_ACK, 0, 0, 0, 1, cur->tag, NULL, NULL);
}
RB_REMOVE(pf_state_tree_id, &tree_id, cur);
+#if NPFLOW
+ if (cur->state_flags & PFSTATE_PFLOW)
+ export_pflow(cur);
+#endif
#if NPFSYNC
if (cur->creatorid == pf_status.hostid)
pfsync_delete_state(cur);
@@ -3453,6 +3459,8 @@ pf_create_state(struct pf_rule *r, struct pf_rule *nr, struct pf_rule *a,
s->state_flags |= PFSTATE_ALLOWOPTS;
if (r->rule_flag & PFRULE_STATESLOPPY)
s->state_flags |= PFSTATE_SLOPPY;
+ if (r->rule_flag & PFRULE_PFLOW)
+ s->state_flags |= PFSTATE_PFLOW;
s->log = r->log & PF_LOG_ALL;
if (nr != NULL)
s->log |= nr->log & PF_LOG_ALL;
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 2e5b13a3e42..3354151eba8 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfvar.h,v 1.277 2008/08/26 12:17:10 henning Exp $ */
+/* $OpenBSD: pfvar.h,v 1.278 2008/09/09 13:56:39 henning Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -627,6 +627,7 @@ struct pf_rule {
/* rule flags again */
#define PFRULE_IFBOUND 0x00010000 /* if-bound */
#define PFRULE_STATESLOPPY 0x00020000 /* sloppy state tracking */
+#define PFRULE_PFLOW 0x00040000
#define PFSTATE_HIWAT 10000 /* default state table size */
#define PFSTATE_ADAPT_START 6000 /* default adaptive timeout start */
@@ -766,9 +767,10 @@ struct pf_state {
#define PFSTATE_SLOPPY 0x02
u_int8_t timeout;
u_int8_t sync_flags;
-#define PFSTATE_NOSYNC 0x01
-#define PFSTATE_FROMSYNC 0x02
-#define PFSTATE_STALE 0x04
+#define PFSTATE_NOSYNC 0x01
+#define PFSTATE_FROMSYNC 0x02
+#define PFSTATE_STALE 0x04
+#define PFSTATE_PFLOW 0x08
};
/*
diff --git a/sys/sys/sockio.h b/sys/sys/sockio.h
index e5dfde95c4e..f3a4a722368 100644
--- a/sys/sys/sockio.h
+++ b/sys/sys/sockio.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sockio.h,v 1.40 2008/05/06 13:33:50 pyr Exp $ */
+/* $OpenBSD: sockio.h,v 1.41 2008/09/09 13:56:39 henning Exp $ */
/* $NetBSD: sockio.h,v 1.5 1995/08/23 00:40:47 thorpej Exp $ */
/*-
@@ -173,4 +173,7 @@
#define SIOCSETPFSYNC _IOW('i', 247, struct ifreq)
#define SIOCGETPFSYNC _IOWR('i', 248, struct ifreq)
+#define SIOCSETPFLOW _IOW('i', 249, struct ifreq)
+#define SIOCGETPFLOW _IOWR('i', 250, struct ifreq)
+
#endif /* !_SYS_SOCKIO_H_ */