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 #include #include #include -#include #include #include @@ -28,8 +27,6 @@ #include #include -#include -#include #include #include @@ -44,60 +41,15 @@ #include #include #include +#include +#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 + * + * 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 */