在单个 Linux 框上模拟网络跃点

Simulating network hops on a single Linux box

我正在尝试模拟许多网络跃点,类似于 Star Wars Traceroute. The author provided a script here,但它是为 Cisco 路由器设计的,使用 vrf,而不是 Linux。

这是我目前的尝试;

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

我尝试使用 Source Based Routing, which apparently reproduces the effects of vrf, see here 完成同样的事情,但附加 tcpdump -i br0 显示根本没有流量,而是在 lo0 上接收数据包。

替代解决方案似乎包括修改源 IP 和其他使用 iptables 的诡计,感觉很脏,所以我不确定我的工作重点在哪里。

任何 tips/pointers 将不胜感激

我能够使用 Linux 命名空间来实现这一点,它们目前是 Linux 等同于 vrf。这只是一个手写的 POC,您可能希望将其包装在脚本中并按程序生成。

我花了 8 个多小时才弄明白,希望它能对以后的其他人有所帮助。

最终结果

$ ping 172.16.101.2
PING 172.16.101.2 (172.16.101.2) 56(84) bytes of data.
64 bytes from 172.16.101.2: icmp_seq=1 ttl=63 time=0.064 ms
64 bytes from 172.16.101.2: icmp_seq=2 ttl=63 time=0.043 ms

$ traceroute 172.16.101.2
traceroute to 172.16.101.2 (172.16.101.2), 30 hops max, 60 byte packets
 1  172.16.100.2 (172.16.100.2)  0.055 ms  0.006 ms  0.004 ms
 2  172.16.101.2 (172.16.101.2)  0.059 ms  0.009 ms  0.009 ms

解决方案

# add the namespaces
ip netns add hop1
ip netns add hop2

ip netns exec hop1   ip link set lo up
ip netns exec hop2   ip link set lo up

ip netns exec hop1   sysctl net.ipv4.ip_forward net.ipv4.ip_forward=1
ip netns exec hop2   sysctl net.ipv4.ip_forward net.ipv4.ip_forward=1

# create host link to first hop
ip link add hop1 type veth peer name veth1

ip addr change 172.16.100.1/24 dev hop1
ip link set hop1 up

ip link set veth1 netns hop1
ip netns exec hop1   ip link set veth1 name eth0
ip netns exec hop1   ip link set eth0 up
ip netns exec hop1   ip addr change 172.16.100.2/24 dev eth0

# create hop2 (link to hop1)
ip link add veth0 type veth peer name veth1

ip link set veth0 netns hop1
ip netns exec hop1   ip link set veth0 name eth1
ip netns exec hop1   ip link set eth1 up
ip netns exec hop1   ip addr change 172.16.101.1/24 dev eth1

ip link set veth1 netns hop2
ip netns exec hop2   ip link set veth1 name eth0
ip netns exec hop2   ip link set eth0 up
ip netns exec hop2   ip addr change 172.16.101.2/24 dev eth0

# set namespace routing
ip netns exec hop1   route add default gw 172.16.101.2
ip netns exec hop2   route add default gw 172.16.101.1

# set host routing
route add -net 172.16.101.0/24 gw 172.16.100.2

我把这个想法变成了一个脚本,供那些仍然发现这个问题并尝试使用 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