<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>iptables</title>
  <link rel="alternate" type="text/html" href="http://tumbleweed.org.za/tags/iptables"/>
  <link rel="self" type="application/atom+xml" href="http://tumbleweed.org.za/taxonomy/term/173/atom/feed"/>
  <id>http://tumbleweed.org.za/taxonomy/term/173/atom/feed</id>
  <updated>2008-09-19T00:06:06+00:00</updated>
  <entry>
    <title>Bandwidth accounting with ulogd</title>
    <link rel="alternate" type="text/html" href="http://tumbleweed.org.za/2008/04/02/bandwidth-accounting-ulogd" />
    <id>http://tumbleweed.org.za/2008/04/02/bandwidth-accounting-ulogd</id>
    <published>2008-04-02T22:31:36+00:00</published>
    <updated>2008-09-19T00:06:06+00:00</updated>
    <author>
      <name>tumbleweed</name>
    </author>
    <category term="accounting" />
    <category term="debian" />
    <category term="iptables" />
    <category term="mysql" />
    <category term="technical" />
    <category term="traffic" />
    <category term="ulogd" />
    <summary type="html"><![CDATA[<p>My <a href="/2008/04/02/my-first-real-debian-repo">post about repositories</a> wasn&#8217;t just a <em>little</em> attempt to stave off work, it was part of a&nbsp;larger&nbsp;scheme.</p>

<p>I share the <span class="caps"><span class="caps">ADSL</span></span> line in my digs with 3 other people. We do <a href="/local-only-dsl">split-routing</a> to save money, but we still have to divide the phone bill at the end of the month. Rather than buy a fixed cap, and have a fight over who&#8217;s fault it was when we get capped, we are running a pay-per-use system (with local use free, subsidised by me). It means you don&#8217;t have to restrain yourself for the common cap, but it also means I need to calculate who&nbsp;owes&nbsp;what.</p>

<p>For the first month, I used my old standby, <a href="http://bandwidthd.sourceforge.net/">bandwidthd</a>. It uses pcap to count traffic, and gives you totals and graphs. For simplicity of logging, I gave each person a /28 for their machines and configured static <span class="caps"><span class="caps">DHCP</span></span> leases. Then bandwidthd totalled up the internet use for&nbsp;each&nbsp;/28.</p>

<p>This was sub-optimal. bandwidthd either sees the local network, in which case it can&#8217;t see which packets went out over which link. Or it can watch the international link, but then not know which user&nbsp;is&nbsp;responsible.</p>

<p>I could have installed some netflow utilities at this point, but I wanted to roll my own with the correct Linux approach (ulog) rather than any pcapping. <a href="http://www.netfilter.org/projects/ulogd/index.html">ulogd</a> is the easy&nbsp;ulog&nbsp;solution.</p>

<p>Ulogd can pick up packets that you &#8220;-j <span class="caps"><span class="caps">ULOG</span></span>&#8221; from iptables. It receives them over a netlink interface. You can tell iptables how many bytes of each packet to send, and how many to queue up before sending&nbsp;them.&nbsp;E.g.</p>

<div class="geshifilter"><div class="text geshifilter-text" style="font-family:monospace;"># iptables -I <span class="caps"><span class="caps">INPUT</span></span> 1 -j <span class="caps"><span class="caps">ULOG</span></span> &#8212;ulog-nlgroup 1 &#8212;ulog-qthreshold 50 &#8212;ulog-cprange 48 &#8212;ulog-prefix input</div></div>

<p>will log the first 48 bytes of any incoming packet to netlink-group 1. It will tag the packet as being &#8220;input&#8221;, and send them in batches of 50. 48 bytes is usually enough to catch any data you could want from the headers. If you were only need size, 4 bytes will do, and for source and destination as&nbsp;well,&nbsp;20.</p>

<p>Now, we tell ulogd to listen for this stuff and log it. Ulogd has a pluggable architecture. IPv4 decoding is a plugin, and there are various logging plugins for &#8220;-j <span class="caps"><span class="caps">LOG</span></span>&#8221; emulation, Text files, pcap-files, MySQL, PostgreSQL, and SQLite. For my purposes, I used MySQL as the router in question already had MySQL on it (for <a href="http://cacti.sourceforge.net/">Cacti</a>). Otherwise, I would have opted for SQLite. Be warned that the etch version of ulogd doesn&#8217;t automatically reconnect to the MySQL server should the connection break for any reason. I <a href="http://mirrors.tumbleweed.org.za/sr-backports/pool/main/u/ulogd/">backported the lenny version</a> to etch to get around that. (You also need to provide the <span class="geshifilter"><code class="geshifilter-text">reconnect</code></span> and <span class="geshifilter"><code class="geshifilter-text">connect_timeout</code></span>&nbsp;options.)</p>

<p>Besides the reconnection issue, the <span class="caps"><span class="caps">SQL</span></span> implementations are quite nice. They have a set schema, and you just need to create a table with the columns in it that you are interested in. No other configuration (beyond connection details)&nbsp;is&nbsp;necessary.</p>

<p>My&nbsp;MySQL&nbsp;table:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=CREATE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">CREATE</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=TABLE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">TABLE</span></span></span></a> <span class="st0">`ulog`</span> <span class="br0">&#40;</span><br />
&nbsp; <span class="st0">`id`</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4">int</span></a><span class="br0">&#40;</span><span class="nu0">10</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6">unsigned</span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=AUTO_INCREMENT&amp;lr=lang_en"><span class="kw6">auto_increment</span></a><span class="sy2">,</span><br />
&nbsp; <span class="st0">`oob<span class="es1">_</span>time<span class="es1">_</span>sec`</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4">int</span></a><span class="br0">&#40;</span><span class="nu0">10</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6">unsigned</span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a><span class="sy2">,</span><br />
&nbsp; <span class="st0">`oob<span class="es1">_</span>prefix`</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/string-functions.html"><span class="kw14">char</span></a><span class="br0">&#40;</span><span class="nu0">4</span><span class="br0">&#41;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a><span class="sy2">,</span><br />
&nbsp; <span class="st0">`ip<span class="es1">_</span>totlen`</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=SMALLINT&amp;lr=lang_en"><span class="kw4">smallint</span></a><span class="br0">&#40;</span><span class="nu0">5</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6">unsigned</span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=PRIMARY%20KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">PRIMARY</span></span> <span class="caps"><span class="caps">KEY</span></span></span></a> &nbsp;<span class="br0">&#40;</span><span class="st0">`id`</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNIQUE&amp;lr=lang_en"><span class="kw6"><span class="caps"><span class="caps">UNIQUE</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">KEY</span></span></span></a> <span class="st0">`id`</span> <span class="br0">&#40;</span><span class="st0">`id`</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">KEY</span></span></span></a> <span class="st0">`oob<span class="es1">_</span>prefix`</span> <span class="br0">&#40;</span><span class="st0">`oob<span class="es1">_</span>prefix`</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">KEY</span></span></span></a> <span class="st0">`oob<span class="es1">_</span>time<span class="es1">_</span>sec`</span> <span class="br0">&#40;</span><span class="st0">`oob<span class="es1">_</span>time<span class="es1">_</span>sec`</span><span class="br0">&#41;</span><br />
<span class="br0">&#41;</span><span class="sy2">;</span></div></div>

<p>My&nbsp;ulogd.conf:</p>

<div class="geshifilter"><div class="ini geshifilter-ini" style="font-family:monospace;"><span class="re0"><span class="br0">&#91;</span>global<span class="br0">&#93;</span></span><br />
# netlink multicast group <span class="br0">&#40;</span>the same as the iptables &#8212;ulog-nlgroup param<span class="br0">&#41;</span><br />
<span class="re1">nlgroup</span><span class="sy0">=</span><span class="re2"><span class="nu0">1</span> &nbsp; &nbsp;</span><br />
# logfile for status messages<br />
<span class="re1">logfile</span><span class="sy0">=</span><span class="st0">&quot;/var/log/ulog/ulogd.log&quot;</span> &nbsp; &nbsp;<br />
# loglevel: debug<span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span>, info<span class="br0">&#40;</span><span class="nu0">3</span><span class="br0">&#41;</span>, notice<span class="br0">&#40;</span><span class="nu0">5</span><span class="br0">&#41;</span>, error<span class="br0">&#40;</span><span class="nu0">7</span><span class="br0">&#41;</span> or fatal<span class="br0">&#40;</span><span class="nu0">8</span><span class="br0">&#41;</span><br />
<span class="re1">loglevel</span><span class="sy0">=</span><span class="re2"><span class="nu0">5</span> &nbsp; &nbsp;</span><br />
# socket receive buffer size <span class="br0">&#40;</span>should be at least the size of the<br />
# in-kernel buffer <span class="br0">&#40;</span>ipt_ULOG.o &#8216;nlbufsiz&#8217; parameter<span class="br0">&#41;</span><br />
<span class="re1">rmem</span><span class="sy0">=</span><span class="re2"><span class="nu0">131071</span> &nbsp; &nbsp;</span><br />
# libipulog/ulogd receive buffer size, should be &gt; rmem<br />
<span class="re1">bufsize</span><span class="sy0">=</span><span class="re2">150000</span><br />
# ulogd_BASE.so - interpreter plugin for basic IPv4 header fields<br />
# &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; you will always need this<br />
<span class="re1">plugin</span><span class="sy0">=</span><span class="st0">&quot;/usr/lib/ulogd/ulogd_BASE.so&quot;</span><br />
<span class="re1">plugin</span><span class="sy0">=</span><span class="st0">&quot;/usr/lib/ulogd/ulogd_MYSQL.so&quot;</span><br />
<br />
<span class="re0"><span class="br0">&#91;</span><span class="caps"><span class="caps">MYSQL</span></span><span class="br0">&#93;</span></span><br />
<span class="re1">table</span><span class="sy0">=</span><span class="st0">&quot;ulog&quot;</span><br />
<span class="re1">pass</span><span class="sy0">=</span><span class="st0">&quot;foo&quot;</span><br />
<span class="re1">user</span><span class="sy0">=</span><span class="st0">&quot;ulog&quot;</span><br />
<span class="re1">db</span><span class="sy0">=</span><span class="st0">&quot;ulog&quot;</span><br />
<span class="re1">host</span><span class="sy0">=</span><span class="st0">&quot;localhost&quot;</span><br />
<span class="re1">reconnect</span><span class="sy0">=</span><span class="re2">5</span><br />
<span class="re1">connect_timeout</span><span class="sy0">=</span><span class="re2">10</span></div></div>

<p>The relevant parts of my&nbsp;firewall&nbsp;rules:</p>

<div class="geshifilter"><div class="bash geshifilter-bash" style="font-family:monospace;"><span class="co0"># Count proxy usage (transparent and explicit)</span><br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-p</span> <span class="sy0">!</span> tcp <span class="re5">-j</span> <span class="caps"><span class="caps">RETURN</span></span><br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-p</span> tcp <span class="re5">-m</span> multiport <span class="re5">&#8212;destination-ports</span> <span class="sy0">!</span> <span class="nu0">3128</span>,<span class="nu0">8080</span> <span class="re5">-j</span> <span class="caps"><span class="caps">RETURN</span></span><br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-s</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-p<br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-s</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-p<br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-s</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-p<br />
<br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-p</span> <span class="sy0">!</span> tcp <span class="re5">-j</span> <span class="caps"><span class="caps">RETURN</span></span><br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-p</span> tcp <span class="re5">-m</span> multiport <span class="re5">&#8212;source-ports</span> <span class="sy0">!</span> <span class="nu0">3128</span>,<span class="nu0">8080</span> <span class="re5">-j</span> <span class="caps"><span class="caps">RETURN</span></span><br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-d</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-p<br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-d</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-p<br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-d</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-p<br />
<br />
<span class="co0"># Count forwarded traffic (excluding local internet connection - ppp2)</span><br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-i</span> ppp2 <span class="re5">-j</span> <span class="caps"><span class="caps">RETURN</span></span><br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-d</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-f<br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-d</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-f<br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-d</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-f<br />
<br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-o</span> ppp2 <span class="re5">-j</span> <span class="caps"><span class="caps">RETURN</span></span><br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-s</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-f<br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-s</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-f<br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-s</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-f<br />
<br />
<span class="co0"># Glue</span><br />
iptables <span class="re5">-A</span> <span class="caps"><span class="caps">INPUT</span></span> <span class="re5">-i</span> eth0 <span class="re5">-j</span> count-from-inside<br />
iptables <span class="re5">-A</span> <span class="caps"><span class="caps">OUTPUT</span></span> &nbsp;<span class="re5">-o</span> eth0 <span class="re5">-j</span> count-to-inside<br />
iptables <span class="re5">-A</span> <span class="caps"><span class="caps">FORWARD</span></span> <span class="re5">-i</span> ppp+ <span class="re5">-j</span> count-forward-in<br />
iptables <span class="re5">-A</span> <span class="caps"><span class="caps">FORWARD</span></span> <span class="re5">-o</span> ppp+ <span class="re5">-j</span> count-forward-out</div></div>

<p>So, traffic for my /28 (sr) will be counted as <span class="geshifilter"><code class="geshifilter-text">sr-f</code></span> or <span class="geshifilter"><code class="geshifilter-text">sr-p</code></span> so I can tally up proxy <span class="amp">&amp;</span> forwarded traffic separately. (Yes, I can count traffic with squid too, but doing it all in one place is simpler.) <span class="geshifilter"><code class="geshifilter-text">fb</code></span> is random housemate Foo Bar, and <span class="geshifilter"><code class="geshifilter-text">gu</code></span> guest (unreserved&nbsp;<span class="caps">IP</span>&nbsp;addresses).</p>

<p>You can query the usage this month with&nbsp;for&nbsp;example:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=SELECT&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">SELECT</span></span></span></a> oob_prefix<span class="sy2">,</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/group-by-functions-and-modifiers.html"><span class="kw22"><span class="caps"><span class="caps">SUM</span></span></span></a><span class="br0">&#40;</span>ip_totlen<span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=FROM&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">FROM</span></span></span></a> ulog <a href="http://search.mysql.com/search?site=refman-51&amp;q=WHERE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">WHERE</span></span></span></a> oob_time_sec <span class="sy1">&gt;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-01 00:00:00&#8217;</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=GROUP%20BY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">GROUP</span></span> <span class="caps"><span class="caps">BY</span></span></span></a> oob_prefix<span class="sy2">;</span></div></div>

<p>Your table will fill up fast. We are averaging around 200 000 rows per day. So obviously some caching is&nbsp;in&nbsp;order:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=CREATE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">CREATE</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=TABLE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">TABLE</span></span></span></a> daily <span class="br0">&#40;</span><br />
&nbsp; id <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4"><span class="caps"><span class="caps">INT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6"><span class="caps"><span class="caps">UNSIGNED</span></span></span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=AUTO_INCREMENT&amp;lr=lang_en"><span class="kw6">AUTO_INCREMENT</span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=TIME&amp;lr=lang_en"><span class="kw5">time</span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=TIMESTAMP&amp;lr=lang_en"><span class="kw4"><span class="caps"><span class="caps">TIMESTAMP</span></span></span></a><span class="sy2">,</span><br />
&nbsp; oob_prefix <a href="http://dev.mysql.com/doc/refman/5.1/en/string-functions.html"><span class="kw14"><span class="caps"><span class="caps">CHAR</span></span></span></a><span class="br0">&#40;</span><span class="nu0">4</span><span class="br0">&#41;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=DATA&amp;lr=lang_en"><span class="kw1">data</span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4"><span class="caps"><span class="caps">INT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6"><span class="caps"><span class="caps">UNSIGNED</span></span></span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=PRIMARY%20KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">PRIMARY</span></span> <span class="caps"><span class="caps">KEY</span></span></span></a> <span class="br0">&#40;</span>id<span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">KEY</span></span></span></a> <span class="br0">&#40;</span>oob_prefix<span class="br0">&#40;</span><span class="nu0">4</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">KEY</span></span></span></a> <span class="br0">&#40;</span><a href="http://search.mysql.com/search?site=refman-51&amp;q=TIME&amp;lr=lang_en"><span class="kw5">time</span></a><span class="br0">&#41;</span><br />
<span class="br0">&#41;</span><span class="sy2">;</span></div></div>

<p>And every night, run&nbsp;something&nbsp;like:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=INSERT&amp;lr=lang_en"><span class="kw2"><span class="caps"><span class="caps">INSERT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INTO&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">INTO</span></span></span></a> daily <span class="br0">&#40;</span><a href="http://search.mysql.com/search?site=refman-51&amp;q=TIME&amp;lr=lang_en"><span class="kw5">time</span></a><span class="sy2">,</span> oob_prefix<span class="sy2">,</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=DATA&amp;lr=lang_en"><span class="kw1">data</span></a><span class="br0">&#41;</span><br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=SELECT&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">SELECT</span></span></span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">FROM_UNIXTIME</span></a><span class="br0">&#40;</span><a href="http://dev.mysql.com/doc/refman/5.1/en/group-by-functions-and-modifiers.html"><span class="kw22"><span class="caps"><span class="caps">MAX</span></span></span></a><span class="br0">&#40;</span>oob_time_sec<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy2">,</span> oob_prefix<span class="sy2">,</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/group-by-functions-and-modifiers.html"><span class="kw22"><span class="caps"><span class="caps">SUM</span></span></span></a><span class="br0">&#40;</span>ip_totlen<span class="br0">&#41;</span><br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=FROM&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">FROM</span></span></span></a> ulog<br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=WHERE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">WHERE</span></span></span></a> oob_time_sec <span class="sy1">&gt;=</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-01 00:00:00&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">AND</span></span></span></a> oob_time_sec <span class="sy1">&lt;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-02 00:00:00&#8217;</span><span class="br0">&#41;</span><br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=GROUP%20BY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">GROUP</span></span> <span class="caps"><span class="caps">BY</span></span></span></a> oob_prefix<span class="sy2">;</span><br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=DELETE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">DELETE</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=FROM&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">FROM</span></span></span></a> ulog <a href="http://search.mysql.com/search?site=refman-51&amp;q=WHERE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">WHERE</span></span></span></a> oob_time_sec &nbsp;<span class="sy1">&gt;=</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-01 00:00:00&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">AND</span></span></span></a> oob_time_sec <span class="sy1">&lt;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-02 00:00:00&#8217;</span><span class="br0">&#41;</span><span class="sy2">;</span></div></div>

<p>Finally, I have a simple little <span class="caps"><span class="caps">PHP</span></span> script that provides reporting and calculates&nbsp;dues.&nbsp;Done.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>My <a href="/2008/04/02/my-first-real-debian-repo">post about repositories</a> wasn&#8217;t just a <em>little</em> attempt to stave off work, it was part of a larger&nbsp;scheme.</p>

<p>I share the <span class="caps">ADSL</span> line in my digs with 3 other people. We do <a href="/local-only-dsl">split-routing</a> to save money, but we still have to divide the phone bill at the end of the month. Rather than buy a fixed cap, and have a fight over who&#8217;s fault it was when we get capped, we are running a pay-per-use system (with local use free, subsidised by me). It means you don&#8217;t have to restrain yourself for the common cap, but it also means I need to calculate who owes&nbsp;what.</p>

<p>For the first month, I used my old standby, <a href="http://bandwidthd.sourceforge.net/">bandwidthd</a>. It uses pcap to count traffic, and gives you totals and graphs. For simplicity of logging, I gave each person a /28 for their machines and configured static <span class="caps">DHCP</span> leases. Then bandwidthd totalled up the internet use for each&nbsp;/28.</p>

<p>This was sub-optimal. bandwidthd either sees the local network, in which case it can&#8217;t see which packets went out over which link. Or it can watch the international link, but then not know which user is&nbsp;responsible.</p>

<p>I could have installed some netflow utilities at this point, but I wanted to roll my own with the correct Linux approach (ulog) rather than any pcapping. <a href="http://www.netfilter.org/projects/ulogd/index.html">ulogd</a> is the easy ulog&nbsp;solution.</p>

<p>Ulogd can pick up packets that you &#8220;-j <span class="caps">ULOG</span>&#8221; from iptables. It receives them over a netlink interface. You can tell iptables how many bytes of each packet to send, and how many to queue up before sending them.&nbsp;E.g.</p>

<div class="geshifilter"><div class="text geshifilter-text" style="font-family:monospace;"># iptables -I <span class="caps">INPUT</span> 1 -j <span class="caps">ULOG</span> &#8212;ulog-nlgroup 1 &#8212;ulog-qthreshold 50 &#8212;ulog-cprange 48 &#8212;ulog-prefix input</div></div>

<p>will log the first 48 bytes of any incoming packet to netlink-group 1. It will tag the packet as being &#8220;input&#8221;, and send them in batches of 50. 48 bytes is usually enough to catch any data you could want from the headers. If you were only need size, 4 bytes will do, and for source and destination as well,&nbsp;20.</p>

<p>Now, we tell ulogd to listen for this stuff and log it. Ulogd has a pluggable architecture. IPv4 decoding is a plugin, and there are various logging plugins for &#8220;-j <span class="caps">LOG</span>&#8221; emulation, Text files, pcap-files, MySQL, PostgreSQL, and SQLite. For my purposes, I used MySQL as the router in question already had MySQL on it (for <a href="http://cacti.sourceforge.net/">Cacti</a>). Otherwise, I would have opted for SQLite. Be warned that the etch version of ulogd doesn&#8217;t automatically reconnect to the MySQL server should the connection break for any reason. I <a href="http://mirrors.tumbleweed.org.za/sr-backports/pool/main/u/ulogd/">backported the lenny version</a> to etch to get around that. (You also need to provide the <code>reconnect</code> and <code>connect_timeout</code>&nbsp;options.)</p>

<p>Besides the reconnection issue, the <span class="caps">SQL</span> implementations are quite nice. They have a set schema, and you just need to create a table with the columns in it that you are interested in. No other configuration (beyond connection details) is&nbsp;necessary.</p>

<p>My MySQL&nbsp;table:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=CREATE&amp;lr=lang_en"><span class="kw1"><span class="caps">CREATE</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=TABLE&amp;lr=lang_en"><span class="kw1"><span class="caps">TABLE</span></span></a> <span class="st0">`ulog`</span> <span class="br0">&#40;</span><br />
&nbsp; <span class="st0">`id`</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4">int</span></a><span class="br0">&#40;</span><span class="nu0">10</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6">unsigned</span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps">NULL</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=AUTO_INCREMENT&amp;lr=lang_en"><span class="kw6">auto_increment</span></a><span class="sy2">,</span><br />
&nbsp; <span class="st0">`oob<span class="es1">_</span>time<span class="es1">_</span>sec`</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4">int</span></a><span class="br0">&#40;</span><span class="nu0">10</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6">unsigned</span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps">NULL</span></span></a><span class="sy2">,</span><br />
&nbsp; <span class="st0">`oob<span class="es1">_</span>prefix`</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/string-functions.html"><span class="kw14">char</span></a><span class="br0">&#40;</span><span class="nu0">4</span><span class="br0">&#41;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps">NULL</span></span></a><span class="sy2">,</span><br />
&nbsp; <span class="st0">`ip<span class="es1">_</span>totlen`</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=SMALLINT&amp;lr=lang_en"><span class="kw4">smallint</span></a><span class="br0">&#40;</span><span class="nu0">5</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6">unsigned</span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps">NULL</span></span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=PRIMARY%20KEY&amp;lr=lang_en"><span class="kw1"><span class="caps">PRIMARY</span> <span class="caps">KEY</span></span></a> &nbsp;<span class="br0">&#40;</span><span class="st0">`id`</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNIQUE&amp;lr=lang_en"><span class="kw6"><span class="caps">UNIQUE</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps">KEY</span></span></a> <span class="st0">`id`</span> <span class="br0">&#40;</span><span class="st0">`id`</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps">KEY</span></span></a> <span class="st0">`oob<span class="es1">_</span>prefix`</span> <span class="br0">&#40;</span><span class="st0">`oob<span class="es1">_</span>prefix`</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps">KEY</span></span></a> <span class="st0">`oob<span class="es1">_</span>time<span class="es1">_</span>sec`</span> <span class="br0">&#40;</span><span class="st0">`oob<span class="es1">_</span>time<span class="es1">_</span>sec`</span><span class="br0">&#41;</span><br />
<span class="br0">&#41;</span><span class="sy2">;</span></div></div>

<p>My&nbsp;ulogd.conf:</p>

<div class="geshifilter"><div class="ini geshifilter-ini" style="font-family:monospace;"><span class="re0"><span class="br0">&#91;</span>global<span class="br0">&#93;</span></span><br />
# netlink multicast group <span class="br0">&#40;</span>the same as the iptables &#8212;ulog-nlgroup param<span class="br0">&#41;</span><br />
<span class="re1">nlgroup</span><span class="sy0">=</span><span class="re2"><span class="nu0">1</span> &nbsp; &nbsp;</span><br />
# logfile for status messages<br />
<span class="re1">logfile</span><span class="sy0">=</span><span class="st0">&quot;/var/log/ulog/ulogd.log&quot;</span> &nbsp; &nbsp;<br />
# loglevel: debug<span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span>, info<span class="br0">&#40;</span><span class="nu0">3</span><span class="br0">&#41;</span>, notice<span class="br0">&#40;</span><span class="nu0">5</span><span class="br0">&#41;</span>, error<span class="br0">&#40;</span><span class="nu0">7</span><span class="br0">&#41;</span> or fatal<span class="br0">&#40;</span><span class="nu0">8</span><span class="br0">&#41;</span><br />
<span class="re1">loglevel</span><span class="sy0">=</span><span class="re2"><span class="nu0">5</span> &nbsp; &nbsp;</span><br />
# socket receive buffer size <span class="br0">&#40;</span>should be at least the size of the<br />
# in-kernel buffer <span class="br0">&#40;</span>ipt_ULOG.o &#8216;nlbufsiz&#8217; parameter<span class="br0">&#41;</span><br />
<span class="re1">rmem</span><span class="sy0">=</span><span class="re2"><span class="nu0">131071</span> &nbsp; &nbsp;</span><br />
# libipulog/ulogd receive buffer size, should be &gt; rmem<br />
<span class="re1">bufsize</span><span class="sy0">=</span><span class="re2">150000</span><br />
# ulogd_BASE.so - interpreter plugin for basic IPv4 header fields<br />
# &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; you will always need this<br />
<span class="re1">plugin</span><span class="sy0">=</span><span class="st0">&quot;/usr/lib/ulogd/ulogd_BASE.so&quot;</span><br />
<span class="re1">plugin</span><span class="sy0">=</span><span class="st0">&quot;/usr/lib/ulogd/ulogd_MYSQL.so&quot;</span><br />
<br />
<span class="re0"><span class="br0">&#91;</span><span class="caps">MYSQL</span><span class="br0">&#93;</span></span><br />
<span class="re1">table</span><span class="sy0">=</span><span class="st0">&quot;ulog&quot;</span><br />
<span class="re1">pass</span><span class="sy0">=</span><span class="st0">&quot;foo&quot;</span><br />
<span class="re1">user</span><span class="sy0">=</span><span class="st0">&quot;ulog&quot;</span><br />
<span class="re1">db</span><span class="sy0">=</span><span class="st0">&quot;ulog&quot;</span><br />
<span class="re1">host</span><span class="sy0">=</span><span class="st0">&quot;localhost&quot;</span><br />
<span class="re1">reconnect</span><span class="sy0">=</span><span class="re2">5</span><br />
<span class="re1">connect_timeout</span><span class="sy0">=</span><span class="re2">10</span></div></div>

<p>The relevant parts of my firewall&nbsp;rules:</p>

<div class="geshifilter"><div class="bash geshifilter-bash" style="font-family:monospace;"><span class="co0"># Count proxy usage (transparent and explicit)</span><br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-p</span> <span class="sy0">!</span> tcp <span class="re5">-j</span> <span class="caps">RETURN</span><br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-p</span> tcp <span class="re5">-m</span> multiport <span class="re5">&#8212;destination-ports</span> <span class="sy0">!</span> <span class="nu0">3128</span>,<span class="nu0">8080</span> <span class="re5">-j</span> <span class="caps">RETURN</span><br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-s</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-p<br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-s</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-p<br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-s</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-p<br />
<br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-p</span> <span class="sy0">!</span> tcp <span class="re5">-j</span> <span class="caps">RETURN</span><br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-p</span> tcp <span class="re5">-m</span> multiport <span class="re5">&#8212;source-ports</span> <span class="sy0">!</span> <span class="nu0">3128</span>,<span class="nu0">8080</span> <span class="re5">-j</span> <span class="caps">RETURN</span><br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-d</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-p<br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-d</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-p<br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-d</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-p<br />
<br />
<span class="co0"># Count forwarded traffic (excluding local internet connection - ppp2)</span><br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-i</span> ppp2 <span class="re5">-j</span> <span class="caps">RETURN</span><br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-d</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-f<br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-d</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-f<br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-d</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-f<br />
<br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-o</span> ppp2 <span class="re5">-j</span> <span class="caps">RETURN</span><br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-s</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-f<br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-s</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-f<br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-s</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-f<br />
<br />
<span class="co0"># Glue</span><br />
iptables <span class="re5">-A</span> <span class="caps">INPUT</span> <span class="re5">-i</span> eth0 <span class="re5">-j</span> count-from-inside<br />
iptables <span class="re5">-A</span> <span class="caps">OUTPUT</span> &nbsp;<span class="re5">-o</span> eth0 <span class="re5">-j</span> count-to-inside<br />
iptables <span class="re5">-A</span> <span class="caps">FORWARD</span> <span class="re5">-i</span> ppp+ <span class="re5">-j</span> count-forward-in<br />
iptables <span class="re5">-A</span> <span class="caps">FORWARD</span> <span class="re5">-o</span> ppp+ <span class="re5">-j</span> count-forward-out</div></div>

<p>So, traffic for my /28 (sr) will be counted as <code>sr-f</code> or <code>sr-p</code> so I can tally up proxy <span class="amp">&amp;</span> forwarded traffic separately. (Yes, I can count traffic with squid too, but doing it all in one place is simpler.) <code>fb</code> is random housemate Foo Bar, and <code>gu</code> guest (unreserved IP&nbsp;addresses).</p>

<p>You can query the usage this month with for&nbsp;example:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=SELECT&amp;lr=lang_en"><span class="kw1"><span class="caps">SELECT</span></span></a> oob_prefix<span class="sy2">,</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/group-by-functions-and-modifiers.html"><span class="kw22"><span class="caps">SUM</span></span></a><span class="br0">&#40;</span>ip_totlen<span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=FROM&amp;lr=lang_en"><span class="kw1"><span class="caps">FROM</span></span></a> ulog <a href="http://search.mysql.com/search?site=refman-51&amp;q=WHERE&amp;lr=lang_en"><span class="kw1"><span class="caps">WHERE</span></span></a> oob_time_sec <span class="sy1">&gt;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-01 00:00:00&#8217;</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=GROUP%20BY&amp;lr=lang_en"><span class="kw1"><span class="caps">GROUP</span> <span class="caps">BY</span></span></a> oob_prefix<span class="sy2">;</span></div></div>

<p>Your table will fill up fast. We are averaging around 200 000 rows per day. So obviously some caching is in&nbsp;order:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=CREATE&amp;lr=lang_en"><span class="kw1"><span class="caps">CREATE</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=TABLE&amp;lr=lang_en"><span class="kw1"><span class="caps">TABLE</span></span></a> daily <span class="br0">&#40;</span><br />
&nbsp; id <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4"><span class="caps">INT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6"><span class="caps">UNSIGNED</span></span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps">NULL</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=AUTO_INCREMENT&amp;lr=lang_en"><span class="kw6">AUTO_INCREMENT</span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=TIME&amp;lr=lang_en"><span class="kw5">time</span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=TIMESTAMP&amp;lr=lang_en"><span class="kw4"><span class="caps">TIMESTAMP</span></span></a><span class="sy2">,</span><br />
&nbsp; oob_prefix <a href="http://dev.mysql.com/doc/refman/5.1/en/string-functions.html"><span class="kw14"><span class="caps">CHAR</span></span></a><span class="br0">&#40;</span><span class="nu0">4</span><span class="br0">&#41;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps">NULL</span></span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=DATA&amp;lr=lang_en"><span class="kw1">data</span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4"><span class="caps">INT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6"><span class="caps">UNSIGNED</span></span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps">NULL</span></span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=PRIMARY%20KEY&amp;lr=lang_en"><span class="kw1"><span class="caps">PRIMARY</span> <span class="caps">KEY</span></span></a> <span class="br0">&#40;</span>id<span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps">KEY</span></span></a> <span class="br0">&#40;</span>oob_prefix<span class="br0">&#40;</span><span class="nu0">4</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps">KEY</span></span></a> <span class="br0">&#40;</span><a href="http://search.mysql.com/search?site=refman-51&amp;q=TIME&amp;lr=lang_en"><span class="kw5">time</span></a><span class="br0">&#41;</span><br />
<span class="br0">&#41;</span><span class="sy2">;</span></div></div>

<p>And every night, run something&nbsp;like:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=INSERT&amp;lr=lang_en"><span class="kw2"><span class="caps">INSERT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INTO&amp;lr=lang_en"><span class="kw1"><span class="caps">INTO</span></span></a> daily <span class="br0">&#40;</span><a href="http://search.mysql.com/search?site=refman-51&amp;q=TIME&amp;lr=lang_en"><span class="kw5">time</span></a><span class="sy2">,</span> oob_prefix<span class="sy2">,</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=DATA&amp;lr=lang_en"><span class="kw1">data</span></a><span class="br0">&#41;</span><br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=SELECT&amp;lr=lang_en"><span class="kw1"><span class="caps">SELECT</span></span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">FROM_UNIXTIME</span></a><span class="br0">&#40;</span><a href="http://dev.mysql.com/doc/refman/5.1/en/group-by-functions-and-modifiers.html"><span class="kw22"><span class="caps">MAX</span></span></a><span class="br0">&#40;</span>oob_time_sec<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy2">,</span> oob_prefix<span class="sy2">,</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/group-by-functions-and-modifiers.html"><span class="kw22"><span class="caps">SUM</span></span></a><span class="br0">&#40;</span>ip_totlen<span class="br0">&#41;</span><br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=FROM&amp;lr=lang_en"><span class="kw1"><span class="caps">FROM</span></span></a> ulog<br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=WHERE&amp;lr=lang_en"><span class="kw1"><span class="caps">WHERE</span></span></a> oob_time_sec <span class="sy1">&gt;=</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-01 00:00:00&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">AND</span></span></a> oob_time_sec <span class="sy1">&lt;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-02 00:00:00&#8217;</span><span class="br0">&#41;</span><br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=GROUP%20BY&amp;lr=lang_en"><span class="kw1"><span class="caps">GROUP</span> <span class="caps">BY</span></span></a> oob_prefix<span class="sy2">;</span><br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=DELETE&amp;lr=lang_en"><span class="kw1"><span class="caps">DELETE</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=FROM&amp;lr=lang_en"><span class="kw1"><span class="caps">FROM</span></span></a> ulog <a href="http://search.mysql.com/search?site=refman-51&amp;q=WHERE&amp;lr=lang_en"><span class="kw1"><span class="caps">WHERE</span></span></a> oob_time_sec &nbsp;<span class="sy1">&gt;=</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-01 00:00:00&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">AND</span></span></a> oob_time_sec <span class="sy1">&lt;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-02 00:00:00&#8217;</span><span class="br0">&#41;</span><span class="sy2">;</span></div></div>

<p>Finally, I have a simple little <span class="caps">PHP</span> script that provides reporting and calculates dues.&nbsp;Done.</p>
    ]]></content>
  </entry>
</feed>
