目录

一、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仿真工具安装及网络测量应用案例相关推荐

  1. 对基于ubuntu18.04搭建双线adsl路由器和私有云服务器(samba、ftp和http)一文的更正2

    在<基于ubuntu18.04搭建双线adsl路由器和私有云服务器(samba.ftp和http)>一文中对于adsl断线重拨后自动更新路由表的脚本,少了自动平衡双线adsl流量的命令,将 ...

  2. 基于Ubuntu18.04下深度学习服务器搭建

    基于Ubuntu18.04下深度学习服务器搭建 目录: 基于Ubuntu18.04下深度学习服务器搭建 主要模块组成 Anaconda安装 CUDA安装 pytorch安装 CuDNN安装 其他常用指 ...

  3. 对基于ubuntu18.04搭建双线adsl路由器和私有云服务器(samba、ftp和http)一文的更正

    在<基于ubuntu18.04搭建双线adsl路由器和私有云服务器(samba.ftp和http)>一文中有两处错误,更正如下: 1)"7b. 配置双线adsl路由表" ...

  4. Ubuntu18.04 + Nvida GTX 1660ti显卡 驱动安装

    文章目录 Ubuntu18.04 + Nvidia GTX 1660ti显卡 驱动安装 删除旧的显卡驱动(如果装有cuda,就不要删除了) 检查你的nvida显卡驱动版本,找到免费推荐的驱动 安装驱动 ...

  5. 基于Ubuntu16.04的GeForce GTX 1080驱动安装,遇到的问题及对应的解决方法

    基于Ubuntu16.04的GeForce GTX 1080驱动安装,遇到的问题及对应的解决方法 参考文章: (1)基于Ubuntu16.04的GeForce GTX 1080驱动安装,遇到的问题及对 ...

  6. Ubuntu18.04 intel wifi6 ax201无线网卡驱动安装

    Ubuntu18.04 intel wifi6 ax201无线网卡驱动安装 前言 新买的笔记本电脑装Ubuntu系统,发现没有无线网卡,经查阅资料发现由于网卡刚没多久,Ubuntu没有集成网卡驱动,需 ...

  7. Ubuntu18.04 + NVIDIA Quadro T1000显卡驱动安装

    Ubuntu18.04 + NVIDIA Quadro T1000显卡驱动安装 下载官方驱动安装文件 官网搜索,下载对应版本的run file 禁用Nouveau sudo gedit /etc/mo ...

  8. Ubuntu18.04系统下charm-crypto0.5的安装以及测试

    Ubuntu18.04系统下charm-crypto0.5的安装以及测试 文章目录 前言 一.前期准备 二.编译安装OpenSSL 1.检查已安装的OpenSSL版本 三.安装GMP 1.下载GMP压 ...

  9. Docker:基于ubuntu18.04的介绍,安装与使用 - 最新无死角

    以下链接是个人关于深度学习环境搭建的所有链接,包含了各个框架: 深度学习环境搭建-史上最全无死角系列 有兴趣的朋友可以添加微信 17575010159 相互交流. 注意:本人编写该博客的时间为2020 ...

最新文章

  1. python3--装饰器
  2. 2020-11-25(多级页表的补充)
  3. websocket 获取连接id_nodejs做后端,用websocket写聊天室,怎么获取连接用户的ip呢?...
  4. c语言实现结构体变量private,C语言中结构体变量私有化详解
  5. 如何把开源项目发布到Jcenter
  6. 21-04-08 cms日志分析
  7. 我奋斗了18年才和你坐在一起喝咖啡
  8. php斗鱼弹幕接口,php实现斗鱼弹幕,一起来欣赏弹幕吧~
  9. 小学计算机键盘指法课件,小学信息技术键盘指法练习.ppt
  10. CN_计算机网络体系结构概念@IP数据报(分组)结构@各层报文(PDU)之间的关系@PDU协议数据单元
  11. echarts(一)之地图连线动效
  12. google maps js v3 api教程(3) -- 创建infowindow
  13. 使用监听器Listener实现在线人数统计功能
  14. C# wpf 实现简单的颜色板
  15. 基于unity的愤怒的小鸟设计
  16. WPF 窗口最大化正确方法
  17. Was8.5静默安装完整步骤
  18. 神州泰岳王宁:从高校团委书记到中关村富豪
  19. (附源码)计算机毕业设计SSM智能导诊系统
  20. 软件评测师——知识产权与标准化

热门文章

  1. 05_02_Mybatis||day02_Mybatis第二天||(总结篇,原文很详细)
  2. PHP常用正则表达式
  3. 测试——Web网站测试主要测试那些内容
  4. win7更新错误0x800b0109_解决公司 Win7 更新时的 0x80092004 错误
  5. 腾讯数据库TcaplusDB X 大主宰·大千世界|万家决战,谁领风骚
  6. 公众号自动回复小程序 html代码,图文和公众号自动回复跳转小程序
  7. 遍历二叉树的递归算法
  8. 小心“格局很大”的人!
  9. java版mc要多少美元,Minecraft Java版 20w28a 发布
  10. #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道