root@vmx1.lab> show interfaces terse | grep ge-0/0/ ge-0/0/0 up up ge-0/0/0.0 up up inet 192.168.10.1/24 ge-0/0/1 up up ge-0/0/1.0 up up inet 192.168.10.1/24 ge-0/0/2 up down ge-0/0/3 up down ge-0/0/4 up down ge-0/0/5 up down ge-0/0/6 up down ge-0/0/7 up down ge-0/0/8 up down ge-0/0/9 up down root@vmx1.lab>
root@vmx1.lab> show lldp neighbors Local Interface Parent Interface Chassis Id Port info System Name ge-0/0/1 - 2c:6b:f5:4a:23:c0 ge-0/0/0 vmx3.lab ge-0/0/0 - 2c:6b:f5:f9:08:c0 ge-0/0/1 vmx2.lab root@vmx1.lab>
在Linux上配置
使用网络空间
如果这对你来说仍然不足以说服你,那么如果我们要在 Linux 服务上使用网络命名空间来提供 L3 隔离,让我们考虑相同的场景。假设我们有一个看起来像这样的 Linux 服务器。
配置这样的东西可能看起来像这样…
ip netns add namespace-1 ip linkset dev ens6 netns namespace-1 ip netns exec namespace-1 ip linkset dev ens6 up ip netns exec namespace-1 ip address add 192.168.10.1/24 dev ens6 ip netns exec namespace-1 ip route add 0.0.0.0/0 via 192.168.10.254 ip netns exec namespace-1 ip route add 172.64.32.0/24 via 192.168.10.101 ip netns add namespace-2 ip linkset dev ens7 netns namespace-2 ip netns exec namespace-2 ip linkset dev ens7 up ip netns exec namespace-2 ip address add 192.168.10.1/24 dev ens7 ip netns exec namespace-2 ip route add 0.0.0.0/0 via 192.168.10.10 ip netns exec namespace-2 ip route add 192.168.128.0/24 via 192.168.10.20
root@vm1:~# ip netns exec namespace-1 ip route show default via 192.168.10.254 dev ens6 172.64.32.0/24 via 192.168.10.101 dev ens6 192.168.10.0/24 dev ens6 proto kernel scope link src 192.168.10.1 root@vm1:~# ip netns exec namespace-2 ip route show default via 192.168.10.10 dev ens7 192.168.10.0/24 dev ens7 proto kernel scope link src 192.168.10.1 192.168.128.0/24 via 192.168.10.20 dev ens7 root@vm1:~#
root@vm1:~# apt -y install lldpd Reading package lists... Done Building dependency tree Reading state information... Done Suggested packages: snmpd The following NEW packages will be installed: lldpd 0 upgraded, 1 newly installed, 0 to remove and 126 not upgraded. Need to get 0 B/154 kB of archives. After this operation, 511 kB of additional disk space will be used. Selecting previously unselected package lldpd. (Reading database ... 107391 files and directories currently installed.) Preparing to unpack .../lldpd_1.0.4-1build2_amd64.deb ... Unpacking lldpd (1.0.4-1build2) ... Setting up lldpd (1.0.4-1build2) ... Processing triggers for libc-bin (2.31-0ubuntu9.1) ... Processing triggers for systemd (245.4-4ubuntu3.3) ... Processing triggers for man-db (2.9.1-1) ... root@vm1:~# root@vm1:~# lldpcli show interfaces ------------------------------------------------------------------------------- LLDP interfaces: ------------------------------------------------------------------------------- Interface: ens3, via: unknown, Time: 0 day, 00:19:09 Chassis: ChassisID: mac 52:ab:54:ab:01:01 SysName: vm1 SysDescr: Ubuntu 20.04.1 LTS Linux 5.9.4-050904-generic #202011042130 SMP Wed Nov 4 21:36:10 UTC 2020 x86_64 MgmtIP: 192.168.127.1 MgmtIP: fe80::50ab:54ff:feab:101 Capability: Bridge, off Capability: Router, off Capability: Wlan, off Capability: Station, on Port: PortID: mac 52:ab:54:ab:01:01 PortDescr: ens3 TTL: 120 ------------------------------------------------------------------------------- root@vm1:~# lldpcli show neighbors ------------------------------------------------------------------------------- LLDP neighbors: ------------------------------------------------------------------------------- root@vm1:~#
其实完全没有必要像这样隔离不需要隔离的东西——我们这样做唯一得到的就是在路由器上产生更多的开销。也就是说,我希望这表明,虽然命名空间确实提供了必要的隔离,但对于我们试图实现的目标来说,它们有点矫枉过正。正如 Cumulus 在他们的文章 “Network Namespace as a VRF? Just say No(用网络命名空间作为 VRF?不可以!)”。
使用VRF
因此,现在我们已经研究了解决此问题的其他可能选项,让我们来谈谈在 Linux 中使用实际的 VRF。因此,我们把再做一些上面的事——但现在用的是VRF!
root@vm1:~# ip link add vrf-1 type vrf table 1 root@vm1:~# ip link set dev vrf-1 up root@vm1:~# ip vrf Name Table ----------------------- vrf-1 1 root@vm1:~#
简单!所以现在我们有了一个 VRF,那么用它做什么?好吧,我们可以很容易地向VRF添加接口…
root@vm1:~# ip link set dev ens6 master vrf-1 root@vm1:~# ip addr add 192.168.10.1/24 dev ens6 root@vm1:~# root@vm1:~# ip route show vrf vrf-1 192.168.127.0/24 dev ens6 proto kernel scope link src 192.168.127.1 root@vm1:~#
上面我们添加ens6到VRFvrf-1,然后向其添加IP地址。然后,我们可以使用命令ip route show vrf查看vrf-1的路由表。这里要指出,虽然ens6接口现在位于 VRF 中,但这并不意味着它像在切换命名空间中一样从全局命名空间中消失了…
root@vm1:~# ip -br link lo UNKNOWN 00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP> ens3 UP 52:ab:54:ab:01:01 <BROADCAST,MULTICAST,UP,LOWER_UP> ens6 UP 52:ab:54:cd:01:01 <BROADCAST,MULTICAST,UP,LOWER_UP> ens7 UP 52:ab:54:cd:01:02 <BROADCAST,MULTICAST,UP,LOWER_UP> vrf-1 UP 4e:19:81:5c:ad:ee <NOARP,MASTER,UP,LOWER_UP> root@vm1:~#
因此,现在我们在 VRF 中有了一个接口——我们可以添加静态路由…
root@vm1:~# ip route add 0.0.0.0/0 via 192.168.127.254 vrf vrf-1 root@vm1:~# ip route add 172.64.32.0/24 via 192.168.127.101 vrf vrf-1 root@vm1:~# ip route show vrf vrf-1 default via 192.168.127.254 dev ens6 172.64.32.0/24 via 192.168.127.101 dev ens6 192.168.127.0/24 dev ens6 proto kernel scope link src 192.168.127.1 root@vm1:~#
现在让我们直接把vrf-2的所有东西配置好…
ip link add vrf-2 type vrf table 2 ip linkset dev vrf-2 up ip linkset dev ens7 master vrf-2 ip addr add 192.168.127.1/24 dev ens7 ip route add 0.0.0.0/0 via 192.168.127.10 vrf vrf-2 ip route add 192.168.128.0/24 via 192.168.127.20 vrf vrf-2
root@vm1:~# ping -I vrf-1 192.168.10.254 ping: Warning: source address might be selected on device other than: vrf-1 PING 192.168.10.254 (192.168.10.254) from 192.168.10.1 vrf-1: 56(84) bytes of data. 64 bytes from 192.168.10.254: icmp_seq=1 ttl=64 time=0.250 ms 64 bytes from 192.168.10.254: icmp_seq=2 ttl=64 time=0.253 ms 64 bytes from 192.168.10.254: icmp_seq=3 ttl=64 time=0.444 ms
我们还可以将 IP 地址添加到 VRF 接口本身…
root@vm1:~# ip addr add 1.1.1.1/32 dev vrf-1 root@vm1:~# ip route get vrf vrf-1 1.1.1.1 local 1.1.1.1 dev vrf-1 table 1 src 1.1.1.1 uid 0 cache <local> root@vm1:~#
我们已经展示了如何创建和使用 VRF,但我们还没有真正讨论如何实际将流量引入 VRF。我们知道,当我们创建 VRF 时,我们必须为其分配一个路由表编号。在处理路由表时,我们一般需要制作一些规则集来处理它们,所以让我们来看看现有的 IP 规则…
root@vm1:~# ip rule show 0: from all lookup local 1000: from all lookup [l3mdev-table] 32766: from all lookup main 32767: from all lookup default root@vm1:~#
注意规则 1000。现在看一个在尚未配置任何 VRF之前的rule…
root@vm2:~# ip rule show 0: from all lookup local 32766: from all lookup main 32767: from all lookup default root@vm2:~#
root@vm1:~# ip route show table local broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1 local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1 local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1 broadcast 192.168.127.0 dev ens3 proto kernel scope link src 192.168.127.1 local 192.168.127.1 dev ens3 proto kernel scope host src 192.168.127.1 broadcast 192.168.127.255 dev ens3 proto kernel scope link src 192.168.127.1 root@vm1:~#
root@vm1:~# ping -I vrf-1 192.168.10.254 ping: Warning: source address might be selected on device other than: vrf-1 PING 192.168.10.254 (192.168.10.254) from 192.168.10.1 vrf-1: 56(84) bytes of data. 64 bytes from 192.168.10.254: icmp_seq=1 ttl=64 time=0.437 ms 64 bytes from 192.168.10.254: icmp_seq=2 ttl=64 time=0.260 ms 64 bytes from 192.168.10.254: icmp_seq=3 ttl=64 time=0.186 ms
现在让我们删除规则 1000 再试试…
root@vm1:~# ip rule del pref 1000 root@vm1:~# ping -I vrf-1 192.168.10.254 -c 2 ping: Warning: source address might be selected on device other than: vrf-1 PING 192.168.10.254 (192.168.10.254) from 192.168.127.1 vrf-1: 56(84) bytes of data. --- 192.168.10.254 ping statistics --- 2 packets transmitted, 0 received, 100% packet loss, time 1017ms root@vm1:~#
没有成功。因此,让我们在 VRF 中进行路由查找,看看发生了什么…
root@vm1:~# ip route get vrf vrf-1 192.168.10.254 192.168.10.254 via 192.168.127.100 dev ens3 src 192.168.127.1 uid 0 cache root@vm1:~# ip route default via 192.168.127.100 dev ens3 proto static 10.20.30.0/24 via 192.168.127.100 dev ens3 proto static 192.168.127.0/24 dev ens3 proto kernel scope link src 192.168.127.1 root@vm1:~#
root@vm1:~# ip rule show 0: from all lookup local 32764: from all iif vrf-1 lookup 1 32765: from all oif vrf-1 lookup 1 32766: from all lookup main 32767: from all lookup default root@vm1:~# ip rule del pref 32764 root@vm1:~# ip rule del pref 32765 root@vm1:~# ip rule add l3mdev pref 1000 root@vm1:~# ip rule show 0: from all lookup local 1000: from all lookup [l3mdev-table] 32766: from all lookup main 32767: from all lookup default root@vm1:~#
root@vm1:~# ip -4 rule add pref 32765 table local root@vm1:~# ip -4 rule del pref 0 root@vm1:~# ip rule show 1000: from all lookup [l3mdev-table] 32765: from all lookup local 32766: from all lookup main 32767: from all lookup default root@vm1:~# root@vm1:~# ping -I vrf-1 192.168.10.254 -c 2 ping: Warning: source address might be selected on device other than: vrf-1 PING 192.168.10.254 (192.168.10.254) from 192.168.10.1 vrf-1: 56(84) bytes of data. 64 bytes from 192.168.10.254: icmp_seq=1 ttl=64 time=0.564 ms 64 bytes from 192.168.10.254: icmp_seq=2 ttl=64 time=0.293 ms --- 192.168.10.254 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1030ms rtt min/avg/max/mdev = 0.293/0.428/0.564/0.135 ms root@vm1:~#
从 v4.8 开始,内核支持 l3mdev FIB 规则,其中单个ip rule规则1000: from all lookup [l3mdev-table]就可以涵盖所有 VRF。不必再为每一个VRF设备创建rule
2. 列出 VRF
要列出已创建的 VRF,请执行以下操作:
$ ip [-d] link show type vrf NOTE: 需要-d参数展示关联的table表ID
例如:
$ ip -d link show type vrf 11: mgmt: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 72:b3:ba:91:e2:24 brd ff:ff:ff:ff:ff:ff promiscuity 0 vrf table 1 addrgenmode eui64 12: red: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether b6:6f:6e:f6:da:73 brd ff:ff:ff:ff:ff:ff promiscuity 0 vrf table 10 addrgenmode eui64 13: blue: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 36:62:e8:7d:bb:8c brd ff:ff:ff:ff:ff:ff promiscuity 0 vrf table 66 addrgenmode eui64 14: green: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether e6:28:b8:63:70:bb brd ff:ff:ff:ff:ff:ff promiscuity 0 vrf table 81 addrgenmode eui64
或者简要输出:
$ ip -br link show type vrf mgmt UP 72:b3:ba:91:e2:24 <NOARP,MASTER,UP,LOWER_UP> red UP b6:6f:6e:f6:da:73 <NOARP,MASTER,UP,LOWER_UP> blue UP 36:62:e8:7d:bb:8c <NOARP,MASTER,UP,LOWER_UP> green UP e6:28:b8:63:70:bb <NOARP,MASTER,UP,LOWER_UP>
$ ip link show vrf NAME $ ip link show master NAME
例如:
$ ip link show vrf red 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP mode DEFAULT group default qlen 1000 link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff 4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP mode DEFAULT group default qlen 1000 link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff 7: eth5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master red state DOWN mode DEFAULT group default qlen 1000 link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff
或者使用简短的输出:
$ ip -br link show vrf red eth1 UP 02:00:00:00:02:02 <BROADCAST,MULTICAST,UP,LOWER_UP> eth2 UP 02:00:00:00:02:03 <BROADCAST,MULTICAST,UP,LOWER_UP> eth5 DOWN 02:00:00:00:02:06 <BROADCAST,MULTICAST>
5. 显示 VRF 的相邻条目
列出与从属于 VRF 设备的设备关联的邻居条目——将 master 选项添加到 ip 命令中:
$ ip [-6] neigh show vrf NAME $ ip [-6] neigh show master NAME
例如:
$ ip neigh show vrf red 10.2.1.254 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE 10.2.2.254 dev eth2 lladdr 5e:54:01:6a:ee:80 REACHABLE
$ ip -6 neigh show vrf red 2002:1::64 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE
6. 显示 VRF 的地址
要显示与 VRF 关联的接口的地址,添加master选项到ip命令中:
$ ip addr show vrf NAME $ ip addr show master NAME
例如:
$ ip addr show vrf red 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP group default qlen 1000 link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff inet 10.2.1.2/24 brd 10.2.1.255 scope global eth1 valid_lft forever preferred_lft forever inet6 2002:1::2/120 scope global valid_lft forever preferred_lft forever inet6 fe80::ff:fe00:202/64 scope link valid_lft forever preferred_lft forever 4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP group default qlen 1000 link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff inet 10.2.2.2/24 brd 10.2.2.255 scope global eth2 valid_lft forever preferred_lft forever inet6 2002:2::2/120 scope global valid_lft forever preferred_lft forever inet6 fe80::ff:fe00:203/64 scope link valid_lft forever preferred_lft forever 7: eth5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master red state DOWN group default qlen 1000 link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff
或以简短的格式:
$ ip -br addr show vrf red eth1 UP 10.2.1.2/24 2002:1::2/120 fe80::ff:fe00:202/64 eth2 UP 10.2.2.2/24 2002:2::2/120 fe80::ff:fe00:203/64 eth5 DOWN
7. 显示 VRF 的路由
要显示 VRF 的路由,请使用 ip 命令显示与VRF设备关联的路由表:
$ ip [-6] route show vrf NAME $ ip [-6] route show table ID
例如:
$ ip route show vrf red unreachable default metric 4278198272 broadcast 10.2.1.0 dev eth1 proto kernel scope link src 10.2.1.2 10.2.1.0/24 dev eth1 proto kernel scope link src 10.2.1.2 local 10.2.1.2 dev eth1 proto kernel scope host src 10.2.1.2 broadcast 10.2.1.255 dev eth1 proto kernel scope link src 10.2.1.2 broadcast 10.2.2.0 dev eth2 proto kernel scope link src 10.2.2.2 10.2.2.0/24 dev eth2 proto kernel scope link src 10.2.2.2 local 10.2.2.2 dev eth2 proto kernel scope host src 10.2.2.2 broadcast 10.2.2.255 dev eth2 proto kernel scope link src 10.2.2.2
$ ip -6 route show vrf red local 2002:1:: dev lo proto none metric 0 pref medium local 2002:1::2 dev lo proto none metric 0 pref medium 2002:1::/120 dev eth1 proto kernel metric 256 pref medium local 2002:2:: dev lo proto none metric 0 pref medium local 2002:2::2 dev lo proto none metric 0 pref medium 2002:2::/120 dev eth2 proto kernel metric 256 pref medium local fe80:: dev lo proto none metric 0 pref medium local fe80:: dev lo proto none metric 0 pref medium local fe80::ff:fe00:202 dev lo proto none metric 0 pref medium local fe80::ff:fe00:203 dev lo proto none metric 0 pref medium fe80::/64 dev eth1 proto kernel metric 256 pref medium fe80::/64 dev eth2 proto kernel metric 256 pref medium ff00::/8 dev red metric 256 pref medium ff00::/8 dev eth1 metric 256 pref medium ff00::/8 dev eth2 metric 256 pref medium unreachable default dev lo metric 4278198272 error -101 pref medium
8. VRF 的路由查找
可以测试VRF的路由查找:
$ ip [-6] route get vrf NAME ADDRESS $ ip [-6] route get oif NAME ADDRESS
例如:
$ ip route get 10.2.1.40 vrf red 10.2.1.40 dev eth1 table red src 10.2.1.2 cache
$ ip -6 route get 2002:1::32 vrf red 2002:1::32 from :: dev eth1 table red proto kernel src 2002:1::2 metric 256 pref medium