Detecting torrent traffic on a Linux box


At home I am sharing my Internet connection with several other family members. Sometimes my Internet is very slow with high latencies, causing my interactive SSH connections to stutter. The problem is always the same: somebody is downloading a torrent. And although I have no objection against torrent technology (it has many good applications), I hate it when I cannot work properly on my remote servers. So I decided to take action.

Wireshark and Tshark to the rescue

Wireshark has a command line version called “tshark”. It has a bittorent protocol analyzer and can be used to do Deep Packet Inspection (DPI). I decided to make a simple script that runs every 5 minutes and samples the network traffic for 10 seconds. After that it sends a report (top list, including packet count) of the local IP addresses that do the most torrent traffic (if there are any).  It can be ran using:

sudo tshark -a "duration:10" -Y bittorrent -f 'not port 80 and not port 22 and not port 443' | grep -o "192\.168\.1\.[0-9]\+" | sort | uniq -c | sort -rn | head | mail -E -s "LAN abusers"

It is using postfix to send email via the gmail SMTP server (gmail account required). I am runnig the above in a cron job every 5 minutes. You may simply run this script on the gateway of your network. In case you can setup a port mirror on the switch of your up-link, then you can run this in promiscuous mode. Tshark will try to enable this mode by default, if it does not work, then check the FAQ here.

Blocking on detection

There are several ways to block the user that is abusing your network. I feel that temporary null routing the IP address is the simplest way. Additionally you may add an entry to your DHCP lease table to avoid that the user can simply request a new IP address. Filtering the good from the bad traffic is actually much more complicated. For one, because you need to find all the bad packets (as the software may try to avoid the block, switching protocols). If you really want to give it a try, you may look at netfilter string match. If you do, then make sure you enter good offsets and ranges to avoid negative performance impact on your network. Also I would not know where to get a maintained and complete set of protocol signatures.


Detecting uTP

If you are using the “Deluge” torrent client, you will be quickly detected by the above script. When you are using “Transmission” (another client) you may get away undetected. This is caused by the Micro Transport Protocol (aka “uTP”). This is a UDP based torrent protocol that cannot be recognized by Tshark yet. It is not very hard to actually make a custom rule that detects “uTP”. This is the custom filter:

sudo tshark -a "duration:10" -Y 'udp[8:5] == "\x64\x32\x3A\x69\x70" or bittorrent' -f 'not port 80 and not port 22 and not port 443' | grep -o "192\.168\.1\.[0-9]\+" | sort | uniq -c | sort -rn | head | mail -E -s "LAN abusers"

The above command will detect also the “undetectable” uTP protocol. You may even extend the match a little as there are more fixed position bytes that can be matched.


Fast dynamic DNS with cron, PHP and DuckDNS

ducky_icon_mediumMy home connection has a 200 mbit cable Internet connection with 20 mbit up. Great for running a server, but every two days my ISP changes my IP address. When this happens I cannot connect to my home network anymore using VPN. Annoying, but certainly a (programming) challenge to me. The simple solution for this is to use a dynamic DNS solution. The name DynDNS popped up in my head, but apparently they are not free anymore (bummer). That’s why I chose to use the free dynamic DNS service “DuckDNS“. Then I realized that I do want a fast update of my dynamic DNS entry when my IP address changes, but I do not want to hammer DuckDNS. That’s why I wrote a small script to achieve this. You find it below.

DuckDNS PHP script to avoid hammering

On my website I installed the following PHP script that will call DuckDNS if the IP address of the caller has changed. It is must be called with a post request that holds a shared secret. This will avoid bots (or hackers) to change the DNS entry. Note that additionally HTTPS (SSL) is used to guarantee confidentiality.

// settings
$domains = 'cable-at-home'; //
$token = 'eb1183a2-153b-11e5-b60b-1697f925ec7b';
$file = '/tmp/duckdns.txt';
$secret = 'VeryHardToGuess';
// compare secret
if (!isset($_POST['secret']) || $_POST['secret']!=$secret) { http_response_code(403); die(); }
// compare with current ip
if ($ip==file_get_contents($file)) { http_response_code(304); die('OK'); }
// create url
$url = "$domains&token=$token&ip=$ip";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
$result = curl_exec($ch);
// if success update current ip
if ($result!='OK') { http_response_code(400); die($result); }

Install this script somewhere in your Apache “DocumentRoot” and name it “duckdns.php”.

Cron script that runs every minute

I installed the following cron job on my server that runs in my home and is connected with cable to the Internet using the “crontab -e” command:

* * * * * /usr/bin/curl -X POST -d 'secret=VeryHardToGuess'

Every minute this cron job executes a curl call to the duckdns.php PHP script on my website ( Only if the IP address is changed the call to DuckDNS ( is made to update the IP address. This avoids hammering the DuckDNS service, but also allows you to get the fastest response to an IP address change.


Note that in order to make this work you have to create an account at DuckDNS and then modify the “$domains” and “$token” parameters in the PHP script accordingly. You need to change “” in the cron job with the URL of your website. Also do not forget to replace “VeryHardToGuess” in both the PHP script as the cron job with a real secret. Any questions? Use the comments below!


Linux multi-player command line games (daemon-games)


Daemon-games is a set of multi-user console games for Linux. The name is a tribute to the well known Debian “bsdgames” package. A “daemon” is defined in Wikipedia as:

In multitasking computer operating systems, a daemon (/ˈdiːmən/ or /ˈdeɪmən/) is a computer program that runs as a background process, rather than being under the direct control of an interactive user. – Wikipedia

The first game in this series is multi-player snake and it is named “snaked” (the “d” is for daemon). You can see it in the screen-shot above. The next game I want to make is multi-player tetris (named “tetrisd”).

Implemented in C

This server is implemented in C. You can find the common code in the files “daemon.c” and “daemon.h”. It aims to be a simple implementation and that is why it is using the “select” call. The select call waits for input on a set of sockets (or file descriptors). When one of them has data the call returns and you can handle the socket(s) that have data. The server calculates the screens and sends them over in ANSI. It also calculates the differences between frames to reduce bandwidth. All user input is directly forwarded to the server.

Getting started with “snaked”

Running the server is as easy as:

git clone
cd daemon-games
./snaked 9000

Running the client is as easy as:

bash 9000

You should replace “” by the IP address of the server. The number “9000” is the TCP port the server runs on. You can easily review the client to see that it actually just running netcat. You can play with the “wasd” keys and “q” for quit.

Download the source code

You can! Please play with it and send me improvements or make complete new games. I would love to accept any PR. All code is on GitHub:

Use Ubuntu’s “hostapd” to monitor your smartphone

I don’t like smart-phones at all. I do not like how people use them in bars and restaurants. I also don’t like that the phone is always online. Especially not since all kinds of “apps” and background processes are constant leaking information about me. Call me a fool, but I’m worried about my privacy. Since my friends nowadays refuse to send me SMS (they solely rely on WhatsApp) I was getting socially isolated (showing up at canceled events and so on). That is why I recently gave up my stubbornness and decided to buy a smartphone as well. Still everyday I am wondering what (and how) my smartphone is communicating over the Internet. To get an answer to this question I decided to investigate this.


SitecomWL113WirelessNetworkUSBAdapter wireshark

I had the above Sitecom (WL-113) USB wifi dongle laying around that could serve as an access point for my phone so that I could peek into the communication on my PC using the excellent open source Wireshark software. This is a diagram of the infrastructure:


I am running Xubuntu 14.04 and I connected my USB dongle.


First I ran “lsusb” to confirm the adapter was identified.

maurits@nuc:~$ lsusb
Bus 002 Device 024: ID 0df6:9071 Sitecom Europe B.V. WL-113 rev 1 Wireless Network USB Adapter

And yes it was. Great! Now to see what the system says about it when I connected it. Running “dmesg” showed me the driver that was loaded:

maurits@nuc:~$ dmesg
[20068.576242] usb 2-1.4: new high-speed USB device number 24 using ehci-pci
[20068.669492] usb 2-1.4: New USB device found, idVendor=0df6, idProduct=9071
[20068.669498] usb 2-1.4: New USB device strings: Mfr=16, Product=32, SerialNumber=0
[20068.669501] usb 2-1.4: Product: USB2.0 WLAN
[20068.669504] usb 2-1.4: Manufacturer: Sitecom
[20068.744236] usb 2-1.4: reset high-speed USB device number 24 using ehci-pci
[20068.837283] ieee80211 phy12: Selected rate control algorithm 'minstrel_ht'
[20068.837521] zd1211rw 2-1.4:1.0: phy12
[20068.855382] IPv6: ADDRCONF(NETDEV_UP): wlan0: link is not ready

To see whether the adapter was really there I ran “ifconfig -a” and yes it was and it was named “wlan0”:

maurits@nuc:~$ ifconfig -a
lo        Link encap:Local Loopback
          inet addr:  Mask:
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:5387 errors:0 dropped:0 overruns:0 frame:0
          TX packets:5387 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:634228 (634.2 KB)  TX bytes:634228 (634.2 KB)

wlan0     Link encap:Ethernet  HWaddr 00:00:de:ad:be:ef
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

I did get a little curious of what the dongle would look like on the inside, so I Googled for “zydas wl-113”. I found the following image on


I also found a guy who had a Sitecom WL-113 with a Ralink 2571WF chip inside (probably not a “rev 1” model). But I am pretty sure mine has a ZyDAS 1211 as in the above picture (but I did not open it up). Before we can do “nice” things with it we need to see whether it supports “master mode“. This means that the dongle goes into a mode in which it behaves as an access point. Ubuntu has a tool called “iw” (install it with “sudo apt-get install iw”) that allows you to list the supported modes (amongst many other things) like this:

maurits@nuc:~$ iw list
Wiphy phy12
    Supported interface modes:
         * IBSS
         * managed
         * AP
         * AP/VLAN
         * monitor
         * mesh point

Bingo! Our dongle supports “AP” mode (many devices do not). You may want to try to put the adapter in master mode with the following command:

maurits@nuc:~$ iwconfig wlan0 mode master
Error for wireless request "Set Mode" (8B06) :
    SET failed on device wlan0 ; Operation not permitted.

But that fails. After reading the web a little I found that this does not mean that the dongle does not support it.

Installing “hostapd” the host access point daemon

You just need to install “hostapd” program using “sudo apt-get install hostapd”. Before you can start the hostapd application you need to take a few steps. First I had to create the “/etc/hostapd/hostapd.conf” file with the following contents:


Now edit the file “/etc/default/hostapd” and uncomment the “DAEMON_CONF” line and make it:


If we want the PC to temporarily act like a router we need to enable IPv4 forwarding:

sudo sysctl -w net.ipv4.ip_forward=1

Now you can start the “hostapd” access point software with:

sudo hostapd /etc/hostapd/hostapd.conf

If all goes well it should show:

maurits@nuc:~$ sudo hostapd /etc/hostapd/hostapd.conf
Configuration file: /etc/hostapd/hostapd.conf
Using interface wlan0 with hwaddr 00:00:de:ad:be:ef and ssid "MyNetwork"
wlan0: interface state UNINITIALIZED->ENABLED

If it does not work you may want to run the following:

sudo nmcli nm wifi off
sudo rfkill unblock wlan

This is because network manager has detected the wlan interface and grabbed it. If you need debug output you may run:

sudo hostapd -d /etc/hostapd/hostapd.conf

If you need even more debug output you may run:

sudo hostapd -dd /etc/hostapd/hostapd.conf

If this fails with the following message:

hostapd_free_hapd_data: Interface wlan0 wasn't started

Then execute:

sudo service hostapd stop

If it says it started (using “sudo hostapd”), but you actually don’t see the Wifi network on your smartphone then reconnecting the dongle and starting all over again may help. Note that the “hostapd” service will automatically be started on next boot.

Bridging to get Internet access


Now you may want to configure a bridge between eth0 (your Internet connection) and wlan0 (your dongle access point). First we remove the IP address from eth0 using. Then we add eth0 to bridge br0 (which already contains wlan0). After that we bring the bridge up, let it do DHCP and which also adds a default route to the gateway using:

sudo ifconfig eth0 inet
sudo brctl addif br0 eth0
sudo ifconfig br0 up
sudo dhclient br0

Now you should still be able to surf the Internet while you also have a software access point running on your computer. If you want to undo the bridge configuration you may run:

sudo ifconfig br0 down
sudo brctl delif br0 wlan0
sudo brctl delif br0 eth0
sudo ifconfig br0 down
sudo dhclient eth0

Permanent configuration (persist on reboot)

The IPv4 forwarding setting can be made permanent by uncommenting the following line in “/etc/sysctl.conf”:

# Uncomment the next line to enable packet forwarding for IPv4

If you want to make the bridge configuration permanent you can add the following to “/etc/network/interfaces”:

manual wlan0
manual eth0

auto br0
iface br0 inet dhcp
        bridge_ports eth0

Note that this wont work since the network manager will still grab the wlan0 and execute “rfkill”. To avoid this you can turn off the network manager completely (and permanently) with:

sudo service network-manager stop
echo "manual" | sudo tee /etc/init/network-manager.override

To re-enable the network manager simply do the opposite:

sudo rm /etc/init/network-manager.override
sudo service network-manager start

Capturing with Wifi with Wireshark

Now we can start Wireshark on the wlan0 interface using:

sudo wireshark wlan0

And we get nice output:


Using this tool I can record and analyze the communication of the apps I installed on my smartphone.


Figuring all the above out was not possible without the following websites:


Setup your own Linux router using iptables – Part 1

When using Linux on servers we all know that one basic tool to secure the setup is iptables. Iptables not only allows you to secure your setup, it will also allow you create a routing service in a very controlled and efficient way.router_hw_example

Every distribution has it’s how way to configure and deploy the desired set of rules, Ubuntu and it’s variants use ufw service, Redhat and it’s clones iptables service, OpenSuSE uses SuSEfirewall2 service, etc.

To be honest my preferred way of managing is the Redhat style. It’s cleaner, doesn’t have hidden features and it doesn’t try to make it easier for the user. Although some of those functions may seam good in the long run they may become limitations.

So, why not make your own custom iptables startup script and iptables rule set that will be compatible with most of Linux distributions? This blog post will focus in two distinct areas:

  1. Create an init script to enforce our iptables rule set
  2. Create a set of iptables rules that could be put in place on a Linux gateway


  1. You have ip_forwarding enabled on your system (if not you may check this link)
  2. You have a mainstream Linux distro with iptables and it’s most used modules (like conntrack)
  3. You have root access to the Linux GW
  4. I’m using OpenSuSE 13.1 x86_64, nevertheless this guide should work on other distributions only with minor changes on configuration variables

Lets start by defining the scope of the rule set, the example scenario will be (the IPs were randomly selected) one Linux box serving as gateway for three networks):

  • eth1 – lets call it orange and use it as DMZ (we will have the server providing external services in this network)
  • eth2 – lets call it green and use it as users networks, having some servers on the subnet, we also want to give VPN users to this subnet
  • tun0 – lest call it blue and use it for VPN access

In the examples we use different network cards but it can also be done with VLANs and/or with a plain network interface having all the logic based on src/dst IPs, ports and protocols.

On the Internet access interface (eth0, we may call it red) we will have 6 usable IP addresses These IPs will be exposed to the Internet.

On those six available IP addresses we will want different services also exposed to the Internet, the network address translation table will be:

  • port 53 -> port 53 (udp)
  • port 53 -> port 53 (tcp)
  • port 80 -> port 80 (tcp)
  • port 443 -> port 443 (tcp)

Apart from the NAT table we will also make sure that:

  • Access to other destination IP/port/protocol combination will be denied
  • The default gateway to access the wan will be

In this example we won’t talk about having more than one gateway, although it’s easily achievable.

Finally we won’t allow internal IP traffic to access the Internet freely (we all know how dangerous it is :)), so  we will limit the access to the following ports/protocols:

  • TCP: 25,80,110,144,443,465,993
  • UDP: 53,1194
  • ICMP: type 8

Please note that this doesn’t limit the access from our Linux GW to the Internet, it will be applied on the forward chain so the effect will only be visible on the clients using our Linux GW as their GW. The Linux GW will have complete access to all the networks, including unfiltered access to the Internet.

Bellow is a diagram about how the network should like:

        ------------         ---------------------------------------
        |          |         | Linux GW:                           |
        | Internet |---------| Ext net (eth0):  *   |
        |          |         | Int net (eth1):     |
        ------------         | Int net (eth2):    |
                             | VPN net (tun0):     |
                              /               |                \
           output            /          input |    output       \  output
          (Internet/        /      everywhere |   (to Internet)  \(to orange / Internet)
            orange)        /      to services |                   \
                ----------------         ------------        ----------------
                   green                   orange                       blue
                               - udp 53
                               - tcp 80
                               - tcp 443

* the external network includes all the IPs between -

Lets get back to the first target on this blog post: Create an init script.

Please create a file named pmso-fw (in reality you can name it whatever you like) in /etc/init.d

# PMSO fw initializer
# pmso-fw:   Pedro Oliveira FW builder
# chkconfig: 345 03 03
# description:  This is a daemon for managing firewall scripts \
#               config file should be located in \
#               /etc/sysconfig/iptables
# notes:        please set rc.status according to your OS / Distro
# processname: pmso-fw
# pidfile: /var/run/

. /etc/rc.status


test -f $CONFIG_FILE

start() {
  sleep 3650d &
  echo $! > $PIDFILE
  echo -n $"Starting $SERVICE "
  rc_status -v

stop() {
  echo -n $"Stopping $SERVICE "
  $IPT4 -F && \
  $IPT4 -X && \
  $IPT4 -t nat -F && \
  $IPT4 -t nat -X && \
  $IPT4 -t mangle -F && \
  $IPT4 -t mangle -X && \
  rc_status -v

restart() {

stat() {
  rc_status -v

# See how we were called.
case "$1" in
    echo -n "Checking status of $SERVICE "
    rc_status -v
    echo $"Usage: $0 {start|stop|status|restart}"
    rc_failed 2

Lets describe what this script does:

  1. The first section look a regular comment section, nevertheless it’s in this zone that are configured the systemd options. Be careful if editing
  2. /etc/rc.status – this line means that the /etc/rc.status file will be imported, this file contains the systemd control functions
  3. General script configs and main utilities location section
  4. Script functionality functions:
  5. start() – The start function starts with stop, this may seam odd but if we are starting the FW service we want to guarantee that no rules will be duplicated. I also issue a sleep command, this is due to the fact that iptables isn’t a process and we need to keep the process open so we have a clean process status
  6. stop() – The stop function cleans all the tables
  7. restart() – No explanation needed
  8. stat() – Stat will use the systemd functions to return the status of the fw rules

We also need to make the script executable:

chmod u+x /etc/init.d/pmso-fw

Now you should create the systemd config file IF you use systemd. The file should be placed in: /etc/systemctl/system/pmso-fw.service and it will have the following content:


ExecStart=/etc/init.d/pmso-fw start
ExecStop=/etc/init.d/pmso-fw stop


Finally lets setup the rule set.
The format of the this rule set is compatible with iptables-save and iptables-restore

The location of the file will be: /etc/sysconfig/iptables

:LOG-INPUT - [0:0]
:LOG-FORWARD - [0:0]
:RED-TO-ORANGE - [0:0]
:BLUE-TO-GREEN - [0:0]
:GREEN-TO-BLUE - [0:0]
:TO-RED - [0:0]
:TO-GREEN - [0:0]
:TO-ORANGE - [0:0]
:T0-BLUE - [0:0]
:FROM-RED - [0:0]
:FROM-GREEN - [0:0]
:FROM-ORANGE - [0:0]
:FROM-BLUE - [0:0]

-A LOG-INPUT -j LOG --log-prefix "IPTables-reject-INPUT: "
-A LOG-FORWARD -j LOG --log-prefix "IPTables-reject-FORWARD: "
-A LOG-BLUE-TO-GREEN -j LOG --log-prefix "IPTables-reject-BLUE-TO-GREEN: "
-A LOG-GREEN-TO-BLUE -j LOG --log-prefix "IPTables-reject-GREEN-TO-BLUE: "

-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -m limit --limit 2/sec -j ACCEPT
-A INPUT -d -p tcp -m tcp --dport 22 -j ACCEPT -m comment --comment "Access to the SSH interface only on the DMZ interface"
-A INPUT -m limit --limit 2/sec -j LOG-INPUT
-A INPUT -m limit --limit 2/sec -j LOG-INPUT
-A INPUT -j REJECT --reject-with icmp-port-unreachable

-A FORWARD -i eth1 -o eth0 -s -j TO-RED
-A FORWARD -i eth2 -o eth0 -s -j TO-RED
-A FORWARD -i tun0 -o eth0 -s -j TO-RED

-A FORWARD -o eth1 -d -j TO-ORANGE
-A FORWARD -o eth2 -d -j TO-GREEN
#-A FORWARD -o tun0 -d -j TO-BLUE

-A FORWARD -i eth1 -s -j FROM-ORANGE
-A FORWARD -i eth2 -s -j FROM-GREEN
-A FORWARD -i tun0 -s -j FROM-BLUE

-A FORWARD -i tun0 -s -o eth2 -d -j BLUE-TO-GREEN
-A FORWARD -i tun0 -s -o eth1 -d -j BLUE-TO-ORANGE
-A FORWARD -i eth2 -s -o eth1 -d -j GREEN-TO-ORANGE

-A FORWARD -i eth1 -o eth1 -j ACCEPT
-A FORWARD -i eth2 -o eth2 -j ACCEPT
-A FORWARD -i tun0 -o tun0 -j ACCEPT

-A FORWARD -m limit --limit 2/sec -j LOG-FORWARD
-A FORWARD -j REJECT --reject-with icmp-port-unreachable

-A BLUE-TO-GREEN -m limit --limit 2/sec -j LOG-BLUE-TO-GREEN
-A BLUE-TO-GREEN -j REJECT --reject-with icmp-port-unreachable



-A GREEN-TO-BLUE -m limit --limit 2/sec -j LOG-GREEN-TO-BLUE
-A GREEN-TO-BLUE -j REJECT --reject-with icmp-port-unreachable

-A TO-RED -p icmp -m icmp --icmp-type 8  -j ACCEPT
-A TO-RED -p tcp -m multiport --dports 25,80,110,144,443,465,993 -j ACCEPT
-A TO-RED -p udp -m multiport --dports 53,1194 -j ACCEPT



-A PREROUTING  -d -p udp --dport 53 -j DNAT --to-destination
-A PREROUTING  -d -p tcp --dport 53 -j DNAT --to-destination

-A PREROUTING  -d -p udp --dport 53 -j DNAT --to-destination
-A PREROUTING  -d -p tcp --dport 53 -j DNAT --to-destination

-A PREROUTING  -d -p tcp --dport 80 -j DNAT --to-destination
-A PREROUTING  -d -p tcp --dport 443 -j DNAT --to-destination




Now that we have the files ready we need to setup our newly created installation script, this can be done with the following commands if you use systemv:

insserv /etc/init.d/pmso-fw (if you use systemv)
/etc/init.d/pmso-fw status

or if you use systemd

systemctl daemon-reload
systemctl enable pmso-fw # to make it permanent
systemctl start pmso-fw # to start it

you’ll be able to check the status of the fw with:

systemctl status pmso-fw

if you want to see the rules in use at any given moment you may use:



iptables -n -L --line-numbers
iptables -n -L -t NAT --line-numbers

After editing the rules you can also reload the service with:

systemctl restart pmso-fw

As you might have noticed we log the dropped packets, not all because if we have an exposed router the log will be immense but we limit the writes to two entries per second. This will allow us to debug something that isn’t right on our configuration but won’t fill our log file system or root partition (depending on how you partitioned your system).

In OpenSuSE 13.1 you may check your logs in “/var/log/firewall”.

On the next post we will get into more detail on the rules, detailing what’s done on the main blocks of the iptables rule set. I hope you have as much fun adapting this how to to your needs as I had making it.

Pedro M. S. Oliveira