码迷,mamicode.com
首页 > Web开发 > 详细

Neutron 理解 (6): Neutron 是怎么实现虚拟三层网络的 [How Neutron implements virtual L3 network]

时间:2015-07-21 09:05:16      阅读:540      评论:0      收藏:0      [点我收藏+]

标签:

 学习 Neutron 系列文章:

(1)Neutron 所实现的虚拟化网络

(2)Neutron OpenvSwitch + VLAN 虚拟网络

(3)Neutron OpenvSwitch + GRE/VxLAN 虚拟网络

(4)Neutron OVS OpenFlow 流表 和 L2 Population

(5)Neutron DHCP Agent

(6)Neutron L3 Agent  

(7)Neutron LBaas

    Neutron 对虚拟三层网络的实现是通过其 L3 Agent (neutron-l3-agent)。该 Agent 利用 Linux IP 栈、route 和 iptables 来实现内网内不同网络内的虚机之间的网络流量,以及虚机和外网之间网络流量的路由和转发。为了在同一个Linux 系统上支持可能的 IP 地址空间重叠,它使用了 Linux network namespace 来提供隔离的转发上下文。

1. 基础知识

1.1 Linux network namespace

1.1.1 概念和操作

    在二层网络上,VLAN 可以将一个物理交换机分割成几个独立的虚拟交换机。类似地,在三层网络上,Linux network namespace(netns) 可以将一个物理三层网络分割成几个独立的虚拟三层网络。

   Network namespace (netns)从 Linux 2.6.24 版本开始添加,直到 2.6.29 添加完成。每个 netns  拥有独立的 (virtual)network devices, IP addresses, IP routing tables, /proc/net directory, ports 等等。新创建的 netns 默认只包含 loopback device。除了这个设备,每个 network device,不管是物理的还是虚拟的网卡还是网桥等,都只能存在于一个 netns。而且,连接物理硬件的物理设备只能存在于 root netns。其它普通的网络设备可以被创建和添加到某个 netns。

   使用 ip 命令来操作 netns。

#添加 network namespace
ip netnas add <network namespace name>
#Example:
ip netns add nstest

#列表所有 netns
ip netns list

#删除某 netns
ip netns delete <network namespace name>

#在 network namespace 中运行命令
ip netns exec <network namespace name> <command>
#Example using the namespace from above:
ip netns exec nstest ip addr

#添加 virtual interfaces 到 network namespace
ip link add veth-a type veth peer name veth-b #创建一对虚拟网卡veth-a 和 veth-b,两者由一根虚拟网线连接
#将 veth-b 添加到 network namespace
ip link set veth-b netns nstest

#设置 VI 的 IP 地址
#defaut namespace 中
ip addr add 10.0.0.1/24 dev veth-a
ip link set dev veth-a up

# namespace nstest 中
ip netns exec nstest ip addr add 10.0.0.2/24 dev veth-b
ip netns exec nstest ip link set dev veth-b up
#互通
# ping 10.1.1.1 PING 10.1.1.1 (10.1.1.1) 56(84) bytes of data. 64 bytes from 10.1.1.1: icmp_seq=1 ttl=64 time=0.087 ms # ip netns exec netns1 ping 10.1.1.2 PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data. 64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.054 ms
#查看路由表和 iptbales
# ip netns exec netns1 route # ip netns exec netns1 iptables -L

1.1.2 namespace 间的通信

(1)一种简单的方式是使用 Linux veth pair 来实现两个 network namespace 之间的通信:

技术分享

(2)当有两个以上的 network namespace 之间需要通信时,需要使用一个虚机交换机,和两个 veth pair。传统的方式是 Linux bridge:

技术分享

你也可以使用 Open vSwitch:

技术分享

(3)再一种方式是使用 Open vSwitch 和 OVS internal ports:

技术分享

来源,可见详细的配置命令。)

veth (irtual Ethernet interfaces) 设备:这是一种成对出现的特殊网络设备,它们象一根管道一样连接在一起。VETH 设备总是成对出现,送到一端请求发送的数据总是从另一端以请求接受的形式出现。该设备不能被用户程序直接操作,但使用起来比较简单。创建并配置正确后,向其一端输入数据,VETH 会改变数据的方向并将其送入内核网络核心,完成数据的注入。在另一端能读到此数据。

关于几种方式的性能比较,这篇文章也给出了它的测试结论:

  • 使用 OVS patch ports:性能更好
  • 不要使用 Linux veth pairs:它会带来明显的性能下降

在 Neutron 中,可以使用配置项 ovs_use_veth 来配置是否使用 veth,默认为 false,表示默认使用 OVS internal port。

1.2 iptables

1.2.1 基本概念

   netfilter/iptables(简称为iptables)组成 Linux 平台下的包过滤防火墙。其中,iptables只是 Linux 防火墙的管理工具,位于/sbin/iptables。真正实现防火墙功能的是netfilter,它是Linux内核中实现包过滤的内部结构。

   iptables 是一个 CLI 类型的 Linux 用户空间工具,它使得系统管理员能够配置 Linux kenerl firewall 的表(tables)、链(chains)和规则(rules)。Linux 使用不同的内核模块和应用来管理不同的网络协议iptable 适用于 ipv4,ip6tables 适用于 ipv6,arptables 适用于 ARP,ebtables 适用于网络帧。iptales 需要管理员权限。当要在你的机器上建立一个连接时,iptables 查询是否满足它已配置的规则。如果不存在配置的规则,那么转到默认的行为。

  规则(rules)其实就是网络管理员预定义的条件,规则一般的定义为“如果数据包头符合这样的条件,就这样处理这个数据包”。规则存储在内核空间的信息包过滤表中,这些规则分别指定了源地址、目的地址、传输协议(如TCP、UDP、ICMP)和服务类型(如HTTP、FTP和SMTP)等。当数据包与规则匹配时,iptables就根据规则所定义的方法来处理这些数据包,如放行(accept)、拒绝(reject)和丢弃(drop)等。配置防火墙的主要工作就是添加、修改和删除这些规则。

操作 iptables 服务:

# /etc/init.d/iptables start/stop/restart

iptables 中rule 的组织:iptables -> Tables -> Chains -> Rules

技术分享

命令(-A,-D 等):

技术分享

来源 

iptables [-t table] {-A|-C|-D} chain rule-specification #A - 添加 add,C - 检查 check,D - 删除 delete
iptables [-t table] -I chain [rulenum] rule-specification # I - 插入 insert
iptables [-t table] -R chain rulenum rule-specification # R - 替换 replace
iptables [-t table] -D chain rulenum # D - 删除 delete
iptables [-t table] -S [chain [rulenum]] # S - list
iptables [-t table] {-F|-L|-Z} [chain [rulenum]] [options...] 
iptables [-t table] -N chain # N - 创建一个新的chain
iptables [-t table] -X [chain] #X - 删除一个 chain
iptables [-t table] -P chain target # P - 设置 target 的 policy
iptables [-t table] -E old-chain-name new-chain-name # E - rename

参数判断(-p,-s 等):

 技术分享

来源) 

  • -p, --protocol protocol: tcp, udp, udplite, icmp, esp, ah, sctp 之一
  • -s, --source address[/mask] a network name, a hostname, a network IP address (with /mask), or a plain IP address.
  • -d, --destination address[/mask][,...]:同 -s
  • -j, --jump target:match 后的 target。
  • -g, --goto chain
  • [!] -i, --in-interface name:连接进来的 interface 名称。!表示否。
  • [!] -o, --out-interface name

处理结果(-j):

    当数据包进入后,会依次比照 iptables 中的每条规则,直到有一条规则可以对该报文进行匹配,这时该报文将被执行"ACCEPT","DORP","REJECT" 或者其它动作,除 REDIRECT 外,执行完后停止跟余下的 iptables 规则匹配。

  • -ACCEPT: 将封包放行,进行完此处理动作后,将不再比对其它规则,直接跳往下一个规则链。
  • -REJECT:   拦阻该封包,并传送封包通知对方。
  • -DROP: 丢弃封包不予处理,进行完此处理动作后,将不再比对其它规则,直接中断过滤程序。
  • -DNAT:DNAT 改写封包目的地 IP 为某特定 IP 或 IP 范围,可以指定 port 对应的范围,进行完此处理动作后,将会直接跳往下一个规则链。
  • -REDIRECT:    将封包重新导向到另一个端口(PNAT),进行完此处理动作后,将会继续比对其它规则。
  • -SNAT: 改写封包来源 IP 为某特定 IP 或 IP 范围,可以指定 port 对应的范围,进行完此处理动作后,将直接跳往下一个规则链。

表(-t):

表功能链功能
Filter 包过滤
FORWARD
过滤发往其它服务器的包(Filters packets to servers accessible by another NIC on the firewall)
INPUT
过滤发往防火墙的包 (Filters packets destined to the firewall)
OUTPUT
过滤离开防火墙的包 (Filters packets originating from the firewall)
Nat 网络地址转换
PREROUTING
在路由前做地址转换,使得目的地址能够匹配上防火墙的路由表,常用于转换目的地址。
POSTROUTING
在路由后做地址转换。这意味着不需要在路由前修改目的地址。常用语转换源地址。
OUTPUT

对防火墙产生的包做地址转换(很少量地用于 SOHO 环境中)

Mangle TCP 头修改
PREROUTING
POSTROUTING
OUTPUT
INPUT
FORWARD
在路由器修改 TCP 包的 QoS(很少量地用在 SOHO 环境中)

连接处理过程:

技术分享

  • 路径A:网络包进入Linux 主机使用主机资源。在路由判断后确定是向 Linux 主机要资料的包,由 filter 表的 INPUT 链进行控管。
  • 路径B:网络报进入Linux,但不使用主机资源,而是向后端流动。
  • 路径C:网络包由本机发送出去。先由路由判断,决定输出路径,然后通过 filter 的 OUTPUT 链来传送。

例子:

  • iptables -A INPUT -s 10.10.10.10 -j DROP #丢弃从 10.10.10.10 主机来的所有包
  • iptables -A INPUT -s 10.10.10.0/24 -j DROP #丢弃从 10.10.10.0/24 网段进来所有包
  • iptables -A INPUT -p tcp --dport ssh -s 10.10.10.10 -j DROP # 如果协议是 tcp,目标端口是 ssh 端口,源IP 为 10.10.10.10,那么丢弃它
  • iptables -A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT #接受从 virbr0 进来的所有目标端口 53 的 udp 包
  • iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT #接受 RELEASED 和 ESTABLISHED  状态的连接。Linux 3.7 以后,--state 被替换成了 --conntrack
  • iptables -A FORWARD -d 192.168.122.0/24 -o virbr0 -m state --state RELATED,ESTABLISHED -j ACCEPT #转发时接受这些包
  • iptables -A FORWARD -p icmp -j ACCEPT #转发时接受所有 ICMP 路由包。

详细的说明可以看 这里,以及 这里 和 这里,以及 这里

1.2.2 NAT 的实现

NAT 包括 SNAT (源地址转换)和 DNAT (目的地址转换)。两者的区别在于做地址转换是在路由前还是路由后:

(1)SNAT:路由 - 转换 - 发出

数据经过时, 源地址发生改变,目的地址不变。SNAT 的具体数据流向:

  1. 封包先经过 PREROUTING,检查目的 IP 是不是本网段的地址。是的话,走路径A。
  2. 如果不是,则开始查询路由表,查找到相应路由条目后(查找路由的过程在 PREROUTING 和 FORWARD 之间),经过 FORWARD 链进行转发,再通过 postrouting 时进行NAT转换。

从这里可以看出,SNAT转换的步骤在 POSTROUTING 链上实现, PREROUTING 只是用来做路由选择。因此,要做 SNAT 的话,需要添加 POSTROUTING 规则,使用 “-j SNAT -to-source”。比如:

iptables -t nat -A POSTROUTING -s 192.168.252.0/24 -j SNAT -to-source 100.100.100.1

(2)DNAT:转换 - 路由- 发出

DNAT 的功能正好和 SNAT 相反,源地址不变,目的地址发生改变。DNAT 可以用作 PNAT,可以将一个 IP 的端口转换成另一个IP的另外一个端口号,经常用于内网服务器映射到公网,用来隐藏服务器的真实地址。DNAT 的具体数据流向:

  1. 在 DNAT 中,NAT 是在 PREROUTING 上做的。在数据进入主机后,路由选择过程是在 PREROUTING 和 FORWARD 之间的,所以应该先做地址转换之后,再进行路由选择,而后经过 FORWARD,最后从 POSTROUTING 出去。
  2. 因此,要做 DNAT,需要添加 PREROUTING 规则,使用 “-j DNAT --to-destination”。比如:
iptables -t nat -A PREROUTING -d 100.100.100.1 -p tcp --dport 80 -j DNAT --to-destination 192.168.252.1

技术分享

有一类比较特殊的 DNAT 是使用 “-j REDIRECT” 做端口号转换:

## Send incoming port-80 web traffic to our squid (transparent) proxy
# iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80-j REDIRECT --to-port 3128

关于 SNAT 和 DNAT 的更多解释可以参考 这里 和 这里

1.3 route (Linux 路由表)

Linux 系统的 route 命令用于显示和操作 IP 路由表,它的主要作用是创建一个静态路由来指定一个主机或者一个网络通过一个网络接口,如eth0。

route [-CFvnee]

route [-v] [-A family] add [-net|-host] target [netmask Nm] [gw Gw] [metric N] [mss M] [window W] [irtt I] [reject] [mod] [dyn] [rein-
state] [[dev] If]

route [-v] [-A family] del [-net|-host] target [gw Gw] [netmask Nm] [metric N] [[dev] If]

例子:

  • route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0 #增加一条经过 eth0 到达 244.0.0.0 的路由
  • route add -net 224.0.0.0 netmask 240.0.0.0 reject #增加一条屏蔽的路由,目的地址为224.x.x.x将被拒绝。
  • route del -net 224.0.0.0 netmask 240.0.0.0
  • route del -net 224.0.0.0 netmask 240.0.0.0 reject
  • route del default gw 192.168.120.240
  • route add default gw 192.168.120.240

这个命令比较简单,可以参考 这个

1.4 路由器的辅助(Secondary) IP

先来看一个 Virutal Router 的 interface 的 ip 配置: 
42: qg-3c8d6a68-97: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether fa:16:3e:2e:5b:23 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.110/24 brd 192.168.1.255 scope global qg-3c8d6a68-97
       valid_lft forever preferred_lft forever
    inet 192.168.1.104/32 brd 192.168.1.104 scope global qg-3c8d6a68-97
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe2e:5b23/64 scope link
       valid_lft forever preferred_lft forever

<BROADCAST,UP,LOWER_UP>:端口的各种状态

  • UP: device is functioning (enabled 状态,可通过 ip * up/down 设置。)
  • BROADCAST: device can send traffic to all hosts on the link (能够发广播)
  • MULTICAST: device can perform and receive multicast packets (能够发多播)
  • ALLMULTI: device receives all multicast packets on the link (能够接收多播)
  • PROMISC: device receives all traffic on the link (接收所有的traffic)
  • LOWER_UP:the state of the Ethernet link(表示线已接上) 

inet/brd/scope:IP 地址及子网掩码,广播地址,作用域

scope:

  • global:valid everywhere
  • site:valid only within this site (IPv6)
  • link:valid only on this device
  • host:valid only inside this host (machine)

    注意到这个interface有两个静态 IP 地址。第一个是主要的(primary)IP,第二个是辅助的( secondary) 的 IP。当一个网卡配置了静态IP后,你可以添加secondary  IP 给它。这样它就拥有了多个 IP 地址了。Secondary IP 不可以通过 DHCP 分配。它所有的IP 地址都关联到它唯一的一个 MAC 地址上。那为什么需要 secondary IP 地址呢? 路由器有个 Secondary IP 的概念,这个特性可以创建逻辑子网,也就是说在一个物理网口上连接两个子网,比如这个网口接到一台交换机上,如 果这个网口没有配置Secondary IP的话,那么这台交换机只能连接一个网段的主机,比如 192.168.1.1/24,但是,如果它配置了Secondary IP,那么就可以连接两个网段的主机,比如 192.168.1.1/24 和 10.0.0.1/24。更详细的解释可以看这里 和 这里

命令:

#增加 secondary IP
ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 ip a add dev qg-3c8d6a68-97 192.168.1.105/32 brd 192.168.1.105

#删除 secondar IP
ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 ip addr del 192.168.1.104/32 dev qg-3c8d6a68-97

2. Neutron L3 Agent 的实现原理

    每个 L3 Agent 运行在一个 network namespace 中,每个 namespace 由 qrouter-<router-UUID>命名,比如 qrouter-e506f8fe-3260-4880-bd06-32246225aeae。网络节点如果不支持 Linux namespace 的话,只能运行一个 Virtual Router。也可以通过设置配置项 use_namespaces = True/False 来使用或者不使用 namespace。

   Neutron L3 Agent 负责路由(routing)、浮动 IP 分配(floatingip allocation), 地址转换(SNAT/DNAT)和 Security Group 管理(Blueprint 在这里。在后面的文章中打算和 Nova 中的 Security Group 一起分析)。

2.1 路由 (Routing)

一个 Virtual Router 连接几个 subnet 就会有几个 virtual interface。每个 interface 的地址是该 subnet 的 gateway 地址。比如:

root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 ip addr

33: qr-2aa928c9-e8: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default #IP 设为它连接的子网的 Gateway IP
link/ether fa:16:3e:90:e5:50 brd ff:ff:ff:ff:ff:ff
inet 91.1.180.1/24 brd 91.1.180.255 scope global qr-2aa928c9-e8
37: qr-a5c6ed86-c1: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/ether fa:16:3e:87:40:f3 brd ff:ff:ff:ff:ff:ff
inet 81.1.180.1/24 brd 81.1.180.255 scope global qr-a5c6ed86-c1
48: qg-3c8d6a68-97: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/ether fa:16:3e:2e:5b:23 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.110/24 brd 192.168.1.255 scope global qg-3c8d6a68-97

    L3 Agent 在启动时设置如下的路由规则:

root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 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 0 0 0 qg-3c8d6a68-97
81.1.180.0 0.0.0.0 255.255.255.0 U 0 0 0 qr-a5c6ed86-c1 #到哪个网段的traffic发到相应的 interface
91.1.180.0 0.0.0.0 255.255.255.0 U 0 0 0 qr-2aa928c9-e8
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 qg-3c8d6a68-97

   虚机的 IP 栈在发现数据包的目的虚机的 IP 地址不在自己网段的话,会将其发到 Router 上对应其 subnet 的 Virtual Interface。然后,Virtual Router 根据配置的路由规则和目的IP地址,将包转发到目的端口发出。

2.2 源地址转换 SNAT

2.2.1 Neutron 的 SNAT iptables chains

在没有设置浮动 IP 时,当主机访问外网时,需要将主机的固定 IP 转换成外网网段的 gateway 的 IP 地址,以免暴露内部 IP 地址。其做法是 Neutron 在 iptables 表中增加了 POSTROUTING 链。

root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N neutron-l3-agent-OUTPUT      #Neutorn 增加的 chain
-N neutron-l3-agent-POSTROUTING #Neutorn 增加的 SNAT chain
-N neutron-l3-agent-PREROUTING
-N neutron-l3-agent-float-snat  #Neutorn 增加的 SNAT chain
-N neutron-l3-agent-snat        #Neutorn 增加的 SNAT chain
-N neutron-postrouting-bottom   #Neutorn 增加的 SNAT chain
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A POSTROUTING -j neutron-l3-agent-POSTROUTING #(1)将 SNAT chain 转到自定义的 neutron-l3-agent-POSTROUTING
-A POSTROUTING -j neutron-postrouting-bottom   #(3)将 SNAT chain 转到自定义的 neutron-postrouting-bottom
-A neutron-l3-agent-POSTROUTING ! -i qg-3c8d6a68-97 ! -o qg-3c8d6a68-97 -m conntrack ! --ctstate DNAT -j ACCEPT #(2)如果出口或者入口不是 qg-3c8d6a68-97 并且状态不是 DNAT 的都接受
-A neutron-l3-agent-snat -j neutron-l3-agent-float-snat
-A neutron-l3-agent-snat -s 91.1.180.0/24 -j SNAT --to-source 192.168.1.110 #(5)将 91.1.180.0/24 网段的包的目的 IP 转为 192.168.1.110
-A neutron-l3-agent-snat -s 81.1.180.0/24 -j SNAT --to-source 192.168.1.110 #(5)将 91.1.180.0/24 网段的包的目的 IP 转为 192.168.1.110
-A neutron-postrouting-bottom -j neutron-l3-agent-snat                      #(4)再转到 neutron-l3-agent-snat

而且 route 的外网 gateway interface 上增加了浮动 IP 作为 辅助 IP: 

42: qg-3c8d6a68-97: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether fa:16:3e:2e:5b:23 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.110/24 brd 192.168.1.255 scope global qg-3c8d6a68-97
       valid_lft forever preferred_lft forever
    inet 192.168.1.104/32 brd 192.168.1.104 scope global qg-3c8d6a68-97
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe2e:5b23/64 scope link
       valid_lft forever preferred_lft forever

2.2.2 实验:从虚机  81.1.180.12 ping 外网 192.168.1.15

1. 在 router 的连接 81.1.180.12 网段 interface 上: 

root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 tcpdump -envi qr-a5c6ed86-c1 -vvv
tcpdump: listening on qr-a5c6ed86-c1, link-type EN10MB (Ethernet), capture size 65535 bytes
^C17:42:48.904820 fa:16:3e:2b:3e:2a > fa:16:3e:87:40:f3, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 28892, offset 0, flags [DF], proto ICMP (1), length 84)
    81.1.180.12 > 192.168.1.15: ICMP echo request, id 32769, seq 0, length 64
17:42:48.906601 fa:16:3e:87:40:f3 > fa:16:3e:2b:3e:2a, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 24799, offset 0, flags [none], proto ICMP (1), length 84)
    192.168.1.15 > 81.1.180.12: ICMP echo reply, id 32769, seq 0, length 64
17:42:49.906238 fa:16:3e:2b:3e:2a > fa:16:3e:87:40:f3, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 28893, offset 0, flags [DF], proto ICMP (1), length 84)

2. 在 route 的连接 192.168.1.15 网段 interface 上

root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 tcpdump -envi qg-3c8d6a68-97 -vvv
tcpdump: listening on qg-3c8d6a68-97, link-type EN10MB (Ethernet), capture size 65535 bytes
^C17:44:47.661916 fa:16:3e:2e:5b:23 > 08:00:27:c7:cf:ca, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 28896, offset 0, flags [DF], proto ICMP (1), length 84)
    192.168.1.110 > 192.168.1.15: ICMP echo request, id 33281, seq 0, length 64
17:44:47.663300 08:00:27:c7:cf:ca > fa:16:3e:2e:5b:23, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 36308, offset 0, flags [none], proto ICMP (1), length 84)
    192.168.1.15 > 192.168.1.110: ICMP echo reply, id 33281, seq 0, length 64

可见在外网网段的 interface 收到数据包之前,SRC IP 已经被替换成了外网网段的 Gateway IP 了。 

2.3 目的地址转换 DNAT

    要使外网内的机器能访问虚机,需要设置虚机的浮动IP。浮动 IP 在 Virtual Router 连接的 external network 的 subnet 内分配。注意浮动 IP 只有在 Virtual Router 上配置了 External network subnet gateway 才有意义。

2.3.1 浮动IP分配 

创建浮动IP: 

root@sun:~# neutron floatingip-create Extnet
Created a new floatingip:
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| fixed_ip_address    |                                      |
| floating_ip_address | 10.8.127.11                          |
| floating_network_id | 9c9436d4-2b7c-4787-8535-9835e6d9ac8e |
| id                  | 7b4cee72-ffcd-4484-a5d8-371b23bb3cc3 | 

关联到一个 port: 

root@sun:~# neutron port-list | grep 192.168.10.26
| d74c703e-824a-41b1-b4b3-3cd4edfa22b3 |      | fa:16:3e:14:ff:6d | {"subnet_id": "ccc80588-2b0d-459b-82e9-972ff4291b79", "ip_address": "192.168.10.26"} |
root@sun:~# neutron floatingip-associate 7b4cee72-ffcd-4484-a5d8-371b23bb3cc3 d74c703e-824a-41b1-b4b3-3cd4edfa22b3
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| fixed_ip_address    | 192.168.10.26                        |
| floating_ip_address | 10.8.127.11                          |

    每个浮动 IP 唯一对应一个 Router:浮动IP -> 关联的 Port -> 所在的 Subnet -> 包含该 subnet 以及 external subnet 的 Router。创建浮动 IP 时,在 Neutron 完成数据库操作来分配浮动IP后,它通过 RPC 来通知该浮动IP对应的 router 去设置该浮动IP对应的 iptables 规则。上面的例子中,固定IP 为 ‘192.168.10.26’ 的虚机可以在外网中使用浮动 IP  ‘10.8.127.11’ 来访问了。

2.3.2 Neturon DNAT Chains 

  外网访问虚机时,目的 IP 地址为虚机的浮动 IP 地址,因此,必须由 iptables 将其转化为固定 IP 地址,然后再将它路由到虚机。我们需要关注的是 iptables 的 nat 表的 PREOUTING chain:

root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N neutron-l3-agent-OUTPUT
-N neutron-l3-agent-PREROUTING               #neutron 增加的 DNAT chain
-A PREROUTING -j neutron-l3-agent-PREROUTING # DNAT 由 neutron 新增的 chain 负责处理
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A neutron-l3-agent-OUTPUT -d 192.168.1.104/32 -j DNAT --to-destination #本机访问浮动IP 修改为固定 IP
-A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9697 #将虚机访问 metadata server 的 traffic 端口由 80 改到 9697(由配置项 metadata_port 设置,默认为 9697),那里有 application 在监听。具体内容很深,可以参考这篇文章
-A neutron-l3-agent-PREROUTING -d 192.168.1.104/32 -j DNAT --to-destination 91.1.180.14 #到浮动IP的traffic的目的IP 换成虚机的固定 IP

每个浮动 IP,增加三个规则:

-A neutron-l3-agent-PREROUTING -d <floatingip> -j DNAT --to-destination <fixedip> #从本机访问虚机,Dst IP 由浮动IP该为访问固定IP 
-A neutron-l3-agent-OUTPUT -d <floatingip> -j DNAT --to <fixedip>                 #从别的机器上访问虚机,DST IP 由浮动IP改为固定IP
-A neutron-l3-agent-float-snat -s <fixedip> -j SNAT --to <floatingip>             #虚机访问外网,将Src IP 由固定IP改为浮动IP

这里可以看到当设置了浮动 IP 以后,SNAT 不在使用 External Gateway 的 IP 了,而是使用浮动 IP 。虽然 entires 依然存在,但是因为 neutron-l3-agent-float-snat 比 neutron-l3-agent-snat 靠前而得到执行。

-A neutron-l3-agent-float-snat -s 91.1.180.14/32 -j SNAT --to-source 192.168.1.104
-A neutron-l3-agent-snat -j neutron-l3-agent-float-snat
-A neutron-l3-agent-snat -s 91.1.180.0/24 -j SNAT --to-source 192.168.1.110
-A neutron-l3-agent-snat -s 81.1.180.0/24 -j SNAT --to-source 192.168.1.110
-A neutron-postrouting-bottom -j neutron-l3-agent-snat

2.3.3 实验:从外网192.168.1.15 ping虚机 81.1.180.14 的浮动IP 192.168.1.104

1. 在route 的连接外网网段的interace 上: 

17:58:46.116944 08:00:27:c7:cf:ca > fa:16:3e:2e:5b:23, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 25176, offset 0, flags [DF], proto ICMP (1), length 84)
    192.168.1.15 > 192.168.1.104: ICMP echo request, id 24530, seq 4, length 64
17:58:46.117910 fa:16:3e:2e:5b:23 > 08:00:27:c7:cf:ca, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 23128, offset 0, flags [none], proto ICMP (1), length 84)
    192.168.1.104 > 192.168.1.15: ICMP echo reply, id 24530, seq 4, length 64

 2. 在 router 的连接内网网段的interface上:

root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 tcpdump -envi qr-2aa928c9-e8 -vvv
tcpdump: listening on qr-2aa928c9-e8, link-type EN10MB (Ethernet), capture size 65535 bytes
^C19:46:12.266739 fa:16:3e:90:e5:50 > fa:16:3e:f3:1e:c0, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 53299, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.15 > 91.1.180.14: ICMP echo request, id 2831, seq 1, length 64
19:46:12.269143 fa:16:3e:f3:1e:c0 > fa:16:3e:90:e5:50, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 23157, offset 0, flags [none], proto ICMP (1), length 84)
91.1.180.14 > 192.168.1.15: ICMP echo reply, id 2831, seq 1, length 64

2.4 L3 Agent iptables 完整流程实验

该实验中使用 “ iptables -t nat  -L -nv” 命令来查看每个链上匹配到的数据包数目。

2.4.1 虚机 91.1.180.14 ping 另一个虚机 81.1.180.12

技术分享

可见:

(1)DNAT 匹配到的是默认的 Policy

(2)SNAT 匹配到 “-A neutron-l3-agent-POSTROUTING ! -i qg-3c8d6a68-97 ! -o qg-3c8d6a68-97 -m conntrack ! --ctstate DNAT -j ACCEPT” 规则后就被 Accept 了。

2.4.1 虚机 91.1.180.14 ping 外网 192.168.1.4

技术分享

可见:

(1)DNAT 匹配到的是默认的 Policy ACCEPT

(2)SNAT 匹配到 “-A neutron-l3-agent-float-snat -s 91.1.180.14/32 -j SNAT --to-source 192.168.1.104” 规则后做 SNAT。

2.4.3 外网 192.168.1.4 ping 虚机 192.168.1.104(91.1.180.14)

技术分享

可见:

(1)DNAT 匹配到 “-A neutron-l3-agent-PREROUTING -d 192.168.1.104/32 -j DNAT --to-destination 91.1.180.14” 然后做 DNAT。

(2)SNAT 匹配到的是默认的 Policy ACCEPT。

  为什么结果中显示的 pacakge 数目只是1呢?参考网上文章,对于 SNAT 和 DNAT target 来说,如果一个包被匹配了,那么和它属于同一个流的所有的包都会被自动转换,然后就可以被路由到正确的主机或网络,这么说来,一个流中被匹配的包的数目就是1了。

总结:

技术分享

(图片来源。Neutron 代码分析也可以参考这篇文章。) 

3. Neutron L3 Agent 主要代码结构

L3 Agent 启动后,它有若干个 Workers 去 MQ 中拿数据,然后将数据放进一个内部的 queue 中。它还会启动一个循环线程去queue 中取数据。当发现有 router 相关的操作发生后,即调用 _process_routers_loop 方法去处理获取的数据。

3.1 Router 处理

L3 Agent 的核心是 Router 的处理。

(1)处理 external gateway (比如,external_gateway_added 增加 external gateway:获取该 router 的所有浮动 IP,在agent_conf.external_network_bridge 所指定的外网物理 OVS bridge 上增加一个 tap 设备,名称为 “gq-*”,然后设置其 MAC 地址,MTU 等)

(2)修改路由表 (routes_updated)

(3)在有 external gateway 时,设置 SNAT iptables (_handle_router_snat_rules):先删除当前所有 POSTROUTING 和 snat chains,然后再增加:

#增加 SNAT chain
-N neutron-l3-agent-float-snat
#为 external gateway
-A neutron-l3-agent-POSTROUTING ! -i qg-3c8d6a68-97 ! -o qg-3c8d6a68-97 -m conntrack ! --ctstate DNAT -j ACCEPT

#为每一个子网创建一条 SNAT 规则
-A neutron-l3-agent-snat -s 91.1.180.0/24 -j SNAT --to-source 192.168.1.110 #(5)将 91.1.180.0/24 网段的包的目的 IP 转为 192.168.1.110 -A neutron-l3-agent-snat -s 81.1.180.0/24 -j SNAT --to-source 192.168.1.110 #(5)将 91.1.180.0/24 网段的包的目的 IP 转为 192.168.1.110

(4)如果有 external gateway 的话,处理浮动 IP 的 SNAT/DNAT iptables (process_snat_dnat_for_fip)

#为每一个浮动 IP,以 192.168.1.104 为例
-A neutron-l3-agent-PREROUTING -d 192.168.1.104/32 -j DNAT --to-destination 91.1.180.14 #DNAT
-A neutron-l3-agent-OUTPUT -d 192.168.1.104/32 -j DNAT --to-destination 91.1.180.14 #本机访问
-A neutron-l3-agent-float-snat -s 91.1.180.14/32 -j SNAT --to-source 192.168.1.104 #SNAT

(5)将浮动IP 配置到 external gateway port (process_floating_ip_addresses -> add_floating_ip) 

42: qg-3c8d6a68-97: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether fa:16:3e:2e:5b:23 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.110/24 brd 192.168.1.255 scope global qg-3c8d6a68-97
       valid_lft forever preferred_lft forever
    inet 192.168.1.104/32 brd 192.168.1.104 scope global qg-3c8d6a68-97
       valid_lft forever preferred_lft forever

  看起来,Neutron 使用 Secondary address 应该不是用于连接不同的子网,因为这些 IP 是同一个网段的。参考别的文章,它的作用应该是“用于NAT,转换后地址并非路由器直连地址(利用secondary address 为转换后地址)”。但是,把该 IP 删除后,网络之间的访问并没有受到影响,因此它的作用还待进一步的研究。

3.2 浮动 IP 处理

在创建、绑定、去绑定或者删除浮动IP时,neutron server 首先执行 DB 操作,然后调用 RPC (notify_routers_updated) 去通知指定的 Router:”create_floatingip“,”update_floatingip“,”delete_floatingip“ 等。

Neutron 理解 (6): Neutron 是怎么实现虚拟三层网络的 [How Neutron implements virtual L3 network]

标签:

原文地址:http://www.cnblogs.com/sammyliu/p/4636091.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!