Flow based network accounting with Linux Harald Welte 2005 Harald Welte
Abstract Many networking scenarios require some form of network accounting that goes beyond some simple packet and byte counters as available from the 'ifconfig' output. Network accounting can generally be done in a number of different ways. The traditional way is to capture all packets by some userspace program. Capturing can be done via a number of mechanisms such as PF_PACKET sockets, mmap()ed PF_PACKET, ipt_ULOG, or ip_queue. This userspace program then analyzes the packets and aggregates the result into per-flow data structures. Whatever mechanism used, this scheme has a fundamental performance limitation, since all packets need to be copied and analyzed by a userspace process. The author has implemented a different approach, by which the accounting information is stored in the in-kernel connection tracking table of the ip_conntrack stateful firewall state machine. On all firewalls, that state table has to be kept anyways - the additional overhead introduced by accounting is minimal.
Network accounting Network accounting generally describes the process of counting and potentially summarizing metadata of network traffic. The kind of metadata is largely dependant on the particular application, but usually includes data such as numbers of packets, numbers of bytes, source and destination ip address. There are many reasons for doing accounting of networking traffic, among them transfer volume or bandwisth based billing monitoring of network utilization, bandwidth distribution and link usage research, such as distribution of traffic among protocols, average packet size, ...
Existing accounting solutions for Linux There are a number of existing packages to do network accounting with Linux. The following subsections intend to give a short overview about the most commonly used ones.
nacctd nacctd also known as net-acct is probably the oldest known tool for network accounting under Linux (also works on other Unix-like operating systems). The author of this paper has used nacctd as an accounting tool as early as 1995. It was originally developed by Ulrich Callmeier, but apparently abandoned later on. The development seems to have continued in multiple branches, one of them being the netacct-mysql branch, currently at version 0.79rc2. It's principle of operation is to use an AF_PACKET socket via libpcap in order to capture copies of all packets on configurable network interfaces. It then does TCP/IP header parsing on each packet. Summary information such as port numbers, ip addresses, number of bytes are then stored in an internal table for aggregation of successive packets of the same flow. The table entries are evicted and stored in a human-readable ASCII file. Patches exist for sending information directly into SQL databases, or saving data in machine-readable data format. As a pcap-based solution, it suffers from the performance penalty of copying every full packet to userspace. As a packet-based solution, it suffers from the penalty of having to interpret every single packet.
ipt_LOG based The Linux packet filtering subsystem iptables offers a way to log policy violations via the kernel message ring buffer. This mechanism is called ipt_LOG (or LOG target). Such messages are then further processed by klogd and syslogd, which put them into one or multiple system log files. As ipt_LOG was designed for logging policy violations and not for accounting, it's overhead is significant. Every packet needs to be interpreted in-kernel, then printed in ASCII format to the kernel message ring buffer, then copied from klogd to syslogd, and again copied into a text file. Even worse, most syslog installations are configured to write kernel log messages synchronously to disk, avoiding the usual write buffering of the block I/O layer and disk subsystem. To sum up and anlyze the data, often custom perl scripts are used. Those perl scripts have to parse the LOG lines, build up a table of flows, add the packet size fields and finally export the data in the desired format. Due to the inefficient storage format, performance is again wasted at analyzation time.
ipt_ULOG based (ulogd, ulog-acctd) The iptables ULOG target is a more efficient version of the LOG target described above. Instead of copying ascii messages via the kernel ring buffer, it can be configured to only copies the header of each packet, and send those copies in large batches. A special userspace process, normally ulogd, receives those partial packet copies and does further interpretation. ulogd is intended for logging of security violations and thus resembles the functionality of LOG. it creates one logfile entry per packet. It supports logging in many formats, such as SQL databases or PCAP format. ulog-acctd is a hybrid between ulogd and nacctd. It replaces the nacctd libpcap/PF_PACKET based capture with the more efficient ULOG mechanism. Compared to ipt_LOG, ipt_ULOG reduces the amount of copied data and required kernel/userspace context switches and thus improves performance. However, the whole mechanism is still intended for logging of security violations. Use for accounting is out of its design.
iptables based (ipac-ng) Every packet filtering rule in the Linux packet filter (iptables, or even its predecessor ipchains) has two counters: number of packets and number of bytes matching this particular rule. By carefully placing rules with no target (fallthrough) rules in the packetfilter ruleset, one can implement an accounting setup, i.e. one rule per customer. A number of tools exist to parse the iptables command output and summarized the counters. The most commonly used package is ipac-ng. It supports advanced features such as storing accounting data in SQL databases. The approach works quite efficiently for small installations (i.e. small number of accounting rules). Therefore, the accounting granularity can only be very low. One counter for each single port number at any given ip address is certainly not applicable.
ipt_ACCOUNT ipt_ACCOUNT is a special-purpose iptables target available from the netfilter project patch-o-matic-ng repository. It requires kernel patching and is not included in the mainline kernel. ipt_ACCOUNT keeps byte counters per IP address in a given subnet, up to a '/8' network. Those counters can be read via a special "iptaccount" commandline tool. Being limited to local network segments up to '/8' size, and only having per-ip granularity are two limiteations that defeat ipt_ACCOUNT as a generich accounting mechainism. It's highly-optimized, but also special-purpose.
ntop (including PF_RING) ntop is a network traffic probe to show network usage. It uses libpcap to capture the packets, and then aggregates flows in userspace. On a fundamental level it's therefore similar to what nacctd does. From the ntop project, there's also nProbe, a network traffic probe that exports flow based information in NETFLOW v5/v9 format. To increase performance of the probe, the author (Luca Deri) has implemented PF_RING, a new zero-copy mmap()ed implementation for packet capture. There is a libpcap compatibility layer on top, so any pcap-using application can benefit from PF_RING. PF_RING is a major performance improvement, please look at the documentation and the paper published by Luca Deri. However, ntop / nProbe / PF_RING are all packet-based accounting solutions. Every packet needs to be analyzed by some userspace process - even if there is no copying involved. Due to PF_RING optimiziation, it is probably as efficient as this approach can get.
New ip_conntrack based accounting The fundamental idea is to (ab)use the connection tracking subsystem of the Linux 2.4.x / 2.6.x kernel for accounting purposes. There are several reasons why this is a good fit: It already keeps per-connection state information. Extending this information to contain a set of counters is easy. Lots of routers/firewalls are already running it, and therefore paying it's performance penalty for security reasons. Bumping a couple of counters will introduce very little additional penalty. There was already an (out-of-tree) system to dump connection tracking information to userspace, called ctnetlink So given that a particular machine was already running ip_conntrack, adding flow based acconting to it comes almost for free. I do not advocate the use of ip_conntrack merely for accounting, since that would be again a waste of performance.
ip_conntrack_acct ip_conntrack_acct is how the in-kernel ip_conntrack counters are called. There is a set of four counters: numbers of packets and bytes for original and reply direction of a given connection. If you configure a recent (>= 2.6.9) kernel, it will prompt you for CONFIG_IP_NF_CT_ACCT. By enabling this configuration option, the per-connection counters will be added, and the accounting code will be compiled in. However, there is still no efficient means of reading out those counters. They can be accessed via "cat /proc/net/ip_conntrack", but that's not a real solution. The kernel iterates over all connections and ASCII-formats the data. Also, it is a polling-based mechanism. If the polling interval is too short, connections might get evicted from the state table before their final counters are being read. If the interval is too small, performance will suffer. To counter this problem, a combination of conntrack notifiers and ctnetlink is being used.
conntrack notifiers Conntrack notifiers use the core kernel notifier infrastructure (struct notifier_block) to notify other parts of the kernel about connection tracking events. Such events include creation, deletion and modification of connection tracking entries. The conntrack notifiers can help us overcome the polling architecture. If we'd only listen to "conntrack delete" events, we would always get the byte and packet counters at the end of a connection. However, the events are in-kernel events and therefore not directly suitable for an accounting application to be run in userspace.
ctnetlink ctnetlink (short form for conntrack netlink) is a mechanism for passing connection tracking state information between kernel and userspace, originally developed by Jay Schulist and Harald Welte. As the name implies, it uses Linux AF_NETLINK sockets as its underlying communication facility. The focus of ctnetlink is to selectively read or dump entries from the connection tracking table to userspace. It also allows userspace processes to delete and create conntrack entries as well as "conntrack expectations". The initial nature of ctnetlink is therefore again polling-based. An userspace process sends a request for certain information, the kernel responds with the requested information. By combining conntrack notifiers with ctnetlink, it is possible to register a notifier handler that in turn sends ctnetlink event messages down the AF_NETLINK socket. A userspace process can now listen for such DELETE event messages at the socket, and put the counters into it's accounting storage. There are still some shortcomings inherent to that DELETE event scheme: We only know the amount of traffic after the connection is over. If a connection lasts for a long time (let's say days, weeks), then it is impossible to use this form of accounting for any kind of quota-based billing, where the user would be informed (or disconnected, traffic shaped, whatever) when he exceeds his quota. Also, the conntrack entry does not contain information about when the connection started - only the timestamp of the end-of-connection is known. To overcome limitation number one, the accounting process can use a combined event and polling scheme. The granularity of accounting can therefore be configured by the polling interval, and a compromise between performance and accuracy can be made. To overcome the second limitation, the accounting process can also listen for NEW event messages. By correlating the NEW and DELETE messages of a connection, accounting datasets containign start and end of connection can be built.
ulogd2 As described earlier in this paper, ulogd is a userspace packet filter logging daemon that is already used for packet-based accounting, even if it isn't the best fit. ulogd2, also developed by the author of this paper, takes logging beyond per-packet based information, but also includes support for per-connection or per-flow based data. Instead of supporting only ipt_ULOG input, a number of interpreter and output plugins, ulogd2 supports a concept called plugin stacks. Multiple stacks can exist within one deamon. Any such stack consists out of plugins. A plugin can be a source, sink or filter. Sources acquire per-packet or per-connection data from ipt_ULOG or ip_contnrack_acct. Filters allow the user to filter or aggregate information. Filtering is requird, since there is no way to filter the ctnetlink event messages within the kernel. Either the functionality is enabled or not. Multiple connections can be aggregated to a larger, encompassing flow. Packets could be aggregated to flows (like nacctd), and flows can be aggregated to even larger flows. Sink plugins store the resulting data to some form of non-volatile storage, such as SQL databases, binary or ascii files. Another sink is a NETFLOW or IPFIX sink, exporting information in industy-standard format for flow based accounting.
Status of implementation ip_conntrack_acct is already in the kernel since 2.6.9. ctnetlink and the conntrack event notifiers are considered stable and will be submitted for mainline inclusion soon. Both are available from the patch-o-matic-ng repository of the netfilter project. At the time of writing of this paper, ulogd2 development was not yet finished. However, the ctnetlink event messages can already be dumped by the use of the "conntrack" userspace program, available from the netfilter project. The "conntrack" prorgram can listen to the netlink event socket and dump the information in human-readable form (one ASCII line per ctnetlink message) to stdout. Custom accounting solutions can read this information from stdin, parse and process it according to their needs.
Summary Despite the large number of available accounting tools, the author is confident that inventing yet another one is worthwhile. Many existing implementations suffer from performance issues by design. Most of them are very special-purpose. nProbe/ntop together with PF_RING are probably the most universal and efficient solution for any accounting problem. Still, the new ip_conntrack_acct, ctnetlink based mechanism described in this paper has a clear performance advantage if you want to do acconting on your Linux-based stateful packetfilter - which is a common case. The firewall is suposed to be at the edge of your network, exactly where you usually do accounting of ingress and/or egress traffic.