基于Ubuntu18.04的OVS与Mininet仿真工具安装及网络测量应用案例
目录
一、Ubuntu18.04 安装
1.1镜像下载地址
1.2在VMware Workstation中安装镜像
1.3在Ubuntu18.04apt修改为国内的阿里云镜像源
1.4Ubuntu18.04的NAT8静态网络配置及连接互联网
二、OVS安装使用及应用案例
2.1OVS的构建与安装(可直接安装Mininet包括OVS)
2.2OVS使用说明
2.3应用案例
2.3.1实例介绍
2.3.2实例开发
三、Mininet安装使用及简单案例
3.1软件安装
3.2使用说明
3.3应用案例
3.3.1案例准备
3.3.2案例说明
3.3.3案例具体实现
3.4测试结果
一、Ubuntu18.04 安装
1.1镜像下载地址
http://mirrors.163.com/ubuntu-releases/18.04/
1.2在VMware Workstation中安装镜像
1)打开VMware,点击"文件"--->"新建虚拟机"--->选择"典型"
2)选择"稍后安装操作系统"--->选择"Linux",版本选择"Ubuntu 64位"--->选择用于存放虚拟机的"位置"--->指定"磁盘大小",然后选择"将虚拟磁盘存储为单个文件"--->在"已准备好创建虚拟机"窗口中,选择"完成"
3)选择"虚拟机"--->"设置",根据自己的硬件条件,适当分配一些硬件资源。必须设置的一项为"CD/DVD(STAT)",在右侧"连接"板块内选择"使用ISO镜像文件",并指定前面下载的Ubuntu镜像文件存放目录。分配好虚拟机的硬件资源后点击"确定"。
4)运行虚拟机,加载一段时间后弹出"安装"界面。语言栏倒数第三个是中文。点击"安装 Ubuntu"。 然后在键盘布局界面选择"继续"
5)选择"正常安装",点击"继续"
6)点击"现在安装",然后在出现的"将改动写入此盘吗",选择"继续"
7)你在什么地方,选择"Shanghai"--->"继续"
8)根据自身需求填写信息,点击"继续",等待一会完成安装。
以下为可选配置
1.3在Ubuntu18.04apt修改为国内的阿里云镜像源
1)查看当前系统的版本信息
命令:lsb_release -a
2)切换为root用户
命令:su root
注:首次进入若输入密码老是验证失败则输入命令: sudo passwd root 然后输入密码,并确认密码,最后再次切换root用户输入刚才设置的密码即可。
3)备份系统默认的源(安全起见可不备份)
命令:cp /etc/apt/sources.list /etc/apt/sources.list.backups
4)配置阿里云镜像源
命令:vim /etc/apt/sources.list
镜像资源网站:https://developer.aliyun.com/mirror/ubuntu?spm=a2c6h.13651102.0.0.2c741b118CQEPe
注:将系统默认的镜像源注释掉,然后添加阿里云镜像源
5)更新镜像源
命令:sudo apt-get update
1.4Ubuntu18.04的NAT8静态网络配置及连接互联网
1)网络配置命令
sudo vim /etc/netplan/01-network-manager-all.yaml
注:01-network-manager-all.yaml 每个ubuntu主机名字不同
2)修改内容
ens33:
分别是ip地址,网关地址,是否动态解析地址,域名解析(DNS)地址。
3)网络重启
命令:sudo netplan apply
4)重启系统
命令:reboot
二、OVS安装使用及应用案例
此节可先不看应用案例,直接看第三节Mininet。
2.1OVS的构建与安装(可直接安装Mininet包括OVS)
1)下载指定版本压缩包
下载地址:http://www.openvswitch.org/download/
2)将下载的.tar.gz压缩包通过软件上传到ubuntu中(此处我用的是SecureCRT)
上传命令: rz -E
3)解压压缩包
命令:sudo tar -zxvf openvswitch-2.12.1.tar.gz
注:openvswitch-2.12.1.tar.gz是我下载的版本,且一定要在对应文件目录下解压,否则要指定压缩包的目录
4)将解压完的文件放到一个对应的目录下
命令:mv openvswitch-2.12.1 /opt/modules/
注:/opt/modules/ 是我存放的目录
5)进入openvswitch-2.12.1目录,配置。
首先如果代码是从OVS GIT树上下载的,则在代码的第一层目录执行boot.sh脚本。
命令:./boot.sh
在代码包的第一层目录,通过运行configure脚本来配置代码包的编译环境。通常可以直接调用不带任何参数命令进行默认配置。
命令: sudo ./configure
默认所有的文件都会安装在/usr/local目录下,如果想安装在指定目录下,可以添加如下参数(以安装在/usr/和/var/ 目录为例)。
命令:sudo ./configure --prefix=/usr --localstatedir=/var
默认情况下,静态库被构建和链接,如果想使用共享库的话,操作如下。
命令:sudo ./configure --enable-shared
若需要使用特定的C编译器来编译OVS用户程序,可以在configure命令行上指定编译器及版本。
命令:sudo ./configure CC=gcc-4.2
也可以在configure命令行指定clang编译。
命令:sudo ./configure CC=clang
可以将安装的软件创建指定目录存放,操作如下。
命令:sudo mkdir _gcc && (cd _gcc && ../configure CC=gcc)
或者使用如下命令。
命令: sudo mkdir _clang && (cd _clang && ../configure CC=clang)
若需要构建Linux内核模块,操作如下。
命令:sudo ./configure --with-linux=/lib/modules/'uname -r'/build
6)构建OVS,在代码包的第一层目录下运行make。
命令:sudo make
7)切换到root用户,运行“make install”安装OVS到系统中,默认安装在/usr/local目录
命令:
su root
make install
8)安装OVS,加载Open vSwitch模块
命令:
make modules_install
/sbin/modprobe openvswitch
9)OVS安装成功之后,可使用ovsdb-tool来初始化配置数据库
ovsdb-tool create /usr/local/etc/openvswitch/conf.db vswitchd/vswitch.ovsschema
2.2OVS使用说明
OVS安装完成后,相关设备还不能被称作一台交换机,在它发挥交换机功能之前,还需要手动创建ovsdb并对其进行初始化。另外,仅创建了ovsdb的OVS还是一台没有生命的交换机,还需要开启OVS的主进程vswitchd,才能完成OVS启动。完成上述步骤后。安装OVS的设备才可以成为一台名副其实的交换机。
OVS的启动和停止可参考如下步骤。
1)在启动ovs-vswitchd之前,需要用下面的命令设置环境变量。
#export PATH=$PATH:/usr/local/share/openvswitch/scripts/
2)启动OVS 。
命令:
ovs-ctl start
3)若用户需要停止OVS,可使用如下命令。
命令:
ovs-ctl stop
详细文档使用可参考网站:https://docs.openvswitch.org/en/stable/intro
OVS常用的命令主要是ovs-vsctl和ovs-ofctl这两个工具。
(1)ovs-vsctl
该工具是一个查询和配置OVS数据库的实用工具,用于查询或者变更vswitchd的配置信息,通过它可以直接更新ovsdb。
(2)ovs-ofctl
该工具是OpenFlow交换机的命令行管理工具,用于管理OVS作为OpenFlow交换机时的各种参数,用户可以通过ovs-ofctl查询或修改OpenFlow交换机的状态、配置和流表项等信息。
2.3应用案例
2.3.1实例介绍
从OpenFlow协议的角度讲,控制平面的建设分为带内(In-Band)和带外(Out-of-Band)两种模式,在组建大型网络时,为例保证控制平面的稳定性,一般采用Out-of-Band模式,通常的做法是物理分离的专用网络。然而,在实际SDN部署过程中,某些交换机之间可能只有一条物理链路,在这种情况下,需要对Out-of-Band模式进行一些调整,使部分物理链路可以同时承载两条逻辑链路(数据平面、控制平面),并且相互隔离。
此案例介绍如何基于OVS部署一个共享物理链路、Out-of-Band组网的SDN,具体拓扑如下图所示,图中OVS01与OVS02之前只有一条物理链路,OVS01的控制平面端口需要通过该链路连接至控制器,主机A也需要通过该链路与主机B进行数据平面通信,因此需要OVS对流量进行VLAN(虚拟局域网)划分和QoS保障,以保证数据平面不影响控制平面。
2.3.2实例开发
与传统的网络转发设备一样,OVS需要在进行配置后才能实现相应的功能。下面列举了对上述OVS01的配置,其他的OVS配置类似。
待配置的OVS安装在一台有8个以太网口的服务器上,需要将其中的eth0、eth1分配给控制平面使用,eth2~eth6分配给数据平面使用,eth7为数据平面与控制平面共享的物理链路接口。OVS通过TCP端口连接到IP地址为192.168.87.200的控制器。在完成以上基本配置后,还需基于端口设置队列和QoS,保障数据平面与控制平面的最低带宽。
如下图所示,OVS01内部划分为3个网桥,其中br0为数据平面网桥,br1为控制平面网桥,br2为普通网桥。Patch为OVS内部虚拟接口,br0与br2、br1通过Patch接口建立内部连接。
在br2内通过使用VLAN实现控制平面和数据平面的数据分流和隔离的具体方法是:给Patch21端口的数据流打上VLAN标签,将控制平面数据流隔离在VLAN999内,而Patch20端口的数据流不打VLAN标签,这样通过区分数据流是否带有VLAN标签来区分处理两个平面的数据流量,实现数据平面与控制平面的隔离。按照这种方法配置OVS后,控制平面流量经br1通过Patch接口进入br2并打上VLAN999的标签通过eth7送到相邻OVS。 br2接收到数据分组如果是带VLAN ID=999的数据分组就从Patch21转发出去,如果是无VLAN标签的数据分组就从Patch20转发出去。 最终,当同样配置的两台OVS级联时,两台OVS中的控制平面数据分组不会进入数据平面中,而数据平面的数据分组也不会进入控制平面,而两台OVS间的物理链路实现了控制平面与数据平面的复用。
当OVS中控制平面与数据平面流量复用同一物理链路时,存在如何对两种数据流量进行带宽分配管理的问题。这里通过在OVS上配置QoS策略来对复用链路上的两类数据流量进行带宽分配管理。可采用的方法是:基于端口eth7创建QoS和队列(Queue),并设置各队列的优先级和最低保障带宽,为数据平面和控制平面提供最低保证带宽的QoS。
1)删除OVS配置,当用户需要删除OVS上的网桥、QoS、队列时,可以参考下面命令。
ovs-vsctl del-br0 #删除网桥
ovs-vsctl del-br1
ovs-vsctl del-br2
ovs-vsctl -- --all destroy qos #删除QoS
ovs-vsctl -- --all destroy queue #删除队列
注:若想要删除网桥上的端口,可使用命令ovs-vsctl del-port [br name] [port name]
2)创建网桥,并且设置网桥的DPID。可参考如下命令。
ovs-vsctl add-br br0 -- set bridge br0 other_config:Datapath-id=0000000000000001 #新建网桥br0,并为br0配置一个DPID
ovs-vsctl add-br br1 #新建网桥br1
ovs-vsctl add-br br2 #新建网桥br2
3)配置网桥,包括在网桥上添加物理端口、在网桥上设置控制器、设置网桥的IP地址、设置网桥支持的OpenFlow协议版本。可参考如下命令。
ovs-vsctl add-port br0 eth2 #为网桥br0添加物理以太网口eth2
ovs-vsctl add-port br0 eth3 #为网桥br0添加物理以太网口eth3
ovs-vsctl add-port br0 eth4 #为网桥br0添加物理以太网口eth4
ovs-vsctl add-port br0 eth5 #为网桥br0添加物理以太网口eth5
ovs-vsctl add-port br0 eth6 #为网桥br0添加物理以太网口eth6
ovs-vsctl set-controller br0 tcp:192.168.87.200:6633 #连接控制器,网桥br0与IP为192.168.87.200的控制器通过TCP的6633端口建立连接
ovs-vsctl set bridge br0 fail_mode=secure #指定br0以secure模式连接控制器
ovs-vsctl set bridge br0 protocols=OpenFlow10,OpenFlow12,OpenFlow13 #设置支持OpenFlow版本号
ovs-vsctl add-port br1 eth0 #为网桥br1添加物理以太网口eth0
ovs-vsctl add-port br1 eth1 #为网桥br1添加物理以太网口eth1
sudo ifconfig br1 192.168.87.1 #设定网桥IP地址
ovs-vsctl add-port br2 eth7 #为网桥br2添加物理以太网口eth7
4)连接网桥,OVS支持多个网桥之间的内部连接,连接时需要创建虚拟端口Patch。一下命令实现第(2)节图的配置,其他网桥的连接可参考此命令。
sudo ovs-vsctl add-port br0 patch02 -- set interface patch02 type=patch options:peer=patch20 #为网桥br0添加patch类型的Interface:patch02,并设置对端patch interface:patch20
sudo ovs-vsctl add-port br1 patch12 -- set interface patch12 type=patch options:peer=patch21 #为网桥br1添加patch类型的Interface:patch12,并设置对端patch interface:patch21
sudo ovs-vsctl add-port br2 patch21 -- set interface patch21 type=patch options:peer=patch12 #为网桥br2添加patch类型的Interface:patch21,并设置对端patch interface:patch12
sudo ovs-vsctl add-port br2 patch20 -- set interface patch20 type=patch options:peer=patch02 #为网桥br0添加patch类型的Interface:patch02,并设置对端patch interface:patch20
5)设置基于端口的QoS,OVS支持基于端口设置QoS,下面命令是eth7处添加linux-htb(类似令牌桶算法)类型的QoS和队列,并设置最大速率和优先级。
ovs-vsctl set port eth7 qos=@newqos -- --id=@newqos create qos type=linux-htb queues=100=@q0,101=@q1 other_config:max-rate='ovs-vsctl get interface eth7 link-speed' -- --id=@q0 create queue other_config:priority=100 other_config:min-rate=10000000 -- --id=@q1 create queue other_config:priority=10 other_config:min-rate=10000000
6)设置流表,下面命令主要实现在网桥上删除和添加流表命令,并设置流表的优先级和队列号,其他类似的操作可参考此命令。
ovs-ofctl del-flows br2 #删除网桥br2下的流表
ovs-ofctl add-flow br2 in_port='ovs-vsctl get interface patch20 ofport',priority=60000,actions=enqueue:'ovs-vsctl get interface eth7 ofport':100 #给网桥br2添加流表:“流表优先级为60000,针对从端口patch20进入的数据分组,执行动作---入队,端口号eth7,队列号100”
ovs-ofctl add-flow br2 in_port='ovs-vsctl get interface patch21 ofport',priority=61000,actions=mod_vlan_vid:999,enqueue:'ovs-vsctl get interface eth7 ofport':101 #给网桥br2添加流表:“流表优先级为61000,针对从端口patch21进入的数据分组,执行动作---改变数据分组的VLAN id=999,然后入队,端口号分别eth7,队列号101”
ovs-ofctl add-flow br2 in_port='ovs-vsctl get interface eth7 ofport',priority=60000,actions=output:'ovs-vsctl get interface patch20 ofport' #给网桥br2添加流表:“流表优先级为60000,针对从端口eth7进入的数据分组,执行动作---经由patch20端口转发出去”
ovs-ofctl add-flow br2 in_port='ovs-vsctl get interface eth7 ofport',dl_vlan=999,priority=61000,actions=strip_vlan,output:'ovs-vsctl get interface patch21 ofport' #给网桥br2添加流表:“流表优先级为61000,针对从端口eth7进入VLAN id=999的数据分组,执行动作---去掉数据分组的VLAN标签,然后经由patch21端口转发出去”
7)启动以太网端口
ifconfig eth0 up #开启以太网网口eth0
ifconfig eth1 up #开启以太网网口eth1
ifconfig eth2 up #开启以太网网口eth2
.
.
.
ifconfig eth7 up #开启以太网网口eth7
注:若虚拟机中没有以太网口需要添加
命令:
sudo ip tuntap add dev eth6 mode tun #创建tun模式虚拟网卡,名为:eth6
sudo ip addr add 192.168.87.160/24 dev eth6 #指定虚拟网卡地址
想了解具体内容(tun/tap)可查看:https://www.junmajinlong.com/virtual/network/all_about_tun_tap/
如前面所述,实现数据控制平面的隔离需要根据数据分组的VLAN标签对其进行分类,并以此为基础提供有差别的QoS保障。采用的方法是在转发端口设置两个不同优先级的队列,并为每一个队列设置最小保证带宽,针对来自不同端口的数据分组各自进行排队,保障不同数据流的带宽。这样,通过设置不同的队列优先级,保证只有在处理完优先级较高的队列后才会处理优先级较低的队列。同时,基于流表实现了对不同类型数据流差别化的流表匹配策略,优先级高的流规则先于优先级较低的流规则进行匹配,匹配后的动作也先于优先级低的流规则执行。
三、Mininet安装使用及简单案例
3.1软件安装
1)本地安装Mininet源代码
首先获取Mininet源代码:
git clone git://github.com/mininet/mininet
注:如没有git,安装命令:sudo apt-get install git
获取Mininet源代码后即可安装Mininet,安装Mininet的命令是:
mininet/util/install.sh [options]
options 包括:
①-a :此命令将安装Mininet VM中的所有工具,包括Open vSwitch、Wireshark抓包工具和POX,默认情况下这些工具安装在用户的主目录(root目录)下。
②-nfv :此命令默认安装Mininet、User Switch和Open vSwitch
③-s mydir :此命令可以将Mininet安装在指定的目录下,而不是默认主目录。
出现Enjoy Mininet表示安装成功!!!
2)安装Mininet文件包
如果更新过Ubuntu或者Mininet,在安装前可以运行以下命令以确保删除Mininet、Open vSwitch以前版本的痕迹,否则影响新版本的安装。
sudo rm -rf /usr/local/bin/mn /usr/local/bin/mnexec \/usr/local/lib/python*/*/*mininet* \/usr/local/bin/ovs-* /usr/local/sbin/ovs-*
安装Mininet文件命令:
sudo apt-get install mininet
Mininet安装完成后,验证openvswitch-controller是否在运行,如果正在运行,应该将其停止,以确保Mininet在启动时可以指定自己的控制器。
sudo service openvswitch-controller stop
sudo update-rc.d openvswitch-controller disable
Mininet安装完成后,即可使用Mininet创建模拟的SDN实验网络。为检验网络搭建是否可以正常通信,一般的做法是使用ping命令在两个主机之间进行ping操作,同样,在Mininet中可以使用如下命令直接检验Mininet是否安装成功。
sudo mn --test pingall
Mininet安装成功后,只需要用如下命令即可启动Mininet。
sudo mn
执行上述命令后,会创建默认的一个小型测试网络。
经过短暂时间的等待即可进入以“mininet>”引导的命令行界面。进入“mininet>”命令行界面后,默认拓扑创建成功,即将拥有一个一台控制节点(Controller)、一台交换机(Switch)和两台主机(Host)的网络。
3.2使用说明
Mininet除了创建默认的网络拓扑之外,还提供了丰富的参数设定方式用来设定网络拓扑、交换机、控制器、MAC地址、链路属性等,以满足使用者在仿真过程中多样性的需求。
(1)设定网络拓扑
--topo 用于指定OpenFlow的网络拓扑。Mininet已经为大多数应用实现了5中类型的OpenFlow网络拓扑,分别为Tree、Single、Reversed、Linear和Minimal。缺省情况下,创建的是Minimal拓扑,该拓扑为一个交换机与两个主机相连;
--topo single ,n 则表示1个OpenFlow交换机下挂连接n个主机;Reversed与Single类型相似,区别在于Single的主机编号和相连的交换机端口编号同序,而Reversed的主机编号和相连的交换机端口编号反序;
--topo linear,n 则表示将创建n个OpenFlow交换机,且每个交换机只连接一个主机,并且所有交换机连接成直线;
--topo tree,depth=n,fanout=m 则表示创建一个树形拓扑,深度是n,扇出是m,例如,当depth=2,fanout=8时,将创建9个交换机连接64个主机(每个交换机连接8个设备,设备中包括交换机及主机)。
--custom :在上述已有拓扑的基础上,Mininet支持自定义拓扑,使用一个简单的Python API即可,例如导入自定义的mytopo。
sudo mn --custom ~/mininet/custom/topo-2sw-2host.py --topo mytopo -- test pingall
例如:
创建8个主机,2个交换机的拓扑。
创建文件:topo-2sw-8host.py
输入命令:
sudo mn --custom ~/mininet/custom/topo-2sw-8host.py --topo mytopo --switch ovs,protocol=OpenFlow13 --controller=remote,ip=192.168.0.103,port=6633
打开控制器可看见创建的拓扑图如下:
(2)设置交换机
--switch :Mininet支持4类交换机,分别是UserSwitch、OVS交换机、OVSLegacyKernelSwitch和IVS交换机。其中,运行在内核空间交换机的性能和吞吐量要高于用户空间交换机,可以通过运行iperf命令测试链路的TCP带宽速率来验证。
sudo mn --switch ovsk --test iperf
此外,在switch属性中添加protocols参数时可以指定OpenFlow协议版本,例如OpenFlow v1.0和OpenFlow v1.3的指定。
sudo mn --topo single,3 --controller remote,ip=[controller IP] -- switch ovsk,protocols=OpenFlow10
sudo mn --topo single,3 --controller remote,ip=[controller IP] -- switch ovsk,protocols=OpenFlow13
使用以下命令查看不同OpenFlow版本的OVS交换机信息。
ovs-ofctl -O OpenFlow10 show s1
ovs-ofctl -O OpenFlow13 show s1
(3)设置控制器
--controller :通过参数设置的控制器可以是Mininet默认的控制器(NOX)或者虚拟机之外的远端控制器,如Floodlight、POX等,指定远端控制器的方法如下。
sudo mn --controller=remote,ip=[controller IP],port=[controllerlistening port]
(4)配置MAC地址
--mac :设置MAC地址的作用是增强设备MAC地址的易读性,即将交换机和主机的MAC地址设置为一个较小的、唯一的、易读的ID,以便在后续工作中减少对设备识别的难度。
sudo mn -mac
(5)设置链路属性
--link :链路属性可以是默认Link及TCLink。将链路类型指定为TC后,可以进一步指定具体参数。具体参数命令显示如下。
sudo mn --link tc,bw=[bandwidth],delay=[delay time],loss=[loss rate],max_que_size=[queue size]
bw表示链路带宽,用Mbit/s表示单位;延迟delay以字符串形式表示,如"5 ms"、"100 us"、"1 s";loss表示数据分组丢失率的百分比,用0~100的一个百分数表示;max_queue_size表示最大排队长度,使用数据分组的数量表示。
3.3应用案例
3.3.1案例准备
本案例使用Floodlight控制器。
在windows下打开idea,导入Floodlight源代码。
(可用git直接clone下Floodlight源代码,可参考官网安装:https://floodlight.atlassian.net/wiki/spaces/floodlightcontroller/pages/1343544/Installation+Guide)
运行Main.class,然后在虚拟机中运行Mininet连接控制器命令如下:
sudo mn --switch ovs,protocols=OpenFlow13 --controller=remote,ip=192.168.0.102,port=6633
注:上述ip是控制器所在主机的ip地址
3.3.2案例说明
本案例实现网络测量,其中包括带宽测量、丢包率和时延。
在Floolight源码src/main/java目录下创建net.floodlightcontroller.networkmeter模块,然后在此模块下创建NetworkMeter、BandMeter、PacketLossMeter、TimeDelayMeter四个类,用来实现网络测量、带宽测量、丢包率、时延,因为我们实现对网络测量的实时监测,所以要使用多线程,从而创建NetworkMeterThread类实现。
(1)对NetworkMeter类简单描述:(测试控制器与交换机建立连接(握手成功)接收到PACKET_IN消息)
public class NetworkMeter implements IFloodlightModule, IOFMessageListener {protected IFloodlightProviderService floodlightProvider; //声明Floodlight控制器/*** 接收PACKET_IN消息* @param sw the OpenFlow switch that sent this message* @param msg the message* @param cntx a Floodlight message context object you can use to pass* information between listeners* @return*/@Overridepublic Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {System.out.println("receive PACKET_IN");return Command.CONTINUE; //以后调整模块接收PACKET_IN的顺序,为了防止之后的模块接收不到PACKET_IN消息。 CONTINUE:继续遍历 STOP:跳出循环终止}@Overridepublic String getName() {return NetworkMeter.class.getSimpleName(); //返回模块名称}@Overridepublic boolean isCallbackOrderingPrereq(OFType type, String name) {return false;}@Overridepublic boolean isCallbackOrderingPostreq(OFType type, String name) {return false;}@Overridepublic Collection<Class<? extends IFloodlightService>> getModuleServices() {return null;}@Overridepublic Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {return null;}/*** 说明模块依赖关系* @return*/@Overridepublic Collection<Class<? extends IFloodlightService>> getModuleDependencies() {Collection<Class< ? extends IFloodlightService>> list=new ArrayList<Class< ? extends IFloodlightService>>();list.add(IFloodlightProviderService.class);return list;}//执行时先执行init,在执行startup@Overridepublic void init(FloodlightModuleContext context) throws FloodlightModuleException {floodlightProvider =context.getServiceImpl(IFloodlightProviderService.class);//获取服务实例}@Overridepublic void startUp(FloodlightModuleContext context) throws FloodlightModuleException {floodlightProvider.addOFMessageListener(OFType.PACKET_IN,this); //添加监听器}
}
注:运行前需要在src\main\resources\META-INF\services\net.floodlightcontroller.core.module.IFloodlightModule中添加net.floodlightcontroller.networkmeter.NetworkMeter
在src/main/resources/floodlightdefault.properties中添加net.floodlightcontroller.networkmeter.NetworkMeter,也就是在Floodlighth中注册一下此模块。
(2)此时运行控制器(Mininet在控制器开启前或后都行),然后在虚拟机上启动的Mininet中输入命令:h1 ping h2,控制器会输出结果:
在网页http://Controller-ip:8080/ui/pages/index.html中查看到有一个交换机和2个主机,如下图所示。
3.3.3案例具体实现
(1)NetworkMeter类
/*** 网络测量:带宽测量、丢包率、时延*/
public class NetworkMeter implements IFloodlightModule, IOFMessageListener {//Floodlight中所有的管理例如链路管理和设备管理都有自己的Service,所以需要声明指定的Serviceprotected IFloodlightProviderService floodlightProvider; //声明Floodlight控制器protected IOFSwitchService switchService;protected ILinkDiscoveryService linkService;protected NetworkMeterThread networkMeterThread; //声明线程变量//声明网络测量具体操作的对象protected BandMeter bandMeter;protected PacketLossMeter packetLossMeter;protected TimeDelayMeter timeDelayMeter;/*** 接收PACKET_IN消息* @param sw the OpenFlow switch that sent this message* @param msg the message* @param cntx a Floodlight message context object you can use to pass* information between listeners* @return*/@Overridepublic Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {//System.out.println("receive PACKET_IN");switch (msg.getType()){case PACKET_IN:Ethernet ethernet = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);//获取bcStore队列里面的消息if(timeDelayMeter.isDoingTimeDelayMeter(ethernet)){NetworkStore networkStore=NetworkStore.getInstance();System.out.println("*********处理PacketIn*********");networkStore.handlePacketIn(ethernet.getPayload(),linkService);}break;}return Command.CONTINUE; //以后调整模块接收PACKET_IN的顺序,为了防止之后的模块接收不到PACKET_IN消息。 CONTINUE:继续遍历 STOP:跳出循环终止}@Overridepublic String getName() {return NetworkMeter.class.getSimpleName(); //返回模块名称}/*** 用来调整各个模块之间接收Packet_IN消息的顺序* @param type the object type to which this applies* @param name the name of the module* @return*/@Overridepublic boolean isCallbackOrderingPrereq(OFType type, String name) {return false;}/*** 用来调整各个模块接收Packet_IN消息的顺序* @param type the object type to which this applies* @param name the name of the module* @return*/@Overridepublic boolean isCallbackOrderingPostreq(OFType type, String name) {//针对PACKET_IN消息调整顺序,并且是在linkdiscovery模块之前,因为linkdiscovery模块是Floodlight下发PACKET_IN消息的第一个模块return (type.equals(OFType.PACKET_IN)&&(name.equals("linkdiscovery")));}@Overridepublic Collection<Class<? extends IFloodlightService>> getModuleServices() {return null;}@Overridepublic Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {return null;}/*** 说明模块依赖关系* @return*/@Overridepublic Collection<Class<? extends IFloodlightService>> getModuleDependencies() {Collection<Class< ? extends IFloodlightService>> list=new ArrayList<Class< ? extends IFloodlightService>>();list.add(IFloodlightProviderService.class);return list;}//执行时先执行init,在执行startup@Overridepublic void init(FloodlightModuleContext context) throws FloodlightModuleException {floodlightProvider =context.getServiceImpl(IFloodlightProviderService.class);//获取Floodlight服务实例switchService=context.getServiceImpl(IOFSwitchService.class);linkService=context.getServiceImpl(ILinkDiscoveryService.class);this.bandMeter=new BandMeter();this.packetLossMeter=new PacketLossMeter();this.timeDelayMeter=new TimeDelayMeter();networkMeterThread=new NetworkMeterThread(this); //构造networkMeterThread对象时把当前对象NetworkMeter传给networkMeterThread的构造函数}@Overridepublic void startUp(FloodlightModuleContext context) throws FloodlightModuleException {//添加监听器,只能添加连接建立(握手成功)以后的数据包比如PACKET_IN,像FlowStatsReply等统计数据包是在握手阶段创建,所以不能添加监听器获取floodlightProvider.addOFMessageListener(OFType.PACKET_IN,this);networkMeterThread.start(); //启动线程 因为是继承Thread类,不是Runnable接口所以调用start()方法,而不是run()方法}//获取到所有交换机public IOFSwitchService getSwitchService() {return switchService;}public BandMeter getBandMeter() {return bandMeter;}public PacketLossMeter getPacketLossMeter() {return packetLossMeter;}public TimeDelayMeter getTimeDelayMeter() {return timeDelayMeter;}public ILinkDiscoveryService getLinkService() {return linkService;}/*** FlowStatsReply的一个中继* @param reply*/public static void handleFlowStatsReply(OFFlowStatsReply reply, IOFSwitchBackend sw){NetworkStore networkStore =NetworkStore.getInstance();networkStore.handleFlowStatsReply(reply, sw);}/*** DescStatsReply的一个中继* @param reply*/public static void handleDescStatsReply(OFPortDescStatsReply reply){NetworkStore networkStore=NetworkStore.getInstance();networkStore.handleDescStatsReply(reply);}/*** EchoReply的一个中继* @param reply*/public static void handleEchoReply(OFEchoReply reply){NetworkStore networkStore=NetworkStore.getInstance();networkStore.handleEchoReply(reply);}/*** PortStatsReply的一个中继* @param reply* @param sw*/public static void handPortStatsReply(OFPortStatsReply reply, IOFSwitchBackend sw) {NetworkStore networkStore = NetworkStore.getInstance();networkStore.handlePortStatsReply(reply,sw);}
}
(2)NetworkMeterThread类
在线程实现里面需要注意一个思路,就是控制器需要下发给数据平面数据包,所以数据平面获取到控制器中devicemanager模块(此模块负责网络设备的追踪与管理)中所有交换机,然后去遍历List拿到每一个交换机,最后对每一个交换机下发一个数据包。
具体实现:
/*** 添加多线程,让网络测量实现周期性/实时性监测*/
public class NetworkMeterThread extends Thread{protected NetworkMeter networkMeter;public NetworkMeterThread(NetworkMeter nm) {this.networkMeter=nm;}public void run(){while (true) {try {sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//清空clearNetworkStore networkStore=NetworkStore.getInstance();networkStore.calCurrentBand();networkStore.nextMeterBegin();//获取所有交换机IOFSwitchService switchService=networkMeter.getSwitchService();//遍历所有交换机,获取交换机的DPIDfor(DatapathId datapathId: switchService.getAllSwitchDpids()){IOFSwitch sw= switchService.getSwitch(datapathId); //通过DPID获取到交换机if(sw==null){Log.error("sw is null");continue;}//TODOnetworkMeter.getBandMeter().doBand(sw); //带宽networkMeter.getPacketLossMeter().doPacketLoss(sw); //丢包率}//时延networkMeter.getTimeDelayMeter().doTimeDelay(networkMeter);}}
}
注:此处自行创建一个Log类,打印信息。
(3)NetworkStore类
因为进行网络测量时,需要把测试的结果保存起来,所以创建NetworkStore类。
此类同时也完成在NetworkMeter类中处理接收到数据包(*****Reply)函数的具体实现,以便完成相应存储。
/*** 存储类,以便于进行网路预测*/
public class NetworkStore {//现在信息 ----- 历史信息protected static NetworkStore networkStore;protected List<LinkDataInfo> currentLinkStatus;protected List<LinkDataInfo> historyLinkStatus;protected List<LinkTimeInfo> linkTimeStatus;protected final static int echoMessageLength=2;protected long maxBand;public long getMaxBand() {return maxBand;}public void setMaxBand(long maxBand) {this.maxBand = maxBand;}public NetworkStore() {currentLinkStatus=new ArrayList<LinkDataInfo>();historyLinkStatus=new ArrayList<LinkDataInfo>();linkTimeStatus = new ArrayList<LinkTimeInfo>();}/*** 单例模式* @return*/public static synchronized NetworkStore getInstance(){ //关键字synchronized: 表示若有多个地方同时请求NetworkStore对象时,会按照一定策略逐一分配,防止线程阻塞if(networkStore==null){networkStore=new NetworkStore();}return networkStore;}/*** 处理Packet_In消息,获取传输总时延* @param payload* @param linkService*/public void handlePacketIn(IPacket payload, ILinkDiscoveryService linkService){System.out.println("****************收到PacketIn*******************");IPv4 ip=(IPv4)payload; //封装时将IP数据报封装在ethernet帧中,调用时ehernet.getPayload就得到第三层的数据包Data data=(Data)ip.getPayload(); //获取IP包中封装的信息timeStampString mess[] =new String(data.getData()).split("<>");if(mess.length!=5){Log.error("length is not 5!!!");return ;}SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");Date currentTime=new Date();long allTime=0;try {Date sendTime=df.parse(mess[0]);allTime=currentTime.getTime()-sendTime.getTime(); //当前时间-发送时间} catch (ParseException e) {e.printStackTrace();}System.out.println("**************所有时间allTime:"+allTime+" *****************");//进行存储工作Map<Link, LinkInfo> links=linkService.getLinks();for(Link l:links.keySet()){if(l.getSrc().equals(DatapathId.of(mess[1]) ) &&l.getSrcPort().getPortNumber() ==Integer.parseInt(mess[2]) &&l.getDst().equals(DatapathId.of(mess[3]) ) &&l.getDstPort().getPortNumber() ==Integer.parseInt(mess[4])){LinkTimeInfo linkTimeInfo=new LinkTimeInfo();linkTimeInfo.setLink(l);linkTimeInfo.setAllTime(allTime);linkTimeStatus.add(linkTimeInfo);break;}}}/***处理EchoReply的数据包,计算链路时延* @param reply*/public void handleEchoReply(OFEchoReply reply){if (reply.getData().length<=0){ //判断EchoReply是否是需要的Replyreturn ;}String[] data=new String(reply.getData()).split("<>");if(data.length!=echoMessageLength){Log.error("*********length is not 2!!!*********");return;}SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");Date currentTime=new Date();long Time=0;try {Date sendTime=df.parse(data[0]); //封装时data[0]放的是时间戳Time=currentTime.getTime() -sendTime.getTime();} catch (ParseException e) {e.printStackTrace();}//更新for(LinkTimeInfo lti:linkTimeStatus){ //linkTimeStatus为之前存储过数据的数据结构if(lti.getLink().getSrc().equals(DatapathId.of(data[1]))){lti.setControllerToSrcSwTime(Time/2);}if(lti.getLink().getDst().equals(DatapathId.of(data[1]))){lti.setControllerToDesSWTime(Time/2);}if(lti.getControllerToDesSWTime() !=-1 && lti.getControllerToSrcSwTime() !=-1){long delay=lti.getAllTime()-lti.getControllerToDesSWTime()-lti.getControllerToSrcSwTime();lti.setDelay(delay>=0?delay : 0);Log.info("***********时延:"+lti.getDelay()+"***********");}}}/*** 处理DescStatsReply,获取当前带宽* @param reply*/public void handleDescStatsReply(OFPortDescStatsReply reply){long speed=0;Log.info("***********收到DescStatsReply**************");List<OFPortDesc> entries=reply.getEntries();for(OFPortDesc e:entries){speed=e.getCurrSpeed();if(speed>0)break;}NetworkStore.getInstance().setMaxBand(speed);}/*** 处理PortStatsReply数据包* @param reply* @param sw*/public void handlePortStatsReply(OFPortStatsReply reply, IOFSwitchBackend sw) {List<OFPortStatsEntry> entries=reply.getEntries();for(OFPortStatsEntry e:entries){System.out.println("*************接收的丢包数:"+e.getRxDropped()+"************");}}/*** 处理FlowStatsReply* @param reply* @param sw*/public void handleFlowStatsReply(OFFlowStatsReply reply, IOFSwitchBackend sw){OFSwitch fromSW=null; //源交换机OFSwitch toSW=null; //目的交换机OFPort in_Port=null; //接入端口OFPort out_Port=null; //输出端口long byteCount=0; //匹配字节数long maxBand=0; //最大带宽long currentBand=0; //当前带宽Log.info("*******************收到FlowStatsReply******************");fromSW=toSW=(OFSwitch)sw;List<OFFlowStatsEntry> entries= reply.getEntries();for(OFFlowStatsEntry e:entries){byteCount=e.getByteCount().getValue();in_Port=e.getMatch().get(MatchField.IN_PORT);if(in_Port==null){ //匹配域中并没有指定入端口,所以此流表项要发给控制器in_Port=OFPort.ALL;}//得到out_portList<OFInstruction> instruction=e.getInstructions(); //得到流表项的指令集for(OFInstruction i:instruction){if(i instanceof OFInstructionApplyActions){ //如果当前的instruction是OFInstructionApplyActions类/接口的一个实例List<OFAction> actions=((OFInstructionApplyActions) i).getActions();for(OFAction a:actions){if(a.getType()== OFActionType.OUTPUT){out_Port=((OFActionOutput)a).getPort();break;}}}elsecontinue;}//默认的流表项不需要存储 默认流表项:进/出端口的值小于1if(in_Port==OFPort.ALL || out_Port.getPortNumber()<1 ){continue;}//流表项非默认流表项,则需要构造链路信息对象// maxBand=calculateMaxBand(fromSW,toSW,in_Port,out_Port); 基于物理交换机获取的最大带宽maxBand=NetworkStore.getInstance().getMaxBand();LinkDataInfo linkData=new LinkDataInfo();linkData.setIn_Port(in_Port);linkData.setOut_Port(out_Port);linkData.setByteCount(byteCount);linkData.setFromSW(fromSW);linkData.setToSW(toSW);linkData.setMaxBand(maxBand);// Log.info("对象构造完毕");//存储storeLinkStatus(linkData);}}//存储public void storeLinkStatus(LinkDataInfo linkDataInfo){/*** 流表项种类:* 入端口 :1 出端口 :2 匹配 :IP 100kb* 入端口 :1 出端口 :2 匹配 : TCP 100kb* 入端口 :1 出端口 :2 匹配 : HTTP 200kb** 所以需要合并流表项*/if(currentLinkStatus.size()==0) //第一次currentLinkStatus.add(linkDataInfo);else{for(LinkDataInfo l:currentLinkStatus){//判断是否是同一链路信息,若是更新链路信息的相关字段,如不是则将新的链路信息添加进来 源和目的交换机及源和目的端口相同就是同一链路信息if(l.getFromSW().getId() ==linkDataInfo.getFromSW().getId() &&l.getToSW().getId() == linkDataInfo.getToSW().getId() &&l.getIn_Port().getPortNumber() ==linkDataInfo.getIn_Port().getPortNumber() &&l.getOut_Port().getPortNumber() ==linkDataInfo.getOut_Port().getPortNumber()){l.setByteCount(l.getByteCount()+linkDataInfo.getByteCount());}}//当前存储的链路信息linkDataInfo,在所有存储中不存在,则添加此链路信息currentLinkStatus.add(linkDataInfo);}}public void nextMeterBegin(){ //下一次测量的开始historyLinkStatus.clear();for(LinkDataInfo l:currentLinkStatus){historyLinkStatus.add(l);}currentLinkStatus.clear();//清空LinkDataInfo保存之前的链路信息linkTimeStatus.clear(); //清空LinkTimeInfo保存之前链路上的时延信息}//计算链路的最大带宽public long calculateMaxBand(OFSwitch fromSW, OFSwitch toSW, OFPort inPort,OFPort outPort){long fromBand=0,toBand=0;//inport入口带宽OFPortDesc inPortDesc= fromSW.getPort(inPort);Set<OFPortFeatures> inFeatures=inPortDesc.getAdvertised(); //物理特性的声明//for(OFPortFeatures f:inFeatures){fromBand=f.getPortSpeed().getSpeedBps(); //单位bpsSystem.out.println("******fromBand**********:"+fromBand);if(fromBand>0)break; //inFeatures表示的属性有三个,只有第一个不是0,剩下全是0}//outPort 出口带宽OFPortDesc outPortDesc=toSW.getPort(outPort);Set<OFPortFeatures> outFeatures=outPortDesc.getAdvertised();for(OFPortFeatures f:outFeatures){toBand=f.getPortSpeed().getSpeedBps();if(toBand > 0)break;}System.out.println("-----------"+fromBand +" "+toBand+"-----------");return (fromBand>=toBand?toBand:fromBand);}//计算当前带宽,输出public void calCurrentBand(){for(LinkDataInfo h:historyLinkStatus)for(LinkDataInfo c:currentLinkStatus){if(h.getFromSW().getId()==c.getFromSW().getId() &&h.getToSW().getId()==c.getToSW().getId() &&h.getIn_Port().getPortNumber()==c.getIn_Port().getPortNumber() &&h.getOut_Port().getPortNumber()==c.getOut_Port().getPortNumber()){long speed=(c.getByteCount()-h.getByteCount())/1; //1秒钟之内增加的流量float band=(float)(speed*1.0/c.maxBand);System.out.println("currentSpeed: "+speed+"Bps"+"---------"+"currentBand: "+band*100+"%");}}}}//
class LinkDataInfo{ //交换机之间链路数据信息protected OFSwitch fromSW; //源交换机protected OFSwitch toSW; //目的交换机protected OFPort in_Port; //接入端口protected OFPort out_Port; //输出端口protected long byteCount; //匹配字节数protected long maxBand; //最大带宽protected long currentBand; //当前带宽public OFSwitch getFromSW() {return fromSW;}public void setFromSW(OFSwitch fromSW) {this.fromSW = fromSW;}public OFSwitch getToSW() {return toSW;}public void setToSW(OFSwitch toSW) {this.toSW = toSW;}public OFPort getIn_Port() {return in_Port;}public void setIn_Port(OFPort in_Port) {this.in_Port = in_Port;}public OFPort getOut_Port() {return out_Port;}public void setOut_Port(OFPort out_Port) {this.out_Port = out_Port;}public long getByteCount() {return byteCount;}public void setByteCount(long byteCount) {this.byteCount = byteCount;}public long getMaxBand() {return maxBand;}public void setMaxBand(long maxBand) {this.maxBand = maxBand;}public long getCurrentBand() {return currentBand;}public void setCurrentBand(long currentBand) {this.currentBand = currentBand;}
}class LinkTimeInfo{protected Link link; //链路信息protected long allTime=-1;protected long controllerToSrcSwTime=-1; //控制器到源交换机的时延protected long controllerToDesSWTime=-1; //控制器到目的交换机的时延protected long delay=-1; //链路时延public Link getLink() {return link;}public void setLink(Link link) {this.link = link;}public long getAllTime() {return allTime;}public void setAllTime(long allTime) {this.allTime = allTime;}public long getControllerToSrcSwTime() {return controllerToSrcSwTime;}public void setControllerToSrcSwTime(long controllerToSrcSwTime) {this.controllerToSrcSwTime = controllerToSrcSwTime;}public long getControllerToDesSWTime() {return controllerToDesSWTime;}public void setControllerToDesSWTime(long controllerToDesSWTime) {this.controllerToDesSWTime = controllerToDesSWTime;}public long getDelay() {return delay;}public void setDelay(long delay) {this.delay = delay;}
}
(4)BandMeter类
/*** 带宽测量* --主动测量 : 控制器下发消息* --被动测量Flowremove :流过期时会自动将流的相关信息上交给控制器** 带宽预测* --小波分析* --深度学习* --神经网络*/
public class BandMeter {public BandMeter(){}/*** 下发指定报文* OpenFlow1.3中有两种类型的统计* --OFFlowStatsRequest : 针对每一条流的信息* --OFAggregateStatsRequest : 针对所有流的信息* @param sw*/public void doBand(IOFSwitch sw){//交换机下发statsrequest报文OFFlowStatsRequest.Builder statsRequest=sw.getOFFactory().buildFlowStatsRequest(); //根据交换机得到OpenFlow工厂,然后创建出statsrequest报文statsRequest.setTableId(TableId.ALL);statsRequest.setOutPort(OFPort.ANY);statsRequest.setOutGroup(OFGroup.ANY);statsRequest.setCookie(AppCookie.makeCookie(2,0));Log.info("************send statsRequest************");//交换机下发OFPortDescStatsRequest.Builder descRequest = sw.getOFFactory().buildPortDescStatsRequest();Log.info("************send descRequest*************");sw.write(descRequest.build());sw.write(statsRequest.build()); //把构造出来的request对象进行序列化,变成一个字节数组,通过交换机下发}
}
(5)PacketLossMeter类
/*** 丢包率*/
public class PacketLossMeter {public PacketLossMeter() {}public void doPacketLoss(IOFSwitch sw){Collection<OFPortDesc> ports=sw.getEnabledPorts(); //获取交换机启用的接口(插网线或有连接亮灯的接口)for(OFPortDesc pd :ports){OFPortStatsRequest.Builder portStatsRequestBuild = sw.getOFFactory().buildPortStatsRequest();portStatsRequestBuild.setPortNo(pd.getPortNo());//发送OFPortStatsRequest包sw.write(portStatsRequestBuild.build());}}
}
(6)TimeDelayMeter类
/*** 时延*/
public class TimeDelayMeter {public static byte[] destinationMACAddress={0x00,0x00,0x00,0x00,0x00,0x00}; //8C-C6-81-CE-53-33 --->-116,-58,-127,0-50,0x53,0x33public static byte[] sourceinationMACAddress={0x00,0x00,0x00,0x00,0x00,0x01};public final static int MacAddressLength=6;public TimeDelayMeter(){}/*** 判断当前处理的数据包是不是时延需要的数据包* @return*/public boolean isDoingTimeDelayMeter(Ethernet ethernet){byte[] desmac= ethernet.getDestinationMACAddress().getBytes();byte[] sourcemac=ethernet.getSourceMACAddress().getBytes();if(desmac.length!=MacAddressLength || sourcemac.length!=MacAddressLength){return false;}for(int i=0;i<MacAddressLength;i++){if(desmac[i] !=destinationMACAddress[i] ||sourcemac[i] != sourceinationMACAddress[i]){System.out.println("****************isDoingTimeDelayMeter:false*****************");return false;}}System.out.println("****************isDoingTimeDelayMeter:True*****************");return true;}public void doTimeDelay(NetworkMeter networkMeter){//1.获取链路ILinkDiscoveryService linkService = networkMeter.getLinkService();Map<Link, LinkInfo> links = linkService.getLinks(); //links中存放所有路径的双向链路System.out.println("***************链路长度:"+links.size()+"****************");for(Link l:links.keySet()){//2.得到链路两端的交换机IOFSwitch fromSW = networkMeter.getSwitchService().getSwitch(l.getSrc());IOFSwitch toSW = networkMeter.getSwitchService().getSwitch(l.getDst());OFPort inPort = l.getSrcPort();OFPort outPort = l.getDstPort();//3.向交换机发送消息sendPacketOut(fromSW,inPort,toSW, outPort); //发送PacketOut包后,可以得到从控制器到交换机-交换机再到控制器的总时延sendEchoRequest(fromSW); //控制器向交换机发送EchoRequest后,可以得到控制器到交换机的时延sendEchoRequest(toSW);System.out.println("***********发送PacketOut,EchoRequest***************");}}/*** 控制器向交换机发送PacketOut包* @param fromSW* @param inPort* @param toSW* @param outPort*/public void sendPacketOut(IOFSwitch fromSW, OFPort inPort, IOFSwitch toSW, OFPort outPort){OFPacketOut.Builder packetOutBuilder = fromSW.getOFFactory().buildPacketOut();//指定ActionList<OFAction> actions =new ArrayList<OFAction>();actions.add(fromSW.getOFFactory().actions().output(inPort,Integer.MAX_VALUE)); //注意参数是inPort,因为报文从源交换机fromW发出,经过链路上的inPort才能进入目的交换机toSW。packetOutBuilder.setActions(actions); //PacketOut包中存在一些转发指令//发送的消息:Ethernet (以太帧格式包括:目的地址、源地址、类型(用来标志上一层使用的是什么协议)、数据、FCS)Ethernet eth=new Ethernet();eth.setDestinationMACAddress(destinationMACAddress);eth.setSourceMACAddress(sourceinationMACAddress);eth.setEtherType(EthType.IPv4);//发送的消息:IPIPv4 ip=new IPv4();ip.setSourceAddress(0);ip.setDestinationAddress(0);ip.setProtocol(IpProtocol.NONE);//时间戳timestampStringBuilder sb=new StringBuilder(getCurruentTime());sb.append("<>").append(fromSW.getId()).append("<>").append(inPort.getPortNumber()).append("<>").append(toSW.getId()).append("<>").append(outPort.getPortNumber());String mess=new String(sb);Data data = new Data(); // Data extend iPacketdata.setData(mess.getBytes());ip.setPayload(data);eth.setPayload(ip);packetOutBuilder.setData(eth.serialize());fromSW.write(packetOutBuilder.build());System.out.println("********send PacketOut**********");}/*** 控制器向交换机发送EchoRequest* @param sw*/public void sendEchoRequest(IOFSwitch sw){OFEchoRequest.Builder buildEchoRequest = sw.getOFFactory().buildEchoRequest();//时间戳timestampStringBuilder sb=new StringBuilder(getCurruentTime());sb.append("<>").append(sw.getId());String mess=new String(sb);Data data=new Data();data.setData(mess.getBytes());buildEchoRequest.setData(data.serialize());System.out.println("********send EchoRequest**********");sw.write(buildEchoRequest.build());}/*** 得到当前的系统时间* @return*/public String getCurruentTime(){SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");return df.format(new Date());}
}
(7)修改Floodlight源代码
1)修改src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java代码,主要是处理StatsReply(统计数据包)时进行分类,是PORT_StatsReply还是FLOW_StatsReply,r如果是FLOW_StatsReply就是我们自行处理。
主要修改的内容如下:
//添加握手之前的信息void processOFStatsReply(OFStatsReply m) {switch(m.getStatsType()) {case PORT_DESC://处理descStatsReplyNetworkMeter.handleDescStatsReply((OFPortDescStatsReply) m);processPortDescStatsReply((OFPortDescStatsReply) m);break;//添加case FLOW: //networkmeter更改NetworkMeter.handleFlowStatsReply((OFFlowStatsReply)m,sw);break;case PORT: //networkMeter更改NetworkMeter.handPortStatsReply((OFPortStatsReply)m,sw);break;default:unhandledMessageReceived(m);}}
2)修改src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
void processOFMessage(OFMessage m)throws IOException {// Handle Channel Handshakeif (!state.channelHandshakeComplete) {switch(m.getType()) {case HELLO:processOFHello((OFHello)m);break;case ERROR:processOFError((OFErrorMsg)m);break;case FEATURES_REPLY:processOFFeaturesReply((OFFeaturesReply)m);break;case EXPERIMENTER:processOFExperimenter((OFExperimenter)m);break;/* echos can be sent at any time */case ECHO_REPLY:processOFEchoReply((OFEchoReply)m);break;case ECHO_REQUEST:processOFEchoRequest((OFEchoRequest)m);break;case PORT_STATUS:processOFPortStatus((OFPortStatus)m);break;default:illegalMessageReceived(m);break;}}else{switch(m.getType()){case ECHO_REPLY://添加EchoReply处理函数NetworkMeter.handleEchoReply((OFEchoReply)m);processOFEchoReply((OFEchoReply)m);break;case ECHO_REQUEST:processOFEchoRequest((OFEchoRequest)m);break;// Send to SwitchManager and thus higher orders of controldefault:sendMessageToConnection(m);break;}}}
修改内容:
//添加EchoReply处理函数NetworkMeter.handleEchoReply((OFEchoReply)m);
当链路建立完成时,我们在计算链路之间的时延时,需要发送EchoRequest数据包获取控制器到交换机之间的时间,而接收EchoReply数据包在Floodlight控制器的OFChannelHandler.java类中。
3.4测试结果
注:此处我没搞清Floodlight中获取到速度的单位,所以带宽计算出现问题。
注:由于在minnet仿真工具上测试,且拓扑简单所以不存在丢包情况
此时拓扑图:
控制器信息:
mininet开启:
通过h1 ping h4测试
基于Ubuntu18.04的OVS与Mininet仿真工具安装及网络测量应用案例相关推荐
- 对基于ubuntu18.04搭建双线adsl路由器和私有云服务器(samba、ftp和http)一文的更正2
在<基于ubuntu18.04搭建双线adsl路由器和私有云服务器(samba.ftp和http)>一文中对于adsl断线重拨后自动更新路由表的脚本,少了自动平衡双线adsl流量的命令,将 ...
- 基于Ubuntu18.04下深度学习服务器搭建
基于Ubuntu18.04下深度学习服务器搭建 目录: 基于Ubuntu18.04下深度学习服务器搭建 主要模块组成 Anaconda安装 CUDA安装 pytorch安装 CuDNN安装 其他常用指 ...
- 对基于ubuntu18.04搭建双线adsl路由器和私有云服务器(samba、ftp和http)一文的更正
在<基于ubuntu18.04搭建双线adsl路由器和私有云服务器(samba.ftp和http)>一文中有两处错误,更正如下: 1)"7b. 配置双线adsl路由表" ...
- Ubuntu18.04 + Nvida GTX 1660ti显卡 驱动安装
文章目录 Ubuntu18.04 + Nvidia GTX 1660ti显卡 驱动安装 删除旧的显卡驱动(如果装有cuda,就不要删除了) 检查你的nvida显卡驱动版本,找到免费推荐的驱动 安装驱动 ...
- 基于Ubuntu16.04的GeForce GTX 1080驱动安装,遇到的问题及对应的解决方法
基于Ubuntu16.04的GeForce GTX 1080驱动安装,遇到的问题及对应的解决方法 参考文章: (1)基于Ubuntu16.04的GeForce GTX 1080驱动安装,遇到的问题及对 ...
- Ubuntu18.04 intel wifi6 ax201无线网卡驱动安装
Ubuntu18.04 intel wifi6 ax201无线网卡驱动安装 前言 新买的笔记本电脑装Ubuntu系统,发现没有无线网卡,经查阅资料发现由于网卡刚没多久,Ubuntu没有集成网卡驱动,需 ...
- Ubuntu18.04 + NVIDIA Quadro T1000显卡驱动安装
Ubuntu18.04 + NVIDIA Quadro T1000显卡驱动安装 下载官方驱动安装文件 官网搜索,下载对应版本的run file 禁用Nouveau sudo gedit /etc/mo ...
- Ubuntu18.04系统下charm-crypto0.5的安装以及测试
Ubuntu18.04系统下charm-crypto0.5的安装以及测试 文章目录 前言 一.前期准备 二.编译安装OpenSSL 1.检查已安装的OpenSSL版本 三.安装GMP 1.下载GMP压 ...
- Docker:基于ubuntu18.04的介绍,安装与使用 - 最新无死角
以下链接是个人关于深度学习环境搭建的所有链接,包含了各个框架: 深度学习环境搭建-史上最全无死角系列 有兴趣的朋友可以添加微信 17575010159 相互交流. 注意:本人编写该博客的时间为2020 ...
最新文章
- python3--装饰器
- 2020-11-25(多级页表的补充)
- websocket 获取连接id_nodejs做后端,用websocket写聊天室,怎么获取连接用户的ip呢?...
- c语言实现结构体变量private,C语言中结构体变量私有化详解
- 如何把开源项目发布到Jcenter
- 21-04-08 cms日志分析
- 我奋斗了18年才和你坐在一起喝咖啡
- php斗鱼弹幕接口,php实现斗鱼弹幕,一起来欣赏弹幕吧~
- 小学计算机键盘指法课件,小学信息技术键盘指法练习.ppt
- CN_计算机网络体系结构概念@IP数据报(分组)结构@各层报文(PDU)之间的关系@PDU协议数据单元
- echarts(一)之地图连线动效
- google maps js v3 api教程(3) -- 创建infowindow
- 使用监听器Listener实现在线人数统计功能
- C# wpf 实现简单的颜色板
- 基于unity的愤怒的小鸟设计
- WPF 窗口最大化正确方法
- Was8.5静默安装完整步骤
- 神州泰岳王宁:从高校团委书记到中关村富豪
- (附源码)计算机毕业设计SSM智能导诊系统
- 软件评测师——知识产权与标准化
热门文章
- 05_02_Mybatis||day02_Mybatis第二天||(总结篇,原文很详细)
- PHP常用正则表达式
- 测试——Web网站测试主要测试那些内容
- win7更新错误0x800b0109_解决公司 Win7 更新时的 0x80092004 错误
- 腾讯数据库TcaplusDB X 大主宰·大千世界|万家决战,谁领风骚
- 公众号自动回复小程序 html代码,图文和公众号自动回复跳转小程序
- 遍历二叉树的递归算法
- 小心“格局很大”的人!
- java版mc要多少美元,Minecraft Java版 20w28a 发布
- #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道