本文旨在通过全部手动的方式,来完成DVR的手动部署。大体思路是,在一个DVR的环境里,手动创建虚拟机,手动创建各种Linux网络设备,手动添加各种流表,完成新建虚拟机和在DVR模式下的虚拟机的DHCP,二层,三层通信。
拓扑结构和机器角色信息
拓扑结构
机器角色信息
Server-233:Allinone节点,上面运行着Neutron Server,DHCP agent,L3 agent,OVS agent等
Server-64:Compute节点,上面运行着L3 agent,OVS agent等
Server-68:Compute节点,上面运行着L3 agent,OVS agent等
这三台机器之间通过p3p1.1124进行VXLAN隧道的通信。
Human Defined DVR之DHCP
手动创建虚拟机
下载镜像
wget http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img
镜像格式转化(img->qcow2)
qemu-img convert -O qcow2 cirros-0.3.4-x86_64-disk.img cirros-0.3.4-x86_64-disk.qcow2
创建XML模板
Expand source
通过Libvirt启动虚拟机
在通过Libvirt启动虚拟机之前,由于我们在XML文件中定义了虚拟机的TAP设备会挂载到Linux Bridge qbr-cirros下,因此我们手动创建这个桥
brctl addbr qbr-cirros
ok,启动虚拟机
virsh define cirros.xml
virsh start cirros
验证虚拟机是否正常发包
在虚拟机里DHCP,并在tap设备上抓包
虚拟机里发包:
sudo ifdown eth0; sudo ifup eth0
宿主机上抓包:
[root@server-233 ~]# tcpdump -nei tap-cirros
tcpdump: WARNING: tap-cirros: no IPv4 address assigned
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tap-cirros, link-type EN10MB (Ethernet), capture size 65535 bytes
15:57:59.003843 fa:16:3e:ff:aa:66 > Broadcast, ethertype IPv4 (0x0800), length 332: 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from fa:16:3e:ff:aa:66, length 290
ok,一切正常。
配置虚拟机用到的网络设备
设置网桥转发延迟为0s:
brctl setfd qbr-cirros 0
关闭生成树:
brctl stp qbr-cirros off
关闭网桥的multicat_snoop
echo 0 |tee /sys/class/net/qbr7eb6afbb-69/bridge/multicast_snooping
创建Linux Veth设备
ip link add qvb-cirros type veth peer name qvo-cirros
UP网络设备
ip link set qvo-cirros up
设置qvb设备为混杂模式
ip link set qvb-cirros promisc on
UP网络设备
ip link set qvb-cirros up
桥接网络设备
brctl addif qbr-cirros qvb-cirros
把qvo设备桥接到br-int上
ovs-vsctl add-port br-int qvo-cirros
ok,现在的拓扑是这样的:
我们想让虚拟机获取IP为192.168.10.168/24的地址,如何才能让虚拟机获取呢?
下面介绍两种方式:
一. DHCP Server在本节点上
DHCP Server里的TAP设备对应的VLAN tag是1,那么也把qvo设备的tag设置成1:
ovs-vsctl set Port qvo-cirros tag=1
接下来,手动修改DHCP配置文件
host文件:
addn_hosts文件:
修改后,给Dnsmasq服务发送reload信号:
kill -HUP [pid]
虚拟机DHCP即可获取IP地址
二. DHCP Server不在本节点上
方式一比较简单,下面介绍当DHCP Server不在本节点上时的虚拟机DHCP过程。
首先,由于qvo设备直接桥接在了br-int上,我们将qvo的流量直接导到br-tun上,添加如下流表:
ovs-ofctl add-flow br-int priority=1,actions=NORMAL
然后,由于br-int和br-tun有patch口相连接,那么,虚拟机的DHCP请求包就会到达br-tun的table0上,我们让从patch口来的数据包,resubmit table1,
table1上默认的规则是resubmit table2:
ovs-ofctl add-flow br-tun table=0,priority=1,in_port=1,actions=resubmit(,1)
ovs-ofctl add-flow br-tun table1,priority=0,actions=resubmit(,2)
在table2上,对于请求的单播地址,resubmit table 20
ovs-ofctl add-flow br-tun table=2,priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00,actions=resubmit(,20)
对于请求目的地址是广播地址的,resubmit table 22
ovs-ofctl add-flow br-tun table=2,priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00,actions=resubmit(,22)
对于DHCP请求,是广播地址,会resubmit到table 22
在table22上做VLAN和VXLAN的转化,发到其他两个计算节点:
ovs-ofctl add-flow br-tun table=22,dl_vlan=1,actions=strip_vlan,set_tunnel:0x813,output:2,output:3
注,0x813的来历:
ok,我们抓到了DHCP请求,我们在br-tun上添加流表,让从233(in_port是3,可以通过ovs-ofctl dump-ports-desc br-tun看到port)上发来的数据包resubmit table4:
ovs-ofctl add-flow br-tun table=0,priority=1,in_port=3,actions=resubmit(,4)
我们在table4上完成VXLAN和本地VLAN的转换,然后resubmit table 9:
ovs-ofctl add-flow br-tun table=4,priority=1,tun_id=0x813 actions=mod_vlan_vid:1,resubmit(,9)
table9默认resubmit table 10
ovs-ofctl add-flow br-tun table=9,priority=0,actions=resubmit(,10)
table10上写MAC学习的流表,然后output到br-int的patch口
ovs-ofctl add-flow br-tun table=10,priority=1,actions=learn(table=20,hard_timeout=300,priority=1,cookie=0xb8a7772eecbcd5b8,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:0->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]),output:1
这样br-int上就会收到这个带有本地VLAN1的DHCP请求了,这是仍然不能获取IP,因为DHCP Server里没有写租约信息,在本节点上进行和233上一样的动作
图略
此时在虚拟机里DHCP一把:
搞定。
Human Defined DVR之虚拟机二层通信
下面介绍如何手动实现DVR场景下的虚拟机二层通信。首先看一张拓扑图:
我们要实现cirros虚拟机和VM-1、VM-2的通信。
Cirros和本地虚拟机的二层通信
因为本地的虚拟机VM-1在br-int上的Tag是1,那么我们首先在qvo-cirros上打上Tag为1
ovs-vsctl set Port qvo-cirros tag=1
虚拟机里开ping,咦,怎么不通?
抓下VM-1的qvo设备上的包和tap设备上的包看下
[root@server-233 ~(keystone_admin)]# tcpdump -nei tapd65c7b33-e0 arp or icmp
tcpdump: WARNING: tapd65c7b33-e0: no IPv4 address assigned
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tapd65c7b33-e0, link-type EN10MB (Ethernet), capture size 65535 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel
[root@server-233 ~(keystone_admin)]# tcpdump -nei qvod65c7b33-e0 arp or icmp
tcpdump: WARNING: qvod65c7b33-e0: no IPv4 address assigned
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on qvod65c7b33-e0, link-type EN10MB (Ethernet), capture size 65535 bytes
09:48:35.290981 fa:16:3e:ff:aa:66 > fa:16:3e:ff:3a:67, ethertype IPv4 (0x0800), length 98: 192.168.10.222 > 192.168.10.10: ICMP echo request, id 27649, seq 95, length 64
09:48:36.291047 fa:16:3e:ff:aa:66 > fa:16:3e:ff:3a:67, ethertype IPv4 (0x0800), length 98: 192.168.10.222 > 192.168.10.10: ICMP echo request, id 27649, seq 96, length 64
09:48:37.291100 fa:16:3e:ff:aa:66 > fa:16:3e:ff:3a:67, ethertype IPv4 (0x0800), length 98: 192.168.10.222 > 192.168.10.10: ICMP echo request, id 27649, seq 97, length 64
^C
3 packets captured
3 packets received by filter
0 packets dropped by kernel
天呐!典型的安全组没有允许ICMP的原因呀!因为qvo设备上有包,tap设备上没包。
果断找到对端虚拟机的Port对应的安全组,并添加相应的ICMP规则:
[root@server-233 ~(keystone_admin)]# neutron port-list –device-id 764bacc1-dc11-42ea-bc12-54334c31efb2
+————————————–+——+——————-+————————————————————————————–+
| id | name | mac_address | fixed_ips |
+————————————–+——+——————-+————————————————————————————–+
| d65c7b33-e05b-4a3e-aaa5-ba9ec3b8dd1b | | fa:16:3e:ff:3a:67 | {“subnet_id”: “bb6d2cda-c31d-40f5-9d00-50f9b6540753″, “ip_address”: “192.168.10.10”} |
+————————————–+——+——————-+————————————————————————————–+
[root@server-233 ~(keystone_admin)]# neutron port-show d65c7b33-e05b-4a3e-aaa5-ba9ec3b8dd1b|grep security
| security_groups | 4616d6e2-3491-43cc-9411-9463b20d1a11 |
[root@server-233 ~(keystone_admin)]# neutron security-group-rule-create –protocol=icmp 4616d6e2-3491-43cc-9411-9463b20d1a11
Created a new security_group_rule:
+——————-+————————————–+
| Field | Value |
+——————-+————————————–+
| direction | ingress |
| ethertype | IPv4 |
| id | 44a52ecc-83ca-4c19-a84d-506ab3753d26 |
| port_range_max | |
| port_range_min | |
| protocol | icmp |
| remote_group_id | |
| remote_ip_prefix | |
| security_group_id | 4616d6e2-3491-43cc-9411-9463b20d1a11 |
| tenant_id | ef979882f1954a0fa4ce7daf244aa557 |
+——————-+————————————–+
加完后,搞定!ping通了
Cirros和其他计算节点上虚拟机的二层通信
依然给qvo-cirros上打上Tag为1
ovs-vsctl set Port qvo-cirros tag=1
这里需要说明一点,为了防止虚拟机进行中间人攻击,即:虚拟机对目的IP不是自己的请求进行回复,我们通过流表的形式进行防止,规则如下:
ovs-ofctl add-flow br-int “priority=10,arp,in_port=43,actions=resubmit(,24)”
ovs-ofctl add-flow br-int “table=24,priority=2,arp,in_port=43,arp_spa=192.168.10.222,actions=NORMAL”
其中,43是Cirros虚拟机的ofport,这样获得:
[root@server-233 ~(keystone_admin)]# ovs-vsctl get Interface qvo-cirros ofport
43
arp_spa的含义是:进行ARP回复的原地址是192.168.10.222
OK,开ping!通了!