在单个 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
我正在尝试模拟许多网络跃点,类似于 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