Migrating from Gargoyle - Bandwidth Management

Introduction

I have been using OpenWrt for years. It’s on all my routers except one. It still has Gargoyle. I would like to switch it over to OpenWrt. For that, I need to be able to report daily, weekly and monthly Internet data usage. I also need to be able to report totals and for all or an IP subset like the smart TVs. Also, if usage is over a given quota, I want to restrict the bandwidth for all or just some devices.

I have not seen any posts here describing something like this. I have been searching for awhile. So, for the Gargoyle migration I made some notes and wrote some scripts. I thought I would share. Maybe someone has a similar need.

Scripts are available at https://github.com/RVgo4it/OpenWrt.

Data Collection

To collect the data, I decided on “bandwidthd-sqlite”. It has nice graphs and I can easily pull the details I need from the SQLite database.

To install, use these commands:

opkg update; opkg install bandwidthd-sqlite sqlite3-cli

To configure, we need to stop the service and check the configuration. Use the following commands:

/etc/init.d/bandwidthd stop
uci show bandwidthd

Confirm the subnet and interface device is correct for the router. If needed, adjust using commands like these:

uci set bandwidthd.@bandwidthd[0].subnets='192.168.n.0/24'
uci set bandwidthd.@bandwidthd[0].dev='br-lan'
uci commit bandwidthd

We need to fine tune the data capture. We just want data to/from the Internet. For this, we’ll need some info from the network interface. Use this command:

 
ifconfig `uci get bandwidthd.@bandwidthd[0].dev`

Make note of the MAC address and the IP address. Edit and use the following configuration commands:

uci set bandwidthd.@bandwidthd[0].filter='ip and ether host xx:xx:xx:xx:xx:xx and not host 192.168.n.1'
uci set bandwidthd.@bandwidthd[0].promiscuous='false'
uci commit bandwidthd
/etc/init.d/bandwidthd start

The filter will tell “bandwidthd” to only look at packets sent to/from the router but the router was not the sender or receiver. Basically, that’s default route packets.

The path for the SQLite database is actually under /tmp, so it will be lost during a power cycle of the router. However, we need that data. So, use a thumb drive to save it on a regular schedule. Create a folder on it called “bandwidthd” and add the following to LuCI → System → Scheduled Tasks. This example will run every 10 minutes. Adjust path and schedule as needed.

*/10 * * * * DEST=/mnt/sda1/bandwidthd/stats.db;rm $DEST;sqlite3 `uci get bandwidthd.@bandwidthd[0].sqlite_filename` "vacuum into '$DEST'"

We’ll need to put the database file back before the service is started at boot time. Disable auto-start with this command:

/etc/init.d/bandwidthd disable

Then add the following commands via LuCI → System → Startup and select the Local Startup tab. Adjust the path as needed.

mkdir /tmp/bandwidthd
cp /mnt/sda1/bandwidthd/stats.db /tmp/bandwidthd
/etc/init.d/bandwidthd start

The database can grow quite large over time. It should be cleaned up on a regular basis. The following commands can be used to clean it of older records not needed any more. This example keeps totals for 26 weeks and details for 8 weeks. The commands can be placed in a script and scheduled to run weekly.

TOTALWEEKS=26
DETAILWEEKS=8
NOWTS=`date '+%s'`
TOTALTS=$((NOWTS - TOTALWEEKS * 7 * 24 * 60 * 60))
DETAILTS=$((NOWTS - DETAILWEEKS * 7 * 24 * 60 * 60))
DB=`uci get bandwidthd.@bandwidthd[0].sqlite_filename`
sqlite3 $DB 'DELETE FROM bd_rx_log WHERE timestamp < $DETAILTS;'
sqlite3 $DB 'DELETE FROM bd_tx_log WHERE timestamp < $DETAILTS;'
sqlite3 $DB 'DELETE FROM bd_rx_total_log WHERE timestamp < $TOTALTS;'
sqlite3 $DB 'DELETE FROM bd_tx_total_log WHERE timestamp < $TOTALTS;'

Reporting

To view the data as charts, use the IP address of your router and append “/bandwidthd” to it. It will be something like http://192.168.1.1/bandwidthd.

Note: The data for the charts does not come from the database. So, after a power cycle, the charts will be missing older data. However, the attached script “bandwidth_used.sh” will query the database that contains all the data.

Syntax is: bandwidth_used.sh [arguments]
Arguments are as follows:
  -d --daily         Turnover is daily.  Default.
  -w --weekly[=n]    Turnover is weekly.  Default is Sat(0).
  -m --monthly[=n]   Turnover is monthly. For day of month, use n. Default is first(1).
  -h --hour=n        Turnover hour, 00-23.  Default is 12.
  -s --spans=n       Look back this many turnover spans.  Default is 10.
  -b --bytes=x       Scale the bytes, k, m or g.  Default is Kbytes.
  -q --quota=x       Report % of quota.  Also, return non zero if current span over quota.  Default 1 Tbytes.
  -x --exclude=s     Exclude comma separated list of dotted IP addresses from total in report.
  -i --include=s     Include only comma separated list of dotted IP addresses in report.
  -n --note=s        Include short note on the report title.

For example, to query the daily 6 AM usage of the smart TV at 192.168.1.76, with a quota of 8 Gbytes, use this command:

./bandwidth_used.sh --note=TV --hour=06 --bytes=m --include=192.168.1.76 --quota=8192
Bandwidth Quota Daily TV Report for: 2022-28-29 15:28
Time span (from - to)	Total	Bytes	% of 8192 Mbytes
2022-08-29 06:00 - 2022-08-30 06:00	221	Mbytes	2%
2022-08-28 06:00 - 2022-08-29 06:00	6074	Mbytes	74%
2022-08-27 06:00 - 2022-08-28 06:00	0	Mbytes	0%
2022-08-26 06:00 - 2022-08-27 06:00	839	Mbytes	10%
2022-08-25 06:00 - 2022-08-26 06:00	3501	Mbytes	42%
2022-08-24 06:00 - 2022-08-25 06:00	7696	Mbytes	93%
2022-08-23 06:00 - 2022-08-24 06:00	0	Mbytes	0%
2022-08-22 06:00 - 2022-08-23 06:00	0	Mbytes	0%
2022-08-21 06:00 - 2022-08-22 06:00	0	Mbytes	0%
2022-08-20 06:00 - 2022-08-21 06:00	0	Mbytes	0%

To convert the report to HTML, pipe the report to the following awk command:

awk -F '\t' 'BEGIN { print "<!DOCTYPE html><html><body><table>" }\
  { if (substr($1,1,9) == "Bandwidth")\
  { print "<tr><td colspan='4' align='center'><b><h3>" $1 "</h3></b></td></tr>"} else\
  { print "<tr><td>" $1 "</td><td align='right'>" $2 "</td><td>" $3\
    "</td><td align='right'>" $4 "</td></tr>"} } END { print "</table></body></html>" }'

I use the HTML version of the reports for a custom page under /www/bandwidthd and for emails.

Traffic Shaping

The “bandwidth_used.sh” returns an error code of 250 if the current time span, first row of the report, is 100% or more. It can be used to trigger another script. The triggered script could, for example, perform traffic shaping so as to limit the data usage for some or all devices.

The attached “bandwidth_shape.sh” script uses qdisc CAKE to limit the upload and download speed. It needs the traffic control packages. Use this command to install them:

opkg update; opkg install tc-full kmod-ifb kmod-sched-cake

Details of the script are as follows:

Syntax is: bandwidth_shape.sh start | stop [arguments]
Arguments are as follows:
  start | stop       Start or stop traffic shaping.  Required.
  -d --download=nbit Download speed in bits/s.  Default is 1Mbit.
  -u --upload=nbit   Upload speed in bits/s.  Default is 500Kbit.
  -x --exclude=s     Exclude comma separated list of dotted IP addresses from traffic shaping.
  -i --include=s     Include only comma separated list of dotted IP addresses in traffic shaping.

For example, to shape the data usage for the smart TV to the defaults, forcing it to standard definition, use the following command:

./bandwidth_shape.sh start --include=192.168.1.76

Summary

I hope others find this useful. I have tested these procedures and scripts on OpenWrt 21.02.3. I don’t know if they will work on older or future versions. Also, I want to send a big thank you out to the OpenWrt team and supporters plus all the contributors on the forms for all their hard work on the awesome open source project called OpenWrt.

7 Likes

Thanks for sharing this alternative

1 Like

why not add it to wiki ? https://forum.openwrt.org/t/applying-for-openwrt-wiki-account/

I was thinking about that. This post was more about what I did. I posted it so others could use it as tips for what they might need to do. If I were to add to the wiki, it would be much more general. I could add to the Bandwidthd wiki page and talk about the filter and the script I wrote to query the SQLite database. The other script is for traffic shaping and does not need to be tied to Bandwidthd. I'll have to think about where that would go.

cool. How does the shaping performance compare vs a vs gargoyle?

It appears to perform just as good or better than Gargoyle. CAKE is very impressive.

Thanks for this! Possibilities :grinning:

I just noticed the cron code displays without the single quotes for the uci get filename.db entry in your OP. It copies intact, but anybody using cli to copy verbatim from the forum post might have a bit of a head scratcher.

1 Like

Thanks for catching that. It appears too late to do any more edits. So, can't fix it. Let's see if it looks okay here:

*/10 * * * * DEST=/mnt/sda1/bandwidthd/stats.db;rm $DEST;sqlite3 `uci get bandwidthd.@bandwidthd[0].sqlite_filename` "vacuum into '$DEST'"

Preview looks good. Hopefully post's that way.

1 Like

This is a terrific project! A couple requests:

  • As the original poster, you should be able to go back to edit the cron command that copies the SQLite database to the thumb drive. If not, let me know and I can help

  • I recommend that you copy/paste your original post into the README.md of your Github repo - it's already formatted in Markdown, and will make the repo look more inviting. (You can also avoid maintaining the doc.html.md file...)

  • Would you also include a screen shot or two of the charts in Github? That would help readers decide whether to try your project.

Thanks.

1 Like

Thanks Rich. Great ideas.

I have edited the post once already, first day, different format issue. I got the "Editor" badge. But it did warn me I "won’t be able to edit [my] posts forever". Now I don't see the edit button any more. Appreciate any help you can provide.

I renamed the files out at Github as you suggested.

I added a screen shot of a Bandwidth chart. That was a good idea. The Bandwidthd user guide https://openwrt.org/docs/guide-user/services/network_monitoring/bandwidthd has no sample charts or screen shots.

I believe that if you click the "..." below the post, you get more options, including the pencil icon that will permit you to edit the post.

In addition, that displays the Markdown formatting that you can paste into your README on Github. (And thanks for the images! They should be at the top of the page...)

I tried the "...", no pencil button. :frowning:

I just bumped you from Basic to Regular member. Try to edit now...

1 Like

That did the trick. I fixed the formatting. Thanks so much!

Excellent. Now if you copy the text from the original post into the README, (along with the charts), the Github repo will look terrific!

Actually, I did those updates at Github when you first suggested. The screen shots appear in the area of the README where it is in context.

Thanks again for all the help.

Uh oh. Following your instructions (above) on a fresh install of OpenWrt 22.03.0 (it's finally released!) I get this error message in syslog after typing /etc/init.d/bandwidthd

Mon Sep 5 20:07:01 2022 daemon.err bandwidthd: Syntax Error "syntax error" on line 2

I see a directory of files at /tmp/bandwidthd, however there's no .sqlite file in evidence. What other diagnostic info could I provide? Thanks.

Sounds like something in the config file. Can you do both these commands and paste results please?
cat /etc/bandwidthd.conf
uci show bandwidthd

Update: Found it myself... I should have supplied a subnet - e.g., a /24 in the subnets, like this:

uci set bandwidthd.@bandwidthd[0].subnets='192.168.249.0/24'

Here's what it looks like when it's working:

root@Belkin-HBTL:~# cat /etc/bandwidthd.conf
# auto-generated config file from /etc/config/bandwidthd
subnet 192.168.249.0/24
dev "br-lan"
skip_intervals 0
graph_cutoff 1024
promiscuous false
output_cdf false
recover_cdf false
filter "ip and ether host E8:9F:80:E3:C1:8D and not host 192.168.249.1"
graph true
meta_refresh 150
sqlite_filename "/www/bandwidthd/stats.db"
sensor_id "default"

root@Belkin-HBTL:~# uci show bandwidthd
bandwidthd.@bandwidthd[0]=bandwidthd
bandwidthd.@bandwidthd[0].dev='br-lan'
bandwidthd.@bandwidthd[0].skip_intervals='0'
bandwidthd.@bandwidthd[0].graph_cutoff='1024'
bandwidthd.@bandwidthd[0].output_cdf='false'
bandwidthd.@bandwidthd[0].recover_cdf='false'
bandwidthd.@bandwidthd[0].graph='true'
bandwidthd.@bandwidthd[0].meta_refresh='150'
bandwidthd.@bandwidthd[0].sqlite_filename='/www/bandwidthd/stats.db'
bandwidthd.@bandwidthd[0].sensor_id='default'
bandwidthd.@bandwidthd[0].filter='ip and ether host E8:9F:80:E3:C1:8D and not host 192.168.249.1'
bandwidthd.@bandwidthd[0].promiscuous='false'
bandwidthd.@bandwidthd[0].subnets='192.168.249.0/24'

Wondering if I can add additional subnets

The wording from the wiki appears to indicate I should be able to add something like:

	option subnets '10.10.1.0/24'	# Trusted
    option subnets '10.10.3.0/24'	# Guest
 	option subnets '10.10.10.0/24'	# IOT

and also tried as list

    option subnets '10.10.1.0/24, 10.10.3.0/24, 10.10.10.0/24'

but that doesn't appear to catch either.