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.