Auto update AP How-to

Organisations running wifidog often have a lot of access points in different locations. When needing to upgrade the firmware, it is a pain to go around all those hotspots to upgrade them one by one. It would be preferable if the hotspots upgraded themselves when a new firmware version is available.

This how-to describe how to compile a firmware so that it can be upgraded automatically.

Of course, you should test the system for your own setting before deploying it!

Pre-requisites

* This how-to supposes a knowledge of how to compile a custom image of the firmware (for that, the following document has been a great help:  http://nbd.name/openwrt.pdf).

* Access to a web site that will serve as a custom repository. This repository will contain the custom firmware images and a directory for custom versions of packages (if different from the openwrt packages)

Compilation

References:
 http://nbd.name/openwrt.pdf
 http://nuwiki.openwrt.org/oldwiki/ImageBuilderHowTo (not up to date with the latest version of kamikaze)

Suppose BUILD_ROOT is the root directory of the openwrt image builder.

To add custom files to the image, create a new directory named files in BUILD_ROOT. In this directory, you may copy all files in subdirectories as they would go on the image.

  • Create a file that will contain the image number
cd files
mkdir etc 
cd etc
touch custom_compil 

Now edit the custom_compil file so it contains the version of the custom image

1 
  • Add the configuration file to keep configuration options for the customupgrade script. From the BUILD_ROOT/files directory
mkdir etc/config
cd etc/config
touch customupgrade.conf

Edit customupgrade.conf with the following information

# Config file for custom configuration options for the auto-upgrade script
# All options are defined with default values in the script

# custom_file : default /etc/custom_compil
# Specifies the file on the router that contains the number of the custom compilation of this image
# 
custom_file /etc/custom_compil

# temp_file : default /tmp/latest
# The temporary file to which to download the 'latest' file from the server
# Default value should do usually, unless the tmp directory is somewhere else
# temp_file /tmp/latest

# server 
# MANDATORY
# The web server root where the 'latest' file, and 'package-list' files are kept
server http://wifidog.testserver/files/

# filename : default latest
# The name of the file on the server containing the information on the latest image
#
filename latest

# packagelist : default packages-list
# The name of the file on the server containing the information on the package 
# versions that should be installed on the server
packagelist packages-list

# temp_package: default /tmp/packages-list
# The temporary file to which will be downloaded the packages list file from the server
# Default value should do usually
# temp_package /tmp/packages-list
  • Add the script that will check for image updates and package updates. From the BUILD_ROOT/files directory
mkdir usr
mkdir usr/bin
cd usr/bin
touch customupgrade
a+x customupgrade 

Edit customupgrade with this script (may need to be tweaked to your installation)

#!/bin/sh
#
# This script verifies if a new firmware is available on the server
# It supposes that a file on the machine exists in /etc/custom_compil.txt containing
# the custom firmware's version.  On the server, a file named "latest" contains the compil
# number of the latest firmware

CUSTOM_FILE=/etc/custom_compil.txt
TEMP_FILE=/tmp/latest
SERVER=http://wifidog.testserver/files/
FILENAME=latest
PACKAGELIST=packages-list
TEMP_PACKAGE=/tmp/packages-list

CONF_FILE=/etc/config/customupgrade.conf

# Overwrite the default values with the config if necessary
if egrep -v '^#.*' "$CONF_FILE" | grep 'custom_file'; then
        CUSTOM_FILE=$(egrep -v '^#.*' "$CONF_FILE" | grep 'custom_file' | awk '{print $2}')
fi
if egrep -v '^#.*' "$CONF_FILE" | grep 'temp_file'; then
        TEMP_FILE=$(egrep -v '^#.*' "$CONF_FILE" | grep 'temp_file' | awk '{print $2}')
fi
if egrep -v '^#.*' "$CONF_FILE" | grep 'server'; then
        SERVER=$(egrep -v '^#.*' "$CONF_FILE" | grep 'server' | awk '{print $2}')
fi
if egrep -v '^#.*' "$CONF_FILE" | grep 'filename'; then
        FILENAME=$(egrep -v '^#.*' "$CONF_FILE" | grep 'filename' | awk '{print $2}')
fi
if egrep -v '^#.*' "$CONF_FILE" | grep 'packagelist'; then
        PACKAGELIST=$(egrep -v '^#.*' "$CONF_FILE" | grep 'packagelist' | awk '{print $2}')
fi
if egrep -v '^#.*' "$CONF_FILE" | grep 'temp_package'; then
        TEMP_PACKAGE=$(egrep -v '^#.*' "$CONF_FILE" | grep 'temp_package' | awk '{print $2}')
fi

# Verify is a new firmware upgrade is available on the server
if [ -f $CUSTOM_FILE ]; then
        ACTUAL=$(cat $CUSTOM_FILE)

        if [ -f $TEMP_FILE ]; then
                rm $TEMP_FILE
        fi
        wget -O $TEMP_FILE $SERVER$FILENAME

        if [ -f $TEMP_FILE ]; then
                cat $TEMP_FILE | grep 'version' | cut -d: -f2 | awk '{print $1}'
                LATEST=$(cat $TEMP_FILE | grep 'version' | cut -d: -f2 | awk '{print $1}')
                LATESTIMG=$(cat $TEMP_FILE | grep 'file' | cut -d: -f2 | awk '{print $1}')
                MD5SUM=$(cat $TEMP_FILE | grep 'md5sum' | cut -d: -f2 | awk '{print $1}')
        fi

        if [ $LATEST -gt $ACTUAL ]; then
                wget -O "/tmp/"$LATESTIMG $SERVER$LATESTIMG
                ACTUALMD5=$(md5sum "/tmp/"$LATESTIMG | awk '{print $1}')
                if [ $MD5SUM = $ACTUALMD5 ]; then
                        sysupgrade "/tmp/"$LATESTIMG
                fi
        fi
fi

# If we get here in the script, then no new firmware was installed, we check for new packages

# First, verify if the custom package repository is in the opkg.conf file
#
# If you do not plan to have one such directory on your server, you may comment out
# the following lines
#
REPO=$(grep "$SERVER" /etc/opkg.conf)
if [ -z "$REPO" ]; then
        echo "src custom_pack "$SERVER"packages" >> /etc/opkg.conf
fi

# update the package repository to get all the latest versions
opkg update

# Download the packages-list file from the custom server.  
# This file is such that for each line contains the package name and version to be installed 
# example: 
#
# wifidog 1.1.5-1
# ntp 2.1-1
#
# Only those packages will be installed if the version is different from the one currently installed
if [ -f $TEMP_PACKAGE ]; then
        echo "removing file "$TEMP_PACKAGE
        rm $TEMP_PACKAGE
fi

wget -O $TEMP_PACKAGE $SERVER$PACKAGELIST

if [ -f $TEMP_PACKAGE ]; then
        cat $TEMP_PACKAGE | while read line; do
                PACKAGE=$(echo $line | cut -f1,2 | awk '{print $1}')
                VERSION=$(echo $line | cut -f1,2 | awk '{print $2}')
                if opkg status $PACKAGE | grep 'Version' | grep $VERSION > /dev/null; then
                        echo "package "$PACKAGES" "$VERSION" is already installed"
                else
                        echo "installing package "$PACKAGE
                        opkg install $PACKAGE
                fi              
        done
fi 
  • Now we need to add a cron job to run this script every day, or whenever necessary. Again, from the BUILD_ROOT/files directory
cd etc
mkdir crontabs
cd crontabs
touch root 

Edit the root file so it contains the cron job as necessary

# /etc/crontab/root:  Cron job to be run on custom openwrt firmware

# m	h	dom	mon	dow	command
*	2	*	*	*	/usr/bin/customupgrade > /tmp/custom.log

You are now ready to compile your custom image!

Server setup

When you are ready to deploy the image, you may put it on a server where the upgrade script can fetch it. The url of the repository with the image and packages will correspond to the SERVER variable you defined in the customupgrade script.

The root of the repository (the SERVER variable) will contain the following files

  • latest
  • packages-list
  • the images .trx files

latest has the following syntax

version: 1
file: myimage_v1.trx
md5sum: 83081f198d0a890e6d2c33114ccd35c4

It indicates that the current version the APs should be running is 3 and the image file to download is myimage_v3.trx, located in the same directory as the latest file. Note, this version must match the version number in the CUSTOM_FILE on the image, or else it will keep flashing every time the cron job is run!!

The packages-list file lists the packages that should be on the server (not exhaustive) along with the version that should be running, one per line. The customupgrade script will look at the current version of each of those packages and if it is not the one running, it will upgrade it (note that the upgrade uses opkg, so the version should really match what opkg will install on the AP, or else, at each execution of the script, the package will be detected as different and will upgrade again)

It looks like this

wifidog	20091125-1
dropbear	0.51-2 

This means that the wifidog package on the AP should be version 20091125-1 and the dropbear package should be 0.51-2. If the current version is different, it will be upgraded. If you want to add the openvpn package on the AP, you can just update the packages-list file like this.

wifidog	20091125-1
dropbear	0.51-2 
openvpn		2.0.9-5.1

If any unlisted package is released in the openwrt directory, it will not be upgraded. Only the packages in the packages-list file are upgraded (why? because the organisation wants to control what packages/versions are on the router, because it has been tested like this and is known to work. What if a new release of a package causes another one to not work! The last thing you want is to go to each AP to correct the error)

Custom package repository

You may need to compile custom version of packages, or add new packages that are not in the openwrt default repository. The customupgrade supposes that SERVER/packages is a package repository for your image, so it adds it to the repositories opkg looks for (if it is not the case, you may comment out those lines in the script).

The repository directory on the server must contain the files necessary for such a repository. For example, this repository contains only one package, the wifidog package and has the following files:

  • Packages
  • wifidog_20091125-1_mipsel.ipk

and the Packages file is as follows

Package: wifidog
Version: 20091125-1
Depends: iptables-mod-extra, iptables-mod-ipopt, iptables-mod-nat, iptables-mod-nat-extra, libpthread
Provides: 
Source: /home/nbd/release/packages/net/wifidog
Section: net
Priority: optional
Maintainer: Wifidog development team <http://dev.wifidog.org>
Architecture: mipsel
Installed-Size: 122549
Filename: ./wifidog_20091125-1_mipsel.ipk
Size: 48397
MD5Sum: 79c358e24bed3b6e6b29f5fe0a6e8794
Description:  The Wifidog project is a complete and embeddable captive
 portal solution for wireless community groups or individuals
 who wish to open a free Hotspot while still preventing abuse
 of their Internet connection.

Multiple platforms

Your organisation may not use only one type of hardware. A different image and sets of packages needs to be compiled for each platform.

Each platform would have its own directory on the repository server. So each customupgrade file will need to point to the right url. That needs only to be done the first time a compilation is made.

When compiling, you'll need to use build environments, one for each platform, and edit the custom upgrade files and makefiles accordingly. See  http://nbd.name/openwrt.pdf page 29 for a simple explanation on how to do this. Each time you compile a new version of the image, you just need to switch between environments, update the compilation number for each and compile.

Compiling new images

The first time an image is built with this system, all the files mentioned earlier will have been created. From one compilation to the other, these files won't have to be modified.

The only files that needs to be updated on the router is the files/etc/custom_compil.txt file to reflect the compilation number of this build.

After the compilation, the new image file needs to be deployed on the server and the latest file needs to be upgraded to reflect the new image. You can calculate the md5 checksum of the new package with the following command

> md5sum myimage_v2.trx
51c50a0c62484151b8e459ad79f71e7c  myimage_v2.trx

Latest will now look like this:

version: 2
file: myimage_v2.trx
md5sum: 51c50a0c62484151b8e459ad79f71e7c