To test a multipath application that I am currently working on, I need to setup my laptop with two active network interfaces. As always, I cooked a script to do the work, which uses NetworkManager to read the needed information and iproute2 to setup the routing table(s):
Let me explain how it works...
Since we are going to work with multiple routing tables, we need to tell the kernel when to use which table. This is done by setting up rules, but before doing this, the script calls the function flush_rule()
. Here, the list of rules is flushed with ip rule flush
and then two "standard" rules are added:
ip rule add from all lookup default prio $prio
ip rule add from all lookup main prio $prio
The $prio
variable is initialized to the very high value 32767 and decreased by one every time we add a rule. This ensures that our later rules with get higher priority than the standard rules.
Next, the script reads two arguments from the caller: $dev1
and $dev2
, which specifies which interfaces to use. For each interface, the script calls the function read_info()
to collect the needed information about the network interface. The function uses nmcli
to get information from NetworkManager, and then some grepping to read out the needed parts:
nminfo=$(nmcli device list iface $1)
iface=$(echo -e "$nminfo" | grep "GENERAL.IP-IFACE" | cut -f2 -d:)
addr=$(echo -e "$nminfo" | grep ip_address | cut -f2 -d=)
gw=$(echo -n "$nminfo" | grep " routers " | cut -f2 -d=)
net=$(echo -n "$nminfo" | grep network_number | cut -f2 -d=)
mask=$(echo -n "$nminfo" | grep " subnet_mask " | cut -f2 -d=)
cidr=$(oct2cidr $mask)
The second line might seem a bit weird, but NetworkManager uses a different name than the kernel for my internal 3g-modem (ttyACM0 vs. wwan0). This line tells me the kernel name of the interface. I found this approach the easiest way to collect the gateway address received by DHCP. Another way would have been to tell NetworkManager to use dhcpcd instead of dhclient (put dhcp=dhcpcd
in the [main] section of /etc/NetworkManager/NetworkManager.conf
), and then parse the lease-file from dhcpcd.
With the needed information available, we can setup the routing tables for the two interfaces. Before using the script, you must create these tables in the iproute2 configuration by adding these to lines to /etc/iproute2/rt_tables
:
2 t1
3 t2
The two integers are not line numbers. With these in place, the script can call the function setup_table()
to configure routes for the interface that we have just received information about with NetworkManager:
table=$1
ip route flush table $table
ip route del $net/$cidr
ip route add $net/$cidr dev $iface src $addr
ip route add $net/$cidr dev $iface src $addr table $table
ip route add default via $gw dev $iface table $table
ip rule add from $addr table $table prio $prio
ip route add 127.0.0.0/8 dev lo table $table
The function first flushes the table in case the script was previously used, and then add subnet routing entries for the interface, a default route to the new table and a rule to tell linux to use this table.
When both interface has been setup by first reading the information and then creating the table, the script can call the function setup_balance
to start using the two interface:
ip route del default
ip route add default scope global \
nexthop via $gw1 dev $if1 weight $3 \
nexthop via $gw2 dev $if2 weight $4
The two $gw
variables are created before this in the function to know the kernel names of the interfaces. The first call to ip
deletes the current default route, which is most likely from one the two interfaces. Then add a new default route is added. This specifies two "default" routes via the two interfaces and how they should be balanced. In some cases you just wanna use the two routes by calling bind (2)
on the socket before connection, and in this case you don't need this special default route.
So, with all this in place, we can run the script like this:
./multi_homed.sh wlan0 usb0
This makes your box use both your wifi and your phone (if using USB tethering in Android). In case you also have an internal 3g-modem, you can retrieve its NetworkManager name by calling nmcli
:
nmcli device
The next thing I need to do is to setup a box with MultiPath TCP (MPTCP), so that I can compare my multipath application against MPTCP.
Happy networking :)