I’ve taken inspiration from the following sources:
The main ideas would be:
However, for local development you most likely won’t need this. An alternative solution (which will be used in the tutorial later on) would be to copy the source to the build directory in the preparation faze.
Our goal (taken from the first linke tutorial) is to get the following little C program to compile and run:
/****************
* Helloworld.c
* The most simplistic C program ever written.
* An epileptic monkey on crack could write this code.
*****************/
#include <stdio.h>
int main(void)
{
printf("Hell! O' world, why won't my code compile?nn");
return 0;
}
The first step is to create a Makefile for it:
# build helloworld executable when user executes "make"
helloworld: helloworld.o
$(CC) $(LDFLAGS) helloworld.o -o helloworld
helloworld.o: helloworld.c
$(CC) $(CFLAGS) -c helloworld.c
# remove object files and executable when user executes "make clean"
clean:
rm *.o helloworld
Place the files in the packages/helloworld/src
directory of either the checked-out OpenWrt source or the Openwrt SDK. Now change into this directory and make sure that everything builds on our local system (without the crosscompiling magic):
make
./helloworld <-- this should output the message
make clean <-- clean up after ourselves
Now to create the openwrt makefile. This will be located one level up (ie. packages/helloworld/Makefile
):
#
# Copyright (C) 2008 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
# $Id$
include $(TOPDIR)/rules.mk
PKG_NAME:=helloworld
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define Package/helloworld
SECTION:=utils
CATEGORY:=Utilities
TITLE:=Helloworld -- prints a snarky message
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Configure
endef
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
endef
define Package/helloworld/install
$(INSTALL_DIR) $(1)/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/helloworld $(1)/bin/
endef
$(eval $(call BuildPackage,helloworld))
The makefile should be pretty self-explanatory. A couple things I would like to highlight:
Build/Prepare
is the step where we copy our source-code to the build directory. This is a hack to circumvent the need of downloading the tgz file, but it is a hack which works well (you might want to add the –r switch to cp if you have nested directories in src – this isn’t the case for this simple example) Build/Compile
step it is very important to include the $(TARGET_CONFIGURE_OPTS)
part. Without it, the thing will build, but it will link with the standard libc, rather than the ulibc available on the Openwrt router. Tracking down this error is made harder by the unintuitive error messages. Specifically, you will see something like this on your router: “/bin/ash: The command /bin/helloworld can not be found
”, even though you see that the file exists and it has execute permissions! To verify that your issues are caused by this problem, simply too a “less /bin/helloworld
” on your router and check to see if you have strings indicating glibc (instead of ulibc). Now you are ready to compile:
make V=99
command make menuconfig
”, make sure that your package is checked for build (you should see the letter M near it) and then issue make V=99
. Be aware that compiling the full tree can take a considerable time (more than a hour in some cases). Your package should now be ready. Copy the package (you will find it in the bin/packages/target-...
subfolder) to your router (or better yet, a Qemu VM running OpenWrt – for safety) and test that everything works:
scp helloworld.ipkg root@router:/root
[root@router]# opkg install helloworld.ipkg
[root@router]# helloworld <-- the message should be printed
This would be all :-). Because of simplicity, this tutorial doesn’t cover the the calling of configuration scripts. Also, as far as I’ve seen, there is no easy way to include parts of other projects. For example, if I wish to create a package for LuaFileSystem, I would need lua.h
(and some other, related files). However, I haven’t found an easy way reference it from the lua
package, and have opted for putting a local copy in the src/lua
subdirectory.
Picture taken from cantrell.david’s buddy icon with permission.
]]>An other assumption I make is that you have a separate Linux machine. The techniques can be also adapted to Windows, but it is easier on Linux.
The first step is to make more space. Typical routers come equipped with small amount of flash (between 8 and 20MB), which isn’t even enough to install all the packages. This means that some kind of external storage needs to employed. In this example I’m assuming that an USB flash drive is used (a hidden assumption also is that the router in question has USB ports – for example some of the older WRT54Gs don’t, but ASUS 500 series do).
opkg update
(in version 8.09 the list of packages is kept in RAM, so it needs to refreshed after each reboot)
opkg install kmod-usb-uhci kmod-usb2 kmod-usb-storage kmod-fs-ext3
# The insmod commands might not be necessary, because I got the message
# "insmod: a module named X already exists" for all of them, but better
# safe than sorry
insmod usbcore
insmod uhci
insmod ehci-hcd
insmod scsi_mod
insmod sd_mod
insmod usb-storage
insmod ext3
sudo cfdisk /dev/sdx #delete other partitions and create a Linux partition
mkfs.ext2 -j /dev/sdx1 #make sure to use the correct device :-)
You might also want to consider dedicating part of the stick to swap (since the RAM of the router is also quite limited)
mkdir /mnt/usbstick
mount /dev/scsi/host0/bus0/target0/lun0/part1 /mnt/usbstick
This elaborate dance in needed because opkg (the package manager) insists on having X amount of free space on / before starting the install, even if /usr (where the packages will ultimately end up) is mounted from a separate device. opkg does have options which theoretically can work around this problem, however I couldn’t use them successfully.
mkdir /mnt/usbstick/usr_backup
# these commands will take some time
cp -R /usr/* /mnt/usbstick
cp -R /usr/* /mnt/usbstick/usr_backup
rm -rf /usr/*
umount /mnt/usbstick
mount /dev/scsi/host0/bus0/target0/lun0/part1 /usr
# now install the new packages. a few comments:
# - nano is so that we can do some basic text editing (yeah, vi is too hard for me :-))
# - php5-cli is needed because in the future an update capability will be added to
# the webhoneypot, which will be run from the command line
# - php5-mod-curl - it is possible that this will be a dependency in the future
# - php5-mod-openssl - the updates will be (possibly) done trough SSL in the future
opkg install lighttpd lighttpd-mod-cgi lighttpd-mod-rewrite nano php5 php5-cli
php5-mod-curl php5-mod-openssl php5-mod-pcre php5-mod-sockets
# now copy back everything to /usr
umount /usr
mount /dev/scsi/host0/bus0/target0/lun0/part1 /mnt/usbstick
cp -R /mnt/usbstick/usr_backup/* /usr/
# and remount the stick again
umount /mnt/usbstick
mount /dev/scsi/host0/bus0/target0/lun0/part1 /usr
Now we have the packages installed. What follows is the fetching of the honeypot code from the repository and its installation to the router.
svn export http://webhoneypot.googlecode.com/svn/trunk/
lighttpd.conf
server.modules = ("mod_rewrite", "mod_cgi") server.document-root = "/usr/wh/html/" server.upload-dirs = ( "/tmp/" ) server.errorlog = "/usr/wh/logs/lighttpd_error.log" index-file.names = ( "index.php", "index.html", static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) server.port = 80 server.bind = "0.0.0.0" server.pid-file = "/var/run/lighttpd.pid" dir-listing.encoding = "utf-8" server.dir-listing = "disable" url.rewrite-once = ( "^/(.*)$" => "/index.php/$1" ) cgi.assign = ( ".php" => "/usr/bin/php-cgi" ) # debug.log-request-handling = "enable"
php.ini
[PHP] engine = On short_open_tag = Off asp_tags = Off output_buffering = Off max_execution_time = 5 max_input_time = 60 memory_limit = 8M error_reporting = E_ALL & ~E_NOTICE register_globals = Off post_max_size = 8M magic_quotes_gpc = Off magic_quotes_runtime = Off extension_dir = "./" enable_dl = Off cgi.force_redirect = 1 file_uploads = Off allow_url_fopen = On allow_url_include = Off apc.enabled = Off extension_dir = "/usr/lib/php/" extension=pcre.so
We set up lighttpd to run PHP scripts using the CGI protocol (FastCGI would be more efficient, but also more complicated). The steps were adapted from this tutorial. The php.ini file is needed for two reasons: first, Perl regex support is not compiled into the PHP binary, so we must load it. Second APC support is compiled into the PHP library, so we must disable it, since it tries to allocate 32M of memory by default, which makes PHP fail, since we have around 20M of memory in total :-). To test that your PHP installation is workin, issue the following command on the router: /usr/bin/php-cgi -v
It should output some basic information about PHP (lik version, copyright, etc). If it fails because of the APC cache, it outputs error message like the one described here: [apc-error] apc_shm_create: shmget(0, 8388608, 658) failed: No error. It is possible that the chosen SHM segment size is higher than the operation system allows. Linux has usually a default limit of 32MB per segment.
# on the router:
mkdir /usr/wh
# on the Linux box - replace 192.168.1.1 with your router's IP
scp -r * [email protected]:/usr/wh
# on the router:
mkdir /etc/lighttpd
mv /usr/wh/lighttp.conf /etc/lighttpd
mv /usr/wh/php.ini /usr/bin
# start the webserver
lighttpd /etc/lighttpd/lighttp.conf
Check that everything is working by accessing the address http://192.168.1.1/phpbb/ from you box (where 192.168.1.1 should be replaced with your router’s address) nano /usr/wh/etc/config.local
. One thing I would suggest is to add loglevel=4
to it, so that the request details are also stored locally. Picture taken from mightyohm’s photostream with permission.
]]>