Adding accounting to OpenSSH

[ Home page | OpenSSH patches ]

This is a bit of a hack, especially since the SSH2 protocol is implemented in a very different way from SSH1 in OpenSSH (presumably because the SSH1 code is based on the original Tatu `Trademarks' Ylonen implementation, whereas the SSH2 implementation is novel).

The basic game is that we have a pair of global variables in the server for the amount of data read and written; in the patch, these live in account.c and are called total_read and total_written. Then, the packet input/output methods in packet.c update these variables when data are received from or sent to the connected host.

The question now becomes, `when should the values of the counters should be reported?' What I have done is to take the following (ugly) approach: reporting takes place immediately prior to calling fork(2), whenever a `channel' is closed, and immediately before the program exits. This seems to cover all the bases, but I may have missed something. I do not think that the code will ever double-count. The counters are reset whenever their values are reported, so a user's total bandwidth usage is the sum of all the reported values. Unfortunately, there may be more than one report line per session, but this is unavoidable without a considerably more complex approach.

Obviously this doesn't log the bandwidth used by TCP and IP packet headers. The simplest approach (assuming that logging in the kernel is not possible) here would be to analyse a bunch of traffic, and find out what fraction of it is TCP and IP headers; then estimate the total traffic. This would be fairly simple to do; in fact, all that needs to be done is to find the mean length of a TCP packet in SSH traffic. Then, since an IP header is 20 bytes and a TCP header also 20 bytes, a plausible estimate is

\hbox{total traffic} = \frac { \langle \hbox{packet length } \rangle } { \langle \hbox{packet length } \rangle - 40 } \times \hbox{traffic without headers}

(Note that if you want to count the ethernet headers, you need to take account of another 14 bytes per packet.) Something like

tcpdump -qlni $interface port 22 | awk 'BEGIN { total = 0; } { total += $7 + 40; print $7 + 40" \tmean = "total / NR;}'

will get you an estimate of this. (Versions of tcpdump differ in where they print the data length in the packet; experiment to get it right). I find that 72 bytes is about correct, so that the multiplier is about 2.25, but this is on a system with lots of PPP-over-SSH VPN traffic, so do your own measurements.

Copyright (c) 2001 Chris Lightfoot. All rights reserved.