邓晓涛,当前就职于江苏省未来网络创新研究院,是CDN团队的一名研发人员,主要从事SDN相关的研发相关工作。曾就职于三星电子于先行解决方案研发组任高级工程师、思科系统于云协作应用技术部(CCATG)任工程师。
-----------------------------------------------------------------------------------------------------
【分享正文】
今天想跟大家分享如何通过ODL控制器下发流表来创建VxLAN网络。ODL作为当前流行的控制器,已经有广泛的应用。基于ODL提供了丰富的北向接口,使得应用对网络有了更好的掌控。我们为什么需要研究VxLAN网络,VxLAN网络现在已经成为多数据中心网络的解决方案,提供丰富的网络功能,比如更多的租户数量、虚拟机迁移、IP冲突等问题得到有效解决。
一.实验目的
通过本次分享,你将会了解到以下内容:
- 构建VxLAN网络的基本步骤。
- Mininet构建网络拓扑。
- ODL北向接口Restconf的使用。
- XML格式的流表创建
二.实验准备
- Mininet虚拟机:实验中使用mininet-2.2.1-150420-ubuntu-14.04-server-amd64
- VMware Workstation:加载Mininet虚拟机、运行ODL虚拟机。
- OpenDaylight: Lithium版本(Oracle JDK8)。
- Wireshark:抓包分析,建议利用远程方式抓包。
- Postman:发送REST请求。
三.构建环境
实验中共使用了3台虚拟机,如下图所示,VM1和VM2是Mininet虚拟机,VM3运行ODL。
如上图所示,我们所要构建的网络假定VM1和VM2分别代表两个互通的数据中心。颜色不同的host代表不同的租户。假定当前租户RED和BLUE的网络需求如下表,即租户RED和BLUE拥有两个IP和MAC相同的主机,为了在同一个数据中心网络中保证租户间的网络隔离,并且使得租户网络层实现L2通信。
Table1. 租户信息表
DC | Host | IP | MAC | TENANT |
VM1 | red1 | 10.0.0.1/8 | 00:00:00:00:aa:01 | RED |
blue1 | 10.0.0.1/8 | 00:00:00:00:aa:01 | BLUE | |
VM2 | red2 | 10.0.0.2/8 | 00:00:00:00:aa:02 | RED |
blue2 | 10.0.0.2/8 | 00:00:00:00:aa:02 | BLUE |
四.实验过程
实验过程分为以下几个步骤:
- 创建网络拓扑:构建如图1所需要的实验场景。
- 创建隧道:构建VxLAN网络,建立L2通信隧道。
- 下发流表:控制器下发流表,演示流表的工作原理。
- 验证网络:验证VxLAN网络。
1.创建网络拓扑
首先启动三台虚拟机,VM1(192.168.1.10)和VM2(192.168.2.20)是Mininet虚拟机,VM3(192.168.3.30)为安装有ODL的控制器服务器。
启动控制器,安装下列组件。下列组件中虽然不是所有的都是该实验必须的,没有经过仔细的排查。如果运行的环境中已经有安装的组件,可以从列表中忽略。另外,有些组件会与当前的组件有端口占用情况,请仔细查看日子。在VM3上运行ODL,通过karaf控制台执行如下命令进行安装:
Shell
1 2 |
$> feature:install odl-l2switch-all odl-ovsdb-all odl-base-all odl-aaa-authn odl-restconf-all odl-nsf-all odl-adsal-northbound odl-mdsal-apidocs odl-dlux-all $> feature:install odl-ovsdb-southbound-api odl-ovsdb-southbound-impl odl-ovsdb-southbound-impl-rest odl-ovsdb-southbound-impl-ui odl-openflowplugin-all-li odl-openflowplugin-adsal-compatibility-all |
如图1所示的网络部署图,分别启动Mininet虚拟机,复制Mininet的自定义的拓扑文件,并修改如下部分,一下代码为VM1的执行,请根据具体情况执行VM2创建脚本。
Shell
1 2 3 |
$> cp ~/mininet/custom/ topo-2sw-2host.py ~/vm1.py #修改vm1.py $> vi ~/vm1.py |
仅对添加Hosts和Switches以及Links的部分进行修改,修改如下:
Shell
1 2 3 4 5 6 7 |
# Add hosts and switches leftHost = self.addHost( ‘red1‘, ip="10.0.0.1/8", mac="00:00:00:00:aa:01") rightHost = self.addHost(‘blue1‘, ip="10.0.0.1/8", mac="00:00:00:00:aa:01") leftSwitch = self.addSwitch( ‘s1‘ ) # Add links self.addLink( leftHost, leftSwitch ) self.addLink( leftSwitch, rightSwitch ) |
修改完成后创建拓扑:
Shell
1 |
$> sudo mn --controller remote,ip=192.168.3.30 --custom ~/vm1.py |
创建完网络拓扑后,对网络拓扑进行相应的检查,比如创建的端口编号,对应端口的链路,以及当前网桥的流表,知道这些细节对后面流表编程有帮助。
检查创建的端口以及端口
Shell
1 |
mininet> sh ovs-ofctl show s1 |
检查当前网络链路
Shell
1 |
mininet> net |
检查当前网络端口号
Shell
1 |
mininet> sh ovs-vsctl -- --columns=name,ofport list Interface |
查看manager和controller的状态是否连接控制器成功
Shell
1 |
mininet> sh ovs-vsctl show |
如果没有连接成功,先删除,再重新设置
Shell
1 2 3 |
mininet> sh ovs-vsctl del-manager mininet> sh ovs-vsctl del-controller s1 mininet> sh ovs-vsctl set-manager tcp:192.168.3.30:6640 |
设置s1网桥控制器,注意端口号,有的控制器默认端口是6653
Shell
1 |
mininet> sh ovs-vsctl set-controller tcp:192.168.3.30:6633 |
2.创建隧道
网络拓扑创建完成后,可以登录ODL的dlux界面查看创建的端口以及拓扑情况。如果在mininet上执行ping命令,ODL会下发相关流表到对应的网桥上。开始创建VxLAN隧道,当然我们也可以用命令行进行创建。命令行创建相对来说比较简单,命令行如下:
Shell
1 |
> sh ovs-vsctl add-port s1 vtep -- set interface vtep type=vxlan option:remote_ip=192.168.2.20 option:key=flow ofport_request=10 |
如上面命令所示: 在网桥s1上创建端口vtep并在端口下创建同名的接口,接口类型为vxlan,可选参数中指定隧道的remote_ip为192.168.2.20,key=flow表示隧道的VNI是通过流表来指定,ofport_request=10表示创建默认端口号10的端口作为VxLAN通信端口,如果端口被占用,系统自动分配,该端口号在创建流表时会用到。需要注意的是隧道创建是双向的,即是说在一端创建了VTEP,在另一个所指的remote机器上也应创建相应的vxlan端口。 我们接下来不准备用命令行创建,而是用ODL的北向接口创建隧道。在VM3的服务器上利用Postman依次完成以下操作:
1).获得OVS节点
Postman发送Get请求,并在参数列表中添加Basic Auth,默认为 admin/admin
Shell
1 |
GET: http://192.168.3.30:8282/ovsdb/nb/v3/node |
GET结果返回值如下:
Shell
1 2 3 4 |
[ "OVS|192.168.1.10:63344", "OVS|192.168.2.20:63647" ] |
以上端口分别为OVS实例。分别在VM1和VM2上。
2)创建隧道端口(port)
根据返回的节点,依据上面的创建隧道命名,依次创建端口,接口和隧道,其中node后的参数为Get获得的OVS实例。
Shell
1 |
POST: http:// 192.168.3.30:8282/ovsdb/nb/v2/node/OVS/192.168.1.10:63344/tables/port/rows |
BODY内容如下,parent_uuid是s1的UUID,可以通过相关的REST API获得。
Shell
1 2 3 4 5 6 7 8 |
{ "parent_uuid":"4d2742b0-e5d8-4228-8c56-4a283f50038e", "row":{ "Port":{ "name":"vtep" } } } |
3)创建隧道接口
依然在VM1上创建
Shell
1 |
POST: http:// 192.168.3.30:8282/ovsdb/nb/v2/node/OVS/192.168.1.10:63344/tables/interface/rows |
BODY如下,请根据实际情况调整uuid.
Shell
1 2 3 4 5 6 7 8 9 |
{ "parent_uuid": "2fdc88be-0908-4738-ae8c-9328aeb4fcc5", "row":{ "Interface":{ "name":"vtep", "type":"vxlan" } } } |
4)更新隧道配置项的值。
通过Postman的put更新VxLAN的interface的值。注意UUID是interface.
Shell
1 |
PUT: http:// 192.168.3.30:8282/ovsdb/nb/v2/node/OVS/192.168.1.10:63344/tables/interface/rows/${UUIU} |
Shell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "row":{ "Interface":{ "type":"vxlan", "ofport_request":10, "options": ["map", [ ["key","flow"], ["local_ip","192.168.1.10"], ["remote_ip","192.168.2.20"] ] ] } } } |
注:只有等Interface创建好后,对属性设置才有效。如果在创建过程中指定属性值,属性值可能不会被写入。
上述过程描述的是利用OpenDaylight的REST接口创建隧道,相对于命令而言显得复杂。如命令行创建隧道一样,在一个节点创建隧道,统一需要对另一个节点创建隧道,是一个双向的操作过程,在实践过程中,请注意根据实际情况对REST中相应的参数进行修改。值得注意的是:REST请求的接口有时用v2,有时用v3,是由于Lithium版本的v3接口实现了部分功能,但当前的v2版本是向下兼容的。
3.下发流表
当隧道建立完毕,对应的网桥中的流表为空,也就是当前OVS对任何流不做处理,下面我们就通过控制器下发流表,让OVS完成我们所需要的功能:
- 根据端口号为租户设置VNI。
- 根据MAC地址转发相应的包。
- 根据IP地址转发相应的包。
以下流表是需要下发到VM1上OVS的s1的网桥中,以完成整个VxLAN网络的建立。当然目前只是针对4个host构建的拓扑网络,相对比较简单。如果大家有兴趣,可以参考OpenStack的Neutron网络中是如何规划VxLAN的流表,其中涉及到了Mac地址学习等。所以当前直接把Mac地址写入到流表。大家可以根据以下流表看出转发包的逻辑,以端口1的包转发为例解释如下:
第1条:端口1的流为其设置VNI为100,并转到下一级流表处理;
第4条:VNI为100的包,如果其目的地址为aa:01,则从1号口出,即转发给red1。
第6条:VNI为100的包,如果目的地址为00:a2, 这将包从隧道端口10发送出去。
第8条:VNI为100的ARP包,网络地址为本地IP,从端口1转出。
第10条:VNI为100的ARP包,网络地址为远端IP,从隧道端口10转出。
Shell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
table=0,in_port=1,actions=set_field:100->tun_id,resubmit(,1) table=0,in_port=2,actions=set_field:200->tun_id,resubmit(,1) table=0,actions=resubmit(,1) table=1,tun_id=100,dl_dst=00:00:00:00:aa:01,actions=output:1 table=1,tun_id=200,dl_dst=00:00:00:00:aa:01,actions=output:2 table=1,tun_id=100,dl_dst=00:00:00:00:aa:02,actions=output:10 table=1,tun_id=200,dl_dst=00:00:00:00:aa:02,actions=output:10 table=1,tun_id=100,arp,nw_dst=10.0.0.1,actions=output:1 table=1,tun_id=200,arp,nw_dst=10.0.0.1,actions=output:2 table=1,tun_id=100,arp,nw_dst=10.0.0.2,actions=output:10 table=1,tun_id=200,arp,nw_dst=10.0.0.2,actions=output:10 table=1,priority=100,actions=drop |
下面将通过ODL的restconf API来下发流表。根据上面的讲述,对于VM1和VM2,都需要下发12条流,如果用命令行,那么只需要将流表保存于文本中,利用ovs-ofctl add-flows命令可以一次性加入,如果用REST API下发流表,则需要逐条发送。
将文本流表转换成XML格式的流表,请参考ODL官方指南,另外大家可以参考ask.opendaylight.org针对该问题的解决方案。此处列举了流表第1条的例子。更完整的例子可以到SDNLAB官方下载。
Shell
1 |
POST: http://192.168.3.30:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/0/ |
Body:
Shell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <flow xmlns="urn:opendaylight:flow:inventory"> <flow-name>S1T0F1</flow-name> <table_id>0</table_id> <id>1</id> <installHw>false</installHw> <strict>false</strict> <priority>10</priority> <instructions> <instruction> <order>0</order> <apply-actions> <action> <order>0</order> <set-field> <tunnel> <tunnel-id>100</tunnel-id> </tunnel> </set-field> </action> </apply-actions> </instruction> <instruction> <order>1</order> <go-to-table> <table_id>1</table_id> </go-to-table> </instruction> </instructions> <match> <in-port>1</in-port> </match> </flow> |
注:这个是本次实验的重点和难点。由于在网上找不到关于流表的DTD的定义,很多字段靠自己揣摩和反复实验,才知道是否是正确的。更难的是,因为最终的效果必须等到12条流表全部正确,才能知道结果是否正确。
注:实验过程中,有时候流表是错误的,但是POST的消息没有反馈任何错误提示,也是试验中的坑。
4.验证网络
可以通过ping命令对网络进行验证。为了确定租户网络是否隔离,可以尝试分别断开相同MAC地址的网络连接,再尝试ping,看是否能达到预期效果。测试命令如下:
在VM2上断开blue2的链接
Shell
1 |
mininet> blue2 ip link set dev blue2-eth0 down |
在VM1上租户red1 ping 对端主机
Shell
1 |
mininet> red1 ping 10.0.0.2 |
注:验证过程中,特别提醒,租户IP最好不要选用10网段,有可能你的网络本身不通,但是遇到10.0.0.1这样的IP可能误解为网络正常。
五.实验结论
ODL提供了丰富的API对底层的转发设备进行控制,可以基于API对网络进行高效的编排和控制。实验中验证的VxLAN网络也具有实际的应用场景,例如OpenStack的Neutron网络是一个很好的案例。相对于本文介绍的实验,在数据中心间构建VxLAN网络更具挑战性,我们期待有从事这方面研究的朋友一起讨论并分享这方面的经验。
Q&A
Q1:安徽-桥
ODL会下发相关流表到对应的网桥上,网桥要自己设置吗?
会到网桥上。你可以通过ovs-ofctl 查询到 通过POST创建的流表。ovs-vsctl show出来通过Post创建的port和interface.
Q2:心随风飞
我想请问下,在你做的实验里,流表中的参数都需要手动制定,包括流表下发。这些动作能否都自动实现呢?
流表中的参数都是属于业务逻辑。比如你要创建100个租户的,那么你的NID就需要规划了。我们利用ODL创建流表,也就是通过编程的方式实现网络的编排。演示过程只是表示ODL可以这么做。所以自动实现是没有问题的。
Q3:新疆-m0ster
我做过VXLAN使用命令行生成隧道的实验,OVS后面的模拟租户通过隧道成功的互通了,这个过程没有流表的下发,它是怎么实现互通的?
你创建隧道的过程中的一个参数 key=flow这个参数没有指定,对吧。默认所有的流表都属于一个租户。所以是没有租户概念的。本实验中体现除了租户隔离的这个功能,所以用了key=flow这个参数,也就是让流表对隧道VNI进行指定。
Q4:如之何
想问下,如果租户自己本身就有几个vlan, 要怎么通过vxlan互通呢?
租户的VLAN有VLAN_ID,那么这个可以通过流表的Modify操作来进行Mapping,转换成VNI。只要租户想通信的网络拥有相同的VNI就没有问题。你可以参考OpenStack的相关流表。比如在OpenStack中,你可以在同一个虚拟路由上创建多个子网,每个子网属于一个VLAN,那么一个vRouter出去后,则属于同一个VNI,那么这些子网是可以相互通信的,就算这些计算节点是分布的。
Q5:大连-吉祥
如何用控制器控制网络中链路的带宽,时延,抖动等qos参数
你的问题我也遇到过,可以参考https://wiki.opendaylight.org/view/OpenDaylight_OpenFlow_Plugin:End_to_End_Flows由于没有官方的XML的流表DTD说明文档,有时候只能靠摸索,然后通过查流表中的参数来验证配置是否正确。但是很遗憾的是,ODL在容错处理上不友好,比如有时候流表的XML参数配置错误,但你POST状态显示成功。
Q6:成都-东风
这个比上次的清楚些老,上次完全坐飞机。初学者感到很费解的,普通交换机二层转发自动通过ARP应答,三层通过IP和网关,现在SDN了难道每一个2层通讯动作都要这么复杂,一个过程就要写那么多条流表?还要事先知道Mac地址?
你的问题很好!你可以参考OpenStack的Neutron网络,它在实现长采用了Mac自学习的方式来配置流表。有兴趣的同学可以去了解如何通过学习Mac地址来简化流表,很好的问题。关于ARP应答,这个就比较复杂,在这里不深入展开,有兴趣可以参考OpenStack的虚拟路由的实现。
Q7:新疆-m0ster
使用命令行创建VXLAN隧道的时候也没有指定VNI信息,当时我就觉得很费解,理论VXLAN支持1600万,这个量体现在哪?
那个1600万就体现在VNI上
Q8:Beyond myself
分享过程中涉及到创建vxlan网络,构建l2 tunnel隧道,odl代码实现过程存在add br-int 和br-tun网桥这些网桥吗
ODL通过Rest API创建 Brige, Port和Interface是机上是操作OVSDB的表。在实例中,VXLAN的port同样是用Post创建的,所以应该没有问题。只是本例中将部分过程省略。
Q9:成都-东风
从刚才的Vxlan配置看也很不复杂,但最初的包转发就一大堆流表需要写?这才2台VM,如果大型数据中心咋办?
你说得对。当然只有增加节点是会下发相关流表。另一方面,前面回答过,也有学习Mac地址的实现方式,请参考OpenStack的Neutron网络中如何实现学习流表的。Q10:小柠檬
vxlan是解决数据中心vlan-id不够分的问题吗
VxLAN解决 VLAN很多的问题,不只是ID不够分,vxlan 一个是扩展vlan 数 另外是建立l2over l3问题 还设计 unicast muticast mapping 问题 到了multicast 这个模型类似 mvpn了Q11:Beyond myself
还有就是为每个端口配置规则下发流表的时候,ovsdb api里面有支持检测arp欺骗流表吗,如果想配置这样的流表,我们该如何着手,或者自己写这样的代码
关于ARP欺骗这些,也有很多做攻防的利用SDN方式解决,应该也是通过流表来实现。这方面我没有了解过。-----------------------------------------------------------------------------------------------------
OpenDayLight SDNLAB研究群(群号:194240432)定位为面向SDN/ODL相关技术的初学者进行交流、学习、分享,吸引了来自高校、云服务提供商、互联网厂商、设备厂商、运营商等单位的从业人员近千人,每周会组织定向的技术及业界动态分享,如果你有需要分享的请加Q:117511567联系。