OpenStack网络实现--Linux虚拟网络基础

最近研究OpenStack,发现Neutron很有趣,在宿主机上执行ifconfig可以看到很多tap/br等网络设备关键字,于是,不得不研究Linux虚拟网络基础。

tap

tap虚拟网络设备,tap设备位于ISO的2层,数据链路层。

数据链路层的主要协议有:

  1. 点对点协议
  2. 以太网协议
  3. 高级数据链路协议
  4. 帧中继
  5. 异步传输模式

但是tap只与其中的以太网协议对应。所以,tap也称为虚拟以太设备。

Linux使用tun模块实现了tun/tap。

使用linux命令来操作tap:

检查是否有tun模块

# modinfo tun

filename:       /lib/modules/3.10.0-229.el7.x86_64/kernel/drivers/net/tun.ko

alias:          devname:net/tun

alias:          char-major-10-200

......

 

查看tun模块是否加载

# lsmod | grep tun

tun                    27183  0

如果以上没有输出,即没有加载tun,则使用以下命令加载tun模块

# modprobe tun

tun模块有了,还需要tunctl工具来操作tap/tun设备

# yum install tunctl -y

创建一个tap设备

# tunctl -t tap_test

Set 'tap_test' persistent and owned by uid 0

查看刚刚创建的tap设备

# ip link list

...

17: tap_test: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 500

link/ether 72:3f:cd:fd:0b:ee brd ff:ff:ff:ff:ff:ff

给tap设备绑定IP

# ip addr add local 192.168.12.0/24 dev tap_test

或者:

# ifconfig tap_test 192.168.12.0/24

查看是否成功绑定IP

# ifconfig -a

...

tap_test: flags=4098<BROADCAST,MULTICAST>  mtu 1500

        inet 192.168.12.0  netmask 255.255.255.0  broadcast 0.0.0.0

        ether 72:3f:cd:fd:0b:ee  txqueuelen 500  (Ethernet)

        RX packets 0  bytes 0 (0.0 B)

        RX errors 0  dropped 0  overruns 0  frame 0

        TX packets 0  bytes 0 (0.0 B)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

 

namespace

Namespace,即命名空间,主要是实现linux的资源隔离,我们这里主要研究对网络资源的隔离。

Linux操作网络namespace的命令是ip netns。

 

查看命令帮助:

# ip netns help

Usage: ip netns list

       ip netns add NAME

       ip netns delete NAME

       ip netns identify PID

       ip netns pids NAME

       ip netns exec NAME cmd ...

       ip netns monitor

查看ns

# ip netns list

创建一个ns:名字是ns_test
 

# ip netns add ns_test

再查看

# ip netns list

ns_test

接下来,我们帮上面创建的tap设备tap_test迁移到这个ns里边去:

# ip link set tap_test netns ns_test

迁移成功后,原来的主机里面,执行ip link list命令,就会发现这个tap_test已经消失了

操作namespace里边的设备

ip [all] netns exec [NAME] cmd ...     // cmd为想要操作的命令行

在ns里边执行ip link list

# ip netns exec ns_test ip link list

1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT

    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

17: tap_test: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 500

    link/ether 72:3f:cd:fd:0b:ee brd ff:ff:ff:ff:ff:ff

在ns里绑定IP地址

# ip netns exec ns_test ifconfig tap_test 192.168.12.1/24 up

查看IP地址

# ip netns exec ns_test ifconfig -a

...

tap_test: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500

        inet 192.168.12.1  netmask 255.255.255.0  broadcast 192.168.12.255

        ether 72:3f:cd:fd:0b:ee  txqueuelen 500  (Ethernet)

        RX packets 0  bytes 0 (0.0 B)

        RX errors 0  dropped 0  overruns 0  frame 0

        TX packets 0  bytes 0 (0.0 B)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

 

veth pair

veth pair不是一个设备,而是一对设备,用以连接两个虚拟以太端口。

操作veth pair,需要跟namespace一起配合,否则就没有意义。

 

设计一个测试用例,如下图:

两个namespace ns1/ns2中各有一个tap组成veth pair,两者的IP地址如图所示,两个IP进行互ping测试。

 

具体实现:

创建veth pair

# ip link add tap1 type veth peer name tap2

创建namespace:ns1,ns2

# ip netns add ns1

# ip netns add ns2

把两个tap分别迁移到对应的namespace中

# ip link set tap1 netns ns1

# ip link set tap2 netns ns2

分别给两个tap绑定IP地址

# ip netns exec ns1 ip addr add local 192.168.12.1/24 dev tap1

# ip netns exec ns2 ip addr add local 192.168.12.2/24 dev tap2

将两个tap设置为UP

# ip netns exec ns1 ifconfig tap1 up

# ip netns exec ns2 ifconfig tap2 up

Ping测试

# ip netns exec ns2 ping 192.168.12.1

PING 192.168.12.1 (192.168.12.1) 56(84) bytes of data.

64 bytes from 192.168.12.1: icmp_seq=1 ttl=64 time=0.052 ms

...



# ip netns exec ns1 ping 192.168.12.2

PING 192.168.12.2 (192.168.12.2) 56(84) bytes of data.

64 bytes from 192.168.12.2: icmp_seq=1 ttl=64 time=0.206 ms

...

通过上面的测试用例,我们了解到通过veth pair连接两个namespace的方法。

但是,如果是3个namespace之间需要互通呢?或者多个namespace之间需要互通呢?

veth pair只有一对tap,无法胜任,这时就需要用到Bridge/Switch。

 

Bridge

在Linux里边,bridge(网桥)和switch(交换机)都是实现2层的功能,概念相近,所以这里也不做区分。

Linux实现Bridge功能的是brctl模块。

 

安装brctl

# yum install -y bridge-utils

先查看帮助

# brctl help

...

测试用例图:

图中,有4个namespace,每个ns都有一个tap与虚拟网桥vb上一个tap口组成veth pair。

这样4个namespace就通过veth pair及bridge互联起来。

 

具体实现:

创建veth pair

# ip link add tap1 type veth peer name tap1_peer

# ip link add tap2 type veth peer name tap2_peer

# ip link add tap3 type veth peer name tap3_peer

# ip link add tap4 type veth peer name tap4_peer

创建namespace

# ip netns add ns1

# ip netns add ns2

# ip netns add ns3

# ip netns add ns4

把tap设备迁移到对应的namespace中

# ip link set tap1 netns ns1

# ip link set tap2 netns ns2

# ip link set tap3 netns ns3

# ip link set tap4 netns ns4

创建bridge

# brctl addbr br1

把相应tap添加到bridge中

# brctl addif br1 tap1_peer

# brctl addif br1 tap2_peer

# brctl addif br1 tap3_peer

# brctl addif br1 tap4_peer

查看网桥

# brctl show

bridge name bridge id STP enabled interfaces

br1 8000.3601df1a8177 no tap1_peer

tap2_peer

tap3_peer

tap4_peer

配置相应tap的IP地址

# ip netns exec ns1 ip addr add local 192.168.12.1/24 dev tap1

# ip netns exec ns2 ip addr add local 192.168.12.2/24 dev tap2

# ip netns exec ns3 ip addr add local 192.168.12.3/24 dev tap3

# ip netns exec ns4 ip addr add local 192.168.12.4/24 dev tap4

将bridge及所有tap状态设置为tap

# ip link set br1 up

# ip link set tap1_peer up

# ip link set tap2_peer up

# ip link set tap3_peer up

# ip link set tap4_peer up

# ip netns exec ns1 ip link set tap1 up

# ip netns exec ns2 ip link set tap2 up

# ip netns exec ns3 ip link set tap3 up

# ip netns exec ns4 ip link set tap4 up

Ping测试

# ip netns exec ns1 ping 192.168.12.2

PING 192.168.12.2 (192.168.12.2) 56(84) bytes of data.

64 bytes from 192.168.12.2: icmp_seq=1 ttl=64 time=0.143 ms

...



# ip netns exec ns3 ping 192.168.12.1

PING 192.168.12.1 (192.168.12.1) 56(84) bytes of data.

64 bytes from 192.168.12.1: icmp_seq=1 ttl=64 time=0.134 ms

...

 

Router

Linux服务器本身就可以作为路由器,只需要开启转发功能。

 

查看Linux转发功能

# cat /proc/sys/net/ipv4/ip_forward

0

临时开启转发

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

再次查看

# cat /proc/sys/net/ipv4/ip_forward

1

测试用例图:

图中,ns1/tap1与ns2/tap2不在同一个网段中,中间需要经过一个路由器进行转发才能互通。

图中的router是一个示意,其实就是Linux开启了路由转发功能。

 

具体实现:

创建veth pair

# ip link add tap1 type veth peer name tap1_pair

# ip link add tap2 type veth peer name tap2_pair

创建namespace

# ip netns add ns1

# ip netns add ns2

将tap迁移到namespace

# ip link set tap1 netns ns1

# ip link set tap2 netns ns2

配置tap 的IP地址

# ip addr add local 192.168.12.1/24 dev tap1_pair

# ip addr add local 192.168.22.1/24 dev tap2_pair

# ip netns exec ns1 ip addr add local 192.168.12.2/24 dev tap1

# ip netns exec ns2 ip addr add local 192.168.22.2/24 dev tap2

将tap设置为UP

# ip link set tap1_pair up

# ip link set tap2_pair up

# ip netns exec ns1 ip link set tap1 up

# ip netns exec ns2 ip link set tap2 up

查看路由表

# route -n

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

0.0.0.0         192.168.1.1     0.0.0.0         UG    100    0        0 eno50332208

192.168.12.0    0.0.0.0         255.255.255.0   U     0      0        0 tap1_pair

192.168.22.0    0.0.0.0         255.255.255.0   U     0      0        0 tap2_pair

可以看到,当我们添加了tap设备并为其绑定了IP,Linux会自动生成直连路由。

Ping测试

# ip netns exec ns1 ping 192.168.22.2

connect: 网络不可达

ping不通,我们再看一下ns1的路由表

# ip netns exec ns1 route -n

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

192.168.12.0    0.0.0.0         255.255.255.0   U     0      0        0 tap1

ns1中并没有到达192.168.22.0/24的路由表项,需要我们手动添加。

ns1,ns2都添加静态路由,分别到达对方的网段

# ip netns exec ns1 route add -net 192.168.22.0 netmask 255.255.255.0 gw 192.168.12.1

# ip netns exec ns2 route add -net 192.168.12.0 netmask 255.255.255.0 gw 192.168.22.1

再查看ns1的路由表

# ip netns exec ns1 route -n

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

192.168.12.0    0.0.0.0         255.255.255.0   U     0      0        0 tap1

192.168.22.0    192.168.12.1    255.255.255.0   UG    0      0        0 tap1

再次ping测试

# ip netns exec ns1 ping 192.168.22.2

PING 192.168.22.2 (192.168.22.2) 56(84) bytes of data.

64 bytes from 192.168.22.2: icmp_seq=1 ttl=63 time=0.364 ms

...



# ip netns exec ns2 ping 192.168.12.2

PING 192.168.12.2 (192.168.12.2) 56(84) bytes of data.

64 bytes from 192.168.12.2: icmp_seq=1 ttl=63 time=0.204 ms

...

 

tun

tun位于ISO的第3层,是一个点对点设备,它启用了IP层隧道功能。

Linux原生支持的三层隧道,可以通过命令行ip tunnel help来查。

 

可以发现,Linux一共原生支持5种三层隧道,如下

隧道

简述

ipip

IP in IP,在IPv4报文的基础上再封装一个IPv4报文头,属于IPv4 IN IPv4。

gre

通用路由封装(Generic Routing Encapsulation),定义了在任意一种网络层协议上封装任意一个其他网络层协议的协议,属于IPv4/IPv6 over IPv4。

sit

这个跟ipip类似,只不过是用一个IPv4的报文头封装IPv6的报文,属于IPv6 over IPv4。

isatap

站内自动隧道寻址协议,一般用于IPv4网络中的IPv6/IPv4节点间的通信。

vti

全称是Virtual Tunnel Interface,为IPsec隧道提供一个可路由的接口类型。

测试用例图:

假设tap1与tap2能通,我们之前已经实现。

怎样才能让tun1与tun2互通呢?

具体实现:

这里以ipip隧道为例进行配置。

查看Linux系统是否已经加载ipip模块

# lsmod | grep ipip

加载ipip模块

# modprobe ipip

# lsmod | grep ipip

ipip                   13472  0

tunnel4                13252  1 ipip

ip_tunnel              23760  1 ipip

在ns1上创建tun1和ipip tunnel

# ip netns exec ns1 ip tunnel add tun1 mode ipip remote 192.168.22.2 local 192.168.12.2 ttl 255

# ip netns exec ns1 ip link set tun1 up

# ip netns exec ns1 ip addr add 192.168.88.8 peer 192.168.99.9 dev tun1

在ns2上创建tun2和ipip tunnel

# ip netns exec ns2 ip tunnel add tun2 mode ipip remote 192.168.12.2 local 192.168.22.2 ttl 255

# ip netns exec ns2 ip link set tun2 up

# ip netns exec ns2 ip addr add 192.168.99.9 peer 192.168.88.8 dev tun2

Ping测试(都不通,没找到原因,有解决的朋友留言告知一下)

# ip netns exec ns1 ping 192.168.99.9

PING 192.168.99.9 (192.168.99.9) 56(84) bytes of data.

^C

--- 192.168.99.9 ping statistics ---

6 packets transmitted, 0 received, 100% packet loss, time 5030ms



# ip netns exec ns2 ping 192.168.88.8

PING 192.168.88.8 (192.168.88.8) 56(84) bytes of data.

^C

--- 192.168.88.8 ping statistics ---

4 packets transmitted, 0 received, 100% packet loss, time 3012ms

查看这个tun设备的信息

# ip netns exec ns1 ifconfig -a

...

tun1: flags=209<UP,POINTOPOINT,RUNNING,NOARP>  mtu 1480

        inet 192.168.88.8  netmask 255.255.255.255  destination 192.168.99.9

        tunnel   txqueuelen 0  (IPIP Tunnel)

        RX packets 0  bytes 0 (0.0 B)

        RX errors 0  dropped 0  overruns 0  frame 0

        TX packets 347  bytes 29148 (28.4 KiB)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

可以看到,tun1是一个ipip隧道的一个端点,IP是192.168.88.8,其对端IP是192.168.99.9。

再看看路由表

# ip netns exec ns1 route -n

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

192.168.12.0    0.0.0.0         255.255.255.0   U     0      0        0 tap1

192.168.22.0    192.168.12.1    255.255.255.0   UG    0      0        0 tap1

192.168.99.9    0.0.0.0         255.255.255.255 UH    0      0        0 tun1

路由表结果告诉我们,到达目的地192.168.99.9的路由的一个直连路由直接从tun1出去即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章