目录
具体问题可以私聊博主
一、设计目标
1.1应用场景介绍
1.2应用场景设计要求
网络配置方式
网络技术要求
网络拓扑要求
互联互通
二、课程设计内容与原理
(1)预期网络拓扑结构和功能
(1)网络设备信息
(2)DHCP自动配置
(6)防火墙技术
(7)DHCP
三、课程设计方案
四、课程设计配置与实现
五、课程设计验证与结果分析
代码
网络拓扑
WLAN代码
参考资料
一、设计目标
1.1应用场景介绍
现有一个中学,需要设计一个中小型网络满足学校的网络需求。
学校有教学区,实验室区域和服务器区域。教学区为了满足接入的灵活性和信号强度要求,需要满足设计WIFI满足日常教室和同学需求,接入上限为200人。实验室区域需要有一台教师主机方便管理实验室其余电脑,并且教师主机可以访问学校服务器,方便为同学们实验室授课。学校服务器区域设有学校相关重要文件和学校网页相关内容。最后学校还会接入互联网。
1.2应用场景设计要求
-
网络配置方式
网络配置的方式应该便捷,方便管理和排错,主机和转发设备应该采用什么配置方式,服务器采用什么类型。
-
网络技术要求
SDN架构实现数控分离、防火墙,路由协议,NAT,其余适合的网络技术或私有技术。
-
网络拓扑要求
链路要有冗余度,链路同时还要具有复杂度。
-
互联互通
仿真设计的网络需要互联互通,访问互联网,与其他小组互通
二、课程设计内容与原理
2.1网络拓扑
(1)预期网络拓扑结构和功能
图2-1 网络拓扑示意图
注:此网络拓扑只是表示拓扑联系,不代表实际实现拓扑
网络1是WIFI区域,此区域主要对应应用场景中的教学区域,此区域拥有多个无线AP接入点,可以实现设备的AP接入点切换。
网络2是实验室区域,此部分区域由三个交换机成环连接,在此需要实现STP,以此满足实验室考试网络拥堵和网络故障特殊情况,保证网络的质量和考试的网络质量。
网络3是学校的服务器区域,此区域部署了三台服务器,分别是DHCP服务器,网页服务器和文件服务器,三台服务器通过交换机连接在路由器上面。
路由区域部分需要实现报文的传递和交付。
最后整个校区会通过一个路由器接入互联网。
- 实际网路拓扑
在我们实现的网络拓扑当中,网络的拓扑结构如下。
图2-2 实际拓扑示意图
子网1下的交换机连接三个无线AP接入点,站点通过不同的策略连接到AP之上(后续会详细叙述)。
2.2网络配置
(1)网络设备信息
表2-1 WLAN0中设备接口IP地址分配情况
设备 | 接口 | IP地址 | 子网掩码 | |
WLAN0 | sta1 | sta1-wlan0 | 192.168.4.1 | 255.255.255.0 |
sta2 | sta2-wlan0 | 192.168.4.2 | 255.255.255.0 | |
sta3 | sta3-wlan0 | 192.168.4.3 | 255.255.255.0 | |
sta4 | sta4-wlan0 | 192.168.4.4 | 255.255.255.0 | |
sta5 | sta5-wlan0 | 192.168.4.5 | 255.255.255.0 | |
sta6 | sta6-wlan0 | 192.168.4.6 | 255.255.255.0 |
表2-2 子网2设备和接口的IP设置
设备 | IP地址 | 子网掩码 | |
子网2 | H1 | 192.168.1.1 | 255.255.255.0 |
H2 | DHCP服务器分配(地址池192.168.1.2~192.168.1.6) | 255.255.255.0 | |
H3 | 255.255.255.0 | ||
H4 | 255.255.255.0 | ||
H5 | 255.255.255.0 | ||
H6 | 255.255.255.0 | ||
默认网关 | R2-eth2 | 192.168.1.254 | 255.255.255.0 |
表2-3 服务器区域设备和接口IP划分
设备 | IP地址 | 子网掩码 | |
子网3 | H7 | 192.168.2.1 | 255.255.255.0 |
H8 | 192.168.2.2 | 255.255.255.0 | |
默认网关 | R3-eth2 | 192.168.2.254 | 255.255.255.0 |
表2-4 路由器IP设置
路由器 | 接口 | IP | 掩码 |
R1 | eth1 | 192.168.3.2 | 255.255.255.252 |
R1 | eth2 | 192.168.3.5 | 255.255.255.252 |
R1 | eth3 | 192.168.10.2 | 255.255.255.252 |
R2 | eth1 | 192.168.3.1 | 255.255.255.252 |
R2 | eth2 | 192.168.1.254 | 255.255.255.0 |
R3 | eth1 | 192.168.3.6 | 255.255.255.252 |
R3 | eth2 | 192.168.2.254 | 255.255.255.0 |
表2-5 交换机连接设置
交换机 | 接口 | 连接设备 |
S1 | eth1 | R2 |
eth2 | S2 | |
eth3 | S3 | |
eth4 | H1 | |
eth5 | H2 | |
S2 | eth1 | S1 |
eth2 | H3 | |
eth3 | H4 | |
eth4 | S3 | |
S3 | eth1 | S1 |
eth2 | H5 | |
eth3 | H6 | |
eth4 | S2 | |
S4 | eth1 | R3 |
eth2 | H7 | |
eth3 | H8 |
(2)DHCP自动配置
网络配置采用自动配置有诸多的优点,下面列举了一些:
可以很容易地在网络中添加新的客户端。
IP地址是由DHCP集中管理的。
IP地址可以重复使用,从而减少了对IP地址总数的要求。
DHCP服务器上的IP地址空间可以很容易地进行重新配置,而不需要单独重新配置客户端。
网络管理员可以利用DHCP协议提供的方法,从集中区域配置网络。
综上,我们网络采用DHCP的配置方式。
2.3网络技术和原理
- SDN
1)SDN的网络体系架构
应用层:这一层主要是体现用户意图的各种上层应用程序,此类应用程序称为协同层应用程序,典型的应用包括OSS(Operation support system 运营支撑系统)、Openstack等。传统的IP网络同样具有转发平面、控制平面和管理平面,SDN网络架构也同样包含这3个平面,只是传统的IP网络是分布式控制的,而SDN网络架构下是集中控制的。
控制层:控制层是系统的控制中心,负责网络的内部交换路径和边界业务路由的生成,并负责处理网络状态变化事件。
转发层:转发层主要由转发器和连接器的线路构成基础转发网络,这一层负责执行用户数据的转发,转发过程中所需要的转发表项是由控制层生成的。
北向接口:应用层和控制层通信的接口,应用层通过控制开放的API,控制设备转发功能
南向接口:控制层和数据层通信的接口,控制器通过OpenFlow或其他协议下发流表
图2-3 SDN架构示意图
2)SDN的主要特征
转控分离:网元的控制平面在控制器上,负责协议计算,产生流表;而转发平面只在网络设备上。
集中控制:设备网元通过控制器集中管理和下发流表,这样就不需要对设备进行逐一操作,只需要对控制器进行配置即可。
开放接口:第三方应用只需要通过控制器提供的开放接口,通过编程方式定义一个新的网络功能,然后在控制器上运行即可。
SDN控制器既不是网管,也不是规划工具:
网管没有实现转控分离:网管只负责管理网络拓扑、监控设备告警和性能、下发配置脚本等操作,但这些仍然需要设备的控制平面负责产生转发表项。
- STP
生成树协议(英语:Spanning Tree Protocol,STP),是一种工作在OSI网络模型中的第二层(数据链路层)的通信协议,基本应用是防止交换机冗余链路产生的环路。用于确保以太网中无环路的逻辑拓扑结构。从而避免了广播风暴,大量占用交换机的资源。
在SDN中,如果Mininet建立的拓扑中存在交换机环路,则如果利用普通的Ryu Learning Switch APP进行ryu-manager部署,会出现ping、pingall不通的问题,其原因在于环路中出现了广播风暴。为了在Mininet中使用带有环路的拓扑,需要让交换机开启STP协议。
STP协议实现的关键是BDPU封包,通过传送该包发现环路,决定交换机的哪些端口需要Forward,哪些需要Block。下图展示了BDPU包的传送过程。
图2-4 STP协议实现关键-BDPU包
- OPSF路由
开放式最短路径优先(Open Shortest Path First,OSPF)是广泛使用的一种动态路由协议,它属于链路状态路由协议,具有路由变化收敛速度快、无路由环路、支持变长子网掩码(VLSM)和汇总、层次区域划分等优点。
在网络中使用OSPF协议后,大部分路由将由OSPF协议自行计算和生成,无须网络管理员人工配置,当网络拓扑发生变化时,协议可以自动计算、更正路由,极大地方便了网络管理。
OSPF协议依靠五种不同类型的分组来建立邻接关系和交换路由信息,即问候分组、数据库描述分组、链路状态请求分组、链路状态更新分组和链路状态确认分组:
图2-5 OSPF协议的5种分组及功能
- NAT
NAPT在进行地址转换的同时还进行端口转换,可以实现多个私网用户共同使用一个公网IP地址上网。NAPT根据端口来区分不同用户,真正做到了地址复用。
图2-6 NAPT原理图
当Host访问Web Server时,设备的处理过程如下:
1.设备收到Host发送的报文后查找NAT策略,发现需要对报文进行地址转换。
2.设备根据源IP Hash算法从NAT地址池中选择一个公网IP地址,替换报文的源IP地址,同时使用新的端口号替换报文的源端口号,并建立会话表,然后将报文发送至Internet。
3.设备收到Web Server响应Host的报文后,通过查找会话表匹配到步骤2中建立的表项,将报文的目的地址替换为Host的IP地址,将报文的目的端口号替换为原始的端口号,然后将报文发送至Intranet。
- WIFI
①WLAN仿真平台
WLAN部分使用Mininet-WiFi进行仿真。Mininet-WiFi 是 Mininet SDN 网络仿真器的一个分支,通过添加基于标准 Linux 无线驱动程序和 80211_hwsim 无线仿真驱动程序的虚拟化 WiFi 站和接入点来扩展 Mininet 的功能。这意味着已添加新类以支持在 Mininet 网络场景中添加这些无线设备并模拟移动站的属性,例如相对于接入点的位置和移动。
下图描述了一个简单拓扑结构中的组件和连接,其中有两个站点(或主机)是使用 Mininet-WiFi 创建的,其中新实现的组件(以灰色突出显示)沿原始 Mininet 构建块显示。尽管工作站默认配备无线接口,但它们也可以通过有线链路(veth 对)与接入点连接。
图2-7 一个简单拓扑结构中的组件和连接[1]
②无线站点
站(sta)是通过身份验证和关联连接到接入点的设备。在Mininet-WiFi中,每个站都有一个无线网卡(staX-wlan0,其中 X 为每个站的编号)。
③接入点(AP)
AP是管理关联站的设备。通过 hostapd 守护程序进行虚拟化,并将虚拟无线接口用于接入点和身份验证服务器。Mininet-WiFi 当前包括对用户空间参考实现和内核和用户空间模式下的 Open vSwitch 的支持。
无线站点和接入点都使用 cfg80211 与无线设备驱动程序通信,这是一个 Linux 802.11 配置 API,可提供工作站与 mac80211 之间的通信。该框架反过来通过用于配置 cfg80211 设备和内核-用户空间通信的 netlink 套接字(或更具体地说是 nl80211)直接与 WiFi 设备驱动程序通信。[1]
④频谱分析
仿真使用802.11b标准,在下图给出的例子中,可以看出802.11b标准是如何在2.4835Ghz的2.4GHz频段上定义13个信道,每个信道分配22MHz,间隔5MHz。可以看出,通过这种安排,只有通道 1、6 和 11 可以在没有频带重叠的情况下运行。
正在上传…重新上传取消图2-8 802.11b中信道定义情况[2]
⑤主动扫描与被动扫描
对于一个站点来说,如果希望连接到AP,首先必须发现AP。发现的方式就两种被动扫描(passive scan)和主动扫描(active scan)。
通俗地讲,主动即“搜”,是指sta每隔一段时间发送probe request帧,用来询问AP的信息,probe request可以单独发给某个AP,也可以在一个信道上进行广播,多数时候是在一个信道上进行广播,广播完成后,会在当前信道等待一段时间,如果收到帧反馈,则做记录,如果超时,就会切换到下个信道进行广播。
被动即“听”,是指手机被动的接收AP发出的beacon帧,通过解析beacon帧中的信息来获取当前AP列表。手机在监听beacon帧的时候也不停切换信道,保证每个信道都能监听到。这种模式下,只监听beacon帧,不发送探测帧,比较节省资源,不过获取AP列表时间相对较长,不如主动扫描获得信息更及时。
客户端如果通过主动扫描发现很多AP具有相同SSID,它会根据信号强度和信号质量选择最佳的AP连接,通常客户端还会持续在其他信道发送probe request,以得到一个可用AP的list,用于漫游。这是我们后续使用的“后台扫描”原理。
(6)防火墙技术
它是一种位于内部网络与外部网络之间的网络安全系统。是一项信息安全的防护系统,依照特定的规则,允许或是限制传输的数据通过。
网络层防火墙可视为一种 IP 封包过滤器,运作在底层的TCP/IP协议堆栈上。可以以枚举的方式只允许符合特定规则的封包通过,其余的一概禁止穿越防火墙(病毒除外,防火墙不能防止病毒侵入)。这些规则通常可以经由管理员定义或修改,不过某些防火墙设备可能只能套用内置的规则。
下图展示了防火墙的设置规则,通过设置能透过数据报文的源和目的以及通过协议类型和操作类型来设置防火墙。
图2-9 防火墙技术的原理示意图
应用层防火墙是在 TCP/IP 堆栈的“应用层”上运作,您使用浏览器时所产生的数据流或是使用 FTP 时的数据流都是属于这一层。应用层防火墙可以拦截进出某应用程序的所有封包,并且封锁其他的封包(通常是直接将封包丢弃)。理论上,这一类的防火墙可以完全阻绝外部的数据流进到受保护的机器里。
(7)DHCP
1. DHCP的原理
DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是一个局域网的网络协议,使用UDP协议工作, 主要有两个用途:给内部网络或网络服务供应商自动分配IP地址,给用户或者内部网络管理员作为对所有计算机作中央管理的手段。
DHCP的整个工作流程如下图所示,主要有四个阶段:
图2-10 DHCP工作流程
1)发现阶段,DHCP客户端以广播的形式寻找DHCP服务器。
2)提供阶段,接收到DHCP Discover的服务器都会响应,DHCP服务器从未出租的IP地址中挑选一个给客户端,发送DHCP Offer包。
3)选择阶段,DHCP客户端选择第一个收到的DHCP Offer包信息来接收,以广播方式回答一个DHCP Request请求信息。
4)确认阶段,被选择的DHCP服务器确认所提供的IP地址,发送DHCP Ack,未被选择的服务器回收曾提供的IP地址。
2.DHCP的工作方式
1)下载一个DHCP服务器。
2)下面有两种方式:
1.直接修改步骤1中的DHCP服务器的dhcpd.conf文件,此方法可以通过编写subnet进行DHCP中继,但有一个使用前提为当前拓扑能够ping通。
2.自己在各个子网中搭建分DHCP服务器,并自主生成各个服务器的.conf和发送端的.log文件。此方式在拓扑无法ping通的情况下仍可以实现DHCP服务。
本次选用的是步骤2中的第二个方法。
3.网络配置
在上述子网1和子网2中分别选用一台主机作为DHCP服务器,本次实验子网1选用的是h1,子网2选用的是h7。
三、课程设计方案
3.1 SDN设计方案
SDN的架构我们采用mininet+RYU 的方式,使用RYU控制器控制我们网络拓扑里面的交换机,以此来下发流表。
图3-1 SDN架构拓扑结构
注:左侧WIFI部分实际拓扑以图2-2为准
图3-1中使用的控制器C0,C1皆是远程控制器RYU,下图3-2是RYU控制器的结构示意图。
图3-2 RYU架构
此外,由于控制器只能设定一个端口,我们在实现防火墙应用程序的时候增加了控制器C1。
3.2 OSPF设计方案
我们没有选择使用openflow交换机通过流表来实现路由,因为我们发现它能实现的前提是整个网络的ip都是由统一的大地址划分而来。而使用专门的ospf路由协议则没有这样的限制。
OSPF的设计我们采用了quagga软件,它可以将linux设备变成一个功能完整的路由器。支持rip,ospf,bgp等协议。这里我们只使用它的ospf功能。
图3-3 ospf运行流程示意图
上图中的ospfd守护进程负责ospf协议的路由更新,而zebra守护进程则负责更新内核的路由表。
图3-4 ospf相关文件配置情况
3.3 STP的设计方案
我们设计的校园网络中,涉及到实验室场景的模拟。我们希望实验室区域的网络拥有充足的健壮性,所以我们设计了环路,希望正常情况能防止环路形成,特殊情况(某条链路断开)能恢复连通性,保证网络畅通。
aaaaaa
图3-5 STP拓扑设计方案示意图
如图所示的形成环路的三台交换机,都连接了同一个ryu控制器c0,那么我们只需要ryu对应的模块上进行设定就可以实现stp协议。
3.4 DHCP的设计方案
1)尝试过的方案——DHCP
1.配置信息
图3-6 DHCP建立流程图
创建两台unbantu18.0的虚拟机A,B,在一台虚拟机A上面配置DHCPservever,使其成为DHCP服务器,而在虚拟机B上搭建拓扑,使用添加网卡的方式使得两台虚拟机实现互通,使得虚拟机B上面中mininet的主机能够访问虚拟机A,从而实现DHCP的功能。
2.存在着的问题
此方法能够实现对虚拟机B的IP地址的自动分配,但无法实现对mininet中的主机进行IP地址的自动分配。
自我诊断原因:由于一开始IP地址的缺失,导致虚拟机B上的网络处于ping不通的未完成状态,因此,即使虚拟机A能够实现与虚拟机B的通信,但无法进行对未完成状态的网络进行DHCP。
- 最终使用的方案——DHCP
1.配置信息
图3-7 DHCP建立流程图
本方案使用1台虚拟机A,在此台虚拟机上配置DHCP服务器,在每个需要DHCP的子网中选取一台主机作为该子网的DHCP服务器,然后规定该子网的地址池,最后生成上述DHCP服务器的独有的.conf文件。当请求方发出请求时,会生成对该主机的.log文件,然后对DHCP服务器进行相应,使用mininet中host.defaultIntf.updateIP()函数进行IP地址的更新。
- 实现的方案——DHCP中继(未使用)
1.配置信息
图3-8 DHCP中继建立流程图
在2)中的DHCP基础之上,对虚拟机中配置的DHCP服务器自带的dhcpd.conf进行地址池的配置——加入sudnet,其余与.conf文件的配置一致。
此方案无需在各个子网中单独设置DHCP服务器,只需在整个拓扑上层直接使用下载的DHCP服务器即可。
最终能够实现各个子网的IP地址的分配,即实现了DHCP中继。
2.发现的问题
此方案需要在各个子网中以及子网之间均需要能够ping通,所以在初始化网络的各项配置的阶段无法使用,因此没有将此功能加入网络拓扑。
3.5 NAT的设计方案
NAPT的设计方案是采用Mininet自带的NAT类型结点,通过将此节点的接口与虚拟机的网卡绑定,实现自动的NAPT转换,同时与外网通信。并且实现从虚拟机访问Mininet的设备。
图3-9 NAPT流程图
结合整个网络的拓扑,最终的NAPT方案如下:
图3-10 NAPT实现拓扑图
3.6防火墙的设计方案
防火墙的设计我们采用的是Mininet+RYU的方式,RYU运行防火墙程序。如下图所示,在拓扑的右侧当中是我们设置的防火墙控制器,交换机S4运行防火墙程序,阻塞不能通过
的流表。其中运行防火墙的控制器和另一个RYU控制器C0不是同一控制器,因为同一个控制器运行两个程序会存在冲突,所以采用了两台控制器。
图3-11 防火墙拓扑设计方案示意图
RYU控制器的防火墙程序是自带的rest_firewall.py文件,可以通过RESTAPI连接到RYU控制器防火墙后台设置下发的规则,下图是可以设置过滤的报文的类型和内容。
图3-12 rest_firewal.py文件可以设置的类型和内容
3.7 WIFI的设计方案
WLAN中,我们计划使用三个AP,每个AP有三个站点对应连接。AP与站点的空间位置关系如下图所示:
图3-13 无线网络中的接入点与站点物理位置示意
容易看出,三个AP的覆盖范围存在交叠。为了避免交叠带来信号冲突,从而导致无线网络效率下降,我们将这三个AP的信道分别配置成1、6和11。这样,三个AP发射的信号在频谱上就完全没有交叠。具体的原理我们在之前的原理介绍部分已经提到了,在此将频谱图复现。
图3-14 802.11b中信道定义情况[2]
三个AP拥有相同的SSID,统一命名为’handover’,并且使用WPA-2认证,使用‘123456789a’作为密码。
终端配置使用后台扫描(bgscan)完成在ESS内的漫游。配置后,bgscan将以定期和预配置的时间间隔搜索与客户端当前关联的接入点相比具有最佳信号的接入点。如果找到具有最佳信号的接入点,bgscan将启动客户端与接入点之间的关联过程。信号质量优良的判断原则有信号强度、负载大小等,此处我们选用信号强度作为信号质量优良判断的原则,即终端在选择AP时,遵循信号最强AP优先原则。
本组最初的方案是使用Mininet-WiFi直接完成网络中所有模块和功能的配置,但Mininet-WiFi与本组使用的zebra等其他工具会发生冲突,且我们在有限的时间无法解决这个问题,导致我们必须把WLAN部分与网络的其他部分分离开,分开运行两个网络以避免冲突。
但将网络分开后,无法避免的一个问题就是如何将WLAN部分接入其余网络。我们借助了所谓的隧道技术,原理是Mininet内的出口OVS(Open vSwitch)占用宿主机ubuntu主机网卡,从而达到Mininet内虚拟主机与ubuntu宿主机在网络中同等地位的目的。为了实现这一技术,我们首先需要在两个Ubuntu虚拟机上各再创建一张新的网卡。创建网络卡后虚拟机可以用如下示意图表示:
图3-15 为两台虚拟机新加网卡
再将两张网卡(Ubuntu1的ens34和Ubuntu2的ens32)桥接在一起即可。这样,Mininet-WiFi就作为有线网络交换机S1下的一个局域网的一部分了。
综上所述,WLAN部分设计详细拓扑如下:
图3-16 WLAN详细拓扑(设备接口连接情况)
四、课程设计配置与实现
4.1 SDN设计配置与实现
SDN采用RYU控制器和mininet的方式,交换机采用的是openflow13协议。
- 配置
主要是涉及Mininet和RYU控制器的安装,一起路由定义软件Quagga的安装和配置。
详细的配置步骤在网络上都可以找到,在此不在叙述。
- 实现
第一步:打开RYU控制器,运行相应的程序(比如simple_switch_13.py默认路由,rest_firewall.py防火墙程序)
第二步:运行拓扑代码,连接到控制器
4.2 OSPF设计配置与实现
ospf采用quagga实现,需要对每个路由器配置相应的守护进程,在通过命令行开启ospf算法,最终实现路由的收敛。
(1)配置
1.在quagga安装目录里写配置文件
r1ospfd.conf(设置r1的直连网络) |
hostname r1_ospfd password 123 enable password 123 router ospf ospf router-id 192.168.3.2 network 192.168.3.0/30 area 0 network 192.168.3.4/30 area 0 network 192.168.10.0/30 area 0 debug ospf event log stdout |
r2ospfd.conf(设置r2的直连网络) |
hostname r2_ospfd password 123 enable password 123 router ospf ospf router-id 192.168.3.1 network 192.168.1.0/24 area 0 network 192.168.3.0/30 area 0 debug ospf event log stdout |
r3ospfd.conf(设置r3的直连网络) |
hostname r3_ospfd password 123 enable password 123 router ospf ospf router-id 192.168.3.6 network 192.168.2.0/24 area 0 network 192.168.3.4/30 area 0 debug ospf event log stdout |
r1zebra.conf、r2zebra.conf、r3zebra.conf 3个文件无需额外配置,直接复制示例模板即可。
2.命令行启动quagga及运行
命令行打开quagga软件,命令如下:
sudo zebra -d |
软件打开后,在拓扑代码final.py中嵌入以下命令,运行ospf:
r1.cmd('zebra -f /etc/quagga/r1zebra.conf -d -z /tmp/r1zebra.api -i /tmp/r1zebra.interface') r2.cmd('zebra -f /etc/quagga/r2zebra.conf -d -z /tmp/r2zebra.api -i /tmp/r2zebra.interface') r3.cmd('zebra -f /etc/quagga/r3zebra.conf -d -z /tmp/r3zebra.api -i /tmp/r3zebra.interface') time.sleep(1) # time for zebra to create api socket r1.cmd('ospfd -f /etc/quagga/r1ospfd.conf -d -z /tmp/r1zebra.api -i /tmp/r1ospfd.interface') r2.cmd('ospfd -f /etc/quagga/r2ospfd.conf -d -z /tmp/r2zebra.api -i /tmp/r2ospfd.interface') r3.cmd('ospfd -f /etc/quagga/r3ospfd.conf -d -z /tmp/r3zebra.api -i /tmp/r3ospfd.interface') |
(2)实现
通过直接运行final.py文件
图4-1启动ospf操作示意图
4.3 STP设计配置与实现
Stp采用ryu控制器实现,通过设置可以处理的流表类型来允许处理BDPU包,从而开启stp,实现防止环路形成/链路恢复。
(1)配置
在ryu的模块simple_switch_13.py中配置
simple_switch_13.py |
…… …… from ryu.lib import stplib #改变1:引入stplib,以支持STP协议
class SimpleSwitch13(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] _CONTEXTS = {'stplib': stplib.Stp} #改变2:指定STP协议参考类 …… …… #@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) @set_ev_cls(stplib.EventPacketIn, MAIN_DISPATCHER)#改变3:将EventOFPPacketIn 换成EventPacketIn,后者封装了前者,且可以处理BDPU包 …… …… |
(2)实现
由于是直接在ryu控制器中进行设置,那么无需在拓扑代码中再进行设置,直接运行拓扑文件final.py文件即可。
图4-2启动stp操作示意图
4.4 DHCP设计配置与实现
DHCP采用的是下载额外的DHCP服务器,然后通过mininet自带的host.defaultIntf.updateIP()函数进行IP地址的更新。
- 配置
checkRequired()函数检查各项配置 |
def checkRequired(): "Check for required executables" required = [ 'udhcpd', 'udhcpc', 'dnsmasq', 'curl', 'firefox' ] for r in required: if not quietRun( 'which ' + r ): print '* Installing', r print quietRun( 'apt-get install -y ' + r ) if r == 'dnsmasq': print quietRun( 'update-rc.d dnsmasq disable' ) |
- 实现
主要流程如下图:
图4-3 DHCP运行流程图
下面介绍各个流程核心代码
地址池的设置(例子) |
DNSTemplate = """ start #开始IP end #结束IP option subnet #子网掩码 option domain local option lease 7 # seconds """ |
创建DHCP服务器.conf文件 |
def makeDHCPconfig( filename, intf, gw, dns ): "Create a DHCP configuration file" config = ( 'interface %s' % intf, DNSTemplate, 'option router %s' % gw, 'option dns %s' % dns, '' ) with open( filename, 'w' ) as f: f.write( '\n'.join( config ) ) |
启动DHCP服务器 |
def startDHCPserver( host, gw, dns ): "Start DHCP server on host with specified DNS server" info( '* Starting DHCP server on', host, 'at', host.IP(), '\n' ) dhcpConfig = '/tmp/%s-udhcpd.conf' % host makeDHCPconfig( dhcpConfig, host.defaultIntf(), gw, dns ) host.cmd( 'udhcpd -f', dhcpConfig, '1>/tmp/%s-dhcp.log 2>&1 &' % host ) |
启动请求方,并创建.log文件 |
def startDHCPclient( host ): "Start DHCP client on host" intf = host.defaultIntf() host.cmd( 'dhclient -v -d -r', intf ) host.cmd( 'dhclient -v -d 1> /tmp/dhclient.log 2>&1', intf, '&' ) |
请求方等待IP地址,若失败则进行重复请求 |
def waitForIP( host ): "Wait for an IP address" info( '*', host, 'waiting for IP address' ) while True: host.defaultIntf().updateIP() if host.IP(): break info( '.' ) sleep( 1 ) info( '\n' ) |
至此,DHCP分配结束。
4.5 NAT配置与实现
NAPT的配置在拓扑代码里实现。通过以下代码在拓扑中加入NAT节点。
NAT节点添加代码 |
nat0 = self.addNode('nat0', cls=NAT, ip='192.168.10.1/30', subnet='192.168.0.0/16', inNamespace=False) |
其中subnet属性用于配置nat节点所负责的子网范围,只有处于subnet内的主机通信时,nat节点才会为其提供转换。
之后我们还要为所有节点配置默认路由,并为nat节点配置路由使得外面发送的信息能够成功传入Mininet内。
默认路由配置代码 |
r1.cmd('route add default gw 192.168.10.1 dev r1-eth3') r2.cmd('route add default gw 192.168.3.2 dev r2-eth1') r3.cmd('route add default gw 192.168.3.5 dev r3-eth1') nat0.cmd('route add -net 192.168.0.0/16 gw 192.168.10.2 dev nat0-eth1') |
通过以上配置之后,Mininet内的主机便可以与外界主机通信了。
4.6防火墙配置与实现
防火墙采用的是RYU控制器,通过在RYU控制器中的防火墙程序(rest_firewall.py)文件进行实现。
(1)配置
1.通过编写代码设置防火墙规则
Firewall.py(实现防火墙的过滤规则) |
import requests data1='{"nw_src":"192.168.1.1","nw_dst":"192.168.2.1"}' data2='{"nw_src":"192.168.2.1","nw_dst":"192.168.1.1"}' data3='{"nw_src":"192.168.2.2","nw_dst":"192.168.1.1"}' data4='{"nw_src":"192.168.1.1","nw_dst":"192.168.2.2"}' def firewall(): # set rules for switch by ryu/ryu.app/rest_firewall.py # data1-data4 is the rule by myself) response = requests.put('http://localhost:8080/firewall/module/enable/0000 000000000004') response= requests.post('http://127.0.0.1:8080/firewall/rules/0000000000000004', data=data1) response = requests.post('http://127.0.0.1:8080/firewall/rules/0000000000000004', data=data2) response = requests.post('http://127.0.0.1:8080/firewall/rules/0000000000000004', data=data3) response = requests.post('http://127.0.0.1:8080/firewall/rules/0000000000000004', data=data4)
firewall() |
2.命令行设置防火墙规则
命令行打开S4,设置为可以enable状态,配置命令如下:
curl -X PUT http://localhost:8080/firewall/module/enable/0000000000000004 |
之后在enable状态下,在链接到RYU控制器后台设置防火墙的过滤规则。
curl -X POST -d '{"nw_src":"192.168.2.1/32","nw_dst":"192.168.1.1/32"}' http://127.0.0.1:8080/firewall/rules/0000000000000004 curl -X POST -d '{"nw_src":"192.168.1.1/32","nw_dst":"192.168.2.1/32"}' http://127.0.0.1:8080/firewall/rules/0000000000000004 |
(2)实现
1)启动控制器C1的终端,启动RYU控制器的防火墙程序
图4-4 启动防火墙操作示意图
- 通过直接运行firewall.py文件或者命令行方式设置防火墙过滤的规则
图4-5 启动设置防火墙规则操作示意图
4.7 WIFI配置与实现
WLAN部分的实现以代码为主,以下展示具体的实现思路和核心代码。
(1)设置网络类型为Mininet-WiFi,并配置控制器
设置网络类型为Mininet-WiFi,并配置控制器 |
net = Mininet_wifi(topo=None, build=False, link=wmediumd , wmediumd_mode=interference) info('***Adding controllerln ' ) c1= net.addController( name= 'c1 ',controller=Controller, protocol= 'tcp ' ,port=6653) |
(2)配置AP的名称、MAC地址、SSID、信道编号、加密方式、密码和物理位置。
配置AP(以AP1为例) |
ap1 = net.addAccessPoint( 'ap1', mac='00:00:00:00:00:01', ssid="handover",mode="g", channel="1", passwd= '123456789a',encrypt='wpa2', position='123.0,323.0,0') |
(3)配置站点的名称、IP地址、物理位置、扫描信号强度门限、短间隔、长间隔和扫描模式。
配置站点(以sta1为例) |
sta1 = net.addStation( 'sta1', ip= '192.168.4.1/24',position= '54.0,434.0,0 ', bgscan_threshold=-60,s_inverval=5, l_interval=10,bgscan_module="simpie") |
(4)设置无线信号的指数传播模型
设置无线信号的指数传播模型 |
net.setPropagationModel( model="logDistance" , exp=3) |
4.8虚拟机隧道配置与实现
为了实现两个Mininet网络的互连,我们首先需要在两个Ubuntu虚拟机上各再创建一张新的网卡。创建新网卡后,需要将新添加的网卡与Open vSwitch交换机端口绑定。具体实现过程如下:
①在两个Ubuntu虚拟机上各再创建一张新的网卡。添加新的虚拟机网卡需要在VMware Player中设置,如图。
图4-6 创建一张新的网卡
②进入虚拟机,释放新添加的虚拟机网卡。使用的具体命令如图。
图4-7 释放虚拟机网卡使用的命令
③然后将两台虚拟机中新创建的虚拟网卡绑定到同一VMnet网卡上(即下图的VMnet0),并且需要将对应VMnet设置成桥接模式。
图4-8 虚拟机网卡配置
从上图可以看出,VMnet0网卡桥接到了物理主机的无线网卡(Microsoft-WiFi Direct Virtual Adapter)上,因此,我们可以画出如下的设备连接逻辑图。
图4-9 占用网卡后的无线网网络拓扑
④将OVS交换机端口与刚释放出来的虚拟网卡相绑定。使用的代码如下:
代码-将OVS交换机端口与刚释放出来的虚拟网卡相绑定 |
import os …… os. popen( 'ovs-vsctl add-port s1 ens34 ') |
五、课程设计验证与结果分析
5.1 SDN的验证和结果分析
(1)操作验证分析
图5-1 RYU控制器运行simple_switch_13.py程序
从图5-2中可以看到已经连接到控制器C0,控制器C1还未连接到的原因是控制器C1还没有运行防火墙程序(后续会讲到)。
图5-2 拓扑对于远程控制器的反应
(2)实验验证
后续防火墙技术,STP,OSPF,DHCP的成功也可以验证SDN。
5.2 OSPF的验证与结果分析
(1)路由表项对比分析
在主机通过dhcp分配到ip后,ospf协议开始运行,以路由器R3的路由表为例,网络收敛前的表项:
图5-3 R3未收敛时的路由表项展示图
经过约40s后,路由完全收敛,再次查看R3的路由表:
图5-4 R3收敛后的路由表项展示图
(2)抓包分析
用wireshark抓包后,我们发现有大量的ospf的hello报文
图5-5ospf运行时报文展示
(3)网路ping测试
从下图中可以看到,在路由收敛前,只有在同一子网中的主机可以ping通,跨子网的主机ping不通。
图5-6 路由收敛前的pingall情况
当路由收敛后,不同子网下的主机也能ping通(不通的为防火墙规则)
图5-7 路由收敛后的pingall情况
5.3 STP的验证与结果分析
(1)流表分析
运行ryu和拓扑代码后,开始运行stp
图5-8 stp运行时产生的流表输出
经过约40s后,s链路收敛,流表输出中告知s3的port2被关闭;
图5-9 R3链路收敛后的展示图
(2)抓包分析
用wireshark抓包后,我们发现有大量的广播的stp报文
图5-10 stp运行时报文展示
(3)网路ping测试
从下图中可以看到,stp协议运行成功
图5-11 stp收敛后的pingall情况
(4)异常情况测试
我们考虑stp的初衷是,增强链路的健壮性,设置冗余链路。这也要求当正常链路出现故障(断开)后,网络也能保持联通。 所以我们以断开s2与s3的链路为例:
图5-12 断开s2与 s3的链路
我们发现,链路断开后,网络发生了变化,立刻有stp的流表输出,说明网络正在计算新的拓扑。
图5-13链路变化后的流表输出
等stp重新收敛后,进行pingall测试,结果与链路变化前一致,达到了设计目的:
图5-14链路变化后的pinall情况
5.4 DHCP的验证与结果分析(其中例子)
(1)实验效果
在地址池设定如下的情况下:
图5-15 地址池设定
实验结果如下
图5-16 实验过程图示
(2)结果分析
截获到如图所示的报文:
图5-17 截获报文总览
对Discover报文进行分析:
图5-18 Discover报文总览
由图可知广播地址为255.255.255.255,Src为0.0.0.0(设定需要DHCP服务主机的初始IP)。
对offer报文进行分析:
图5-19 offer报文总览
Src为192.168.2.3即设定的该子网内的DHCP服务器的IP地址,此时目的IP为255.255.255.255即进行广播,其中附带了所分配的IP地址为192.168.2.20。
对Request报文进行分析:
图5-20 Request报文总览
此时Src仍为0.0.0.0,发送方式为广播。
对ACK报文进行分析:
图5-21 ACK报文总览
此时可以看到Src为192.168.2.3,重复了Your (client) IP address:192.168.2.20。
至此,整个DHCP服务运行完成。
5.5 NAT的验证与结果分析
(1)内网ping外网连通性测试
同时使用网络中的h1和h2ping外网,最终的连通性如下:
图5-22 ping外网验证图
可以看到h1和h2都是可以成功ping通外网的。
(2)NAPT测试
之后我们再通过windows上的抓包可以验证h1和h2在ping外网时使用的是同一个IP地址。
图5-23 ping外网wireshark抓包图
上图中可以看到h1和h2都在使用192.168.137.136这一个地址ping外网。证明我们实现了NAPT功能。
(3)Linux虚拟机ping内部主机
我们在linux虚拟机中ping h1的IP地址,其结果如下:
图5-24 ping内网验证图
同时我们在wireshark的抓包中也能抓到相应的报文:
图5-25 ping内网wireshark抓包图
5.6防火墙的验证与结果分析
(1)网络连通性测试
在整个网络进行pingall测试,发现只有H1(应用环境中的教师主机)可以访问服务器H7和H8,其余的主机和路由器是不能访问服务器的,符合我们的预期的效果和设置的规则。
图5-26 防火墙pingall测试结果展示图
(2)流表分析
从交换机S4的流表中可以看出设置的四条规则的操作时正常的,说明:防火墙是默认阻塞所有的报文,我们设置可以通过的IP流向的报文以此来达到防火墙目的。当然,同时我们也可以设置默认所有的报文都可以通过,设置不可以通过报文的规则。由于应用场景的需求,我们采用了第一种方案。
图5-27交换机S4的流表展示
(3)控制器终端分析
从下图中可以看到,控制器终端C1的返回结果,相应的规则已经被加入到防火墙中。
图5-28 控制器C1终端防火墙规则设置返回结果
从控制终端的流表中也可以看到,被阻塞的报文如下。只要是我们在防火墙程序中没有设置的规则,都会被防火墙程序阻塞,无法通过交换机S4。
图5-29 控制器C1的流表
5.7 WLAN的验证与结果分析
(1)网络连通性测试
由于pingall命令产生了非常多行结果,这里只展示部分截图。pingall可以很直观地看出WLAN内部的连通性没有问题。
图5-30 WLAN中使用pingall命令测试结果的部分截图
(2)后台扫描结果演示
为了更清晰地演示后台扫描过程与结果,此处只保留sta1一个站点,并且我们改变了ap3的位置,使得sta1与站点间的距离关系更加直观。
①如图,sta1最初距离ap1最近。根据最强信号优先原则,sta1应该关联ap1。
图5-31 后台扫描实验使用的网络拓扑
②验证:sta1确实关联ap1,黄框内为ap1的MAC地址。
图5-32 后台扫描实验中设备关联最初情况
③打开sta1命令行,观察sta1后台扫描过程。
图5-33 后台扫描实验中打开sta1的终端
④改变sta1的物理位置。
图5-34 后台扫描实验中改变sta1的位置
⑤sta1后台扫描发现ap2。
图5-35 后台扫描实验中sta1发现ap2
⑥能够看到实际上sta1与接入点的关联确实发生了变化。
图5-36 后台扫描实验中设备关联最终情况
(3)结果分析:
通过上面的实验可以得到结论:通过配置后台扫描参数,终端能够在连接到某一个AP后仍然以某一个时间周期进行扫描,并且主动连接到与当前AP的SSID相同且信号最强的AP。这说明我们配置的终端能够实现ESS内的漫游。
代码
附件2:final.py(网络拓扑,DHCP,NAT,SDN,防火墙控制器设置) |
# !/usr/bin/python3 from mininet.topo import Topo from mininet.net import Mininet from mininet.node import Node from mininet.log import setLogLevel, info from mininet.cli import CLI import os from mininet.node import Controller, RemoteController, OVSController from mininet.link import TCLink from mininet.util import quietRun from mininet.term import makeTerms from sys import exit, stdin, argv from re import findall from time import sleep import time from mininet.nodelib import NAT class LinuxRouter(Node): "A Node with IP forwarding enabled."
def config(self, **params): super(LinuxRouter, self).config(**params) # Enable forwarding on the router self.cmd('sysctl net.ipv4.ip_forward=1')
def terminate(self): self.cmd('sysctl net.ipv4.ip_forward=0') super(LinuxRouter, self).terminate()
class NetworkTopo(Topo): "A LinuxRouter connecting three IP subnets"
def build(self, **_opts): defaultIP1 = '192.168.3.2/30' # IP address for r1-eth1 defaultIP2 = '192.168.3.1/30' defaultIP3 = '192.168.3.6/30' router1 = self.addNode('r1', cls=LinuxRouter, ip=defaultIP1) router2 = self.addNode('r2', cls=LinuxRouter, ip=defaultIP2) router3 = self.addNode('r3', cls=LinuxRouter, ip=defaultIP3) nat0 = self.addNode('nat0', cls=NAT, ip='192.168.10.1/30', subnet='192.168.0.0/16', inNamespace=False)
h1 = self.addHost('h1', ip='192.168.1.1/24', defaultRoute='via 192.168.1.254') # define gateway h2 = self.addHost('h2', ip='0.0.0.0/24', defaultRoute='via 192.168.1.254') h3 = self.addHost('h3', ip='0.0.0.0/24', defaultRoute='via 192.168.1.254') h4 = self.addHost('h4', ip='0.0.0.0/24', defaultRoute='via 192.168.1.254') h5 = self.addHost('h5', ip='0.0.0.0/24', defaultRoute='via 192.168.1.254') h6 = self.addHost('h6', ip='0.0.0.0/24', defaultRoute='via 192.168.1.254') h7 = self.addHost('h7', ip='192.168.2.1/24', defaultRoute='via 192.168.2.254') h8 = self.addHost('h8', ip='192.168.2.2/24', defaultRoute='via 192.168.2.254') h9 = self.addHost('h9', ip='0.0.0.0', defaultRoute='via 192.168.2.254') h10 = self.addHost('h10', ip='0.0.0.0', defaultRoute='via 192.168.2.254') h11 = self.addHost('h11', ip='0.0.0.0', defaultRoute='via 192.168.2.254')
self.addLink(router1, router2, intfName1='r1-eth1', intfName2='r2-eth1') self.addLink(router3, router1, intfName1='r3-eth1', intfName2='r1-eth2', params2={'ip': '192.168.3.5/30'}) self.addLink(router1, nat0, intfName1='r1-eth3', intfName2='nat0-eth1',params1={'ip': '192.168.10.2/30'})
s1=self.addSwitch('s1') s2=self.addSwitch('s2') s3=self.addSwitch('s3') s4=self.addSwitch('s4') #s5=self.addSwitch('s5') self.addLink(s1, router2, intfName2='r2-eth2', params2={'ip': '192.168.1.254/24'}) self.addLink(s4, router3, intfName2='r3-eth2', params2={'ip': '192.168.2.254/24'}) self.addLink(s1, s2) self.addLink(s1, s3) #self.addLink(s2, s3) self.addLink(s1, h1) self.addLink(s1, h2) self.addLink(s2, h3) self.addLink(s2, h4) self.addLink(s3, h5) self.addLink(s3, h6) self.addLink(s3, s2) #self.addLink(s4, s5) self.addLink(s4, h7) self.addLink(s4, h8) #self.addLink(s5, h7) #self.addLink(s5, h8) self.addLink(s4, h9) self.addLink(s4,h10) self.addLink(s4,h11)
DNSTemplate = """ start 192.168.1.2 end 192.168.1.6 option subnet 255.255.255.0 option domain local option lease 7 # seconds """
def makeDHCPconfig( filename, intf, gw, dns ): "Create a DHCP configuration file" config = ( 'interface %s' % intf, DNSTemplate, 'option router %s' % gw, 'option dns %s' % dns, '' ) with open( filename, 'w' ) as f: f.write( '\n'.join( config ) ) def startDHCPserver( host, gw, dns ): "Start DHCP server on host with specified DNS server" info( '* Starting DHCP server on', host, 'at', host.IP(), '\n' ) dhcpConfig = '/tmp/%s-udhcpd.conf' % host makeDHCPconfig( dhcpConfig, host.defaultIntf(), gw, dns ) host.cmd( 'udhcpd -f', dhcpConfig, '1>/tmp/%s-dhcp.log 2>&1 &' % host ) def stopDHCPserver( host ): "Stop DHCP server on host" info( '* Stopping DHCP server on', host, 'at', host.IP(), '\n' ) host.cmd( 'kill %udhcpd' )
# DHCP client functions def startDHCPclient( host ): "Start DHCP client on host" intf = host.defaultIntf() host.cmd( 'dhclient -v -d -r', intf ) host.cmd( 'dhclient -v -d 1> /tmp/dhclient.log 2>&1', intf, '&' ) def stopDHCPclient( host ): host.cmd( 'kill %dhclient' ) def waitForIP( host ): "Wait for an IP address" info( '*', host, 'waiting for IP address' ) while True: host.defaultIntf().updateIP() #print(host.IP()) if host.IP(): break info( '.' ) sleep(1) info( '\n' ) def readline(): "Read a line from stdin" return stdin.readline()
def prompt( s=None ): "Print a prompt and read a line from stdin" if s is None: s = "Press return to continue: " print (s), return readline()
def DHCP(host,dhcp): "Rogue DHCP server demonstration" startDHCPserver( dhcp, gw="192.168.1.254", dns='8.8.8.8') startDHCPclient( host ) waitForIP( host ) print(host.IP()) stopDHCPserver( dhcp ) stopDHCPclient( host ) def usage(): "Print usage message" print ("%s [ -h | -text ]") print ("-h: print this message") print ("-t: run in text/batch vs. firefox/x11 mode") def run(): topo = NetworkTopo() net = Mininet(topo=topo, build=False,controller=RemoteController) c0=net.addController('c0',ip='127.0.0.1',port=6633) #ryu controller c1=net.addController('c1',ip='127.0.0.1',port=6634) #ryu controller net.build() net.start() #net.get('s1').start([c0]) h10 = net.getNodeByName( 'h10') dhcp = net.getNodeByName('h1') h11 = net.getNodeByName('h11') h8 = net.getNodeByName( 'h8') h2 = net.getNodeByName( 'h2') h3 = net.getNodeByName( 'h3') h4 = net.getNodeByName( 'h4') h5 = net.getNodeByName( 'h5') h6 = net.getNodeByName( 'h6') h9 = net.getNodeByName( 'h9') s1 = net.getNodeByName( 's1') s2 = net.getNodeByName( 's2') s3 = net.getNodeByName( 's3') s4 = net.getNodeByName( 's4') #net.get('s4').start([c1])ss DHCP(h2,dhcp) DHCP(h3,dhcp) DHCP(h4,dhcp) DHCP(h5,dhcp) DHCP(h6,dhcp) #os.popen('ovs-vsctl add-port s1 ens36') net.get('s1').start([c0]) net.get('s2').start([c0]) net.get('s3').start([c0]) net.get('s4').start([c1]) #DHCP(h9,dhcp) #DHCP(h10,dhcp) #DHCP(h11,dhcp)
info('*** Routing Table on Router:\n')
r1 = net.getNodeByName('r1') r2 = net.getNodeByName('r2') r3 = net.getNodeByName('r3') nat0 = net.getNodeByName('nat0') s1 = net.getNodeByName('s1') s2 = net.getNodeByName('s2') s3 = net.getNodeByName('s3') #s1.cmd('ovs-vsctl set bridge s1 stp_enable=true') #s2.cmd('ovs-vsctl set bridge s2 stp_enable=true') #s3.cmd('ovs-vsctl set bridge s3 stp_enable=true') info('starting zebra and ospfd service:\n')
r1.cmd('route add default gw 192.168.10.1 dev r1-eth3') r2.cmd('route add default gw 192.168.3.2 dev r2-eth1') r3.cmd('route add default gw 192.168.3.5 dev r3-eth1') nat0.cmd('route add -net 192.168.0.0/16 gw 192.168.10.2 dev nat0-eth1')
r1.cmd('zebra -f /etc/quagga/r1zebra.conf -d -z /tmp/r1zebra.api -i /tmp/r1zebra.interface') r2.cmd('zebra -f /etc/quagga/r2zebra.conf -d -z /tmp/r2zebra.api -i /tmp/r2zebra.interface') r3.cmd('zebra -f /etc/quagga/r3zebra.conf -d -z /tmp/r3zebra.api -i /tmp/r3zebra.interface') time.sleep(1) # time for zebra to create api socket r1.cmd('ospfd -f /etc/quagga/r1ospfd.conf -d -z /tmp/r1zebra.api -i /tmp/r1ospfd.interface') r2.cmd('ospfd -f /etc/quagga/r2ospfd.conf -d -z /tmp/r2zebra.api -i /tmp/r2ospfd.interface') r3.cmd('ospfd -f /etc/quagga/r3ospfd.conf -d -z /tmp/r3zebra.api -i /tmp/r3ospfd.interface')
CLI(net) net.stop() os.system("killall -9 ospfd zebra") os.system("rm -f /tmp/*.api") os.system("rm -f /tmp/*.interface")
if __name__ == '__main__': setLogLevel('info') # setLogLevel('debug') run() |
附件3:WLAN.py |
# ! /usr/binpython from mininet.node import Controller, ovSKernelSwitch from mininet.log import setLogLevel, info from mn_wifi.net import Mininet_wifi from mn_wifi.node import Station, ovSKernelAPfrom mn_wifi.cli import CLI from mn_wifi.link import wmediumd from mn_wifi.wmediumdConnector import interference from subprocess import call from mininet.nodelib import NAT import os def myNetwork( ): net = Mininet_wifi(topo=None, build=False, link=wmediumd , wmediumd_mode=interference) info('***Adding controllerln ' ) c1= net.addController( name= 'c1 ',controller=Controller,protocol= 'tcp ' ,port=6653) info('***Add switches/APs\n ' ) ap1 = net.addAccessPoint( 'ap1', mac='00:00:00:00:00:01', ssid="handover",mode="g", channel="1", passwd= '123456789a',encrypt='wpa2', position='123.0,323.0,0') ap2 = net.addAccessPoint( 'ap2' , mac='00:00:00:00:00:02' , ssid="handover ",mode="g" , channel="6",passwd=' 123456789a',encrypt='wpa2', position='375.0, 332.0,0') ap3 = net.addAccessPoint( 'ap3 ' , mac='00:00:00:00:00:03', ssid="handover " ,node="g", channel="1",passwd=' 123456789a',encrypt='wpa2', position=' 606.0,313.0,0') s2 = net. addSwitch( 's2 ', cls=ovSKernelSwitch) info('***Add hosts/stations\n ' ) sta1 = net.addStation( 'sta1', ip= '192.168.4.1/24',position= '54.0,434.0,0 ', bgscan_threshold=-60,s_inverval=5, l_interval=10,bgscan_module="simpie") sta2 = net.addStation( 'sta2 ', ip= '192.168.4.2/24 ',position= '156.0,433.0,0 ', bgscan_threshold=-60,s_inverval=5, l_interval=10,bgscan_module="simpie") sta3 = net. addStation( 'sta3 ', ip= '192.168.4.3/24',position= '321.0,435.0,0 ', bgscan_threshold=-60,s_inverval=5, l_interval=10,bgscan_module="simpie") sta4 = net.addStation( 'sta4 ', ip= '192.168.4.4/24',position='430.0,433.0,0 ', bgscan_threshold=-60,s_inverval=5, l_interval=10,bgscan_module="simpie") sta5 = net.addStation( 'sta5 ', ip= '192.168.4.5/24',position= '555.0,429.0,0 ', bgscan_threshold=-60,s_inverval=5, l_interval=10,bgscan_module="simpie") sta6 = net.addStation( 'sta6 ' , ip= '192.168.4.6/24' ,position= '556,430,0 ', bgscan_threshold=-60,s_inverval=5, l_interval=10,bgscan_module="simpie") nat0=net. addHost( 'nat0' ,cls=NAT, ip= '192.168.4.8/24 ' , subnet=' 192.168.4.0/24',inNamespace=False) net. addLink(nat0,s1) info( "***Configuring Propagation Model\n" ) net.setPropagationModel( model="logDistance" , exp=3) info( "***Configuring wifi nodes\n " ) net . configurewifiNodes( ) info(·***Add linksln ')net. addLink( s1, s2) net.addLink(ap1, s2) net.addLink(s2, ap2) net. addLink(s2, ap3) net.plotGraph( max_x=1000,max_y=1000) info('***Starting networkin ' ) net. start() os. popen( 'ovs-vsctl add-port s1 ens34 ') CLI(net) net.stop( ) ifname__=='__main__': setLogLevel( info') myNetwork( ) |
附件4:
Rest_firewall.py(Ryu控制器防火墙程序) |
由于代码程序过长打包成文件一起发送 |
附件5:
Simple_switch_13.py(Ryu控制器路由程序+STP) |
from ryu.base import app_manager from ryu.controller import ofp_event from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER from ryu.controller.handler import set_ev_cls from ryu.ofproto import ofproto_v1_3 from ryu.lib.packet import packet from ryu.lib.packet import ethernet from ryu.lib.packet import ether_types from ryu.lib import stplib #change!!!!!
class SimpleSwitch13(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] _CONTEXTS = {'stplib': stplib.Stp} ##change!!!!!!!
def __init__(self, *args, **kwargs): super(SimpleSwitch13, self).__init__(*args, **kwargs) self.mac_to_port = {}
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): datapath = ev.msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser
# install table-miss flow entry # # We specify NO BUFFER to max_len of the output action due to # OVS bug. At this moment, if we specify a lesser number, e.g., # 128, OVS will send Packet-In with invalid buffer_id and # truncated packet data. In that case, we cannot output packets # correctly. The bug has been fixed in OVS v2.1.0. match = parser.OFPMatch() actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] self.add_flow(datapath, 0, match, actions)
def add_flow(self, datapath, priority, match, actions, buffer_id=None): ofproto = datapath.ofproto parser = datapath.ofproto_parser
inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] if buffer_id: mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, priority=priority, match=match, instructions=inst) else: mod = parser.OFPFlowMod(datapath=datapath, priority=priority, match=match, instructions=inst) datapath.send_msg(mod)
#@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) @set_ev_cls(stplib.EventPacketIn, MAIN_DISPATCHER)#change!!!!!!! def _packet_in_handler(self, ev): # If you hit this you might want to increase # the "miss_send_length" of your switch if ev.msg.msg_len < ev.msg.total_len: self.logger.debug("packet truncated: only %s of %s bytes", ev.msg.msg_len, ev.msg.total_len) msg = ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser in_port = msg.match['in_port']
pkt = packet.Packet(msg.data) eth = pkt.get_protocols(ethernet.ethernet)[0]
if eth.ethertype == ether_types.ETH_TYPE_LLDP: # ignore lldp packet return dst = eth.dst src = eth.src
dpid = format(datapath.id, "d").zfill(16) self.mac_to_port.setdefault(dpid, {})
self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
# learn a mac address to avoid FLOOD next time. self.mac_to_port[dpid][src] = in_port
if dst in self.mac_to_port[dpid]: out_port = self.mac_to_port[dpid][dst] else: out_port = ofproto.OFPP_FLOOD
actions = [parser.OFPActionOutput(out_port)]
# install a flow to avoid packet_in next time if out_port != ofproto.OFPP_FLOOD: match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) # verify if we have a valid buffer_id, if yes avoid to send both # flow_mod & packet_out if msg.buffer_id != ofproto.OFP_NO_BUFFER: self.add_flow(datapath, 1, match, actions, msg.buffer_id) return else: self.add_flow(datapath, 1, match, actions) data = None if msg.buffer_id == ofproto.OFP_NO_BUFFER: data = msg.data
out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port, actions=actions, data=data) datapath.send_msg(out) |
网络拓扑
# !/usr/bin/python3
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import Node
from mininet.log import setLogLevel, info
from mininet.cli import CLI
import os
from mininet.node import Controller, RemoteController, OVSController
from mininet.link import TCLink
from mininet.util import quietRun
from mininet.term import makeTerms
from sys import exit, stdin, argv
from re import findall
from time import sleep
import time
from mininet.nodelib import NAT
class LinuxRouter(Node):
"A Node with IP forwarding enabled."
def config(self, **params):
super(LinuxRouter, self).config(**params)
# Enable forwarding on the router
self.cmd('sysctl net.ipv4.ip_forward=1')
def terminate(self):
self.cmd('sysctl net.ipv4.ip_forward=0')
super(LinuxRouter, self).terminate()
class NetworkTopo(Topo):
"A LinuxRouter connecting three IP subnets"
def build(self, **_opts):
defaultIP1 = '192.168.3.2/30' # IP address for r1-eth1
defaultIP2 = '192.168.3.1/30'
defaultIP3 = '192.168.3.6/30'
router1 = self.addNode('r1', cls=LinuxRouter, ip=defaultIP1)
router2 = self.addNode('r2', cls=LinuxRouter, ip=defaultIP2)
router3 = self.addNode('r3', cls=LinuxRouter, ip=defaultIP3)
nat0 = self.addNode('nat0', cls=NAT, ip='192.168.10.1/30', subnet='192.168.0.0/16', inNamespace=False)
h1 = self.addHost('h1', ip='192.168.1.1/24', defaultRoute='via 192.168.1.254') # define gateway
h2 = self.addHost('h2', ip='0.0.0.0/24', defaultRoute='via 192.168.1.254')
h3 = self.addHost('h3', ip='0.0.0.0/24', defaultRoute='via 192.168.1.254')
h4 = self.addHost('h4', ip='0.0.0.0/24', defaultRoute='via 192.168.1.254')
h5 = self.addHost('h5', ip='0.0.0.0/24', defaultRoute='via 192.168.1.254')
h6 = self.addHost('h6', ip='0.0.0.0/24', defaultRoute='via 192.168.1.254')
h7 = self.addHost('h7', ip='192.168.2.1/24', defaultRoute='via 192.168.2.254')
h8 = self.addHost('h8', ip='192.168.2.2/24', defaultRoute='via 192.168.2.254')
h9 = self.addHost('h9', ip='0.0.0.0', defaultRoute='via 192.168.2.254')
h10 = self.addHost('h10', ip='0.0.0.0', defaultRoute='via 192.168.2.254')
h11 = self.addHost('h11', ip='0.0.0.0', defaultRoute='via 192.168.2.254')
self.addLink(router1, router2, intfName1='r1-eth1', intfName2='r2-eth1')
self.addLink(router3, router1, intfName1='r3-eth1', intfName2='r1-eth2',
params2={'ip': '192.168.3.5/30'})
self.addLink(router1, nat0, intfName1='r1-eth3', intfName2='nat0-eth1',params1={'ip': '192.168.10.2/30'})
s1=self.addSwitch('s1')
s2=self.addSwitch('s2')
s3=self.addSwitch('s3')
s4=self.addSwitch('s4')
#s5=self.addSwitch('s5')
self.addLink(s1, router2, intfName2='r2-eth2',
params2={'ip': '192.168.1.254/24'})
self.addLink(s4, router3, intfName2='r3-eth2',
params2={'ip': '192.168.2.254/24'})
self.addLink(s1, s2)
self.addLink(s1, s3)
#self.addLink(s2, s3)
self.addLink(s1, h1)
self.addLink(s1, h2)
self.addLink(s2, h3)
self.addLink(s2, h4)
self.addLink(s3, h5)
self.addLink(s3, h6)
self.addLink(s3, s2)
#self.addLink(s4, s5)
self.addLink(s4, h7)
self.addLink(s4, h8)
#self.addLink(s5, h7)
#self.addLink(s5, h8)
self.addLink(s4, h9)
self.addLink(s4,h10)
self.addLink(s4,h11)
DNSTemplate = """
start 192.168.1.2
end 192.168.1.6
option subnet 255.255.255.0
option domain local
option lease 7 # seconds
"""
def makeDHCPconfig( filename, intf, gw, dns ):
"Create a DHCP configuration file"
config = (
'interface %s' % intf,
DNSTemplate,
'option router %s' % gw,
'option dns %s' % dns,
'' )
with open( filename, 'w' ) as f:
f.write( '\n'.join( config ) )
def startDHCPserver( host, gw, dns ):
"Start DHCP server on host with specified DNS server"
info( '* Starting DHCP server on', host, 'at', host.IP(), '\n' )
dhcpConfig = '/tmp/%s-udhcpd.conf' % host
makeDHCPconfig( dhcpConfig, host.defaultIntf(), gw, dns )
host.cmd( 'udhcpd -f', dhcpConfig,
'1>/tmp/%s-dhcp.log 2>&1 &' % host )
def stopDHCPserver( host ):
"Stop DHCP server on host"
info( '* Stopping DHCP server on', host, 'at', host.IP(), '\n' )
host.cmd( 'kill %udhcpd' )
# DHCP client functions
def startDHCPclient( host ):
"Start DHCP client on host"
intf = host.defaultIntf()
host.cmd( 'dhclient -v -d -r', intf )
host.cmd( 'dhclient -v -d 1> /tmp/dhclient.log 2>&1', intf, '&' )
def stopDHCPclient( host ):
host.cmd( 'kill %dhclient' )
def waitForIP( host ):
"Wait for an IP address"
info( '*', host, 'waiting for IP address' )
while True:
host.defaultIntf().updateIP()
#print(host.IP())
if host.IP():
break
info( '.' )
sleep(1)
info( '\n' )
def readline():
"Read a line from stdin"
return stdin.readline()
def prompt( s=None ):
"Print a prompt and read a line from stdin"
if s is None:
s = "Press return to continue: "
print (s),
return readline()
def DHCP(host,dhcp):
"Rogue DHCP server demonstration"
startDHCPserver( dhcp, gw="192.168.1.254", dns='8.8.8.8')
startDHCPclient( host )
waitForIP( host )
print(host.IP())
stopDHCPserver( dhcp )
stopDHCPclient( host )
def usage():
"Print usage message"
print ("%s [ -h | -text ]")
print ("-h: print this message")
print ("-t: run in text/batch vs. firefox/x11 mode")
def run():
topo = NetworkTopo()
net = Mininet(topo=topo, build=False,controller=RemoteController)
c0=net.addController('c0',ip='127.0.0.1',port=6633) #ryu controller
c1=net.addController('c1',ip='127.0.0.1',port=6634) #ryu controller
net.build()
net.start()
#net.get('s1').start([c0])
h10 = net.getNodeByName( 'h10')
dhcp = net.getNodeByName('h1')
h11 = net.getNodeByName('h11')
h8 = net.getNodeByName( 'h8')
h2 = net.getNodeByName( 'h2')
h3 = net.getNodeByName( 'h3')
h4 = net.getNodeByName( 'h4')
h5 = net.getNodeByName( 'h5')
h6 = net.getNodeByName( 'h6')
h9 = net.getNodeByName( 'h9')
s1 = net.getNodeByName( 's1')
s2 = net.getNodeByName( 's2')
s3 = net.getNodeByName( 's3')
s4 = net.getNodeByName( 's4')
#net.get('s4').start([c1])ss
DHCP(h2,dhcp)
DHCP(h3,dhcp)
DHCP(h4,dhcp)
DHCP(h5,dhcp)
DHCP(h6,dhcp)
#os.popen('ovs-vsctl add-port s1 ens36')
net.get('s1').start([c0])
net.get('s2').start([c0])
net.get('s3').start([c0])
net.get('s4').start([c1])
#DHCP(h9,dhcp)
#DHCP(h10,dhcp)
#DHCP(h11,dhcp)
info('*** Routing Table on Router:\n')
r1 = net.getNodeByName('r1')
r2 = net.getNodeByName('r2')
r3 = net.getNodeByName('r3')
nat0 = net.getNodeByName('nat0')
s1 = net.getNodeByName('s1')
s2 = net.getNodeByName('s2')
s3 = net.getNodeByName('s3')
#s1.cmd('ovs-vsctl set bridge s1 stp_enable=true')
#s2.cmd('ovs-vsctl set bridge s2 stp_enable=true')
#s3.cmd('ovs-vsctl set bridge s3 stp_enable=true')
info('starting zebra and ospfd service:\n')
r1.cmd('route add default gw 192.168.10.1 dev r1-eth3')
r2.cmd('route add default gw 192.168.3.2 dev r2-eth1')
r3.cmd('route add default gw 192.168.3.5 dev r3-eth1')
nat0.cmd('route add -net 192.168.0.0/16 gw 192.168.10.2 dev nat0-eth1')
r1.cmd('zebra -f /etc/quagga/r1zebra.conf -d -z /tmp/r1zebra.api -i /tmp/r1zebra.interface')
r2.cmd('zebra -f /etc/quagga/r2zebra.conf -d -z /tmp/r2zebra.api -i /tmp/r2zebra.interface')
r3.cmd('zebra -f /etc/quagga/r3zebra.conf -d -z /tmp/r3zebra.api -i /tmp/r3zebra.interface')
time.sleep(1) # time for zebra to create api socket
r1.cmd('ospfd -f /etc/quagga/r1ospfd.conf -d -z /tmp/r1zebra.api -i /tmp/r1ospfd.interface')
r2.cmd('ospfd -f /etc/quagga/r2ospfd.conf -d -z /tmp/r2zebra.api -i /tmp/r2ospfd.interface')
r3.cmd('ospfd -f /etc/quagga/r3ospfd.conf -d -z /tmp/r3zebra.api -i /tmp/r3ospfd.interface')
CLI(net)
net.stop()
os.system("killall -9 ospfd zebra")
os.system("rm -f /tmp/*.api")
os.system("rm -f /tmp/*.interface")
if __name__ == '__main__':
setLogLevel('info')
# setLogLevel('debug')
run()
WLAN代码
# ! /usr/binpython
from mininet.node import Controller, ovSKernelSwitch
from mininet.log import setLogLevel, info
from mn_wifi.net import Mininet_wifi
from mn_wifi.node import Station, ovSKernelAPfrom mn_wifi.cli
import CLI
from mn_wifi.link import wmediumd
from mn_wifi.wmediumdConnector import interference
from subprocess import call
from mininet.nodelib import NAT
import os
def myNetwork( ):
net = Mininet_wifi(topo=None,
build=False,
link=wmediumd ,
wmediumd_mode=interference)
info('***Adding controllerln ' )
c1= net.addController( name= 'c1 ',controller=Controller,protocol= 'tcp ' ,port=6653)
info('***Add switches/APs\n ' )
ap1 = net.addAccessPoint( 'ap1', mac='00:00:00:00:00:01', ssid="handover",mode="g", channel="1", passwd= '123456789a',encrypt='wpa2', position='123.0,323.0,0')
ap2 = net.addAccessPoint( 'ap2' , mac='00:00:00:00:00:02' , ssid="handover ",mode="g" , channel="6",passwd=' 123456789a',encrypt='wpa2', position='375.0, 332.0,0')
ap3 = net.addAccessPoint( 'ap3 ' , mac='00:00:00:00:00:03', ssid="handover " ,node="g", channel="1",passwd=' 123456789a',encrypt='wpa2', position=' 606.0,313.0,0')
s2 = net. addSwitch( 's2 ', cls=ovSKernelSwitch)
info('***Add hosts/stations\n ' )
sta1 = net.addStation( 'sta1', ip= '192.168.4.1/24',position= '54.0,434.0,0 ', bgscan_threshold=-60,s_inverval=5, l_interval=10,bgscan_module="simpie")
sta2 = net.addStation( 'sta2 ', ip= '192.168.4.2/24 ',position= '156.0,433.0,0 ', bgscan_threshold=-60,s_inverval=5, l_interval=10,bgscan_module="simpie")
sta3 = net. addStation( 'sta3 ', ip= '192.168.4.3/24',position= '321.0,435.0,0 ', bgscan_threshold=-60,s_inverval=5, l_interval=10,bgscan_module="simpie")
sta4 = net.addStation( 'sta4 ', ip= '192.168.4.4/24',position='430.0,433.0,0 ', bgscan_threshold=-60,s_inverval=5, l_interval=10,bgscan_module="simpie")
sta5 = net.addStation( 'sta5 ', ip= '192.168.4.5/24',position= '555.0,429.0,0 ', bgscan_threshold=-60,s_inverval=5, l_interval=10,bgscan_module="simpie")
sta6 = net.addStation( 'sta6 ' , ip= '192.168.4.6/24' ,position= '556,430,0 ', bgscan_threshold=-60,s_inverval=5, l_interval=10,bgscan_module="simpie")
nat0=net. addHost( 'nat0' ,cls=NAT, ip= '192.168.4.8/24 ' , subnet=' 192.168.4.0/24',inNamespace=False)
net. addLink(nat0,s1)
info( "***Configuring Propagation Model\n" )
net.setPropagationModel( model="logDistance" , exp=3)
info( "***Configuring wifi nodes\n " )
net . configurewifiNodes( )
info(·***Add linksln ')net. addLink( s1, s2)
net.addLink(ap1, s2)
net.addLink(s2, ap2)
net. addLink(s2, ap3)
net.plotGraph( max_x=1000,max_y=1000)
info('***Starting networkin ' )
net. start()
os. popen( 'ovs-vsctl add-port s1 ens34 ')
CLI(net)
net.stop( )
ifname__=='__main__':
setLogLevel( info')
myNetwork( )
参考资料
[1]Mininet-WiFi official guideline. https://mininet-wifi.github.io/
[2] R. R. Fontes, S. Afzal, S. H. B. Brito, M. A. S. Santos and C. E. Rothenberg, "Mininet-WiFi: Emulating software-defined wireless networks," 2015 11th International Conference on Network and Service Management (CNSM), 2015, pp. 384-389, doi: 10.1109/CNSM.2015.7367387.