Index: pfflowd/ChangeLog
diff -u pfflowd/ChangeLog:1.15 pfflowd/ChangeLog:1.19
--- pfflowd/ChangeLog:1.15	Thu May  6 14:06:45 2004
+++ pfflowd/ChangeLog	Mon Sep  6 22:29:07 2004
@@ -1,7 +1,16 @@
+20040906
+ - (djm) NetFlow v.5 support, largely from ben AT tilderoot.com
+ - (djm) Release pfflowd-0.6
+
+20040712
+ - (djm) Import af-independent host/port parsing code from softflowd, allowing
+   IPv6 flow export
+ - (djm) Just use pcap_loop instead of our complicated main loop
+
 20040506
  - (djm) Change license to simplified ISC license, update copyright year
  - (djm) Tidy README
- - (djm) Release pfflowd-0.4
+ - (djm) Release pfflowd-0.5
 
 20040415
  - (djm) Fix uninitialised getsockopt len
Index: pfflowd/Makefile
diff -u pfflowd/Makefile:1.2 pfflowd/Makefile:1.3
--- pfflowd/Makefile:1.2	Mon Feb 16 14:30:46 2004
+++ pfflowd/Makefile	Mon Sep  6 22:25:57 2004
@@ -2,14 +2,14 @@
 	-Wall -Waggregate-return -Wcast-align -Wcast-qual \
 	-Wmissing-declarations -Wmissing-prototypes -Wno-conversion \
 	-Wpointer-arith -Wshadow -Wuninitialized -Wcast-align \
-	-Wcast-qual -WformatC=2 -Wformat-nonliteral -Werror
+	-Wcast-qual -WformatC=2 -Wformat-nonliteral -Wunused
 
 LIBS=-lpcap -lutil #-lefence
 LDFLAGS=-g
 
 CFLAGS=-g -O $(WARNFLAGS)
 
-# Uncomment this if you are using pfflowd on OpenBSD <=3.4
+# Uncomment this if you are using pfflowd on OpenBSD <= 3.4
 #CFLAGS+=-DOLD_PFSYNC
 
 TARGETS=pfflowd
Index: pfflowd/TODO
diff -u pfflowd/TODO:1.4 pfflowd/TODO:1.5
--- pfflowd/TODO:1.4	Fri Aug 15 11:51:23 2003
+++ pfflowd/TODO	Mon Sep  6 22:25:57 2004
@@ -5,10 +5,6 @@
     states whose labels match
     - What to do if ruleset changes under us?
 
-* NetFlow v.5 support
-  - A few extra fields
-  - Very easy
-
 * NetFlow v.9 export
   - Supports IPv6!
   - http://www.cisco.com/warp/public/cc/pd/iosw/prodlit/tflow_wp.htm
@@ -16,7 +12,7 @@
 * sflow export (www.sflow.org)
 
 * IPv6 export support
-  - Needs sflow or NetFlow v.9
+  - Needs IPFIX, sflow or NetFlow v.9
 
 * Cleanup of code
   - Kill globals in favour of ctxt
@@ -37,7 +33,8 @@
   - Upon receipt of a packet which would cause either flow counter to wrap,
     send a (new) pfsync info message with the state data
   - Reset state's counters to zero + len of current packet
-
+  - Maybe can do this by watching update messages and flagging ones close to
+    wrap
 * Kernel changes to account for generated packets
   - Which ones? ICMP is arguably not appropriate...
 
Index: pfflowd/pfflowd.8
diff -u pfflowd/pfflowd.8:1.5 pfflowd/pfflowd.8:1.6
--- pfflowd/pfflowd.8:1.5	Mon Mar 15 14:35:47 2004
+++ pfflowd/pfflowd.8	Mon Sep  6 22:25:57 2004
@@ -1,4 +1,4 @@
-.\" $Id: pfflowd.8,v 1.5 2004/03/15 03:35:47 djm Exp $
+.\" $Id: pfflowd.8,v 1.6 2004/09/06 12:25:57 djm Exp $
 .\"
 .\" Copyright (c) 2003 Damien Miller.  All rights reserved.
 .\"
@@ -31,10 +31,11 @@
 .Sh SYNOPSIS
 .Nm pfflowd
 .Op Fl dDh
+.Op Fl S Ar direction
 .Op Fl i Ar interface
-.Op Fl r Ar pcap_file
 .Op Fl n Ar host:port
-.Op Fl S Ar direction
+.Op Fl r Ar pcap_file
+.Op Fl v Ar netflow_version
 .Op bpf_program
 .Sh DESCRIPTION
 .Nm
@@ -99,6 +100,17 @@
 the 
 .Fl d
 flag to keep the process in the foreground in this mode.
+.It Fl v Ar netflow_version
+Select which version of the NetFlow protocol that 
+.Nm
+should send.
+Currently,
+.Nm
+support NetFlow versions
+.Ar 1
+and 
+.Ar 5 .
+The default is to send NetFlow v.5 datagrams.
 .It Fl d
 Specify that 
 .Nm
Index: pfflowd/pfflowd.c
diff -u pfflowd/pfflowd.c:1.15 pfflowd/pfflowd.c:1.18
--- pfflowd/pfflowd.c:1.15	Thu May  6 14:06:45 2004
+++ pfflowd/pfflowd.c	Mon Sep  6 22:25:57 2004
@@ -14,13 +14,12 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: pfflowd.c,v 1.15 2004/05/06 04:06:45 djm Exp $ */
+/* $Id: pfflowd.c,v 1.18 2004/09/06 12:25:57 djm Exp $ */
 
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/time.h>
 #include <sys/socket.h>
-#include <sys/poll.h>
 
 #include <net/if.h>
 #include <net/bpf.h>
@@ -28,8 +27,6 @@
 #include <net/if_pfsync.h>
 
 #include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
 #include <arpa/inet.h>
 
 #include <errno.h>
@@ -44,60 +41,15 @@
 #include <time.h>
 #include <unistd.h>
 #include <util.h>
+#include <netdb.h>
+#include "pfflowd.h"
 
-#define	PROGNAME		"pfflowd"
-#define	PROGVER			"0.5"
-
-#ifndef PRIVDROP_USER
-# define PRIVDROP_USER		"nobody"
-#endif
-
-#define PRIVDROP_CHROOT_DIR	"/var/empty"
-
-#define DEFAULT_INTERFACE	"pfsync0"
-#define LIBPCAP_SNAPLEN		2020	/* Default MTU */
-
-#ifdef OLD_PFSYNC
-# define _PFSYNC_STATE		pf_state
-# define _PFSYNC_VER		1
-#else
-# define _PFSYNC_STATE		pfsync_state
-# define _PFSYNC_VER		2
-#endif
-
-static int verbose_flag = 0;		/* Debugging flag */
-static int exit_flag = 0;		/* Signal handler flags */
-static struct timeval start_time;	/* "System boot" time, for SysUptime */
-static int netflow_socket = -1;
-static int direction = 0;
-
-/*
- * This is the Cisco Netflow(tm) version 1 packet format
- * Based on:
- * http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_0/nfc_ug/nfcform.htm
- */
-struct NF1_HEADER {
-	u_int16_t version, flows;
-	u_int32_t uptime_ms, time_sec, time_nanosec;
-};
-struct NF1_FLOW {
-	u_int32_t src_ip, dest_ip, nexthop_ip;
-	u_int16_t if_index_in, if_index_out;
-	u_int32_t flow_packets, flow_octets;
-	u_int32_t flow_start, flow_finish;
-	u_int16_t src_port, dest_port;
-	u_int16_t pad1;
-	u_int8_t protocol, tos, tcp_flags;
-	u_int8_t pad2, pad3, pad4;
-	u_int32_t reserved1;
-#if 0
- 	u_int8_t reserved2; /* XXX: no longer used */
-#endif
-};
-/* Maximum of 24 flows per packet */
-#define NF1_MAXFLOWS		24
-#define NF1_MAXPACKET_SIZE	(sizeof(struct NF1_HEADER) + \
-				 (NF1_MAXFLOWS * sizeof(struct NF1_FLOW)))
+static int verbose_flag = 0;            /* Debugging flag */
+static struct timeval start_time;       /* "System boot" time, for SysUptime */
+static int netflow_socket = -1;		/* Send socket */
+static int direction = 0;		/* Filter for direction */
+static u_int export_version = 5;	/* Currently v.1 and v.5 supported */
+static u_int32_t flows_exported = 0;	/* Used for v.5 header */
 
 /* 
  * Drop privileges and chroot, will exit on failure
@@ -155,6 +107,7 @@
 	fprintf(stderr, "  -S direction    Generation flows for \"in\" or \"out\" bound states (default any)\n");
 	fprintf(stderr, "  -d              Don't daemonise\n");
 	fprintf(stderr, "  -D              Debug mode: don't daemonise + verbosity\n");
+	fprintf(stderr, "  -v              NetFlow export packet version (default %d)\n", export_version);
 	fprintf(stderr, "  -h              Display this help\n");
 	fprintf(stderr, "\n");
 }
@@ -162,7 +115,11 @@
 /* Signal handlers */
 static void sighand_exit(int signum)
 {
-	exit_flag = signum;
+	struct syslog_data sd = SYSLOG_DATA_INIT;
+
+	syslog_r(LOG_INFO, &sd, "%s exiting on signal %d", PROGNAME, signum);
+
+	_exit(0);
 }
 
 /*
@@ -183,53 +140,67 @@
 }
 
 /*
- * Parse IPv4 host:port into sockaddr. Will exit on failure
+ * Parse host:port into sockaddr. Will exit on failure
  */
 static void
-parse_hostport(const char *s, struct sockaddr_in *addr)
+parse_hostport(const char *s, struct sockaddr *addr, socklen_t *len)
 {
-	char *host, *port;
+	char *orig, *host, *port;
+	struct addrinfo hints, *res;
+	int herr;
 
-	if ((host = strdup(s)) == NULL) {
+	if ((host = orig = strdup(s)) == NULL) {
 		fprintf(stderr, "Out of memory\n");
 		exit(1);
 	}
-	if ((port = strchr(host, ':')) == NULL || *(++port) == '\0') {
-		fprintf(stderr, "Invalid -n option.\n");
+	if ((port = strrchr(host, ':')) == NULL ||
+	    *(++port) == '\0' || *host == '\0') {
+		fprintf(stderr, "Invalid -n argument.\n");
 		usage();
 		exit(1);
 	}
 	*(port - 1) = '\0';
-	addr->sin_family = AF_INET;
-	addr->sin_port = atoi(port);
-	if (addr->sin_port <= 0 || addr->sin_port >= 65536) {
-		fprintf(stderr, "Invalid -n port.\n");
-		usage();
+	
+	/* Accept [host]:port for numeric IPv6 addresses */
+	if (*host == '[' && *(port - 2) == ']') {
+		host++;
+		*(port - 2) = '\0';
+	}
+
+	memset(&hints, '\0', sizeof(hints));
+	hints.ai_socktype = SOCK_DGRAM;
+	if ((herr = getaddrinfo(host, port, &hints, &res)) == -1) {
+		fprintf(stderr, "Address lookup failed: %s\n",
+		    gai_strerror(herr));
 		exit(1);
 	}
-	addr->sin_port = htons(addr->sin_port);
-	if (inet_aton(host, &addr->sin_addr) == 0) {
-		fprintf(stderr, "Invalid -n host.\n");
-		usage();
+	if (res == NULL || res->ai_addr == NULL) {
+		fprintf(stderr, "No addresses found for %s:%s\n", host, port);
+		exit(1);
+	}
+	if (res->ai_addrlen > *len) {
+		fprintf(stderr, "Address too long\n");
 		exit(1);
 	}
-	free(host);
+	memcpy(addr, res->ai_addr, res->ai_addrlen);
+	free(orig);
+	*len = res->ai_addrlen;
 }
 
 /*
- * Return a connected PF_INET socket to the specified address
+ * Return a connected socket to the specified address
  */
 static int
-connnected_socket(struct sockaddr_in *addr)
+connsock(struct sockaddr *addr, socklen_t len)
 {
 	int s;
 
-	if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+	if ((s = socket(addr->sa_family, SOCK_DGRAM, 0)) == -1) {
 		fprintf(stderr, "socket() error: %s\n", 
 		    strerror(errno));
 		exit(1);
 	}
-	if (connect(s, (struct sockaddr*)addr, sizeof(*addr)) == -1) {
+	if (connect(s, addr, len) == -1) {
 		fprintf(stderr, "connect() error: %s\n",
 		    strerror(errno));
 		exit(1);
@@ -257,43 +228,19 @@
 		strlcpy(buf, err, n);
 }
 
-/*
- * Per-packet callback function from libpcap. 
- */
-static void
-packet_cb(u_char *user_data, const struct pcap_pkthdr* phdr, 
-    const u_char *pkt)
+static int
+send_netflow_v1(const struct _PFSYNC_STATE *st, u_int n, int *flows_exp)
 {
-	const struct pfsync_header *ph;
-	int i;
-	size_t off;
-	time_t now;
-	struct tm now_tm;
-	struct timeval now_tv;
 	char now_s[64];
-	u_int32_t uptime_ms;
-	u_int8_t packet[NF1_MAXPACKET_SIZE];	/* Maximum allowed packet size (24 flows) */
-	struct NF1_HEADER *hdr = NULL;
-	struct NF1_FLOW *flw = NULL;
-	int j, offset, num_packets, err;
+	int i, j, offset, num_packets, err;
 	socklen_t errsz;
-
-	if (phdr->caplen < PFSYNC_HDRLEN) {
-		syslog(LOG_WARNING, "Runt pfsync packet header");
-		return;
-	}
-
-	ph = (const struct pfsync_header*)pkt;
-
-	if (ph->version != _PFSYNC_VER) {
-		syslog(LOG_WARNING, "Unsupported pfsync version %d, skipping",
-		    ph->version);
-		/* XXX - exit */
-		return;
-	}
-
-	if (ph->action != PFSYNC_ACT_DEL)
-		return;
+	struct NF1_FLOW *flw = NULL;
+	struct NF1_HEADER *hdr = NULL;
+	struct timeval now_tv;
+	struct tm now_tm;
+	time_t now;
+	u_int32_t uptime_ms;
+	u_int8_t packet[NF1_MAXPACKET_SIZE];
 
 	if (verbose_flag) {
 		now = time(NULL);
@@ -305,8 +252,7 @@
 	uptime_ms = timeval_sub_ms(&now_tv, &start_time);
 
 	hdr = (struct NF1_HEADER *)packet;
-	for(num_packets = offset = j = i = 0; i < ph->count; i++) {
-		const struct _PFSYNC_STATE *st;
+	for(num_packets = offset = j = i = 0; i < n; i++) {
 		struct pf_state_host src, dst;
 		u_int32_t bytes_in, bytes_out;
 		u_int32_t packets_in, packets_out;
@@ -315,15 +261,11 @@
 		u_int32_t creation;
 		struct tm creation_tm;
 
-		off = sizeof(*ph) + (sizeof(*st) * i);
-		if (off + sizeof(*st) > phdr->caplen) {
-			syslog(LOG_WARNING, "Runt pfsync packet");
-			return;
-		}
-
 		if (netflow_socket != -1 && j >= NF1_MAXFLOWS - 1) {
-			if (verbose_flag)
-				syslog(LOG_DEBUG, "Sending flow packet len = %d", offset);
+			if (verbose_flag) {
+				syslog(LOG_DEBUG,
+				    "Sending flow packet len = %d", offset);
+			}
 			hdr->flows = htons(hdr->flows);
 			errsz = sizeof(err);
 			getsockopt(netflow_socket, SOL_SOCKET, SO_ERROR,
@@ -331,7 +273,7 @@
 			if (send(netflow_socket, packet,
 			    (size_t)offset, 0) == -1) {
 				syslog(LOG_DEBUG, "send: %s", strerror(errno));
-				return;	
+				return -1;
 			}
 			j = 0;
 			num_packets++;
@@ -347,52 +289,51 @@
 			offset = sizeof(*hdr);
 		}
 
-		st = (const struct _PFSYNC_STATE *)(pkt + off);
-		if (st->af != AF_INET)
-			continue; /* XXX IPv6 support */
-		if (direction != 0 && st->direction != direction)
+		if (st[i].af != AF_INET)
+			continue;
+		if (direction != 0 && st[i].direction != direction)
 			continue;
+
 		/* Copy/convert only what we can eat */
-		creation = ntohl(st->creation) * 1000;
+		creation = ntohl(st[i].creation) * 1000;
 		if (creation > uptime_ms)
 			creation = uptime_ms; /* Avoid u_int wrap */
 
-		if (st->direction == PF_OUT) {
-			memcpy(&src, &st->lan, sizeof(src));
-			memcpy(&dst, &st->ext, sizeof(dst));
+		if (st[i].direction == PF_OUT) {
+			memcpy(&src, &st[i].lan, sizeof(src));
+			memcpy(&dst, &st[i].ext, sizeof(dst));
 		} else {
-			memcpy(&src, &st->ext, sizeof(src));
-			memcpy(&dst, &st->lan, sizeof(dst));
+			memcpy(&src, &st[i].ext, sizeof(src));
+			memcpy(&dst, &st[i].lan, sizeof(dst));
 		}
 
-		/* XXX - IPv4 only for now */
 		flw = (struct NF1_FLOW *)(packet + offset);
-		if (netflow_socket != -1 && st->packets[0] != 0) {
+		if (netflow_socket != -1 && st[i].packets[0] != 0) {
 			flw->src_ip = src.addr.v4.s_addr;
 			flw->dest_ip = dst.addr.v4.s_addr;
 			flw->src_port = src.port;
 			flw->dest_port = dst.port;
-			flw->flow_packets = st->packets[0];
-			flw->flow_octets = st->bytes[0];
+			flw->flow_packets = st[i].packets[0];
+			flw->flow_octets = st[i].bytes[0];
 			flw->flow_start = htonl(uptime_ms - creation);
 			flw->flow_finish = htonl(uptime_ms);
-			flw->protocol = st->proto;
+			flw->protocol = st[i].proto;
 			flw->tcp_flags = 0;
 			offset += sizeof(*flw);
 			j++;
 			hdr->flows++;
 		}
 		flw = (struct NF1_FLOW *)(packet + offset);
-		if (netflow_socket != -1 && st->packets[1] != 0) {
+		if (netflow_socket != -1 && st[i].packets[1] != 0) {
 			flw->src_ip = dst.addr.v4.s_addr;
 			flw->dest_ip = src.addr.v4.s_addr;
 			flw->src_port = dst.port;
 			flw->dest_port = src.port;
-			flw->flow_packets = st->packets[1];
-			flw->flow_octets = st->bytes[1];
+			flw->flow_packets = st[i].packets[1];
+			flw->flow_octets = st[i].bytes[1];
 			flw->flow_start = htonl(uptime_ms - creation);
 			flw->flow_finish = htonl(uptime_ms);
-			flw->protocol = st->proto;
+			flw->protocol = st[i].proto;
 			flw->tcp_flags = 0;
 			offset += sizeof(*flw);
 			j++;
@@ -401,22 +342,22 @@
 		flw = (struct NF1_FLOW *)(packet + offset);
 
 		if (verbose_flag) {
-			packets_out = ntohl(st->packets[0]);
-			packets_in = ntohl(st->packets[1]);
-			bytes_out = ntohl(st->bytes[0]);
-			bytes_in = ntohl(st->bytes[1]);
+			packets_out = ntohl(st[i].packets[0]);
+			packets_in = ntohl(st[i].packets[1]);
+			bytes_out = ntohl(st[i].bytes[0]);
+			bytes_in = ntohl(st[i].bytes[1]);
 
 			creation_tt = now - (creation / 1000);
 			localtime_r(&creation_tt, &creation_tm);
 			strftime(creation_s, sizeof(creation_s), 
 			    "%Y-%m-%dT%H:%M:%S", &creation_tm);
 
-			format_pf_host(src_s, sizeof(src_s), &src, st->af);
-			format_pf_host(dst_s, sizeof(dst_s), &dst, st->af);
-			inet_ntop(st->af, &st->rt_addr, rt_s, sizeof(rt_s));
+			format_pf_host(src_s, sizeof(src_s), &src, st[i].af);
+			format_pf_host(dst_s, sizeof(dst_s), &dst, st[i].af);
+			inet_ntop(st[i].af, &st[i].rt_addr, rt_s, sizeof(rt_s));
 
-			if (st->proto == IPPROTO_TCP || 
-			    st->proto == IPPROTO_UDP) {
+			if (st[i].proto == IPPROTO_TCP || 
+			    st[i].proto == IPPROTO_UDP) {
 				snprintf(pbuf, sizeof(pbuf), ":%d", 
 				    ntohs(src.port));
 				strlcat(src_s, pbuf, sizeof(src_s));
@@ -425,10 +366,10 @@
 				strlcat(dst_s, pbuf, sizeof(dst_s));
 			}
 
-			syslog(LOG_DEBUG, "IFACE %s\n", st->ifname); 
-			syslog(LOG_DEBUG, "GWY %s\n", rt_s); 
+			syslog(LOG_DEBUG, "IFACE %s", st[i].ifname); 
+			syslog(LOG_DEBUG, "GWY %s", rt_s); 
 			syslog(LOG_DEBUG, "FLOW proto %d direction %d", 
-			    st->proto, st->direction);
+			    st[i].proto, st[i].direction);
 			syslog(LOG_DEBUG, "\tstart %s(%u) finish %s(%u)",
 			    creation_s, uptime_ms - creation, 
 			    now_s, uptime_ms);
@@ -440,20 +381,248 @@
 	}
 	/* Send any leftovers */
 	if (netflow_socket != -1 && j != 0) {
-		if (verbose_flag)
-			syslog(LOG_DEBUG, "Sending flow packet len = %d", offset);
+		if (verbose_flag) {
+			syslog(LOG_DEBUG, "Sending flow packet len = %d",
+			    offset);
+		}
 		hdr->flows = htons(hdr->flows);
 		errsz = sizeof(err);
 		getsockopt(netflow_socket, SOL_SOCKET, SO_ERROR,
 		    &err, &errsz); /* Clear ICMP errors */
 		if (send(netflow_socket, packet, (size_t)offset, 0) == -1) {
 			syslog(LOG_DEBUG, "send: %s", strerror(errno));
-			return;	
+			return -1;
 		}
 		num_packets++;
 	}
 
-/*	return (num_packets); */
+	return (ntohs(hdr->flows));
+}
+
+static int
+send_netflow_v5(const struct _PFSYNC_STATE *st, u_int n, int *flows_exp)
+{
+	char now_s[64];
+	int i, j, offset, num_packets, err;
+	socklen_t errsz;
+	struct NF5_FLOW *flw = NULL;
+	struct NF5_HEADER *hdr = NULL;
+	struct timeval now_tv;
+	struct tm now_tm;
+	time_t now;
+	u_int32_t uptime_ms;
+	u_int8_t packet[NF5_MAXPACKET_SIZE];
+
+	if (verbose_flag) {
+		now = time(NULL);
+		localtime_r(&now, &now_tm);
+		strftime(now_s, sizeof(now_s), "%Y-%m-%dT%H:%M:%S", &now_tm);
+	}
+
+	gettimeofday(&now_tv, NULL);
+	uptime_ms = timeval_sub_ms(&now_tv, &start_time);
+
+	hdr = (struct NF5_HEADER *)packet;
+	for(num_packets = offset = j = i = 0; i < n; i++) {
+		struct pf_state_host src, dst;
+		u_int32_t bytes_in, bytes_out, packets_in, packets_out;
+		u_int32_t creation;
+		char src_s[64], dst_s[64], rt_s[64], pbuf[16], creation_s[64];
+		time_t creation_tt;
+		struct tm creation_tm;
+
+		if (netflow_socket != -1 && j >= NF5_MAXFLOWS - 1) {
+			if (verbose_flag) {
+				syslog(LOG_DEBUG,
+				    "Sending flow packet len = %d", offset);
+			}
+			hdr->flows = htons(hdr->flows);
+			errsz = sizeof(err);
+			getsockopt(netflow_socket, SOL_SOCKET, SO_ERROR,
+			    &err, &errsz); /* Clear ICMP errors */
+			if (send(netflow_socket, packet,
+			    (size_t)offset, 0) == -1) {
+				syslog(LOG_DEBUG, "send: %s", strerror(errno));
+				return -1;
+			}
+			j = 0;
+			num_packets++;
+		}
+
+		if (netflow_socket != -1 && j == 0) {
+			memset(&packet, '\0', sizeof(packet));
+			hdr->version = htons(5);
+			hdr->flows = 0; /* Filled in as we go */
+			hdr->uptime_ms = htonl(uptime_ms);
+			hdr->time_sec = htonl(now_tv.tv_sec);
+			hdr->time_nanosec = htonl(now_tv.tv_usec * 1000);
+			hdr->flow_sequence = htonl(*flows_exp);
+			/* Other fields are left zero */
+			offset = sizeof(*hdr);
+		}
+
+		if (st[i].af != AF_INET)
+			continue;
+		if (direction != 0 && st[i].direction != direction)
+			continue;
+
+		/* Copy/convert only what we can eat */
+		creation = ntohl(st[i].creation) * 1000;
+		if (creation > uptime_ms)
+			creation = uptime_ms; /* Avoid u_int wrap */
+
+		if (st[i].direction == PF_OUT) {
+			memcpy(&src, &st[i].lan, sizeof(src));
+			memcpy(&dst, &st[i].ext, sizeof(dst));
+		} else {
+			memcpy(&src, &st[i].ext, sizeof(src));
+			memcpy(&dst, &st[i].lan, sizeof(dst));
+		}
+
+		flw = (struct NF5_FLOW *)(packet + offset);
+		if (netflow_socket != -1 && st[i].packets[0] != 0) {
+			flw->src_ip = src.addr.v4.s_addr;
+			flw->dest_ip = dst.addr.v4.s_addr;
+			flw->src_port = src.port;
+			flw->dest_port = dst.port;
+			flw->flow_packets = st[i].packets[0];
+			flw->flow_octets = st[i].bytes[0];
+			flw->flow_start = htonl(uptime_ms - creation);
+			flw->flow_finish = htonl(uptime_ms);
+			flw->tcp_flags = 0;
+			flw->protocol = st[i].proto;
+			offset += sizeof(*flw);
+			j++;
+			hdr->flows++;
+		}
+		flw = (struct NF5_FLOW *)(packet + offset);
+		if (netflow_socket != -1 && st[i].packets[1] != 0) {
+			flw->src_ip = dst.addr.v4.s_addr;
+			flw->dest_ip = src.addr.v4.s_addr;
+			flw->src_port = dst.port;
+			flw->dest_port = src.port;
+			flw->flow_packets = st[i].packets[1];
+			flw->flow_octets = st[i].bytes[1];
+			flw->flow_start = htonl(uptime_ms - creation);
+			flw->flow_finish = htonl(uptime_ms);
+			flw->tcp_flags = 0;
+			flw->protocol = st[i].proto;
+			offset += sizeof(*flw);
+			j++;
+			hdr->flows++;
+		}
+		flw = (struct NF5_FLOW *)(packet + offset);
+
+		if (verbose_flag) {
+			packets_out = ntohl(st[i].packets[0]);
+			packets_in = ntohl(st[i].packets[1]);
+			bytes_out = ntohl(st[i].bytes[0]);
+			bytes_in = ntohl(st[i].bytes[1]);
+
+			creation_tt = now - (creation / 1000);
+			localtime_r(&creation_tt, &creation_tm);
+			strftime(creation_s, sizeof(creation_s), 
+			    "%Y-%m-%dT%H:%M:%S", &creation_tm);
+
+			format_pf_host(src_s, sizeof(src_s), &src, st[i].af);
+			format_pf_host(dst_s, sizeof(dst_s), &dst, st[i].af);
+			inet_ntop(st[i].af, &st[i].rt_addr, rt_s, sizeof(rt_s));
+
+			if (st[i].proto == IPPROTO_TCP || 
+			    st[i].proto == IPPROTO_UDP) {
+				snprintf(pbuf, sizeof(pbuf), ":%d", 
+				    ntohs(src.port));
+				strlcat(src_s, pbuf, sizeof(src_s));
+				snprintf(pbuf, sizeof(pbuf), ":%d", 
+				    ntohs(dst.port));
+				strlcat(dst_s, pbuf, sizeof(dst_s));
+			}
+
+			syslog(LOG_DEBUG, "IFACE %s", st[i].ifname); 
+			syslog(LOG_DEBUG, "GWY %s", rt_s); 
+			syslog(LOG_DEBUG, "FLOW proto %d direction %d", 
+			    st[i].proto, st[i].direction);
+			syslog(LOG_DEBUG, "\tstart %s(%u) finish %s(%u)",
+			    creation_s, uptime_ms - creation, 
+			    now_s, uptime_ms);
+			syslog(LOG_DEBUG, "\t%s -> %s %d bytes %d packets",
+			    src_s, dst_s, bytes_out, packets_out);
+			syslog(LOG_DEBUG, "\t%s -> %s %d bytes %d packets",
+			    dst_s, src_s, bytes_in, packets_in);
+		}
+	}
+	/* Send any leftovers */
+	if (netflow_socket != -1 && j != 0) {
+		if (verbose_flag) {
+			syslog(LOG_DEBUG, "Sending flow packet len = %d",
+			    offset);
+		}
+		hdr->flows = htons(hdr->flows);
+		errsz = sizeof(err);
+		getsockopt(netflow_socket, SOL_SOCKET, SO_ERROR,
+		    &err, &errsz); /* Clear ICMP errors */
+		if (send(netflow_socket, packet, (size_t)offset, 0) == -1) {
+			syslog(LOG_DEBUG, "send: %s", strerror(errno));
+			return -1;
+		}
+		num_packets++;
+	}
+
+	return (ntohs(hdr->flows));
+}
+
+/*
+ * Per-packet callback function from libpcap. 
+ */
+static void
+packet_cb(u_char *user_data, const struct pcap_pkthdr* phdr, 
+    const u_char *pkt)
+{
+	const struct pfsync_header *ph = (const struct pfsync_header *)pkt;
+	const struct _PFSYNC_STATE *st;
+	int r = 0;
+
+	if (phdr->caplen < PFSYNC_HDRLEN) {
+		syslog(LOG_WARNING, "Runt pfsync packet header");
+		return;
+	}
+	if (ph->version != _PFSYNC_VER) {
+		syslog(LOG_WARNING, "Unsupported pfsync version %d, exiting",
+		    ph->version);
+		exit(1);
+	}
+	if (ph->count == 0) {
+		syslog(LOG_WARNING, "Empty pfsync packet");
+		return;
+	}
+	/* Skip non-delete messages */
+	if (ph->action != PFSYNC_ACT_DEL)
+		return;
+	if (sizeof(*ph) + (sizeof(*st) * ph->count) > phdr->caplen) {
+		syslog(LOG_WARNING, "Runt pfsync packet");
+		return;
+	}
+
+	st = (const struct _PFSYNC_STATE *)((const u_int8_t *)ph + sizeof(*ph));
+
+	switch (export_version) {
+	case 1:
+		r = send_netflow_v1(st, ph->count, &flows_exported);
+		break;
+	case 5:
+		r = send_netflow_v5(st, ph->count, &flows_exported);
+		break;
+	default:
+		/* should never reach this point */
+		syslog(LOG_DEBUG, "Invalid netflow version, exiting");
+		exit(1);
+	}
+
+	if (r > 0) {
+		flows_exported += r;
+		if (verbose_flag)
+			syslog(LOG_DEBUG, "flows_exported = %d", flows_exported);
+	}
 }
 
 /*
@@ -546,13 +715,15 @@
 	extern char *__progname;
 	int ch, dontfork_flag, r;
 	pcap_t *pcap = NULL;
-	struct sockaddr_in netflow_dest;
-	
+	struct sockaddr_storage dest;
+	socklen_t destlen;
+
 	bpf_prog = NULL;
 	dev = capfile = NULL;
 	dontfork_flag = 0;
-	memset(&netflow_dest, '\0', sizeof(netflow_dest));
-	while ((ch = getopt(argc, argv, "hdDi:n:r:S:")) != -1) {
+	memset(&dest, '\0', sizeof(dest));
+	destlen = 0;
+	while ((ch = getopt(argc, argv, "hdDi:n:r:S:v:")) != -1) {
 		switch (ch) {
 		case 'h':
 			usage();
@@ -588,7 +759,9 @@
 			break;
 		case 'n':
 			/* Will exit on failure */
-			parse_hostport(optarg, &netflow_dest);
+			destlen = sizeof(dest);
+			parse_hostport(optarg, (struct sockaddr *)&dest,
+			    &destlen);
 			break;
 		case 'r':
 			if (capfile != NULL || dev != NULL) {
@@ -599,6 +772,16 @@
 			capfile = optarg;
 			dontfork_flag = 1;
 			break;
+		case 'v':
+			switch((export_version = atoi(optarg))) {
+			case 1:
+			case 5:
+				break;
+			default:
+				fprintf(stderr, "Invalid NetFlow version\n");
+				exit(1);
+			}
+			break;
 		default:
 			fprintf(stderr, "Invalid commandline option.\n");
 			usage();
@@ -616,8 +799,15 @@
 	setup_packet_capture(&pcap, dev, capfile, bpf_prog);
 	
 	/* Netflow send socket */
-	if (netflow_dest.sin_family != 0)
-		netflow_socket = connnected_socket(&netflow_dest);
+	if (dest.ss_family != 0)
+		netflow_socket = connsock((struct sockaddr *)&dest, destlen);
+	else {
+		fprintf(stderr, "No export target defined\n");
+		if (!verbose_flag)
+			exit(1);
+	}
+
+fprintf(stderr, "ZZZZ %d\n", netflow_socket);
 
 	if (dontfork_flag) {
 		if (!verbose_flag)
@@ -647,44 +837,15 @@
 
 	/* Main processing loop */
 	gettimeofday(&start_time, NULL);
-	for(;;) {
-		struct pollfd pl[1];
-
-		if (exit_flag) {
-			syslog(LOG_NOTICE, "Exiting on signal %d", exit_flag);
-			exit(0);
-		}
-
-		/*
-		 * Silly libpcap's timeout function doesn't work, so we
-		 * do it here (only if we are reading live)
-		 */
-		r = 0;
-		if (capfile == NULL) {
-			memset(pl, '\0', sizeof(pl));
-			pl[0].events = POLLIN|POLLERR|POLLHUP;
-			pl[0].fd = pcap_fileno(pcap);
-			if (poll(pl, 1, -1) == -1) {
-				if (errno == EINTR)
-					continue;
-				syslog(LOG_ERR, "poll: %s", strerror(errno));
-				exit(1);
-			}
-			/* Shouldn't happen unless we specify a timeout */
-			if (pl[0].revents == 0)
-				continue;
-		}
 
-		r = pcap_dispatch(pcap, 1024, packet_cb, NULL);
-		if (r == -1) {
-			syslog(LOG_ERR, "pcap_dispatch: %s", pcap_geterr(pcap));
-			exit(1);
-		} else if (r == 0) {
-			syslog(LOG_NOTICE, "Exiting on pcap EOF");
-			exit(0);
-		}
+	r = pcap_loop(pcap, -1, packet_cb, NULL);
+	if (r == -1) {
+		syslog(LOG_ERR, "pcap_dispatch: %s", pcap_geterr(pcap));
+		exit(1);
 	}
 
-	/* NOTREACHED */
-	exit(1);
+	if (r == 0 && capfile == NULL)
+		syslog(LOG_NOTICE, "Exiting on pcap EOF");
+
+	exit(0);
 }
Index: pfflowd/pfflowd.h
diff -u /dev/null pfflowd/pfflowd.h:1.2
--- /dev/null	Mon Sep  6 22:36:54 2004
+++ pfflowd/pfflowd.h	Mon Sep  6 22:27:58 2004
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org>
+ *
+ * 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.
+ */
+
+#ifndef _PFFLOWD_H
+#define _PFFLOWD_H
+
+#define PROGNAME                "pfflowd"
+#define PROGVER                 "0.6"
+
+#ifndef PRIVDROP_USER
+# define PRIVDROP_USER          "nobody"
+#endif   
+
+#define PRIVDROP_CHROOT_DIR     "/var/empty"
+
+#define DEFAULT_INTERFACE       "pfsync0"
+#define LIBPCAP_SNAPLEN         2020    /* Default MTU */
+ 
+#ifdef OLD_PFSYNC
+# define _PFSYNC_STATE          pf_state   
+# define _PFSYNC_VER            1
+#else
+# define _PFSYNC_STATE          pfsync_state
+# define _PFSYNC_VER            2
+#endif
+
+/*
+ * This is the Cisco Netflow(tm) version 1 packet format
+ * Based on:
+ * http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_0/nfc_ug/nfc
+form.htm
+ */
+struct NF1_HEADER {
+        u_int16_t version, flows;
+        u_int32_t uptime_ms, time_sec, time_nanosec;
+};
+struct NF1_FLOW {
+        u_int32_t src_ip, dest_ip, nexthop_ip;
+        u_int16_t if_index_in, if_index_out;
+        u_int32_t flow_packets, flow_octets;
+        u_int32_t flow_start, flow_finish;
+        u_int16_t src_port, dest_port;
+        u_int16_t pad1;
+        u_int8_t protocol, tos, tcp_flags;
+        u_int8_t pad2, pad3, pad4;
+        u_int32_t reserved1;
+#if 0
+        u_int8_t reserved2; /* XXX: no longer used */
+#endif
+};
+/* Maximum of 24 flows per packet */
+#define NF1_MAXFLOWS            24
+#define NF1_MAXPACKET_SIZE      (sizeof(struct NF1_HEADER) + \
+                                 (NF1_MAXFLOWS * sizeof(struct NF1_FLOW)))
+
+/*
+ * This is the Cisco Netflow(tm) version 5 packet format
+ * Based on:
+ * http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_0/nfc_ug/nfc
+form.htm
+ */
+struct NF5_HEADER {
+   u_int16_t version, flows;
+   u_int32_t uptime_ms, time_sec, time_nanosec, flow_sequence;
+   u_int8_t engine_type, engine_id, reserved1, reserved2;
+};
+struct NF5_FLOW {
+   u_int32_t src_ip, dest_ip, nexthop_ip;
+   u_int16_t if_index_in, if_index_out;
+   u_int32_t flow_packets, flow_octets;
+   u_int32_t flow_start, flow_finish;
+   u_int16_t src_port, dest_port;
+   u_int8_t pad1;
+   u_int8_t tcp_flags, protocol, tos;
+   u_int16_t src_as, dest_as;
+   u_int8_t src_mask, dst_mask;
+   u_int16_t pad2;
+};
+/* Maximum of 30 flows per packet */
+#define NF5_MAXFLOWS    30
+#define NF5_MAXPACKET_SIZE (sizeof(struct NF5_HEADER) + \
+             (NF5_MAXFLOWS * sizeof(struct NF5_FLOW)))
+
+#endif /* _PFFLOWD_H */
