基本中型网络的仿真(RYU+Mininet的SDN架构)-以校园为例

news2024/12/22 13:17:55

目录

​​​​​​​具体问题可以私聊博主

一、设计目标

1.1应用场景介绍

1.2应用场景设计要求

网络配置方式

网络技术要求

网络拓扑要求

互联互通

二、课程设计内容与原理

(1)预期网络拓扑结构和功能

(1)网络设备信息

(2)DHCP自动配置

(6)防火墙技术

(7)DHCP

三、课程设计方案

四、课程设计配置与实现

五、课程设计验证与结果分析

代码

网络拓扑

WLAN代码

参考资料



一、设计目标

1.1应用场景介绍

现有一个中学,需要设计一个中小型网络满足学校的网络需求。

学校有教学区,实验室区域和服务器区域。教学区为了满足接入的灵活性和信号强度要求,需要满足设计WIFI满足日常教室和同学需求,接入上限为200人。实验室区域需要有一台教师主机方便管理实验室其余电脑,并且教师主机可以访问学校服务器,方便为同学们实验室授课。学校服务器区域设有学校相关重要文件和学校网页相关内容。最后学校还会接入互联网。

1.2应用场景设计要求

  1. 网络配置方式

网络配置的方式应该便捷,方便管理和排错,主机和转发设备应该采用什么配置方式,服务器采用什么类型。

  1. 网络技术要求

SDN架构实现数控分离、防火墙,路由协议,NAT,其余适合的网络技术或私有技术。

  1. 网络拓扑要求

链路要有冗余度,链路同时还要具有复杂度。

  1. 互联互通

仿真设计的网络需要互联互通,访问互联网,与其他小组互通

二、课程设计内容与原理

2.1网络拓扑

(1)预期网络拓扑结构和功能

图2-1 网络拓扑示意图

注:此网络拓扑只是表示拓扑联系,不代表实际实现拓扑

网络1是WIFI区域,此区域主要对应应用场景中的教学区域,此区域拥有多个无线AP接入点,可以实现设备的AP接入点切换。

网络2是实验室区域,此部分区域由三个交换机成环连接,在此需要实现STP,以此满足实验室考试网络拥堵和网络故障特殊情况,保证网络的质量和考试的网络质量。

网络3是学校的服务器区域,此区域部署了三台服务器,分别是DHCP服务器,网页服务器和文件服务器,三台服务器通过交换机连接在路由器上面。

路由区域部分需要实现报文的传递和交付。

最后整个校区会通过一个路由器接入互联网。

  1. 实际网路拓扑

在我们实现的网络拓扑当中,网络的拓扑结构如下。

图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网络技术和原理

  1. SDN

1)SDN的网络体系架构

应用层:这一层主要是体现用户意图的各种上层应用程序,此类应用程序称为协同层应用程序,典型的应用包括OSS(Operation support system 运营支撑系统)、Openstack等。传统的IP网络同样具有转发平面、控制平面和管理平面,SDN网络架构也同样包含这3个平面,只是传统的IP网络是分布式控制的,而SDN网络架构下是集中控制的。

控制层:控制层是系统的控制中心,负责网络的内部交换路径和边界业务路由的生成,并负责处理网络状态变化事件。

转发层:转发层主要由转发器和连接器的线路构成基础转发网络,这一层负责执行用户数据的转发,转发过程中所需要的转发表项是由控制层生成的。

北向接口:应用层和控制层通信的接口,应用层通过控制开放的API,控制设备转发功能

南向接口:控制层和数据层通信的接口,控制器通过OpenFlow或其他协议下发流表

图2-3 SDN架构示意图

2)SDN的主要特征

转控分离:网元的控制平面在控制器上,负责协议计算,产生流表;而转发平面只在网络设备上。

集中控制:设备网元通过控制器集中管理和下发流表,这样就不需要对设备进行逐一操作,只需要对控制器进行配置即可。

开放接口:第三方应用只需要通过控制器提供的开放接口,通过编程方式定义一个新的网络功能,然后在控制器上运行即可。

SDN控制器既不是网管,也不是规划工具:

网管没有实现转控分离:网管只负责管理网络拓扑、监控设备告警和性能、下发配置脚本等操作,但这些仍然需要设备的控制平面负责产生转发表项。

  1. 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包

  1. OPSF路由

开放式最短路径优先(Open Shortest Path First,OSPF)是广泛使用的一种动态路由协议,它属于链路状态路由协议,具有路由变化收敛速度快、无路由环路、支持变长子网掩码(VLSM)和汇总、层次区域划分等优点。

在网络中使用OSPF协议后,大部分路由将由OSPF协议自行计算和生成,无须网络管理员人工配置,当网络拓扑发生变化时,协议可以自动计算、更正路由,极大地方便了网络管理。

OSPF协议依靠五种不同类型的分组来建立邻接关系和交换路由信息,即问候分组、数据库描述分组、链路状态请求分组、链路状态更新分组和链路状态确认分组

图2-5 OSPF协议的5种分组及功能

  1. 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。

  1. 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。

  1. 最终使用的方案——DHCP

1.配置信息

图3-7 DHCP建立流程图

本方案使用1台虚拟机A,在此台虚拟机上配置DHCP服务器,在每个需要DHCP的子网中选取一台主机作为该子网的DHCP服务器,然后规定该子网的地址池,最后生成上述DHCP服务器的独有的.conf文件。当请求方发出请求时,会生成对该主机的.log文件,然后对DHCP服务器进行相应,使用mininet中host.defaultIntf.updateIP()函数进行IP地址的更新。

  1. 实现的方案——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协议。

  1. 配置

主要是涉及Mininet和RYU控制器的安装,一起路由定义软件Quagga的安装和配置。

详细的配置步骤在网络上都可以找到,在此不在叙述。

  1. 实现

第一步:打开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地址的更新。

  1. 配置

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' )

  1. 实现

主要流程如下图:

图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 启动防火墙操作示意图

  1. 通过直接运行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.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/372190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

aws ecr 使用golang实现的简单镜像转换工具

https://pkg.go.dev/github.com/docker/docker/client#section-readme 通过golang实现一个简单的镜像下载工具 总体步骤 启动一台海外区域的ec2实例安装docker和awscli配置凭证访问国内ecr仓库编写web服务实现镜像转换和自动推送 安装docker和awscli sudo yum remove awsc…

超市怎么做微信小程序_线上超市小程序开发可以实现什么功能呢

1。开发超市小程序有什么价值&#xff1f; 1、对于消费者来说&#xff1a;通过超市小程序能够更加直接的购买到想要的产品&#xff0c;消费者无需再到门店寻找商品可以直接通过超市小程序进行在线浏览&#xff1b;通过在线搜索的方式能够更加便捷的搜索到相应的商品&#xff0…

第一篇自我介绍(单片机)

小白的单片机之旅 &#x1f914;自我介绍&#x1f914; &#x1f60a;学习目标&#x1f60a; &#x1f61c;关于单片机&#x1f61c; &#x1f31d;小结&#x1f31d; &#x1f389;博客主页&#xff1a;小智_x0___0x_ &#x1f389;欢迎关注&#xff1a;&#x1f44d;点赞&…

JavaSE学习笔记day14

二、Set Set集合是Collection集合的子接口,该集合中不能有重复元素!! Set集合提供的方法签名,与父接口Collection的方法完全一致!! 即没有关于下标操作的方法 Set接口,它有两个常用的子实现类HashSet,TreeSet 三、HashSet HashSet实现了Set接口,底层是hash表(实际上底层是HashM…

QML 中的 5 大布局

作者: 一去、二三里 个人微信号: iwaleon 微信公众号: 高效程序员 在 QML 中,可以通过多种方式对元素进行布局 - 手动定位、坐标绑定定位、锚定位(anchors)、定位器和布局管理器。 说到 anchors,可能很多人都不太了解,它是 QML 中一个非常重要的概念,主要提供了一种相…

C语言几种判断语句简述

C 判断 判断结构要求程序员指定一个或多个要评估或测试的条件&#xff0c;以及条件为真时要执行的语句&#xff08;必需的&#xff09;和条件为假时要执行的语句&#xff08;可选的&#xff09;。 C 语言把任何非零和非空的值假定为 true&#xff0c;把零或 null 假定为 fals…

Vuex基础语法

Vuex vuex官网 文章目录Vuexvuex的工作原理图2.vuex的环境搭建3.vuex的使用1.actons2. mutations3.getters4.vuex中的map映射属性4.1 mapState和mapGetters4.2 mapMutations和mapActions5.vuex多组件通信1.通过计算属性获得2.通过mapState获得6.vuex模块化和命名空间6.1模块化…

为什么要用线程池?

1.降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 2.提高响应速度。当任务到达时&#xff0c;任务可以不需要的等到线程创建就能立即执行。 3.提高线程的可管理性。线程是稀缺资源&#xff0c;如果无限制的创建&#xff0c;不仅会消耗系统资源&#…

王道《操作系统》学习(一)——计算机系统概述

1.1 操作系统的概念、功能 1.1.1 操作系统的概念&#xff08;定义&#xff09; &#xff08;1&#xff09;操作系统是系统资源的管理者 &#xff08;2&#xff09;向上层用户、软件提供方便易用的服务 &#xff08;3&#xff09;是最接近硬件的一层软件 1.1.2 操作系统的功能…

Java 输入输出流

应用程序经常需要访问文件和目录&#xff0c;读取文件信息或写入信息到文件&#xff0c;即从外界输入数据或者向外界传输数据&#xff0c;这些数据可以保存在磁盘文件、内存或其他程序中。在Java中&#xff0c;对这些数据的操作是通过 I/O 技术来实现的。所谓 I/O 技术&#xf…

Vue2.0开发之——使用ref引用DOM元素(40)

一 概述 什么是ref引用ref引用示例 二 什么是ref引用 ref用来辅助开发者在不依赖于jQuery的情况下&#xff0c;获取DOM元素或组件的引用每个vue的组件实例上&#xff0c;都包含一个$refs对象&#xff0c;里面存储着对应的DOM元素或组件的应用默认情况下&#xff0c;组件的$re…

Vue3之事件绑定

何为事件绑定 当我们开发完UI界面后&#xff0c;还需要和用户交互&#xff0c;所谓交互也就是用户可以点击界面上的按钮&#xff0c;文字&#xff0c;链接等以及点击键盘上的按钮&#xff0c;我们开发的程序可以做出对应的反应。做出的反应会通过UI界面再反馈给用户&#xff0c…

CSS 之层叠规则(权级、权重、顺序)详解

文章目录参考描述定义层叠层叠与冲突规则权重&#xff08;优先级&#xff09;权重值的叠加顺序权级权级层叠规则的运用顺序尾声参考 项目描述MDN WEB Docs优先级Amily_mo令人烦恼的css选择器权值问题 - Amily_mo深入解析CSS基思J.格兰特 / 黄小、高楠 译MDN WEB Docs:not() 描…

华为OD机试用Python实现 -【字符串重新排序】(2023-Q1 新题)

华为OD机试题 华为OD机试300题大纲字符串重新排序题目描述输入描述输出描述示例一输入输出示例二输入输出Python 代码实现算法思路华为OD机试300题大纲 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:blog.csdn.…

JavaEE|TCP/IP协议栈之TCP协议工作机制下

文章目录一、滑动窗口二、流量控制三、拥塞控制四、延时应答五、捎带应答六、面向字节流&#xff08;了解&#xff09;七、异常情况&#xff08;了解&#xff09;关于其他传输层协议一、滑动窗口 为什么要引入滑动窗口&#xff1f; 确认应答和超时重传为TCP可靠传输机制提供支持…

FTP中的TCP传输服务(电子科技大学TCP/IP实验五)

目录 一&#xff0e;实验目的 二&#xff0e;预备知识 三&#xff0e;实验原理 四&#xff0e;实验内容 五&#xff0e;实验步骤 八、总结及心得体会 九、对本实验过程及方法、手段的改进建议 一&#xff0e;实验目的 1、掌握 TCP 协议工作原理 2、掌握 TCP 的连接建立…

阿里团队刚发布的重磅图像生成基础模型,多重条件引导+图像合成,SD级别,5B参数...

一个多小时前刚发的论文&#xff0c;Composer: Creative and Controllable Image Synthesis with Composable Conditions。 我读完了快速帮大家概要一下啊。论文地址见文章最后。阿里巴巴团队开发的这个重磅图像生成模型 Compose&#xff0c;支持多重引导条件的图像生成(合成)&…

containerd安装配置

containerd基本使用命令 containerd安装 容器运行时containerd安装配置 https://blog.csdn.net/rendongxingzhe/article/details/124595415 yum list | grep containerd containerd的本地CLI工具ctr命令 containerd的组件 containerd提供包括容器的运行、测试、发布和接口…

improve-1

类型及检测方式 1. JS内置类型 JavaScript 的数据类型有下图所示 其中&#xff0c;前 7 种类型为基础类型&#xff0c;最后 1 种&#xff08;Object&#xff09;为引用类型&#xff0c;也是你需要重点关注的&#xff0c;因为它在日常工作中是使用得最频繁&#xff0c;也是需要…

DevOps是什么?DevOps能够给我们带来什么?

目录专栏导读一、DevOps是什么&#xff1f;二、为什么会出现DevOps&#xff1f;1、容器化技术的发展&#xff0c;微服务架构的发展&#xff0c;直接促进了DevOps的迅速发展2、敏态需求的增加&#xff0c;即探索性工作的增加3、软件开发活动在企业经营活动中占比的不断增加4、企…