I am trying to set up a VPN (using OpenVPN) such that all of the traffic, and only the traffic, to/from specific processes goes through the VPN; other processes should continue to use the physical device directly.  It is my understanding that the way to do this in Linux is with network namespaces.If I use OpenVPN normally (i.e. funnelling all traffic from the client through the VPN), it works fine.  Specifically, I start OpenVPN like this:# openvpn --config destination.ovpn --auth-user-pass credentials.txt(A redacted version of destination.ovpn is at the end of this question.)I'm stuck on the next step, writing scripts that restrict the tunnel device to namespaces.  I have tried:Putting the tunnel device directly in the namespace with# ip netns add tns0# ip link set dev tun0 netns tns0# ip netns exec tns0 ( ... commands to bring up tun0 as usual ... )These commands execute successfully, but traffic generated inside the namespace (e.g. with ip netns exec tns0 traceroute -n 8.8.8.8) falls into a black hole.On the assumption that you can [still] only assign virtual Ethernet (veth) interfaces to a network namespace (which, if true, takes this year's award for most ridiculously unnecessary API restriction), creating a veth pair and a bridge, and putting one end of the veth pair in the namespace.  This doesn't even get as far as dropping traffic on the floor: it won't let me put the tunnel into the bridge!  [EDIT: This appears to be because only tap devices can be put into bridges.  Unlike the inability to put arbitrary devices into a network namespace, that actually makes sense, what with bridges being an Ethernet-layer concept; unfortunately, my VPN provider does not support OpenVPN in tap mode, so I need a workaround.]# ip addr add dev tun0 local 0.0.0.0/0 scope link# ip link set tun0 up# ip link add name teo0 type veth peer name tei0# ip link set teo0 up# brctl addbr tbr0# brctl addif tbr0 teo0# brctl addif tbr0 tun0can't add tun0 to bridge tbr0: Invalid argumentThe scripts at the end of this question are for the veth approach.  The scripts for the direct approach may be found in the edit history.  Variables in the scripts that appear to be used without setting them first are set in the environment by the openvpn program -- yes, it's sloppy and uses lowercase names.Please offer specific advice on how to get this to work.  I'm painfully aware that I'm programming by cargo cult here -- has anyone written comprehensive documentation for this stuff? I can't find any -- so general code review of the scripts is also appreciated.In case it matters:# uname -srvmLinux 3.14.5-x86_64-linode42 #1 SMP Thu Jun 5 15:22:13 EDT 2014 x86_64# openvpn --version | head -1OpenVPN 2.3.2 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [eurephia] [MH] [IPv6] built on Mar 17 2014# ip -Vip utility, iproute2-ss140804# brctl --versionbridge-utils, 1.5The kernel was built by my virtual hosting provider (Linode) and, although compiled with CONFIG_MODULES=y, has no actual modules -- the only CONFIG_* variable set to m according to /proc/config.gz was CONFIG_XEN_TMEM, and I do not actually have that module (the kernel is stored outside my filesystem; /lib/modules is empty, and /proc/modules indicates that it was not magically loaded somehow).  Excerpts from /proc/config.gz provided on request, but I don't want to paste the entire thing here.netns-up.sh#! /bin/shmask2cidr () {    local nbits dec    nbits=0    for dec in $(echo $1 | sed 's/\./ /g') ; do        case $dec in            (255) nbits=$(($nbits + 8)) ;;            (254) nbits=$(($nbits + 7)) ;;            (252) nbits=$(($nbits + 6)) ;;            (248) nbits=$(($nbits + 5)) ;;            (240) nbits=$(($nbits + 4)) ;;            (224) nbits=$(($nbits + 3)) ;;            (192) nbits=$(($nbits + 2)) ;;            (128) nbits=$(($nbits + 1)) ;;            (0)   ;;            (*) echo Error: $dec is not a valid netmask component >&2                exit 1                ;;        esac    done    echo $nbits}mask2network () {    local host mask h m result    host=$1.    mask=$2.    result=    while [ -n $host ]; do        h=${host%%.*}        m=${mask%%.*}        host=${host#*.}        mask=${mask#*.}        result=$result.$(($h & $m))    done    echo ${result#.}}maybe_config_dns () {    local n option servers    n=1    servers=    while [ $n -lt 100 ]; do       eval option=\$foreign_option_$n       [ -n $option ] || break       case $option in           (*DNS*)               set -- $option               servers=$serversnameserver $3               ;;           (*) ;;       esac       n=$(($n + 1))    done    if [ -n $servers ]; then        cat > /etc/netns/$tun_netns/resolv.conf <<EOF# name servers for $tun_netns$serversEOF    fi}config_inside_netns () {    local ifconfig_cidr ifconfig_network    ifconfig_cidr=$(mask2cidr $ifconfig_netmask)    ifconfig_network=$(mask2network $ifconfig_local $ifconfig_netmask)    ip link set dev lo up    ip addr add dev $tun_vethI \        local $ifconfig_local/$ifconfig_cidr \        broadcast $ifconfig_broadcast \        scope link    ip route add default via $route_vpn_gateway dev $tun_vethI    ip link set dev $tun_vethI mtu $tun_mtu up}PATH=/sbin:/bin:/usr/sbin:/usr/binexport PATHset -ex# For no good reason, we can't just put the tunnel device in the# subsidiary namespace; we have to create a virtual Ethernet# device pair, put one of its ends in the subsidiary namespace,# and put the other end in a bridge with the tunnel device.tun_tundv=$devtun_netns=tns${dev#tun}tun_bridg=tbr${dev#tun}tun_vethI=tei${dev#tun}tun_vethO=teo${dev#tun}case $tun_netns in     (tns[0-9] | tns[0-9][0-9] | tns[0-9][0-9][0-9]) ;;     (*) exit 1;;esacif [ $# -eq 1 ] && [ $1 = INSIDE_NETNS ]; then    [ $(ip netns identify $$) = $tun_netns ] || exit 1    config_inside_netnselse    trap rm -rf /etc/netns/$tun_netns ||:          ip netns del $tun_netns      ||:          ip link del $tun_vethO       ||:          ip link set $tun_tundv down  ||:          brctl delbr $tun_bridg       ||:          0    mkdir /etc/netns/$tun_netns    maybe_config_dns    ip addr add dev $tun_tundv local 0.0.0.0/0 scope link    ip link set $tun_tundv mtu $tun_mtu up    ip link add name $tun_vethO type veth peer name $tun_vethI    ip link set $tun_vethO mtu $tun_mtu up    brctl addbr $tun_bridg    brctl setfd $tun_bridg 0    #brctl sethello $tun_bridg 0    brctl stp $tun_bridg off    brctl addif $tun_bridg $tun_vethO    brctl addif $tun_bridg $tun_tundv    ip link set $tun_bridg up    ip netns add $tun_netns    ip link set dev $tun_vethI netns $tun_netns    ip netns exec $tun_netns $0 INSIDE_NETNS    trap  0finetns-down.sh#! /bin/shPATH=/sbin:/bin:/usr/sbin:/usr/binexport PATHset -extun_netns=tns${dev#tun}tun_bridg=tbr${dev#tun}case $tun_netns in     (tns[0-9] | tns[0-9][0-9] | tns[0-9][0-9][0-9]) ;;     (*) exit 1;;esac[ -d /etc/netns/$tun_netns ] || exit 1pids=$(ip netns pids $tun_netns)if [ -n $pids ]; then    kill $pids    sleep 5    pids=$(ip netns pids $tun_netns)    if [ -n $pids ]; then        kill -9 $pids    fifi# this automatically cleans up the the routes and the veth device pairip netns delete $tun_netnsrm -rf /etc/netns/$tun_netns# the bridge and the tunnel device must be torn down separatelyip link set $dev downbrctl delbr $tun_bridgdestination.ovpnclientauth-user-passping 5dev tunresolv-retry infinitenobindpersist-keypersist-tunns-cert-type serververb 3route-metric 1proto tcpping-exit 90remote [REDACTED]<ca>[REDACTED]</ca><cert>[REDACTED]</cert><key>[REDACTED]</key>
Feed all traffic through OpenVPN for a specific network namespace only
linux;vpn;openvpn;network namespaces
It turns out that you can put a tunnel interface into a network namespace.  My entire problem was down to a mistake in bringing up the interface:ip addr add dev $tun_tundv \    local $ifconfig_local/$ifconfig_cidr \    broadcast $ifconfig_broadcast \    scope linkThe problem is scope link, which I misunderstood as only affecting routing.  It causes the kernel to set the source address of all packets sent into the tunnel to 0.0.0.0; presumably the OpenVPN server would then discard them as invalid per RFC1122; even if it didn't, the destination would obviously be unable to reply. Everything worked correctly in the absence of network namespaces because openvpn's built-in network configuration script did not make this mistake.  And without scope link, my original script works as well.(How did I discover this, you ask?  By running strace on the openvpn process, set to hexdump everything it read from the tunnel descriptor, and then manually decoding the packet headers.)