blob: 0345375b34a0edc2cf4468df11c00d42bb6059b4 [file] [log] [blame]
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -08001#!/usr/bin/python
2import os
3import re
4from optparse import OptionParser
5from ipaddress import ip_network
6from mininet.node import RemoteController, OVSBridge, Host
7from mininet.link import TCLink
8from mininet.log import setLogLevel
9from mininet.net import Mininet
10from mininet.topo import Topo
11from mininet.nodelib import NAT
12from mininet.cli import CLI
13
14from routinglib import BgpRouter, RoutedHost
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -080015from trellislib import DhcpServer, TaggedRoutedHost, DualHomedRoutedHost, DualHomedTaggedRoutedHost, DhcpClient, Dhcp6Client, DhcpServer, Dhcp6Server, TrellisHost
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -080016
17# Parse command line options and dump results
18def parseOptions():
19 "Parse command line options"
20 parser = OptionParser()
21 parser.add_option( '--dhcp', dest='dhcp', type='int', default=0,
22 help='Configure hosts with dhcp or not' )
23 parser.add_option( '--routers', dest='routers', type='int', default=0,
24 help='Configure external routers or not in the topology' )
25 parser.add_option( '--ipv6', dest='ipv6', type='int', default=0,
26 help='Configure hosts with ipv6 or not' )
27 parser.add_option( '--ipv4', dest='ipv4', type='int', default=1,
28 help='Configure hosts with ipv4 or not' )
29 parser.add_option( '--onos-ip', dest='onosIp', type='str', default='',
30 help='IP address list of ONOS instances, separated by comma(,). Overrides --onos option' )
31
32 ( options, args ) = parser.parse_args()
33 return options, args
34
35opts, args = parseOptions()
36
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -080037class ComcastLeafSpineFabric(Topo):
38
39 spines = dict()
40 leafs = dict()
41 hosts_dict = dict()
42
43 def createIpv4Hosts(self, dhcp):
44
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -080045 h1 = self.addHost('h1v4', cls=TrellisHost,
46 mac='00:aa:00:00:00:01', ips=['10.1.0.1/24'],
47 gateway='10.1.0.254', dhcpClient=dhcp)
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -080048 self.addLink(h1, self.leafs[0])
49 self.hosts_dict['h1v4'] = h1
50
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -080051 h2 = self.addHost('h2v4', cls=TrellisHost,
52 mac='00:aa:00:00:01:01', ips=['10.1.10.1/24'],
53 gateway='10.1.10.254', dhcpClient=dhcp)
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -080054 self.addLink(h2, self.leafs[0])
55 self.hosts_dict['h2v4'] = h2
56
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -080057 h3 = self.addHost('h3v4', cls=TrellisHost,
58 mac='00:aa:00:00:00:02', ips=['10.2.0.1/24'],
59 gateway='10.2.0.254', dhcpClient=dhcp)
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -080060 self.addLink(h3, self.leafs[1])
61 self.hosts_dict['h3v4'] = h3
62
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -080063 h4 = self.addHost('h4v4', cls=TrellisHost,
64 mac='00:aa:00:00:00:03', ips=['10.2.30.1/24'],
65 gateway='10.2.30.254', dhcpClient=dhcp,
66 dualHomed=True)
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -080067 self.addLink(h4, self.leafs[1])
68 self.addLink(h4, self.leafs[2])
69 self.hosts_dict['h4v4'] = h4
70
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -080071 h5 = self.addHost('h5v4', cls=TrellisHost,
72 mac='00:aa:00:00:00:04', ips=['10.2.20.1/24'],
73 gateway='10.2.20.254', dhcpClient=dhcp, vlan=30,
74 dualHomed=True)
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -080075 self.addLink(h5, self.leafs[1])
76 self.addLink(h5, self.leafs[2])
77 self.hosts_dict['h5v4'] = h5
78
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -080079 h6 = self.addHost('h6v4', cls=TrellisHost,
80 mac='00:aa:00:00:00:05', ips=['10.2.10.1/24'],
81 gateway='10.2.10.254', dhcpClient=dhcp, vlan=20)
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -080082 self.addLink(h6, self.leafs[2])
83 self.hosts_dict['h6v4'] = h6
84
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -080085 h7 = self.addHost('h7v4', cls=TrellisHost,
86 mac='00:aa:00:00:01:05', ips=['10.2.40.1/24'],
87 gateway='10.2.40.254', dhcpClient=dhcp, vlan=40)
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -080088 self.addLink(h7, self.leafs[2])
89 self.hosts_dict['h7v4'] = h7
90
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -080091 h8 = self.addHost('h8v4', cls=TrellisHost,
92 mac='00:aa:00:00:00:06', ips=['10.3.0.1/24'],
93 gateway='10.3.0.254', dhcpClient=dhcp, vlan=30)
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -080094 self.addLink(h8, self.leafs[3])
95 self.hosts_dict['h8v4'] = h8
96
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -080097 h9 = self.addHost('h9v4', cls=TrellisHost,
98 mac='00:aa:00:00:00:07', ips=['10.3.10.1/24'],
99 gateway='10.3.10.254', dhcpClient=dhcp, vlan=40,
100 dualHomed=True)
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -0800101 self.addLink(h9, self.leafs[3])
102 self.addLink(h9, self.leafs[4])
103 self.hosts_dict['h9v4'] = h9
104
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800105 h10 = self.addHost('h10v4', cls=TrellisHost,
106 mac='00:aa:00:00:00:08', ips=['10.3.30.1/24'],
107 gateway='10.3.30.254', dhcpClient=dhcp, vlan=40,
108 dualHomed=True)
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -0800109 self.addLink(h10, self.leafs[3])
110 self.addLink(h10, self.leafs[4])
111 self.hosts_dict['h10v4'] = h10
112
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800113 h11 = self.addHost('h11v4', cls=TrellisHost,
114 mac='00:aa:00:00:00:0a', ips=['10.3.20.1/24'],
115 gateway='10.3.20.254', dhcpClient=dhcp, vlan=40)
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -0800116 self.addLink(h11, self.leafs[4])
117 self.hosts_dict['h11v4'] = h11
118
119 return
120
121 def createIpv6Hosts(self, dhcp):
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800122
123 h1 = self.addHost('h1v6', cls=TrellisHost,
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800124 mac='00:bb:00:00:00:01', ips=["1000::3fe/120"],
125 gateway='1000::3ff', dhcpClient=dhcp, ipv6=1)
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800126 self.addLink(h1, self.leafs[0])
127 self.hosts_dict['h1v6'] = h1
128
129 h2 = self.addHost('h2v6', cls=TrellisHost,
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800130 mac='00:bb:00:00:01:01', ips=['1001::3fe/120'],
131 gateway='1001::3ff', dhcpClient=dhcp, ipv6=1)
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800132 self.addLink(h2, self.leafs[0])
133 self.hosts_dict['h2v6'] = h2
134
135 h3 = self.addHost('h3v6', cls=TrellisHost,
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800136 mac='00:bb:00:00:00:02', ips=['1002::3fe/120'],
137 gateway='1002::3ff', dhcpClient=dhcp, ipv6=1)
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800138 self.addLink(h3, self.leafs[1])
139 self.hosts_dict['h3v6'] = h3
140
141 h4 = self.addHost('h4v6', cls=TrellisHost,
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800142 mac='00:bb:00:00:00:03', ips=['1003::3fe/120'],
143 gateway='1003::3ff', dhcpClient=dhcp, ipv6=1,
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800144 dualHomed=True)
145 self.addLink(h4, self.leafs[1])
146 self.addLink(h4, self.leafs[2])
147 self.hosts_dict['h4v6'] = h4
148
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800149 # TODO: Remove this line when we
150 # find WHY dhcpv6 with tagged clients
151 # does not work.
152 dhcp = False
153
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800154 h5 = self.addHost('h5v6', cls=TrellisHost,
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800155 mac='00:bb:00:00:00:04', ips=['1004::3fe/120'],
156 gateway='1004::3ff', dhcpClient=dhcp, ipv6=1,
157 vlan=30,
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800158 dualHomed=True)
159 self.addLink(h5, self.leafs[1])
160 self.addLink(h5, self.leafs[2])
161 self.hosts_dict['h5v6'] = h5
162
163 h6 = self.addHost('h6v6', cls=TrellisHost,
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800164 mac='00:bb:00:00:00:05', ips=['1005::3fe/120'],
165 gateway='1005::3ff', dhcpClient=dhcp, vlan=20,
166 ipv6=1)
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800167 self.addLink(h6, self.leafs[2])
168 self.hosts_dict['h6v6'] = h6
169
170 h7 = self.addHost('h7v6', cls=TrellisHost,
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800171 mac='00:bb:00:00:01:05', ips=['1006::3fe/120'],
172 gateway='1006::3ff', dhcpClient=dhcp, ipv6=1,
173 vlan=40)
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800174 self.addLink(h7, self.leafs[2])
175 self.hosts_dict['h7v6'] = h7
176
177 h8 = self.addHost('h8v6', cls=TrellisHost,
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800178 mac='00:bb:00:00:00:06', ips=['1007::3fe/120'],
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800179 gateway='1007::3ff', dhcpClient=dhcp, vlan=30)
180 self.addLink(h8, self.leafs[3])
181 self.hosts_dict['h8v6'] = h8
182
183 h9 = self.addHost('h9v6', cls=TrellisHost,
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800184 mac='00:bb:00:00:00:07', ips=['1008::3fe/120'],
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800185 gateway='1008::3ff', dhcpClient=dhcp, vlan=40,
186 dualHomed=True)
187 self.addLink(h9, self.leafs[3])
188 self.addLink(h9, self.leafs[4])
189 self.hosts_dict['h9v6'] = h9
190
191 h10 = self.addHost('h10v6', cls=TrellisHost,
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800192 mac='00:bb:00:00:00:08', ips=['1009::3fe/120'],
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800193 gateway='1009::3ff', dhcpClient=dhcp, vlan=40,
194 dualHomed=True)
195 self.addLink(h10, self.leafs[3])
196 self.addLink(h10, self.leafs[4])
197 self.hosts_dict['h10v6'] = h10
198
199 h11 = self.addHost('h11v6', cls=TrellisHost,
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800200 mac='00:bb:00:00:00:0a', ips=['1010::3fe/120'],
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800201 gateway='1010::3ff', dhcpClient=dhcp, vlan=40)
202 self.addLink(h11, self.leafs[4])
203 self.hosts_dict['h11v6'] = h11
204
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -0800205 return
206
207 '''
208 Creates the topology employed by Comcast which is a 2x5
209 leaf spine traffic.
210
211 S1 S2
212
213 L1 L2 L3 L4 L5
214
215 Where L2/L3 and L4/L5 are paired switches.
216 Parameters for this topology :
217 dhcp = True/False : set up dhcp servers
218 routers = True/False : set up external routers
219 '''
220 def __init__(self, dhcp=False, routers=False, ipv4=False, ipv6=False, **opts):
221 Topo.__init__(self, **opts)
222
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -0800223 linkopts = dict( bw=10 )
224
225 spine = 2
226 leaf = 5
227
228 # Create spine switches
229 for s in range(spine):
230 self.spines[s] = self.addSwitch('spine10%s' % (s + 1), dpid = "00000000010%s" % (s + 1) )
231
232 # Create leaf switches
233 for ls in range(leaf):
234 self.leafs[ls] = self.addSwitch('leaf%s' % (ls + 1), dpid = "00000000000%s" % ( ls + 1) )
235
236 # connecting leaf and spines, leafs 1-5 have double links
237 for s in range( spine ):
238 spine_switch = self.spines[s]
239
240 for ls in range( leaf ):
241 leaf_switch = self.leafs[ls]
242
243 self.addLink( spine_switch, leaf_switch, **linkopts )
244 if ls > 0:
245 self.addLink( spine_switch, leaf_switch, **linkopts )
246
247 # connect paired leafs
248 self.addLink(self.leafs[1], self.leafs[2], **linkopts)
249 self.addLink(self.leafs[3], self.leafs[4], **linkopts)
250
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800251 # create dhcp servers
252 if dhcp:
253 if ipv4:
254 dhcp4 = self.addHost( 'dhcp', cls=TrellisHost,
255 mac="00:bb:00:00:00:01", ips=["10.0.3.253/24"],
256 gateway="10.0.3.254", dhcpServer=True)
257 self.addLink(self.spines[1], dhcp4, **linkopts)
258 if ipv6:
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800259 dhcp6 = self.addHost( 'dhcp6', cls=TrellisHost,
260 mac="00:cc:00:00:00:01", ips=["2000::3fd/120"],
261 gateway="2000::3ff", dhcpServer=True, ipv6=True)
262 self.addLink(self.spines[1], dhcp6, **linkopts)
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800263
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -0800264 # create hosts
265 if ipv6:
266 self.createIpv6Hosts(dhcp)
267
268 if ipv4:
269 self.createIpv4Hosts(dhcp)
270
271 if not ipv4 and not ipv6:
272 print("No hosts were created!")
273
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800274 # create quagga routers
275 if routers:
276 last_ls = self.leafs[4]
277 last_paired_ls = self. leafs[3]
278
279 # Control plane switch (for quagga fpm)
280 cs0 = self.addSwitch('cs0', cls=OVSBridge)
281
282 # Control plane NAT (for quagga fpm)
283 nat = self.addHost('nat', cls=NAT,
284 ip='172.16.0.1/12',
285 subnet=str(ip_network(u'172.16.0.0/12')), inNamespace=False)
286 self.addLink(cs0, nat)
287
288 # Internal Quagga bgp1
289 intfs = {'bgp1-eth0': [{'ipAddrs': ['10.0.1.2/24', '2000::102/120'], 'mac': '00:88:00:00:00:03', 'vlan': '110'},
290 {'ipAddrs': ['10.0.7.2/24', '2000::702/120'], 'mac': '00:88:00:00:00:03', 'vlan': '170'}],
291 'bgp1-eth1': {'ipAddrs': ['172.16.0.3/12']}}
292 bgp1 = self.addHost('bgp1', cls=BgpRouter,
293 interfaces=intfs,
294 quaggaConfFile='./bgpdbgp1.conf',
295 zebraConfFile='./zebradbgp1.conf')
296 self.addLink(bgp1, last_paired_ls)
297 self.addLink(bgp1, cs0)
298
299 # Internal Quagga bgp2
300 intfs = {'bgp2-eth0': [{'ipAddrs': ['10.0.5.2/24', '2000::502/120'], 'mac': '00:88:00:00:00:04', 'vlan': '150'},
301 {'ipAddrs': ['10.0.6.2/24', '2000::602/120'], 'mac': '00:88:00:00:00:04', 'vlan': '160'}],
302 'bgp2-eth1': {'ipAddrs': ['172.16.0.4/12']}}
303 bgp2 = self.addHost('bgp2', cls=BgpRouter,
304 interfaces=intfs,
305 quaggaConfFile='./bgpdbgp2.conf',
306 zebraConfFile='./zebradbgp2.conf')
307 self.addLink(bgp2, last_ls)
308 self.addLink(bgp2, cs0)
309
310 # External Quagga r1
311 intfs = {'r1-eth0': {'ipAddrs': ['10.0.1.1/24', '2000::101/120'], 'mac': '00:88:00:00:00:01'},
312 'r1-eth1': {'ipAddrs': ['10.0.5.1/24', '2000::501/120'], 'mac': '00:88:00:00:00:11'},
313 'r1-eth2': {'ipAddrs': ['10.0.99.1/16']},
314 'r1-eth3': {'ipAddrs': ['2000::9901/120']},
315 'r1-eth4': {'ipAddrs': ['2000::7701/120']}}
316 r1 = self.addHost('r1', cls=BgpRouter,
317 interfaces=intfs,
318 quaggaConfFile='./bgpdr1.conf')
319 self.addLink(r1, last_paired_ls)
320 self.addLink(r1, last_ls)
321
322 # External IPv4 Host behind r1
323 rh1 = self.addHost('rh1', cls=RoutedHost, ips=['10.0.99.2/24'], gateway='10.0.99.1')
324 self.addLink(r1, rh1)
325
326 # External IPv6 Host behind r1
327 rh1v6 = self.addHost('rh1v6', cls=RoutedHost, ips=['2000::9902/120'], gateway='2000::9901')
328 self.addLink(r1, rh1v6)
329
330 # Another external IPv6 Host behind r1
331 rh11v6 = self.addHost('rh11v6', cls=RoutedHost, ips=['2000::7702/120'], gateway='2000::7701')
332 self.addLink(r1, rh11v6)
333
334 # External Quagga r2
335 intfs = {'r2-eth0': {'ipAddrs': ['10.0.6.1/24', '2000::601/120'], 'mac': '00:88:00:00:00:02'},
336 'r2-eth1': {'ipAddrs': ['10.0.7.1/24', '2000::701/120'], 'mac': '00:88:00:00:00:22'},
337 'r2-eth2': {'ipAddrs': ['10.0.99.1/16']},
338 'r2-eth3': {'ipAddrs': ['2000::9901/120']},
339 'r2-eth4': {'ipAddrs': ['2000::8801/120']}}
340 r2 = self.addHost('r2', cls=BgpRouter,
341 interfaces=intfs,
342 quaggaConfFile='./bgpdr2.conf')
343 self.addLink(r2, last_ls)
344 self.addLink(r2, last_paired_ls)
345
346 # External IPv4 Host behind r2
347 rh2 = self.addHost('rh2', cls=RoutedHost, ips=['10.0.99.2/24'], gateway='10.0.99.1')
348 self.addLink(r2, rh2)
349
350 # External IPv6 Host behind r2
351 rh2v6 = self.addHost('rh126', cls=RoutedHost, ips=['2000::9902/120'], gateway='2000::9901')
352 self.addLink(r2, rh2v6)
353
354 # Another external IPv6 Host behind r1
355 rh22v6 = self.addHost('rh22v6', cls=RoutedHost, ips=['2000::8802/120'], gateway='2000::8801')
356 self.addLink(r2, rh22v6)
357
358
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -0800359def config( opts ):
360
361 dhcp = bool(opts.dhcp)
362 routers = bool(opts.routers)
363 ipv6 = bool(opts.ipv6)
364 ipv4 = bool(opts.ipv4)
365
366 if opts.onosIp != '':
367 controllers = opts.onosIp.split( ',' )
368 else:
369 controllers = ['127.0.0.1']
370 topo = ComcastLeafSpineFabric(dhcp=dhcp, routers=routers, ipv6=ipv6,
371 ipv4=ipv4)
372
373 net = Mininet( topo=topo, link=TCLink, build=False,
374 controller=None, autoSetMacs=True )
375 i = 0
376 for ip in controllers:
377 net.addController( "c%s" % ( i ), controller=RemoteController, ip=ip )
378 i += 1
379
380 net.build()
381 net.start()
382 CLI( net )
383 net.stop()
384
385
386if __name__ == '__main__':
387 setLogLevel('info')
388 config(opts)
389 os.system('sudo mn -c')
390