[
  {
    "path": ".gitignore",
    "content": "*.db\n*.htm\n*.log\n*.old\n*~\n.RData\n\\#*\nold\nTAGS\n*.tgz\n*.ipk\n.gawk*\n"
  },
  {
    "path": "README.md",
    "content": "# wrtbwmon\nModified from https://code.google.com/p/wrtbwmon/.\n\n## Testers needed for IPv6 support\nExperimental release: https://github.com/pyrovski/wrtbwmon/releases/tag/0.37_ipv6\n\n## Features\n - \"User\" column has mouseover text containing MAC and IP addresses\n - \"First seen\" and \"Total\" columns in usage table\n - Monitoring of locally generated traffic on a per-interface basis\n - `remove` function to delete `iptables` rules\n\n### What does it do?\n`wrtbwmon` was designed to track bandwidth consumption on home routers. \nIt accomplishes this with `iptables` rules, which means you don't need to run an extra process just to track bandwidth. \n`wrtbwmon` conveniently tracks bandwidth consumption on a per-IP address basis, \nso you can easily determine which user/device is the culprit.\n\nHere is an example usage table:\n![image](example.png)\n\n### How do I use it?\n- Install: Download and install ipk from the [releases page](https://github.com/pyrovski/wrtbwmon/releases/)\n- Setup: `wrtbwmon setup`\n- Update table: `wrtbwmon update /tmp/usage.db` (you can place the data table anywhere)\n- Create html page: `wrtbwmon publish /tmp/usage.db /tmp/usage.htm`\n- Dump table to terminal: `wrtbwmon dump /tmp/usage.db`\n- Remove: `wrtbwmon remove`\n\n### Installation options\n- Install ipk\n  - e.g.,: \n    - `cd /tmp`\n    - HTTPS: `curl -LO https://github.com/pyrovski/wrtbwmon/releases/download/0.36/wrtbwmon_0.36_all.ipk`\n    - HTTP: you're on your own :( Busybox wget usually doesn't have SSL support.\n    - OpenWrt: `opkg install /tmp/wrtbwmon_0.36_all.ipk`\n- Install deb from [the releases page](https://github.com/pyrovski/wrtbwmon/releases)\n- Or, if you don't want to use an ipk or a deb:\n  - `cd /tmp`\n  - HTTPS: `curl -L https://github.com/pyrovski/wrtbwmon/archive/0.36.tar.gz | tar xvz`\n  - `cd wrtbwmon-0.36`\n  - `./install.sh wrtbwmon readDB.awk usage.htm1 usage.htm2 wrtbwmon`\n    - Currently, this depends on the `install` program. OpenWrt chose to provide this as the \"coreutils-install\" package.\n- Or, if you have `make`, just `make install` as root after cloning/unpacking.\n\n### Configuring the published table\n- `wrtbwmon` checks a few files for MAC -> name maps:\n  - 4th argument to `wrtbwmon publish <DB> <userDB>`\n  - `/tmp/dhcp.leases`\n  - `/tmp/dnsmasq.conf`\n  - `/etc/dnsmasq.conf`\n  - `/etc/hosts`\n- If all of the above do not yield a match, the script will optionally perform a reverse DNS lookup directed at the DNS server specified in the `DNS` variable. If `DNS` is blank or unset, the script will not perform such lookups.\n\n### Regular updates\n- Add the following to root's crontab:\n\n        # adapt PATH to your needs\n        PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n\t\n        * * * * * <script location> update /tmp/usage.db\n        0 * * * * <script location> publish /tmp/usage.db /tmp/usage.htm\n\n- On OpenWRT you need to [enable cron](https://wiki.openwrt.org/doc/howto/cron#activating_cron) as it is disabled by default:\n\n        /etc/init.d/cron start\n        /etc/init.d/cron enable\n\n- Enable web serving of the generated page (optional)\n  This varies by environment, but for lighttpd:\n         \n        ln -s /tmp/usage.htm /var/www/html/\n\n### Remove `iptables` rules\n - `wrtbwmon remove`\n"
  },
  {
    "path": "changelog",
    "content": "wrtbwmon (0.36) UNRELEASED; urgency=medium\n\n  * Packaging changes: remove .sh extension from init script, use 'tar' for final packing step.\n\n -- Peter Bailey <peter.eldridge.bailey@gmail.com>  Sun, 15 Jan 2017 19:16:40 -0800\n\nwrtbwmon (0.35) unstable; urgency=low\n\n  * Added deb package, changed back to wrtbwmon from wrtbwmon.sh\n\n -- Peter Bailey <peter.eldridge.bailey@gmail.com>  Sun, 20 Nov 2016 00:58:09 -0800\n\nwrtbwmon (0.34) unstable; urgency=low\n\n  * Added ipk and install targets with default paths for scripts and resources.\n\n -- Peter Bailey <peter.eldridge.bailey@gmail.com>  Fri, 19 Feb 2016 00:00:00 -0800\n"
  },
  {
    "path": "control",
    "content": "Package: wrtbwmon\nVersion: 0.36\nArchitecture: all\nMaintainer: Peter Bailey <peter.eldridge.bailey+wrtbwmon@gmail.com>\nSection: net\nPriority: optional\nDescription: wrtbwmon was designed to track bandwidth consumption on home routers. It accomplishes this with iptables rules, which means you don't need to run an extra process just to track bandwidth. wrtbwmon conveniently tracks bandwidth consumption on a per-IP address basis, so you can easily determine which user/device is the culprit.\nSource: https://github.com/pyrovski/wrtbwmon\n#Depends: gawk | mawk | busybox, iptables, sed | busybox, iproute2 | ip\n"
  },
  {
    "path": "copyright",
    "content": "wrtbwmon\nCopyright (C) 2016 Peter Bailey\n\nThis program is free software; you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation; either version 2 of the License, or (at\nyour option) any later version.\n\nThis program is distributed in the hope that it will be useful, but\nWITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\nGeneral Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA\n02110-1301, USA.\n"
  },
  {
    "path": "debian/.gitignore",
    "content": "debhelper*\nfiles\nwrtbwmon*\n"
  },
  {
    "path": "debian/compat",
    "content": "9\n"
  },
  {
    "path": "debian/control",
    "content": "Source: wrtbwmon\nMaintainer: Peter Bailey <peter.eldridge.bailey@gmail.com>\nSection: net\nPriority: optional\nStandards-Version: 3.9.7\nBuild-Depends: debhelper (>= 9)\n\nPackage: wrtbwmon\nArchitecture: all\nDepends: iproute2 | iproute, iptables, ${shlibs:Depends}, ${misc:Depends}, \nDescription: iptables-based bandwidth monitor\n wrtbwmon was designed to track bandwidth consumption on home\n routers. It accomplishes this with iptables rules, which means you\n don't need to run an extra process just to track bandwidth. wrtbwmon\n conveniently tracks bandwidth consumption on a per-IP address basis,\n so you can easily determine which user/device is the culprit.\n"
  },
  {
    "path": "debian/install",
    "content": "# TODO: share this between makefile/ipk/deb\nwrtbwmon /usr/sbin\nreadDB.awk /usr/sbin\nusage.htm1 /usr/share/wrtbwmon\nusage.htm2 /usr/share/wrtbwmon\ninit/wrtbwmon /etc/init.d\n"
  },
  {
    "path": "debian/rules",
    "content": "#!/usr/bin/make -f\n%:\n\tdh $@\n"
  },
  {
    "path": "fileMap",
    "content": "usr/sbin: wrtbwmon readDB.awk\nusr/share/wrtbwmon: usage.htm1 usage.htm2\nCONTROL: control postinst\netc/init.d: init/wrtbwmon\n"
  },
  {
    "path": "init/wrtbwmon",
    "content": "#!/bin/sh /etc/rc.common\n#\n# start/stop wrtbwmon bandwidth monitor\n\n### BEGIN INIT INFO\n# Provides:           wrtbwmon\n# Required-Start:     $network $local_fs $remote_fs\n# Required-Stop:      $local_fs $remote_fs\n# Default-Start:      2 3 4 5\n# Default-Stop:       0 1 6\n# Short-Description:  iptables-based bandwidth monitor\n### END INIT INFO\n\nSTART=91\n\nstart(){\n    /usr/sbin/wrtbwmon setup /tmp/usage.db\n}\n\nstop(){\n    /usr/sbin/wrtbwmon remove\n}\n"
  },
  {
    "path": "install.sh",
    "content": "do_copy() {\n    local dest perm file\n    for file in $*; do\n\tdest=`egrep \".+ $file( |$)\" ./fileMap | cut -d':' -f1`\n\tmkdir -p $DESTDIR/$dest\n\tif [ -n `echo $dest | egrep '/s*bin$'` ]; then\n    \t    perm=0744\n\telse\n    \t    perm=0644\n\tfi\n\tinstall -m $perm -t $DESTDIR/$dest/ $file\n    done\n}\n\nbase=`basename $0`\nif [ \"$base\" = \"mkipk.sh\" ]; then\n    DESTDIR=$(mktemp -d)\n    do_copy $*\n    fakeroot -- ipkg-build -c $DESTDIR\n    rm -rf $DESTDIR\nelse\n    DESTDIR=${DESTDIR:-\"/\"}\n    do_copy $*\nfi\n"
  },
  {
    "path": "makefile",
    "content": "DESTDIR?=/\ninstall-files=$(shell grep -v CONTROL fileMap  | cut -d: -f2- | tr -d '\\n')\nipk-files=$(shell grep CONTROL fileMap | cut -d: -f2-)\nversion:=$(shell egrep '^Version:' ./control | awk '{print $$2}')\ntarget=wrtbwmon_$(version)_all.ipk\n\nall: $(target)\n\n$(target): $(install-files) $(ipk-files)\n\t./mkipk.sh $^\n\ninstall: $(install-files)\n\t./install.sh $^\n\ndeb:\n\tgbp buildpackage -Pdebian --git-ignore-new\n\nclean:\n\trm -f *.ipk\n\n.PHONY: deb install clean\n\n.SUFFIXES:\n"
  },
  {
    "path": "postinst",
    "content": "#!/bin/sh\n\n# Enable on boot\n/etc/init.d/wrtbwmon enable\n\n# Configure our cron\nif [ -d \"/etc/crontabs\" ]; then\n  echo \"PATH=/usr/sbin:/usr/bin:/sbin:/bin\" > /etc/crontabs/wrtbwmon\n  echo \"* * * * * /usr/sbin/wrtbwmon update /tmp/usage.db\" >> /etc/crontabs/wrtbwmon\n  echo \"0 * * * * /usr/sbin/wrtbwmon publish /tmp/usage.db /tmp/usage.htm\" >> /etc/crontabs/wrtbwmon\nfi\n/etc/init.d/cron reload\n\n# Symlink to our webdir\nif [ -d \"/var/www/html\" ]; then\n  ln -s /tmp/usage.htm /var/www/html/usage.htm\nelif [ -d \"/www\" ]; then\n ln -s /tmp/usage.htm /www/usage.htm\nfi\n\nexit 0\n"
  },
  {
    "path": "readDB.awk",
    "content": "#!/usr/bin/awk\n\nfunction inInterfaces(host){\n    return(interfaces ~ \"(^| )\"host\"($| )\")\n}\n\nfunction newRule(arp_ip,\n    ipt_cmd){\n    # checking for existing rules shouldn't be necessary if newRule is\n    # always called after db is read, arp table is read, and existing\n    # iptables rules are read.\n    ipt_cmd=\"iptables -t mangle -j RETURN -s \" arp_ip\n    system(ipt_cmd \" -C RRDIPT_FORWARD 2>/dev/null || \" ipt_cmd \" -A RRDIPT_FORWARD\")\n    ipt_cmd=\"iptables -t mangle -j RETURN -d \" arp_ip\n    system(ipt_cmd \" -C RRDIPT_FORWARD 2>/dev/null || \" ipt_cmd \" -A RRDIPT_FORWARD\")\n}\n\nfunction total(i){\n    return(bw[i \"/in\"] + bw[i \"/out\"])\n}\n\nfunction date(    cmd, d){\n    cmd=\"date +%d-%m-%Y_%H:%M:%S\"\n    cmd | getline d\n    close(cmd)\n    #!@todo could start a process with \"while true; do date ...; done\"\n    return(d)\n}\n\nBEGIN {\n    od=\"\"\n    fid=1\n    debug=0\n    rrd=0\n}\n\n/^#/ { # get DB filename\n    FS=\",\"\n    dbFile=FILENAME\n    next\n}\n\n# data from database; first file\nFNR==NR { #!@todo this doesn't help if the DB file is empty.\n    if($2 == \"NA\")\n\t#!@todo could get interface IP here\n\tn=$1\n    else\n\tn=$2\n\n    hosts[n] = \"\" # add this host/interface to hosts\n    mac[n]        =  $1\n    ip[n]         =  $2\n    inter[n]      =  $3\n    bw[n \"/in\"]   =  $4\n    bw[n \"/out\"]  =  $5\n    firstDate[n]  =  $7\n    lastDate[n]   =  $8\n    next\n}\n\n# not triggered on the first file\nFNR==1 {\n    FS=\" \"\n    fid++ #!@todo use fid for all files; may be problematic for empty files\n    next\n}\n\n# arp: ip hw flags hw_addr mask device\nfid==2 {\n    #!@todo regex match IPs and MACs for sanity\n    arp_ip    = $1\n    arp_flags = $3\n    arp_mac   = $4\n    arp_dev   = $6\n    if(arp_flags != \"0x0\" && !(arp_ip in ip)){\n\tif(debug)\n\t    print \"new host:\", arp_ip, arp_flags > \"/dev/stderr\"\n\thosts[arp_ip] = \"\"\n\tmac[arp_ip]   = arp_mac\n\tip[arp_ip]    = arp_ip\n\tinter[arp_ip] = arp_dev\n\tbw[arp_ip \"/in\"] = bw[arp_ip \"/out\"] = 0\n\tfirstDate[arp_ip] = lastDate[arp_ip] = date()\n    }\n    next\n}\n\n#!@todo could use mangle chain totals or tailing \"unnact\" rules to\n# account for data for new hosts from their first presence on the\n# network to rule creation. The \"unnact\" rules would have to be\n# maintained at the end of the list, and new rules would be inserted\n# at the top.\n\n# skip line\n# read the chain name and deal with the data accordingly\nfid==3 && $1 == \"Chain\"{\n    rrd=$2 ~ /RRDIPT_.*/\n    next\n}\n\nfid==3 && rrd && (NF < 9 || $1==\"pkts\"){ next }\n\nfid==3 && rrd { # iptables input\n    if($6 != \"*\"){\n\tm=$6\n\tn=m \"/out\"\n    } else if($7 != \"*\"){\n\tm=$7\n\tn=m \"/in\"\n    } else if($8 != \"0.0.0.0/0\"){\n\tm=$8\n\tn=m \"/out\"\n    } else { # $9 != \"0.0.0.0/0\"\n\tm=$9\n\tn=m \"/in\"\n    }\n\n    # remove host from array; any hosts left in array at END get new\n    # iptables rules\n\n    #!@todo this deletes a host if any rule exists; if only one\n    # directional rule is removed, this will not remedy the situation\n    delete hosts[m]\n\n    if($2 > 0){ # counted some bytes\n\tif(mode == \"diff\" || mode == \"noUpdate\")\n\t    print n, $2\n\tif(mode!=\"noUpdate\"){\n\t    if(inInterfaces(m)){ # if label is an interface\n\t\tif(!(m in mac)){ # if label was not in db (also not in\n\t\t\t\t # arp table, but interfaces won't be\n\t\t\t\t # there anyway)\n\t\t    firstDate[m] = date()\n\t\t    mac[m] = inter[m] = m\n\t\t    ip[m] = \"NA\"\n\t\t    bw[m \"/in\"]=bw[m \"/out\"]= 0\n\t\t}\n\t    }\n\t    bw[n]+=$2\n\t    lastDate[m] = date()\n\t}\n    }\n}\n\nEND {\n    if(mode==\"noUpdate\") exit\n    close(dbFile)\n    system(\"rm -f \" dbFile)\n    print \"#mac,ip,iface,in,out,total,first_date,last_date\" > dbFile\n    OFS=\",\"\n    for(i in mac)\n\tprint mac[i], ip[i], inter[i], bw[i \"/in\"], bw[i \"/out\"], total(i), firstDate[i], lastDate[i] > dbFile\n    close(dbFile)\n    # for hosts without rules\n    for(host in hosts) if(!inInterfaces(host)) newRule(host)\n}\n"
  },
  {
    "path": "release-notes",
    "content": "- transition from invocation as \"wrtbwmon.sh\" back to \"wrtbwmon\"\n- update readme\n- add ipk\n- install files according a standard filesystem layout\n- remove peak/offpeak distinction\n- add init script\n- perform reverse DNS lookups to find hostnames\n- fix locking issues\n- add \"dump\" option\n"
  },
  {
    "path": "todo",
    "content": "- add logger\n- cache DNS results\n- use a config file\n- check for existence of tool dependencies before using them\n- fix DNS reverse lookups for systems without nslookup\n- add tests\n"
  },
  {
    "path": "usage.htm1",
    "content": "<html><head><title>Traffic</title>\n<script type=\"text/javascript\">\nfunction getSize(size) {\n    var prefix=new Array(\"\",\"k\",\"M\",\"G\",\"T\",\"P\",\"E\",\"Z\"); var base=1000;\n    var pos=0;\n    while (size>base) {\n        size/=base; pos++;\n    }\n    if (pos > 2) precision=1000; else precision = 1;\n    return (Math.round(size*precision)/precision)+' '+prefix[pos];}\n</script></head>\n<body><h1>Total Usage:</h1>\n<table border=\"1\">\n<tr bgcolor=silver>\n<th>User</th>\n<th>Download</th>\n<th>Upload</th>\n<th>Total</th>\n<th>First seen</th>\n<th>Last seen</th>\n</tr>\n<script type=\"text/javascript\">\nvar values = new Array(\n"
  },
  {
    "path": "usage.htm2",
    "content": "var totalIn = 0;\nvar totalOut = 0;\nfor (i=0; i < values.length-1; i++) {\n    totalIn += values[i][3];\n    totalOut += values[i][4];\n    document.write(\"<tr><td><div title=\\\"\" + values[i][1] + \" (\" + values[i][2] + \")\" + \"\\\">\" + values[i][0] + \"</div></td>\");\n    for (j=3; j < 6; j++)\n        document.write(\"<td>\" + getSize(values[i][j]) + \"</td>\");\n    document.write(\"<td>\" + values[i][6] + \"</td><td>\" + values[i][7] + \"</td></tr>\");\n}\ndocument.write(\"<tr><td>TOTAL</td><td>\" + getSize(totalIn) + \"</td><td>\" + getSize(totalOut) + \"</td><td>\" + getSize(totalIn + totalOut) + \"</td><td></td><td></td></tr>\");\n</script></table>\n<br /><small>This page was generated on (date)</small>\n</body></html>\n"
  },
  {
    "path": "wrtbwmon",
    "content": "#!/bin/sh\n#\n# wrtbwmon: traffic logging tool for routers\n#\n# Peter Bailey (peter.eldridge.bailey+wrtbwmon AT gmail.com)\n#\n# Based on work by:\n# Emmanuel Brucy (e.brucy AT qut.edu.au)\n# Fredrik Erlandsson (erlis AT linux.nu)\n# twist - http://wiki.openwrt.org/RrdTrafficWatch\n\ntrap \"rm -f /tmp/*_$$.tmp; kill $$\" INT\nbinDir=/usr/sbin\ndataDir=/usr/share/wrtbwmon\nlockDir=/tmp/wrtbwmon.lock\npidFile=$lockDir/pid\nnetworkFuncs=/lib/functions/network.sh\nuci=`which uci 2>/dev/null`\nnslookup=`which nslookup 2>/dev/null`\nnvram=`which nvram 2>/dev/null`\n\nchains='INPUT OUTPUT FORWARD'\nDEBUG=\ninterfaces='eth0 tun0' # in addition to detected WAN\nDB=$2\nmode=\n\n# DNS server for reverse lookups provided in \"DNS\".\n# don't perform reverse DNS lookups by default\nDO_RDNS=${DNS-}\n\nheader=\"#mac,ip,iface,in,out,total,first_date,last_date\"\n\ncreateDbIfMissing()\n{\n    [ ! -f \"$DB\" ] && echo $header > \"$DB\"\n}\n\ncheckDbArg()\n{\n    [ -z \"$DB\" ] && echo \"ERROR: Missing argument 2 (database file)\" && exit 1\n}\n\ncheckDB()\n{\n    [ ! -f \"$DB\" ] && echo \"ERROR: $DB does not exist\" && exit 1\n    [ ! -w \"$DB\" ] && echo \"ERROR: $DB is not writable\" && exit 1   \n}\n\ncheckWAN()\n{\n    [ -z \"$wan\" ] && echo \"Warning: failed to detect WAN interface.\"\n}\n\nlookup()\n{\n    MAC=$1\n    IP=$2\n    userDB=$3\n    for USERSFILE in $userDB /tmp/dhcp.leases /tmp/dnsmasq.conf /etc/dnsmasq.conf /etc/hosts; do\n\t[ -e \"$USERSFILE\" ] || continue\n\tcase $USERSFILE in\n\t    /tmp/dhcp.leases )\n\t\tUSER=$(grep -i \"$MAC\" $USERSFILE | cut -f4 -s -d' ')\n\t\t;;\n\t    /etc/hosts )\n\t\tUSER=$(grep \"^$IP \" $USERSFILE | cut -f2 -s -d' ')\n\t\t;;\n\t    * )\n\t\tUSER=$(grep -i \"$MAC\" \"$USERSFILE\" | cut -f2 -s -d,)\n\t\t;;\n\tesac\n\t[ \"$USER\" = \"*\" ] && USER=\n\t[ -n \"$USER\" ] && break\n    done\n    if [ -n \"$DO_RDNS\" -a -z \"$USER\" -a \"$IP\" != \"NA\" -a -n \"$nslookup\" ]; then\n\tUSER=`$nslookup $IP $DNS | awk '!/server can/{if($4){print $4; exit}}' | sed -re 's/[.]$//'`\n    fi\n    [ -z \"$USER\" ] && USER=${MAC}\n    echo $USER\n}\n\ndetectIF()\n{\n    if [ -f \"$networkFuncs\" ]; then\n\tIF=`. $networkFuncs; network_get_device netdev $1; echo $netdev`\n\t[ -n \"$IF\" ] && echo $IF && return\n    fi\n\n    if [ -n \"$uci\" -a -x \"$uci\" ]; then\n\tIF=`$uci get network.${1}.ifname 2>/dev/null`\n\t[ $? -eq 0 -a -n \"$IF\" ] && echo $IF && return\n    fi\n\n    if [ -n \"$nvram\" -a -x \"$nvram\" ]; then\n\tIF=`$nvram get ${1}_ifname 2>/dev/null`\n\t[ $? -eq 0 -a -n \"$IF\" ] && echo $IF && return\n    fi\n}\n\ndetectLAN()\n{\n    [ -e /sys/class/net/br-lan ] && echo br-lan && return\n    lan=$(detectIF lan)\n    [ -n \"$lan\" ] && echo $lan && return\n}\n\ndetectWAN()\n{\n    [ -n \"$WAN_IF\" ] && echo $WAN_IF && return\n    wan=$(detectIF wan)\n    [ -n \"$wan\" ] && echo $wan && return\n    wan=$(ip route show 2>/dev/null | grep default | sed -re '/^default/ s/default.*dev +([^ ]+).*/\\1/')\n    [ -n \"$wan\" ] && echo $wan && return\n    [ -f \"$networkFuncs\" ] && wan=$(. $networkFuncs; network_find_wan wan; echo $wan)\n    [ -n \"$wan\" ] && echo $wan && return\n}\n\nlock()\n{\n    attempts=0\n    while [ $attempts -lt 10 ]; do\n\tmkdir $lockDir 2>/dev/null && break\n\tattempts=$((attempts+1))\n\tpid=`cat $pidFile 2>/dev/null`\n\tif [ -n \"$pid\" ]; then\n\t    if [ -d \"/proc/$pid\" ]; then\n\t\t[ -n \"$DEBUG\" ] && echo \"WARNING: Lockfile detected but process $(cat $pidFile) does not exist !\"\n\t\trm -rf $lockDir\n\t    else\n\t\tsleep 1\n\t    fi\n\tfi\n    done\n    mkdir $lockDir 2>/dev/null\n    echo $$ > $pidFile\n    [ -n \"$DEBUG\" ] && echo $$ \"got lock after $attempts attempts\"\n    trap '' INT\n}\n\nunlock()\n{\n    rm -rf $lockDir\n    [ -n \"$DEBUG\" ] && echo $$ \"released lock\"\n    trap \"rm -f /tmp/*_$$.tmp; kill $$\" INT\n}\n\n# chain\nnewChain()\n{\n    chain=$1\n    # Create the RRDIPT_$chain chain (it doesn't matter if it already exists).\n    iptables -t mangle -N RRDIPT_$chain 2> /dev/null\n    \n    # Add the RRDIPT_$chain CHAIN to the $chain chain if not present\n    iptables -t mangle -C $chain -j RRDIPT_$chain 2>/dev/null\n    if [ $? -ne 0 ]; then\n\t[ -n \"$DEBUG\" ] && echo \"DEBUG: iptables chain misplaced, recreating it...\"\n\tiptables -t mangle -I $chain -j RRDIPT_$chain\n    fi\n}\n\n# chain tun\nnewRuleIF()\n{\n    chain=$1\n    IF=$2\n    \n    #!@todo test\n    if [ \"$chain\" = \"OUTPUT\" ]; then\n\tcmd=\"iptables -t mangle -o $IF -j RETURN\"\n\teval $cmd \" -C RRDIPT_$chain 2>/dev/null\" || eval $cmd \" -A RRDIPT_$chain\"\n    elif [ \"$chain\" = \"INPUT\" ]; then\n\tcmd=\"iptables -t mangle -i $IF -j RETURN\"\n\teval $cmd \" -C RRDIPT_$chain 2>/dev/null\" || eval $cmd \" -A RRDIPT_$chain\"\n    fi\n}\n\nupdate()\n{\n    #!@todo could let readDB.awk handle this; that would place header\n    #!info in fewer places\n    createDbIfMissing\n    \n    checkDB\n    checkWAN\n\n    > /tmp/iptables_$$.tmp\n    lock\n    # only zero our own chains\n    for chain in $chains; do\n\tiptables -nvxL RRDIPT_$chain -t mangle -Z >> /tmp/iptables_$$.tmp\n    done\n    # the iptables and readDB commands have to be separate. Otherwise,\n    # they will fight over iptables locks\n    awk -v mode=\"$mode\" -v interfaces=\\\"\"$interfaces\"\\\" -f $binDir/readDB.awk \\\n\t$DB \\\n\t/proc/net/arp \\\n\t/tmp/iptables_$$.tmp\n    unlock\n}\n\n############################################################\n\ncase $1 in\n    \"dump\" )\n\tcheckDbArg\n\tlock\n\ttr ',' '\\t' < \"$DB\"\n\tunlock\n    ;;\n\n    \"update\" )\n\tcheckDbArg\n\twan=$(detectWAN)\n\tinterfaces=\"$interfaces $wan\"\n\tupdate\n\trm -f /tmp/*_$$.tmp\n\texit\n\t;;\n\n    \"publish\" )\n\tcheckDbArg\n\t[ -z \"$3\" ] && echo \"ERROR: Missing argument 3 (output html file)\" && exit 1\n\t\n\t# sort DB\n\tlock\n\n\t# busybox sort truncates numbers to 32 bits\n\tgrep -v '^#' $DB | awk -F, '{OFS=\",\"; a=sprintf(\"%f\",$4/1e6); $4=\"\"; print a,$0}' | tr -s ',' | sort -rn | awk -F, '{OFS=\",\";$1=sprintf(\"%f\",$1*1e6);print}' > /tmp/sorted_$$.tmp\n\n        # create HTML page\n\trm -f $3.tmp\n\tcp $dataDir/usage.htm1 $3.tmp\n\n\t#!@todo fix publishing\n\twhile IFS=, read PEAKUSAGE_IN MAC IP IFACE PEAKUSAGE_OUT TOTAL FIRSTSEEN LASTSEEN\n\tdo\n\t    echo \"\nnew Array(\\\"$(lookup $MAC $IP $4)\\\",\\\"$MAC\\\",\\\"$IP\\\",\n$PEAKUSAGE_IN,$PEAKUSAGE_OUT,$TOTAL,\\\"$FIRSTSEEN\\\",\\\"$LASTSEEN\\\"),\" >> $3.tmp\n\tdone < /tmp/sorted_$$.tmp\n\techo \"0);\" >> $3.tmp\n\t\n\tsed \"s/(date)/`date`/\" < $dataDir/usage.htm2 >> $3.tmp\n\tmv $3.tmp $3\n\n\tunlock\n\t\n\t#Free some memory\n\trm -f /tmp/*_$$.tmp\n\t;;\n    \n    \"setup\" )\n\tcheckDbArg\n\t[ -w \"$DB\" ] && echo \"Warning: using existing $DB\"\n\tcreateDbIfMissing\n\t\n\tfor chain in $chains; do\n\t    newChain $chain\n\tdone\n\n\t#lan=$(detectLAN)\n\twan=$(detectWAN)\n\tcheckWAN\n\tinterfaces=\"$interfaces $wan\"\n\n\t# track local data\n\tfor chain in INPUT OUTPUT; do\n\t    for interface in $interfaces; do\n\t\t[ -n \"$interface\" ] && [ -e \"/sys/class/net/$interface\" ] && newRuleIF $chain $interface\n\t    done\n\tdone\n\n\t# this will add rules for hosts in arp table\n\tupdate\n\n\trm -f /tmp/*_$$.tmp\n\t;;\n\n    \"remove\" )\n\tiptables-save | grep -v RRDIPT | iptables-restore\n\trm -rf \"$lockDir\"\n\t;;\n\n    *)\n\techo \\\n\"Usage: $0 {setup|update|publish|remove} [options...]\nOptions:\n   $0 setup database_file\n   $0 update database_file\n   $0 publish database_file path_of_html_report [user_file]\nExamples:\n   $0 setup /tmp/usage.db\n   $0 update /tmp/usage.db\n   $0 publish /tmp/usage.db /www/user/usage.htm /jffs/users.txt\n   $0 remove\nNote: [user_file] is an optional file to match users with MAC addresses.\n       Its format is \\\"00:MA:CA:DD:RE:SS,username\\\", with one entry per line.\"\n\t;;\nesac\n"
  }
]