├── .cvsignore ├── LICENSE ├── Makefile.in ├── README ├── TODO ├── aclocal.m4 ├── closefrom.c ├── collector.pl ├── common.h ├── configure.ac ├── convtime.c ├── convtime.h ├── daemon.c ├── freelist.c ├── freelist.h ├── install-sh ├── ipfix.c ├── log.c ├── log.h ├── mkinstalldirs ├── netflow1.c ├── netflow5.c ├── netflow9.c ├── softflowctl.8 ├── softflowctl.c ├── softflowd.8 ├── softflowd.c ├── softflowd.h ├── softflowd.init ├── softflowd.spec ├── softflowd.sysconfig ├── strlcat.c ├── strlcpy.c ├── sys-tree.h └── treetype.h /.cvsignore: -------------------------------------------------------------------------------- 1 | softflowd 2 | softflowctl 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Original code is licensed under the following BSD-style license: 2 | 3 | /* 4 | * Copyright 2002-2006 Damien Miller All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | closefrom.c: 28 | 29 | /* 30 | * Copyright (c) 2004 Todd C. Miller 31 | * 32 | * Permission to use, copy, modify, and distribute this software for any 33 | * purpose with or without fee is hereby granted, provided that the above 34 | * copyright notice and this permission notice appear in all copies. 35 | * 36 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 37 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 38 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 39 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 40 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 41 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 42 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 43 | */ 44 | 45 | convtime.c: 46 | 47 | /* 48 | * Copyright (c) 2001 Kevin Steves. All rights reserved. 49 | * 50 | * Redistribution and use in source and binary forms, with or without 51 | * modification, are permitted provided that the following conditions 52 | * are met: 53 | * 1. Redistributions of source code must retain the above copyright 54 | * notice, this list of conditions and the following disclaimer. 55 | * 2. Redistributions in binary form must reproduce the above copyright 56 | * notice, this list of conditions and the following disclaimer in the 57 | * documentation and/or other materials provided with the distribution. 58 | * 59 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 60 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 61 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 62 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 63 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 64 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 65 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 66 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 67 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 68 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 69 | */ 70 | 71 | daemon.c: 72 | 73 | /*- 74 | * Copyright (c) 1990, 1993 75 | * The Regents of the University of California. All rights reserved. 76 | * 77 | * Redistribution and use in source and binary forms, with or without 78 | * modification, are permitted provided that the following conditions 79 | * are met: 80 | * 1. Redistributions of source code must retain the above copyright 81 | * notice, this list of conditions and the following disclaimer. 82 | * 2. Redistributions in binary form must reproduce the above copyright 83 | * notice, this list of conditions and the following disclaimer in the 84 | * documentation and/or other materials provided with the distribution. 85 | * 3. Neither the name of the University nor the names of its contributors 86 | * may be used to endorse or promote products derived from this software 87 | * without specific prior written permission. 88 | * 89 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 90 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 91 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 92 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 93 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 94 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 95 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 96 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 97 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 98 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 99 | * SUCH DAMAGE. 100 | */ 101 | 102 | strlcat.c, strlcpy.c: 103 | 104 | /* 105 | * Copyright (c) 1998 Todd C. Miller 106 | * 107 | * Permission to use, copy, modify, and distribute this software for any 108 | * purpose with or without fee is hereby granted, provided that the above 109 | * copyright notice and this permission notice appear in all copies. 110 | * 111 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 112 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 113 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 114 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 115 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 116 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 117 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 118 | */ 119 | 120 | sys-tree.h: 121 | 122 | /* 123 | * Copyright 2002 Niels Provos 124 | * All rights reserved. 125 | * 126 | * Redistribution and use in source and binary forms, with or without 127 | * modification, are permitted provided that the following conditions 128 | * are met: 129 | * 1. Redistributions of source code must retain the above copyright 130 | * notice, this list of conditions and the following disclaimer. 131 | * 2. Redistributions in binary form must reproduce the above copyright 132 | * notice, this list of conditions and the following disclaimer in the 133 | * documentation and/or other materials provided with the distribution. 134 | * 135 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 136 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 137 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 138 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 139 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 140 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 141 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 142 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 143 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 144 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 145 | */ 146 | 147 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | bindir=@bindir@ 4 | sbindir=@sbindir@ 5 | libexecdir=@libexecdir@ 6 | datadir=@datadir@ 7 | mandir=@mandir@ 8 | sysconfdir=@sysconfdir@ 9 | srcdir=@srcdir@ 10 | top_srcdir=@top_srcdir@ 11 | VPATH=@srcdir@ 12 | CC=@CC@ 13 | LDFLAGS=@LDFLAGS@ 14 | CFLAGS=@CFLAGS@ 15 | CPPFLAGS=-I$(srcdir) @CPPFLAGS@ 16 | LIBS=@LIBS@ 17 | EXEEXT=@EXEEXT@ 18 | INSTALL=@INSTALL@ 19 | 20 | #CFLAGS+=-DFLOW_RB # Use red-black tree for flows 21 | CFLAGS+=-DFLOW_SPLAY # Use splay tree for flows 22 | CFLAGS+=-DEXPIRY_RB # Use red-black tree for expiry events 23 | #CFLAGS+=-DEXPIRY_SPLAY # Use splay tree for expiry events 24 | 25 | TARGETS=softflowd${EXEEXT} softflowctl${EXEEXT} 26 | 27 | COMMON=convtime.o strlcpy.o strlcat.o closefrom.o daemon.o 28 | SOFTFLOWD=softflowd.o log.o netflow1.o netflow5.o netflow9.o ipfix.o freelist.o 29 | 30 | all: $(TARGETS) 31 | 32 | softflowd${EXEEXT}: ${SOFTFLOWD} $(COMMON) 33 | $(CC) $(LDFLAGS) -o $@ ${SOFTFLOWD} $(COMMON) $(LIBS) 34 | 35 | softflowctl${EXEEXT}: softflowctl.o $(COMMON) 36 | $(CC) $(LDFLAGS) -o $@ softflowctl.o $(COMMON) $(LIBS) 37 | 38 | clean: 39 | rm -f $(TARGETS) *.o core *.core 40 | 41 | realclean: clean 42 | rm -rf autom4te.cache Makefile config.log config.status 43 | 44 | distclean: realclean 45 | rm -f config.h* configure 46 | 47 | strip: 48 | strip $(TARGETS) 49 | 50 | install: 51 | [ -d $(DESTDIR)$(sbindir) ] || \ 52 | $(srcdir)/mkinstalldirs $(DESTDIR)$(sbindir) 53 | [ -d $(DESTDIR)$(mandir)/man8 ] || \ 54 | $(srcdir)/mkinstalldirs $(DESTDIR)$(mandir)/man8 55 | $(INSTALL) -m 0755 -s softflowd $(DESTDIR)$(sbindir)/softflowd 56 | $(INSTALL) -m 0755 -s softflowctl $(DESTDIR)$(sbindir)/softflowctl 57 | $(INSTALL) -m 0644 softflowd.8 $(DESTDIR)$(mandir)/man8/softflowd.8 58 | $(INSTALL) -m 0644 softflowctl.8 $(DESTDIR)$(mandir)/man8/softflowctl.8 59 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Welcome to softflowd, a flow-based network monitor. 2 | 3 | Introduction 4 | ------------ 5 | 6 | softflowd listens promiscuously on a network interface and semi-statefully 7 | tracks network flows. These flows can be reported using NetFlow version 1, 5 8 | or 9 datagrams. softflowd is fully IPv6 capable: it can track IPv6 flows and 9 | export to IPv6 hosts. 10 | 11 | More details about softflowd's function and usage may be found in the 12 | supplied manpages, which you can view prior to installation using 13 | 14 | /usr/bin/nroff -c -mandoc softflowd.8 | less 15 | /usr/bin/nroff -c -mandoc softflowctl.8 | less 16 | 17 | If you are in need of a NetFlow collector, you may be interested in 18 | softflowd's companion project "flowd" (http://www.mindrot.org/flowd.html). 19 | flowd is a NetFlow collector that is maintained in parallel with 20 | softflowd and includes a few handy features, such as the ability 21 | to filter flows it receives as well as Perl and Python APIs to its 22 | storage format. NB. You don't have to use flowd: any NetFlow compatible 23 | collector should work with softflowd. An example Perl collector is included 24 | for testing purposes as collector.pl, but it doesn't yet support NetFlow v.9 25 | 26 | Installing 27 | ---------- 28 | 29 | Building softflowd should be as simple as typing: 30 | 31 | ./configure 32 | make 33 | make install 34 | 35 | Unfortunately some systems like to make life complicated. Things work 36 | fine on the systems that I develop and test on (OpenBSD and Linux). 37 | There is peliminary support for Solaris 9 (i.e. it compiled), but no 38 | testing on this platform has been performed. 39 | 40 | Licensing 41 | --------- 42 | 43 | Softflowd is licensed under a two-term BSD license (see the source 44 | files for details). The code in sys-tree.h is Copyright Niels Provos 45 | and comes straight from OpenBSD CVS, convtime.c 46 | comes is Copyright Kevin Steves and comes from OpenSSH (misc.c). Both 47 | of these files are licensed under two-term BSD licenses too. strlcpy.c, 48 | strlcat.c and closefrom.c also come from OpenBSD CVS and are Copyright 49 | Todd C. Miller. Please refer to the LICENSE file for full details. 50 | 51 | Reporting Bugs 52 | -------------- 53 | 54 | Please report bugs in softflowd to http://bugzilla.mindrot.org/ If you 55 | find a security bug, please report it directly by email. If you have any 56 | feedback or questions, please email me: 57 | 58 | Contributing 59 | ------------ 60 | 61 | Softflowd has an extensive TODO list of interesting features, large and 62 | small, that are waiting to be implemented. If you are interested in 63 | helping, please contact me. 64 | 65 | The latest source code may be obtained from Google Code: 66 | http://code.google.com/p/softflowd/ 67 | 68 | Damien Miller 69 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Things yet to do: 2 | 3 | softflowd 4 | --------- 5 | 6 | - Use strtonum() 7 | 8 | Flow tracking engine 9 | - Calculate hash over flow and use it as a key to avoid lots of 10 | cache-trashing comparisons 11 | - Verify checksums (maybe. perhaps bad for accounting, good for flow tracking) 12 | - Fragment processing 13 | - We don't handle fragments right 14 | - This shouldn't be too hard or too memory intensive. We just need to 15 | keep a tree of fragment entries. Each entry would need to contain 16 | enough information to reconstruct the flow (source/dest addr, etc), 17 | but also fragment related info: IP id, list of fragment offsets. etc. 18 | - When we receive a new fragment, we add an entry to this tree (keyed 19 | by source IP, protocol, IP id) 20 | - Each new fragment matched in the tree gets its offset added to the 21 | list, until all fragments have been seen 22 | - Must be careful, as later fragments may arrive before inital one 23 | - When does accounting occur? 24 | - Upon receipt of inital fragment? (and thus for ever frag thereafter) 25 | - When we have seen all fragments? (what if we don't?) 26 | - Must limit size of tree 27 | - Must have fragment timeout (what happens then, apart from removal?) 28 | - Timeouts 29 | - Timeout for unanswered TCP connection 30 | - Ditto orphaned connections (one packet in one direction only) 31 | - Track ICMP generated by TCP/UDP session (painful, probably unecessary) 32 | - More datalink types 33 | - Improve fast-expiry of TCP session by tracking FIN sequence numbers 34 | - Multiple interface support 35 | - Requires some way to avoid duplicate recording of flows (IP ID) 36 | - Track IPsec SPIs 37 | - Track ToS / DSCP 38 | - Make counters 64 bits 39 | - We can report these directly for NetFlow v.9 40 | - For older NetFlow, report by sending multiple flows until counter < 2^32 41 | 42 | Misc features 43 | - Ability to open more than one interface (maybe) 44 | - Ability to read more than one pcap file (maybe) 45 | - Fork for ctlsock actions? (don't block mainloop) 46 | - Remote control over network (requires SSL) 47 | 48 | Performance 49 | - Profile and see where the hot spots are 50 | - Fast "new flow" test using a bloom filter 51 | - See if we can reduce per-packet overhead more 52 | - Cost of expiry remove and re-add per packet 53 | - Stop run-time malloc (maybe) 54 | - Preallocate a pool of expiry events and flow entries 55 | - keep a queue, pick/push first from head 56 | 57 | Exporter features 58 | - sflow support (www.sflow.org) 59 | - Needs XDR encoding 60 | - Ability to export to multiple hosts 61 | - Partly done, just need to keep a list of targets instead of a single one 62 | - Ability to directly write to file (maybe. If so, reuse flowd store code) 63 | - NetFlow v.9 field selection 64 | - Get AS numbers from bgpd and fill in to Netflow packets 65 | 66 | Statistics code 67 | - Collect more statistics (maybe) 68 | - Advanced packet analysis: store hash of packet payload, keep 69 | statistics on traffic similarity 70 | - Bloom filter? 71 | - Option to record histograms of 72 | - Flow lifetime and size, packet size 73 | - Flow bandwidth 74 | - Per well-known-port 75 | - How to do this quicky? Memory efficiently? 76 | - Per IP address/range 77 | - How to do this quicky? Memory efficiently? 78 | - Moving averages 79 | - Track traffic over lifetime of flow 80 | - Maintain linked list traffic counts, keyed by time interval 81 | - E.g. key by (now / 300) 82 | - Or (now - start_time) / 300 (better) 83 | - When new packet comes in: 84 | - If timestamp of HEAD of list == (now / xxx), then counter += octets 85 | - Otherwise create new traffic counter at HEAD and update it 86 | - Then trim tail if the list length is too big 87 | - Maybe store "hunks" of data, rather than individual counts in the 88 | list, as storing a single int is a huge waste of space 89 | - Maybe a rrdtool-like heirarchy of timespans 90 | - 300 seconds (5 minutes) (2400 bytes) 91 | - 360 1-minute blocks (6 hours) (2880 bytes) 92 | - 288 10-minute blocks (2 days) (2304 bytes) 93 | - 336 1-hour blocks (2 weeks) (2688 bytes) 94 | - Total 10kb worst-case per-flow (scary, probably overkill) 95 | 96 | softflowctl 97 | ----------- 98 | 99 | - Extend interface 100 | - Query for specific flows (e.g. by address) 101 | - Do this in softflowd or softflowctl? 102 | - Expire/delete specific flows (maybe) 103 | - Runtime respecify timeouts 104 | - Real-time binary dump of flowtable (shm/mmap fd pass?) 105 | - ntop like view 106 | - Spiffy GUI (separate tool) 107 | 108 | -------------------------------------------------------------------------------- /aclocal.m4: -------------------------------------------------------------------------------- 1 | dnl Nothing here 2 | -------------------------------------------------------------------------------- /closefrom.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004 Todd C. Miller 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "common.h" 18 | 19 | #ifndef HAVE_CLOSEFROM 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #ifdef HAVE_DIRENT_H 29 | # include 30 | # define NAMLEN(dirent) strlen((dirent)->d_name) 31 | #else 32 | # define dirent direct 33 | # define NAMLEN(dirent) (dirent)->d_namlen 34 | # ifdef HAVE_SYS_NDIR_H 35 | # include 36 | # endif 37 | # ifdef HAVE_SYS_DIR_H 38 | # include 39 | # endif 40 | # ifdef HAVE_NDIR_H 41 | # include 42 | # endif 43 | #endif 44 | 45 | #ifndef OPEN_MAX 46 | # define OPEN_MAX 256 47 | #endif 48 | 49 | #ifndef lint 50 | static const char rcsid[] = "$Sudo: closefrom.c,v 1.6 2004/06/01 20:51:56 millert Exp $"; 51 | #endif /* lint */ 52 | 53 | /* 54 | * Close all file descriptors greater than or equal to lowfd. 55 | */ 56 | void 57 | closefrom(int lowfd) 58 | { 59 | long fd, maxfd; 60 | #if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) 61 | char fdpath[PATH_MAX], *endp; 62 | struct dirent *dent; 63 | DIR *dirp; 64 | int len; 65 | 66 | /* Check for a /proc/$$/fd directory. */ 67 | len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); 68 | if (len != -1 && len <= sizeof(fdpath) && (dirp = opendir(fdpath))) { 69 | while ((dent = readdir(dirp)) != NULL) { 70 | fd = strtol(dent->d_name, &endp, 10); 71 | if (dent->d_name != endp && *endp == '\0' && 72 | fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) 73 | (void) close((int) fd); 74 | } 75 | (void) closedir(dirp); 76 | } else 77 | #endif 78 | { 79 | /* 80 | * Fall back on sysconf() or getdtablesize(). We avoid checking 81 | * resource limits since it is possible to open a file descriptor 82 | * and then drop the rlimit such that it is below the open fd. 83 | */ 84 | #ifdef HAVE_SYSCONF 85 | maxfd = sysconf(_SC_OPEN_MAX); 86 | #else 87 | maxfd = getdtablesize(); 88 | #endif /* HAVE_SYSCONF */ 89 | if (maxfd < 0) 90 | maxfd = OPEN_MAX; 91 | 92 | for (fd = lowfd; fd < maxfd; fd++) 93 | (void) close((int) fd); 94 | } 95 | } 96 | 97 | #endif /* HAVE_CLOSEFROM */ 98 | 99 | -------------------------------------------------------------------------------- /collector.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | # This is a Cisco NetFlow datagram collector 4 | 5 | # Netflow protocol reference: 6 | # http://www.cisco.com/en/US/products/sw/netmgtsw/ps1964/products_implementation_design_guide09186a00800d6a11.html 7 | 8 | # XXX Doesn't support NetFlow 9 9 | 10 | my $af; 11 | 12 | BEGIN { 13 | use strict; 14 | use warnings; 15 | use IO qw(Socket); 16 | use Socket; 17 | use Carp; 18 | use POSIX qw(strftime); 19 | use Getopt::Long; 20 | eval "use IO::Socket::INET6;"; 21 | eval "use Socket6;"; 22 | } 23 | 24 | ############################################################################ 25 | 26 | sub timestamp() 27 | { 28 | return strftime "%Y-%m-%dT%H:%M:%S", localtime; 29 | } 30 | 31 | sub fuptime($) 32 | { 33 | my $t = shift; 34 | my $r = ""; 35 | my $tmp; 36 | 37 | # Milliseconds 38 | $tmp = $t % 1000; 39 | $r = sprintf ".%03u%s", $tmp, $r; 40 | 41 | # Seconds 42 | $t = int($t / 1000); 43 | $tmp = $t % 60; 44 | $r = "${tmp}s${r}"; 45 | 46 | # Minutes 47 | $t = int($t / 60); 48 | $tmp = $t % 60; 49 | $r = "${tmp}m${r}" if $tmp; 50 | 51 | # Hours 52 | $t = int($t / 60); 53 | $tmp = $t % 24; 54 | $r = "${tmp}h${r}" if $tmp; 55 | 56 | # Days 57 | $t = int($t / 24); 58 | $tmp = $t % 7; 59 | $r = "${tmp}d${r}" if $tmp; 60 | 61 | # Weeks 62 | $t = int($t / 7); 63 | $tmp = $t % 52; 64 | $r = "${tmp}w${r}" if $tmp; 65 | 66 | # Years 67 | $t = int($t / 52); 68 | $r = "${tmp}y${r}" if $tmp; 69 | 70 | return $r; 71 | } 72 | 73 | sub do_listen($$) 74 | { 75 | my $port = shift 76 | or confess "No UDP port specified"; 77 | 78 | my $socket; 79 | 80 | if ($af == 4) { 81 | $socket = IO::Socket::INET->new(Proto=>'udp', LocalPort=>$port) 82 | or croak "Couldn't open UDP socket: $!"; 83 | } elsif ($af == 6) { 84 | $socket = IO::Socket::INET6->new(Proto=>'udp', LocalPort=>$port) 85 | or croak "Couldn't open UDP socket: $!"; 86 | } else { 87 | croak "Unsupported AF"; 88 | } 89 | 90 | return $socket; 91 | } 92 | 93 | sub process_nf_v1($$) 94 | { 95 | my $sender = shift; 96 | my $pkt = shift; 97 | my %header; 98 | my %flow; 99 | my $sender_s; 100 | 101 | %header = qw(); 102 | 103 | $sender_s = inet_ntoa($sender) if $af == 4; 104 | $sender_s = inet_ntop(AF_INET6, $sender) if $af == 6; 105 | 106 | ($header{ver}, $header{flows}, $header{uptime}, $header{secs}, 107 | $header{nsecs}) = unpack("nnNNN", $pkt); 108 | 109 | if (length($pkt) < (16 + (48 * $header{flows}))) { 110 | printf STDERR timestamp()." Short Netflow v.1 packet: %d < %d\n", 111 | length($pkt), 16 + (48 * $header{flows}); 112 | return; 113 | } 114 | 115 | printf timestamp() . " HEADER v.%u (%u flow%s)\n", $header{ver}, 116 | $header{flows}, $header{flows} == 1 ? "" : "s"; 117 | 118 | for(my $i = 0; $i < $header{flows}; $i++) { 119 | my $off = 16 + (48 * $i); 120 | my $ptr = substr($pkt, $off, 52); 121 | 122 | %flow = qw(); 123 | 124 | (my $src1, my $src2, my $src3, my $src4, 125 | my $dst1, my $dst2, my $dst3, my $dst4, 126 | my $nxt1, my $nxt2, my $nxt3, my $nxt4, 127 | $flow{in_ndx}, $flow{out_ndx}, $flow{pkts}, $flow{bytes}, 128 | $flow{start}, $flow{finish}, $flow{src_port}, $flow{dst_port}, 129 | my $pad1, $flow{protocol}, $flow{tos}, $flow{tcp_flags}) = 130 | unpack("CCCCCCCCCCCCnnNNNNnnnCCC", $ptr); 131 | 132 | $flow{src} = sprintf "%u.%u.%u.%u", $src1, $src2, $src3, $src4; 133 | $flow{dst} = sprintf "%u.%u.%u.%u", $dst1, $dst2, $dst3, $dst4; 134 | $flow{nxt} = sprintf "%u.%u.%u.%u", $nxt1, $nxt2, $nxt3, $nxt4; 135 | 136 | printf timestamp() . " " . 137 | "from %s started %s finish %s proto %u %s:%u > %s:%u %u " . 138 | "packets %u octets\n", 139 | $sender_s, 140 | fuptime($flow{start}), fuptime($flow{finish}), 141 | $flow{protocol}, 142 | $flow{src}, $flow{src_port}, $flow{dst}, $flow{dst_port}, 143 | $flow{pkts}, $flow{bytes}; 144 | } 145 | } 146 | 147 | sub process_nf_v5($$) 148 | { 149 | my $sender = shift; 150 | my $pkt = shift; 151 | my %header; 152 | my %flow; 153 | my $sender_s; 154 | 155 | %header = qw(); 156 | 157 | $sender_s = inet_ntoa($sender) if $af == 4; 158 | $sender_s = inet_ntop(AF_INET6, $sender) if $af == 6; 159 | 160 | ($header{ver}, $header{flows}, $header{uptime}, $header{secs}, 161 | $header{nsecs}, $header{flow_seq}, ) = unpack("nnNNNN", $pkt); 162 | 163 | if (length($pkt) < (24 + (48 * $header{flows}))) { 164 | printf STDERR timestamp()." Short Netflow v.1 packet: %d < %d\n", 165 | length($pkt), 24 + (48 * $header{flows}); 166 | return; 167 | } 168 | 169 | printf timestamp() . " HEADER v.%u (%u flow%s) seq %u\n", $header{ver}, 170 | $header{flows}, $header{flows} == 1 ? "" : "s", $header{flow_seq}; 171 | 172 | for(my $i = 0; $i < $header{flows}; $i++) { 173 | my $off = 24 + (48 * $i); 174 | my $ptr = substr($pkt, $off, 52); 175 | 176 | %flow = qw(); 177 | 178 | (my $src1, my $src2, my $src3, my $src4, 179 | my $dst1, my $dst2, my $dst3, my $dst4, 180 | my $nxt1, my $nxt2, my $nxt3, my $nxt4, 181 | $flow{in_ndx}, $flow{out_ndx}, $flow{pkts}, $flow{bytes}, 182 | $flow{start}, $flow{finish}, $flow{src_port}, $flow{dst_port}, 183 | my $pad1, $flow{tcp_flags}, $flow{protocol}, $flow{tos}, 184 | $flow{src_as}, $flow{dst_as}, 185 | $flow{src_mask}, $flow{dst_mask}) = 186 | unpack("CCCCCCCCCCCCnnNNNNnnCCCCnnCC", $ptr); 187 | 188 | $flow{src} = sprintf "%u.%u.%u.%u", $src1, $src2, $src3, $src4; 189 | $flow{dst} = sprintf "%u.%u.%u.%u", $dst1, $dst2, $dst3, $dst4; 190 | $flow{nxt} = sprintf "%u.%u.%u.%u", $nxt1, $nxt2, $nxt3, $nxt4; 191 | 192 | printf timestamp() . " " . 193 | "from %s started %s finish %s proto %u %s:%u > %s:%u %u " . 194 | "packets %u octets\n", 195 | $sender_s, 196 | fuptime($flow{start}), fuptime($flow{finish}), 197 | $flow{protocol}, 198 | $flow{src}, $flow{src_port}, $flow{dst}, $flow{dst_port}, 199 | $flow{pkts}, $flow{bytes}; 200 | } 201 | } 202 | 203 | ############################################################################ 204 | 205 | # Commandline options 206 | my $debug = 0; 207 | my $af4 = 0; 208 | my $af6 = 0; 209 | my $port; 210 | 211 | # Long option Short option 212 | GetOptions( 'debug+' => \$debug, 'd+' => \$debug, 213 | '4+' => \$af4, 214 | '6+' => \$af6, 215 | 'port=i' => \$port, 'p=i' => \$port); 216 | 217 | # Unbuffer output 218 | $| = 1; 219 | 220 | die "The -4 and -6 are mutually exclusive\n" if $af4 && $af6; 221 | die "You must specify a port (collector.pl -p XXX).\n" unless $port; 222 | 223 | $af4 = $af = 4 if $af4 || (!$af4 && !$af6); 224 | $af6 = $af = 6 if $af6; 225 | 226 | # These modules aren't standard everywhere, so load them only if necessary 227 | 228 | # Main loop - receive and process a packet 229 | for (;;) { 230 | my $socket; 231 | my $from; 232 | my $payload; 233 | my $ver; 234 | my $failcount = 0; 235 | my $netflow; 236 | my $junk; 237 | my $sender; 238 | 239 | # Open the listening port if we haven't already 240 | $socket = do_listen($port, $af) unless defined $socket; 241 | 242 | # Fetch a packet 243 | $from = $socket->recv($payload, 8192, 0); 244 | 245 | ($junk, $sender) = unpack_sockaddr_in($from) if $af4; 246 | ($junk, $sender) = unpack_sockaddr_in6($from) if $af6; 247 | 248 | # Reopen listening socket on error 249 | if (!defined $from) { 250 | $socket->close; 251 | undef $socket; 252 | 253 | $failcount++; 254 | die "Couldn't recv: $!\n" if ($failcount > 5); 255 | next; # Socket will be reopened at start of loop 256 | } 257 | 258 | if (length($payload) < 16) { 259 | printf STDERR timestamp()." Short packet recevied: %d < 16\n", 260 | length($payload); 261 | next; 262 | } 263 | 264 | # The version is always the first 16 bits of the packet 265 | ($ver) = unpack("n", $payload); 266 | 267 | if ($ver == 1) { process_nf_v1($sender, $payload); } 268 | elsif ($ver == 5) { process_nf_v5($sender, $payload); } 269 | else { 270 | printf STDERR timestamp()." Unsupported netflow version %d\n", 271 | $ver; 272 | next; 273 | } 274 | 275 | undef $payload; 276 | next; 277 | } 278 | 279 | exit 0; 280 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002 Damien Miller. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef _SFD_COMMON_H 26 | #define _SFD_COMMON_H 27 | 28 | #include "config.h" 29 | 30 | #define _BSD_SOURCE /* Needed for BSD-style struct ip,tcp,udp on Linux */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | 64 | #if defined(HAVE_NET_BPF_H) 65 | #include 66 | #elif defined(HAVE_PCAP_BPF_H) 67 | #include 68 | #endif 69 | #if defined(HAVE_INTTYPES_H) 70 | #include 71 | #endif 72 | 73 | #if defined(HAVE_SYS_ENDIAN_H) 74 | #include 75 | #elif defined(HAVE_ENDIAN_H) 76 | #include 77 | #endif 78 | 79 | /* The name of the program */ 80 | #define PROGNAME "softflowd" 81 | 82 | /* The name of the program */ 83 | #define PROGVER "0.9.9" 84 | 85 | /* Default pidfile */ 86 | #define DEFAULT_PIDFILE "/var/run/" PROGNAME ".pid" 87 | 88 | /* Default control socket */ 89 | #define DEFAULT_CTLSOCK "/var/run/" PROGNAME ".ctl" 90 | 91 | #define RCSID(msg) \ 92 | static /**/const char *const flowd_rcsid[] = \ 93 | { (const char *)flowd_rcsid, "\100(#)" msg } \ 94 | 95 | #ifndef IP_OFFMASK 96 | # define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ 97 | #endif 98 | #ifndef IPV6_VERSION 99 | #define IPV6_VERSION 0x60 100 | #endif 101 | #ifndef IPV6_VERSION_MASK 102 | #define IPV6_VERSION_MASK 0xf0 103 | #endif 104 | #ifndef IPV6_FLOWINFO_MASK 105 | #define IPV6_FLOWINFO_MASK ntohl(0x0fffffff) 106 | #endif 107 | #ifndef IPV6_FLOWLABEL_MASK 108 | #define IPV6_FLOWLABEL_MASK ntohl(0x000fffff) 109 | #endif 110 | 111 | #ifndef _PATH_DEVNULL 112 | # define _PATH_DEVNULL "/dev/null" 113 | #endif 114 | 115 | #ifndef MIN 116 | # define MIN(a,b) (((a)<(b))?(a):(b)) 117 | #endif 118 | #ifndef MAX 119 | # define MAX(a,b) (((a)>(b))?(a):(b)) 120 | #endif 121 | #ifndef offsetof 122 | # define offsetof(type, member) ((size_t) &((type *)0)->member) 123 | #endif 124 | 125 | #if defined(__GNUC__) 126 | # ifndef __dead 127 | # define __dead __attribute__((__noreturn__)) 128 | # endif 129 | # ifndef __packed 130 | # define __packed __attribute__((__packed__)) 131 | # endif 132 | #endif 133 | 134 | #if !defined(HAVE_INT8_T) && defined(OUR_CFG_INT8_T) 135 | typedef OUR_CFG_INT8_T int8_t; 136 | #endif 137 | #if !defined(HAVE_INT16_T) && defined(OUR_CFG_INT16_T) 138 | typedef OUR_CFG_INT16_T int16_t; 139 | #endif 140 | #if !defined(HAVE_INT32_T) && defined(OUR_CFG_INT32_T) 141 | typedef OUR_CFG_INT32_T int32_t; 142 | #endif 143 | #if !defined(HAVE_INT64_T) && defined(OUR_CFG_INT64_T) 144 | typedef OUR_CFG_INT64_T int64_t; 145 | #endif 146 | #if !defined(HAVE_U_INT8_T) && defined(OUR_CFG_U_INT8_T) 147 | typedef OUR_CFG_U_INT8_T u_int8_t; 148 | #endif 149 | #if !defined(HAVE_U_INT16_T) && defined(OUR_CFG_U_INT16_T) 150 | typedef OUR_CFG_U_INT16_T u_int16_t; 151 | #endif 152 | #if !defined(HAVE_U_INT32_T) && defined(OUR_CFG_U_INT32_T) 153 | typedef OUR_CFG_U_INT32_T u_int32_t; 154 | #endif 155 | #if !defined(HAVE_U_INT64_T) && defined(OUR_CFG_U_INT64_T) 156 | typedef OUR_CFG_U_INT64_T u_int64_t; 157 | #endif 158 | 159 | #ifndef HAVE_STRLCPY 160 | size_t strlcpy(char *dst, const char *src, size_t siz); 161 | #endif 162 | #ifndef HAVE_STRLCAT 163 | size_t strlcat(char *dst, const char *src, size_t siz); 164 | #endif 165 | #ifndef HAVE_CLOSEFROM 166 | void closefrom(int lowfd); 167 | #endif 168 | 169 | #ifndef HAVE_STRUCT_IP6_EXT 170 | struct ip6_ext { 171 | u_int8_t ip6e_nxt; 172 | u_int8_t ip6e_len; 173 | } __packed; 174 | #endif 175 | 176 | 177 | /* following lines are copy from unistd.h in Linux for avoidance warnings in compilation */ 178 | #if defined(HAVE_SETRESGID) && !defined(_GNU_SOURCE) 179 | extern int setresgid (__uid_t __ruid, __uid_t __euid, __uid_t __suid); 180 | #endif 181 | #if defined(HAVE_SETRESUID) && !defined(_GNU_SOURCE) 182 | extern int setresuid (__uid_t __ruid, __uid_t __euid, __uid_t __suid); 183 | #endif 184 | 185 | #endif /* _SFD_COMMON_H */ 186 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004 Damien Miller 2 | # 3 | # Permission to use, copy, modify, and distribute this software for any 4 | # purpose with or without fee is hereby granted, provided that the above 5 | # copyright notice and this permission notice appear in all copies. 6 | # 7 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | AC_INIT 16 | AC_CONFIG_SRCDIR([softflowd.c]) 17 | 18 | AC_CONFIG_HEADER(config.h) 19 | AC_PROG_CC 20 | AC_PROG_INSTALL 21 | 22 | # Optional verbose warnings for gcc, see below 23 | WFLAGS="-Wall -Waggregate-return -Wcast-align -Wcast-qual" 24 | WFLAGS="$WFLAGS -Wmissing-declarations -Wmissing-prototypes" 25 | WFLAGS="$WFLAGS -Wno-conversion -Wpointer-arith -Wshadow" 26 | WFLAGS="$WFLAGS -Wuninitialized -Wcast-align -Wcast-qual" 27 | WFLAGS="$WFLAGS -Wformat=2 -Wformat-nonliteral -Wwrite-strings" 28 | 29 | # Process flag arguments early, so they are available for tests later 30 | AC_ARG_ENABLE(gcc-warnings, 31 | [ --enable-gcc-warnings Enable verbose warnings (only for gcc)], 32 | [ if test "x$enableval" = "xyes" ; then CFLAGS="$CFLAGS $WFLAGS"; fi ] 33 | ) 34 | AC_ARG_ENABLE(nf9-vlan, 35 | AC_HELP_STRING([--enable-nf9-vlan], 36 | [enable exporting VLAN (and ICMP_TYPE) field in NetFlow v9 (default is NO)]), 37 | AC_DEFINE([ENABLE_NF9_VLAN], 1, [enable exporting VLAN (and ICMP_TYPE) field in NetFlow v9])) 38 | 39 | AC_ARG_WITH(cflags, 40 | [ --with-cflags Specify additional compiler flags], 41 | [ if test "x$withval" != "xno" ; then CFLAGS="$CFLAGS $withval"; fi ] 42 | ) 43 | AC_ARG_WITH(cppflags, 44 | [ --with-cppflags Specify additional preprocessor flags] , 45 | [ if test "x$withval" != "xno"; then CPPFLAGS="$CPPFLAGS $withval"; fi ] 46 | ) 47 | AC_ARG_WITH(ldflags, 48 | [ --with-ldflags Specify additional linker flags], 49 | [ if test "x$withval" != "xno" ; then LDFLAGS="$LDFLAGS $withval"; fi ] 50 | ) 51 | AC_ARG_WITH(libs, 52 | [ --with-libs Specify additional libraries to link with], 53 | [ if test "x$withval" != "xno" ; then LIBS="$LIBS $withval"; fi ] 54 | ) 55 | AC_ARG_WITH(chrootdir, 56 | [ --with-chrootdir Specify chroot directory], 57 | [ AC_DEFINE_UNQUOTED([PRIVDROP_CHROOT_DIR], ["${withval}"], [privdrop chroot directory]) ] 58 | ) 59 | 60 | AC_DEFINE([_BSD_SOURCE], [], [Define BSD SOURCE for Linux]) 61 | AC_CHECK_HEADERS(net/bpf.h pcap.h pcap-bpf.h sys/endian.h endian.h) 62 | 63 | dnl AC_CHECK_HEADERS(netinet/in_systm.h netinet/tcp.h netinet/udp.h) 64 | dnl 65 | dnl # This ugliness is because of autoconf's stupid default include list 66 | dnl AC_CHECK_HEADERS([netinet/ip.h], 67 | dnl [AC_DEFINE([HAVE_HAVE_NETINET_IP_H], 1, [has netinet/ip.h])], [], 68 | dnl [ 69 | dnl #include 70 | dnl #include 71 | dnl #if HAVE_NETINET_IN_SYSTM_H 72 | dnl #include 73 | dnl #endif 74 | dnl ]) 75 | 76 | AC_CHECK_MEMBER([struct sockaddr.sa_len], 77 | [AC_DEFINE([SOCK_HAS_LEN], 1, [struct sockaddr contains length])], , 78 | [#include 79 | #include ]) 80 | 81 | AC_CHECK_MEMBER(struct ip6_ext.ip6e_nxt, 82 | [AC_DEFINE([HAVE_STRUCT_IP6_EXT], 1, [struct ip6_ext.ip6e_nxt exists])], 83 | [], 84 | [ 85 | #include 86 | #include 87 | #include 88 | #include 89 | ]) 90 | 91 | AC_SEARCH_LIBS(daemon, bsd) 92 | AC_SEARCH_LIBS(gethostbyname, nsl) 93 | AC_SEARCH_LIBS(socket, socket) 94 | AC_CHECK_LIB(pcap, pcap_open_live) 95 | 96 | AC_CHECK_FUNCS(closefrom daemon setresuid setreuid setresgid setgid strlcpy strlcat strsep) 97 | AC_CHECK_DECLS([htobe64, htonll]) 98 | 99 | AC_CHECK_TYPES([u_int64_t, int64_t, uint64_t, u_int32_t, int32_t, uint32_t]) 100 | AC_CHECK_TYPES([u_int16_t, int16_t, uint16_t, u_int8_t, int8_t, uint8_t]) 101 | AC_CHECK_SIZEOF(char, 1) 102 | AC_CHECK_SIZEOF(short int, 2) 103 | AC_CHECK_SIZEOF(int, 4) 104 | AC_CHECK_SIZEOF(long int, 4) 105 | AC_CHECK_SIZEOF(long long int, 8) 106 | 107 | if test "x$ac_cv_type_uint8_t" = "xyes" ; then 108 | AC_DEFINE([OUR_CFG_U_INT8_T], [uint8_t], [8-bit unsigned int]) 109 | elif test "x$ac_cv_sizeof_char" = "x1" ; then 110 | AC_DEFINE([OUR_CFG_U_INT8_T], [unsigned char], [8-bit unsigned int]) 111 | else 112 | AC_MSG_ERROR([No 8-bit unsigned int type found]) 113 | fi 114 | if test "x$ac_cv_sizeof_char" = "x1" ; then 115 | AC_DEFINE([OUR_CFG_INT8_T], [signed char], [8-bit signed int]) 116 | else 117 | AC_MSG_ERROR([No 8-bit signed int type found]) 118 | fi 119 | 120 | if test "x$ac_cv_type_uint16_t" = "xyes" ; then 121 | AC_DEFINE([OUR_CFG_U_INT16_T], [uint16_t], [16-bit unsigned int]) 122 | elif test "x$ac_cv_sizeof_short_int" = "x2" ; then 123 | AC_DEFINE([OUR_CFG_U_INT16_T], [unsigned short int], [16-bit unsigned int]) 124 | else 125 | AC_MSG_ERROR([No 16-bit unsigned int type found]) 126 | fi 127 | if test "x$ac_cv_sizeof_short_int" = "x2" ; then 128 | AC_DEFINE([OUR_CFG_INT16_T], [short int], [16-bit signed int]) 129 | else 130 | AC_MSG_ERROR([No 16-bit signed int type found]) 131 | fi 132 | 133 | if test "x$ac_cv_type_uint32_t" = "xyes" ; then 134 | AC_DEFINE([OUR_CFG_U_INT32_T], [uint32_t], [32-bit unsigned int]) 135 | elif test "x$ac_cv_sizeof_int" = "x4" ; then 136 | AC_DEFINE([OUR_CFG_U_INT32_T], [unsigned int], [32-bit unsigned int]) 137 | else 138 | AC_MSG_ERROR([No 32-bit unsigned int type found]) 139 | fi 140 | if test "x$ac_cv_sizeof_int" = "x4" ; then 141 | AC_DEFINE([OUR_CFG_INT32_T], [int], [32-bit signed int]) 142 | else 143 | AC_MSG_ERROR([No 32-bit signed int type found]) 144 | fi 145 | 146 | if test "x$ac_cv_type_uint64_t" = "xyes" ; then 147 | AC_DEFINE([OUR_CFG_U_INT64_T], [uint64_t], [64-bit unsigned int]) 148 | elif test "x$ac_cv_sizeof_long_int" = "x8" ; then 149 | AC_DEFINE([OUR_CFG_U_INT64_T], [unsigned long int], [64-bit unsigned int]) 150 | elif test "x$ac_cv_sizeof_long_long_int" = "x8" ; then 151 | AC_DEFINE([OUR_CFG_U_INT64_T], [unsigned long long int], [64-bit unsigned int]) 152 | else 153 | AC_MSG_ERROR([No 64-bit unsigned int type found]) 154 | fi 155 | if test "x$ac_cv_sizeof_long_int" = "x8" ; then 156 | AC_DEFINE([OUR_CFG_INT64_T], [long int], [64-bit signed int]) 157 | elif test "x$ac_cv_sizeof_long_long_int" = "x8" ; then 158 | AC_DEFINE([OUR_CFG_INT64_T], [long long int], [64-bit signed int]) 159 | else 160 | AC_MSG_ERROR([No 64-bit signed int type found]) 161 | fi 162 | 163 | if test "x$ac_cv_header_pcap_bpf_h" != "xyes" && \ 164 | test "x$ac_cv_header_net_bpf_h" != "xyes" ; then 165 | AC_MSG_ERROR([No BPF header found]) 166 | fi 167 | if test "x$ac_cv_header_pcap_h" != "xyes" ; then 168 | AC_MSG_ERROR([No pcap.h header found]) 169 | fi 170 | if test "x$ac_cv_lib_pcap_pcap_open_live" != "xyes" ; then 171 | AC_MSG_ERROR([libpcap not found]) 172 | fi 173 | 174 | 175 | 176 | AC_EXEEXT 177 | AC_CONFIG_FILES([Makefile]) 178 | AC_OUTPUT 179 | 180 | -------------------------------------------------------------------------------- /convtime.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001 Kevin Steves. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include "common.h" 26 | #include "convtime.h" 27 | 28 | #define SECONDS 1 29 | #define MINUTES (SECONDS * 60) 30 | #define HOURS (MINUTES * 60) 31 | #define DAYS (HOURS * 24) 32 | #define WEEKS (DAYS * 7) 33 | 34 | long int 35 | convtime(const char *s) 36 | { 37 | long total, secs; 38 | const char *p; 39 | char *endp; 40 | 41 | errno = 0; 42 | total = 0; 43 | p = s; 44 | 45 | if (p == NULL || *p == '\0') 46 | return -1; 47 | 48 | while (*p) { 49 | secs = strtol(p, &endp, 10); 50 | if (p == endp || 51 | (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || 52 | secs < 0) 53 | return -1; 54 | 55 | switch (*endp++) { 56 | case '\0': 57 | endp--; 58 | case 's': 59 | case 'S': 60 | break; 61 | case 'm': 62 | case 'M': 63 | secs *= MINUTES; 64 | break; 65 | case 'h': 66 | case 'H': 67 | secs *= HOURS; 68 | break; 69 | case 'd': 70 | case 'D': 71 | secs *= DAYS; 72 | break; 73 | case 'w': 74 | case 'W': 75 | secs *= WEEKS; 76 | break; 77 | default: 78 | return -1; 79 | } 80 | total += secs; 81 | if (total < 0) 82 | return -1; 83 | p = endp; 84 | } 85 | 86 | return total; 87 | } 88 | -------------------------------------------------------------------------------- /convtime.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001 Kevin Steves. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef _SFD_CONVTIME_H 26 | 27 | /* 28 | * Convert a time string into seconds; format is 29 | * a sequence of: 30 | * time[qualifier] 31 | * 32 | * Valid time qualifiers are: 33 | * seconds 34 | * s|S seconds 35 | * m|M minutes 36 | * h|H hours 37 | * d|D days 38 | * w|W weeks 39 | * 40 | * Examples: 41 | * 90m 90 minutes 42 | * 1h30m 90 minutes 43 | * 2d 2 days 44 | * 1w 1 week 45 | * 46 | * Return -1 if time string is invalid. 47 | */ 48 | long int convtime(const char *s); 49 | 50 | #endif /* _SFD_CONVTIME_H */ 51 | -------------------------------------------------------------------------------- /daemon.c: -------------------------------------------------------------------------------- 1 | /* OPENBSD ORIGINAL: lib/libc/gen/daemon.c */ 2 | 3 | /*- 4 | * Copyright (c) 1990, 1993 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | */ 31 | 32 | #include "common.h" 33 | 34 | #ifndef HAVE_DAEMON 35 | 36 | #if defined(LIBC_SCCS) && !defined(lint) 37 | static char rcsid[] = "$OpenBSD: daemon.c,v 1.5 2003/07/15 17:32:41 deraadt Exp $"; 38 | #endif /* LIBC_SCCS and not lint */ 39 | 40 | int 41 | daemon(int nochdir, int noclose) 42 | { 43 | int fd; 44 | 45 | switch (fork()) { 46 | case -1: 47 | return (-1); 48 | case 0: 49 | #ifdef HAVE_CYGWIN 50 | register_9x_service(); 51 | #endif 52 | break; 53 | default: 54 | #ifdef HAVE_CYGWIN 55 | /* 56 | * This sleep avoids a race condition which kills the 57 | * child process if parent is started by a NT/W2K service. 58 | */ 59 | sleep(1); 60 | #endif 61 | _exit(0); 62 | } 63 | 64 | if (setsid() == -1) 65 | return (-1); 66 | 67 | if (!nochdir) 68 | (void)chdir("/"); 69 | 70 | if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { 71 | (void)dup2(fd, STDIN_FILENO); 72 | (void)dup2(fd, STDOUT_FILENO); 73 | (void)dup2(fd, STDERR_FILENO); 74 | if (fd > 2) 75 | (void)close (fd); 76 | } 77 | return (0); 78 | } 79 | 80 | #endif /* !HAVE_DAEMON */ 81 | 82 | -------------------------------------------------------------------------------- /freelist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007 Damien Miller. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include "common.h" 26 | #include "freelist.h" 27 | #include "log.h" 28 | 29 | #define FREELIST_MAX_ALLOC 0x1000000 30 | #define FREELIST_ALLOC_ALIGN 16 31 | #define FREELIST_INITIAL_ALLOC 16 32 | 33 | #ifndef roundup 34 | #define roundup(x, y) ((((x) + (y) - 1)/(y))*(y)) 35 | #endif /* roundup */ 36 | 37 | #undef FREELIST_DEBUG 38 | #ifdef FREELIST_DEBUG 39 | # define FLOGIT(a) logit a 40 | #else 41 | # define FLOGIT(a) 42 | #endif 43 | 44 | void 45 | freelist_init(struct freelist *fl, size_t allocsz) 46 | { 47 | size_t sizeof_fl = sizeof(fl); 48 | FLOGIT((LOG_DEBUG, "%s: %s(%p, %zu)", __func__, __func__, fl, allocsz)); 49 | bzero(fl, sizeof_fl); 50 | fl->allocsz = roundup(allocsz, FREELIST_ALLOC_ALIGN); 51 | fl->free_entries = NULL; 52 | } 53 | 54 | static int 55 | freelist_grow(struct freelist *fl) 56 | { 57 | size_t i, oldnalloc, need; 58 | void *p; 59 | 60 | FLOGIT((LOG_DEBUG, "%s: %s(%p)", __func__, __func__, fl)); 61 | FLOGIT((LOG_DEBUG, "%s: nalloc = %zu", __func__, fl->nalloc)); 62 | 63 | /* Sanity check */ 64 | if (fl->nalloc > FREELIST_MAX_ALLOC) 65 | return -1; 66 | 67 | oldnalloc = fl->nalloc; 68 | if (fl->nalloc == 0) 69 | fl->nalloc = FREELIST_INITIAL_ALLOC; 70 | else 71 | fl->nalloc <<= 1; 72 | if (fl->nalloc > FREELIST_MAX_ALLOC) 73 | fl->nalloc = FREELIST_MAX_ALLOC; 74 | FLOGIT((LOG_DEBUG, "%s: nalloc now %zu", __func__, fl->nalloc)); 75 | 76 | /* Check for integer overflow */ 77 | if (SIZE_MAX / fl->nalloc < fl->allocsz || 78 | SIZE_MAX / fl->nalloc < sizeof(*fl->free_entries)) { 79 | FLOGIT((LOG_DEBUG, "%s: integer overflow", __func__)); 80 | resize_fail: 81 | fl->nalloc = oldnalloc; 82 | return -1; 83 | } 84 | 85 | /* Allocate freelist - max size of nalloc */ 86 | need = fl->nalloc * sizeof(*fl->free_entries); 87 | if ((p = realloc(fl->free_entries, need)) == NULL) { 88 | FLOGIT((LOG_DEBUG, "%s: realloc(%zu) failed", __func__, need)); 89 | goto resize_fail; 90 | } 91 | 92 | /* Allocate the entries */ 93 | fl->free_entries = p; 94 | need = (fl->nalloc - oldnalloc) * fl->allocsz; 95 | if ((p = malloc(need)) == NULL) { 96 | FLOGIT((LOG_DEBUG, "%s: malloc(%zu) failed", __func__, need)); 97 | goto resize_fail; 98 | } 99 | /* 100 | * XXX store these malloc ranges in a tree or list, so we can 101 | * validate them in _get/_put. Check that r_low <= addr < r_high, and 102 | * (addr - r_low) % fl->allocsz == 0 103 | */ 104 | 105 | fl->navail = fl->nalloc - oldnalloc; 106 | for (i = 0; i < fl->navail; i++) 107 | fl->free_entries[i] = (u_char *)p + (i * fl->allocsz); 108 | for (i = fl->navail; i < fl->nalloc; i++) 109 | fl->free_entries[i] = NULL; 110 | 111 | FLOGIT((LOG_DEBUG, "%s: done, navail = %zu", __func__, fl->navail)); 112 | return 0; 113 | } 114 | 115 | void * 116 | freelist_get(struct freelist *fl) 117 | { 118 | void *r; 119 | 120 | FLOGIT((LOG_DEBUG, "%s: %s(%p)", __func__, __func__, fl)); 121 | FLOGIT((LOG_DEBUG, "%s: navail = %zu", __func__, fl->navail)); 122 | 123 | if (fl->navail == 0) { 124 | if (freelist_grow(fl) == -1) 125 | return NULL; 126 | } 127 | 128 | /* Sanity check */ 129 | if (fl->navail == 0 || fl->navail > FREELIST_MAX_ALLOC || 130 | fl->free_entries[fl->navail - 1] == NULL) { 131 | logit(LOG_ERR, "%s: invalid navail", __func__); 132 | raise(SIGSEGV); 133 | } 134 | 135 | fl->navail--; 136 | r = fl->free_entries[fl->navail]; 137 | fl->free_entries[fl->navail] = NULL; 138 | 139 | FLOGIT((LOG_DEBUG, "%s: done, navail = %zu", __func__, fl->navail)); 140 | return r; 141 | } 142 | 143 | void 144 | freelist_put(struct freelist *fl, void *p) 145 | { 146 | FLOGIT((LOG_DEBUG, "%s: %s(%p, %zu)", __func__, __func__, fl, p)); 147 | FLOGIT((LOG_DEBUG, "%s: navail = %zu", __func__, fl->navail)); 148 | FLOGIT((LOG_DEBUG, "%s: nalloc = %zu", __func__, fl->navail)); 149 | 150 | /* Sanity check */ 151 | if (fl->navail >= fl->nalloc) { 152 | logit(LOG_ERR, "%s: freelist navail >= nalloc", __func__); 153 | raise(SIGSEGV); 154 | } 155 | if (fl->free_entries[fl->navail] != NULL) { 156 | logit(LOG_ERR, "%s: free_entries[%lu] != NULL", 157 | __func__, (unsigned long)fl->navail); 158 | raise(SIGSEGV); 159 | } 160 | 161 | fl->free_entries[fl->navail] = p; 162 | fl->navail++; 163 | 164 | FLOGIT((LOG_DEBUG, "%s: done, navail = %zu", __func__, fl->navail)); 165 | } 166 | 167 | 168 | -------------------------------------------------------------------------------- /freelist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2007 Damien Miller. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef _FREELIST_H 26 | #define _FREELIST_H 27 | 28 | #include "common.h" 29 | 30 | /* Simple freelist of fixed-sized allocations */ 31 | struct freelist { 32 | size_t allocsz; 33 | size_t nalloc; 34 | size_t navail; 35 | void **free_entries; 36 | }; 37 | 38 | /* 39 | * Initialise a freelist. 40 | * allocsz is the size of the individual allocations 41 | */ 42 | void freelist_init(struct freelist *freelist, size_t allocsz); 43 | 44 | /* 45 | * Get an entry from a freelist. 46 | * Will allocate new entries if necessary 47 | * Returns pointer to allocated memory or NULL on failure. 48 | */ 49 | void *freelist_get(struct freelist *freelist); 50 | 51 | /* 52 | * Returns an entry to the freelist. 53 | * p must be a pointer to an allocation from the freelist. 54 | */ 55 | void freelist_put(struct freelist *freelist, void *p); 56 | 57 | #endif /* FREELIST_H */ 58 | 59 | -------------------------------------------------------------------------------- /install-sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # install - install a program, script, or datafile 4 | # This comes from X11R5 (mit/util/scripts/install.sh). 5 | # 6 | # Copyright 1991 by the Massachusetts Institute of Technology 7 | # 8 | # Permission to use, copy, modify, distribute, and sell this software and its 9 | # documentation for any purpose is hereby granted without fee, provided that 10 | # the above copyright notice appear in all copies and that both that 11 | # copyright notice and this permission notice appear in supporting 12 | # documentation, and that the name of M.I.T. not be used in advertising or 13 | # publicity pertaining to distribution of the software without specific, 14 | # written prior permission. M.I.T. makes no representations about the 15 | # suitability of this software for any purpose. It is provided "as is" 16 | # without express or implied warranty. 17 | # 18 | # Calling this script install-sh is preferred over install.sh, to prevent 19 | # `make' implicit rules from creating a file called install from it 20 | # when there is no Makefile. 21 | # 22 | # This script is compatible with the BSD install script, but was written 23 | # from scratch. It can only install one file at a time, a restriction 24 | # shared with many OS's install programs. 25 | 26 | 27 | # set DOITPROG to echo to test this script 28 | 29 | # Don't use :- since 4.3BSD and earlier shells don't like it. 30 | doit="${DOITPROG-}" 31 | 32 | 33 | # put in absolute paths if you don't have them in your path; or use env. vars. 34 | 35 | mvprog="${MVPROG-mv}" 36 | cpprog="${CPPROG-cp}" 37 | chmodprog="${CHMODPROG-chmod}" 38 | chownprog="${CHOWNPROG-chown}" 39 | chgrpprog="${CHGRPPROG-chgrp}" 40 | stripprog="${STRIPPROG-strip}" 41 | rmprog="${RMPROG-rm}" 42 | mkdirprog="${MKDIRPROG-mkdir}" 43 | 44 | transformbasename="" 45 | transform_arg="" 46 | instcmd="$mvprog" 47 | chmodcmd="$chmodprog 0755" 48 | chowncmd="" 49 | chgrpcmd="" 50 | stripcmd="" 51 | rmcmd="$rmprog -f" 52 | mvcmd="$mvprog" 53 | src="" 54 | dst="" 55 | dir_arg="" 56 | 57 | while [ x"$1" != x ]; do 58 | case $1 in 59 | -c) instcmd="$cpprog" 60 | shift 61 | continue;; 62 | 63 | -d) dir_arg=true 64 | shift 65 | continue;; 66 | 67 | -m) chmodcmd="$chmodprog $2" 68 | shift 69 | shift 70 | continue;; 71 | 72 | -o) chowncmd="$chownprog $2" 73 | shift 74 | shift 75 | continue;; 76 | 77 | -g) chgrpcmd="$chgrpprog $2" 78 | shift 79 | shift 80 | continue;; 81 | 82 | -s) stripcmd="$stripprog" 83 | shift 84 | continue;; 85 | 86 | -t=*) transformarg=`echo $1 | sed 's/-t=//'` 87 | shift 88 | continue;; 89 | 90 | -b=*) transformbasename=`echo $1 | sed 's/-b=//'` 91 | shift 92 | continue;; 93 | 94 | *) if [ x"$src" = x ] 95 | then 96 | src=$1 97 | else 98 | # this colon is to work around a 386BSD /bin/sh bug 99 | : 100 | dst=$1 101 | fi 102 | shift 103 | continue;; 104 | esac 105 | done 106 | 107 | if [ x"$src" = x ] 108 | then 109 | echo "install: no input file specified" 110 | exit 1 111 | else 112 | true 113 | fi 114 | 115 | if [ x"$dir_arg" != x ]; then 116 | dst=$src 117 | src="" 118 | 119 | if [ -d $dst ]; then 120 | instcmd=: 121 | chmodcmd="" 122 | else 123 | instcmd=mkdir 124 | fi 125 | else 126 | 127 | # Waiting for this to be detected by the "$instcmd $src $dsttmp" command 128 | # might cause directories to be created, which would be especially bad 129 | # if $src (and thus $dsttmp) contains '*'. 130 | 131 | if [ -f $src -o -d $src ] 132 | then 133 | true 134 | else 135 | echo "install: $src does not exist" 136 | exit 1 137 | fi 138 | 139 | if [ x"$dst" = x ] 140 | then 141 | echo "install: no destination specified" 142 | exit 1 143 | else 144 | true 145 | fi 146 | 147 | # If destination is a directory, append the input filename; if your system 148 | # does not like double slashes in filenames, you may need to add some logic 149 | 150 | if [ -d $dst ] 151 | then 152 | dst="$dst"/`basename $src` 153 | else 154 | true 155 | fi 156 | fi 157 | 158 | ## this sed command emulates the dirname command 159 | dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` 160 | 161 | # Make sure that the destination directory exists. 162 | # this part is taken from Noah Friedman's mkinstalldirs script 163 | 164 | # Skip lots of stat calls in the usual case. 165 | if [ ! -d "$dstdir" ]; then 166 | defaultIFS=' 167 | ' 168 | IFS="${IFS-${defaultIFS}}" 169 | 170 | oIFS="${IFS}" 171 | # Some sh's can't handle IFS=/ for some reason. 172 | IFS='%' 173 | set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` 174 | IFS="${oIFS}" 175 | 176 | pathcomp='' 177 | 178 | while [ $# -ne 0 ] ; do 179 | pathcomp="${pathcomp}${1}" 180 | shift 181 | 182 | if [ ! -d "${pathcomp}" ] ; 183 | then 184 | $mkdirprog "${pathcomp}" 185 | else 186 | true 187 | fi 188 | 189 | pathcomp="${pathcomp}/" 190 | done 191 | fi 192 | 193 | if [ x"$dir_arg" != x ] 194 | then 195 | $doit $instcmd $dst && 196 | 197 | if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && 198 | if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && 199 | if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && 200 | if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi 201 | else 202 | 203 | # If we're going to rename the final executable, determine the name now. 204 | 205 | if [ x"$transformarg" = x ] 206 | then 207 | dstfile=`basename $dst` 208 | else 209 | dstfile=`basename $dst $transformbasename | 210 | sed $transformarg`$transformbasename 211 | fi 212 | 213 | # don't allow the sed command to completely eliminate the filename 214 | 215 | if [ x"$dstfile" = x ] 216 | then 217 | dstfile=`basename $dst` 218 | else 219 | true 220 | fi 221 | 222 | # Make a temp file name in the proper directory. 223 | 224 | dsttmp=$dstdir/#inst.$$# 225 | 226 | # Move or copy the file name to the temp name 227 | 228 | $doit $instcmd $src $dsttmp && 229 | 230 | trap "rm -f ${dsttmp}" 0 && 231 | 232 | # and set any options; do chmod last to preserve setuid bits 233 | 234 | # If any of these fail, we abort the whole thing. If we want to 235 | # ignore errors from any of these, just make sure not to ignore 236 | # errors from the above "$doit $instcmd $src $dsttmp" command. 237 | 238 | if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && 239 | if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && 240 | if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && 241 | if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && 242 | 243 | # Now rename the file to the real destination. 244 | 245 | $doit $rmcmd -f $dstdir/$dstfile && 246 | $doit $mvcmd $dsttmp $dstdir/$dstfile 247 | 248 | fi && 249 | 250 | 251 | exit 0 252 | -------------------------------------------------------------------------------- /ipfix.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002 Damien Miller All rights reserved. 3 | * Copyright 2012 Hitoshi Irino All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #include "common.h" 27 | #include "log.h" 28 | #include "treetype.h" 29 | #include "softflowd.h" 30 | 31 | #if defined (HAVE_DECL_HTONLL) && !defined (HAVE_DECL_HTOBE64) 32 | #define htobe64 htonll 33 | #endif 34 | #define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ 35 | 36 | /* IPFIX a.k.a. Netflow v.10 */ 37 | struct IPFIX_HEADER { 38 | u_int16_t version, length; 39 | u_int32_t export_time; /* in seconds */ 40 | u_int32_t sequence, od_id; 41 | } __packed; 42 | struct IPFIX_SET_HEADER { 43 | u_int16_t set_id, length; 44 | } __packed; 45 | struct IPFIX_TEMPLATE_RECORD_HEADER { 46 | u_int16_t template_id, count; 47 | } __packed; 48 | struct IPFIX_TEMPLATE_SET_HEADER { 49 | struct IPFIX_SET_HEADER c; 50 | struct IPFIX_TEMPLATE_RECORD_HEADER r; 51 | } __packed; 52 | struct IPFIX_OPTION_TEMPLATE_SET_HEADER { 53 | struct IPFIX_SET_HEADER c; 54 | struct IPFIX_TEMPLATE_RECORD_HEADER r; 55 | u_int16_t scope_count; 56 | } __packed; 57 | struct IPFIX_FIELD_SPECIFIER { 58 | u_int16_t ie, length; 59 | } __packed; 60 | struct IPFIX_VENDOR_FIELD_SPECIFIER { 61 | u_int16_t ie, length; 62 | u_int32_t pen; 63 | } __packed; 64 | #define REVERSE_PEN 29305 65 | 66 | #define IPFIX_TEMPLATE_SET_ID 2 67 | #define IPFIX_OPTION_TEMPLATE_SET_ID 3 68 | #define IPFIX_MIN_RECORD_SET_ID 256 69 | 70 | /* Flowset record ies the we care about */ 71 | #define IPFIX_octetDeltaCount 1 72 | #define IPFIX_packetDeltaCount 2 73 | /* ... */ 74 | #define IPFIX_protocolIdentifier 4 75 | #define IPFIX_ipClassOfService 5 76 | /* ... */ 77 | #define IPFIX_tcpControlBits 6 78 | #define IPFIX_sourceTransportPort 7 79 | #define IPFIX_sourceIPv4Address 8 80 | /* ... */ 81 | #define IPFIX_ingressInterface 10 82 | #define IPFIX_destinationTransportPort 11 83 | #define IPFIX_destinationIPv4Address 12 84 | /* ... */ 85 | #define IPFIX_egressInterface 14 86 | /* ... */ 87 | #define IPFIX_flowEndSysUpTime 21 88 | #define IPFIX_flowStartSysUpTime 22 89 | /* ... */ 90 | #define IPFIX_sourceIPv6Address 27 91 | #define IPFIX_destinationIPv6Address 28 92 | /* ... */ 93 | #define IPFIX_icmpTypeCodeIPv4 32 94 | /* ... */ 95 | /* ... */ 96 | #define IPFIX_vlanId 58 97 | 98 | #define IPFIX_ipVersion 60 99 | /* ... */ 100 | #define IPFIX_icmpTypeCodeIPv6 139 101 | /* ... */ 102 | #define IPFIX_meteringProcessId 143 103 | /* ... */ 104 | #define IPFIX_flowStartSeconds 150 105 | #define IPFIX_flowEndSeconds 151 106 | #define IPFIX_flowStartMilliSeconds 152 107 | #define IPFIX_flowEndMilliSeconds 153 108 | #define IPFIX_flowStartMicroSeconds 154 109 | #define IPFIX_flowEndMicroSeconds 155 110 | #define IPFIX_flowStartNanoSeconds 156 111 | #define IPFIX_flowEndNanoSeconds 157 112 | /* ... */ 113 | #define IPFIX_systemInitTimeMilliseconds 160 114 | /* ... */ 115 | #define PSAMP_selectorAlgorithm 304 116 | #define PSAMP_samplingPacketInterval 305 117 | #define PSAMP_samplingPacketSpace 306 118 | 119 | #define PSAMP_selectorAlgorithm_count 1 120 | 121 | /* Stuff pertaining to the templates that softflowd uses */ 122 | #define IPFIX_SOFTFLOWD_TEMPLATE_COMMONRECORDS 14 123 | #define IPFIX_SOFTFLOWD_TEMPLATE_TIMERECORDS 2 124 | #define IPFIX_SOFTFLOWD_TEMPLATE_VENDORRECORDS 5 125 | 126 | #define IPFIX_SOFTFLOWD_TEMPLATE_NRECORDS \ 127 | IPFIX_SOFTFLOWD_TEMPLATE_COMMONRECORDS + \ 128 | IPFIX_SOFTFLOWD_TEMPLATE_TIMERECORDS 129 | 130 | #define IPFIX_SOFTFLOWD_TEMPLATE_BIDIRECTION_NRECORDS \ 131 | IPFIX_SOFTFLOWD_TEMPLATE_COMMONRECORDS + \ 132 | IPFIX_SOFTFLOWD_TEMPLATE_VENDORRECORDS + \ 133 | IPFIX_SOFTFLOWD_TEMPLATE_TIMERECORDS 134 | 135 | struct IPFIX_SOFTFLOWD_TEMPLATE { 136 | struct IPFIX_TEMPLATE_SET_HEADER h; 137 | struct IPFIX_FIELD_SPECIFIER r[IPFIX_SOFTFLOWD_TEMPLATE_NRECORDS]; 138 | } __packed; 139 | 140 | struct IPFIX_SOFTFLOWD_BIDIRECTION_TEMPLATE { 141 | struct IPFIX_TEMPLATE_SET_HEADER h; 142 | struct IPFIX_FIELD_SPECIFIER r[IPFIX_SOFTFLOWD_TEMPLATE_COMMONRECORDS]; 143 | struct IPFIX_VENDOR_FIELD_SPECIFIER v[IPFIX_SOFTFLOWD_TEMPLATE_VENDORRECORDS]; 144 | struct IPFIX_FIELD_SPECIFIER t[IPFIX_SOFTFLOWD_TEMPLATE_TIMERECORDS]; 145 | } __packed; 146 | 147 | #define IPFIX_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS 1 148 | #define IPFIX_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS 4 149 | struct IPFIX_SOFTFLOWD_OPTION_TEMPLATE { 150 | struct IPFIX_OPTION_TEMPLATE_SET_HEADER h; 151 | struct IPFIX_FIELD_SPECIFIER s[IPFIX_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS]; 152 | struct IPFIX_FIELD_SPECIFIER r[IPFIX_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS]; 153 | } __packed; 154 | 155 | /* softflowd data set */ 156 | struct IPFIX_SOFTFLOWD_DATA_COMMON { 157 | u_int32_t octetDeltaCount, packetDeltaCount; 158 | u_int32_t ingressInterface, egressInterface; 159 | u_int16_t sourceTransportPort, destinationTransportPort; 160 | u_int8_t protocolIdentifier, tcpControlBits, ipVersion, ipClassOfService; 161 | u_int16_t icmpTypeCode, vlanId; 162 | } __packed; 163 | 164 | struct IPFIX_SOFTFLOWD_DATA_BIDIRECTION { 165 | u_int32_t octetDeltaCount, packetDeltaCount; 166 | u_int8_t tcpControlBits, ipClassOfService; 167 | u_int16_t icmpTypeCode; 168 | } __packed; 169 | 170 | union IPFIX_SOFTFLOWD_DATA_TIME { 171 | struct { 172 | u_int32_t start; 173 | u_int32_t end; 174 | } u32; 175 | struct { 176 | u_int64_t start; 177 | u_int64_t end; 178 | } u64; 179 | }; 180 | 181 | struct IPFIX_SOFTFLOWD_DATA_V4 { 182 | u_int32_t sourceIPv4Address, destinationIPv4Address; 183 | struct IPFIX_SOFTFLOWD_DATA_COMMON c; 184 | union IPFIX_SOFTFLOWD_DATA_TIME t; 185 | } __packed; 186 | 187 | struct IPFIX_SOFTFLOWD_BIDIRECTION_DATA_V4 { 188 | u_int32_t sourceIPv4Address, destinationIPv4Address; 189 | struct IPFIX_SOFTFLOWD_DATA_COMMON c; 190 | struct IPFIX_SOFTFLOWD_DATA_BIDIRECTION b; 191 | union IPFIX_SOFTFLOWD_DATA_TIME t; 192 | } __packed; 193 | 194 | struct IPFIX_SOFTFLOWD_DATA_V6 { 195 | struct in6_addr sourceIPv6Address, destinationIPv6Address; 196 | struct IPFIX_SOFTFLOWD_DATA_COMMON c; 197 | union IPFIX_SOFTFLOWD_DATA_TIME t; 198 | } __packed; 199 | 200 | struct IPFIX_SOFTFLOWD_BIDIRECTION_DATA_V6 { 201 | struct in6_addr sourceIPv6Address, destinationIPv6Address; 202 | struct IPFIX_SOFTFLOWD_DATA_COMMON c; 203 | struct IPFIX_SOFTFLOWD_DATA_BIDIRECTION b; 204 | union IPFIX_SOFTFLOWD_DATA_TIME t; 205 | } __packed; 206 | 207 | struct IPFIX_SOFTFLOWD_OPTION_DATA { 208 | struct IPFIX_SET_HEADER c; 209 | u_int32_t scope_pid; 210 | u_int64_t systemInitTimeMilliseconds; 211 | u_int16_t samplingAlgorithm; 212 | u_int16_t samplingInterval; 213 | u_int32_t samplingSpace; 214 | } __packed; 215 | 216 | /* Local data: templates and counters */ 217 | #define IPFIX_SOFTFLOWD_MAX_PACKET_SIZE 512 218 | #define IPFIX_SOFTFLOWD_V4_TEMPLATE_ID 1024 219 | #define IPFIX_SOFTFLOWD_V6_TEMPLATE_ID 2048 220 | #define IPFIX_SOFTFLOWD_OPTION_TEMPLATE_ID 256 221 | 222 | #define IPFIX_DEFAULT_TEMPLATE_INTERVAL 16 223 | 224 | /* ... */ 225 | #define IPFIX_OPTION_SCOPE_SYSTEM 1 226 | #define IPFIX_OPTION_SCOPE_INTERFACE 2 227 | #define IPFIX_OPTION_SCOPE_LINECARD 3 228 | #define IPFIX_OPTION_SCOPE_CACHE 4 229 | #define IPFIX_OPTION_SCOPE_TEMPLATE 5 230 | /* ... */ 231 | #define IPFIX_SAMPLING_ALGORITHM_DETERMINISTIC 1 232 | #define IPFIX_SAMPLING_ALGORITHM_RANDOM 2 233 | /* ... */ 234 | 235 | static struct IPFIX_SOFTFLOWD_TEMPLATE v4_template; 236 | static struct IPFIX_SOFTFLOWD_TEMPLATE v6_template; 237 | static struct IPFIX_SOFTFLOWD_BIDIRECTION_TEMPLATE v4_bidirection_template; 238 | static struct IPFIX_SOFTFLOWD_BIDIRECTION_TEMPLATE v6_bidirection_template; 239 | static struct IPFIX_SOFTFLOWD_OPTION_TEMPLATE option_template; 240 | static struct IPFIX_SOFTFLOWD_OPTION_DATA option_data; 241 | static int ipfix_pkts_until_template = -1; 242 | 243 | static void 244 | ipfix_init_template(struct FLOWTRACKPARAMETERS *param) 245 | { 246 | bzero(&v4_template, sizeof(v4_template)); 247 | v4_template.h.c.set_id = htons(IPFIX_TEMPLATE_SET_ID); 248 | v4_template.h.c.length = htons(sizeof(v4_template)); 249 | v4_template.h.r.template_id = htons(IPFIX_SOFTFLOWD_V4_TEMPLATE_ID); 250 | v4_template.h.r.count = htons(IPFIX_SOFTFLOWD_TEMPLATE_NRECORDS); 251 | v4_template.r[0].ie = htons(IPFIX_sourceIPv4Address); 252 | v4_template.r[0].length = htons(4); 253 | v4_template.r[1].ie = htons(IPFIX_destinationIPv4Address); 254 | v4_template.r[1].length = htons(4); 255 | v4_template.r[2].ie = htons(IPFIX_octetDeltaCount); 256 | v4_template.r[2].length = htons(4); 257 | v4_template.r[3].ie = htons(IPFIX_packetDeltaCount); 258 | v4_template.r[3].length = htons(4); 259 | v4_template.r[4].ie = htons(IPFIX_ingressInterface); 260 | v4_template.r[4].length = htons(4); 261 | v4_template.r[5].ie = htons(IPFIX_egressInterface); 262 | v4_template.r[5].length = htons(4); 263 | v4_template.r[6].ie = htons(IPFIX_sourceTransportPort); 264 | v4_template.r[6].length = htons(2); 265 | v4_template.r[7].ie = htons(IPFIX_destinationTransportPort); 266 | v4_template.r[7].length = htons(2); 267 | v4_template.r[8].ie = htons(IPFIX_protocolIdentifier); 268 | v4_template.r[8].length = htons(1); 269 | v4_template.r[9].ie = htons(IPFIX_tcpControlBits); 270 | v4_template.r[9].length = htons(1); 271 | v4_template.r[10].ie = htons(IPFIX_ipVersion); 272 | v4_template.r[10].length = htons(1); 273 | v4_template.r[11].ie = htons(IPFIX_ipClassOfService); 274 | v4_template.r[11].length = htons(1); 275 | v4_template.r[12].ie = htons(IPFIX_icmpTypeCodeIPv4); 276 | v4_template.r[12].length = htons(2); 277 | v4_template.r[13].ie = htons(IPFIX_vlanId); 278 | v4_template.r[13].length = htons(2); 279 | if (param->time_format == 's') { 280 | v4_template.r[14].ie = htons(IPFIX_flowStartSeconds); 281 | v4_template.r[14].length = htons(sizeof(u_int32_t)); 282 | v4_template.r[15].ie = htons(IPFIX_flowEndSeconds); 283 | v4_template.r[15].length = htons(sizeof(u_int32_t)); 284 | } else if (param->time_format == 'm') { 285 | v4_template.r[14].ie = htons(IPFIX_flowStartMilliSeconds); 286 | v4_template.r[14].length = htons(sizeof(u_int64_t)); 287 | v4_template.r[15].ie = htons(IPFIX_flowEndMilliSeconds); 288 | v4_template.r[15].length = htons(sizeof(u_int64_t)); 289 | } else if (param->time_format == 'M') { 290 | v4_template.r[14].ie = htons(IPFIX_flowStartMicroSeconds); 291 | v4_template.r[14].length = htons(sizeof(u_int64_t)); 292 | v4_template.r[15].ie = htons(IPFIX_flowEndMicroSeconds); 293 | v4_template.r[15].length = htons(sizeof(u_int64_t)); 294 | } else if (param->time_format == 'n') { 295 | v4_template.r[14].ie = htons(IPFIX_flowStartNanoSeconds); 296 | v4_template.r[14].length = htons(sizeof(u_int64_t)); 297 | v4_template.r[15].ie = htons(IPFIX_flowEndNanoSeconds); 298 | v4_template.r[15].length = htons(sizeof(u_int64_t)); 299 | } else { 300 | v4_template.r[14].ie = htons(IPFIX_flowStartSysUpTime); 301 | v4_template.r[14].length = htons(sizeof(u_int32_t)); 302 | v4_template.r[15].ie = htons(IPFIX_flowEndSysUpTime); 303 | v4_template.r[15].length = htons(sizeof(u_int32_t)); 304 | } 305 | 306 | bzero(&v6_template, sizeof(v6_template)); 307 | v6_template.h.c.set_id = htons(IPFIX_TEMPLATE_SET_ID); 308 | v6_template.h.c.length = htons(sizeof(v6_template)); 309 | v6_template.h.r.template_id = htons(IPFIX_SOFTFLOWD_V6_TEMPLATE_ID); 310 | v6_template.h.r.count = htons(IPFIX_SOFTFLOWD_TEMPLATE_NRECORDS); 311 | v6_template.r[0].ie = htons(IPFIX_sourceIPv6Address); 312 | v6_template.r[0].length = htons(16); 313 | v6_template.r[1].ie = htons(IPFIX_destinationIPv6Address); 314 | v6_template.r[1].length = htons(16); 315 | v6_template.r[2].ie = htons(IPFIX_octetDeltaCount); 316 | v6_template.r[2].length = htons(4); 317 | v6_template.r[3].ie = htons(IPFIX_packetDeltaCount); 318 | v6_template.r[3].length = htons(4); 319 | v6_template.r[4].ie = htons(IPFIX_ingressInterface); 320 | v6_template.r[4].length = htons(4); 321 | v6_template.r[5].ie = htons(IPFIX_egressInterface); 322 | v6_template.r[5].length = htons(4); 323 | v6_template.r[6].ie = htons(IPFIX_sourceTransportPort); 324 | v6_template.r[6].length = htons(2); 325 | v6_template.r[7].ie = htons(IPFIX_destinationTransportPort); 326 | v6_template.r[7].length = htons(2); 327 | v6_template.r[8].ie = htons(IPFIX_protocolIdentifier); 328 | v6_template.r[8].length = htons(1); 329 | v6_template.r[9].ie = htons(IPFIX_tcpControlBits); 330 | v6_template.r[9].length = htons(1); 331 | v6_template.r[10].ie = htons(IPFIX_ipVersion); 332 | v6_template.r[10].length = htons(1); 333 | v6_template.r[11].ie = htons(IPFIX_ipClassOfService); 334 | v6_template.r[11].length = htons(1); 335 | v6_template.r[12].ie = htons(IPFIX_icmpTypeCodeIPv6); 336 | v6_template.r[12].length = htons(2); 337 | v6_template.r[13].ie = htons(IPFIX_vlanId); 338 | v6_template.r[13].length = htons(2); 339 | if (param->time_format == 's') { 340 | v6_template.r[14].ie = htons(IPFIX_flowStartSeconds); 341 | v6_template.r[14].length = htons(sizeof(u_int32_t)); 342 | v6_template.r[15].ie = htons(IPFIX_flowEndSeconds); 343 | v6_template.r[15].length = htons(sizeof(u_int32_t)); 344 | } else if (param->time_format == 'm') { 345 | v6_template.r[14].ie = htons(IPFIX_flowStartMilliSeconds); 346 | v6_template.r[14].length = htons(sizeof(u_int64_t)); 347 | v6_template.r[15].ie = htons(IPFIX_flowEndMilliSeconds); 348 | v6_template.r[15].length = htons(sizeof(u_int64_t)); 349 | } else if (param->time_format == 'M') { 350 | v6_template.r[14].ie = htons(IPFIX_flowStartMicroSeconds); 351 | v6_template.r[14].length = htons(sizeof(u_int64_t)); 352 | v6_template.r[15].ie = htons(IPFIX_flowEndMicroSeconds); 353 | v6_template.r[15].length = htons(sizeof(u_int64_t)); 354 | } else if (param->time_format == 'n') { 355 | v6_template.r[14].ie = htons(IPFIX_flowStartNanoSeconds); 356 | v6_template.r[14].length = htons(sizeof(u_int64_t)); 357 | v6_template.r[15].ie = htons(IPFIX_flowEndNanoSeconds); 358 | v6_template.r[15].length = htons(sizeof(u_int64_t)); 359 | } else { 360 | v6_template.r[14].ie = htons(IPFIX_flowStartSysUpTime); 361 | v6_template.r[14].length = htons(sizeof(u_int32_t)); 362 | v6_template.r[15].ie = htons(IPFIX_flowEndSysUpTime); 363 | v6_template.r[15].length = htons(sizeof(u_int32_t)); 364 | } 365 | } 366 | 367 | static void 368 | ipfix_init_template_bidirection(struct FLOWTRACKPARAMETERS *param) 369 | { 370 | bzero(&v4_bidirection_template, sizeof(v4_bidirection_template)); 371 | v4_bidirection_template.h.c.set_id = htons(IPFIX_TEMPLATE_SET_ID); 372 | v4_bidirection_template.h.c.length = htons(sizeof(v4_bidirection_template)); 373 | v4_bidirection_template.h.r.template_id = htons(IPFIX_SOFTFLOWD_V4_TEMPLATE_ID); 374 | v4_bidirection_template.h.r.count = htons(IPFIX_SOFTFLOWD_TEMPLATE_BIDIRECTION_NRECORDS); 375 | v4_bidirection_template.r[0].ie = htons(IPFIX_sourceIPv4Address); 376 | v4_bidirection_template.r[0].length = htons(4); 377 | v4_bidirection_template.r[1].ie = htons(IPFIX_destinationIPv4Address); 378 | v4_bidirection_template.r[1].length = htons(4); 379 | v4_bidirection_template.r[2].ie = htons(IPFIX_octetDeltaCount); 380 | v4_bidirection_template.r[2].length = htons(4); 381 | v4_bidirection_template.r[3].ie = htons(IPFIX_packetDeltaCount); 382 | v4_bidirection_template.r[3].length = htons(4); 383 | v4_bidirection_template.r[4].ie = htons(IPFIX_ingressInterface); 384 | v4_bidirection_template.r[4].length = htons(4); 385 | v4_bidirection_template.r[5].ie = htons(IPFIX_egressInterface); 386 | v4_bidirection_template.r[5].length = htons(4); 387 | v4_bidirection_template.r[6].ie = htons(IPFIX_sourceTransportPort); 388 | v4_bidirection_template.r[6].length = htons(2); 389 | v4_bidirection_template.r[7].ie = htons(IPFIX_destinationTransportPort); 390 | v4_bidirection_template.r[7].length = htons(2); 391 | v4_bidirection_template.r[8].ie = htons(IPFIX_protocolIdentifier); 392 | v4_bidirection_template.r[8].length = htons(1); 393 | v4_bidirection_template.r[9].ie = htons(IPFIX_tcpControlBits); 394 | v4_bidirection_template.r[9].length = htons(1); 395 | v4_bidirection_template.r[10].ie = htons(IPFIX_ipVersion); 396 | v4_bidirection_template.r[10].length = htons(1); 397 | v4_bidirection_template.r[11].ie = htons(IPFIX_ipClassOfService); 398 | v4_bidirection_template.r[11].length = htons(1); 399 | v4_bidirection_template.r[12].ie = htons(IPFIX_icmpTypeCodeIPv4); 400 | v4_bidirection_template.r[12].length = htons(2); 401 | v4_bidirection_template.r[13].ie = htons(IPFIX_vlanId); 402 | v4_bidirection_template.r[13].length = htons(2); 403 | v4_bidirection_template.v[0].ie = htons(IPFIX_octetDeltaCount | 0x8000); 404 | v4_bidirection_template.v[0].length = htons(4); 405 | v4_bidirection_template.v[0].pen = htonl(REVERSE_PEN); 406 | v4_bidirection_template.v[1].ie = htons(IPFIX_packetDeltaCount | 0x8000); 407 | v4_bidirection_template.v[1].length = htons(4); 408 | v4_bidirection_template.v[1].pen = htonl(REVERSE_PEN); 409 | v4_bidirection_template.v[2].ie = htons(IPFIX_tcpControlBits | 0x8000); 410 | v4_bidirection_template.v[2].length = htons(1); 411 | v4_bidirection_template.v[2].pen = htonl(REVERSE_PEN); 412 | v4_bidirection_template.v[3].ie = htons(IPFIX_ipClassOfService | 0x8000); 413 | v4_bidirection_template.v[3].length = htons(1); 414 | v4_bidirection_template.v[3].pen = htonl(REVERSE_PEN); 415 | v4_bidirection_template.v[4].ie = htons(IPFIX_icmpTypeCodeIPv4 | 0x8000); 416 | v4_bidirection_template.v[4].length = htons(2); 417 | v4_bidirection_template.v[4].pen = htonl(REVERSE_PEN); 418 | if (param->time_format == 's') { 419 | v4_bidirection_template.t[0].ie = htons(IPFIX_flowStartSeconds); 420 | v4_bidirection_template.t[0].length = htons(sizeof(u_int32_t)); 421 | v4_bidirection_template.t[1].ie = htons(IPFIX_flowEndSeconds); 422 | v4_bidirection_template.t[1].length = htons(sizeof(u_int32_t)); 423 | } else if (param->time_format == 'm') { 424 | v4_bidirection_template.t[0].ie = htons(IPFIX_flowStartMilliSeconds); 425 | v4_bidirection_template.t[0].length = htons(sizeof(u_int64_t)); 426 | v4_bidirection_template.t[1].ie = htons(IPFIX_flowEndMilliSeconds); 427 | v4_bidirection_template.t[1].length = htons(sizeof(u_int64_t)); 428 | } else if (param->time_format == 'M') { 429 | v4_bidirection_template.t[0].ie = htons(IPFIX_flowStartMicroSeconds); 430 | v4_bidirection_template.t[0].length = htons(sizeof(u_int64_t)); 431 | v4_bidirection_template.t[1].ie = htons(IPFIX_flowEndMicroSeconds); 432 | v4_bidirection_template.t[1].length = htons(sizeof(u_int64_t)); 433 | } else if (param->time_format == 'n') { 434 | v4_bidirection_template.t[0].ie = htons(IPFIX_flowStartNanoSeconds); 435 | v4_bidirection_template.t[0].length = htons(sizeof(u_int64_t)); 436 | v4_bidirection_template.t[1].ie = htons(IPFIX_flowEndNanoSeconds); 437 | v4_bidirection_template.t[1].length = htons(sizeof(u_int64_t)); 438 | } else { 439 | v4_bidirection_template.t[0].ie = htons(IPFIX_flowStartSysUpTime); 440 | v4_bidirection_template.t[0].length = htons(sizeof(u_int32_t)); 441 | v4_bidirection_template.t[1].ie = htons(IPFIX_flowEndSysUpTime); 442 | v4_bidirection_template.t[1].length = htons(sizeof(u_int32_t)); 443 | } 444 | 445 | bzero(&v6_bidirection_template, sizeof(v6_bidirection_template)); 446 | v6_bidirection_template.h.c.set_id = htons(IPFIX_TEMPLATE_SET_ID); 447 | v6_bidirection_template.h.c.length = htons(sizeof(v6_bidirection_template)); 448 | v6_bidirection_template.h.r.template_id = htons(IPFIX_SOFTFLOWD_V6_TEMPLATE_ID); 449 | v6_bidirection_template.h.r.count = htons(IPFIX_SOFTFLOWD_TEMPLATE_BIDIRECTION_NRECORDS); 450 | v6_bidirection_template.r[0].ie = htons(IPFIX_sourceIPv6Address); 451 | v6_bidirection_template.r[0].length = htons(16); 452 | v6_bidirection_template.r[1].ie = htons(IPFIX_destinationIPv6Address); 453 | v6_bidirection_template.r[1].length = htons(16); 454 | v6_bidirection_template.r[2].ie = htons(IPFIX_octetDeltaCount); 455 | v6_bidirection_template.r[2].length = htons(4); 456 | v6_bidirection_template.r[3].ie = htons(IPFIX_packetDeltaCount); 457 | v6_bidirection_template.r[3].length = htons(4); 458 | v6_bidirection_template.r[4].ie = htons(IPFIX_ingressInterface); 459 | v6_bidirection_template.r[4].length = htons(4); 460 | v6_bidirection_template.r[5].ie = htons(IPFIX_egressInterface); 461 | v6_bidirection_template.r[5].length = htons(4); 462 | v6_bidirection_template.r[6].ie = htons(IPFIX_sourceTransportPort); 463 | v6_bidirection_template.r[6].length = htons(2); 464 | v6_bidirection_template.r[7].ie = htons(IPFIX_destinationTransportPort); 465 | v6_bidirection_template.r[7].length = htons(2); 466 | v6_bidirection_template.r[8].ie = htons(IPFIX_protocolIdentifier); 467 | v6_bidirection_template.r[8].length = htons(1); 468 | v6_bidirection_template.r[9].ie = htons(IPFIX_tcpControlBits); 469 | v6_bidirection_template.r[9].length = htons(1); 470 | v6_bidirection_template.r[10].ie = htons(IPFIX_ipVersion); 471 | v6_bidirection_template.r[10].length = htons(1); 472 | v6_bidirection_template.r[11].ie = htons(IPFIX_ipClassOfService); 473 | v6_bidirection_template.r[11].length = htons(1); 474 | v6_bidirection_template.r[12].ie = htons(IPFIX_icmpTypeCodeIPv6); 475 | v6_bidirection_template.r[12].length = htons(2); 476 | v6_bidirection_template.r[13].ie = htons(IPFIX_vlanId); 477 | v6_bidirection_template.r[13].length = htons(2); 478 | v6_bidirection_template.v[0].ie = htons(IPFIX_octetDeltaCount | 0x8000); 479 | v6_bidirection_template.v[0].length = htons(4); 480 | v6_bidirection_template.v[0].pen = htonl(REVERSE_PEN); 481 | v6_bidirection_template.v[1].ie = htons(IPFIX_packetDeltaCount | 0x8000); 482 | v6_bidirection_template.v[1].length = htons(4); 483 | v6_bidirection_template.v[1].pen = htonl(REVERSE_PEN); 484 | v6_bidirection_template.v[2].ie = htons(IPFIX_tcpControlBits | 0x8000); 485 | v6_bidirection_template.v[2].length = htons(1); 486 | v6_bidirection_template.v[2].pen = htonl(REVERSE_PEN); 487 | v6_bidirection_template.v[3].ie = htons(IPFIX_ipClassOfService | 0x8000); 488 | v6_bidirection_template.v[3].length = htons(1); 489 | v6_bidirection_template.v[3].pen = htonl(REVERSE_PEN); 490 | v6_bidirection_template.v[4].ie = htons(IPFIX_icmpTypeCodeIPv6 | 0x8000); 491 | v6_bidirection_template.v[4].length = htons(2); 492 | v6_bidirection_template.v[4].pen = htonl(REVERSE_PEN); 493 | if (param->time_format == 's') { 494 | v6_bidirection_template.t[0].ie = htons(IPFIX_flowStartSeconds); 495 | v6_bidirection_template.t[0].length = htons(sizeof(u_int32_t)); 496 | v6_bidirection_template.t[1].ie = htons(IPFIX_flowEndSeconds); 497 | v6_bidirection_template.t[1].length = htons(sizeof(u_int32_t)); 498 | } else if (param->time_format == 'm') { 499 | v6_bidirection_template.t[0].ie = htons(IPFIX_flowStartMilliSeconds); 500 | v6_bidirection_template.t[0].length = htons(sizeof(u_int64_t)); 501 | v6_bidirection_template.t[1].ie = htons(IPFIX_flowEndMilliSeconds); 502 | v6_bidirection_template.t[1].length = htons(sizeof(u_int64_t)); 503 | } else if (param->time_format == 'M') { 504 | v6_bidirection_template.t[0].ie = htons(IPFIX_flowStartMicroSeconds); 505 | v6_bidirection_template.t[0].length = htons(sizeof(u_int64_t)); 506 | v6_bidirection_template.t[1].ie = htons(IPFIX_flowEndMicroSeconds); 507 | v6_bidirection_template.t[1].length = htons(sizeof(u_int64_t)); 508 | } else if (param->time_format == 'n') { 509 | v6_bidirection_template.t[0].ie = htons(IPFIX_flowStartNanoSeconds); 510 | v6_bidirection_template.t[0].length = htons(sizeof(u_int64_t)); 511 | v6_bidirection_template.t[1].ie = htons(IPFIX_flowEndNanoSeconds); 512 | v6_bidirection_template.t[1].length = htons(sizeof(u_int64_t)); 513 | } else { 514 | v6_bidirection_template.t[0].ie = htons(IPFIX_flowStartSysUpTime); 515 | v6_bidirection_template.t[0].length = htons(sizeof(u_int32_t)); 516 | v6_bidirection_template.t[1].ie = htons(IPFIX_flowEndSysUpTime); 517 | v6_bidirection_template.t[1].length = htons(sizeof(u_int32_t)); 518 | } 519 | } 520 | 521 | 522 | static void 523 | ipfix_init_option(struct timeval *system_boot_time, struct OPTION *option) { 524 | bzero(&option_template, sizeof(option_template)); 525 | option_template.h.c.set_id = htons(IPFIX_OPTION_TEMPLATE_SET_ID); 526 | option_template.h.c.length = htons(sizeof(option_template)); 527 | option_template.h.r.template_id = htons(IPFIX_SOFTFLOWD_OPTION_TEMPLATE_ID); 528 | option_template.h.r.count = htons(IPFIX_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS + IPFIX_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS); 529 | option_template.h.scope_count = htons(IPFIX_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS); 530 | option_template.s[0].ie = htons(IPFIX_meteringProcessId); 531 | option_template.s[0].length = htons(sizeof(option_data.scope_pid)); 532 | option_template.r[0].ie = htons(IPFIX_systemInitTimeMilliseconds); 533 | option_template.r[0].length = htons(sizeof(option_data.systemInitTimeMilliseconds)); 534 | option_template.r[1].ie = htons(PSAMP_selectorAlgorithm); 535 | option_template.r[1].length = htons(sizeof(option_data.samplingAlgorithm)); 536 | option_template.r[2].ie = htons(PSAMP_samplingPacketInterval); 537 | option_template.r[2].length = htons(sizeof(option_data.samplingInterval)); 538 | option_template.r[3].ie = htons(PSAMP_samplingPacketSpace); 539 | option_template.r[3].length = htons(sizeof(option_data.samplingSpace)); 540 | 541 | bzero(&option_data, sizeof(option_data)); 542 | option_data.c.set_id = htons(IPFIX_SOFTFLOWD_OPTION_TEMPLATE_ID); 543 | option_data.c.length = htons(sizeof(option_data)); 544 | option_data.scope_pid = htonl((u_int32_t)option->meteringProcessId); 545 | #if defined(htobe64) || defined(HAVE_DECL_HTOBE64) 546 | option_data.systemInitTimeMilliseconds = htobe64((u_int64_t)system_boot_time->tv_sec * 1000 + (u_int64_t)system_boot_time->tv_usec / 1000); 547 | #endif 548 | option_data.samplingAlgorithm = htons(PSAMP_selectorAlgorithm_count); 549 | option_data.samplingInterval = htons(1); 550 | option_data.samplingSpace = htonl(option->sample > 0 ? option->sample - 1 : 0); 551 | } 552 | 553 | static int 554 | ipfix_flow_to_flowset(const struct FLOW *flow, u_char *packet, u_int len, 555 | u_int16_t ifidx, const struct timeval *system_boot_time, u_int *len_used, 556 | struct FLOWTRACKPARAMETERS *param) 557 | { 558 | union { 559 | struct IPFIX_SOFTFLOWD_DATA_V4 d4; 560 | struct IPFIX_SOFTFLOWD_DATA_V6 d6; 561 | } d[2]; 562 | struct IPFIX_SOFTFLOWD_DATA_COMMON *dc[2]; 563 | union IPFIX_SOFTFLOWD_DATA_TIME *dt[2]; 564 | u_int freclen, ret_len, nflows; 565 | 566 | bzero(d, sizeof(d)); 567 | *len_used = nflows = ret_len = 0; 568 | switch (flow->af) { 569 | case AF_INET: 570 | freclen = sizeof(struct IPFIX_SOFTFLOWD_DATA_V4); 571 | if (!(param->time_format == 'm' || param->time_format == 'M' || param->time_format == 'n')) { 572 | freclen -= (sizeof(u_int64_t) - sizeof(u_int32_t)) * 2; 573 | } 574 | memcpy(&d[0].d4.sourceIPv4Address, &flow->addr[0].v4, 4); 575 | memcpy(&d[0].d4.destinationIPv4Address, &flow->addr[1].v4, 4); 576 | memcpy(&d[1].d4.sourceIPv4Address, &flow->addr[1].v4, 4); 577 | memcpy(&d[1].d4.destinationIPv4Address, &flow->addr[0].v4, 4); 578 | dc[0] = &d[0].d4.c; 579 | dc[1] = &d[1].d4.c; 580 | dt[0] = &d[0].d4.t; 581 | dt[1] = &d[1].d4.t; 582 | dc[0]->ipVersion = dc[1]->ipVersion = 4; 583 | break; 584 | case AF_INET6: 585 | freclen = sizeof(struct IPFIX_SOFTFLOWD_DATA_V6); 586 | if (!(param->time_format == 'm' || param->time_format == 'M' || param->time_format == 'n')) { 587 | freclen -= (sizeof(u_int64_t) - sizeof(u_int32_t)) * 2; 588 | } 589 | memcpy(&d[0].d6.sourceIPv6Address, &flow->addr[0].v6, 16); 590 | memcpy(&d[0].d6.destinationIPv6Address, &flow->addr[1].v6, 16); 591 | memcpy(&d[1].d6.sourceIPv6Address, &flow->addr[1].v6, 16); 592 | memcpy(&d[1].d6.destinationIPv6Address, &flow->addr[0].v6, 16); 593 | dc[0] = &d[0].d6.c; 594 | dc[1] = &d[1].d6.c; 595 | dt[0] = &d[0].d6.t; 596 | dt[1] = &d[1].d6.t; 597 | dc[0]->ipVersion = dc[1]->ipVersion = 6; 598 | break; 599 | default: 600 | return (-1); 601 | } 602 | 603 | if (param->time_format == 's') { 604 | dt[0]->u32.start = dt[1]->u32.start = 605 | htonl(flow->flow_start.tv_sec); 606 | dt[0]->u32.end = dt[1]->u32.end = 607 | htonl(flow->flow_last.tv_sec); 608 | } 609 | #if defined(htobe64) || defined(HAVE_DECL_HTOBE64) 610 | else if (param->time_format == 'm') { /* milliseconds */ 611 | dt[0]->u64.start = dt[1]->u64.start = 612 | htobe64((u_int64_t)flow->flow_start.tv_sec * 1000 + (u_int64_t)flow->flow_start.tv_usec / 1000); 613 | dt[0]->u64.end = dt[1]->u64.end = 614 | htobe64((u_int64_t)flow->flow_last.tv_sec * 1000 + (u_int64_t)flow->flow_last.tv_usec / 1000); 615 | } else if (param->time_format == 'M') { /* microseconds */ 616 | dt[0]->u64.start = dt[1]->u64.start = 617 | htobe64(((u_int64_t)flow->flow_start.tv_sec + JAN_1970) << 32 | (u_int32_t)(((u_int64_t)flow->flow_start.tv_usec << 32) / 1e6)); 618 | dt[0]->u64.end = dt[1]->u64.end = 619 | htobe64(((u_int64_t)flow->flow_last.tv_sec + JAN_1970) << 32 | (u_int32_t)(((u_int64_t)flow->flow_last.tv_usec << 32) / 1e6)); 620 | } else if (param->time_format == 'n') { /* nanoseconds */ 621 | dt[0]->u64.start = dt[1]->u64.start = 622 | htobe64(((u_int64_t)flow->flow_start.tv_sec + JAN_1970) << 32 | (u_int32_t)(((u_int64_t)flow->flow_start.tv_usec << 32) / 1e9)); 623 | dt[0]->u64.end = dt[1]->u64.end = 624 | htobe64(((u_int64_t)flow->flow_last.tv_sec + JAN_1970) << 32 | (u_int32_t)(((u_int64_t)flow->flow_last.tv_usec << 32) / 1e9)); 625 | } 626 | #endif 627 | else { 628 | dt[0]->u32.start = dt[1]->u32.start = 629 | htonl(timeval_sub_ms(&flow->flow_start, system_boot_time)); 630 | dt[0]->u32.end = dt[1]->u32.end = 631 | htonl(timeval_sub_ms(&flow->flow_last, system_boot_time)); 632 | } 633 | dc[0]->octetDeltaCount = htonl(flow->octets[0]); 634 | dc[1]->octetDeltaCount = htonl(flow->octets[1]); 635 | dc[0]->packetDeltaCount = htonl(flow->packets[0]); 636 | dc[1]->packetDeltaCount = htonl(flow->packets[1]); 637 | dc[0]->ingressInterface = dc[0]->egressInterface = htonl(ifidx); 638 | dc[1]->ingressInterface = dc[1]->egressInterface = htonl(ifidx); 639 | dc[0]->sourceTransportPort = dc[1]->destinationTransportPort = flow->port[0]; 640 | dc[1]->sourceTransportPort = dc[0]->destinationTransportPort = flow->port[1]; 641 | dc[0]->protocolIdentifier = dc[1]->protocolIdentifier = flow->protocol; 642 | dc[0]->tcpControlBits = flow->tcp_flags[0]; 643 | dc[1]->tcpControlBits = flow->tcp_flags[1]; 644 | dc[0]->ipClassOfService = flow->tos[0]; 645 | dc[1]->ipClassOfService = flow->tos[1]; 646 | if (flow->protocol == IPPROTO_ICMP || flow->protocol == IPPROTO_ICMPV6) { 647 | dc[0]->icmpTypeCode = dc[0]->destinationTransportPort; 648 | dc[1]->icmpTypeCode = dc[1]->destinationTransportPort; 649 | } 650 | dc[0]->vlanId = dc[1]->vlanId = htons(flow->vlanid); 651 | 652 | if (flow->octets[0] > 0) { 653 | if (ret_len + freclen > len) 654 | return (-1); 655 | memcpy(packet + ret_len, &d[0], freclen); 656 | ret_len += freclen; 657 | nflows++; 658 | } 659 | if (flow->octets[1] > 0) { 660 | if (ret_len + freclen > len) 661 | return (-1); 662 | memcpy(packet + ret_len, &d[1], freclen); 663 | ret_len += freclen; 664 | nflows++; 665 | } 666 | 667 | *len_used = ret_len; 668 | return (nflows); 669 | } 670 | 671 | static int 672 | ipfix_flow_to_bidirection_flowset(const struct FLOW *flow, u_char *packet, 673 | u_int len, u_int16_t ifidx, 674 | const struct timeval *system_boot_time, 675 | u_int *len_used, 676 | struct FLOWTRACKPARAMETERS *param) 677 | { 678 | union { 679 | struct IPFIX_SOFTFLOWD_BIDIRECTION_DATA_V4 d4; 680 | struct IPFIX_SOFTFLOWD_BIDIRECTION_DATA_V6 d6; 681 | } d; 682 | struct IPFIX_SOFTFLOWD_DATA_COMMON *dc; 683 | struct IPFIX_SOFTFLOWD_DATA_BIDIRECTION *db; 684 | union IPFIX_SOFTFLOWD_DATA_TIME *dt; 685 | u_int freclen, ret_len, nflows; 686 | 687 | bzero(&d, sizeof(d)); 688 | *len_used = nflows = ret_len = 0; 689 | switch (flow->af) { 690 | case AF_INET: 691 | freclen = sizeof(struct IPFIX_SOFTFLOWD_BIDIRECTION_DATA_V4); 692 | if (!(param->time_format == 'm' || param->time_format == 'M' || param->time_format == 'n')) { 693 | freclen -= (sizeof(u_int64_t) - sizeof(u_int32_t)) * 2; 694 | } 695 | memcpy(&d.d4.sourceIPv4Address, &flow->addr[0].v4, 4); 696 | memcpy(&d.d4.destinationIPv4Address, &flow->addr[1].v4, 4); 697 | dc = &d.d4.c; 698 | db = &d.d4.b; 699 | dt = &d.d4.t; 700 | dc->ipVersion = 4; 701 | break; 702 | case AF_INET6: 703 | freclen = sizeof(struct IPFIX_SOFTFLOWD_BIDIRECTION_DATA_V6); 704 | if (!(param->time_format == 'm' || param->time_format == 'M' || param->time_format == 'n')) { 705 | freclen -= (sizeof(u_int64_t) - sizeof(u_int32_t)) * 2; 706 | } 707 | memcpy(&d.d6.sourceIPv6Address, &flow->addr[0].v6, 16); 708 | memcpy(&d.d6.destinationIPv6Address, &flow->addr[1].v6, 16); 709 | dc = &d.d6.c; 710 | db = &d.d6.b; 711 | dt = &d.d6.t; 712 | dc->ipVersion = 6; 713 | break; 714 | default: 715 | return (-1); 716 | } 717 | 718 | if (param->time_format == 's') { 719 | dt->u32.start = htonl(flow->flow_start.tv_sec); 720 | dt->u32.end = htonl(flow->flow_last.tv_sec); 721 | } 722 | #if defined(htobe64) || defined(HAVE_DECL_HTOBE64) 723 | else if (param->time_format == 'm') { /* milliseconds */ 724 | dt->u64.start = 725 | htobe64((u_int64_t)flow->flow_start.tv_sec * 1000 + (u_int64_t)flow->flow_start.tv_usec / 1000); 726 | dt->u64.end = 727 | htobe64((u_int64_t)flow->flow_last.tv_sec * 1000 + (u_int64_t)flow->flow_last.tv_usec / 1000); 728 | } else if (param->time_format == 'M') { /* microseconds */ 729 | dt->u64.start = 730 | htobe64(((u_int64_t)flow->flow_start.tv_sec + JAN_1970) << 32 | (u_int32_t)(((u_int64_t)flow->flow_start.tv_usec << 32) / 1e6)); 731 | dt->u64.end = 732 | htobe64(((u_int64_t)flow->flow_last.tv_sec + JAN_1970) << 32 | (u_int32_t)(((u_int64_t)flow->flow_last.tv_usec << 32) / 1e6)); 733 | } else if (param->time_format == 'n') { /* nanoseconds */ 734 | dt->u64.start = 735 | htobe64(((u_int64_t)flow->flow_start.tv_sec + JAN_1970) << 32 | (u_int32_t)(((u_int64_t)flow->flow_start.tv_usec << 32) / 1e9)); 736 | dt->u64.end = 737 | htobe64(((u_int64_t)flow->flow_last.tv_sec + JAN_1970) << 32 | (u_int32_t)(((u_int64_t)flow->flow_last.tv_usec << 32) / 1e9)); 738 | } 739 | #endif 740 | else { 741 | dt->u32.start = 742 | htonl(timeval_sub_ms(&flow->flow_start, system_boot_time)); 743 | dt->u32.end = 744 | htonl(timeval_sub_ms(&flow->flow_last, system_boot_time)); 745 | } 746 | dc->octetDeltaCount = htonl(flow->octets[0]); 747 | db->octetDeltaCount = htonl(flow->octets[1]); 748 | dc->packetDeltaCount = htonl(flow->packets[0]); 749 | db->packetDeltaCount = htonl(flow->packets[1]); 750 | dc->ingressInterface = dc->egressInterface = htonl(ifidx); 751 | dc->sourceTransportPort = flow->port[0]; 752 | dc->destinationTransportPort = flow->port[1]; 753 | dc->protocolIdentifier = flow->protocol; 754 | dc->tcpControlBits = flow->tcp_flags[0]; 755 | db->tcpControlBits = flow->tcp_flags[1]; 756 | dc->ipClassOfService = flow->tos[0]; 757 | db->ipClassOfService = flow->tos[1]; 758 | if (flow->protocol == IPPROTO_ICMP || flow->protocol == IPPROTO_ICMPV6) { 759 | dc->icmpTypeCode = flow->port[1]; 760 | db->icmpTypeCode = flow->port[0]; 761 | } 762 | dc->vlanId = htons(flow->vlanid); 763 | 764 | if (flow->octets[0] > 0 || flow->octets[1] > 0) { 765 | if (ret_len + freclen > len) 766 | return (-1); 767 | memcpy(packet + ret_len, &d, freclen); 768 | ret_len += freclen; 769 | nflows++; 770 | } 771 | 772 | *len_used = ret_len; 773 | return (nflows); 774 | } 775 | 776 | 777 | /* 778 | * Given an array of expired flows, send netflow v9 report packets 779 | * Returns number of packets sent or -1 on error 780 | */ 781 | int 782 | send_ipfix(struct FLOW **flows, int num_flows, int nfsock, 783 | u_int16_t ifidx, struct FLOWTRACKPARAMETERS *param, 784 | int verbose_flag) 785 | { 786 | struct IPFIX_HEADER *ipfix; 787 | struct IPFIX_SET_HEADER *dh; 788 | struct timeval now; 789 | u_int offset, last_af, i, j, num_packets, inc, last_valid; 790 | socklen_t errsz; 791 | int err, r; 792 | u_int records; 793 | u_char packet[IPFIX_SOFTFLOWD_MAX_PACKET_SIZE]; 794 | struct timeval *system_boot_time = ¶m->system_boot_time; 795 | u_int64_t *flows_exported = ¶m->flows_exported; 796 | u_int64_t *records_sent = ¶m->records_sent; 797 | struct OPTION *option = ¶m->option; 798 | 799 | gettimeofday(&now, NULL); 800 | 801 | if (ipfix_pkts_until_template == -1) { 802 | ipfix_init_template(param); 803 | ipfix_pkts_until_template = 0; 804 | if (option != NULL){ 805 | ipfix_init_option(system_boot_time, option); 806 | } 807 | } 808 | 809 | last_valid = num_packets = 0; 810 | for (j = 0; j < num_flows;) { 811 | bzero(packet, sizeof(packet)); 812 | ipfix = (struct IPFIX_HEADER *)packet; 813 | 814 | ipfix->version = htons(10); 815 | ipfix->length = 0; /* Filled as we go, htons at end */ 816 | ipfix->export_time = htonl(time(NULL)); 817 | ipfix->od_id = 0; 818 | offset = sizeof(*ipfix); 819 | 820 | /* Refresh template headers if we need to */ 821 | if (ipfix_pkts_until_template <= 0) { 822 | memcpy(packet + offset, &v4_template, 823 | sizeof(v4_template)); 824 | offset += sizeof(v4_template); 825 | memcpy(packet + offset, &v6_template, 826 | sizeof(v6_template)); 827 | offset += sizeof(v6_template); 828 | if (option != NULL){ 829 | memcpy(packet + offset, &option_template, 830 | sizeof(option_template)); 831 | offset += sizeof(option_template); 832 | memcpy(packet + offset, &option_data, 833 | sizeof(option_data)); 834 | offset += sizeof(option_data); 835 | } 836 | 837 | ipfix_pkts_until_template = IPFIX_DEFAULT_TEMPLATE_INTERVAL; 838 | } 839 | 840 | dh = NULL; 841 | last_af = 0; 842 | records = 0; 843 | for (i = 0; i + j < num_flows; i++) { 844 | if (dh == NULL || flows[i + j]->af != last_af) { 845 | if (dh != NULL) { 846 | if (offset % 4 != 0) { 847 | /* Pad to multiple of 4 */ 848 | dh->length += 4 - (offset % 4); 849 | offset += 4 - (offset % 4); 850 | } 851 | /* Finalise last header */ 852 | dh->length = htons(dh->length); 853 | } 854 | if (offset + sizeof(*dh) > sizeof(packet)) { 855 | /* Mark header is finished */ 856 | dh = NULL; 857 | break; 858 | } 859 | dh = (struct IPFIX_SET_HEADER *) 860 | (packet + offset); 861 | dh->set_id = 862 | (flows[i + j]->af == AF_INET) ? 863 | v4_template.h.r.template_id : 864 | v6_template.h.r.template_id; 865 | last_af = flows[i + j]->af; 866 | last_valid = offset; 867 | dh->length = sizeof(*dh); /* Filled as we go */ 868 | offset += sizeof(*dh); 869 | } 870 | 871 | r = ipfix_flow_to_flowset(flows[i + j], packet + offset, 872 | sizeof(packet) - offset, ifidx, system_boot_time, &inc, param); 873 | if (r <= 0) { 874 | /* yank off data header, if we had to go back */ 875 | if (last_valid) 876 | offset = last_valid; 877 | break; 878 | } 879 | records += (u_int)r; 880 | offset += inc; 881 | dh->length += inc; 882 | last_valid = 0; /* Don't clobber this header now */ 883 | if (verbose_flag) { 884 | logit(LOG_DEBUG, "Flow %d/%d: " 885 | "r %d offset %d ie %04x len %d(0x%04x)", 886 | r, i, j, offset, 887 | dh->set_id, dh->length, 888 | dh->length); 889 | } 890 | } 891 | /* Don't finish header if it has already been done */ 892 | if (dh != NULL) { 893 | if (offset % 4 != 0) { 894 | /* Pad to multiple of 4 */ 895 | dh->length += 4 - (offset % 4); 896 | offset += 4 - (offset % 4); 897 | } 898 | /* Finalise last header */ 899 | dh->length = htons(dh->length); 900 | } 901 | ipfix->length = htons(offset); 902 | *records_sent += records; 903 | ipfix->sequence = htonl((u_int32_t)(*records_sent & 0x00000000ffffffff)); 904 | 905 | if (verbose_flag) 906 | logit(LOG_DEBUG, "Sending flow packet len = %d", offset); 907 | errsz = sizeof(err); 908 | /* Clear ICMP errors */ 909 | getsockopt(nfsock, SOL_SOCKET, SO_ERROR, &err, &errsz); 910 | if (send(nfsock, packet, (size_t)offset, 0) == -1) 911 | return (-1); 912 | num_packets++; 913 | ipfix_pkts_until_template--; 914 | 915 | j += i; 916 | } 917 | 918 | *flows_exported += j; 919 | return (num_packets); 920 | } 921 | 922 | void 923 | ipfix_resend_template(void) 924 | { 925 | if (ipfix_pkts_until_template > 0) 926 | ipfix_pkts_until_template = 0; 927 | } 928 | 929 | /* 930 | * Given an array of expired flows, send netflow v9 report packets 931 | * Returns number of packets sent or -1 on error 932 | */ 933 | int 934 | send_ipfix_bidirection(struct FLOW **flows, int num_flows, int nfsock, 935 | u_int16_t ifidx, struct FLOWTRACKPARAMETERS *param, 936 | int verbose_flag) 937 | { 938 | struct IPFIX_HEADER *ipfix; 939 | struct IPFIX_SET_HEADER *dh; 940 | struct timeval now; 941 | u_int offset, last_af, i, j, num_packets, inc, last_valid; 942 | socklen_t errsz; 943 | int err, r; 944 | u_int records; 945 | u_char packet[IPFIX_SOFTFLOWD_MAX_PACKET_SIZE]; 946 | struct timeval *system_boot_time = ¶m->system_boot_time; 947 | u_int64_t *flows_exported = ¶m->flows_exported; 948 | u_int64_t *records_sent = ¶m->records_sent; 949 | struct OPTION *option = ¶m->option; 950 | 951 | gettimeofday(&now, NULL); 952 | 953 | if (ipfix_pkts_until_template == -1) { 954 | ipfix_init_template_bidirection(param); 955 | ipfix_pkts_until_template = 0; 956 | if (option != NULL){ 957 | ipfix_init_option(system_boot_time, option); 958 | } 959 | } 960 | 961 | last_valid = num_packets = 0; 962 | for (j = 0; j < num_flows;) { 963 | bzero(packet, sizeof(packet)); 964 | ipfix = (struct IPFIX_HEADER *)packet; 965 | 966 | ipfix->version = htons(10); 967 | ipfix->length = 0; /* Filled as we go, htons at end */ 968 | ipfix->export_time = htonl(time(NULL)); 969 | ipfix->od_id = 0; 970 | offset = sizeof(*ipfix); 971 | 972 | /* Refresh template headers if we need to */ 973 | if (ipfix_pkts_until_template <= 0) { 974 | memcpy(packet + offset, &v4_bidirection_template, 975 | sizeof(v4_bidirection_template)); 976 | offset += sizeof(v4_bidirection_template); 977 | memcpy(packet + offset, &v6_bidirection_template, 978 | sizeof(v6_bidirection_template)); 979 | offset += sizeof(v6_bidirection_template); 980 | if (option != NULL){ 981 | memcpy(packet + offset, &option_template, 982 | sizeof(option_template)); 983 | offset += sizeof(option_template); 984 | memcpy(packet + offset, &option_data, 985 | sizeof(option_data)); 986 | offset += sizeof(option_data); 987 | } 988 | 989 | ipfix_pkts_until_template = IPFIX_DEFAULT_TEMPLATE_INTERVAL; 990 | } 991 | 992 | dh = NULL; 993 | last_af = 0; 994 | records = 0; 995 | for (i = 0; i + j < num_flows; i++) { 996 | if (dh == NULL || flows[i + j]->af != last_af) { 997 | if (dh != NULL) { 998 | if (offset % 4 != 0) { 999 | /* Pad to multiple of 4 */ 1000 | dh->length += 4 - (offset % 4); 1001 | offset += 4 - (offset % 4); 1002 | } 1003 | /* Finalise last header */ 1004 | dh->length = htons(dh->length); 1005 | } 1006 | if (offset + sizeof(*dh) > sizeof(packet)) { 1007 | /* Mark header is finished */ 1008 | dh = NULL; 1009 | break; 1010 | } 1011 | dh = (struct IPFIX_SET_HEADER *) 1012 | (packet + offset); 1013 | dh->set_id = 1014 | (flows[i + j]->af == AF_INET) ? 1015 | v4_bidirection_template.h.r.template_id : 1016 | v6_bidirection_template.h.r.template_id; 1017 | last_af = flows[i + j]->af; 1018 | last_valid = offset; 1019 | dh->length = sizeof(*dh); /* Filled as we go */ 1020 | offset += sizeof(*dh); 1021 | } 1022 | 1023 | r = ipfix_flow_to_bidirection_flowset(flows[i + j], 1024 | packet + offset, 1025 | sizeof(packet) - offset, 1026 | ifidx, 1027 | system_boot_time, 1028 | &inc, param); 1029 | if (r <= 0) { 1030 | /* yank off data header, if we had to go back */ 1031 | if (last_valid) 1032 | offset = last_valid; 1033 | break; 1034 | } 1035 | records += (u_int)r; 1036 | offset += inc; 1037 | dh->length += inc; 1038 | last_valid = 0; /* Don't clobber this header now */ 1039 | if (verbose_flag) { 1040 | logit(LOG_DEBUG, "Flow %d/%d: " 1041 | "r %d offset %d ie %04x len %d(0x%04x)", 1042 | r, i, j, offset, 1043 | dh->set_id, dh->length, 1044 | dh->length); 1045 | } 1046 | } 1047 | /* Don't finish header if it has already been done */ 1048 | if (dh != NULL) { 1049 | if (offset % 4 != 0) { 1050 | /* Pad to multiple of 4 */ 1051 | dh->length += 4 - (offset % 4); 1052 | offset += 4 - (offset % 4); 1053 | } 1054 | /* Finalise last header */ 1055 | dh->length = htons(dh->length); 1056 | } 1057 | ipfix->length = htons(offset); 1058 | *records_sent += records; 1059 | ipfix->sequence = htonl((u_int32_t)(*records_sent & 0x00000000ffffffff)); 1060 | 1061 | if (verbose_flag) 1062 | logit(LOG_DEBUG, "Sending flow packet len = %d", offset); 1063 | errsz = sizeof(err); 1064 | /* Clear ICMP errors */ 1065 | getsockopt(nfsock, SOL_SOCKET, SO_ERROR, &err, &errsz); 1066 | if (send(nfsock, packet, (size_t)offset, 0) == -1) 1067 | return (-1); 1068 | num_packets++; 1069 | ipfix_pkts_until_template--; 1070 | 1071 | j += i; 1072 | } 1073 | 1074 | *flows_exported += j; 1075 | return (num_packets); 1076 | } 1077 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2004 Damien Miller All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include "common.h" 26 | #include "log.h" 27 | #include 28 | 29 | static int logstderr = 0; 30 | 31 | void 32 | loginit(const char *ident, int to_stderr) 33 | { 34 | if (to_stderr) 35 | logstderr = 1; 36 | else 37 | openlog(PROGNAME, LOG_PID|LOG_NDELAY, LOG_DAEMON); 38 | } 39 | 40 | void 41 | logit(int level, const char *fmt,...) 42 | { 43 | va_list args; 44 | 45 | va_start(args, fmt); 46 | 47 | if (logstderr) { 48 | vfprintf(stderr, fmt, args); 49 | fputs("\n", stderr); 50 | } else 51 | vsyslog(level, fmt, args); 52 | 53 | va_end(args); 54 | } 55 | 56 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2004 Damien Miller All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef _LOG_H 26 | #define _LOG_H 27 | 28 | void loginit(const char *ident, int to_stderr); 29 | void logit(int level, const char *fmt,...); 30 | 31 | #endif /* _LOG_H */ 32 | -------------------------------------------------------------------------------- /mkinstalldirs: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # mkinstalldirs --- make directory hierarchy 3 | # Author: Noah Friedman 4 | # Created: 1993-05-16 5 | # Public domain 6 | 7 | errstatus=0 8 | 9 | for file 10 | do 11 | set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` 12 | shift 13 | 14 | pathcomp= 15 | for d 16 | do 17 | pathcomp="$pathcomp$d" 18 | case "$pathcomp" in 19 | -* ) pathcomp=./$pathcomp ;; 20 | esac 21 | 22 | if test ! -d "$pathcomp"; then 23 | echo "mkdir $pathcomp" 24 | 25 | mkdir "$pathcomp" || lasterr=$? 26 | 27 | if test ! -d "$pathcomp"; then 28 | errstatus=$lasterr 29 | fi 30 | fi 31 | 32 | pathcomp="$pathcomp/" 33 | done 34 | done 35 | 36 | exit $errstatus 37 | 38 | # mkinstalldirs ends here 39 | -------------------------------------------------------------------------------- /netflow1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002 Damien Miller All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include "common.h" 26 | #include "log.h" 27 | #include "treetype.h" 28 | #include "softflowd.h" 29 | 30 | /* 31 | * This is the Cisco Netflow(tm) version 1 packet format 32 | * Based on: 33 | * http://www.cisco.com/en/US/products/sw/netmgtsw/ps1964/products_implementation_design_guide09186a00800d6a11.html 34 | */ 35 | struct NF1_HEADER { 36 | u_int16_t version, flows; 37 | u_int32_t uptime_ms, time_sec, time_nanosec; 38 | }; 39 | struct NF1_FLOW { 40 | u_int32_t src_ip, dest_ip, nexthop_ip; 41 | u_int16_t if_index_in, if_index_out; 42 | u_int32_t flow_packets, flow_octets; 43 | u_int32_t flow_start, flow_finish; 44 | u_int16_t src_port, dest_port; 45 | u_int16_t pad1; 46 | u_int8_t protocol, tos, tcp_flags; 47 | u_int8_t pad2, pad3, pad4; 48 | u_int32_t reserved1; 49 | #if 0 50 | u_int8_t reserved2; /* XXX: no longer used */ 51 | #endif 52 | }; 53 | /* Maximum of 24 flows per packet */ 54 | #define NF1_MAXFLOWS 24 55 | #define NF1_MAXPACKET_SIZE (sizeof(struct NF1_HEADER) + \ 56 | (NF1_MAXFLOWS * sizeof(struct NF1_FLOW))) 57 | 58 | /* 59 | * Given an array of expired flows, send netflow v1 report packets 60 | * Returns number of packets sent or -1 on error 61 | */ 62 | int 63 | send_netflow_v1(struct FLOW **flows, int num_flows, int nfsock, 64 | u_int16_t ifidx, struct FLOWTRACKPARAMETERS *param, 65 | int verbose_flag) 66 | { 67 | struct timeval now; 68 | u_int32_t uptime_ms; 69 | u_int8_t packet[NF1_MAXPACKET_SIZE]; /* Maximum allowed packet size (24 flows) */ 70 | struct NF1_HEADER *hdr = NULL; 71 | struct NF1_FLOW *flw = NULL; 72 | int i, j, offset, num_packets, err; 73 | socklen_t errsz; 74 | struct timeval *system_boot_time = ¶m->system_boot_time; 75 | u_int64_t *flows_exported = ¶m->flows_exported; 76 | 77 | gettimeofday(&now, NULL); 78 | uptime_ms = timeval_sub_ms(&now, system_boot_time); 79 | 80 | hdr = (struct NF1_HEADER *)packet; 81 | for(num_packets = offset = j = i = 0; i < num_flows; i++) { 82 | if (j >= NF1_MAXFLOWS - 1) { 83 | if (verbose_flag) 84 | logit(LOG_DEBUG, "Sending flow packet len = %d", offset); 85 | param->records_sent += hdr->flows; 86 | hdr->flows = htons(hdr->flows); 87 | errsz = sizeof(err); 88 | getsockopt(nfsock, SOL_SOCKET, SO_ERROR, 89 | &err, &errsz); /* Clear ICMP errors */ 90 | if (send(nfsock, packet, (size_t)offset, 0) == -1) 91 | return (-1); 92 | *flows_exported += j; 93 | j = 0; 94 | num_packets++; 95 | } 96 | if (j == 0) { 97 | memset(&packet, '\0', sizeof(packet)); 98 | hdr->version = htons(1); 99 | hdr->flows = 0; /* Filled in as we go */ 100 | hdr->uptime_ms = htonl(uptime_ms); 101 | hdr->time_sec = htonl(now.tv_sec); 102 | hdr->time_nanosec = htonl(now.tv_usec * 1000); 103 | offset = sizeof(*hdr); 104 | } 105 | 106 | flw = (struct NF1_FLOW *)(packet + offset); 107 | flw->if_index_in = flw->if_index_out = htons(ifidx); 108 | 109 | /* NetFlow v.1 doesn't do IPv6 */ 110 | if (flows[i]->af != AF_INET) 111 | continue; 112 | if (flows[i]->octets[0] > 0) { 113 | flw->src_ip = flows[i]->addr[0].v4.s_addr; 114 | flw->dest_ip = flows[i]->addr[1].v4.s_addr; 115 | flw->src_port = flows[i]->port[0]; 116 | flw->dest_port = flows[i]->port[1]; 117 | flw->flow_packets = htonl(flows[i]->packets[0]); 118 | flw->flow_octets = htonl(flows[i]->octets[0]); 119 | flw->flow_start = 120 | htonl(timeval_sub_ms(&flows[i]->flow_start, 121 | system_boot_time)); 122 | flw->flow_finish = 123 | htonl(timeval_sub_ms(&flows[i]->flow_last, 124 | system_boot_time)); 125 | flw->protocol = flows[i]->protocol; 126 | flw->tcp_flags = flows[i]->tcp_flags[0]; 127 | flw->tos = flows[i]->tos[0]; 128 | offset += sizeof(*flw); 129 | j++; 130 | hdr->flows++; 131 | } 132 | 133 | flw = (struct NF1_FLOW *)(packet + offset); 134 | flw->if_index_in = flw->if_index_out = htons(ifidx); 135 | if (flows[i]->octets[1] > 0) { 136 | flw->src_ip = flows[i]->addr[1].v4.s_addr; 137 | flw->dest_ip = flows[i]->addr[0].v4.s_addr; 138 | flw->src_port = flows[i]->port[1]; 139 | flw->dest_port = flows[i]->port[0]; 140 | flw->flow_packets = htonl(flows[i]->packets[1]); 141 | flw->flow_octets = htonl(flows[i]->octets[1]); 142 | flw->flow_start = 143 | htonl(timeval_sub_ms(&flows[i]->flow_start, 144 | system_boot_time)); 145 | flw->flow_finish = 146 | htonl(timeval_sub_ms(&flows[i]->flow_last, 147 | system_boot_time)); 148 | flw->protocol = flows[i]->protocol; 149 | flw->tcp_flags = flows[i]->tcp_flags[1]; 150 | flw->tos = flows[i]->tos[1]; 151 | offset += sizeof(*flw); 152 | j++; 153 | hdr->flows++; 154 | } 155 | } 156 | 157 | /* Send any leftovers */ 158 | if (j != 0) { 159 | if (verbose_flag) 160 | logit(LOG_DEBUG, "Sending flow packet len = %d", offset); 161 | param->records_sent += hdr->flows; 162 | hdr->flows = htons(hdr->flows); 163 | errsz = sizeof(err); 164 | getsockopt(nfsock, SOL_SOCKET, SO_ERROR, 165 | &err, &errsz); /* Clear ICMP errors */ 166 | if (send(nfsock, packet, (size_t)offset, 0) == -1) 167 | return (-1); 168 | num_packets++; 169 | } 170 | 171 | *flows_exported += j; 172 | return (num_packets); 173 | } 174 | -------------------------------------------------------------------------------- /netflow5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002 Damien Miller All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include "common.h" 26 | #include "log.h" 27 | #include "treetype.h" 28 | #include "softflowd.h" 29 | 30 | /* 31 | * This is the Cisco Netflow(tm) version 5 packet format 32 | * Based on: 33 | * http://www.cisco.com/en/US/products/sw/netmgtsw/ps1964/products_implementation_design_guide09186a00800d6a11.html 34 | */ 35 | struct NF5_HEADER { 36 | u_int16_t version, flows; 37 | u_int32_t uptime_ms, time_sec, time_nanosec, flow_sequence; 38 | u_int8_t engine_type, engine_id; 39 | u_int16_t sampling_interval; 40 | }; 41 | struct NF5_FLOW { 42 | u_int32_t src_ip, dest_ip, nexthop_ip; 43 | u_int16_t if_index_in, if_index_out; 44 | u_int32_t flow_packets, flow_octets; 45 | u_int32_t flow_start, flow_finish; 46 | u_int16_t src_port, dest_port; 47 | u_int8_t pad1; 48 | u_int8_t tcp_flags, protocol, tos; 49 | u_int16_t src_as, dest_as; 50 | u_int8_t src_mask, dst_mask; 51 | u_int16_t pad2; 52 | }; 53 | #define NF5_MAXFLOWS 30 54 | #define NF5_MAXPACKET_SIZE (sizeof(struct NF5_HEADER) + \ 55 | (NF5_MAXFLOWS * sizeof(struct NF5_FLOW))) 56 | 57 | /* 58 | * Given an array of expired flows, send netflow v5 report packets 59 | * Returns number of packets sent or -1 on error 60 | */ 61 | int 62 | send_netflow_v5(struct FLOW **flows, int num_flows, int nfsock, 63 | u_int16_t ifidx, struct FLOWTRACKPARAMETERS *param, 64 | int verbose_flag) 65 | { 66 | struct timeval now; 67 | u_int32_t uptime_ms; 68 | u_int8_t packet[NF5_MAXPACKET_SIZE]; /* Maximum allowed packet size (24 flows) */ 69 | struct NF5_HEADER *hdr = NULL; 70 | struct NF5_FLOW *flw = NULL; 71 | int i, j, offset, num_packets, err; 72 | socklen_t errsz; 73 | struct timeval *system_boot_time = ¶m->system_boot_time; 74 | u_int64_t *flows_exported = ¶m->flows_exported; 75 | struct OPTION *option = ¶m->option; 76 | 77 | gettimeofday(&now, NULL); 78 | uptime_ms = timeval_sub_ms(&now, system_boot_time); 79 | 80 | hdr = (struct NF5_HEADER *)packet; 81 | for (num_packets = offset = j = i = 0; i < num_flows; i++) { 82 | if (j >= NF5_MAXFLOWS - 1) { 83 | if (verbose_flag) 84 | logit(LOG_DEBUG, "Sending flow packet len = %d", offset); 85 | param->records_sent += hdr->flows; 86 | hdr->flows = htons(hdr->flows); 87 | errsz = sizeof(err); 88 | getsockopt(nfsock, SOL_SOCKET, SO_ERROR, 89 | &err, &errsz); /* Clear ICMP errors */ 90 | if (send(nfsock, packet, (size_t)offset, 0) == -1) 91 | return (-1); 92 | *flows_exported += j; 93 | j = 0; 94 | num_packets++; 95 | } 96 | if (j == 0) { 97 | memset(&packet, '\0', sizeof(packet)); 98 | hdr->version = htons(5); 99 | hdr->flows = 0; /* Filled in as we go */ 100 | hdr->uptime_ms = htonl(uptime_ms); 101 | hdr->time_sec = htonl(now.tv_sec); 102 | hdr->time_nanosec = htonl(now.tv_usec * 1000); 103 | hdr->flow_sequence = htonl(*flows_exported); 104 | if (option->sample > 0) { 105 | hdr->sampling_interval = 106 | htons((0x01 << 14) | (option->sample & 0x3FFF)); 107 | } 108 | /* Other fields are left zero */ 109 | offset = sizeof(*hdr); 110 | } 111 | flw = (struct NF5_FLOW *)(packet + offset); 112 | flw->if_index_in = flw->if_index_out = htons(ifidx); 113 | 114 | /* NetFlow v.5 doesn't do IPv6 */ 115 | if (flows[i]->af != AF_INET) 116 | continue; 117 | if (flows[i]->octets[0] > 0) { 118 | flw->src_ip = flows[i]->addr[0].v4.s_addr; 119 | flw->dest_ip = flows[i]->addr[1].v4.s_addr; 120 | flw->src_port = flows[i]->port[0]; 121 | flw->dest_port = flows[i]->port[1]; 122 | flw->flow_packets = htonl(flows[i]->packets[0]); 123 | flw->flow_octets = htonl(flows[i]->octets[0]); 124 | flw->flow_start = 125 | htonl(timeval_sub_ms(&flows[i]->flow_start, 126 | system_boot_time)); 127 | flw->flow_finish = 128 | htonl(timeval_sub_ms(&flows[i]->flow_last, 129 | system_boot_time)); 130 | flw->tcp_flags = flows[i]->tcp_flags[0]; 131 | flw->protocol = flows[i]->protocol; 132 | flw->tos = flows[i]->tos[0]; 133 | offset += sizeof(*flw); 134 | j++; 135 | hdr->flows++; 136 | } 137 | 138 | flw = (struct NF5_FLOW *)(packet + offset); 139 | flw->if_index_in = flw->if_index_out = htons(ifidx); 140 | 141 | if (flows[i]->octets[1] > 0) { 142 | flw->src_ip = flows[i]->addr[1].v4.s_addr; 143 | flw->dest_ip = flows[i]->addr[0].v4.s_addr; 144 | flw->src_port = flows[i]->port[1]; 145 | flw->dest_port = flows[i]->port[0]; 146 | flw->flow_packets = htonl(flows[i]->packets[1]); 147 | flw->flow_octets = htonl(flows[i]->octets[1]); 148 | flw->flow_start = 149 | htonl(timeval_sub_ms(&flows[i]->flow_start, 150 | system_boot_time)); 151 | flw->flow_finish = 152 | htonl(timeval_sub_ms(&flows[i]->flow_last, 153 | system_boot_time)); 154 | flw->tcp_flags = flows[i]->tcp_flags[1]; 155 | flw->protocol = flows[i]->protocol; 156 | flw->tos = flows[i]->tos[1]; 157 | offset += sizeof(*flw); 158 | j++; 159 | hdr->flows++; 160 | } 161 | } 162 | 163 | /* Send any leftovers */ 164 | if (j != 0) { 165 | if (verbose_flag) 166 | logit(LOG_DEBUG, "Sending v5 flow packet len = %d", 167 | offset); 168 | param->records_sent += hdr->flows; 169 | hdr->flows = htons(hdr->flows); 170 | errsz = sizeof(err); 171 | getsockopt(nfsock, SOL_SOCKET, SO_ERROR, 172 | &err, &errsz); /* Clear ICMP errors */ 173 | if (send(nfsock, packet, (size_t)offset, 0) == -1) 174 | return (-1); 175 | num_packets++; 176 | } 177 | 178 | *flows_exported += j; 179 | return (num_packets); 180 | } 181 | 182 | -------------------------------------------------------------------------------- /netflow9.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002 Damien Miller All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include "common.h" 26 | #include "log.h" 27 | #include "treetype.h" 28 | #include "softflowd.h" 29 | 30 | /* Netflow v.9 */ 31 | struct NF9_HEADER { 32 | u_int16_t version, flows; 33 | u_int32_t uptime_ms, time_sec; 34 | u_int32_t package_sequence, source_id; 35 | } __packed; 36 | struct NF9_FLOWSET_HEADER_COMMON { 37 | u_int16_t flowset_id, length; 38 | } __packed; 39 | struct NF9_TEMPLATE_FLOWSET_HEADER { 40 | struct NF9_FLOWSET_HEADER_COMMON c; 41 | u_int16_t template_id, count; 42 | } __packed; 43 | struct NF9_OPTION_TEMPLATE_FLOWSET_HEADER { 44 | struct NF9_FLOWSET_HEADER_COMMON c; 45 | u_int16_t template_id, scope_length, option_length; 46 | } __packed; 47 | struct NF9_TEMPLATE_FLOWSET_RECORD { 48 | u_int16_t type, length; 49 | } __packed; 50 | struct NF9_DATA_FLOWSET_HEADER { 51 | struct NF9_FLOWSET_HEADER_COMMON c; 52 | } __packed; 53 | #define NF9_TEMPLATE_FLOWSET_ID 0 54 | #define NF9_OPTIONS_FLOWSET_ID 1 55 | #define NF9_MIN_RECORD_FLOWSET_ID 256 56 | 57 | /* Flowset record types the we care about */ 58 | #define NF9_IN_BYTES 1 59 | #define NF9_IN_PACKETS 2 60 | /* ... */ 61 | #define NF9_PROTOCOL 4 62 | #define NF9_TOS 5 63 | /* ... */ 64 | #define NF9_TCP_FLAGS 6 65 | #define NF9_L4_SRC_PORT 7 66 | #define NF9_IPV4_SRC_ADDR 8 67 | /* ... */ 68 | #define NF9_IF_INDEX_IN 10 69 | #define NF9_L4_DST_PORT 11 70 | #define NF9_IPV4_DST_ADDR 12 71 | /* ... */ 72 | #define NF9_IF_INDEX_OUT 14 73 | /* ... */ 74 | #define NF9_LAST_SWITCHED 21 75 | #define NF9_FIRST_SWITCHED 22 76 | /* ... */ 77 | #define NF9_IPV6_SRC_ADDR 27 78 | #define NF9_IPV6_DST_ADDR 28 79 | /* ... */ 80 | #define NF9_ICMP_TYPE 32 81 | /* ... */ 82 | #define NF9_SAMPLING_INTERVAL 34 83 | #define NF9_SAMPLING_ALGORITHM 35 84 | /* ... */ 85 | #define NF9_SRC_VLAN 58 86 | /* ... */ 87 | #define NF9_IP_PROTOCOL_VERSION 60 88 | 89 | /* Stuff pertaining to the templates that softflowd uses */ 90 | #ifdef ENABLE_NF9_VLAN 91 | #define NF9_SOFTFLOWD_TEMPLATE_NRECORDS 16 92 | #else /* ENABLE_NF9_VLAN */ 93 | #define NF9_SOFTFLOWD_TEMPLATE_NRECORDS 14 94 | #endif /* ENABLE_NF9_VLAN */ 95 | struct NF9_SOFTFLOWD_TEMPLATE { 96 | struct NF9_TEMPLATE_FLOWSET_HEADER h; 97 | struct NF9_TEMPLATE_FLOWSET_RECORD r[NF9_SOFTFLOWD_TEMPLATE_NRECORDS]; 98 | } __packed; 99 | 100 | #define NF9_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS 1 101 | #define NF9_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS 2 102 | struct NF9_SOFTFLOWD_OPTION_TEMPLATE { 103 | struct NF9_OPTION_TEMPLATE_FLOWSET_HEADER h; 104 | struct NF9_TEMPLATE_FLOWSET_RECORD s[NF9_SOFTFLOWD_OPTION_TEMPLATE_SCOPE_RECORDS]; 105 | struct NF9_TEMPLATE_FLOWSET_RECORD r[NF9_SOFTFLOWD_OPTION_TEMPLATE_NRECORDS]; 106 | } __packed; 107 | 108 | /* softflowd data flowset types */ 109 | struct NF9_SOFTFLOWD_DATA_COMMON { 110 | u_int32_t last_switched, first_switched; 111 | u_int32_t bytes, packets; 112 | u_int32_t if_index_in, if_index_out; 113 | u_int16_t src_port, dst_port; 114 | u_int8_t protocol, tcp_flags, ipproto, tos; 115 | #ifdef ENABLE_NF9_VLAN 116 | u_int16_t icmp_type, vlanid; 117 | #endif /* ENABLE_NF9_VLAN */ 118 | } __packed; 119 | 120 | struct NF9_SOFTFLOWD_DATA_V4 { 121 | u_int32_t src_addr, dst_addr; 122 | struct NF9_SOFTFLOWD_DATA_COMMON c; 123 | } __packed; 124 | 125 | struct NF9_SOFTFLOWD_DATA_V6 { 126 | u_int8_t src_addr[16], dst_addr[16]; 127 | struct NF9_SOFTFLOWD_DATA_COMMON c; 128 | } __packed; 129 | 130 | struct NF9_SOFTFLOWD_OPTION_DATA { 131 | struct NF9_FLOWSET_HEADER_COMMON c; 132 | u_int32_t scope_ifidx; 133 | u_int32_t sampling_interval; 134 | u_int8_t sampling_algorithm; 135 | u_int8_t padding[3]; 136 | } __packed; 137 | 138 | /* Local data: templates and counters */ 139 | #define NF9_SOFTFLOWD_MAX_PACKET_SIZE 512 140 | #define NF9_SOFTFLOWD_V4_TEMPLATE_ID 1024 141 | #define NF9_SOFTFLOWD_V6_TEMPLATE_ID 2048 142 | #define NF9_SOFTFLOWD_OPTION_TEMPLATE_ID 256 143 | 144 | #define NF9_DEFAULT_TEMPLATE_INTERVAL 16 145 | 146 | /* ... */ 147 | #define NF9_OPTION_SCOPE_SYSTEM 1 148 | #define NF9_OPTION_SCOPE_INTERFACE 2 149 | #define NF9_OPTION_SCOPE_LINECARD 3 150 | #define NF9_OPTION_SCOPE_CACHE 4 151 | #define NF9_OPTION_SCOPE_TEMPLATE 5 152 | /* ... */ 153 | #define NF9_SAMPLING_ALGORITHM_DETERMINISTIC 1 154 | #define NF9_SAMPLING_ALGORITHM_RANDOM 2 155 | /* ... */ 156 | 157 | static struct NF9_SOFTFLOWD_TEMPLATE v4_template; 158 | static struct NF9_SOFTFLOWD_TEMPLATE v6_template; 159 | static struct NF9_SOFTFLOWD_OPTION_TEMPLATE option_template; 160 | static struct NF9_SOFTFLOWD_OPTION_DATA option_data; 161 | static int nf9_pkts_until_template = -1; 162 | 163 | static void 164 | nf9_init_template(void) 165 | { 166 | bzero(&v4_template, sizeof(v4_template)); 167 | v4_template.h.c.flowset_id = htons(NF9_TEMPLATE_FLOWSET_ID); 168 | v4_template.h.c.length = htons(sizeof(v4_template)); 169 | v4_template.h.template_id = htons(NF9_SOFTFLOWD_V4_TEMPLATE_ID); 170 | v4_template.h.count = htons(NF9_SOFTFLOWD_TEMPLATE_NRECORDS); 171 | v4_template.r[0].type = htons(NF9_IPV4_SRC_ADDR); 172 | v4_template.r[0].length = htons(4); 173 | v4_template.r[1].type = htons(NF9_IPV4_DST_ADDR); 174 | v4_template.r[1].length = htons(4); 175 | v4_template.r[2].type = htons(NF9_LAST_SWITCHED); 176 | v4_template.r[2].length = htons(4); 177 | v4_template.r[3].type = htons(NF9_FIRST_SWITCHED); 178 | v4_template.r[3].length = htons(4); 179 | v4_template.r[4].type = htons(NF9_IN_BYTES); 180 | v4_template.r[4].length = htons(4); 181 | v4_template.r[5].type = htons(NF9_IN_PACKETS); 182 | v4_template.r[5].length = htons(4); 183 | v4_template.r[6].type = htons(NF9_IF_INDEX_IN); 184 | v4_template.r[6].length = htons(4); 185 | v4_template.r[7].type = htons(NF9_IF_INDEX_OUT); 186 | v4_template.r[7].length = htons(4); 187 | v4_template.r[8].type = htons(NF9_L4_SRC_PORT); 188 | v4_template.r[8].length = htons(2); 189 | v4_template.r[9].type = htons(NF9_L4_DST_PORT); 190 | v4_template.r[9].length = htons(2); 191 | v4_template.r[10].type = htons(NF9_PROTOCOL); 192 | v4_template.r[10].length = htons(1); 193 | v4_template.r[11].type = htons(NF9_TCP_FLAGS); 194 | v4_template.r[11].length = htons(1); 195 | v4_template.r[12].type = htons(NF9_IP_PROTOCOL_VERSION); 196 | v4_template.r[12].length = htons(1); 197 | v4_template.r[13].type = htons(NF9_TOS); 198 | v4_template.r[13].length = htons(1); 199 | #ifdef ENABLE_NF9_VLAN 200 | v4_template.r[14].type = htons(NF9_ICMP_TYPE); 201 | v4_template.r[14].length = htons(2); 202 | v4_template.r[15].type = htons(NF9_SRC_VLAN); 203 | v4_template.r[15].length = htons(2); 204 | #endif /* ENABLE_NF9_VLAN */ 205 | bzero(&v6_template, sizeof(v6_template)); 206 | v6_template.h.c.flowset_id = htons(NF9_TEMPLATE_FLOWSET_ID); 207 | v6_template.h.c.length = htons(sizeof(v6_template)); 208 | v6_template.h.template_id = htons(NF9_SOFTFLOWD_V6_TEMPLATE_ID); 209 | v6_template.h.count = htons(NF9_SOFTFLOWD_TEMPLATE_NRECORDS); 210 | v6_template.r[0].type = htons(NF9_IPV6_SRC_ADDR); 211 | v6_template.r[0].length = htons(16); 212 | v6_template.r[1].type = htons(NF9_IPV6_DST_ADDR); 213 | v6_template.r[1].length = htons(16); 214 | v6_template.r[2].type = htons(NF9_LAST_SWITCHED); 215 | v6_template.r[2].length = htons(4); 216 | v6_template.r[3].type = htons(NF9_FIRST_SWITCHED); 217 | v6_template.r[3].length = htons(4); 218 | v6_template.r[4].type = htons(NF9_IN_BYTES); 219 | v6_template.r[4].length = htons(4); 220 | v6_template.r[5].type = htons(NF9_IN_PACKETS); 221 | v6_template.r[5].length = htons(4); 222 | v6_template.r[6].type = htons(NF9_IF_INDEX_IN); 223 | v6_template.r[6].length = htons(4); 224 | v6_template.r[7].type = htons(NF9_IF_INDEX_OUT); 225 | v6_template.r[7].length = htons(4); 226 | v6_template.r[8].type = htons(NF9_L4_SRC_PORT); 227 | v6_template.r[8].length = htons(2); 228 | v6_template.r[9].type = htons(NF9_L4_DST_PORT); 229 | v6_template.r[9].length = htons(2); 230 | v6_template.r[10].type = htons(NF9_PROTOCOL); 231 | v6_template.r[10].length = htons(1); 232 | v6_template.r[11].type = htons(NF9_TCP_FLAGS); 233 | v6_template.r[11].length = htons(1); 234 | v6_template.r[12].type = htons(NF9_IP_PROTOCOL_VERSION); 235 | v6_template.r[12].length = htons(1); 236 | v6_template.r[13].type = htons(NF9_TOS); 237 | v6_template.r[13].length = htons(1); 238 | #ifdef ENABLE_NF9_VLAN 239 | v6_template.r[14].type = htons(NF9_ICMP_TYPE); 240 | v6_template.r[14].length = htons(2); 241 | v6_template.r[15].type = htons(NF9_SRC_VLAN); 242 | v6_template.r[15].length = htons(2); 243 | #endif /* ENABLE_NF9_VLAN */ 244 | } 245 | 246 | static void 247 | nf9_init_option(u_int16_t ifidx, struct OPTION *option) { 248 | bzero(&option_template, sizeof(option_template)); 249 | option_template.h.c.flowset_id = htons(NF9_OPTIONS_FLOWSET_ID); 250 | option_template.h.c.length = htons(sizeof(option_template)); 251 | option_template.h.template_id = htons(NF9_SOFTFLOWD_OPTION_TEMPLATE_ID); 252 | option_template.h.scope_length = htons(sizeof(option_template.s)); 253 | option_template.h.option_length = htons(sizeof(option_template.r)); 254 | option_template.s[0].type = htons(NF9_OPTION_SCOPE_INTERFACE); 255 | option_template.s[0].length = htons(sizeof(option_data.scope_ifidx)); 256 | option_template.r[0].type = htons(NF9_SAMPLING_INTERVAL); 257 | option_template.r[0].length = htons(sizeof(option_data.sampling_interval)); 258 | option_template.r[1].type = htons(NF9_SAMPLING_ALGORITHM); 259 | option_template.r[1].length = htons(sizeof(option_data.sampling_algorithm)); 260 | 261 | bzero(&option_data, sizeof(option_data)); 262 | option_data.c.flowset_id = htons(NF9_SOFTFLOWD_OPTION_TEMPLATE_ID); 263 | option_data.c.length = htons(sizeof(option_data)); 264 | option_data.scope_ifidx = htonl(ifidx); 265 | option_data.sampling_interval = htonl(option->sample); 266 | option_data.sampling_algorithm = NF9_SAMPLING_ALGORITHM_DETERMINISTIC; 267 | } 268 | static int 269 | nf_flow_to_flowset(const struct FLOW *flow, u_char *packet, u_int len, 270 | u_int16_t ifidx, const struct timeval *system_boot_time, u_int *len_used) 271 | { 272 | union { 273 | struct NF9_SOFTFLOWD_DATA_V4 d4; 274 | struct NF9_SOFTFLOWD_DATA_V6 d6; 275 | } d[2]; 276 | struct NF9_SOFTFLOWD_DATA_COMMON *dc[2]; 277 | u_int freclen, ret_len, nflows; 278 | 279 | bzero(d, sizeof(d)); 280 | *len_used = nflows = ret_len = 0; 281 | switch (flow->af) { 282 | case AF_INET: 283 | freclen = sizeof(struct NF9_SOFTFLOWD_DATA_V4); 284 | memcpy(&d[0].d4.src_addr, &flow->addr[0].v4, 4); 285 | memcpy(&d[0].d4.dst_addr, &flow->addr[1].v4, 4); 286 | memcpy(&d[1].d4.src_addr, &flow->addr[1].v4, 4); 287 | memcpy(&d[1].d4.dst_addr, &flow->addr[0].v4, 4); 288 | dc[0] = &d[0].d4.c; 289 | dc[1] = &d[1].d4.c; 290 | dc[0]->ipproto = dc[1]->ipproto = 4; 291 | break; 292 | case AF_INET6: 293 | freclen = sizeof(struct NF9_SOFTFLOWD_DATA_V6); 294 | memcpy(&d[0].d6.src_addr, &flow->addr[0].v6, 16); 295 | memcpy(&d[0].d6.dst_addr, &flow->addr[1].v6, 16); 296 | memcpy(&d[1].d6.src_addr, &flow->addr[1].v6, 16); 297 | memcpy(&d[1].d6.dst_addr, &flow->addr[0].v6, 16); 298 | dc[0] = &d[0].d6.c; 299 | dc[1] = &d[1].d6.c; 300 | dc[0]->ipproto = dc[1]->ipproto = 6; 301 | break; 302 | default: 303 | return (-1); 304 | } 305 | 306 | dc[0]->first_switched = dc[1]->first_switched = 307 | htonl(timeval_sub_ms(&flow->flow_start, system_boot_time)); 308 | dc[0]->last_switched = dc[1]->last_switched = 309 | htonl(timeval_sub_ms(&flow->flow_last, system_boot_time)); 310 | dc[0]->bytes = htonl(flow->octets[0]); 311 | dc[1]->bytes = htonl(flow->octets[1]); 312 | dc[0]->packets = htonl(flow->packets[0]); 313 | dc[1]->packets = htonl(flow->packets[1]); 314 | dc[0]->if_index_in = dc[0]->if_index_out = htonl(ifidx); 315 | dc[1]->if_index_in = dc[1]->if_index_out = htonl(ifidx); 316 | dc[0]->src_port = dc[1]->dst_port = flow->port[0]; 317 | dc[1]->src_port = dc[0]->dst_port = flow->port[1]; 318 | dc[0]->protocol = dc[1]->protocol = flow->protocol; 319 | dc[0]->tcp_flags = flow->tcp_flags[0]; 320 | dc[1]->tcp_flags = flow->tcp_flags[1]; 321 | dc[0]->tos = flow->tos[0]; 322 | dc[1]->tos = flow->tos[1]; 323 | #ifdef ENABLE_NF9_VLAN 324 | if (flow->protocol == IPPROTO_ICMP || flow->protocol == IPPROTO_ICMPV6) { 325 | dc[0]->icmp_type = dc[0]->dst_port; 326 | dc[1]->icmp_type = dc[1]->dst_port; 327 | } 328 | dc[0]->vlanid = dc[1]->vlanid = htons(flow->vlanid); 329 | #endif /* ENABLE_NF9_VLAN */ 330 | if (flow->octets[0] > 0) { 331 | if (ret_len + freclen > len) 332 | return (-1); 333 | memcpy(packet + ret_len, &d[0], freclen); 334 | ret_len += freclen; 335 | nflows++; 336 | } 337 | if (flow->octets[1] > 0) { 338 | if (ret_len + freclen > len) 339 | return (-1); 340 | memcpy(packet + ret_len, &d[1], freclen); 341 | ret_len += freclen; 342 | nflows++; 343 | } 344 | 345 | *len_used = ret_len; 346 | return (nflows); 347 | } 348 | 349 | /* 350 | * Given an array of expired flows, send netflow v9 report packets 351 | * Returns number of packets sent or -1 on error 352 | */ 353 | int 354 | send_netflow_v9(struct FLOW **flows, int num_flows, int nfsock, 355 | u_int16_t ifidx, struct FLOWTRACKPARAMETERS *param, 356 | int verbose_flag) 357 | { 358 | struct NF9_HEADER *nf9; 359 | struct NF9_DATA_FLOWSET_HEADER *dh; 360 | struct timeval now; 361 | u_int offset, last_af, i, j, num_packets, inc, last_valid; 362 | socklen_t errsz; 363 | int err, r; 364 | u_char packet[NF9_SOFTFLOWD_MAX_PACKET_SIZE]; 365 | struct timeval *system_boot_time = ¶m->system_boot_time; 366 | u_int64_t *flows_exported = ¶m->flows_exported; 367 | u_int64_t *packets_sent = ¶m->packets_sent; 368 | struct OPTION *option = ¶m->option; 369 | 370 | gettimeofday(&now, NULL); 371 | 372 | if (nf9_pkts_until_template == -1) { 373 | nf9_init_template(); 374 | nf9_pkts_until_template = 0; 375 | if (option != NULL && option->sample > 1){ 376 | nf9_init_option(ifidx, option); 377 | } 378 | } 379 | 380 | last_valid = num_packets = 0; 381 | for (j = 0; j < num_flows;) { 382 | bzero(packet, sizeof(packet)); 383 | nf9 = (struct NF9_HEADER *)packet; 384 | 385 | nf9->version = htons(9); 386 | nf9->flows = 0; /* Filled as we go, htons at end */ 387 | nf9->uptime_ms = htonl(timeval_sub_ms(&now, system_boot_time)); 388 | nf9->time_sec = htonl(time(NULL)); 389 | nf9->source_id = 0; 390 | offset = sizeof(*nf9); 391 | 392 | /* Refresh template headers if we need to */ 393 | if (nf9_pkts_until_template <= 0) { 394 | memcpy(packet + offset, &v4_template, 395 | sizeof(v4_template)); 396 | offset += sizeof(v4_template); 397 | nf9->flows++; 398 | memcpy(packet + offset, &v6_template, 399 | sizeof(v6_template)); 400 | offset += sizeof(v6_template); 401 | nf9->flows++; 402 | if (option != NULL && option->sample > 1){ 403 | memcpy(packet + offset, &option_template, 404 | sizeof(option_template)); 405 | offset += sizeof(option_template); 406 | nf9->flows++; 407 | memcpy(packet + offset, &option_data, 408 | sizeof(option_data)); 409 | offset += sizeof(option_data); 410 | nf9->flows++; 411 | } 412 | 413 | nf9_pkts_until_template = NF9_DEFAULT_TEMPLATE_INTERVAL; 414 | } 415 | 416 | dh = NULL; 417 | last_af = 0; 418 | for (i = 0; i + j < num_flows; i++) { 419 | if (dh == NULL || flows[i + j]->af != last_af) { 420 | if (dh != NULL) { 421 | if (offset % 4 != 0) { 422 | /* Pad to multiple of 4 */ 423 | dh->c.length += 4 - (offset % 4); 424 | offset += 4 - (offset % 4); 425 | } 426 | /* Finalise last header */ 427 | dh->c.length = htons(dh->c.length); 428 | } 429 | if (offset + sizeof(*dh) > sizeof(packet)) { 430 | /* Mark header is finished */ 431 | dh = NULL; 432 | break; 433 | } 434 | dh = (struct NF9_DATA_FLOWSET_HEADER *) 435 | (packet + offset); 436 | dh->c.flowset_id = 437 | (flows[i + j]->af == AF_INET) ? 438 | v4_template.h.template_id : 439 | v6_template.h.template_id; 440 | last_af = flows[i + j]->af; 441 | last_valid = offset; 442 | dh->c.length = sizeof(*dh); /* Filled as we go */ 443 | offset += sizeof(*dh); 444 | } 445 | 446 | r = nf_flow_to_flowset(flows[i + j], packet + offset, 447 | sizeof(packet) - offset, ifidx, system_boot_time, &inc); 448 | if (r <= 0) { 449 | /* yank off data header, if we had to go back */ 450 | if (last_valid) 451 | offset = last_valid; 452 | break; 453 | } 454 | offset += inc; 455 | dh->c.length += inc; 456 | nf9->flows += r; 457 | last_valid = 0; /* Don't clobber this header now */ 458 | if (verbose_flag) { 459 | logit(LOG_DEBUG, "Flow %d/%d: " 460 | "r %d offset %d type %04x len %d(0x%04x) " 461 | "flows %d", r, i, j, offset, 462 | dh->c.flowset_id, dh->c.length, 463 | dh->c.length, nf9->flows); 464 | } 465 | } 466 | /* Don't finish header if it has already been done */ 467 | if (dh != NULL) { 468 | if (offset % 4 != 0) { 469 | /* Pad to multiple of 4 */ 470 | dh->c.length += 4 - (offset % 4); 471 | offset += 4 - (offset % 4); 472 | } 473 | /* Finalise last header */ 474 | dh->c.length = htons(dh->c.length); 475 | } 476 | param->records_sent += nf9->flows; 477 | nf9->flows = htons(nf9->flows); 478 | nf9->package_sequence = htonl((u_int32_t)((*packets_sent + num_packets + 1) & 0x00000000ffffffff)); 479 | 480 | if (verbose_flag) 481 | logit(LOG_DEBUG, "Sending flow packet len = %d", offset); 482 | errsz = sizeof(err); 483 | /* Clear ICMP errors */ 484 | getsockopt(nfsock, SOL_SOCKET, SO_ERROR, &err, &errsz); 485 | if (send(nfsock, packet, (size_t)offset, 0) == -1) 486 | return (-1); 487 | num_packets++; 488 | nf9_pkts_until_template--; 489 | 490 | j += i; 491 | } 492 | 493 | *flows_exported += j; 494 | return (num_packets); 495 | } 496 | 497 | void 498 | netflow9_resend_template(void) 499 | { 500 | if (nf9_pkts_until_template > 0) 501 | nf9_pkts_until_template = 0; 502 | } 503 | -------------------------------------------------------------------------------- /softflowctl.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2002 Damien Miller. All rights reserved. 2 | .\" 3 | .\" Redistribution and use in source and binary forms, with or without 4 | .\" modification, are permitted provided that the following conditions 5 | .\" are met: 6 | .\" 1. Redistributions of source code must retain the above copyright 7 | .\" notice, this list of conditions and the following disclaimer. 8 | .\" 2. Redistributions in binary form must reproduce the above copyright 9 | .\" notice, this list of conditions and the following disclaimer in the 10 | .\" documentation and/or other materials provided with the distribution. 11 | .\" 12 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 13 | .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 14 | .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 15 | .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 16 | .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 17 | .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 18 | .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 19 | .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 21 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | .\" 23 | .Dd October 18, 2002 24 | .Dt SOFTFLOWCTL 8 25 | .Os 26 | .Sh NAME 27 | .Nm softflowctl 28 | .Nd Remote control program for softflowd 29 | .Sh SYNOPSIS 30 | .Nm softflowctl 31 | .Op Fl h 32 | .Op Fl c Ar ctl_sock 33 | .Ar command 34 | .Sh DESCRIPTION 35 | .Nm 36 | is a remote control program used to control a running 37 | .Xr softflowd 8 38 | daemon. 39 | .Pp 40 | The command line options are as follows: 41 | .Bl -tag -width Ds 42 | .It Fl c Ar ctlsock 43 | Specify an alternate location for the remote control socket. 44 | Default is 45 | .Pa /var/run/softflowd.ctl 46 | .It Fl h 47 | Display command line usage information. 48 | .El 49 | .Pp 50 | .Sh COMMANDS 51 | .Bl -tag -width Ds 52 | .It Pa shutdown 53 | Ask 54 | .Xr softflowd 8 55 | to gracefully exit. 56 | This is equivalent to sending it a 57 | .Dv SIGTERM 58 | or 59 | .Dv SIGINT . 60 | .It Pa exit 61 | Ask 62 | .Xr softflowd 8 63 | to immediately exit. 64 | No flow expiry processing or data export is performed. 65 | .It Pa expire-all 66 | Immediately expire all tracked flows. 67 | .It Pa delete-all 68 | Immediately delete all tracked flows. 69 | No flow expiry processing or data export is performed. 70 | .It Pa statistics 71 | Return statistics collected by 72 | .Xr softflowd 8 73 | on expired flows. 74 | .It Pa debug+ 75 | Increase the debugging level of 76 | .Xr softflowd 8 77 | .It Pa debug- 78 | Decrease the debugging level. 79 | .It Pa stop-gather 80 | Stops network data collection by 81 | .Xr softflowd 8 . 82 | .It Pa start-gather 83 | Resumes network data collection. 84 | .It Pa dump-flows 85 | Return information on all tracked flows. 86 | .It Pa timeouts 87 | Print information on flow timeout parameters. 88 | .It Pa send-template 89 | Resend a NetFlow v.9 template record before the next flow export. 90 | Has no effect for other flow export versions. 91 | .El 92 | .Sh BUGS 93 | All times are unconditionally displayed in UTC, regardless of the system 94 | timezone. 95 | .Sh AUTHORS 96 | .An Damien Miller Aq djm@mindrot.org 97 | .Sh SEE ALSO 98 | .Xr softflowd 8 99 | -------------------------------------------------------------------------------- /softflowctl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002 Damien Miller All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #include "common.h" 26 | 27 | static void 28 | usage(void) 29 | { 30 | fprintf(stderr, "Usage: [-c ctlsock] softflowctl [command]\n"); 31 | } 32 | 33 | int 34 | main(int argc, char **argv) 35 | { 36 | const char *ctlsock_path; 37 | char buf[8192], *command; 38 | struct sockaddr_un ctl; 39 | #ifdef SOCK_HAS_LEN 40 | socklen_t ctllen; 41 | #endif 42 | int ctlsock, ch; 43 | FILE *ctlf; 44 | extern char *optarg; 45 | extern int optind; 46 | 47 | ctlsock_path = DEFAULT_CTLSOCK; 48 | while ((ch = getopt(argc, argv, "hc:")) != -1) { 49 | switch (ch) { 50 | case 'h': 51 | usage(); 52 | return (0); 53 | case 'c': 54 | ctlsock_path = optarg; 55 | break; 56 | default: 57 | fprintf(stderr, "Invalid commandline option.\n"); 58 | usage(); 59 | exit(1); 60 | } 61 | } 62 | 63 | /* Accept only one argument */ 64 | if (optind != argc - 1) { 65 | usage(); 66 | exit(1); 67 | } 68 | command = argv[optind]; 69 | 70 | memset(&ctl, '\0', sizeof(ctl)); 71 | if (strlcpy(ctl.sun_path, ctlsock_path, sizeof(ctl.sun_path)) >= 72 | sizeof(ctl.sun_path)) { 73 | fprintf(stderr, "Control socket path too long.\n"); 74 | exit(1); 75 | } 76 | ctl.sun_path[sizeof(ctl.sun_path) - 1] = '\0'; 77 | ctl.sun_family = AF_UNIX; 78 | #ifdef SOCK_HAS_LEN 79 | ctllen = offsetof(struct sockaddr_un, sun_path) + 80 | strlen(ctlsock_path) + 1; 81 | ctl.sun_len = ctllen; 82 | #endif 83 | if ((ctlsock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { 84 | fprintf(stderr, "ctl socket() error: %s\n", 85 | strerror(errno)); 86 | exit(1); 87 | } 88 | if (connect(ctlsock, (struct sockaddr*)&ctl, sizeof(ctl)) == -1) { 89 | fprintf(stderr, "ctl connect(\"%s\") error: %s\n", 90 | ctl.sun_path, strerror(errno)); 91 | exit(1); 92 | } 93 | 94 | if ((ctlf = fdopen(ctlsock, "r+")) == NULL) { 95 | fprintf(stderr, "fdopen: %s\n", strerror(errno)); 96 | exit(1); 97 | } 98 | setlinebuf(ctlf); 99 | 100 | /* Send command */ 101 | if (fprintf(ctlf, "%s\n", command) < 0) { 102 | fprintf(stderr, "write: %s\n", strerror(errno)); 103 | exit(1); 104 | } 105 | 106 | /* Write out reply */ 107 | while((fgets(buf, sizeof(buf), ctlf)) != NULL) 108 | fputs(buf, stdout); 109 | 110 | fclose(ctlf); 111 | close(ctlsock); 112 | 113 | exit(0); 114 | } 115 | -------------------------------------------------------------------------------- /softflowd.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2002 Damien Miller. All rights reserved. 2 | .\" Portions Copyright (c) 2001 Kevin Steves. All rights reserved. 3 | .\" 4 | .\" Redistribution and use in source and binary forms, with or without 5 | .\" modification, are permitted provided that the following conditions 6 | .\" are met: 7 | .\" 1. Redistributions of source code must retain the above copyright 8 | .\" notice, this list of conditions and the following disclaimer. 9 | .\" 2. Redistributions in binary form must reproduce the above copyright 10 | .\" notice, this list of conditions and the following disclaimer in the 11 | .\" documentation and/or other materials provided with the distribution. 12 | .\" 13 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | .\" 24 | .Dd October 14, 2002 25 | .Dt SOFTFLOWD 8 26 | .Os 27 | .Sh NAME 28 | .Nm softflowd 29 | .Nd Traffic flow monitoring 30 | .Sh SYNOPSIS 31 | .Nm softflowd 32 | .Op Fl 6dDh 33 | .Op Fl L Ar hoplimit 34 | .Op Fl T Ar track_level 35 | .Op Fl c Ar ctl_sock 36 | .Bk -words 37 | .Oo Fl i\ \& 38 | .Sm off 39 | .Oo Ar if_ndx : Oc 40 | .Ar interface 41 | .Sm on 42 | .Oc 43 | .Ek 44 | .Op Fl m Ar max_flows 45 | .Op Fl n Ar host:port 46 | .Op Fl p Ar pidfile 47 | .Op Fl r Ar pcap_file 48 | .Op Fl t Ar timeout_name=seconds 49 | .Op Fl v Ar netflow_version 50 | .Op Fl s Ar sampling_rate 51 | .Op bpf_expression 52 | .Sh DESCRIPTION 53 | .Nm 54 | is a software implementation of a flow-based network traffic monitor. 55 | .Nm 56 | reads network traffic and gathers information about active traffic flows. 57 | A "traffic flow" is communication between two IP addresses or (if the 58 | overlying protocol is TCP or UDP) address/port tuples. 59 | .Pp 60 | The intended use of 61 | .Nm 62 | is as a software implementation of Cisco's NetFlow(tm) traffic account 63 | system. 64 | .Nm 65 | supports data export using versions 1, 5 or 9 of the NetFlow protocol. 66 | .Nm 67 | can also run in statistics-only mode, where it just collects summary 68 | information. 69 | However, too few statistics are collected to make this 70 | mode really useful for anything other than debugging. 71 | .Pp 72 | Network traffic may be obtained by listening on a promiscuous network 73 | interface or by reading stored 74 | .Xr pcap 3 75 | files, such as those written by 76 | .Xr tcpdump 8 . 77 | Traffic may be filtered with an optional 78 | .Xr bpf 4 79 | program, specified on the command-line as 80 | .Ar bpf_expression . 81 | .Nm 82 | is IPv6 capable and will track IPv6 flows if the NetFlow export protocol 83 | supports it (currently only NetFlow v.9 possesses an IPv6 export capability). 84 | .Pp 85 | .Nm 86 | tries to track only active traffic flows. 87 | When the 88 | flow has been quiescent for a period of time it is expired automatically. 89 | Flows may also be expired early if they approach their traffic counts 90 | exceed 2 Gib or if the number of flows being tracked exceeds 91 | .Ar max_flows 92 | (default: 8192). 93 | In this last case, flows are expired oldest-first. 94 | .Pp 95 | Upon expiry, the flow information is accumulated into statistics which may 96 | be viewed using 97 | .Xr softflowctl 8 . 98 | If the 99 | .Fl n 100 | option has been specified the flow information is formatted in a UDP datagram 101 | which is compatible with versions 1, 5 or 9 of Cisco's NetFlow(tm) accounting 102 | export format. 103 | These records are sent to the specified 104 | .Ar host 105 | and 106 | .Ar port . 107 | The host may represent a unicast host or a multicast group. 108 | .Pp 109 | The command-line options are as follows: 110 | .Bl -tag -width Ds 111 | .It Fl n Ar host:port 112 | Specify the 113 | .Ar host 114 | and 115 | .Ar port 116 | that the accounting datagrams are to be sent to. 117 | The host may be specified using a hostname or using a numeric IPv4 or 118 | IPv6 address. 119 | Numeric IPv6 addresses should be encosed in square brackets to avoid ambiguity 120 | between the address and the port. 121 | The destination port may be a portname listed in 122 | .Xr services 5 123 | or a numeric port. 124 | .It Fl i Xo 125 | .Sm off 126 | .Oo Ar if_ndx : Oc 127 | .Ar interface 128 | .Sm on 129 | .Xc 130 | Specify a network interface on which to listen for traffic. 131 | Either the 132 | .Fl i 133 | or the 134 | .Fl r 135 | options must be specified. 136 | .It Fl r Ar pcap_file 137 | Specify that 138 | .Nm 139 | should read from a 140 | .Xr pcap 3 141 | packet capture file (such as one created with the 142 | .Fl w 143 | option of 144 | .Xr tcpdump 8 ) 145 | file rather than a network interface. 146 | .Nm 147 | processes the whole capture file and only expires flows when 148 | .Ar max_flows 149 | is exceeded. 150 | In this mode, 151 | .Nm 152 | will not fork and will automatically print summary statistics before 153 | exiting. 154 | .It Fl p Ar pidfile 155 | Specify an alternate location to store the process ID when in daemon mode. 156 | Default is 157 | .Pa /var/run/softflowd.pid 158 | .It Fl c Ar ctlsock 159 | Specify an alternate location for the remote control socket in daemon mode. 160 | Default is 161 | .Pa /var/run/softflowd.ctl 162 | .It Fl m Ar max_flows 163 | Specify the maximum number of flows to concurrently track. 164 | If this limit is exceeded, the flows which have least recently seen traffic 165 | are forcibly expired. 166 | In practice, the actual maximum may briefly exceed this limit by a 167 | small amount as expiry processing happens less frequently than traffic 168 | collection. 169 | The default is 8192 flows, which corresponds to slightly less 170 | than 800k of working data. 171 | .It Fl t Ar timeout_name=time 172 | Set the timeout names 173 | .Ar timeout_name 174 | to 175 | .Ar time . 176 | Refer to the 177 | .Sx Timeouts 178 | section for the valid timeout names and their meanings. 179 | The 180 | .Ar time 181 | parameter may be specified using one of the formats explained in the 182 | .Sx Time Formats 183 | section below. 184 | .It Fl d 185 | Specify that 186 | .Nm 187 | should not fork and daemonise itself. 188 | .It Fl 6 189 | Force 190 | .Nm 191 | to track IPv6 flows even if the NetFlow export protocol does not support 192 | reporting them. 193 | This is useful for debugging and statistics gathering only. 194 | .It Fl D 195 | Places 196 | .Nm 197 | in a debugging mode. 198 | This implies the 199 | .Fl d 200 | and 201 | .Fl 6 202 | flags and turns on additional debugging output. 203 | .It Fl h 204 | Display command-line usage information. 205 | .It Fl L Ar hoplimit 206 | Set the IPv4 TTL or the IPv6 hop limit to 207 | .Ar hoplimit . 208 | .Nm 209 | will use the default system TTL when exporting flows to a unicast host. 210 | When exporting to a multicast group, the default TTL will be 1 211 | (i.e. link-local). 212 | .It Fl T Ar track_level 213 | Specify which flow elements 214 | .Nm 215 | should be used to define a flow. 216 | .Ar track_level 217 | may be one of: 218 | .Dq full 219 | (track everything in the flow, the default), 220 | .Dq proto 221 | (track source and destination addresses and protocol), or 222 | .Dq ip 223 | (only track source and destination addresses). 224 | Selecting either of the latter options will produce flows with less information 225 | in them (e.g. TCP/UDP ports will not be recorded). 226 | This will cause flows to be consolidated, reducing the quantity of output 227 | and CPU load that 228 | .Nm 229 | will place on the system at the cost of some detail being lost. 230 | .It Fl v Ar netflow_version 231 | Specify which version of the NetFlow(tm) protocol 232 | .Nm 233 | should use for export of the flow data. 234 | Supported versions are 1, 5 and 9. 235 | Default is version 5. 236 | .It Fl s Ar sampling_rate 237 | Specify periodical sampling rate (denominator). 238 | .El 239 | .Pp 240 | Any further command-line arguments will be concatenated together and 241 | applied as a 242 | .Xr bpf 4 243 | packet filter. 244 | This filter will cause 245 | .Nm 246 | to ignore the specified traffic. 247 | .Ss Timeouts 248 | .Pp 249 | .Nm 250 | will expire quiescent flows after user-configurable periods. 251 | The exact timeout used depends on the nature of the flow. 252 | The various timeouts that may be set from the command-line (using the 253 | .Fl t 254 | option) and their meanings are: 255 | .Bl -tag -width Ds 256 | .It Ar general 257 | This is the general timeout applied to all traffic unless overridden by 258 | one of the other timeouts. 259 | .It Ar tcp 260 | This is the general TCP timeout, applied to open TCP connections. 261 | .It Ar tcp.rst 262 | This timeout is applied to a TCP connection when a RST packet has been 263 | sent by one or both endpoints. 264 | .It Ar tcp.fin 265 | This timeout is applied to a TCP connection when a FIN packet has been 266 | sent by both endpoints. 267 | .It Ar udp 268 | This is the general UDP timeout, applied to all UDP connections. 269 | .It Ar maxlife 270 | This is the maximum lifetime that a flow may exist for. 271 | All flows are forcibly expired when they pass 272 | .Ar maxlife 273 | seconds. 274 | To disable this feature, specify a 275 | .Ar maxlife 276 | of 0. 277 | .It Ar expint 278 | Specify the interval between expiry checks. 279 | Increase this to group more flows into a NetFlow packet. 280 | To disable this feature, specify a 281 | .Ar expint 282 | of 0. 283 | .El 284 | .Pp 285 | Flows may also be expired if there are not enough flow entries to hold them 286 | or if their traffic exceeds 2 Gib in either direction. 287 | .Xr softflowctl 8 288 | may be used to print information on the average lifetimes of flows and 289 | the reasons for their expiry. 290 | .Ss Time Formats 291 | .Pp 292 | .Nm 293 | command-line arguments that specify time may be expressed using a sequence 294 | of the form: 295 | .Sm off 296 | .Ar time Op Ar qualifier , 297 | .Sm on 298 | where 299 | .Ar time 300 | is a positive integer value and 301 | .Ar qualifier 302 | is one of the following: 303 | .Pp 304 | .Bl -tag -width Ds -compact -offset indent 305 | .It Cm 306 | seconds 307 | .It Cm s | Cm S 308 | seconds 309 | .It Cm m | Cm M 310 | minutes 311 | .It Cm h | Cm H 312 | hours 313 | .It Cm d | Cm D 314 | days 315 | .It Cm w | Cm W 316 | weeks 317 | .El 318 | .Pp 319 | Each member of the sequence is added together to calculate the total time value. 320 | .Pp 321 | Time format examples: 322 | .Pp 323 | .Bl -tag -width Ds -compact -offset indent 324 | .It 600 325 | 600 seconds (10 minutes) 326 | .It 10m 327 | 10 minutes 328 | .It 1h30m 329 | 1 hour 30 minutes (90 minutes) 330 | .El 331 | .Ss Run-time Control 332 | .Pp 333 | A daemonised 334 | .Nm 335 | instance may be controlled using the 336 | .Xr softflowctl 8 337 | command. 338 | This interface allows one to shut down the daemon, force expiry of 339 | all tracked flows and extract debugging and summary data. 340 | Also, receipt of a 341 | .Dv SIGTERM 342 | or 343 | .Dv SIGINT 344 | will cause 345 | .Nm 346 | to exit, after expiring all flows (and thus sending flow export packets 347 | if 348 | .Fl n 349 | was specified on the command-line). 350 | If you do not want to export flows upon shutdown, clear them first with 351 | .Xr softflowctl 8 352 | or use 353 | .Xr softflowctl 8 's 354 | .Dq exit 355 | command. 356 | .Sh EXAMPLES 357 | .Bl -tag -width Ds 358 | .It softflowd -i fxp0 359 | This command-line will cause 360 | .Nm 361 | to listen on interface fxp0 and to run in statistics gathering mode 362 | only (i.e. no NetFlow data export). 363 | .It softflowd -i fxp0 -n 10.1.0.2:4432 364 | This command-line will cause 365 | .Nm 366 | to listen on interface fxp0 and to export NetFlow v.5 datagrams on flow 367 | expiry to a flow collector running on 10.1.0.2 port 4432. 368 | .It softflowd -v 5 -i fxp0 -n 10.1.0.2:4432 -m 65536 -t udp=1m30s 369 | This command-line increases the number of concurrent flows that 370 | .Nm 371 | will track to 65536 and increases the timeout for UDP flows to 90 seconds. 372 | .It softflowd -v 9 -i fxp0 -n 224.0.1.20:4432 -L 64 373 | This command-line will export NetFlow v.9 flows to the multicast group 374 | 224.0.1.20. 375 | The export datagrams will have their TTL set to 64, so multicast receivers 376 | can be many hops away. 377 | .It softflowd -i fxp0 -p /var/run/sfd.pid.fxp0 -c /var/run/sfd.ctl.fxp0 378 | This command-line specifies alternate locations for the control socket 379 | and pid file. 380 | Similar command-lines are useful when running multiple 381 | instances of 382 | .Nm 383 | on a single machine. 384 | .El 385 | .Sh FILES 386 | .Bl -tag -width Ds 387 | .It Pa /var/run/softflowd.pid 388 | This file stores the process ID when 389 | .Nm 390 | is in daemon mode. 391 | This location may be overridden using the 392 | .Fl p 393 | command-line option. 394 | .It Pa /var/run/softflowd.ctl 395 | This is the remote control socket. 396 | .Nm 397 | listens on this socket for commands from 398 | .Xr softflowctl 8 . 399 | This location may be overridden using the 400 | .Fl c 401 | command-line option. 402 | .El 403 | .Sh BUGS 404 | Currently 405 | .Nm 406 | does not handle maliciously fragmented packets properly, i.e. packets 407 | fragemented such that the UDP or TCP header does not fit into the first 408 | fragment. 409 | It will product correct traffic counts when presented with maliciously 410 | fragmented packets, but will not record TCP or UDP port information. 411 | .Sh AUTHORS 412 | .An Damien Miller Aq djm@mindrot.org 413 | .Sh SEE ALSO 414 | .Xr softflowctl 8 , 415 | .Xr tcpdump 8 , 416 | .Xr pcap 3 , 417 | .Xr bpf 4 418 | .Bd -literal 419 | http://www.ietf.org/rfc/rfc3954.txt 420 | .br 421 | http://www.cisco.com/en/US/products/sw/netmgtsw/ps1964/products_implementation_design_guide09186a00800d6a11.html 422 | .Ed 423 | -------------------------------------------------------------------------------- /softflowd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002 Damien Miller. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | #ifndef _SOFTFLOWD_H 26 | #define _SOFTFLOWD_H 27 | 28 | #include "common.h" 29 | #include "sys-tree.h" 30 | #include "freelist.h" 31 | #include "treetype.h" 32 | 33 | /* User to setuid to and directory to chroot to when we drop privs */ 34 | #ifndef PRIVDROP_USER 35 | # define PRIVDROP_USER "nobody" 36 | #endif 37 | 38 | #ifndef PRIVDROP_CHROOT_DIR 39 | # define PRIVDROP_CHROOT_DIR "/var/empty" 40 | #endif 41 | /* 42 | * Capture length for libpcap: Must fit the link layer header, plus 43 | * a maximally sized ip/ipv6 header and most of a TCP header 44 | */ 45 | #define LIBPCAP_SNAPLEN_V4 96 46 | #define LIBPCAP_SNAPLEN_V6 160 47 | 48 | /* 49 | * Timeouts 50 | */ 51 | #define DEFAULT_TCP_TIMEOUT 3600 52 | #define DEFAULT_TCP_RST_TIMEOUT 120 53 | #define DEFAULT_TCP_FIN_TIMEOUT 300 54 | #define DEFAULT_UDP_TIMEOUT 300 55 | #define DEFAULT_ICMP_TIMEOUT 300 56 | #define DEFAULT_GENERAL_TIMEOUT 3600 57 | #define DEFAULT_MAXIMUM_LIFETIME (3600*24*7) 58 | #define DEFAULT_EXPIRY_INTERVAL 60 59 | 60 | /* 61 | * Default maximum number of flow to track simultaneously 62 | * 8192 corresponds to just under 1Mb of flow data 63 | */ 64 | #define DEFAULT_MAX_FLOWS 8192 65 | 66 | /* Store a couple of statistics, maybe more in the future */ 67 | struct STATISTIC { 68 | double min, mean, max; 69 | }; 70 | 71 | /* Flow tracking levels */ 72 | #define TRACK_FULL 1 /* src/dst/addr/port/proto/tos 6-tuple */ 73 | #define TRACK_IP_PROTO_PORT 2 /* src/dst/addr/port/proto 5-tuple */ 74 | #define TRACK_IP_PROTO 3 /* src/dst/proto 3-tuple */ 75 | #define TRACK_IP_ONLY 4 /* src/dst tuple */ 76 | #define TRACK_FULL_VLAN 5 /* src/dst/addr/port/proto/tos/vlanid 7-tuple */ 77 | 78 | /* 79 | * This structure contains optional information carried by Option Data 80 | * Record. 81 | */ 82 | struct OPTION { 83 | uint32_t sample; 84 | pid_t meteringProcessId; 85 | }; 86 | 87 | struct FLOWTRACKPARAMETERS { 88 | unsigned int num_flows; /* # of active flows */ 89 | unsigned int max_flows; /* Max # of active flows */ 90 | u_int64_t next_flow_seq; /* Next flow ID */ 91 | 92 | /* Stuff related to flow export */ 93 | struct timeval system_boot_time; /* SysUptime */ 94 | int track_level; /* See TRACK_* above */ 95 | 96 | /* Flow timeouts */ 97 | int tcp_timeout; /* Open TCP connections */ 98 | int tcp_rst_timeout; /* TCP flows after RST */ 99 | int tcp_fin_timeout; /* TCP flows after bidi FIN */ 100 | int udp_timeout; /* UDP flows */ 101 | int icmp_timeout; /* ICMP flows */ 102 | int general_timeout; /* Everything else */ 103 | int maximum_lifetime; /* Maximum life for flows */ 104 | int expiry_interval; /* Interval between expiries */ 105 | 106 | /* Statistics */ 107 | u_int64_t total_packets; /* # of good packets */ 108 | u_int64_t non_sampled_packets; /* # of not sampled packets */ 109 | u_int64_t frag_packets; /* # of fragmented packets */ 110 | u_int64_t non_ip_packets; /* # of not-IP packets */ 111 | u_int64_t bad_packets; /* # of bad packets */ 112 | u_int64_t flows_expired; /* # expired */ 113 | u_int64_t flows_exported; /* # of flows sent */ 114 | u_int64_t flows_dropped; /* # of flows dropped */ 115 | u_int64_t flows_force_expired; /* # of flows forced out */ 116 | u_int64_t packets_sent; /* # netflow packets sent */ 117 | u_int64_t records_sent; /* # netflow records sent */ 118 | struct STATISTIC duration; /* Flow duration */ 119 | struct STATISTIC octets; /* Bytes (bidir) */ 120 | struct STATISTIC packets; /* Packets (bidir) */ 121 | 122 | /* Per protocol statistics */ 123 | u_int64_t flows_pp[256]; 124 | u_int64_t octets_pp[256]; 125 | u_int64_t packets_pp[256]; 126 | struct STATISTIC duration_pp[256]; 127 | 128 | /* Timeout statistics */ 129 | u_int64_t expired_general; 130 | u_int64_t expired_tcp; 131 | u_int64_t expired_tcp_rst; 132 | u_int64_t expired_tcp_fin; 133 | u_int64_t expired_udp; 134 | u_int64_t expired_icmp; 135 | u_int64_t expired_maxlife; 136 | u_int64_t expired_overbytes; 137 | u_int64_t expired_maxflows; 138 | u_int64_t expired_flush; 139 | 140 | /* Optional information */ 141 | struct OPTION option; 142 | char time_format; 143 | u_int8_t bidirection; 144 | }; 145 | /* 146 | * This structure is the root of the flow tracking system. 147 | * It holds the root of the tree of active flows and the head of the 148 | * tree of expiry events. It also collects miscellaneous statistics 149 | */ 150 | struct FLOWTRACK { 151 | /* The flows and their expiry events */ 152 | FLOW_HEAD(FLOWS, FLOW) flows; /* Top of flow tree */ 153 | EXPIRY_HEAD(EXPIRIES, EXPIRY) expiries; /* Top of expiries tree */ 154 | 155 | struct freelist flow_freelist; /* Freelist for flows */ 156 | struct freelist expiry_freelist; /* Freelist for expiry events */ 157 | 158 | struct FLOWTRACKPARAMETERS param; 159 | }; 160 | 161 | /* 162 | * This structure is an entry in the tree of flows that we are 163 | * currently tracking. 164 | * 165 | * Because flows are matched _bi-directionally_, they must be stored in 166 | * a canonical format: the numerically lowest address and port number must 167 | * be stored in the first address and port array slot respectively. 168 | */ 169 | struct FLOW { 170 | /* Housekeeping */ 171 | struct EXPIRY *expiry; /* Pointer to expiry record */ 172 | FLOW_ENTRY(FLOW) trp; /* Tree pointer */ 173 | 174 | /* Per-flow statistics (all in _host_ byte order) */ 175 | u_int64_t flow_seq; /* Flow ID */ 176 | struct timeval flow_start; /* Time of creation */ 177 | struct timeval flow_last; /* Time of last traffic */ 178 | 179 | /* Per-endpoint statistics (all in _host_ byte order) */ 180 | u_int32_t octets[2]; /* Octets so far */ 181 | u_int32_t packets[2]; /* Packets so far */ 182 | 183 | /* Flow identity (all are in network byte order) */ 184 | int af; /* Address family of flow */ 185 | u_int32_t ip6_flowlabel[2]; /* IPv6 Flowlabel */ 186 | union { 187 | struct in_addr v4; 188 | struct in6_addr v6; 189 | } addr[2]; /* Endpoint addresses */ 190 | u_int16_t port[2]; /* Endpoint ports */ 191 | u_int8_t tcp_flags[2]; /* Cumulative OR of flags */ 192 | u_int8_t tos[2]; /* Tos */ 193 | u_int16_t vlanid; /* vlanid */ 194 | u_int8_t protocol; /* Protocol */ 195 | }; 196 | 197 | /* 198 | * This is an entry in the tree of expiry events. The tree is used to 199 | * avoid traversion the whole tree of active flows looking for ones to 200 | * expire. "expires_at" is the time at which the flow should be discarded, 201 | * or zero if it is scheduled for immediate disposal. 202 | * 203 | * When a flow which hasn't been scheduled for immediate expiry registers 204 | * traffic, it is deleted from its current position in the tree and 205 | * re-inserted (subject to its updated timeout). 206 | * 207 | * Expiry scans operate by starting at the head of the tree and expiring 208 | * each entry with expires_at < now 209 | * 210 | */ 211 | struct EXPIRY { 212 | EXPIRY_ENTRY(EXPIRY) trp; /* Tree pointer */ 213 | struct FLOW *flow; /* pointer to flow */ 214 | 215 | u_int32_t expires_at; /* time_t */ 216 | enum { 217 | R_GENERAL, R_TCP, R_TCP_RST, R_TCP_FIN, R_UDP, R_ICMP, 218 | R_MAXLIFE, R_OVERBYTES, R_OVERFLOWS, R_FLUSH 219 | } reason; 220 | }; 221 | 222 | /* Prototype for functions shared from softflowd.c */ 223 | u_int32_t timeval_sub_ms(const struct timeval *t1, const struct timeval *t2); 224 | 225 | /* Prototypes for functions to send NetFlow packets, from netflow*.c */ 226 | int send_netflow_v1(struct FLOW **flows, int num_flows, int nfsock, 227 | u_int16_t ifidx, struct FLOWTRACKPARAMETERS *param, 228 | int verbose_flag); 229 | int send_netflow_v5(struct FLOW **flows, int num_flows, int nfsock, 230 | u_int16_t ifidx, struct FLOWTRACKPARAMETERS *param, 231 | int verbose_flag); 232 | int send_netflow_v9(struct FLOW **flows, int num_flows, int nfsock, 233 | u_int16_t ifidx, struct FLOWTRACKPARAMETERS *param, 234 | int verbose_flag); 235 | int send_ipfix(struct FLOW **flows, int num_flows, int nfsock, 236 | u_int16_t ifidx, struct FLOWTRACKPARAMETERS *param, 237 | int verbose_flag); 238 | int send_ipfix_bidirection(struct FLOW **flows, int num_flows, int nfsock, 239 | u_int16_t ifidx, 240 | struct FLOWTRACKPARAMETERS *param, 241 | int verbose_flag); 242 | 243 | /* Force a resend of the flow template */ 244 | void netflow9_resend_template(void); 245 | void ipfix_resend_template(void); 246 | 247 | #endif /* _SOFTFLOWD_H */ 248 | -------------------------------------------------------------------------------- /softflowd.init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # softflowd Starts softflowd NetFlow probe 4 | # 5 | # chkconfig: 2345 95 02 6 | # description: Starts and stops the softflowd Netflow probe 7 | 8 | # Source function library. 9 | . /etc/init.d/functions 10 | 11 | SOFTFLOW_CONF=/etc/sysconfig/softflowd 12 | SOFTFLOW_LOCK=/var/lock/subsys/softflowd 13 | SOFTFLOW_PROG=/usr/sbin/softflowd 14 | SOFTFLOW_OPTS="-i eth0" 15 | 16 | # Source config 17 | if [ -f $SOFTFLOW_CONF ]; then 18 | . $SOFTFLOW_CONF 19 | fi 20 | 21 | [ -x $SOFTFLOW_PROG ] || exit 0 22 | 23 | RETVAL=0 24 | 25 | start() { 26 | echo -n $"Starting softflowd: " 27 | daemon $SOFTFLOW_PROG $SOFTFLOW_OPTS 28 | RETVAL=$? 29 | echo 30 | [ $RETVAL -eq 0 ] && touch $SOFTFLOW_LOCK 31 | return $RETVAL 32 | } 33 | stop() { 34 | echo -n $"Shutting down softflowd: " 35 | killproc $SOFTFLOW_PROG 36 | RETVAL=$? 37 | echo 38 | [ $RETVAL -eq 0 ] && rm -f $SOFTFLOW_LOCK 39 | return $RETVAL 40 | } 41 | restart() { 42 | stop 43 | start 44 | } 45 | 46 | case "$1" in 47 | start) 48 | start 49 | ;; 50 | stop) 51 | stop 52 | ;; 53 | status) 54 | status $SOFTFLOW_PROG 55 | ;; 56 | restart|reload) 57 | restart 58 | ;; 59 | condrestart) 60 | [ -f $SOFTFLOW_LOCK ] && restart || : 61 | ;; 62 | *) 63 | echo $"Usage: $0 {start|stop|status|restart|condrestart}" 64 | exit 1 65 | esac 66 | 67 | exit $? 68 | -------------------------------------------------------------------------------- /softflowd.spec: -------------------------------------------------------------------------------- 1 | # Figure out release tag (e.g. rhel3, fc1) based on redhat-release file 2 | %global _RHTAG %(sed 's/(.*)//;s/ [A-Z]* //;s/[a-z ]*//g' /etc/redhat-release | tr '[:upper:]' '[:lower:]') 3 | 4 | Name: softflowd 5 | Summary: Network traffic analyser capable of Cisco NetFlow data export 6 | Version: 0.9.9 7 | Release: 1.%{_RHTAG} 8 | Source: softflowd-%{version}.tar.gz 9 | Group: System/Utilities 10 | License: BSD 11 | BuildRoot: %{_tmppath}/%{name}-root 12 | URL: http://www.mindrot.org/softflowd.html 13 | Vendor: mindrot.org 14 | 15 | %description 16 | softflowd is a software implementation of a flow-based network traffic 17 | monitor. softflowd reads network traffic and gathers information about 18 | active traffic flows. A "traffic flow" is communication between two IP 19 | addresses or (if the overlying protocol is TCP or UDP) address/port tuples. 20 | The intended use of softflowd is as a software implementation of Cisco’s 21 | NetFlow traffic account system. softflowd supports data export using 22 | versions 1, 5 or 9 of the NetFlow protocol. 23 | 24 | %prep 25 | %setup 26 | 27 | %build 28 | %configure 29 | make 30 | 31 | %install 32 | rm -rf $RPM_BUILD_ROOT 33 | make install DESTDIR=$RPM_BUILD_ROOT 34 | mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d 35 | mkdir -p $RPM_BUILD_ROOT/etc/sysconfig 36 | cp %_sourcedir/softflowd.init $RPM_BUILD_ROOT/etc/rc.d/init.d/softflowd 37 | cp %_sourcedir/softflowd.sysconfig $RPM_BUILD_ROOT/etc/sysconfig/softflowd 38 | 39 | %files 40 | %defattr(-,root,root) 41 | /usr/sbin/* 42 | /usr/share/man/* 43 | %attr(0755,root,root) /etc/rc.d/init.d/softflowd 44 | %config(noreplace) %attr(0644,root,root) /etc/sysconfig/softflowd 45 | %doc ChangeLog README TODO 46 | 47 | %clean 48 | rm -rf $RPM_BUILD_ROOT 49 | -------------------------------------------------------------------------------- /softflowd.sysconfig: -------------------------------------------------------------------------------- 1 | # Config file for softflowd startup 2 | 3 | # Location of softflowd binary 4 | #SOFTFLOW_PROG=/usr/sbin/softflowd 5 | 6 | # Options passed to the softflowd program 7 | # Default - not very useful 8 | #SOFTFLOW_OPTS="-i eth0" 9 | # Example NetFlow v5 export from traffic on eth1 10 | #SOFTFLOW_OPTS="-v 5 -i eth1 -n hostname:2055" 11 | -------------------------------------------------------------------------------- /strlcat.c: -------------------------------------------------------------------------------- 1 | /* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */ 2 | 3 | /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */ 4 | 5 | /* 6 | * Copyright (c) 1998 Todd C. Miller 7 | * 8 | * Permission to use, copy, modify, and distribute this software for any 9 | * purpose with or without fee is hereby granted, provided that the above 10 | * copyright notice and this permission notice appear in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 | */ 20 | 21 | #include "common.h" 22 | #ifndef HAVE_STRLCAT 23 | 24 | #if defined(LIBC_SCCS) && !defined(lint) 25 | static char *rcsid = "$OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $"; 26 | #endif /* LIBC_SCCS and not lint */ 27 | 28 | #include 29 | #include 30 | 31 | /* 32 | * Appends src to string dst of size siz (unlike strncat, siz is the 33 | * full size of dst, not space left). At most siz-1 characters 34 | * will be copied. Always NUL terminates (unless siz <= strlen(dst)). 35 | * Returns strlen(src) + MIN(siz, strlen(initial dst)). 36 | * If retval >= siz, truncation occurred. 37 | */ 38 | size_t 39 | strlcat(char *dst, const char *src, size_t siz) 40 | { 41 | register char *d = dst; 42 | register const char *s = src; 43 | register size_t n = siz; 44 | size_t dlen; 45 | 46 | /* Find the end of dst and adjust bytes left but don't go past end */ 47 | while (n-- != 0 && *d != '\0') 48 | d++; 49 | dlen = d - dst; 50 | n = siz - dlen; 51 | 52 | if (n == 0) 53 | return(dlen + strlen(s)); 54 | while (*s != '\0') { 55 | if (n != 1) { 56 | *d++ = *s; 57 | n--; 58 | } 59 | s++; 60 | } 61 | *d = '\0'; 62 | 63 | return(dlen + (s - src)); /* count does not include NUL */ 64 | } 65 | 66 | #endif /* !HAVE_STRLCAT */ 67 | -------------------------------------------------------------------------------- /strlcpy.c: -------------------------------------------------------------------------------- 1 | /* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */ 2 | 3 | /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */ 4 | 5 | /* 6 | * Copyright (c) 1998 Todd C. Miller 7 | * 8 | * Permission to use, copy, modify, and distribute this software for any 9 | * purpose with or without fee is hereby granted, provided that the above 10 | * copyright notice and this permission notice appear in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 | */ 20 | 21 | #include "common.h" 22 | 23 | #ifndef HAVE_STRLCPY 24 | 25 | #if defined(LIBC_SCCS) && !defined(lint) 26 | static char *rcsid = "$OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $"; 27 | #endif /* LIBC_SCCS and not lint */ 28 | 29 | #include 30 | #include 31 | 32 | /* 33 | * Copy src to string dst of size siz. At most siz-1 characters 34 | * will be copied. Always NUL terminates (unless siz == 0). 35 | * Returns strlen(src); if retval >= siz, truncation occurred. 36 | */ 37 | size_t 38 | strlcpy(char *dst, const char *src, size_t siz) 39 | { 40 | register char *d = dst; 41 | register const char *s = src; 42 | register size_t n = siz; 43 | 44 | /* Copy as many bytes as will fit */ 45 | if (n != 0 && --n != 0) { 46 | do { 47 | if ((*d++ = *s++) == 0) 48 | break; 49 | } while (--n != 0); 50 | } 51 | 52 | /* Not enough room in dst, add NUL and traverse rest of src */ 53 | if (n == 0) { 54 | if (siz != 0) 55 | *d = '\0'; /* NUL-terminate dst */ 56 | while (*s++) 57 | ; 58 | } 59 | 60 | return(s - src - 1); /* count does not include NUL */ 61 | } 62 | 63 | #endif /* !HAVE_STRLCPY */ 64 | -------------------------------------------------------------------------------- /sys-tree.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: tree.h,v 1.9 2004/11/24 18:10:42 tdeval Exp $ */ 2 | /* 3 | * Copyright 2002 Niels Provos 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _SYS_TREE_H_ 28 | #define _SYS_TREE_H_ 29 | 30 | /* 31 | * This file defines data structures for different types of trees: 32 | * splay trees and red-black trees. 33 | * 34 | * A splay tree is a self-organizing data structure. Every operation 35 | * on the tree causes a splay to happen. The splay moves the requested 36 | * node to the root of the tree and partly rebalances it. 37 | * 38 | * This has the benefit that request locality causes faster lookups as 39 | * the requested nodes move to the top of the tree. On the other hand, 40 | * every lookup causes memory writes. 41 | * 42 | * The Balance Theorem bounds the total access time for m operations 43 | * and n inserts on an initially empty tree as O((m + n)lg n). The 44 | * amortized cost for a sequence of m accesses to a splay tree is O(lg n); 45 | * 46 | * A red-black tree is a binary search tree with the node color as an 47 | * extra attribute. It fulfills a set of conditions: 48 | * - every search path from the root to a leaf consists of the 49 | * same number of black nodes, 50 | * - each red node (except for the root) has a black parent, 51 | * - each leaf node is black. 52 | * 53 | * Every operation on a red-black tree is bounded as O(lg n). 54 | * The maximum height of a red-black tree is 2lg (n+1). 55 | */ 56 | 57 | #define SPLAY_HEAD(name, type) \ 58 | struct name { \ 59 | struct type *sph_root; /* root of the tree */ \ 60 | } 61 | 62 | #define SPLAY_INITIALIZER(root) \ 63 | { NULL } 64 | 65 | #define SPLAY_INIT(root) do { \ 66 | (root)->sph_root = NULL; \ 67 | } while (0) 68 | 69 | #define SPLAY_ENTRY(type) \ 70 | struct { \ 71 | struct type *spe_left; /* left element */ \ 72 | struct type *spe_right; /* right element */ \ 73 | } 74 | 75 | #define SPLAY_LEFT(elm, field) (elm)->field.spe_left 76 | #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right 77 | #define SPLAY_ROOT(head) (head)->sph_root 78 | #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) 79 | 80 | /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ 81 | #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ 82 | SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ 83 | SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ 84 | (head)->sph_root = tmp; \ 85 | } while (0) 86 | 87 | #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ 88 | SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ 89 | SPLAY_LEFT(tmp, field) = (head)->sph_root; \ 90 | (head)->sph_root = tmp; \ 91 | } while (0) 92 | 93 | #define SPLAY_LINKLEFT(head, tmp, field) do { \ 94 | SPLAY_LEFT(tmp, field) = (head)->sph_root; \ 95 | tmp = (head)->sph_root; \ 96 | (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ 97 | } while (0) 98 | 99 | #define SPLAY_LINKRIGHT(head, tmp, field) do { \ 100 | SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ 101 | tmp = (head)->sph_root; \ 102 | (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ 103 | } while (0) 104 | 105 | #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ 106 | SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ 107 | SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ 108 | SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ 109 | SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ 110 | } while (0) 111 | 112 | /* Generates prototypes and inline functions */ 113 | 114 | #define SPLAY_PROTOTYPE(name, type, field, cmp) \ 115 | void name##_SPLAY(struct name *, struct type *); \ 116 | void name##_SPLAY_MINMAX(struct name *, int); \ 117 | struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ 118 | struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ 119 | \ 120 | /* Finds the node with the same key as elm */ \ 121 | static __inline struct type * \ 122 | name##_SPLAY_FIND(struct name *head, struct type *elm) \ 123 | { \ 124 | if (SPLAY_EMPTY(head)) \ 125 | return(NULL); \ 126 | name##_SPLAY(head, elm); \ 127 | if ((cmp)(elm, (head)->sph_root) == 0) \ 128 | return (head->sph_root); \ 129 | return (NULL); \ 130 | } \ 131 | \ 132 | static __inline struct type * \ 133 | name##_SPLAY_NEXT(struct name *head, struct type *elm) \ 134 | { \ 135 | name##_SPLAY(head, elm); \ 136 | if (SPLAY_RIGHT(elm, field) != NULL) { \ 137 | elm = SPLAY_RIGHT(elm, field); \ 138 | while (SPLAY_LEFT(elm, field) != NULL) { \ 139 | elm = SPLAY_LEFT(elm, field); \ 140 | } \ 141 | } else \ 142 | elm = NULL; \ 143 | return (elm); \ 144 | } \ 145 | \ 146 | static __inline struct type * \ 147 | name##_SPLAY_MIN_MAX(struct name *head, int val) \ 148 | { \ 149 | name##_SPLAY_MINMAX(head, val); \ 150 | return (SPLAY_ROOT(head)); \ 151 | } 152 | 153 | /* Main splay operation. 154 | * Moves node close to the key of elm to top 155 | */ 156 | #define SPLAY_GENERATE(name, type, field, cmp) \ 157 | struct type * \ 158 | name##_SPLAY_INSERT(struct name *head, struct type *elm) \ 159 | { \ 160 | if (SPLAY_EMPTY(head)) { \ 161 | SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ 162 | } else { \ 163 | int __comp; \ 164 | name##_SPLAY(head, elm); \ 165 | __comp = (cmp)(elm, (head)->sph_root); \ 166 | if(__comp < 0) { \ 167 | SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ 168 | SPLAY_RIGHT(elm, field) = (head)->sph_root; \ 169 | SPLAY_LEFT((head)->sph_root, field) = NULL; \ 170 | } else if (__comp > 0) { \ 171 | SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ 172 | SPLAY_LEFT(elm, field) = (head)->sph_root; \ 173 | SPLAY_RIGHT((head)->sph_root, field) = NULL; \ 174 | } else \ 175 | return ((head)->sph_root); \ 176 | } \ 177 | (head)->sph_root = (elm); \ 178 | return (NULL); \ 179 | } \ 180 | \ 181 | struct type * \ 182 | name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ 183 | { \ 184 | struct type *__tmp; \ 185 | if (SPLAY_EMPTY(head)) \ 186 | return (NULL); \ 187 | name##_SPLAY(head, elm); \ 188 | if ((cmp)(elm, (head)->sph_root) == 0) { \ 189 | if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ 190 | (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ 191 | } else { \ 192 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 193 | (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ 194 | name##_SPLAY(head, elm); \ 195 | SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ 196 | } \ 197 | return (elm); \ 198 | } \ 199 | return (NULL); \ 200 | } \ 201 | \ 202 | void \ 203 | name##_SPLAY(struct name *head, struct type *elm) \ 204 | { \ 205 | struct type __node, *__left, *__right, *__tmp; \ 206 | int __comp; \ 207 | \ 208 | SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ 209 | __left = __right = &__node; \ 210 | \ 211 | while ((__comp = (cmp)(elm, (head)->sph_root))) { \ 212 | if (__comp < 0) { \ 213 | __tmp = SPLAY_LEFT((head)->sph_root, field); \ 214 | if (__tmp == NULL) \ 215 | break; \ 216 | if ((cmp)(elm, __tmp) < 0){ \ 217 | SPLAY_ROTATE_RIGHT(head, __tmp, field); \ 218 | if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ 219 | break; \ 220 | } \ 221 | SPLAY_LINKLEFT(head, __right, field); \ 222 | } else if (__comp > 0) { \ 223 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 224 | if (__tmp == NULL) \ 225 | break; \ 226 | if ((cmp)(elm, __tmp) > 0){ \ 227 | SPLAY_ROTATE_LEFT(head, __tmp, field); \ 228 | if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ 229 | break; \ 230 | } \ 231 | SPLAY_LINKRIGHT(head, __left, field); \ 232 | } \ 233 | } \ 234 | SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ 235 | } \ 236 | \ 237 | /* Splay with either the minimum or the maximum element \ 238 | * Used to find minimum or maximum element in tree. \ 239 | */ \ 240 | void name##_SPLAY_MINMAX(struct name *head, int __comp) \ 241 | { \ 242 | struct type __node, *__left, *__right, *__tmp; \ 243 | \ 244 | SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ 245 | __left = __right = &__node; \ 246 | \ 247 | while (1) { \ 248 | if (__comp < 0) { \ 249 | __tmp = SPLAY_LEFT((head)->sph_root, field); \ 250 | if (__tmp == NULL) \ 251 | break; \ 252 | if (__comp < 0){ \ 253 | SPLAY_ROTATE_RIGHT(head, __tmp, field); \ 254 | if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ 255 | break; \ 256 | } \ 257 | SPLAY_LINKLEFT(head, __right, field); \ 258 | } else if (__comp > 0) { \ 259 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 260 | if (__tmp == NULL) \ 261 | break; \ 262 | if (__comp > 0) { \ 263 | SPLAY_ROTATE_LEFT(head, __tmp, field); \ 264 | if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ 265 | break; \ 266 | } \ 267 | SPLAY_LINKRIGHT(head, __left, field); \ 268 | } \ 269 | } \ 270 | SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ 271 | } 272 | 273 | #define SPLAY_NEGINF -1 274 | #define SPLAY_INF 1 275 | 276 | #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) 277 | #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) 278 | #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) 279 | #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) 280 | #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ 281 | : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) 282 | #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ 283 | : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) 284 | 285 | #define SPLAY_FOREACH(x, name, head) \ 286 | for ((x) = SPLAY_MIN(name, head); \ 287 | (x) != NULL; \ 288 | (x) = SPLAY_NEXT(name, head, x)) 289 | 290 | /* Macros that define a red-black tree */ 291 | #define RB_HEAD(name, type) \ 292 | struct name { \ 293 | struct type *rbh_root; /* root of the tree */ \ 294 | } 295 | 296 | #define RB_INITIALIZER(root) \ 297 | { NULL } 298 | 299 | #define RB_INIT(root) do { \ 300 | (root)->rbh_root = NULL; \ 301 | } while (0) 302 | 303 | #define RB_BLACK 0 304 | #define RB_RED 1 305 | #define RB_ENTRY(type) \ 306 | struct { \ 307 | struct type *rbe_left; /* left element */ \ 308 | struct type *rbe_right; /* right element */ \ 309 | struct type *rbe_parent; /* parent element */ \ 310 | int rbe_color; /* node color */ \ 311 | } 312 | 313 | #define RB_LEFT(elm, field) (elm)->field.rbe_left 314 | #define RB_RIGHT(elm, field) (elm)->field.rbe_right 315 | #define RB_PARENT(elm, field) (elm)->field.rbe_parent 316 | #define RB_COLOR(elm, field) (elm)->field.rbe_color 317 | #define RB_ROOT(head) (head)->rbh_root 318 | #define RB_EMPTY(head) (RB_ROOT(head) == NULL) 319 | 320 | #define RB_SET(elm, parent, field) do { \ 321 | RB_PARENT(elm, field) = parent; \ 322 | RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ 323 | RB_COLOR(elm, field) = RB_RED; \ 324 | } while (0) 325 | 326 | #define RB_SET_BLACKRED(black, red, field) do { \ 327 | RB_COLOR(black, field) = RB_BLACK; \ 328 | RB_COLOR(red, field) = RB_RED; \ 329 | } while (0) 330 | 331 | #ifndef RB_AUGMENT 332 | #define RB_AUGMENT(x) 333 | #endif 334 | 335 | #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ 336 | (tmp) = RB_RIGHT(elm, field); \ 337 | if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ 338 | RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ 339 | } \ 340 | RB_AUGMENT(elm); \ 341 | if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ 342 | if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ 343 | RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ 344 | else \ 345 | RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ 346 | } else \ 347 | (head)->rbh_root = (tmp); \ 348 | RB_LEFT(tmp, field) = (elm); \ 349 | RB_PARENT(elm, field) = (tmp); \ 350 | RB_AUGMENT(tmp); \ 351 | if ((RB_PARENT(tmp, field))) \ 352 | RB_AUGMENT(RB_PARENT(tmp, field)); \ 353 | } while (0) 354 | 355 | #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ 356 | (tmp) = RB_LEFT(elm, field); \ 357 | if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ 358 | RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ 359 | } \ 360 | RB_AUGMENT(elm); \ 361 | if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ 362 | if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ 363 | RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ 364 | else \ 365 | RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ 366 | } else \ 367 | (head)->rbh_root = (tmp); \ 368 | RB_RIGHT(tmp, field) = (elm); \ 369 | RB_PARENT(elm, field) = (tmp); \ 370 | RB_AUGMENT(tmp); \ 371 | if ((RB_PARENT(tmp, field))) \ 372 | RB_AUGMENT(RB_PARENT(tmp, field)); \ 373 | } while (0) 374 | 375 | /* Generates prototypes and inline functions */ 376 | #define RB_PROTOTYPE(name, type, field, cmp) \ 377 | void name##_RB_INSERT_COLOR(struct name *, struct type *); \ 378 | void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ 379 | struct type *name##_RB_REMOVE(struct name *, struct type *); \ 380 | struct type *name##_RB_INSERT(struct name *, struct type *); \ 381 | struct type *name##_RB_FIND(struct name *, struct type *); \ 382 | struct type *name##_RB_NEXT(struct type *); \ 383 | struct type *name##_RB_MINMAX(struct name *, int); \ 384 | \ 385 | 386 | /* Main rb operation. 387 | * Moves node close to the key of elm to top 388 | */ 389 | #define RB_GENERATE(name, type, field, cmp) \ 390 | void \ 391 | name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ 392 | { \ 393 | struct type *parent, *gparent, *tmp; \ 394 | while ((parent = RB_PARENT(elm, field)) && \ 395 | RB_COLOR(parent, field) == RB_RED) { \ 396 | gparent = RB_PARENT(parent, field); \ 397 | if (parent == RB_LEFT(gparent, field)) { \ 398 | tmp = RB_RIGHT(gparent, field); \ 399 | if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ 400 | RB_COLOR(tmp, field) = RB_BLACK; \ 401 | RB_SET_BLACKRED(parent, gparent, field);\ 402 | elm = gparent; \ 403 | continue; \ 404 | } \ 405 | if (RB_RIGHT(parent, field) == elm) { \ 406 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 407 | tmp = parent; \ 408 | parent = elm; \ 409 | elm = tmp; \ 410 | } \ 411 | RB_SET_BLACKRED(parent, gparent, field); \ 412 | RB_ROTATE_RIGHT(head, gparent, tmp, field); \ 413 | } else { \ 414 | tmp = RB_LEFT(gparent, field); \ 415 | if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ 416 | RB_COLOR(tmp, field) = RB_BLACK; \ 417 | RB_SET_BLACKRED(parent, gparent, field);\ 418 | elm = gparent; \ 419 | continue; \ 420 | } \ 421 | if (RB_LEFT(parent, field) == elm) { \ 422 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 423 | tmp = parent; \ 424 | parent = elm; \ 425 | elm = tmp; \ 426 | } \ 427 | RB_SET_BLACKRED(parent, gparent, field); \ 428 | RB_ROTATE_LEFT(head, gparent, tmp, field); \ 429 | } \ 430 | } \ 431 | RB_COLOR(head->rbh_root, field) = RB_BLACK; \ 432 | } \ 433 | \ 434 | void \ 435 | name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ 436 | { \ 437 | struct type *tmp; \ 438 | while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ 439 | elm != RB_ROOT(head)) { \ 440 | if (RB_LEFT(parent, field) == elm) { \ 441 | tmp = RB_RIGHT(parent, field); \ 442 | if (RB_COLOR(tmp, field) == RB_RED) { \ 443 | RB_SET_BLACKRED(tmp, parent, field); \ 444 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 445 | tmp = RB_RIGHT(parent, field); \ 446 | } \ 447 | if ((RB_LEFT(tmp, field) == NULL || \ 448 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ 449 | (RB_RIGHT(tmp, field) == NULL || \ 450 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ 451 | RB_COLOR(tmp, field) = RB_RED; \ 452 | elm = parent; \ 453 | parent = RB_PARENT(elm, field); \ 454 | } else { \ 455 | if (RB_RIGHT(tmp, field) == NULL || \ 456 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ 457 | struct type *oleft; \ 458 | if ((oleft = RB_LEFT(tmp, field)))\ 459 | RB_COLOR(oleft, field) = RB_BLACK;\ 460 | RB_COLOR(tmp, field) = RB_RED; \ 461 | RB_ROTATE_RIGHT(head, tmp, oleft, field);\ 462 | tmp = RB_RIGHT(parent, field); \ 463 | } \ 464 | RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ 465 | RB_COLOR(parent, field) = RB_BLACK; \ 466 | if (RB_RIGHT(tmp, field)) \ 467 | RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ 468 | RB_ROTATE_LEFT(head, parent, tmp, field);\ 469 | elm = RB_ROOT(head); \ 470 | break; \ 471 | } \ 472 | } else { \ 473 | tmp = RB_LEFT(parent, field); \ 474 | if (RB_COLOR(tmp, field) == RB_RED) { \ 475 | RB_SET_BLACKRED(tmp, parent, field); \ 476 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 477 | tmp = RB_LEFT(parent, field); \ 478 | } \ 479 | if ((RB_LEFT(tmp, field) == NULL || \ 480 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ 481 | (RB_RIGHT(tmp, field) == NULL || \ 482 | RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ 483 | RB_COLOR(tmp, field) = RB_RED; \ 484 | elm = parent; \ 485 | parent = RB_PARENT(elm, field); \ 486 | } else { \ 487 | if (RB_LEFT(tmp, field) == NULL || \ 488 | RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ 489 | struct type *oright; \ 490 | if ((oright = RB_RIGHT(tmp, field)))\ 491 | RB_COLOR(oright, field) = RB_BLACK;\ 492 | RB_COLOR(tmp, field) = RB_RED; \ 493 | RB_ROTATE_LEFT(head, tmp, oright, field);\ 494 | tmp = RB_LEFT(parent, field); \ 495 | } \ 496 | RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ 497 | RB_COLOR(parent, field) = RB_BLACK; \ 498 | if (RB_LEFT(tmp, field)) \ 499 | RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ 500 | RB_ROTATE_RIGHT(head, parent, tmp, field);\ 501 | elm = RB_ROOT(head); \ 502 | break; \ 503 | } \ 504 | } \ 505 | } \ 506 | if (elm) \ 507 | RB_COLOR(elm, field) = RB_BLACK; \ 508 | } \ 509 | \ 510 | struct type * \ 511 | name##_RB_REMOVE(struct name *head, struct type *elm) \ 512 | { \ 513 | struct type *child, *parent, *old = elm; \ 514 | int color; \ 515 | if (RB_LEFT(elm, field) == NULL) \ 516 | child = RB_RIGHT(elm, field); \ 517 | else if (RB_RIGHT(elm, field) == NULL) \ 518 | child = RB_LEFT(elm, field); \ 519 | else { \ 520 | struct type *left; \ 521 | elm = RB_RIGHT(elm, field); \ 522 | while ((left = RB_LEFT(elm, field))) \ 523 | elm = left; \ 524 | child = RB_RIGHT(elm, field); \ 525 | parent = RB_PARENT(elm, field); \ 526 | color = RB_COLOR(elm, field); \ 527 | if (child) \ 528 | RB_PARENT(child, field) = parent; \ 529 | if (parent) { \ 530 | if (RB_LEFT(parent, field) == elm) \ 531 | RB_LEFT(parent, field) = child; \ 532 | else \ 533 | RB_RIGHT(parent, field) = child; \ 534 | RB_AUGMENT(parent); \ 535 | } else \ 536 | RB_ROOT(head) = child; \ 537 | if (RB_PARENT(elm, field) == old) \ 538 | parent = elm; \ 539 | (elm)->field = (old)->field; \ 540 | if (RB_PARENT(old, field)) { \ 541 | if (RB_LEFT(RB_PARENT(old, field), field) == old)\ 542 | RB_LEFT(RB_PARENT(old, field), field) = elm;\ 543 | else \ 544 | RB_RIGHT(RB_PARENT(old, field), field) = elm;\ 545 | RB_AUGMENT(RB_PARENT(old, field)); \ 546 | } else \ 547 | RB_ROOT(head) = elm; \ 548 | RB_PARENT(RB_LEFT(old, field), field) = elm; \ 549 | if (RB_RIGHT(old, field)) \ 550 | RB_PARENT(RB_RIGHT(old, field), field) = elm; \ 551 | if (parent) { \ 552 | left = parent; \ 553 | do { \ 554 | RB_AUGMENT(left); \ 555 | } while ((left = RB_PARENT(left, field))); \ 556 | } \ 557 | goto color; \ 558 | } \ 559 | parent = RB_PARENT(elm, field); \ 560 | color = RB_COLOR(elm, field); \ 561 | if (child) \ 562 | RB_PARENT(child, field) = parent; \ 563 | if (parent) { \ 564 | if (RB_LEFT(parent, field) == elm) \ 565 | RB_LEFT(parent, field) = child; \ 566 | else \ 567 | RB_RIGHT(parent, field) = child; \ 568 | RB_AUGMENT(parent); \ 569 | } else \ 570 | RB_ROOT(head) = child; \ 571 | color: \ 572 | if (color == RB_BLACK) \ 573 | name##_RB_REMOVE_COLOR(head, parent, child); \ 574 | return (old); \ 575 | } \ 576 | \ 577 | /* Inserts a node into the RB tree */ \ 578 | struct type * \ 579 | name##_RB_INSERT(struct name *head, struct type *elm) \ 580 | { \ 581 | struct type *tmp; \ 582 | struct type *parent = NULL; \ 583 | int comp = 0; \ 584 | tmp = RB_ROOT(head); \ 585 | while (tmp) { \ 586 | parent = tmp; \ 587 | comp = (cmp)(elm, parent); \ 588 | if (comp < 0) \ 589 | tmp = RB_LEFT(tmp, field); \ 590 | else if (comp > 0) \ 591 | tmp = RB_RIGHT(tmp, field); \ 592 | else \ 593 | return (tmp); \ 594 | } \ 595 | RB_SET(elm, parent, field); \ 596 | if (parent != NULL) { \ 597 | if (comp < 0) \ 598 | RB_LEFT(parent, field) = elm; \ 599 | else \ 600 | RB_RIGHT(parent, field) = elm; \ 601 | RB_AUGMENT(parent); \ 602 | } else \ 603 | RB_ROOT(head) = elm; \ 604 | name##_RB_INSERT_COLOR(head, elm); \ 605 | return (NULL); \ 606 | } \ 607 | \ 608 | /* Finds the node with the same key as elm */ \ 609 | struct type * \ 610 | name##_RB_FIND(struct name *head, struct type *elm) \ 611 | { \ 612 | struct type *tmp = RB_ROOT(head); \ 613 | int comp; \ 614 | while (tmp) { \ 615 | comp = cmp(elm, tmp); \ 616 | if (comp < 0) \ 617 | tmp = RB_LEFT(tmp, field); \ 618 | else if (comp > 0) \ 619 | tmp = RB_RIGHT(tmp, field); \ 620 | else \ 621 | return (tmp); \ 622 | } \ 623 | return (NULL); \ 624 | } \ 625 | \ 626 | struct type * \ 627 | name##_RB_NEXT(struct type *elm) \ 628 | { \ 629 | if (RB_RIGHT(elm, field)) { \ 630 | elm = RB_RIGHT(elm, field); \ 631 | while (RB_LEFT(elm, field)) \ 632 | elm = RB_LEFT(elm, field); \ 633 | } else { \ 634 | if (RB_PARENT(elm, field) && \ 635 | (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ 636 | elm = RB_PARENT(elm, field); \ 637 | else { \ 638 | while (RB_PARENT(elm, field) && \ 639 | (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ 640 | elm = RB_PARENT(elm, field); \ 641 | elm = RB_PARENT(elm, field); \ 642 | } \ 643 | } \ 644 | return (elm); \ 645 | } \ 646 | \ 647 | struct type * \ 648 | name##_RB_MINMAX(struct name *head, int val) \ 649 | { \ 650 | struct type *tmp = RB_ROOT(head); \ 651 | struct type *parent = NULL; \ 652 | while (tmp) { \ 653 | parent = tmp; \ 654 | if (val < 0) \ 655 | tmp = RB_LEFT(tmp, field); \ 656 | else \ 657 | tmp = RB_RIGHT(tmp, field); \ 658 | } \ 659 | return (parent); \ 660 | } 661 | 662 | #define RB_NEGINF -1 663 | #define RB_INF 1 664 | 665 | #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) 666 | #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) 667 | #define RB_FIND(name, x, y) name##_RB_FIND(x, y) 668 | #define RB_NEXT(name, x, y) name##_RB_NEXT(y) 669 | #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) 670 | #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) 671 | 672 | #define RB_FOREACH(x, name, head) \ 673 | for ((x) = RB_MIN(name, head); \ 674 | (x) != NULL; \ 675 | (x) = name##_RB_NEXT(x)) 676 | 677 | #endif /* _SYS_TREE_H_ */ 678 | -------------------------------------------------------------------------------- /treetype.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002 Damien Miller All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | /* Select our tree types for various data structures */ 26 | 27 | #if defined(FLOW_RB) 28 | #define FLOW_HEAD RB_HEAD 29 | #define FLOW_ENTRY RB_ENTRY 30 | #define FLOW_PROTOTYPE RB_PROTOTYPE 31 | #define FLOW_GENERATE RB_GENERATE 32 | #define FLOW_INSERT RB_INSERT 33 | #define FLOW_FIND RB_FIND 34 | #define FLOW_REMOVE RB_REMOVE 35 | #define FLOW_FOREACH RB_FOREACH 36 | #define FLOW_MIN RB_MIN 37 | #define FLOW_NEXT RB_NEXT 38 | #define FLOW_INIT RB_INIT 39 | #elif defined(FLOW_SPLAY) 40 | #define FLOW_HEAD SPLAY_HEAD 41 | #define FLOW_ENTRY SPLAY_ENTRY 42 | #define FLOW_PROTOTYPE SPLAY_PROTOTYPE 43 | #define FLOW_GENERATE SPLAY_GENERATE 44 | #define FLOW_INSERT SPLAY_INSERT 45 | #define FLOW_FIND SPLAY_FIND 46 | #define FLOW_REMOVE SPLAY_REMOVE 47 | #define FLOW_FOREACH SPLAY_FOREACH 48 | #define FLOW_MIN SPLAY_MIN 49 | #define FLOW_NEXT SPLAY_NEXT 50 | #define FLOW_INIT SPLAY_INIT 51 | #else 52 | #error No flow tree type defined 53 | #endif 54 | 55 | #if defined(EXPIRY_RB) 56 | #define EXPIRY_HEAD RB_HEAD 57 | #define EXPIRY_ENTRY RB_ENTRY 58 | #define EXPIRY_PROTOTYPE RB_PROTOTYPE 59 | #define EXPIRY_GENERATE RB_GENERATE 60 | #define EXPIRY_INSERT RB_INSERT 61 | #define EXPIRY_FIND RB_FIND 62 | #define EXPIRY_REMOVE RB_REMOVE 63 | #define EXPIRY_FOREACH RB_FOREACH 64 | #define EXPIRY_MIN RB_MIN 65 | #define EXPIRY_NEXT RB_NEXT 66 | #define EXPIRY_INIT RB_INIT 67 | #elif defined(EXPIRY_SPLAY) 68 | #define EXPIRY_HEAD SPLAY_HEAD 69 | #define EXPIRY_ENTRY SPLAY_ENTRY 70 | #define EXPIRY_PROTOTYPE SPLAY_PROTOTYPE 71 | #define EXPIRY_GENERATE SPLAY_GENERATE 72 | #define EXPIRY_INSERT SPLAY_INSERT 73 | #define EXPIRY_FIND SPLAY_FIND 74 | #define EXPIRY_REMOVE SPLAY_REMOVE 75 | #define EXPIRY_FOREACH SPLAY_FOREACH 76 | #define EXPIRY_MIN SPLAY_MIN 77 | #define EXPIRY_NEXT SPLAY_NEXT 78 | #define EXPIRY_INIT SPLAY_INIT 79 | #else 80 | #error No expiry tree type defined 81 | #endif 82 | --------------------------------------------------------------------------------