The distinction between past, present, and future is only a stubbornly persistent illusion.

Albert Einstein

I love my tomato-backed linksys WRT45GL router, but I had a very hard time determining how to get tcpdump onto it as it doesn’t come in the default image. I had read that openwrt came with it, or at least came with it as an optional package, though I didn’t want to give up the niceties of tomato as well as my familiarity with it and trade it in for openwrt just to get tcpdump. Luckily, I don’t have to! There may be a more elegant solution to do this than the solution I found, but after hours of searching I only found one linked binary to tcpdump which was not only not from a trusted source, but also was dynamically linked to a library that tomato didn’t have installed. Double-doh. Here is how I finally accomplished it:

WARNING: Openwrt requires a case-sensitive filesystem! Default MacOS and Windows partitions are case-insensitive so it will not work, and make menuconfig will tell you as much.

$ svn checkout svn://svn.openwrt.org/openwrt/trunk openwrt-kamikaze
$ cd openwrt-kamikaze/scripts
$ ./feeds update
$ ./feeds install tcpdump
$ cd ../
$ make menuconfig

make menuconfig screen

If you have ever compiled your own kernel before, this screen should look somewhat familiar. Either way, go into the “Network” subsection and select tcpdump, hit the spacebar until an “M” shows next to it, indicating that it will be built but not built directly into the image. Exit out of all the screens until it asks you if you want to save your configuration and select yes.

The last thing you need to do is make sure that tcpdump compiles with static libraries, because the libraries it needs are most likely not on your router now. (If you compiled it dynamically it would most likely result in this kind of error when trying to run it: ./tcpdump: can't load library 'libpcap.so.1.0') To compile it statically linked, edit the file feeds/packages/net/tcpdump/Makefile and add -static to the CCOPT, like so:

define Build/Compile
        $(MAKE) -C $(PKG_BUILD_DIR) \
                CCOPT="-static $(TARGET_CFLAGS)" INCLS="-I. $(TARGET_CPPFLAGS)" \
                DESTDIR="$(PKG_INSTALL_DIR)" \
                $(MAKE_FLAGS) \
                all install
endef

Now just type make in the parent directory of openwrt and wait a while. It will go through and compile the cross-compiler as well as the image and some other stuff you won’t be using (if anyone knows how to just compile the stuff that’s necessary, please comment! Otherwise, this will do just fine.) Once it is complete, you will probably want to isolate tcpdump from everything else, so to do something like copying it to /tmp/tcpdump just run something like this:

$ find ./staging_dir/ -name tcpdump -exec cp {} /tmp/tcpdump \;

To ensure that the file was compiled properly and statically, run the following command and you should receive the following response

$ file /tmp/tcpdump
/tmp/tcpdump: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), statically linked, not stripped

Once you have tcpdump compiled, you will need to put it onto your tomato-backed router. To do that, go to the administration page for the wireless router, select “Administration” and ensure that the SSH daemon starts up. You will ssh in as root, using the same password setup to access the administration interface via HTTP. Before you do that, however, under the “Administration” tab that is now open, click on “JFFS2″. If not enabled, click on “Enable” and “Format/Erase”. Once this is done, there should be a device mounted on your wireless router when you login, like so:

# mount
rootfs on / type rootfs (rw)
/dev/root on / type squashfs (ro)
none on /dev type devfs (rw)
/proc on proc type rw (0)
/tmp on ramfs type rw (0)
/dev/mtdblock/3 on /jffs type jffs2 (rw,noatime,nodiratime)

The JFFS2 partition is quite small (1.0M on my router), but will be large enough to hold tcpdump. This is a place for files that will permanently stay on your router through reboots and the like. If you were to put tcpdump in /root, for example, it would be gone the next time you rebooted.

Now that you have JFFS2 and ssh enabled, simply scp the tcpdump binary to /jffs on the wireless router and you should be able to execute it.

Unfortunately, again due to the storage limits on the wireless router, if you intend to save the output of tcpdump to a file (for example, to examine with wireshark later, etc), you will likely need to enable the “CIFS Client” under the same Administration tab that JFFS2 and ssh were inside of. The CIFS Client is essentially a SMB client, so you can use it to mount a samba share, windows share, mac os share shared via SMB, and so on. Under the /cifs1 entry, the UNC will be the familiar SMB url to the share, for example:

\\192.168.1.2\myshare

Fill in the username/password/etc as necessary. Once it is enabled and saved and the router mounts it, you should have a /cifs1 entry in your mounted devices list, and you can use it to save your tcpdump files to. For example:

# /jffs2/tcpdump -s 1500 -w /cifs1/mynetwork.out


6 Comments to “Compiling tcpdump for tomato firmware”

  1. alt says:

    Great article, it worked like a charm, thanks a lot!

  2. Brian says:

    Another thanks! Exactly the info I needed!

  3. zigomar10 says:

    hey! could you upload your compiled version? thanks :)

  4. Rick says:

    The tcpdump executable has grown larger over the years. To make it fit into /jffs, I had to add the -s option to the same line where the -static option is added. That causes the executable file to be stripped of all unneeded entries and shrinks it down to acceptable size. After that, tcpdump worked quite well. Thanks!

  5. Alex says:

    I am running dd-wrt instead of Tomato. After building `tcpdump’ using the above instructions and then SCPing it over, I get a “Bus error.” Could it be that this `tcpdump’ binary only works on Tomato and OpenWRT firmware? The output from `$file tcpdump’ is identical to Chris’s in the blog post.

  6. Alex says:

    And yes, I am also using a WRT54GL.

Leave a Reply