General solution with network namespaces
It's possible with network namespaces. You can create an additional network namespace, configure its network device(s) and eventually run a browser there.
I assume you're not using namespaces at the moment (ip netns
prints nothing).
There are at least two approaches to make programs in a newly added network namespace use a certain network device:
- You can "move" the device from the defalut network namespace (where it belongs by default) to the added namespace where it will be available directly. See example 1 below.
- If you want or need the device to stay in the default network namespace (e.g. it is in use; or it's configured and you don't want to have to configure it again), you can create a veth pair, i.e. two virtual interfaces: one in the default network namespace, the other within the added namespace. This can work like a cable between a router and a device. You need proper configuration of IP addresses and routing in the added namespace, and NAT in the default namespace. See example 2 below.
Possible problems
- Network device belonging to a network namespace is not available for programs running outside of the namespace. This means your network manager (if any) in the default namespace probably won't help you with devices in the added namespace, unless you run it (also) there. For testing you may simply use
ip
, dhclient
, route
etc. in the added namespace and configure everything (semi-)manually.
DNS may be unavailable in the added network namespace. E.g. in my Kubuntu the /etc/resolv.conf
file reads:
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
# 127.0.0.53 is the systemd-resolved stub resolver.
# run "systemd-resolve --status" to see details about the actual nameservers.
nameserver 127.0.0.53
The added namespace has its own lo
device and I can bring it up. But this is a different interface than lo
in the default network namespace where the stub resolver is listening. The simplest solution for testing is to edit the file anyway and add a nameserver
entry that points to a DNS server outside of your machine; e.g. nameserver 8.8.8.8
or nameserver IP.of.your.router
. I add the entry at the end of the file. Processes in the added namespace cannot use 127.0.0.53
but they will use the other DNS server.
If your resolv.conf
already specifies server(s) that will be reachable from the added namespace then there's nothing to worry about.
Your browser, if you run it for the second time, will probably detect the already running instance, delegate the task to it and exit. Even if you run the second instance in a different network namespace, it will probably delegate the task to the old instance.
I use Vivaldi and the solution is to specify a non-default --user-data-dir
, so the user data directory is differs between browsers. E.g.:
vivaldi --user-data-dir=/tmp/
I believe the same option works for Chrome; I'm not sure about Firefox. The simplest solution may be to run Chrome once and Firefox once.
Example 1: using a device directly
Most commands need sudo
. For convenience you can run sudo su -
and work in an elevated shell.
Define useful variables. Adjust dev
(device name) to your needs.
netns=ns1
dev=wlp2s0
Make sure the namespace doesn't exist.
ip netns del "$netns" 2>/dev/null
Create a new network namespace.
ip netns add "$netns"
Bring the device down in the default namespace (not strictly required).
ip link set dev "$dev" down
Add it to the new namespace (it will disappear from the default namespace).
This is good for a wired interface:
ip link set dev "$dev" netns "$netns"
For wireless interface the above will most likely yield Invalid argument
. Then this approach:
You need to move the PHY
This will probably show you the right phy (expect phy0
or similar):
phy="$(basename "$(cd "/sys/class/net/$dev/phy80211" && pwd -P)")"
echo "$phy"
The actual command:
iw phy "$phy" set netns name "$netns"
Confirm all the devices are where they should be.
ip link show
ip netns exec "$netns" ip link show
# examine output
Bring the interface up. The namespace contains its own loopback device lo
. I bring it up as well because in general programs may want to rely on it.
ip netns exec "$netns" ip link set dev lo up
ip netns exec "$netns" ip link set dev "$dev" up
Note how these last commands used ip netns exec "$netns"
to run the actual commands (ip link …
) in the right namespace. We can do this every time. For convenience let's run a whole new elevated shell in the namespace:
# taking relevant variables to the new shell
export netns dev phy
# assuming the current shell is elevated
# this will replace it with a new elevated shell in the namespace
exec ip netns exec "$netns" su
Everything you invoke from this new shell will run in the network namespace. E.g. sole ip link show
should show you the desired device rather than devices belonging to the default namespace.
Configure the interface in the namespace. Use iw
(if wireless), ip
, route
, dhclient
and/or whatever is needed like you normally would. An example for wireless interface:
wpa_passphrase mySSID myPass | wpa_supplicant -i "$dev" -c /dev/stdin -B
dhclient "$dev"
Take care of DNS (see "possible problems" above).
Run a separate browser (see "possible problems" above) in the network namespace. You should probably run it as a regular user. Example:
# from the elevated shell in the network namespace
sudo -u regularuser vivaldi --user-data-dir=/tmp/
Example 1 continues: reverting changes
The changes made so far are temporary. In case of any trouble just reboot.
Without reboot you can just delete the network namespace with ip netns del "$netns"
. The device will appear in the default namespace when it's no longer being used in the deleted one. You will need to identify and kill processes that are using it (including the browser, the shell maybe, wpa_supplicant
and dhclient
maybe). See this: Interface missed after namespace removal. Useful command:
find /proc/ -name "$dev"
It's better to explicitly "return" the device to the default namespace before deleting the additional one. You will probably want to kill some processes (like dhclient
) anyway, otherwise they will continue running in vain. Find them and terminate even before you "return" the device. But if you miss few, "returning" the device before deleting the namespace will at least make the device appear in the default namespace. You won't end up with a missing device.
Note the default network namespace has no name, therefore I'm using PID of some process that belongs to it. I bet the init process belongs to the default network namespace, hence 1
.
Then delete the namespace:
ip netns del "$netns"
When the device appears in the default network namespace, your network manager (if any) will possibly kick in. Or you will be able to configure the device manually.
Example 2: veth pair
This example is based on Network namespaces by Diego Pino García and iptables - Target to route packet to specific interface. I admit I have only basic understanding of the latter, and since I had to adjust it, the solution may not be optimal.
Most commands need sudo
. For convenience you can run sudo su -
and work in an elevated shell.
Define useful variables. Adjust them to your needs.
# arbitrary mark
mark=11
netns="ns$mark"
dev=wlp2s0
gate=192.168.80.1
# gate is the gateway dev connects to, like AP, home router or so
# veth devices
devdef="v-eth-$netns"
devns="v-peer-$netns"
# and their addresses
ipdef="10.200.$mark.1"
ipns="10.200.$mark.2"
net="10.200.$mark.0"
Make sure the namespace doesn't exist.
ip netns del "$netns" 2>/dev/null
Create a new network namespace.
ip netns add "$netns"
Create a veth pair in the default network namespace.
ip link add "$devdef" type veth peer name "$devns"
Move one end to the other network namespace.
ip link set "$devns" netns "$netns"
Configure interfaces.
ip addr add "$ipdef"/24 dev "$devdef"
ip netns exec "$netns" ip addr add "$ipns"/24 dev "$devns"
Bring the interfaces up. The namespace contains its own loopback device lo. I bring it up as well because in general programs may want to rely on it.
ip netns exec "$netns" ip link set lo up
ip netns exec "$netns" ip link set "$devns" up
ip link set "$devdef" up
Make all external traffic leaving the namespace go through the veth link.
ip netns exec "$netns" ip route add default via "$ipdef" dev "$devns"
Enable IPv4 forwarding and enable masquerading.
echo 1 > /proc/sys/net/ipv4/ip_forward
The linked articles flush various rules. If you have some rules already defined (forwarding etc.) then think twice what you're doing now.
# think twice if you should run these
iptables -F FORWARD
iptables -P FORWARD DROP
iptables -t nat -F
iptables -t raw -F
ip route flush table "$mark"
You will need these:
iptables -t raw -A PREROUTING -i "$devdef" -j MARK --set-mark "$mark"
ip rule add fwmark "$mark" priority 1000 table "$mark"
ip route add default via "$gate" dev "$dev" table "$mark"
ip route flush cache
iptables -t nat -A POSTROUTING -s "$net"/255.255.255.0 -o "$dev" -j MASQUERADE
iptables -A FORWARD -i "$dev" -o "$devdef" -j ACCEPT
iptables -A FORWARD -o "$dev" -i "$devdef" -j ACCEPT
Check if it's possible to ping some external host from within the network namespace.
ip netns exec "$netns" ping 8.8.8.8
Take care of DNS (see "possible problems" above).
Run a separate browser (see "possible problems" above) in the network namespace. You should probably run it as a regular user. Example:
ip netns exec "$netns" sudo -u regularuser vivaldi --user-data-dir=/tmp/
Example 2 continues: reverting changes
The changes made so far are temporary. In case of any trouble just reboot.
If you have some rules (forwarding etc.) not related to the current subject then think twice what you're doing now.
# think twice if you should run these
iptables -F FORWARD
iptables -P FORWARD DROP
iptables -t nat -F
iptables -t raw -F
ip route flush table "$mark"
echo 0 > /proc/sys/net/ipv4/ip_forward
You can certainly run these:
ip rule del fwmark "$mark" priority 1000 table "$mark"
ip netns del "$netns"
But the question is about two interfaces! What now?
In the most fortunate case a browser started normally will use one of the interfaces in question for addresses you want to test. If so, you need to follow my answer only for the other interface, one way (example 1) or another (example 2).
Otherwise you need to follow my answer for each of the two interfaces. You can choose one way (example 1) or another (example 2) or both (example 1 for one interface, example 2 for the other). I would use two separate elevated shells, each with variables adjusted to the respective interface and the chosen method. These are hints:
- Set different
dev
variables (quite obvious).
- Set different
netns
variables.
- If a veth pair (example 2) for each interface then
- different
mark
; remember to adjust gate
(which may or may not be different); other variables depend on mark
, this will make them differ automatically;
- do not mindlessly flush settings while applying the solution for the second interface.
See: https://superuser.com/questions/241178/how-to-use-different-network-interfaces-for-different-processes or https://unix.stackexchange.com/questions/210982/bind-unix-program-to-specific-network-interface
– beeks – 2020-01-09T19:35:02.547