blob: b4bb587d91a34ca74edbae3a0cf04c0104e4d219 [file] [log] [blame]
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -07001#!/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
15from trellislib import DhcpServer, TaggedRoutedHost, DualHomedRoutedHost, DualHomedTaggedRoutedHost, DhcpClient, Dhcp6Client, DhcpServer, Dhcp6Server, TrellisHost
16
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
37class ComcastLeafSpineFabric(Topo):
38
39 spines = dict()
40 leafs = dict()
41 hosts_dict = dict()
42
43 def createIpv4Hosts(self, dhcp):
44
45 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)
48 self.addLink(h1, self.leafs[0])
49 self.hosts_dict['h1v4'] = h1
50
51 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)
54 self.addLink(h2, self.leafs[0])
55 self.hosts_dict['h2v4'] = h2
56
57 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)
60 self.addLink(h3, self.leafs[1])
61 self.hosts_dict['h3v4'] = h3
62
63 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)
67 self.addLink(h4, self.leafs[1])
68 self.addLink(h4, self.leafs[2])
69 self.hosts_dict['h4v4'] = h4
70
71 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)
75 self.addLink(h5, self.leafs[1])
76 self.addLink(h5, self.leafs[2])
77 self.hosts_dict['h5v4'] = h5
78
79 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)
82 self.addLink(h6, self.leafs[2])
83 self.hosts_dict['h6v4'] = h6
84
85 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)
88 self.addLink(h7, self.leafs[2])
89 self.hosts_dict['h7v4'] = h7
90
91 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)
94 self.addLink(h8, self.leafs[3])
95 self.hosts_dict['h8v4'] = h8
96
97 h9 = self.addHost('h9v4', cls=TrellisHost,
98 mac='00:aa:00:00:00:07', ips=['10.3.10.1/24'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -070099 gateway='10.3.10.254', dhcpClient=dhcp, vlan=50,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700100 dualHomed=True)
101 self.addLink(h9, self.leafs[3])
102 self.addLink(h9, self.leafs[4])
103 self.hosts_dict['h9v4'] = h9
104
105 h10 = self.addHost('h10v4', cls=TrellisHost,
106 mac='00:aa:00:00:00:08', ips=['10.3.30.1/24'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700107 gateway='10.3.30.254', dhcpClient=dhcp, vlan=60,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700108 dualHomed=True)
109 self.addLink(h10, self.leafs[3])
110 self.addLink(h10, self.leafs[4])
111 self.hosts_dict['h10v4'] = h10
112
113 h11 = self.addHost('h11v4', cls=TrellisHost,
114 mac='00:aa:00:00:00:0a', ips=['10.3.20.1/24'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700115 gateway='10.3.20.254', dhcpClient=dhcp, vlan=70)
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700116 self.addLink(h11, self.leafs[4])
117 self.hosts_dict['h11v4'] = h11
118
119 h12 = self.addHost('h12v4', cls=TrellisHost,
120 mac='00:aa:00:00:02:01', ips=['10.5.10.1/24'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700121 gateway='10.5.10.254', dhcpClient=dhcp, vlan=80)
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700122 self.addLink(h12, self.leafs[5])
123 self.hosts_dict['h12v4'] = h12
124
125 h13 = self.addHost('h13v4', cls=TrellisHost,
126 mac='00:aa:00:00:02:02', ips=['10.5.20.1/24'],
127 gateway='10.5.20.254', dhcpClient=dhcp)
128 self.addLink(h13, self.leafs[5])
129 self.hosts_dict['h13v4'] = h13
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700130 return
131
132 def createIpv6Hosts(self, dhcp):
133
134 h1 = self.addHost('h1v6', cls=TrellisHost,
135 mac='00:bb:00:00:00:01', ips=["1000::3fe/120"],
136 gateway='1000::3ff', dhcpClient=dhcp, ipv6=1)
137 self.addLink(h1, self.leafs[0])
138 self.hosts_dict['h1v6'] = h1
139
140 h2 = self.addHost('h2v6', cls=TrellisHost,
141 mac='00:bb:00:00:01:01', ips=['1001::3fe/120'],
142 gateway='1001::3ff', dhcpClient=dhcp, ipv6=1)
143 self.addLink(h2, self.leafs[0])
144 self.hosts_dict['h2v6'] = h2
145
146 h3 = self.addHost('h3v6', cls=TrellisHost,
147 mac='00:bb:00:00:00:02', ips=['1002::3fe/120'],
148 gateway='1002::3ff', dhcpClient=dhcp, ipv6=1)
149 self.addLink(h3, self.leafs[1])
150 self.hosts_dict['h3v6'] = h3
151
152 h4 = self.addHost('h4v6', cls=TrellisHost,
153 mac='00:bb:00:00:00:03', ips=['1003::3fe/120'],
154 gateway='1003::3ff', dhcpClient=dhcp, ipv6=1,
155 dualHomed=True)
156 self.addLink(h4, self.leafs[1])
157 self.addLink(h4, self.leafs[2])
158 self.hosts_dict['h4v6'] = h4
159
160 h5 = self.addHost('h5v6', cls=TrellisHost,
161 mac='00:bb:00:00:00:04', ips=['1004::3fe/120'],
162 gateway='1004::3ff', dhcpClient=False, ipv6=1,
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700163 vlan=121,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700164 dualHomed=True)
165 self.addLink(h5, self.leafs[1])
166 self.addLink(h5, self.leafs[2])
167 self.hosts_dict['h5v6'] = h5
168
169 h6 = self.addHost('h6v6', cls=TrellisHost,
170 mac='00:bb:00:00:00:05', ips=['1005::3fe/120'],
171 gateway='1005::3ff', dhcpClient=False, ipv6=1,
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700172 vlan=122)
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700173 self.addLink(h6, self.leafs[2])
174 self.hosts_dict['h6v6'] = h6
175
176 h7 = self.addHost('h7v6', cls=TrellisHost,
177 mac='00:bb:00:00:01:05', ips=['1006::3fe/120'],
178 gateway='1006::3ff', dhcpClient=False, ipv6=1,
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700179 vlan=123)
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700180 self.addLink(h7, self.leafs[2])
181 self.hosts_dict['h7v6'] = h7
182
183 h8 = self.addHost('h8v6', cls=TrellisHost,
184 mac='00:bb:00:00:00:06', ips=['1007::3fe/120'],
185 gateway='1007::3ff', dhcpClient=dhcp, ipv6=1)
186 self.addLink(h8, self.leafs[3])
187 self.hosts_dict['h8v6'] = h8
188
189 h9 = self.addHost('h9v6', cls=TrellisHost,
190 mac='00:bb:00:00:00:07', ips=['1008::3fe/120'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700191 gateway='1008::3ff', dhcpClient=False, vlan=124,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700192 dualHomed=True, ipv6=1)
193 self.addLink(h9, self.leafs[3])
194 self.addLink(h9, self.leafs[4])
195 self.hosts_dict['h9v6'] = h9
196
197 h10 = self.addHost('h10v6', cls=TrellisHost,
198 mac='00:bb:00:00:00:08', ips=['1009::3fe/120'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700199 gateway='1009::3ff', dhcpClient=False, vlan=125,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700200 dualHomed=True, ipv6=1)
201 self.addLink(h10, self.leafs[3])
202 self.addLink(h10, self.leafs[4])
203 self.hosts_dict['h10v6'] = h10
204
205 h11 = self.addHost('h11v6', cls=TrellisHost,
206 mac='00:bb:00:00:00:0a', ips=['1010::3fe/120'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700207 gateway='1010::3ff', dhcpClient=False, vlan=126,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700208 ipv6=1)
209 self.addLink(h11, self.leafs[4])
210 self.hosts_dict['h11v6'] = h11
211
212 h12 = self.addHost('h12v6', cls=TrellisHost,
213 mac='00:bb:00:00:01:0a', ips=['1011::3fe/120'],
Andreas Pantelopoulos1e0665a2018-06-05 13:34:59 -0700214 gateway='1011::3ff', dhcpClient=False, vlan=127,
Andreas Pantelopoulosd5d470f2018-03-23 18:02:47 -0700215 ipv6=1)
216 self.addLink(h12, self.leafs[5])
217 self.hosts_dict['h12v6'] = h12
218
219 h13 = self.addHost('h13v6', cls=TrellisHost,
220 mac='00:bb:00:00:02:0a', ips=['1012::3fe/120'],
221 gateway='1012::3ff', dhcpClient=dhcp, ipv6=1)
222 self.addLink(h13, self.leafs[5])
223 self.hosts_dict['h13v6'] = h13
224
225 return
226
227 '''
228 Creates the HAGG topology employed by Comcast.
229 '''
230 def __init__(self, dhcp=False, routers=False, ipv4=False, ipv6=False, **opts):
231 Topo.__init__(self, **opts)
232
233 linkopts = dict( bw=10 )
234
235 spine = 4
236 leaf = 6
237
238 # Create spine switches
239 for s in range(spine):
240 self.spines[s] = self.addSwitch('spine10%s' % (s + 1), dpid = "00000000010%s" % (s + 1) )
241
242 # Create leaf switches
243 for ls in range(leaf):
244 self.leafs[ls] = self.addSwitch('leaf%s' % (ls + 1), dpid = "00000000000%s" % ( ls + 1) )
245
246 # connecting leaf and spines, leafs 1-5 have double links
247 for s in range(2):
248 spine_switch = self.spines[s]
249
250 for ls in range(1, 5):
251 leaf_switch = self.leafs[ls]
252
253 self.addLink( spine_switch, leaf_switch, **linkopts )
254 self.addLink( spine_switch, leaf_switch, **linkopts )
255
256 # connect paired leafs
257 self.addLink(self.leafs[1], self.leafs[2], **linkopts)
258 self.addLink(self.leafs[3], self.leafs[4], **linkopts)
259
260 # build second fabric with single links
261 for s in range(2, 4):
262 spine_switch = self.spines[s]
263
264 for ls in [0, 5]:
265 leaf_switch = self.leafs[ls]
266 self.addLink( spine_switch, leaf_switch, **linkopts )
267
268 # connect spines together
269 self.addLink(self.spines[2], self.spines[0], **linkopts)
270 self.addLink(self.spines[3], self.spines[1], **linkopts)
271
272 # create hosts
273 if ipv6:
274 self.createIpv6Hosts(dhcp)
275
276 if ipv4:
277 self.createIpv4Hosts(dhcp)
278
279 if not ipv4 and not ipv6:
280 print("No hosts were created!")
281
282 # create quagga routers
283 # Note: Change "fpm connection ip" to $OC1 in zebradbgp1.conf and zebradbgp2.conf to make quagga work correctly
284 if routers:
285 last_ls = self.leafs[4]
286 last_paired_ls = self.leafs[3]
287
288 # Control plane switch (for quagga fpm)
289 cs0 = self.addSwitch('cs0', cls=OVSBridge)
290
291 # Control plane NAT (for quagga fpm)
292 nat = self.addHost('nat', cls=NAT,
293 ip='172.16.0.1/12',
294 subnet=str(ip_network(u'172.16.0.0/12')), inNamespace=False)
295 self.addLink(cs0, nat)
296
297 # Internal Quagga bgp1
298 intfs = {'bgp1-eth0': [{'ipAddrs': ['10.0.1.2/24', '2000::102/120'], 'mac': '00:88:00:00:00:03', 'vlan': '110'},
299 {'ipAddrs': ['10.0.7.2/24', '2000::702/120'], 'mac': '00:88:00:00:00:03', 'vlan': '170'}],
300 'bgp1-eth1': {'ipAddrs': ['172.16.0.3/12']}}
301 bgp1 = self.addHost('bgp1', cls=BgpRouter,
302 interfaces=intfs,
303 quaggaConfFile='./bgpdbgp1.conf',
304 zebraConfFile='./zebradbgp1.conf')
305 self.addLink(bgp1, last_paired_ls)
306 self.addLink(bgp1, cs0)
307
308 # Internal Quagga bgp2
309 intfs = {'bgp2-eth0': [{'ipAddrs': ['10.0.5.2/24', '2000::502/120'], 'mac': '00:88:00:00:00:04', 'vlan': '150'},
310 {'ipAddrs': ['10.0.6.2/24', '2000::602/120'], 'mac': '00:88:00:00:00:04', 'vlan': '160'}],
311 'bgp2-eth1': {'ipAddrs': ['172.16.0.4/12']}}
312 bgp2 = self.addHost('bgp2', cls=BgpRouter,
313 interfaces=intfs,
314 quaggaConfFile='./bgpdbgp2.conf',
315 zebraConfFile='./zebradbgp2.conf')
316 self.addLink(bgp2, last_ls)
317 self.addLink(bgp2, cs0)
318
319 # External Quagga r1
320 intfs = {'r1-eth0': {'ipAddrs': ['10.0.1.1/24', '2000::101/120'], 'mac': '00:88:00:00:00:01'},
321 'r1-eth1': {'ipAddrs': ['10.0.5.1/24', '2000::501/120'], 'mac': '00:88:00:00:00:11'},
322 'r1-eth2': {'ipAddrs': ['10.0.99.1/16']},
323 'r1-eth3': {'ipAddrs': ['2000::9901/120']},
324 'r1-eth4': {'ipAddrs': ['2000::7701/120']},
325 'r1-eth5': {'ipAddrs': ['10.0.88.1/24']},
326 'r1-eth6': {'ipAddrs': ['2000::8701/120']}}
327 r1 = self.addHost('r1', cls=BgpRouter,
328 interfaces=intfs,
329 quaggaConfFile='./bgpdr1.conf')
330 self.addLink(r1, last_paired_ls)
331 self.addLink(r1, last_ls)
332
333 # External IPv4 Host behind r1
334 rh1v4 = self.addHost('rh1v4', cls=RoutedHost, ips=['10.0.99.2/24'], gateway='10.0.99.1')
335 self.addLink(r1, rh1v4)
336
337 # External IPv6 Host behind r1
338 rh1v6 = self.addHost('rh1v6', cls=RoutedHost, ips=['2000::9902/120'], gateway='2000::9901')
339 self.addLink(r1, rh1v6)
340
341 # Another external IPv6 Host behind r1
342 rh11v6 = self.addHost('rh11v6', cls=RoutedHost, ips=['2000::7702/120'], gateway='2000::7701')
343 self.addLink(r1, rh11v6)
344
345 # Add an external ipv4 hosts that is not configured in the bgp conf
346 # files
347 rh5v4 = self.addHost('rh5v4', cls=RoutedHost, ips=['10.0.88.2/24'], gateway='10.0.88.1')
348 self.addLink(r1, rh5v4)
349
350 # Add an external ipv6 hosts that is not configured in the bgp conf
351 # files
352 rh5v6 = self.addHost('rh5v6', cls=RoutedHost, ips=['2000::8702/120'], gateway='2000::8701')
353 self.addLink(r1, rh5v6)
354
355 # External Quagga r2
356 intfs = {'r2-eth0': {'ipAddrs': ['10.0.6.1/24', '2000::601/120'], 'mac': '00:88:00:00:00:02'},
357 'r2-eth1': {'ipAddrs': ['10.0.7.1/24', '2000::701/120'], 'mac': '00:88:00:00:00:22'},
358 'r2-eth2': {'ipAddrs': ['10.0.99.1/16']},
359 'r2-eth3': {'ipAddrs': ['2000::9901/120']},
360 'r2-eth4': {'ipAddrs': ['2000::8801/120']}}
361 r2 = self.addHost('r2', cls=BgpRouter,
362 interfaces=intfs,
363 quaggaConfFile='./bgpdr2.conf')
364 self.addLink(r2, last_ls)
365 self.addLink(r2, last_paired_ls)
366
367 # External IPv4 Host behind r2
368 rh2v4 = self.addHost('rh2v4', cls=RoutedHost, ips=['10.0.99.2/24'], gateway='10.0.99.1')
369 self.addLink(r2, rh2v4)
370
371 # External IPv6 Host behind r2
372 rh2v6 = self.addHost('rh2v6', cls=RoutedHost, ips=['2000::9902/120'], gateway='2000::9901')
373 self.addLink(r2, rh2v6)
374
375 # Another external IPv6 Host behind r1
376 rh22v6 = self.addHost('rh22v6', cls=RoutedHost, ips=['2000::8802/120'], gateway='2000::8801')
377 self.addLink(r2, rh22v6)
378
379 # create dhcp servers
380 if dhcp:
381 cs1 = self.addSwitch('cs1', cls=OVSBridge)
382 self.addLink(cs1, self.leafs[4])
383 if ipv4:
384 dhcp4 = self.addHost( 'dhcp', cls=TrellisHost,
385 mac="00:cc:00:00:00:01", ips=["10.0.3.253/24"],
386 gateway="10.0.3.254", dhcpServer=True)
387 self.addLink(dhcp4, cs1, **linkopts)
388 if ipv6:
389 dhcp6 = self.addHost( 'dhcp6', cls=TrellisHost,
390 mac="00:dd:00:00:00:01", ips=["2000::3fd/120"],
391 gateway="2000::3ff", dhcpServer=True, ipv6=True)
392 self.addLink(dhcp6, cs1, **linkopts)
393
394
395def config( opts ):
396
397 dhcp = bool(opts.dhcp)
398 routers = bool(opts.routers)
399 ipv6 = bool(opts.ipv6)
400 ipv4 = bool(opts.ipv4)
401
402 if opts.onosIp != '':
403 controllers = opts.onosIp.split( ',' )
404 else:
405 controllers = ['127.0.0.1']
406 topo = ComcastLeafSpineFabric(dhcp=dhcp, routers=routers, ipv6=ipv6,
407 ipv4=ipv4)
408
409 net = Mininet( topo=topo, link=TCLink, build=False,
410 controller=None, autoSetMacs=True )
411 i = 0
412 for ip in controllers:
413 net.addController( "c%s" % ( i ), controller=RemoteController, ip=ip )
414 i += 1
415
416 net.build()
417 net.start()
418 CLI( net )
419 net.stop()
420
421
422if __name__ == '__main__':
423 setLogLevel('info')
424 config(opts)
425 os.system('sudo mn -c')
426