2

I am trying to test a Wi-Fi network and I need to have the possibility to open different windows of Chrome or Firefox with a specific network adapter (for example, I have two Wi-Fi network adapters – wifi1 and wifi2 – and I want to open a Google Chrome window with wifi1 and the other one with wifi2).

I checked out iptables but this is not exactly what I am looking for.

Any ideas are welcome.

1
2

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.

  1. Define useful variables. Adjust dev (device name) to your needs.

    netns=ns1
    dev=wlp2s0
    
  2. Make sure the namespace doesn't exist.

    ip netns del "$netns" 2>/dev/null
    
  3. Create a new network namespace.

    ip netns add "$netns"
    
  4. Bring the device down in the default namespace (not strictly required).

    ip link set dev "$dev" down
    
  5. 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"
      
  6. Confirm all the devices are where they should be.

    ip link show
    ip netns exec "$netns" ip link show
    # examine output
    
  7. 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
    
  8. 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.

  9. 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"
    
  10. Take care of DNS (see "possible problems" above).

  11. 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.

  • If wired:

    # from the elevated shell in our additional network namespace
    ip link set dev "$dev" netns 1
    
  • If wireless:

    # from the elevated shell in our additional network namespace
    iw phy "$phy" set netns 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.

  1. 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"
    
  2. Make sure the namespace doesn't exist.

    ip netns del "$netns" 2>/dev/null
    
  3. Create a new network namespace.

    ip netns add "$netns"
    
  4. Create a veth pair in the default network namespace.

    ip link add "$devdef" type veth peer name "$devns"
    
  5. Move one end to the other network namespace.

    ip link set "$devns" netns "$netns"
    
  6. Configure interfaces.

    ip addr add "$ipdef"/24 dev "$devdef"
    ip netns exec "$netns" ip addr add "$ipns"/24 dev "$devns"
    
  7. 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
    
  8. Make all external traffic leaving the namespace go through the veth link.

    ip netns exec "$netns" ip route add default via "$ipdef" dev "$devns"
    
  9. 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
    
  10. Check if it's possible to ping some external host from within the network namespace.

    ip netns exec "$netns" ping 8.8.8.8
    
  11. Take care of DNS (see "possible problems" above).

  12. 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.
0

There is no simple way to do this as web browsers work at a different layer to the OS stack, simply hooking in to the OS to handle the routing.

You may be able to set one (or both) browsers up in therr own VMs each with a different routing table taking advantage of the alternative connection.

If you have a gateway/remote system you can SSH to which is always available only through one connection you could configure one browser instance as a sox proxy (or set up a regular proxy).

It might be possible to set up policy based routing on your box, (non trivial) then use some identifier in iptables to tag packets (this is the hardest bit to work out- only way I can think to tag packets would be on owner, and have each browser launched by a different userr ) based and route packets based on tag.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.