今天想跟大家分享如何通过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控制台执行如下命令进行安装:
代码语言:javascript复制
$> 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创建脚本。
代码语言:javascript复制
$> cp ~/mininet/custom/ topo-2sw-2host.py ~/vm1.py
#修改vm1.py
$> vi ~/vm1.py
仅对添加Hosts和Switches以及Links的部分进行修改,修改如下:
代码语言:javascript复制
# 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 )
修改完成后创建拓扑:
代码语言:javascript复制
$> sudo mn --controller remote,ip=192.168.3.30 --custom ~/vm1.py
创建完网络拓扑后,对网络拓扑进行相应的检查,比如创建的端口编号,对应端口的链路,以及当前网桥的流表,知道这些细节对后面流表编程有帮助。
检查创建的端口以及端口
代码语言:javascript复制
mininet> sh ovs-ofctl show s1
检查当前网络链路
代码语言:javascript复制
mininet> net
检查当前网络端口号
代码语言:javascript复制
mininet> sh ovs-vsctl -- --columns=name,ofport list Interface
查看manager和controller的状态是否连接控制器成功
代码语言:javascript复制
mininet> sh ovs-vsctl show
如果没有连接成功,先删除,再重新设置
代码语言:javascript复制
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
代码语言:javascript复制
mininet> sh ovs-vsctl set-controller tcp:192.168.3.30:6633
2.创建隧道
网络拓扑创建完成后,可以登录ODL的dlux界面查看创建的端口以及拓扑情况。如果在mininet上执行ping命令,ODL会下发相关流表到对应的网桥上。开始创建VxLAN隧道,当然我们也可以用命令行进行创建。命令行创建相对来说比较简单,命令行如下:
代码语言:javascript复制
> 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
代码语言:javascript复制
GET: http://192.168.3.30:8282/ovsdb/nb/v3/node
GET结果返回值如下:
代码语言:javascript复制
[
"OVS|192.168.1.10:63344",
"OVS|192.168.2.20:63647"
]
以上端口分别为OVS实例。分别在VM1和VM2上。 2)创建隧道端口(port) 根据返回的节点,依据上面的创建隧道命名,依次创建端口,接口和隧道,其中node后的参数为Get获得的OVS实例。
代码语言:javascript复制
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获得。
代码语言:javascript复制
{
"parent_uuid":"4d2742b0-e5d8-4228-8c56-4a283f50038e",
"row":{
"Port":{
"name":"vtep"
}
}
}
3)创建隧道接口 依然在VM1上创建
代码语言:javascript复制
POST: http:// 192.168.3.30:8282/ovsdb/nb/v2/node/OVS/192.168.1.10:63344/tables/interface/rows
BODY如下,请根据实际情况调整uuid.
代码语言:javascript复制
{
"parent_uuid": "2fdc88be-0908-4738-ae8c-9328aeb4fcc5",
"row":{
"Interface":{
"name":"vtep",
"type":"vxlan"
}
}
}
4)更新隧道配置项的值。 通过Postman的put更新VxLAN的interface的值。注意UUID是interface.
代码语言:javascript复制
PUT: http:// 192.168.3.30:8282/ovsdb/nb/v2/node/OVS/192.168.1.10:63344/tables/interface/rows/${UUIU}
代码语言:javascript复制
{
"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转出。
代码语言:javascript复制
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官方下载。
代码语言:javascript复制
POST: http://192.168.3.30:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:1/table/0/
Body:
代码语言:javascript复制
<?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>
注:完整Postman流表脚本下载
注:这个是本次实验的重点和难点。由于在网上找不到关于流表的DTD的定义,很多字段靠自己揣摩和反复实验,才知道是否是正确的。更难的是,因为最终的效果必须等到12条流表全部正确,才能知道结果是否正确。
注:实验过程中,有时候流表是错误的,但是POST的消息没有反馈任何错误提示,也是试验中的坑。
4.验证网络
可以通过ping命令对网络进行验证。为了确定租户网络是否隔离,可以尝试分别断开相同MAC地址的网络连接,再尝试ping,看是否能达到预期效果。测试命令如下:
在VM2上断开blue2的链接
代码语言:javascript复制
mininet> blue2 ip link set dev blue2-eth0 down
在VM1上租户red1 ping 对端主机
代码语言:javascript复制
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 这个模型类似 mV**了 Q11:Beyond myself 还有就是为每个端口配置规则下发流表的时候,ovsdb api里面有支持检测arp欺骗流表吗,如果想配置这样的流表,我们该如何着手,或者自己写这样的代码 关于ARP欺骗这些,也有很多做攻防的利用SDN方式解决,应该也是通过流表来实现。这方面我没有了解过