基于python脚本的拓扑搭建

Mininet这个网络仿真软件支持使用python脚本建立拓扑,我们只需要预先写好存储拓扑信息的文件和拓扑建立逻辑(这是两个文件)就可以一键运行,在拓扑规模大、链路数量多的情况下(实验用的拓扑就挺大)能极大提高仿真效率,且不容易出现错误搭建拓扑的情况(比如少拉了一根线,拉错了一根线之类的)。

拓扑搭建逻辑

mininet自带有一个拓扑搭建脚本,我们先来看一下拓扑搭建逻辑。

拓扑搭建逻辑

你可以在上图红色框线所示的文件路径找到系统里的拓扑搭建脚本,里面的代码就只有一页这么多(才34行,简单得不能再简单了)。

  • 绿色框线里的内容是这个 python 脚本成功运行需要的一些库,一般不需要改动(当然了,如果你想给这个脚本增加一些新功能,相应的库也要加进来,比如让他能读取json文件,就需要添加读取json文件需要的库,具体是什么,可以自行搜索~)。
  • 橙色框线的第一行代码读取拓扑信息,注意,此处读取的路径使用了相对路径(./表示当前脚本文件所在的路径),使用绝对路径也是可以的,第15行代码读取拓扑信息文件的第一行,即下图的第一行。下图的第一行数据分别表示这个拓扑里的主机数量(3)和交换机数量(7)
  • 有了这两个信息,上图的紫色框线开始向 mininet 网络中添加主机(line18)和交换机(line21),然后上图的程序继续读取拓扑文件的其他行,用于添加设备之间的链路(上图蓝色框线部分)。
  • 最后,上图白色框线的部分相当于为这个脚本创建的网络起了一个名字,你可以做定制化的改动,比如改成你自己的名字,但是一定要记住自己改成了什么,后面实际运行时需要用到。

拓扑信息

接下来,我们看看拓扑信息文件到底存了什么。你可以在上图中红色框线所示的路径下(其实就是拓扑搭建脚本所在的路径)找到存储拓扑信息的文件(topoinfo.txt)。打开以后其实也就14行,第一行的意思刚刚已经解释过了,仔细观察,你会发现,之后的每一行都对应了要搭建的拓扑里面的一条边。这并不是实验要用的那个拓扑的信息,而是下图所示的拓扑。到这里,我们可以回到第一幅图的蓝色框线部分了,是不是能理解这部分代码在干什么了。

示例拓扑

json文件描述拓扑

这一部分,还有一点补充,第二幅图的文件用最简单的文件格式(实际就是txt)去描述了一个网络的拓扑信息,但并不一定最好用,例如当网络拓扑比较大,且需要给链路附加诸如时延和带宽等属性的时候,文件解析(解析就是指从文件中去找想要的某些信息)起来不太方便,比如DFS、Dijkstra需要获取拓扑中链路的时延属性,最大流需要获取链路的带宽,有时候需要全部链路的这些信息,有时候又只需要某条链路的信息,那从这种txt中去找相关信息就比较麻烦。万幸的是,不论是拓扑搭建脚本、还是ryu控制器的控制逻辑,都是用python写的,python是可以通过调库将json文件读取成字典的(如果你还不知道python字典是什么,自觉去面壁10分钟),到这,你悟了吗?

运行拓扑

Mininet运行自定拓扑

现在我们正式开始运行用户自定义的脚本(革命快要成功了,再坚持一下),上图中橙色框线里橙色横线处的代码表示要自定义脚本,其后紧跟的就是我们拓扑建立逻辑的脚本文件(严格讲应该是脚本文件所在的路径再加文件名,只是这里执行该命令的位置就是文件所在的目录),绿色框线里的内容就相当于这个建立的拓扑叫什么名字了,还记得最开始的图里里白色框线里的东西吗,就是那个玩意儿,蓝色框线的命令告诉mininet这个软件,我这个建起来的拓扑需要连接到一个远程的控制器,这个控制器的ip是127.0.0.1,端口号是6653(openflow协议的默认端口号),最后紫色框线的配置告诉mininet:你给我按照添加主机的顺序,从00:00:00:00:00:01开始依次分配物理地址(注意物理地址用的是十六进制,00:00:00:00:00:10不是10而是16,切记,切记),如果你不加这条命令,每个添加的主机的mac地址就是随机分配的,没有规律(在之前给的b站上的视频里面,作者也提到过这个)。输入密码后,Mininet就开始建立拓扑了,黄色框线内显示了mininet建立拓扑的过程,先添加了三个主机、然后是7个交换机、最后是13条边(注意是双向边)。

结束运行

Mininet的命令状态下输入exit(图5红色框线)会退出,节点和链路都依次停止,蓝色框线里显示mininet停止了多少条链路。

这里还要注意一个问题,对mininet来说,一共增加了13条双向边,其中有三条是主机和交换机之间的链路,剩余10条才是交换机和交换机之间的边,对控制器来说,他只能观测到交换机和交换机之间的边(也就是他能获取到20条边,因为这里的10条边是双向边,所以ryu控制器按两个方向各存一条),而看不到主机和交换机之间的边,原因在于控制器发现拓扑用的是LLDP协议,且控制器控制的是交换机,只有交换机会向控制器请求指示,而在LLDP链路发现过程中发送到主机的测试包会被主机直接丢掉,不存在主机也将这个数据包向上发给控制器的可能,所以LLDP检测不到主机和交换机之间的链路。)

还有一个问题需要解释,你可能都没发现这个问题:图3中交换机的端口号是怎么确定的?Mininet在给设备端口分配编号的时候也是从1开始(不同设备之间的端口编号是独立的),按照设备接口上连接链路的顺序,依次编号。举个栗子:按照图2的拓扑文件,与交换机1有关的链路的添加顺序是h1-s1、s1-s2、s1-s4,所以s1相对应的端口也就被分别编号1、2、3。也就是说,这个端口编号在你给定了拓扑信息和搭建拓扑的脚本逻辑的时候,命运已定。

至此,拓扑搭建的所有内容已经介绍完了,回过头来再看,建立一个拓扑的信息文本其实要比搭建拓扑的逻辑脚本麻烦的多,毕竟后者就几个for的事,前者那可真是实打实地要一条条加呀,就拿之前给的那个24个节点地拓扑来说,交换机到交换机的边有43条(ryu控制器的get_link()拉取的信息中包含的链路数量翻倍),主机到交换机的边有24条,合起来67条(你可以用我上面介绍的办法看看他是不是真的是67条),这你要是拿miniedit拉可得拉多久,想想就头秃。

实验拓扑

为了防止你头秃,这里破例提供两个已建好的拓扑信息的json文件(注意,前面讲解的拓扑搭建代码不能直接处理json文件,你还需要做出相应的调整),如果你愿意,可以直接用我提供的json文件去实现后续的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
{
"host_no": 7,
"switch_no": 7,
"links": [{
"vertexs": ["h1", "s1"],
"delay": 1,
"bw": 100
}, {
"vertexs": ["h2", "s2"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h3", "s3"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h4", "s4"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h5", "s5"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h6", "s6"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h7", "s7"],
"delay": 1,
"bw": 100
},{
"vertexs": ["s1", "s2"],
"delay": 2,
"bw": 18
}, {
"vertexs": ["s1", "s4"],
"delay": 5,
"bw": 9
}, {
"vertexs": ["s2", "s3"],
"delay": 7,
"bw": 14
}, {
"vertexs": ["s2", "s5"],
"delay": 3,
"bw": 22
}, {
"vertexs": ["s3", "s6"],
"delay": 6,
"bw": 19
}, {
"vertexs": ["s4", "s5"],
"delay": 15,
"bw": 16
}, {
"vertexs": ["s4", "s6"],
"delay": 2,
"bw": 21
}, {
"vertexs": ["s5", "s6"],
"delay": 4,
"bw": 10
}, {
"vertexs": ["s5", "s7"],
"delay": 9,
"bw": 6
}, {
"vertexs": ["s6", "s7"],
"delay": 11,
"bw": 11
}]
}

上面代码中一共7个交换机,拓扑图如下,每个交换机下挂一个主机,图中只画出一个主机(h3),但你需要知道还有6个主机并没有画出来,主机和交换机之间的链路属性都按图6中的蓝色数值进行了设置,交换机之间的链路属性也按图中标注进行了设置。

所有链路的排列顺序(正常来说就是后面搭建拓扑的脚本添加链路的顺序,也就是说)如下:首先是主机和交换机之间的链路,按主机编号增序添加;然后是交换机和交换机之间的链路,按交换机编号增序依次添加交换机的所有邻居链路,对邻居的要求是其编号比自己的大,在满足这个条件的邻居里,先添加邻居编号小的链路(这里用文字描述有点绕,没看明白的话多看几次)。按这个规则形成的拓扑中,交换机的各个接口编号以较小的红色数字标注在了对应位置。

image-20230103221453635


类似的,下面给定实验拓扑,只是相比上图,下图追加了按这个文件建立的拓扑的交换机接口编号信息。代码中链路的顺序规则同上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
{
"host_no": 24,
"switch_no": 24,
"links": [{
"vertexs": ["h1", "s1"],
"delay": 1,
"bw": 100
}, {
"vertexs": ["h2", "s2"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h3", "s3"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h4", "s4"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h5", "s5"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h6", "s6"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h7", "s7"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h8", "s8"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h9", "s9"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h10", "s10"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h11", "s11"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h12", "s12"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h13", "s13"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h14", "s14"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h15", "s15"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h16", "s16"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h17", "s17"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h18", "s18"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h19", "s19"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h20", "s20"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h21", "s21"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h22", "s22"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h23", "s23"],
"delay": 1,
"bw": 100
},{
"vertexs": ["h24", "s24"],
"delay": 1,
"bw": 100
},{
"vertexs": ["s1", "s2"],
"delay": 2,
"bw": 19
}, {
"vertexs": ["s1", "s7"],
"delay": 7,
"bw": 21
},{
"vertexs": ["s2", "s3"],
"delay": 10,
"bw": 13
},{
"vertexs": ["s2", "s7"],
"delay": 4,
"bw": 9
},{
"vertexs": ["s3", "s4"],
"delay": 16,
"bw": 6
},{
"vertexs": ["s3", "s5"],
"delay": 24,
"bw": 30
},{
"vertexs": ["s3", "s6"],
"delay": 18,
"bw": 7
},{
"vertexs": ["s4", "s5"],
"delay": 12,
"bw": 11
},{
"vertexs": ["s4", "s8"],
"delay": 3,
"bw": 15
},{
"vertexs": ["s5", "s6"],
"delay": 8,
"bw": 16
},{
"vertexs": ["s6", "s7"],
"delay": 5,
"bw": 9
},{
"vertexs": ["s6", "s8"],
"delay": 29,
"bw": 18
},{
"vertexs": ["s6", "s9"],
"delay": 6,
"bw": 25
},{
"vertexs": ["s7", "s9"],
"delay": 5,
"bw": 5
},{
"vertexs": ["s7", "s10"],
"delay": 15,
"bw": 27
},{
"vertexs": ["s8", "s14"],
"delay": 14,
"bw": 6
},{
"vertexs": ["s9", "s10"],
"delay": 26,
"bw": 17
},{
"vertexs": ["s9", "s12"],
"delay": 8,
"bw": 15
},{
"vertexs": ["s9", "s14"],
"delay": 19,
"bw": 14
},{
"vertexs": ["s10", "s11"],
"delay": 11,
"bw": 10
},{
"vertexs": ["s10", "s12"],
"delay": 6,
"bw": 7
},{
"vertexs": ["s10", "s18"],
"delay": 25,
"bw": 18
},{
"vertexs": ["s11", "s17"],
"delay": 7,
"bw": 18
},{
"vertexs": ["s11", "s19"],
"delay": 20,
"bw": 13
},{
"vertexs": ["s12", "s13"],
"delay": 5,
"bw": 13
},{
"vertexs": ["s12", "s17"],
"delay": 13,
"bw": 22
},{
"vertexs": ["s13", "s14"],
"delay": 14,
"bw": 19
},{
"vertexs": ["s13", "s15"],
"delay": 4,
"bw": 11
},{
"vertexs": ["s13", "s16"],
"delay": 14,
"bw": 2
},{
"vertexs": ["s14", "s15"],
"delay": 18,
"bw": 7
},{
"vertexs": ["s15", "s24"],
"delay": 25,
"bw": 16
},{
"vertexs": ["s16", "s17"],
"delay": 17,
"bw": 6
},{
"vertexs": ["s16", "s21"],
"delay": 11,
"bw": 9
},{
"vertexs": ["s16", "s22"],
"delay": 6,
"bw": 12
},{
"vertexs": ["s16", "s24"],
"delay": 14,
"bw": 6
},{
"vertexs": ["s17", "s20"],
"delay": 21,
"bw": 8
},{
"vertexs": ["s17", "s21"],
"delay": 7,
"bw": 10
},{
"vertexs": ["s18", "s19"],
"delay": 5,
"bw": 15
},{
"vertexs": ["s19", "s20"],
"delay": 15,
"bw": 7
},{
"vertexs": ["s20", "s21"],
"delay": 6,
"bw": 9
},{
"vertexs": ["s21", "s22"],
"delay": 13,
"bw": 14
},{
"vertexs": ["s22", "s23"],
"delay": 6,
"bw": 4
},{
"vertexs": ["s23", "s24"],
"delay": 17,
"bw": 23
}]
}

24节点拓扑图