Managing multiple network profiles using upstart
One of the purposes of buying the Jetway mini-top HBJC600C99-352-BW was using it as wireless router. I installed Ubuntu 11.04 on it. As NetworkManager does not handle wireless interfaces in access point (AP) mode, I decided to design my own network management. The idea is simple: use the required daemons/application and create upstart jobs to handle the activation logic. These are the network profiles I want to support:
- WIFI: The htpc will connect to any known AP available.
- ADSL: The htpc will connect to Internet via ADSL and act as a wireless router.
- 3G: The htpc will connect to the Internet via 3G and act as a wireless router.
List of applications I need to use:
- upstart;
- wpa_supplicant;
- dhclient;
- brctl;
- ifconfig;
- hostapd;
- dhcpd;
- pppd;
- iptables;
- dnsmasq.
To install the needed packages, type the application name in the terminal. Ubuntu will help you with package’s name.
There are thousand ways to do that using upstart, so let me list some constraints I kept in mind when designing the system:
- Customized solution. I believe that generic solutions must be used only when you really need them. Every time you make something more generic you end up adding more logic, creating more corner cases and spending more time with use cases that you will NEVER face. Example of assumptions: my built-in ethernet port is eth0, my wireless interface wlan0 is NOT hotplug.
- Always have a maintenance channel available. Shit happens, all the time. So independent of the network profile I choose I want some way to get a secure shell connection without connect a keyboard (that I don’t have) to the htpc.
- One upstart job per long lived process. Among the applications listed above, some have short lifetime, i.e. they set some configuration and/or perform some action and finish. Others keep running until some orderly finish action. I decided that the formers don’t need to be a job, they can be run as pre or post scripts of other jobs.
1st step: Get rid of Ubuntu networking jobs.
I will manage my network stuff using only upstart jobs, so I don’t need network-manager and ifupdown utilities. So lets disable them:
root$ echo manual >> /etc/init/networking.conf root$ echo manual >> /etc/init/network-interfaces.conf root$ echo manual >> /etc/init/network-manager.conf root$ echo manual >> /etc/init/networking.conf root$ echo manual >> /etc/init/bridge-network-interface.conf
Now, we need a job to raise the loopback interface. The following job is completely based on the Ubuntu’s network-interface job:
/etc/init/loopback.conf
start on net-device-added INTERFACE=lo
stop on net-device-removed INTERFACE=lo
pre-start script
mkdir -p /var/run/network
ifconfig lo 127.0.0.1 up
initctl emit -n net-device-up IFACE=lo LOGICAL=lo
ADDRFAM=inet METHOD=loopback
end script
post-stop exec initctl emit -n net-device-down IFACE=lo LOGICAL=lo
ADDRFAM=inet METHOD=loopback
As upstart will run and take care of all daemons listed above, we must disable the sysinit scripts that start/stop them (that ones in /etc/init.d).
root$ update-rc.d -f hostapd remove root$ update-rc.d -f isc-dhcp-server remove root$ update-rc.d -f dnsmasq remove
2nd step: create mutually exclusive jobs to set the network profile.
/etc/init/net-profile-adsl.conf
stop on (starting net-profile-adsl
or starting net-profile-wifi)
# the 'start on' below means "i'm the default profile"
start on (local-filesystems
and net-device-added INTERFACE=wlan0
and net-device-added INTERFACE=eth0)
etc/init/net-profile-3g.conf
stop on (starting net-profile-3g
or starting net-profile-wifi)
/etc/init/net-profile-wifi.conf
stop on (starting net-profile-3g
or starting net-profile-adsl)
The main ideia of this arrangement is to change the profile using a single command. For instance, if somehow my adsl connection become unavailable I just need to connect my cell phone to the htpc and run the following command:
root$ start net-profile-3g
To reach this goal, all the following jobs will depend on this set of “fake” jobs as you can see in the next steps.
3rd step: set-up the bridge
First, the question. Why do I need a bridge? It’s because I have a printer connected to the htpc via ethernet. As I only have one built-in ethernet port I bought a usb-ethernet adapter (eth1). So, every time the htpc is running the 3g or adsl profile, I want wlan0 and eth1 in a bridge to route the connection to them. I can even say that everytime I plug-in the usb-ethernet adapter, I want dhcpd sending valid IP addresses to it (do you remember that I want a ssh access anytime?). The solution that I chose:
- The bridge (br0) will be set up at startup time and dhcpd will be configured to manage this interface
- Whenever the usb-ethernet is plugged in, its interface will be added to the bridge
- Every time ADSL or 3G profiles are chosen, wlan0 will also be added to the bridge
/etc/init/bridge.conf
start on started loopback
stop on stopping loopback
pre-start script
brctl addbr br0
ifconfig br0 10.0.1.1 netmask 255.255.255.0 broadcast 10.0.1.255
end script
post-stop script
ifconfig br0 down
brctl delbr br0
end script
/etc/init/usb-ethernet.conf
start on net-device-added INTERFACE=eth1
stop on net-device-removed INTERFACE=eth1
pre-start script
ifconfig eth1 up
brctl addif br0 eth1
end script
post-stop exec brctl delif br0 eth1
/etc/init/dhcpd.conf
start on started bridge stop on stopping bridge exec dhcpd -f
Note that the configurations above do not show when the wlan0 interface is added to the bridge. It will be covered in the next steps. Do not forget to change /etc/default/isc-dhcp-server in order to make dhcpd manage the br0 interface. Also, you need a valid configuration in the /etc/dhcp/dhcpd.conf. I will show my config below:
ddns-update-style none;
option domain-name-servers 10.0.1.1;
default-lease-time 600;
max-lease-time 7200;
authoritative;
log-facility local7;
subnet 10.0.1.0 netmask 255.255.255.0 {
range 10.0.1.10 10.0.1.254;
option routers 10.0.1.1;
}
4th step: managing wlan0 when the WIFI profile is chosen
Basically I need wpa_supplicant and dhclient running on wlan0 interface. Also, I need to keep wlan0 out of the bridge. Dumping the config:
/etc/init/wpa_supplicant.conf
start on started net-profile-wifi stop on stopping net-profile-wifi exec wpa_supplicant -iwlan0 -Dnl80211 -c/etc/wpa_supplicant.conf
/etc/init/dhclient.conf
start on started wpa_supplicant
stop on stopping wpa_supplicant
exec dhclient -d wlan0
emits net-device-up
emits net-device-down
post-start exec initctl emit -n net-device-up IFACE=wlan0 LOGICAL=wlan0
ADDRFAM=inet METHOD=dhcp
pre-stop script
ifconfig wlan0 0.0.0.0
initctl emit -n net-device-down IFACE=wlan0 LOGICAL=wlan0
ADDRFAM=inet METHOD=dhcp
end script
Note that you need a valid /etc/wpa_supplicant.conf file in order to be able to connect to a known AP. I don’t think I need to paste my config here. If you have any doubts run ‘man wpa_supplicant.conf’.
5th step: managing wlan0 when either 3G or ADSL profiles are chosen
I only need to launch hostpad and to add wlan0 to the bridge (br0). The job configuration:
/etc/init/hostapd.conf
start on (started net-profile-3g
or started net-profile-adsl)
stop on (stopping net-profile-3g
or stopping net-profile-adsl)
exec hostapd /etc/hostapd.conf
post-start script
while [ "`iw wlan0 info | grep type | cut -d ' ' -f 2`" != "AP" ]; do
sleep 0.5
done
brctl addif br0 wlan0
end script
pre-stop exec brctl delif br0 wlan0
Note how wlan0 is added to the bridge. The current solution is a ugly hack that I’m planning to improve further… The problem is that a wireless interface in ‘managed mode’ cannot be added to a bridge. So when I move from WIFI profile (the wlan0 is in managed mode) to 3G or ADSL profile (wlan0 is in master mode), the brctl addif command fails. The reason is that hostapd daemon does not have enough time to set the master mode before its own post-start script runs.
Lets remind you again about valid configuration files… My wireless device uses ath9k kernel driver and below I’m pasting my hostapd.conf
/etc/hostapd.conf
interface=wlan0 bridge=br0 driver=nl80211 ssid=myssid hw_mode=g channel=1 wme_enabled=1 ieee80211n=1 ht_capab=[HT40+][SHORT-GI-40][TX-STBC][RX-STBC1][DSSS_CCK-40] macaddr_acl=0 auth_algs=1 ignore_broadcast_ssid=0 wpa=2 wpa_passphrase=come_on wpa_key_mgmt=WPA-PSK wpa_pairwise=TKIP rsn_pairwise=CCMP
6th step: managing ppp0 interface when either ADSL or 3G profiles are chosen
I only need to call pppd with the desired profile and create a NAT using iptables. There are bare differences between 3g and adsl pppd jobs as you can notice below:
/etc/init/pppd-gvt.conf
start on started net-profile-adsl
stop on stopping net-profile-adsl
emits net-device-up
emits net-device-down
pre-start exec ifconfig eth0 0.0.0.0 up
exec pppd call gvt
post-start script
iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -o ppp0 -j MASQUERADE
iptables -A FORWARD -s 10.0.1.0/24 -o ppp0 -j ACCEPT
iptables -A FORWARD -d 10.0.1.0/24 -m conntrack
--cstate ESTABLISHED,RELATED -i ppp0 -j ACCEPT
initctl emit -n net-device-up IFACE=ppp0 LOGICAL=ppp0
ADDRFAM=inet METHOD=ppp
end script
pre-stop script
iptables -F
initctl emit -n net-device-down IFACE=ppp0 LOGICAL=ppp0
ADDRFAM=inet METHOD=ppp
end script
pre-start exec ifconfig eth0 down
/etc/init/pppd-vivo3g.conf
start on started net-profile-3g
stop on stopping net-profile-3g
emits net-device-up
emits net-device-down
exec pppd call vivo3g
post-start script
iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -o ppp0 -j MASQUERADE
iptables -A FORWARD -s 10.0.1.0/24 -o ppp0 -j ACCEPT
iptables -A FORWARD -d 10.0.1.0/24 -m conntrack
--cstate ESTABLISHED,RELATED -i ppp0 -j ACCEPT
initctl emit -n net-device-up IFACE=ppp0 LOGICAL=ppp0
ADDRFAM=inet METHOD=ppp
end script
pre-stop script
iptables -F
initctl emit -n net-device-down IFACE=ppp0 LOGICAL=ppp0
ADDRFAM=inet METHOD=ppp
end script
Important notes:
- My adsl modem is in bridge mode and connected to eth0 port.
- GVT is my adsl provider name and Vivo3G is my 3G operator name
Configs to adsl connection:
/etc/ppp/peers/gvt
noipdefault defaultroute hide-password noauth usepeerdns user "turbonet@turbonet" plugin rp-pppoe.so eth0 remotename gvt ipparam gvt
Add the following line to /etc/ppp/pap-secrets
"turbonet@turbonet" gvt "gvt25"
Configs to 3G connection:
/etc/ppp/peers/vivo3g
hide-password noauth connect "/usr/sbin/chat -v -f /etc/chatscripts/vivo3g" /dev/ttyACM0 115200 defaultroute noipdefault user "vivo" remotename vivo3g ipparam vivo3g usepeerdns
/etc/chatscripts/vivo3g
ABORT BUSY ABORT 'NO CARRIER' ABORT VOICE ABORT 'NO DIALTONE' ABORT 'NO DIAL TONE' ABORT 'NO ANSWER' ABORT DELAYED '' ATZ OK AT+CGDCONT=1,"IP","zap.vivo.com.br" OK "ATDT*99#" CONNECT ''
Add the following line to /etc/ppp/pap-secrets
"vivo" vivo3g "vivo"
7th (and last) step: Setup local dns server
To configure the htpc to run as a name server, I’m using dnsmasq:
/etc/init/dnsmasq.conf
start on started loopback stop on stopping loopback exec dnsmasq -k -u dnsmasq -7 /etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new
/etc/dnsmasq.conf
interface=br0 no-dhcp-interface=br0
If you want to use dnsmasq locally, add this line to your /etc/dhcp/dhclient.conf
prepend domain-name-servers 127.0.0.1;
That’s all. This setup is not fully tested (stressed). If you read/applied this and have some suggestions, please reply here

