Skip to content

Internet over USB on Touch – Automating the configuration

In previous post we have seen how to configure a touch device and the host system to provide network connectivity over USB to the devices via USB. This article describes how to automate this setup when a device is connected.

There are 2 distinct phases to this setup:

  1. Configuration of the bridge, trafic forwarding and DHCP on the host.
  2. Add the interface to the bridge when a device is connected.

Step one must be done only once, when the host system starts. In Ubuntu the answer is to write an upstart job that would for example start when networking is ready.

The script below (shamelessly inspired by the work of Serge Hallyn on packaging of LXC) is an example of an upstart job that will go into /etc/init/touch-net.conf:

description "touch usb network"
author "Jean-Baptiste Lallement <jean-baptiste.lallement@canonical.com>"

start on started networking
stop on runlevel [!023456]

env USE_TOUCH_BRIDGE="true"
env TOUCH_BRIDGE="usbbr0"
env TOUCH_ADDR="192.168.124.1"
env TOUCH_NETMASK="255.255.255.0"
env TOUCH_NETWORK="192.168.124.0/24"
env TOUCH_DHCP_RANGE="192.168.124.2,192.168.124.254"
env TOUCH_DHCP_MAX="253"
env TOUCH_DHCP_CONFILE=""
env varrun="/var/run/touch"

pre-start script
    [ -f /etc/default/touch-net ] && . /etc/default/touch-net
    [ "x$USE_TOUCH_BRIDGE" = "xtrue" ] || { stop; exit 0; }

    cleanup() {
        # dnsmasq failed to start, clean up the bridge
        iptables -t nat -D POSTROUTING -s ${TOUCH_NETWORK} ! -d ${TOUCH_NETWORK} -j MASQUERADE || true
        ifconfig ${TOUCH_BRIDGE} down || true
        brctl delbr ${TOUCH_BRIDGE} || true
    }

    if [ -d /sys/class/net/${TOUCH_BRIDGE} ]; then
        if [ ! -f ${varrun}/network_up ]; then
        # bridge exists, but we didn't start it
            stop;
        fi
        exit 0;
    fi

    # set up the usb network
    brctl addbr ${TOUCH_BRIDGE} || { echo "Missing bridge support in kernel"; stop; exit 0; }
    echo 1 > /proc/sys/net/ipv4/ip_forward
    mkdir -p ${varrun}
    ifconfig ${TOUCH_BRIDGE} ${TOUCH_ADDR} netmask ${TOUCH_NETMASK} up
    iptables -t nat -A POSTROUTING -s ${TOUCH_NETWORK} ! -d ${TOUCH_NETWORK} -j MASQUERADE

    dnsmasq --strict-order --bind-interfaces --pid-file=${varrun}/dnsmasq.pid --conf-file=${TOUCH_DHCP_CONFILE} --listen-address ${TOUCH_ADDR} --dhcp-range ${TOUCH_DHCP_RANGE} --dhcp-lease-max=${TOUCH_DHCP_MAX} --dhcp-no-override --except-interface=lo --interface=${TOUCH_BRIDGE} --dhcp-leasefile=/var/lib/misc/dnsmasq.${TOUCH_BRIDGE}.leases --dhcp-authoritative || cleanup
    [ ! -d ${varrun} ] && mkdir -p ${varrun}
    touch ${varrun}/network_up
end script

post-stop script
    [ -f /etc/default/touch ] && . /etc/default/touch
    [ -f "${varrun}/network_up" ] || exit 0;
    # if $TOUCH_BRIDGE has attached interfaces, don't shut it down
    ls /sys/class/net/${TOUCH_BRIDGE}/brif/* > /dev/null 2>&1 && exit 0;

    if [ -d /sys/class/net/${TOUCH_BRIDGE} ]; then
        ifconfig ${TOUCH_BRIDGE} down
        iptables -t nat -D POSTROUTING -s ${TOUCH_NETWORK} ! -d ${TOUCH_NETWORK} -j MASQUERADE || true
        pid=`cat ${varrun}/dnsmasq.pid 2>/dev/null` && kill -9 $pid || true
        rm -f ${varrun}/dnsmasq.pid
        brctl delbr ${TOUCH_BRIDGE}
    fi
    rm -f ${varrun}/network_up
end script

You will recognize in this upstart job the configuration of the bridge, the forwarding rule and dnsmasq. The configuration will be loaded from /etc/default/touch-net if this file exists.

This is all for step 1.

Step 2, adding the interface to the bridge, can be achieved with an udev rule that will run a script when a network interface of type usb is added. This rule looks like:

$ cat /lib/udev/rules.d/77-touch-net.rules
# Udev rules for letting adding a usb network interface to the bridge
ACTION=="add|change", SUBSYSTEM=="net", DRIVERS=="usb", KERNEL=="usb[0-9]*", \
ATTRS{idVendor}=="18d1", RUN+="/usr/local/bin/touch-bridge-addif"

In english this rule says, when a network device using a usb driver with a name starting with usb and a digit from vendor 18d1 (Google Inc.) is added or changed execute the script /usr/local/bin/touch-bridge-addif

The following script simply adds the network device to the bridge.

$ cat /usr/local/bin/touch-bridge-addif
#!/bin/sh -eu
TOUCH_BRIDGE=usbbr0
if ! ip link show $TOUCH_BRIDGE >/dev/null 2>&1; then
    echo "E: Bridge interface $TOUCH_BRIDGE is not ready!"
    exit 1
fi
# $INTERFACE is exported by udev, this script will fail if it is not defined
brctl addif $TOUCH_BRIDGE $INTERFACE

At this stage you can reboot the host system and verify that the bridge is up and dnsmasq is running.

Now, make sure you have a touch device booted and connected to the USB port of the PC.

Add the following entry to the file /etc/network/interfaces of the device

auto usb0
iface usb0 inet dhcp

(or rndis0 on Nexus 7/grouper)

Enable rndis:

setprop sys.usb.config rndis,adb

And the device usb0 will auto-configure and get an IP address from the DHCP running on the host.

We can confirm that everything is configured as expected by:

1. Checking that the device has acquired an IP address:

# adb -s $SERIAL shell ip addr show usb0
20: usb0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 22:81:9e:70:e0:fb brd ff:ff:ff:ff:ff:ff
inet 192.168.124.132/24 brd 192.168.124.255 scope global usb0

2. That this IP address is registered against the DHCP:

$ cat /var/lib/misc/dnsmasq.usbbr0.leases
1374758220 22:81:9e:70:e0:fb 192.168.124.132 ubuntu-phablet *

3. And the exchange with the DHCP is recorded in syslog:

Jul 25 13:00:32 white dnsmasq-dhcp[916]: DHCPOFFER(usbbr0) 192.168.124.132 22:81:9e:70:e0:fb
Jul 25 13:00:32 white dnsmasq-dhcp[916]: DHCPREQUEST(usbbr0) 192.168.124.132 22:81:9e:70:e0:fb
Jul 25 13:00:32 white dnsmasq-dhcp[916]: DHCPACK(usbbr0) 192.168.124.132 22:81:9e:70:e0:fb ubuntu-phablet
Jul 25 13:00:32 white dnsmasq-dhcp[916]: DHCPREQUEST(usbbr0) 192.168.124.132 22:81:9e:70:e0:fb
Jul 25 13:00:32 white dnsmasq-dhcp[916]: DHCPACK(usbbr0) 192.168.124.132 22:81:9e:70:e0:fb ubuntu-phablet

That’s all for today. Volunteer requested to make these steps more robust and package it into phablet-tools.

Internet over USB on Ubuntu Touch devices

When testing touch devices it is necessary to interfere as little as possible with the device under tests. This is the case for network connections on Touch devices (Nexus phones and tablets for example) where the main connection to internet is via Wifi, and even more true if you want to test Wifi itself.

This article describes how to share the network connection of the host system and provide Internet to the Touch devices over USB.

Make sure your device is booted and connected to your PC via the USB cable. Then activate the usb connection on your device and bring in up.

 $ adb shell sh -c "setprop sys.usb.config rndis,adb"
 $ adb kill-server && adb start-server

On the device, ‘ip link’ should show the interface

root@ubuntu-phablet:/# ip link show
[...]
19: usb0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000

On a Nexus 4 (mako) the interface is shown as usb0, while on a Nexus 7 (grouper) it is shown as rndis0.

Now, we have 1 connection to share and several devices connected to the PC, and we’ll want to bridge them together. Create a new bridge interface, and assign it an IP address:

$ sudo brctl addbr usbbr0
$ ifconfig usbbr0 192.168.124.1 netmask 255.255.255.0 up

This will be the end-point of the USB connection on the PC side, but not the end of the connection. All the trafic going through the bridge must be forwarded to the interface connected to internet on the PC. This is done with iptables on the host:

$ sudo iptables --table nat --append POSTROUTING --out-interface eth0 \
    -j MASQUERADE
$ sudo iptables --append FORWARD --in-interface usbbr0 -j ACCEPT
$ echo 1 >> sudo tee /proc/sys/net/ipv4/ip_forward

And finally we can glue eth0 and usb0 together by bringing up usb0 on the host and adding it to the bridge:

$ sudo ip link set usb0 up
$ sudo brctl addif usbbr0 usb0

We have connected all the wires:

  1. a usb network interface on the device,
  2. a usb network interface on the host,
  3. a bridge to share the physical interface of the host with USB network interfaces of several devices,
  4. iptables rules to forward trafic from the bridge to the physical network interface of the host

The last piece to complete the configuration is to assign an address to the network interface of the device. Remember that this setup is for testing, it means that several devices are connected to the same host, and we bring them up and down countless times a day. We’ll want a DHCP for IP address allocation and dnsmasq is a good candidate for this task. Start a dnsmasq instance that listen to the same network than the bridge interface:

$ sudo dnsmasq --strict-order --bind-interfaces \
    --pid-file=/var/run/touch/dnsmasq.pid --conf-file= \
    --listen-address 192.168.124.1 --dhcp-range 192.168.124.2,192.168.124.254 \
    --dhcp-lease-max=253 --dhcp-no-override --except-interface=lo \
    --interface=usbbr0 --dhcp-leasefile=/var/lib/misc/dnsmasq.usbbr0.leases \
    --dhcp-authoritative

(man dnsmasq for details on the command)

You should now be able to acquire an IP address on the device from the DHCP service running on the host:

$ adb shell dhclient usb0
19: usb0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 32:5b:92:84:fc:22 brd ff:ff:ff:ff:ff:ff
inet 192.168.124.3/24 brd 192.168.124.255 scope global usb0

We can verify than the connection is working fine:

1. Ping the device end point from the device:

$ adb shell ping  192.168.124.3
 PING 192.168.124.3 (192.168.124.3) 56(84) bytes of data.
 64 bytes from 192.168.124.3: icmp_seq=1 ttl=64 time=0.030 ms

2. Ping the host end point from the device:

$ adb shell ping  192.168.124.1
 PING 192.168.124.1 (192.168.124.1) 56(84) bytes of data.
 64 bytes from 192.168.124.1: icmp_seq=1 ttl=64 time=0.305 ms

3. Ping an external address by name:

$ adb shell ping www.ubuntu.com
 PING www.ubuntu.com (91.189.90.58) 56(84) bytes of data.
 64 bytes from www-ubuntu-com.avocado.canonical.com (91.189.90.58): icmp_seq=1 ttl=51 time=34.3 ms

And to verify that we are not cheating by using the wireless card:

$ adb shell ip addr show wlan0
 13: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN qlen 100

It’s down!

What next? To add another device to the bridge, activate the usb network interface on the device and the host, add this interface (they are be named usb1, …, usbN) to the bridge and run dhclient on the device.

Executing all these steps automatically on startup and when a new device is connected is left as an exercise :)

Gnome CI with JHBuild and Jenkins on Raring

During Quantal I started working on a Continuous Integration (CI) environment to run commit level testing of Gnome core components on the latest development release of Ubuntu.

This resulted in a Juju charm to make deployment easier which I used to setup a JHBuild enviromnent on Raring. The results of the builds are published on a jenkins instance .

Today was a major milestone, for the first time with the help of Martin Pitt, all the red dots are gone, leaving only blue/green (build successfully and make check pass) or yellow dots (module builds but make check failed) This is a great step forward \o/

The current system is fetching, building and checking of 160 packages on the latest development release of Ubuntu, here is how it works.

The testing environment is an LXC container with a minimal system to run Ubuntu Raring/AMD64. The host system is running on Ubuntu Server 12.10.

In the container, I installed JHBuild from git, system dependencies, a basic webserver to serve the results outside of the container (an alternative would have been to install a Jenkins slave inside the container, but I didn’t choose this option due to communication issues between the slave and the master) and a script run from cron to drive the builds and report the results.

Jenkins has the passive role of reporting the results.

The build/test workflow is the following:

  • Every 15min, the list of modules to build meta-gnome-core is fetched with jhbuild
  • The revision of the modules in the list is compared to the previous run. New packages are added automatically to Jenkins.
  • If a package or one of it’s dependency has changed, it is addded to the rebuild list
  • The rebuild list is then processed to first build the package with jhbuild, then re-run a jhbuild with option check to run a make check if the build succeeded. JHBuild is setup to run non-interactively of course, and to force a checkout and autogen on failure.
  • Logs and results are captured and published via the webserver.
  • Jenkins monitors the state files published by this webserver, updates the results and collects the log files.

The whole system could have been summarized by

while :; do jhbuild build; sleep 60; done

but I wanted a bit more granularity and control over what is being build and tested. Furthermore building the whole stack every single change in glib for example was not really efficient (WebKit tends to use a couple of CPU cycles to build ;))

So, overall it’s pretty simple, there are still reliability issues and some failures need manual interventions, but having kicked all the red dots is encouraging.

The next steps will be to add a notification system (and send messages the right people), detailed test reports (convert gtester results to jUnit XML), rely on jhbuild’s sysdeps instead of a yet another list of static dependencies, improve the performance of reverse dependencies checking, and …

… fix the yellow dots!

Yubikey and French keyboard layout on Ubuntu

If you use a Yubikey with a French keyboard layout and set the Yubikey to OATH-HOTP mode (i.e the key generates 6–8 digits number) then you’ll probably have experienced this disappointing behavior: the key sends non-numerical values that correspond to their numerical values on a french keyboard layout.

This is because the key sends keycodes corresponding to the digits row on the keyboard but digits, on a French layout, are shifted.

The workaround is trivial, hold the <SHIFT> key before touching the YubiKey to get the digits.

If you own a key with firmware 2.3 , you can use the option ‘-o use-numeric-keypad’ and the key will generate a numerical sequence, even with a French keyboard.

But there is no such option with firmware 2.2 and earlier. If you don’t want to burn passwords because you forgot to press the <SHIFT> key then you can configure your system to recognize the key and set it up to use a US keyboard.

The old school way is to configure the device manually in /etc/X11/xorg.conf and add an entry like:

Section "InputClass"
    Identifier "yubikey"
    MatchVendor "Yubico"
    MatchProduct "Yubico Yubikey II"
    Option "XkbModel" "pc101"
    Option "XkbLayout" "us"
    Option "XkbVariant" "intl"
EndSection

But desktop environments like Unity/Gnome or KDE override these settings and there is no UI way (at least for gnome) to configure several keyboards with different layouts. A solution is to use setxkbmap and a udev rule to reconfigure the keymap of the yubikey.

The script below set a US keymap, you can paste it to /usr/local/bin/setyubikeymap

#!/bin/sh

. /lib/udev/hotplug.functions

logger "YubiKey inserted - Setting keymap to US"
eval $(xargs -n 1 -0 < /proc/$(pgrep -f ^gnome-session)/environ| awk '/^DISPLAY|^XAUTHORITY/ {print "export", $0}')

exec 1<&- 2<&- 5<&- 7<&-
(
    wait_for_file /dev/input/by-id/usb-Yubico_Yubico_Yubikey_II-event-kbd
    exec /usr/bin/setxkbmap -device $(/usr/bin/xinput list --id-only 'Yubico Yubico Yubikey II') us
) &

Then add the following rule to /etc/udev/rules.d/99-yubikey to call the script when the key is inserted

# Udev rules for setting keymap of the Yubikey USB to US
ACTION=="add|change", SUBSYSTEM=="usb", \
ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0010", \
RUN+="/usr/local/bin/setyubikeymap"

The script can surely be improved and be more robust but you get the idea. Also the key sometimes loses its configuration on resume from suspend, which is probably fixable with a pm hook.

Hope this helps French keyboard users!

Building a Jenkins plugin on Ubuntu Quantal (12.10)

Today I was fixing the build-publisher plugin that is broken when a matrix configuration changes. So, I git cloned the plugin, installed maven (not maven2), and ran mvn package.

Compilation went fine then come the tests … FAIL!

Hm, it failed right at the start during test injection with the following trace:

-------------------------------------------------------------------------------
Test set: InjectedTest
-------------------------------------------------------------------------------
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.218 sec <<< FAILURE!
initializationError(InjectedTest) Time elapsed: 0.005 sec <<< ERROR!
java.lang.UnsatisfiedLinkError: com.sun.jna.Native.open(Ljava/lang/String;)J
at com.sun.jna.Native.open(Native Method)
at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:236)
at com.sun.jna.Library$Handler.(Library.java:140)
at com.sun.jna.Native.loadLibrary(Native.java:366)
at com.sun.jna.Native.loadLibrary(Native.java:351)
[...]

Not good.

What is that, missing environment variable, broken path, jdk7 on quantal, wrong library ???

After digging a bit around which jna lib was installed on my system, I decided to uninstall libjna-java and all the jenkins stack since it is a dependency of jenkins-common.

Bingo! mvn test passed.

Now, that’s a problem because it’d mean I cannot run jenkins and build jenkins plugin on the same system :/ Hopefully jna is in the maven repository, and we can tell him to use it instead of the version installed on the system. Just add the following to your pom.xml file:

<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>3.2.2</version>
</dependency>

If you want to use the latest version 3.4.0, run maven with the following option:

$ mvn -DargLine="-Djna.nosys=true" test

And that’s it, jenkins and the pluging development environment running hand in hand on Ubuntu Quantal! :)

I filed launchpad bug 1065253 to track this issue.

Commit level build and testing of Gnome on Ubuntu: Juju + JHBuild + Canonistack + Jenkins

I was working on automating commit level building and testing of the gnome stack, and one of the painful and boring task is to setup the development and testing environment. Especially when you know that developers will have to use the same environment to reproduce failures found with testing and the QA Lab has restricted access.

So I went the Juju way and the result is a JHBuild charm .

This charm can be set up for manual use of JHBuild or automated building and testing. It is currently deployed in the Ubuntu QA Lab to build and test the gnome-core moduleset (197 modules) on Quantal.

Deploying JHBuild is as easy as configuring the service in config.yaml to your needs, bootstrapping juju and deploying jhbuild. Depending on the number of modules you want to build, you’ll need a medium or bigger instance type. Which consists in the following instructions:

1. Bootstrap the environment and deploy the service

$ juju bootstrap --constraints "instance-type=m1.medium"
$ juju deploy --config gnome.yaml \
 --repository $HOME/ubuntu/qa/juju/charms \
 local:jhbuild jhbuild

2. after installation, connect to the unit with:

$ juju ssh <unit_or_machine>

3. And run your first build

$ jhbuild build

4. You want to organize a hacking party ;)

$ juju add-unit jhbuild -n 10

6. To setup the service for automated build and publish the results:

$ juju set jhbuild autobuild=True publish=True

Results will be accessible on `http://<unit_public_addr>/`

Please see the README for more details and deployment instructions.

I hope it’ll be useful for you as well.

A basic templating engine in bash

I’m writting a Juju charm to automate the deployment of jhbuild. This system can use different configuration files with sometimes very little difference between them. So, I wanted a basic templating system to manage these files that could run on a minimal system.
The very limited features I needed were file inclusion and variable substitution.

Basically what I wanted is, if I have the following 3 files:
file1.txt

A
${VAR1}
#include file2.txt
B

file2.txt

AA
#include file3.txt
${VAR2}
BB

file3.txt

AAA
BBB

The expected result with VAR1=’this is var1′ and VAR2=’this is var2′ is:

A
this is var1
AA
AAA
BBB
this is var2
BB
B

I went for bash and here is the result:

#!/bin/bash
merge_template () {
    # Merge template files and do variable substitution
    #
    # $1: Template file
    #
    # Supported directives:
    #   #include filename : Include filename and process it.
    #   ${variable}       : substituted by the value of the variable.
    #
    [ -z "$1" ] && return
    set -f
    while IFS='' read -r line; do
        if [[ "$line" =~ \#include\ (.*) ]]; then
            $FUNCNAME ${BASH_REMATCH[1]}
        else
            while [[ "$line" =~ (\$\{[a-zA-Z_][a-zA-Z_0-9]*\}) ]] ; do
                LHS="${BASH_REMATCH[1]}"
                RHS="$(eval echo "\"$LHS\"")"
                line="${line//$LHS/$RHS}"
            done
            printf "%s\n" "$line"
        fi
    done<$1
    set +f
}

merge_template $1

Save it to merge_templates and run it with:

VAR1="This is var1" VAR2="this is var2" ./merge_templates file2.txt

Et voilà

Follow

Get every new post delivered to your Inbox.