Search code examples
linuxnetwork-programmingroutesiptables

Simulating network hops on a single Linux box


I'm trying to simulate many network hops, similar to Star Wars Traceroute. The author provided a script here, however it's designed for Cisco routers by using vrf, rather than Linux.

Here is my current attempt;

echo 1 > /proc/sys/net/ipv4/ip_forward

ifconfig br0 172.16.100.1 netmask 255.255.255.0
ifconfig br0:1 172.16.101.1 netmask 255.255.255.0
ifconfig br0:2 172.16.102.1 netmask 255.255.255.0
ifconfig br0:2 172.16.103.1 netmask 255.255.255.0

ip rule add iif br0 table 100
ip rule add iif br0:1 table 101
ip rule add iif br0:2 table 102

ip route add default table 100 dev 172.16.101.1
ip route add default table 101 dev 172.16.102.1
ip route add default table 102 dev 172.16.103.1

ping -I br0 172.16.103.2

I've tried to accomplish the same thing using Source Based Routing, which apparently reproduces the effects of vrf, see here, but attaching tcpdump -i br0 shows no traffic at all, and the packets are instead being picked up on lo0.

Alternative solutions seem to include mangling source IPs and other trickery using iptables which feels quite dirty, so I'm not sure where to focus my efforts.

Any tips/pointers would be appreciated


Solution

  • I made this idea into a script for those still finding this question trying to make their own Star Wars Traceroute using Linux.

    #!/bin/bash
    #
    # traceroute fun with linux namespaces
    #  each namespace is basically a router we connect
    #  to each other using fancy /32 networking
    #
    # scott nicholas <scott@nicholas.one> 2018-10-27
    #
    
    # how many levels deep are we going?
    depth=16
    prefix=192.168.99.
    
    # can change function easily or just pre-populate ip array
    calcip() { printf '%s%d' "$prefix" $((63 + i)); }
    
    # instead of special casing things, if we bind init's netns into a name
    # all of the code can use "-n ns"
    touch /var/run/netns/default
    mount --bind /proc/1/ns/net /var/run/netns/default
    
    ns[0]=default if[0]=root ip[0]=${prefix}1
    
    for ((i = 1; i <= depth; i++)); do
      ns[i]=hop$i if[i]=hop$i ip[i]=$(calcip $i)
    
      ip netns add "${ns[i]}"
      # interfaces are named by whom is on the other side
      # so it's kinda flip-flopped looking.
      ip -n "${ns[i-1]}" link add "${if[i]}" type veth peer name "${if[i-1]}" \
        netns "${ns[i]}"
    
      ip -n "${ns[i]}" a a "${ip[i]}"/32 dev "${if[i-1]}"
    
      # interfaces must be up before adding routes
      ip -n "${ns[i-1]}" link set "${if[i]}"   up
      ip -n "${ns[i  ]}" link set lo           up
      ip -n "${ns[i  ]}" link set "${if[i-1]}" up
    
      ip -n "${ns[i-1]}" route add "${ip[i  ]}" dev "${if[i]}"
      ip -n "${ns[i  ]}" route add "${ip[i-1]}" dev "${if[i-1]}"
      ip -n "${ns[i  ]}" route add default      via "${ip[i-1]}"
    
      # tell everyone above my parent that i'm down here in this mess
      for ((j = i - 2; j >= 0; j--)); do
        ip -n "${ns[j]}" route add "${ip[i]}" via "${ip[j+1]}"
      done
    done