Available for day contractsFrom 21st September I have availability for day and half day contracts. Please contact for more information.

Contact →
mikepreston.org

Linux Network Tools

Inspecting and configuring Linux host networking with iproute2 (ip, ss), tc, and friends.

Linux Network Tools

Inspecting and configuring Linux host networking with iproute2 (ip, ss), tc, and friends.

Overview

iproute2 is the modern, kernel-aligned toolkit for Linux host networking. It talks to the kernel over the rtnetlink socket and exposes a single, consistent verb structure that has long since replaced the venerable net-tools suite (ifconfig, route, netstat, arp, brctl). On most current distributions net-tools is not even installed by default; reach for ip and ss instead.

The ip command follows an ip OBJECT COMMAND grammar — pick an object (link, addr, route, neigh, rule, netns, …) then a verb (show, add, del, set, flush). ss ("socket statistics") is the netstat replacement for inspecting open sockets, and tc ("traffic control") manages queueing, shaping, and link emulation.

ip OBJECT COMMAND modeliplink(L2 devices)addr(L3 addresses)route(forwarding table)rule(policy routing)neigh(ARP / NDP)netns(namespaces)show | add | del |setip OBJECT COMMAND modeliplink(L2 devices)addr(L3 addresses)route(forwarding table)rule(policy routing)neigh(ARP / NDP)netns(namespaces)show | add | del |set

Most ip subcommands accept a common set of output modifiers:

Modifier Effect
-br, -brief Tabular one-line-per-object output, ideal for scanning
-c, -color Colourise output (use -c=auto for pipe-safe behaviour)
-j, -json JSON output — pipe to jq for scripting
-p, -pretty Pretty-print (pairs well with -j)
-s, -stats Show statistics (repeat -s -s for more detail)
-d, -details Show low-level detail (e.g. device type parameters)
-4 / -6 Restrict to IPv4 / IPv6
# Brisk, scannable interface summary
ip -br -c addr

# JSON straight into jq — extract every interface name
ip -j link show | jq -r '.[].ifname'

# Per-interface counters
ip -s link show eth0

Old → New Command Mapping

net-tools commands still appear in countless blog posts and scripts. Learn the iproute2 (and ss) equivalent — they are faster, IPv6-complete, and netlink-native. The legacy forms below are shown only for translation; never write new scripts against them.

Task Legacy (net-tools) Modern (iproute2 / ss)
Show / set addresses ifconfig ip addr / ip link
Bring interface up/down ifconfig eth0 up ip link set eth0 up
Show / add routes route -n ip route
Default gateway route add default gw … ip route add default via …
Socket / listener list netstat -tulpn ss -tulpn
Routing-table dump netstat -rn ip route
Interface stats netstat -i ip -s link
ARP / neighbour table arp -n ip neigh
Bridge management brctl ip link + bridge
Tunnels iptunnel ip tunnel
Multicast addresses ipmaddr ip maddr
Rename by MAC nameif ip link set … name …
Interface MAC change ifconfig hw ether … ip link set … address …

ip link — Layer 2 Devices

ip link manages network devices at the data-link layer: their state, MTU, MAC address, flags, and naming. It does not touch IP addressing — that is ip addr.

Key Concepts

  • A link is any L2 netdev: physical NIC, VLAN, bridge, bond, veth, tunnel, or virtual device.
  • Administrative state (UP/DOWN) is what you set; operational state (LOWER_UP = carrier present) is reported by the kernel.
  • The master/slave relationship attaches an interface to a bridge or bond.

Common Commands

# Show all links (add -br for one line each)
ip link show
ip -br link

# Show a single device
ip link show eth0

# Bring an interface up / down
ip link set eth0 up
ip link set eth0 down

# Set MTU
ip link set eth0 mtu 9000

# Change MAC address (interface should be down first)
ip link set eth0 down
ip link set eth0 address 02:11:22:33:44:55
ip link set eth0 up

# Enable / disable promiscuous mode
ip link set eth0 promisc on
ip link set eth0 promisc off

# Add a human-readable alias (cosmetic label)
ip link set eth0 alias "uplink to core switch"

# Rename an interface (must be down)
ip link set eth0 down
ip link set eth0 name wan0

# Per-interface statistics (repeat -s for extended counters)
ip -s link show eth0
ip -s -s link show eth0

Bonding and Teaming

Bonds aggregate links for redundancy or throughput. ip link creates the bond device; the slaves are enslaved with master. (The older teamd-based teaming offers similar function via a userspace daemon, but kernel bonding is the default choice on modern systems.)

# Create an 802.3ad (LACP) bond
ip link add bond0 type bond mode 802.3ad

# Enslave interfaces (they must be down)
ip link set eth0 down && ip link set eth0 master bond0
ip link set eth1 down && ip link set eth1 master bond0

ip link set bond0 up

# Inspect bond detail
ip -d link show bond0
cat /proc/net/bonding/bond0   # mode, active slave, link state per slave

ip addr — Layer 3 Addresses

ip addr (alias ip a) assigns and inspects IP addresses on links. A single interface can carry many addresses across both families — there is no concept of a "secondary" alias interface (eth0:0) any more; you simply add more addresses.

Common Commands

# Show all addresses (brief form is excellent here)
ip addr show
ip -br addr

# Show one interface
ip addr show dev eth0

# Add an address with prefix length
ip addr add 192.168.1.10/24 dev eth0

# Add a second (additional) address — no alias interface needed
ip addr add 192.168.1.11/24 dev eth0

# Let the kernel derive the broadcast address (brd +)
ip addr add 10.0.0.5/24 brd + dev eth0

# Add an IPv6 address
ip addr add 2001:db8::5/64 dev eth0

# Label an address (appears as eth0:web in legacy tools)
ip addr add 192.168.1.20/24 dev eth0 label eth0:web

# Set address scope explicitly
ip addr add 169.254.0.1/16 dev eth0 scope link

# Delete a specific address
ip addr del 192.168.1.11/24 dev eth0

# Flush all addresses from an interface
ip addr flush dev eth0

# Flush only IPv6, or only a given scope
ip -6 addr flush dev eth0
ip addr flush dev eth0 scope global

Address Scopes

Scope Meaning
global Routable anywhere (default for normal addresses)
link Valid only on this link (e.g. IPv6 link-local, APIPA)
host Valid only on this host (loopback 127.0.0.1)

ip route — The Forwarding Table

ip route shows and edits the kernel routing table. Crucially, ip route get resolves a destination the way the kernel actually would — it is the single most useful routing diagnostic.

Common Commands

# Show the main routing table
ip route
ip route show

# THE diagnostic: which route, interface, and source would the kernel pick?
ip route get 8.8.8.8
ip route get 8.8.8.8 from 192.168.1.10   # test a specific source

# Default route via a gateway
ip route add default via 192.168.1.1

# Static route to a network via a gateway
ip route add 10.0.0.0/8 via 192.168.1.254

# Route bound to a device (link-scope, no gateway)
ip route add 10.10.0.0/24 dev eth1

# Set a metric (lower wins when destinations tie)
ip route add default via 192.168.1.1 metric 100
ip route add default via 192.168.2.1 metric 200   # backup

# Replace (idempotent — add-or-update without RTNETLINK errors)
ip route replace default via 192.168.1.1

# Drop traffic for a prefix
ip route add blackhole 203.0.113.0/24      # silently discard
ip route add unreachable 198.51.100.0/24   # reply ICMP unreachable
ip route add prohibit 192.0.2.0/24         # reply ICMP admin-prohibited

# Delete a route
ip route del 10.0.0.0/8

# Flush the cache (rarely needed on modern kernels)
ip route flush cache

Reading a Route Line

default via 192.168.1.1 dev eth0 proto dhcp src 192.168.1.10 metric 100
  • via — next hop gateway
  • dev — egress interface
  • proto — who installed it (kernel, static, dhcp, bird, …)
  • src — preferred source address for traffic on this route
  • metric — priority among equal-specificity routes

Policy Routing — ip rule

By default the kernel consults a single main table. Policy routing lets you choose a routing table based on attributes of the packet — source address, firewall mark, inbound interface — via a prioritised rule list. The classic use is source-based routing: traffic from a given address must leave by a given uplink.

Key Concepts

  • Rules live in an ordered list (lower pref/priority is consulted first).
  • Each rule selects a routing table; tables are numbered, with friendly names in /etc/iproute2/rt_tables.
  • Three tables are predefined: local (255), main (254), default (253).
from 10.0.0.0/24fwmark 0x1no matchOutgoing packetip rule listby prioritylookup table isp1lookup table vpnlookup mainRoute decisionfrom 10.0.0.0/24fwmark 0x1no matchOutgoing packetip rule listby prioritylookup table isp1lookup table vpnlookup mainRoute decision

Commands

# Show the rule list
ip rule show

# Register a named table (edit /etc/iproute2/rt_tables)
echo "100 isp1" >> /etc/iproute2/rt_tables

# Populate that table with its own default route
ip route add default via 203.0.113.1 dev eth1 table isp1

# Route by source address into that table
ip rule add from 192.168.10.0/24 table isp1 pref 1000

# Route by destination, firewall mark, or inbound interface
ip rule add to 10.50.0.0/16 table isp1
ip rule add fwmark 0x1 table vpn
ip rule add iif eth2 table isp1

# Inspect a specific table
ip route show table isp1

# Delete a rule (match its selector)
ip rule del from 192.168.10.0/24 table isp1

Worked Example — Source-Based Routing for Two Uplinks

# Two ISPs: eth1 (203.0.113.0/24, gw .1), eth2 (198.51.100.0/24, gw .1)
echo "1 isp1" >> /etc/iproute2/rt_tables
echo "2 isp2" >> /etc/iproute2/rt_tables

# Each table gets the matching uplink's default route and local subnet
ip route add 203.0.113.0/24 dev eth1 src 203.0.113.10 table isp1
ip route add default via 203.0.113.1 table isp1

ip route add 198.51.100.0/24 dev eth2 src 198.51.100.10 table isp2
ip route add default via 198.51.100.1 table isp2

# Reply on the same uplink the request arrived on
ip rule add from 203.0.113.10 table isp1
ip rule add from 198.51.100.10 table isp2

ip neigh — ARP and NDP

ip neigh manages the neighbour table: IPv4 ARP and IPv6 Neighbour Discovery entries mapping L3 addresses to MAC addresses.

# Show the neighbour table
ip neigh show
ip -br neigh
ip neigh show dev eth0

# Add a permanent (static) entry
ip neigh add 192.168.1.50 lladdr 00:11:22:33:44:55 dev eth0 nud permanent

# Replace / update an entry
ip neigh replace 192.168.1.50 lladdr 00:11:22:33:44:66 dev eth0

# Delete an entry
ip neigh del 192.168.1.50 dev eth0

# Flush learned entries on an interface
ip neigh flush dev eth0

Neighbour States (NUD)

State Meaning
REACHABLE Confirmed reachable recently
STALE Known but not recently confirmed; valid until used
DELAY / PROBE Reachability being re-confirmed
FAILED Resolution failed (no reply to probes)
PERMANENT Static entry, never expires
INCOMPLETE First resolution in progress

A flapping or FAILED entry for a host that should be reachable usually points at a duplicate IP, a switching problem, or a downed peer.

VLANs, Bridges, and Bonds

iproute2 creates virtual L2 topology directly. The bridge command (a sibling tool) handles the forwarding database (fdb) and VLAN filtering that ip does not.

VLANs

# 802.1Q VLAN sub-interface (tag 100) on eth0
ip link add link eth0 name eth0.100 type vlan id 100
ip addr add 192.168.100.1/24 dev eth0.100
ip link set eth0.100 up

Bridges

# Create a bridge and attach ports
ip link add br0 type bridge
ip link set eth0 master br0
ip link set eth1 master br0
ip link set br0 up

# Detach a port
ip link set eth0 nomaster

# Inspect the forwarding database and bridge ports
bridge link show
bridge fdb show
bridge fdb show br br0

# VLAN filtering on a bridge (802.1Q-aware bridge)
ip link add br0 type bridge vlan_filtering 1
bridge vlan add dev eth0 vid 100 pvid untagged
bridge vlan show

Bonds and veth

# Bond (see "Bonding and Teaming" above for enslaving)
ip link add bond0 type bond mode active-backup

# veth pair — a virtual patch cable, two ends that pass frames between them
ip link add veth0 type veth peer name veth1
ip link set veth0 up
ip link set veth1 up

Network Namespaces

A network namespace is an isolated copy of the network stack: its own interfaces, routing tables, neighbour tables, and sockets. They underpin containers and are invaluable for testing topology on a single host. A veth pair acts as a virtual cable: put one end in a namespace and the other on the host (or in a bridge) to wire namespaces together.

netns: bluenetns: redHost (root netns)veth pairveth pairbr0bridgeveth-aveth-bveth-a-peer10.0.0.1/24veth-b-peer10.0.0.2/24netns: bluenetns: redHost (root netns)veth pairveth pairbr0bridgeveth-aveth-bveth-a-peer10.0.0.1/24veth-b-peer10.0.0.2/24

Basic Operations

# Create / list / delete namespaces
ip netns add red
ip netns list
ip netns del red

# Run a command inside a namespace
ip netns exec red ip addr
ip netns exec red ping -c1 10.0.0.2
ip netns exec red bash    # interactive shell in the namespace

# Move an existing interface into a namespace
ip link set eth1 netns red

# Each namespace gets its own loopback — bring it up
ip netns exec red ip link set lo up

Worked Example — Two Namespaces via a Bridge

This wires two namespaces (red, blue) onto a shared bridge in the root namespace so they can reach each other on 10.0.0.0/24.

# 1. Namespaces
ip netns add red
ip netns add blue

# 2. Host bridge
ip link add br0 type bridge
ip link set br0 up

# 3. A veth pair per namespace: host end -> bridge, far end -> netns
ip link add veth-red type veth peer name eth0-red
ip link add veth-blue type veth peer name eth0-blue

# 4. Host ends onto the bridge, brought up
ip link set veth-red master br0 && ip link set veth-red up
ip link set veth-blue master br0 && ip link set veth-blue up

# 5. Far ends into the namespaces
ip link set eth0-red netns red
ip link set eth0-blue netns blue

# 6. Configure inside each namespace
ip netns exec red ip addr add 10.0.0.1/24 dev eth0-red
ip netns exec red ip link set eth0-red up
ip netns exec red ip link set lo up

ip netns exec blue ip addr add 10.0.0.2/24 dev eth0-blue
ip netns exec blue ip link set eth0-blue up
ip netns exec blue ip link set lo up

# 7. Test connectivity across the bridge
ip netns exec red ping -c2 10.0.0.2

For a quick point-to-point link with no bridge, drop the bridge and simply put one end of a single veth pair in each namespace:

ip link add red0 type veth peer name blue0
ip link set red0 netns red
ip link set blue0 netns blue
ip netns exec red  ip addr add 10.1.1.1/30 dev red0
ip netns exec blue ip addr add 10.1.1.2/30 dev blue0
ip netns exec red  ip link set red0 up
ip netns exec blue ip link set blue0 up

ss — Socket Statistics

ss replaces netstat for examining open sockets. It reads directly from kernel netlink, so it is dramatically faster than netstat on busy hosts.

The Canonical Invocation

# "What is listening, and which process owns it?"
# -t TCP  -u UDP  -l listening  -p process  -n numeric (no DNS/port lookup)
ss -tulpn

Common Patterns

# All TCP sockets, numeric
ss -tn

# Listening sockets only / established only
ss -tln
ss -tn state listening
ss -tn state established

# UDP and Unix-domain sockets
ss -u
ss -x

# With owning process (needs privilege — see Common Issues)
ss -tp

# Summary of socket counts by type/state
ss -s

# Socket memory usage
ss -tm

# TCP internals: cwnd, rtt, retransmits — congestion debugging
ss -ti

# Timer information (retransmit / keepalive countdowns)
ss -to

Address and Port Filters

ss has its own expressive filter language. Quote expressions containing parentheses or spaces.

# By destination / source port
ss -tn dport = :443
ss -tn sport = :22

# By destination address
ss -tn dst 10.0.0.0/8
ss -tn dst 192.168.1.100

# Compound expressions: keep the `state` keyword OUTSIDE the quoted
# group — folding it into the quotes errors with "an inet prefix is expected".
ss -tn state established '( dport = :443 or dport = :80 )'

# Everything talking to a host on HTTPS
ss -tnp 'dst 203.0.113.10 and dport = :443'

# Combine state filter with port filter
ss -tn state established '( sport = :22 )'

netstat → ss Translation

Goal netstat ss
All listening TCP/UDP + PID netstat -tulpn ss -tulpn
All TCP connections netstat -ant ss -tan
Established connections netstat -ant | grep ESTAB ss -tn state established
Unix sockets netstat -x ss -x
Summary statistics netstat -s ss -s
Per-socket process netstat -p ss -p

tc — Traffic Control (Primer)

tc shapes, schedules, and emulates link behaviour through queueing disciplines (qdiscs) attached to an interface's egress (and, with an ingress qdisc, its inbound path). This is a deep, easy-to-misconfigure subsystem — the notes below are a primer. Always test on a non-production link, and clear with tc qdisc del dev <dev> root when done.

Key Concepts

  • qdisc — the algorithm deciding how packets are queued and dequeued (the egress scheduler).
  • classful vs classless — classful qdiscs (HTB) contain classes you can shape independently; classless ones (fq_codel, cake, netem) do not.
  • root vs ingress — shaping is natural on egress; inbound "policing" is cruder and usually done with an ingress qdisc or redirected to an IFB device.
# Show the qdisc(s) on an interface
tc qdisc show dev eth0
tc -s qdisc show dev eth0   # with statistics (drops, backlog)

Sane Defaults — fq_codel / cake

# fq_codel: modern default, fights bufferbloat with fair queueing + CoDel
tc qdisc replace dev eth0 root fq_codel

# cake: smarter still; bandwidth-aware shaping in one qdisc
tc qdisc replace dev eth0 root cake bandwidth 100mbit

netem — Emulating a Bad Network

netem injects latency, jitter, loss, duplication, and reordering — invaluable for testing application resilience.

# 100ms latency with 10ms jitter, plus 1% random loss
tc qdisc add dev eth0 root netem delay 100ms 10ms loss 1%

# 200ms delay, correlated jitter, packet reordering
tc qdisc add dev eth0 root netem delay 200ms 20ms 25% reorder 5%

# 0.5% duplication and 0.1% corruption
tc qdisc add dev eth0 root netem duplicate 0.5% corrupt 0.1%

# Remove it (ALWAYS clean up)
tc qdisc del dev eth0 root

To emulate impairment only toward a specific destination, hang netem off an HTB class and steer matching traffic into it with a filter.

HTB Shaping Skeleton

Hierarchical Token Bucket carves an interface's bandwidth into classes with guaranteed rates and ceilings.

# 1. Root qdisc with a default class (1:30)
tc qdisc add dev eth0 root handle 1: htb default 30

# 2. Parent class capping total egress at 100mbit
tc class add dev eth0 parent 1: classid 1:1 htb rate 100mbit

# 3. Child classes: guaranteed rate, may borrow up to ceil
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 60mbit ceil 100mbit  # priority
tc class add dev eth0 parent 1:1 classid 1:30 htb rate 10mbit ceil 100mbit  # default

# 4. Classify: send port 22 traffic to the priority class
tc filter add dev eth0 protocol ip parent 1: prio 1 \
    u32 match ip dport 22 0xffff flowid 1:10

# Inspect class-level statistics
tc -s class show dev eth0

Diagnostics

DNS Resolution

ping failing while ip route get succeeds usually means a name-resolution problem, not a routing one. Resolve names the way the system library does, rather than relying on a single resolver.

# What the NSS stack actually returns (honours /etc/nsswitch.conf:
# files, mdns, dns ... — not just DNS)
getent hosts example.com
getent ahosts example.com   # all address families

# systemd-resolved status and per-link DNS
resolvectl status
resolvectl query example.com

# Direct DNS query, bypassing NSS (compare against getent)
dig +short example.com
host example.com

If getent hosts resolves but dig does not (or vice versa), the discrepancy is in /etc/nsswitch.conf ordering, /etc/hosts, or a stub resolver such as systemd-resolved on 127.0.0.53.

MTU and Path MTU Discovery

A connection that opens but stalls on large transfers is the classic PMTU black-hole — ICMP "fragmentation needed" is being dropped somewhere.

# What MTU / source will the kernel use for this destination?
ip route get 1.1.1.1

# Find the largest unfragmented payload (DF set, no fragmentation).
# 1472 bytes payload + 28 (IP+ICMP headers) = 1500 MTU.
ping -M do -s 1472 1.1.1.1     # succeeds at/below path MTU
ping -M do -s 1473 1.1.1.1     # fails if path MTU is 1500

# Probe the path MTU hop by hop
tracepath example.com
tracepath -n 1.1.1.1

ethtool — Link, Speed, and Offloads

# Link state, negotiated speed/duplex
ethtool eth0

# Driver and firmware
ethtool -i eth0

# Per-queue / detailed NIC statistics (drops, errors)
ethtool -S eth0

# Show offload features (GRO, GSO, TSO, checksums)
ethtool -k eth0

# Toggle an offload (e.g. disable GRO when debugging captures)
ethtool -K eth0 gro off

Quick Reference

Task Command
Brief address overview ip -br -c addr
All links, one line each ip -br link
Bring interface up ip link set eth0 up
Add an address ip addr add 192.168.1.10/24 dev eth0
Flush an interface's addresses ip addr flush dev eth0
Show routes ip route
Which route to a destination? ip route get 8.8.8.8
Add a default gateway ip route add default via 192.168.1.1
Idempotent route update ip route replace default via 192.168.1.1
Neighbour (ARP/NDP) table ip neigh
Policy rule list ip rule show
What is listening + PID ss -tulpn
Established connections ss -tn state established
Sockets to a port ss -tn dport = :443
TCP internals (rtt/cwnd) ss -ti
Show qdiscs tc -s qdisc show dev eth0
Emulate latency + loss tc qdisc add dev eth0 root netem delay 100ms loss 1%
Clear all tc on a device tc qdisc del dev eth0 root
Run shell in a namespace ip netns exec red bash
Resolve via NSS getent hosts example.com
Largest unfragmented ping ping -M do -s 1472 1.1.1.1
JSON for scripting ip -j addr | jq …

Common Issues and Solutions

Issue Cause Solution
ip: command not found iproute2 not installed (minimal image) apt install iproute2 (Debian/Ubuntu) or dnf install iproute (RHEL/Fedora)
Changes vanish after reboot ip/tc changes are runtime-only; nothing persists them Encode persistence in the network manager that owns the host (see below)
RTNETLINK answers: File exists Route or address already present Use ip route replace / ip addr replace instead of add, or del first
RTNETLINK answers: Network is unreachable Gateway not on a directly-connected subnet Add the device/subnet route before the via-gateway route
ss shows no process names Need privilege to read socket owners Run with sudo, or grant CAP_NET_ADMIN to the binary
Namespace interface gone after reboot Namespaces and their links are not persistent Recreate on boot via a systemd unit, networkd, or a container runtime
netem/tc tanks throughput A shaping/emulation qdisc was left attached tc qdisc del dev <dev> root; verify with tc qdisc show dev <dev>
MAC/rename change refused Interface is administratively up ip link set <dev> down first, then change, then bring back up
Large transfers stall, small ones fine PMTU black-hole (ICMP being filtered) Lower MTU or fix the path; confirm with ping -M do -s and tracepath
ping fails but route resolves Name resolution, not routing Check with getent hosts, resolvectl status, /etc/nsswitch.conf

Where Persistence Actually Lives

ip and tc never write config to disk. Persistence belongs to whichever subsystem manages the host's networking — edit that, then let it apply the runtime state:

Manager Config location Apply
netplan (Ubuntu Server) /etc/netplan/*.yaml netplan apply
NetworkManager (desktops, RHEL) /etc/NetworkManager/system-connections/ nmcli con reload
systemd-networkd /etc/systemd/network/*.network, *.netdev networkctl reload
ifupdown (legacy Debian) /etc/network/interfaces ifup / ifdown

For policy-routing tables, register friendly names in /etc/iproute2/rt_tables, but the rules and routes themselves still need to be reinstalled by your chosen manager (e.g. networkd [Route]/[RoutingPolicyRule] stanzas) to survive a reboot.

Related Topics

The following topics complement this Linux network tools cheatsheet:

  1. iptables/nftables — packet filtering and NAT that sits alongside ip routing and fwmark-based policy routing
  2. tcpdump/Wireshark — capture and dissect the traffic you are routing and shaping
  3. systemd — systemd-networkd and unit files for persisting interfaces, namespaces, and routes
  4. Docker / Podman networking — veth pairs, bridges, and namespaces as used by container runtimes
  5. Kubernetes networking (CNI) — how Calico/Flannel build on veth, bridges, and routing under the hood
  6. WireGuard / VPN routing — policy routing and fwmark patterns for tunnel egress selection