第一步 生成实验拓扑
实验网络拓扑#!/usr/bin/python
from mininet.net import Mininet
from mininet.topo import Topo
from mininet.log import lg, setLogLevel
from mininet.cli import CLI
from mininet.node import RemoteController
CORES = {
'SEA': {'dpid': '000000000000010%s'},
'SFO': {'dpid': '000000000000020%s'},
'LAX': {'dpid': '000000000000030%s'},
'ATL': {'dpid': '000000000000040%s'},
'IAD': {'dpid': '000000000000050%s'},
'EWR': {'dpid': '000000000000060%s'},
'SLC': {'dpid': '000000000000070%s'},
'MCI': {'dpid': '000000000000080%s'},
'ORD': {'dpid': '000000000000090%s'},
'CLE': {'dpid': '0000000000000a0%s'},
'IAH': {'dpid': '0000000000000b0%s'},
}
FANOUT = 4
class I2Topo(Topo):
def __init__(self, enable_all = True):
"Create Internet2 topology."
# Add default members to class.
super(I2Topo, self).__init__()
# Add core switches
self.cores = {}
for switch in CORES:
self.cores[switch] = self.addSwitch(switch, dpid=(CORES[switch]['dpid'] % '0'))
# Add hosts and connect them to their core switch
for switch in CORES:
for count in xrange(1, FANOUT + 1):
# Add hosts
host = 'h_%s_%s' % (switch, count)
ip = '10.0.0.%s' % count
mac = CORES[switch]['dpid'][4:] % count
h = self.addHost(host, ip=ip, mac=mac)
# Connect hosts to core switches
self.addLink(h, self.cores[switch])
# Connect core switches
self.addLink(self.cores['SFO'], self.cores['SEA'])
self.addLink(self.cores['SEA'], self.cores['SLC'])
self.addLink(self.cores['SFO'], self.cores['LAX'])
self.addLink(self.cores['LAX'], self.cores['SLC'])
self.addLink(self.cores['LAX'], self.cores['IAH'])
self.addLink(self.cores['SLC'], self.cores['MCI'])
self.addLink(self.cores['MCI'], self.cores['IAH'])
self.addLink(self.cores['MCI'], self.cores['ORD'])
self.addLink(self.cores['IAH'], self.cores['ATL'])
self.addLink(self.cores['ORD'], self.cores['ATL'])
self.addLink(self.cores['ORD'], self.cores['CLE'])
self.addLink(self.cores['ATL'], self.cores['IAD'])
self.addLink(self.cores['CLE'], self.cores['IAD'])
self.addLink(self.cores['CLE'], self.cores['EWR'])
self.addLink(self.cores['EWR'], self.cores['IAD'])
if __name__ == '__main__':
topo = I2Topo()
ip = '127.0.0.1'
port = 6633
c = RemoteController('c', ip=ip, port=port)
net = Mininet(topo=topo, autoSetMacs=True, xterms=False, controller=None)
net.addController(c)
net.start()
print "Hosts configured with IPs, switches pointing to OpenVirteX at %s:%s" % (ip, port)
CLI(net)
net.stop()
上述代码为网络拓扑生成,每个交换机的名字和DPID对应在CORES列表中,%s此时对应为0。并且每个交换机的连接了4个主机,每个主机MAC地址编号取对应交换机DPID后6字节,最后一字节以主机顺序号代替,如SFO交换机连接的第三个主机MAC地址为00:00:00:00:02:03。IP地址都是从10.0.0.1到10.0.0.4。
第二步 运行OVX
在第一步mininet中生成网络拓扑后,会有提示没有连接到控制器,运行
$ cd OpenVirteX/scripts
$ sh ovx.sh
则启动了OVX,并自动连接上mininet
第三步 确定租户网络
在配置网络之前,需要先确定自己要定义的OVX虚拟租户网络拓扑结构。
目标租户网络要配置这样一个租户网络,SEA的主机1和LAX的主机2通过SFO互通
可以验证,在mininet中ping两个主机会失败,因为OVX不知道他们的数据属于哪一个网络,简单丢弃数据包。
第四步 网络配置
确定好目标,就要一步步配置网络,使用命令行工具 ovxctl,可以通过JOSN API 对OVX进行配置。先对该工具简单了解
$ python ovxctl.py ‐‐help ```
Options:
-h HOST, --hostname=HOST
Specify the OpenVirteX host; default='localhost'
-p PORT, --port=PORT
Specify the OpenVirteX web port; default=8080
-u OVX_USER, --user=OVX_USER
OpenVirtex admin user; default='admin'
-n, --no-passwd Run ovxctl with no password; default false
-v, --version
--help
Available commands are:
addControllers Adds controllers to a virtual switch
connectHost Connect host to a virtual port
connectLink Connect two virtual ports through a virtual link
connectRoute Connect two virtual ports inside a virtual big-switch
createNetwork Creates a virtual network
createPort Create virtual port
createSwitch Create virtual switch
disconnectHost Disconnect host from a virtual port
disconnectLink Disconnect link between two virtual ports
disconnectRoute Disconnect big-switch internal route between two virtual ports
getPhysicalFlowtable Get the physical flowtable of a specified switch or all switches
getPhysicalHosts Get a list of physical hosts
getPhysicalTopology Get the physical topology
getVirtualAddressMapping Get the virtual to physical address mapping for a specified virtual network
getVirtualFlowtable Get the flowtable in the specified virtual network
getVirtualHosts Get list of hosts in virtual network
getVirtualLinkMapping Get the virtual to physical link mapping
getVirtualSwitchMapping Get the virtual to physical switch mapping
getVirtualTopology Get the virtual topology
listVirtualNetworks Get a list of all virtual network tenant ID's
removeNetwork Remove a virtual network
removePort Remove virtual port
removeSwitch Remove virtual switch
setInternalRouting Set big-switch internal routing mechanism
setLinkPath Set the physical path of a virtual link
startNetwork Start a virtual network
startPort Start a virtual port
startSwitch Start a virtual switch
stopNetwork Stop a virtual network
stopPort Shutdown a virtual port
stopSwitch Shutdown a virtual switch
##### 1、创建网络
``` $ python ovxctl.py ‐n createNetwork tcp:localhost:10000 10.0.0.0 16 ```
表示指定控制器地址为 127.0.0.1,即在该VM上运行,通信端口号为10000,该网络地址范围 10.0.0.0/16。该命令会返回一个tanant ID,若为初次建立则为1。
##### 2、创建虚拟交换机
$ python ovxctl.py ‐n createSwitch 1 00:00:00:00:00:00:01:00
$ python ovxctl.py ‐n createSwitch 1 00:00:00:00:00:00:02:00
$ python ovxctl.py ‐n createSwitch 1 00:00:00:00:00:00:03:00
1代表租户网络,后面代表对应物理交换机的MAC,此处的OVXSwitch与物理网络一一对应,还可以一对多,形成OVXBigSwitcg,在后面会有介绍。每创建一个OVXSwitch,会返回一个DPID,具体描述
>For the curious, the structure of the virtual DPID is as follows: first byte is 0x00, the 24bit ON.Lab OUI (Organizationally Unique Identifier) of a4:23:05, and the final 4 bytes represent a counter (starting at 1) for the number of virtual switches per tenant. For instance, the second virtual switch we create in this virtual network has DPID 00:a4:23:05:00:00:00:02. As before, you should also inspect the OVX log to ensure the virtual switch has been created.
##### 3、创建虚拟端口
$ python ovxctl.py ‐n createPort 1 00:00:00:00:00:00:01:00 1
$ python ovxctl.py ‐n createPort 1 00:00:00:00:00:00:01:00 5
$ python ovxctl.py ‐n createPort 1 00:00:00:00:00:00:02:00 5
$ python ovxctl.py ‐n createPort 1 00:00:00:00:00:00:02:00 6
$ python ovxctl.py ‐n createPort 1 00:00:00:00:00:00:03:00 5
$ python ovxctl.py ‐n createPort 1 00:00:00:00:00:00:03:00 2
创建虚拟端口要指定tenant ID,物理交换机DPID和物理端口号,在这些信息下虚拟出一个端口,该命令会返回一个端口号,编号从1开始。
根据一开始的目标租户网络,可以轻松确定需要的端口号。
##### 4、创建虚拟链路
$ python ovxctl.py ‐n connectLink 1 00:a4:23:05:00:00:00:01 2 00:a4:23:05:00:00:00:02 1 spf 1
$ python ovxctl.py ‐n connectLink 1 00:a4:23:05:00:00:00:02 2 00:a4:23:05:00:00:00:03 1 spf 1
虚拟链路完全是在虚拟交换机上配置,所以需要的是两端的交换机DPID号(第二步中返回信息)和端口号(第三步中返回)信息。因为该条虚拟链路可能跨越多条物理链路,可以指定路由方式,需要两个参数,一是首选,一个是备选,OVX目前支持spf(最短路径),1(手动,通过调用setLinkPath)
##### 5、连接主机
$ python ovxctl.py ‐n connectHost 1 00:a4:23:05:00:00:00:01 1 00:00:00:00:01:01
$ python ovxctl.py ‐n connectHost 1 00:a4:23:05:00:00:00:03 2 00:00:00:00:03:02
将两个主机加入了该租户网络,需要指定虚拟交换机DPID,虚拟端口号和主机MAC地址。该命令返回主机ID。
##### 6、 take off
``` $ python ovxctl.py ‐n startNetwork 1 ```
该步完成两项工作,将虚拟交换机连接到控制器,创建网络时指定了控制器的地址和端口号,这里端口号可以自己指定而不用像6633或6653固定(疑惑),可能与Flooding控制器实现有关;二是握手阶段的一些配置,类似于OpenFlow的setconfig,设置缺省数据发送长度。
当然注意的是,OVX相当于底层物理网络的controller,mininet中的127.0.0.1:6633是指向OVX的,而OVX配置第一部中的127.0.0.1:10000是指向Flooding,也可是其他控制器。
##### 7、 验证分析
查看流表项
``` mininet> dpctl dump‐flows ```
该命令要在执行了 minimet>h_SEA_1 ping -c3 h_LAX_2 后才能查看,且要注意流表项超时时间较短。
> LAX ------------------------------------------------------------------------
NXST_FLOW reply (xid=0x4):
cookie=0x100000002, duration=4.529s, table=0, n_packets=2, n_bytes=196, idle_timeout=5, idle_age=2, priority=0,in_port=5,vlan_tci=0x0000,dl_src=a4:23:05:01:00:00,dl_dst=a4:23:05:20:00:02 actions=mod_nw_src:10.0.0.1,mod_nw_dst:10.0.0.2,mod_dl_src:00:00:00:00:01:01,mod_dl_dst:00:00:00:00:03:02,output:2
cookie=0x100000003, duration=4.496s, table=0, n_packets=2, n_bytes=196, idle_timeout=5, idle_age=2, priority=0,in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:03:02,dl_dst=00:00:00:00:01:01 actions=mod_nw_dst:1.0.0.3,mod_nw_src:1.0.0.8,mod_dl_src:a4:23:05:01:00:00,mod_dl_dst:a4:23:05:20:00:04,output:5
SEA ------------------------------------------------------------------------
NXST_FLOW reply (xid=0x4):
cookie=0x100000003, duration=4.544s, table=0, n_packets=2, n_bytes=196, idle_timeout=5, idle_age=2, priority=0,in_port=5,vlan_tci=0x0000,dl_src=a4:23:05:01:00:00,dl_dst=a4:23:05:10:00:04 actions=mod_nw_src:10.0.0.2,mod_nw_dst:10.0.0.1,mod_dl_src:00:00:00:00:03:02,mod_dl_dst:00:00:00:00:01:01,output:1
cookie=0x100000002, duration=4.569s, table=0, n_packets=2, n_bytes=196, idle_timeout=5, idle_age=2, priority=0,in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:01:01,dl_dst=00:00:00:00:03:02 actions=mod_nw_dst:1.0.0.8,mod_nw_src:1.0.0.3,mod_dl_src:a4:23:05:01:00:00,mod_dl_dst:a4:23:05:10:00:02,output:5
*** SFO ------------------------------------------------------------------------
NXST_FLOW reply (xid=0x4):
cookie=0x100000002, duration=4.573s, table=0, n_packets=2, n_bytes=196, idle_timeout=5, idle_age=2, priority=0,in_port=5,vlan_tci=0x0000,dl_src=a4:23:05:01:00:00,dl_dst=a4:23:05:10:00:02 actions=mod_dl_src:a4:23:05:01:00:00,mod_dl_dst:a4:23:05:20:00:02,output:6
cookie=0x100000003, duration=4.541s, table=0, n_packets=2, n_bytes=196, idle_timeout=5, idle_age=2, priority=0,in_port=6,vlan_tci=0x0000,dl_src=a4:23:05:01:00:00,dl_dst=a4:23:05:20:00:04 actions=mod_dl_src:a4:23:05:01:00:00,mod_dl_dst:a4:23:05:10:00:04,output:5
上面为对应的三个交换机上的流表项,每个交换机包含两个流表项,对SEA,第二条流表,从主机1到主机2,控制器检查原地址发现是tenant 1中的主机,发送packetIn给控制器,控制器根据虚拟网策略,下发流表,流表匹配in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:01:01,dl_dst=00:00:00:00:03:02 ,对应的动作为mod_nw_dst:1.0.0.8,mod_nw_src:1.0.0.3,mod_dl_src:a4:23:05:01:00:00,mod_dl_dst:a4:23:05:10:00:02,output:5,即将IP和MAC地址重写,从端口5交给SFO;对SFO,同样控制器下发流表指导数据转发,当匹配上即SEA重写后的MAC时(in_port=5,vlan_tci=0x0000,dl_src=a4:23:05:01:00:00,dl_dst=a4:23:05:10:00:02 ),对应动作为mod_dl_src:a4:23:05:01:00:00,mod_dl_dst:a4:23:05:20:00:02,output:6,源和目的MAC不变,从端口6转发,可以看出,core OVXSwitch只对数据包进行转发。对LAX,和SEA一样为边缘路由器,负责完成OVX到Physical的映射,匹配上dl_src=a4:23:05:01:00:00,dl_dst=a4:23:05:20:00:02 ,对应动作为actions=mod_nw_src:10.0.0.1,mod_nw_dst:10.0.0.2,mod_dl_src:00:00:00:00:01:01,mod_dl_dst:00:00:00:00:03:02,output:2,即将虚拟的IP、MAC地址转换为实际物理地址。
转换的意义何在:1、该部分还需要系统整理,结合flowVisor、VLAN等优缺点,结合paper阐述。希望在下一个文档呈现。
注意:由于操作不熟练,强调一下,为了避免虚拟网络之间的影响,某个租户网络不使用时需要removeNetwork清除掉,当然,mininet退出后使用 mn -c清除数据。
###进一步实验
######1)多租户网络
$ python ovxctl.py ‐n createNetwork tcp:localhost:20000 10.0.0.0 16
$ python ovxctl.py ‐n createSwitch 2 00:00:00:00:00:00:01:00
$ python ovxctl.py ‐n createSwitch 2 00:00:00:00:00:00:02:00
$ python ovxctl.py ‐n createSwitch 2 00:00:00:00:00:00:03:00
$ python ovxctl.py ‐n createPort 2 00:00:00:00:00:00:01:00 3
$ python ovxctl.py ‐n createPort 2 00:00:00:00:00:00:01:00 5
$ python ovxctl.py ‐n createPort 2 00:00:00:00:00:00:02:00 5
$ python ovxctl.py ‐n createPort 2 00:00:00:00:00:00:02:00 6
$ python ovxctl.py ‐n createPort 2 00:00:00:00:00:00:03:00 5
$ python ovxctl.py ‐n createPort 2 00:00:00:00:00:00:03:00 4
$ python ovxctl.py ‐n connectLink 2 00:a4:23:05:00:00:00:01 2 00:a4:23:05:00:00:00:02 1 spf 1
$ python ovxctl.py ‐n connectLink 2 00:a4:23:05:00:00:00:02 2 00:a4:23:05:00:00:00:03 1 spf 1
$ python ovxctl.py ‐n connectHost 2 00:a4:23:05:00:00:00:01 1 00:00:00:00:01:03
$ python ovxctl.py ‐n connectHost 2 00:a4:23:05:00:00:00:03 2 00:00:00:00:03:04
$ python ovxctl.py ‐n startNetwork 2
按部就班,创建了tenant ID=2的租户网络,该网络也是在上面实验的基础上,只不过使用了不同的虚拟网络,可以实现隔离。
######2)自定义拓扑
OVX特色之一就是可以自定义拓扑,不用限制于底层网络,而实现该技术一是OVX模拟了LLDP过程,二就是OVXBigSwitch的实现。OVXBigSwitch是OVXSwitch的一个基类,与物理交换机之间是一对多关系,是底层交换机在租户网络的一个集合,对租户而言只显示为一个虚拟交换机和虚拟端口,而该OVXBigSwitch两个虚拟端口之间的路径叫做SwitchRoute,实际对应底层若干条链路。
python ovxctl.py ‐createSwitch 3 00:00:00:00:00:00:05:00,00:00:00:00:00:00:06:00,00:00:00:00:00:00:0A:python ovxctl.py -n createNetwork tcp:localhost:30000 10.0.0.0 16
python ovxctl.py -n createSwitch 3 00:00:00:00:00:00:05:00,00:00:00:00:00:00:06:00,00:00:00:00:00:00:0A:00
python ovxctl.py -n createPort 3 00:00:00:00:00:00:05:00 1
python ovxctl.py -n createPort 3 00:00:00:00:00:00:06:00 2
python ovxctl.py -n createPort 3 00:00:00:00:00:00:0A:00 3
python ovxctl.py -n connectHost 3 00:a4:23:05:00:00:00:01 1 00:00:00:00:05:01
python ovxctl.py -n connectHost 3 00:a4:23:05:00:00:00:01 2 00:00:00:00:06:02
python ovxctl.py -n connectHost 3 00:a4:23:05:00:00:00:01 3 00:00:00:00:0A:03
python ovxctl.py -n createSwitch 3 00:00:00:00:00:00:08:00
python ovxctl.py -n createPort 3 00:00:00:00:00:00:05:00 5
python ovxctl.py -n createPort 3 00:00:00:00:00:00:08:00 6
python ovxctl.py -n connectLink 3 00:a4:23:05:00:00:00:01 4 00:a4:23:05:00:00:00:02 1 spf 1
python ovxctl.py -n createPort 3 00:00:00:00:00:00:08:00 4
python ovxctl.py -n connectHost 3 00:a4:23:05:00:00:00:02 2 00:00:00:00:08:04
python ovxctl.py -n startNetwork 3
该拓扑实现了将 交换机CLE、EWR、IAD虚拟为一个OVXBigSwitch,对应每个物理交换机再虚拟出一个接口,各自连接上一个主机。另外将MCI虚拟化,创建虚拟端口,生成两者之间的虚拟链路。主机h_IAD_1、h_EWR_2、h_CLE_3连接在一个OVXBigSwitch上,而h_MCI_4连接在一个OVXSingleSwitch上,在同一个租户网络中。
官网中提到OVXBigSwitch对应的物理交换机没有数量限制,但是一个物理交换机不能对应多个虚拟交换机。
### 脚本配置网络 (take off once again)
如果在命令行一行一行去配置整个网络,当网络规模较大时,工作量会比较大,OVX提供了一个工具embedder,当启动后就会在8000端口等待JSON文件,并根据文件自动配置。
#####1)启动embedder
```$ python utils/embedder.py ```
#####2 ) 脚本文件
"id": "1",
"jsonrpc": "2.0",
"method": "createNetwork",
"params": {
"network": {
"controller": {
"ctrls": [
"tcp:localhost:10000"
],
"type": "custom"
},
"hosts": [
{
"dpid": "00:00:00:00:00:00:02:00",
"mac": "00:00:00:00:02:01",
"port": 1
},
{
"dpid": "00:00:00:00:00:00:05:00",
"mac": "00:00:00:00:05:02",
"port": 2
},
{
"dpid": "00:00:00:00:00:00:06:00",
"mac": "00:00:00:00:06:03",
"port": 3
},
{
"dpid": "00:00:00:00:00:00:09:00",
"mac": "00:00:00:00:09:04",
"port": 4
}
],
"routing": {
"algorithm": "spf",
"backup_num": 1
},
"subnet": "192.168.0.0/24",
"type": "bigswitch"
}
}
}
上述脚本文件实现了将所有物理主机虚拟为一个交换机,并添加了四个主机。需要注意一些格式。
##### 3)运行脚本
``` $ curl localhost:8000 ‐X POST ‐d @bigswitch.json ```
测试时有一个问题,就是发现没有添加进去的主机也能ping通,如下。
mininet> h_SFO_1 ping -c3 h_ORD_2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=41.8 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=1.63 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.135 ms
--- 10.0.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 0.135/14.531/41.825/19.309 ms
mininet> h_SFO_1 ping -c3 h_ATL_2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=45.4 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=1.41 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.060 ms
--- 10.0.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2005ms
rtt min/avg/max/mdev = 0.060/15.636/45.439/21.081 ms
文档中讲到
>be able to ping between hosts h_SFO_1, h_IAD_2, h_EWR_3, and h_ORD_4.
但实际上可以做到网络互通,如前面测试结果显示,h_ATL_2并不在虚拟网络中。
***{可能的原因:ping时其实ping的是主机IP地址,初始网络时每个交换机下面的主机IP地址都是一样的,从10.0.0.1到10.0.0.4,而现网络有点特殊,一是所有的交换机都在同一个tenant 网络中形成一个big switch,二是添加的四个主机地址全覆盖。换句话说,像在本文前面网络,只覆盖了个别交换机,当不再同一个tenant ID中的交换机下的主机就算IP地址一样也无法ping通,二是如果同一个tenant 网络中,该主机地址没有添加进网络也无法ping通。回过头去想,OVX限制一个物理交换机只能属于一个虚拟网络}***
上述理解有个本质的错误,就是OVX中既然能实现地址空间不重叠就是因为边缘交换机是根据MAC地址查找所属的租户网络的,进一步还发现大部分是不能ping通,只有少数例外。待解答。下面这句话也待理解。
>The only restriction we impose
in OVX is that you cannot partition a single physical switch into multiple virtual switches.
#####补充:虚拟网络直接拷贝物理网络
{
"id": "1",
"jsonrpc": "2.0",
"method": "createNetwork",
"params": {
"network": {
"controller": {
"ctrls": [
"tcp:localhost:20000"
],
"type": "custom"
},
"copy-dpid": true,
"hosts": [
{
"dpid": "00:00:00:00:00:00:01:00",
"mac": "00:00:00:00:01:01",
"port": 1
},
{
"dpid": "00:00:00:00:00:00:04:00",
"mac": "00:00:00:00:04:02",
"port": 2
},
{
"dpid": "00:00:00:00:00:00:08:00",
"mac": "00:00:00:00:08:03",
"port": 3
},
{
"dpid": "00:00:00:00:00:00:0B:00",
"mac": "00:00:00:00:0B:04",
"port": 4
}
],
"routing": {
"algorithm": "spf",
"backup_num": 1
},
"subnet": "192.168.0.0/24",
"type": "physical"
}
}
会将物理网络直接拷贝到虚拟网络,并且DPID号相同,这种类型网络的用处暂时还不知道,文档中只是提到
>The option is especially useful as we already know the physical DPIDs and you’re not always sure of the order in which OVX will create the virtual switches.
文档后面提到了当网络在运行而需要对主机修改时,可以用disconnectHost命令,指定网络号和主机号,该主机号为connectHost时返回的 host ID。
后记:花了很多时间去整理这篇文档,在整理过程中也有了很多跟深入的理解,但还是有一些疑问,可能是自己网络设置问题,后续找个时间验证。也希望能相互交流。
*笔者 : 袁其杰 ,北京邮电大学IsWIT研究中心研究生,2016.9至今
QQ 441711184*
网友评论