Recently I read about setting up an ad-blocking accesspoint using a Raspberry Pi on Adafruit. I quite like the idea of having ad-blocking as a centralized part of the network, especially when using a tablet or smartphone to access the Internet. Mobile devices often don't have good Ad-Blockers available or they cost a lot of performance. So in this post I will show how I integrated ad-blocking functionality into my router using the mentioned post as an inspiration.
The basic idea of this setup is to replace all advertisement with something less annoying. This is accomplished as follows: If a website includes ads they are hosted on separate adservers most of the time. So what we do in the router is to take all requests to known adservers and replace them with someting else. Fortunately there exist premade lists with known adservers, which we can use to manipulate what the browser will get as the response. So here ist the schematic overview what happens:
- The client browser sends a DNS-Request for an adserver to the router
- The router first looks the adservers name up in its internal list, and finds it
- The router returns its own address instead of the adservers adress
- The client sends the HTTP-request for the ad to the router
- The router answers with a 1×1 pixel transparent (meaning=invisible) image which is “shown” instead of the ad
So lets see how we have to configure our router to do this.
Before We Begin
Although I try to descripe the steps taken in detail, a basic knowledge of linux is neccessary to understand whats going on. You should for example be able to use ssh to access your routers commandline. If something seems unclear feel free to use the comments below for questions.
The steps described here work on my Asus RT-N16 Router running Toastmans TomatoUSB Version 1.28. It might or might not work on other routers or firmwares.
Also throughout this post 192.168.1.1 is assumed to be your routers internal IP-address. You'll have to use your real IP instead of this one.
Getting a List of Know Adservers
The first step to accomplish our goal is to have a list of all known adservers that we can feed to the router. For this purpose I use a little shell script which fetches the list and manipulates it according to my needs:
#!/bin/sh # Get the list in a format which we can feed to dnsmasq, replace the localhost IP with the routers IP wget -O - "http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext" | sed -e 's/127.0.0.1/192.168.1.1/g' > /tmp/adblock.hosts if [ -f "/tmp/adblock.hosts" ] then # We got a list, so move it to the permanent storage mv /tmp/adblock.hosts /jffs/adblock.hosts else # There was an error -> end the program echo "Error building the ad list, please try again." exit fi # Tell dnsmasq to re-read the config, including the just updated list killall -1 dnsmasq
The script will fetch the adserver list and set the router as the destination for all adservers. You will have to adapt the IP if you use a different one. Just replace the “192.168.1.1” in the wget-line with your routers IP. The script itself is stored on the permanent jffs-storage of my router, you could also put it on a USB-drive if you have one attached. The list of adservers is also saved on the jffs-storage.
To test out the script you have to copy it to the router, e.g. using scp:
~$ scp load_adblock.sh email@example.com:/jffs/load_adblock.sh
You should also make the script executable using
chmod +x /jffs/load_adblock.sh
on the router.
For a test and to get an initial list you should run the script through ssh. If you get a list of adservers in /jffs/adblock.hosts everything went ok.
Since the adservers may change it is a good idea to shedule a regular update for the list, so it is kept up to date automatically. This can be done using Tomatos built-in scheduler. Got to Administration→Scheduler and configure something like this:
This will update the list every sunday at 23:45.
Telling Dnsmasq to Use the List
Now that we have a working list of adservers all pointing to our router, we need to tell the routers DNS-Server, dnsmasq, that it should look up requests in this list.
To do this go to Advanced→DHCP/DNS and add the following line to the Dnsmasq custom configuration field:
This will tell dnsmasq to use our adserver list as an additional source for matching server names with IP-adresses. After saving the router will use the new list and tell any client on the network its own IP for all adservers on the list.
Serving a hidden "Dummy Ad" for all requests
With the above steps done all requests to a known adserver will be directed to the router. To make the setup complete we'll need something to answer this requests. Fortunatley there exists a little program which does just what we need: pixelserv
Pixelserv is a very small http server which responds with a 1×1 px transparent image to any request it recieves. Someone on the Linksysinfo forums already created a binary which can be run on tomato. For convenience you can also get it from here pixelserv.zip.
Just download the file, unzip it and put the binary to the /jffs/ directory of your router.
Before running it we have to make sure that Tomatos built-in webserver for the configuration interface doesn't listen on port 80, because this would conflict with pixelserv which also tries to run on port 80. To check go to Administration → Admin Access on your Routers Web-Interface and check that the value in the field HTTP Port is not 80. If it is 80, you'll have to change it. I use 81 for example. Remember that you'll have to use http://192.168.1.1:81/ to access your routers web interface after the change!
Now to check if it works ssh into your router, and run
This will return immediately since the process runs as a daemon in the background. To verify that it is indeed up and running just point your browser to http://192.168.1.1/something/ which should return a 1×1 px invisible image.
Since we don't want to start pixelserv manually each time the router is restarted we'll add the command to the init script. Navigate to Administration –> Scripts on the webinterface and put the following code into the box under init there:
sleep 10 && /jffs/pixelserv &
This will wait 10 seconds (for the jffs to become mounted) and then start pixelserv. So from now on pixelserv will be started automatically on startup of the router.
Thats it. If you did everything correctly most ads should now be filtered by our router. You won't need an active adblocking plugin in your browser anymore, and surfing an a tablet or smartphone should also be ad-free inside the network. Please note that this solution won't get any ad out there, since there are other methods of inserting ads, or new adservers that aren't on the list yet. For my needs this setup works quite well and gets almost all of the ads on the sites I visit.
As always leave your questions and suggestions in the comments below.