blob: 79b499266ae9fd2ed260eae17c82230f8be9e4c8 [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'],
99 gateway='10.3.10.254', dhcpClient=dhcp, vlan=40,
100 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'],
107 gateway='10.3.30.254', dhcpClient=dhcp, vlan=40,
108 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'],
115 gateway='10.3.20.254', dhcpClient=dhcp, vlan=40)
116 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'],
121 gateway='10.5.10.254', dhcpClient=dhcp, vlan=10)
122 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
130
131 return
132
133 def createIpv6Hosts(self, dhcp):
134
135 h1 = self.addHost('h1v6', cls=TrellisHost,
136 mac='00:bb:00:00:00:01', ips=["1000::3fe/120"],
137 gateway='1000::3ff', dhcpClient=dhcp, ipv6=1)
138 self.addLink(h1, self.leafs[0])
139 self.hosts_dict['h1v6'] = h1
140
141 h2 = self.addHost('h2v6', cls=TrellisHost,
142 mac='00:bb:00:00:01:01', ips=['1001::3fe/120'],
143 gateway='1001::3ff', dhcpClient=dhcp, ipv6=1)
144 self.addLink(h2, self.leafs[0])
145 self.hosts_dict['h2v6'] = h2
146
147 h3 = self.addHost('h3v6', cls=TrellisHost,
148 mac='00:bb:00:00:00:02', ips=['1002::3fe/120'],
149 gateway='1002::3ff', dhcpClient=dhcp, ipv6=1)
150 self.addLink(h3, self.leafs[1])
151 self.hosts_dict['h3v6'] = h3
152
153 h4 = self.addHost('h4v6', cls=TrellisHost,
154 mac='00:bb:00:00:00:03', ips=['1003::3fe/120'],
155 gateway='1003::3ff', dhcpClient=dhcp, ipv6=1,
156 dualHomed=True)
157 self.addLink(h4, self.leafs[1])
158 self.addLink(h4, self.leafs[2])
159 self.hosts_dict['h4v6'] = h4
160
161 h5 = self.addHost('h5v6', cls=TrellisHost,
162 mac='00:bb:00:00:00:04', ips=['1004::3fe/120'],
163 gateway='1004::3ff', dhcpClient=False, ipv6=1,
164 vlan=30,
165 dualHomed=True)
166 self.addLink(h5, self.leafs[1])
167 self.addLink(h5, self.leafs[2])
168 self.hosts_dict['h5v6'] = h5
169
170 h6 = self.addHost('h6v6', cls=TrellisHost,
171 mac='00:bb:00:00:00:05', ips=['1005::3fe/120'],
172 gateway='1005::3ff', dhcpClient=False, ipv6=1,
173 vlan=20)
174 self.addLink(h6, self.leafs[2])
175 self.hosts_dict['h6v6'] = h6
176
177 h7 = self.addHost('h7v6', cls=TrellisHost,
178 mac='00:bb:00:00:01:05', ips=['1006::3fe/120'],
179 gateway='1006::3ff', dhcpClient=False, ipv6=1,
180 vlan=40)
181 self.addLink(h7, self.leafs[2])
182 self.hosts_dict['h7v6'] = h7
183
184 h8 = self.addHost('h8v6', cls=TrellisHost,
185 mac='00:bb:00:00:00:06', ips=['1007::3fe/120'],
186 gateway='1007::3ff', dhcpClient=dhcp, ipv6=1)
187 self.addLink(h8, self.leafs[3])
188 self.hosts_dict['h8v6'] = h8
189
190 h9 = self.addHost('h9v6', cls=TrellisHost,
191 mac='00:bb:00:00:00:07', ips=['1008::3fe/120'],
192 gateway='1008::3ff', dhcpClient=False, vlan=40,
193 dualHomed=True, ipv6=1)
194 self.addLink(h9, self.leafs[3])
195 self.addLink(h9, self.leafs[4])
196 self.hosts_dict['h9v6'] = h9
197
198 h10 = self.addHost('h10v6', cls=TrellisHost,
199 mac='00:bb:00:00:00:08', ips=['1009::3fe/120'],
200 gateway='1009::3ff', dhcpClient=False, vlan=40,
201 dualHomed=True, ipv6=1)
202 self.addLink(h10, self.leafs[3])
203 self.addLink(h10, self.leafs[4])
204 self.hosts_dict['h10v6'] = h10
205
206 h11 = self.addHost('h11v6', cls=TrellisHost,
207 mac='00:bb:00:00:00:0a', ips=['1010::3fe/120'],
208 gateway='1010::3ff', dhcpClient=False, vlan=40,
209 ipv6=1)
210 self.addLink(h11, self.leafs[4])
211 self.hosts_dict['h11v6'] = h11
212
213 h12 = self.addHost('h12v6', cls=TrellisHost,
214 mac='00:bb:00:00:01:0a', ips=['1011::3fe/120'],
215 gateway='1011::3ff', dhcpClient=False, vlan=10,
216 ipv6=1)
217 self.addLink(h12, self.leafs[5])
218 self.hosts_dict['h12v6'] = h12
219
220 h13 = self.addHost('h13v6', cls=TrellisHost,
221 mac='00:bb:00:00:02:0a', ips=['1012::3fe/120'],
222 gateway='1012::3ff', dhcpClient=dhcp, ipv6=1)
223 self.addLink(h13, self.leafs[5])
224 self.hosts_dict['h13v6'] = h13
225
226 return
227
228 '''
229 Creates the HAGG topology employed by Comcast.
230 '''
231 def __init__(self, dhcp=False, routers=False, ipv4=False, ipv6=False, **opts):
232 Topo.__init__(self, **opts)
233
234 linkopts = dict( bw=10 )
235
236 spine = 4
237 leaf = 6
238
239 # Create spine switches
240 for s in range(spine):
241 self.spines[s] = self.addSwitch('spine10%s' % (s + 1), dpid = "00000000010%s" % (s + 1) )
242
243 # Create leaf switches
244 for ls in range(leaf):
245 self.leafs[ls] = self.addSwitch('leaf%s' % (ls + 1), dpid = "00000000000%s" % ( ls + 1) )
246
247 # connecting leaf and spines, leafs 1-5 have double links
248 for s in range(2):
249 spine_switch = self.spines[s]
250
251 for ls in range(1, 5):
252 leaf_switch = self.leafs[ls]
253
254 self.addLink( spine_switch, leaf_switch, **linkopts )
255 self.addLink( spine_switch, leaf_switch, **linkopts )
256
257 # connect paired leafs
258 self.addLink(self.leafs[1], self.leafs[2], **linkopts)
259 self.addLink(self.leafs[3], self.leafs[4], **linkopts)
260
261 # build second fabric with single links
262 for s in range(2, 4):
263 spine_switch = self.spines[s]
264
265 for ls in [0, 5]:
266 leaf_switch = self.leafs[ls]
267 self.addLink( spine_switch, leaf_switch, **linkopts )
268
269 # connect spines together
270 self.addLink(self.spines[2], self.spines[0], **linkopts)
271 self.addLink(self.spines[3], self.spines[1], **linkopts)
272
273 # create hosts
274 if ipv6:
275 self.createIpv6Hosts(dhcp)
276
277 if ipv4:
278 self.createIpv4Hosts(dhcp)
279
280 if not ipv4 and not ipv6:
281 print("No hosts were created!")
282
283 # create quagga routers
284 # Note: Change "fpm connection ip" to $OC1 in zebradbgp1.conf and zebradbgp2.conf to make quagga work correctly
285 if routers:
286 last_ls = self.leafs[4]
287 last_paired_ls = self.leafs[3]
288
289 # Control plane switch (for quagga fpm)
290 cs0 = self.addSwitch('cs0', cls=OVSBridge)
291
292 # Control plane NAT (for quagga fpm)
293 nat = self.addHost('nat', cls=NAT,
294 ip='172.16.0.1/12',
295 subnet=str(ip_network(u'172.16.0.0/12')), inNamespace=False)
296 self.addLink(cs0, nat)
297
298 # Internal Quagga bgp1
299 intfs = {'bgp1-eth0': [{'ipAddrs': ['10.0.1.2/24', '2000::102/120'], 'mac': '00:88:00:00:00:03', 'vlan': '110'},
300 {'ipAddrs': ['10.0.7.2/24', '2000::702/120'], 'mac': '00:88:00:00:00:03', 'vlan': '170'}],
301 'bgp1-eth1': {'ipAddrs': ['172.16.0.3/12']}}
302 bgp1 = self.addHost('bgp1', cls=BgpRouter,
303 interfaces=intfs,
304 quaggaConfFile='./bgpdbgp1.conf',
305 zebraConfFile='./zebradbgp1.conf')
306 self.addLink(bgp1, last_paired_ls)
307 self.addLink(bgp1, cs0)
308
309 # Internal Quagga bgp2
310 intfs = {'bgp2-eth0': [{'ipAddrs': ['10.0.5.2/24', '2000::502/120'], 'mac': '00:88:00:00:00:04', 'vlan': '150'},
311 {'ipAddrs': ['10.0.6.2/24', '2000::602/120'], 'mac': '00:88:00:00:00:04', 'vlan': '160'}],
312 'bgp2-eth1': {'ipAddrs': ['172.16.0.4/12']}}
313 bgp2 = self.addHost('bgp2', cls=BgpRouter,
314 interfaces=intfs,
315 quaggaConfFile='./bgpdbgp2.conf',
316 zebraConfFile='./zebradbgp2.conf')
317 self.addLink(bgp2, last_ls)
318 self.addLink(bgp2, cs0)
319
320 # External Quagga r1
321 intfs = {'r1-eth0': {'ipAddrs': ['10.0.1.1/24', '2000::101/120'], 'mac': '00:88:00:00:00:01'},
322 'r1-eth1': {'ipAddrs': ['10.0.5.1/24', '2000::501/120'], 'mac': '00:88:00:00:00:11'},
323 'r1-eth2': {'ipAddrs': ['10.0.99.1/16']},
324 'r1-eth3': {'ipAddrs': ['2000::9901/120']},
325 'r1-eth4': {'ipAddrs': ['2000::7701/120']},
326 'r1-eth5': {'ipAddrs': ['10.0.88.1/24']},
327 'r1-eth6': {'ipAddrs': ['2000::8701/120']}}
328 r1 = self.addHost('r1', cls=BgpRouter,
329 interfaces=intfs,
330 quaggaConfFile='./bgpdr1.conf')
331 self.addLink(r1, last_paired_ls)
332 self.addLink(r1, last_ls)
333
334 # External IPv4 Host behind r1
335 rh1v4 = self.addHost('rh1v4', cls=RoutedHost, ips=['10.0.99.2/24'], gateway='10.0.99.1')
336 self.addLink(r1, rh1v4)
337
338 # External IPv6 Host behind r1
339 rh1v6 = self.addHost('rh1v6', cls=RoutedHost, ips=['2000::9902/120'], gateway='2000::9901')
340 self.addLink(r1, rh1v6)
341
342 # Another external IPv6 Host behind r1
343 rh11v6 = self.addHost('rh11v6', cls=RoutedHost, ips=['2000::7702/120'], gateway='2000::7701')
344 self.addLink(r1, rh11v6)
345
346 # Add an external ipv4 hosts that is not configured in the bgp conf
347 # files
348 rh5v4 = self.addHost('rh5v4', cls=RoutedHost, ips=['10.0.88.2/24'], gateway='10.0.88.1')
349 self.addLink(r1, rh5v4)
350
351 # Add an external ipv6 hosts that is not configured in the bgp conf
352 # files
353 rh5v6 = self.addHost('rh5v6', cls=RoutedHost, ips=['2000::8702/120'], gateway='2000::8701')
354 self.addLink(r1, rh5v6)
355
356 # External Quagga r2
357 intfs = {'r2-eth0': {'ipAddrs': ['10.0.6.1/24', '2000::601/120'], 'mac': '00:88:00:00:00:02'},
358 'r2-eth1': {'ipAddrs': ['10.0.7.1/24', '2000::701/120'], 'mac': '00:88:00:00:00:22'},
359 'r2-eth2': {'ipAddrs': ['10.0.99.1/16']},
360 'r2-eth3': {'ipAddrs': ['2000::9901/120']},
361 'r2-eth4': {'ipAddrs': ['2000::8801/120']}}
362 r2 = self.addHost('r2', cls=BgpRouter,
363 interfaces=intfs,
364 quaggaConfFile='./bgpdr2.conf')
365 self.addLink(r2, last_ls)
366 self.addLink(r2, last_paired_ls)
367
368 # External IPv4 Host behind r2
369 rh2v4 = self.addHost('rh2v4', cls=RoutedHost, ips=['10.0.99.2/24'], gateway='10.0.99.1')
370 self.addLink(r2, rh2v4)
371
372 # External IPv6 Host behind r2
373 rh2v6 = self.addHost('rh2v6', cls=RoutedHost, ips=['2000::9902/120'], gateway='2000::9901')
374 self.addLink(r2, rh2v6)
375
376 # Another external IPv6 Host behind r1
377 rh22v6 = self.addHost('rh22v6', cls=RoutedHost, ips=['2000::8802/120'], gateway='2000::8801')
378 self.addLink(r2, rh22v6)
379
380 # create dhcp servers
381 if dhcp:
382 cs1 = self.addSwitch('cs1', cls=OVSBridge)
383 self.addLink(cs1, self.leafs[4])
384 if ipv4:
385 dhcp4 = self.addHost( 'dhcp', cls=TrellisHost,
386 mac="00:cc:00:00:00:01", ips=["10.0.3.253/24"],
387 gateway="10.0.3.254", dhcpServer=True)
388 self.addLink(dhcp4, cs1, **linkopts)
389 if ipv6:
390 dhcp6 = self.addHost( 'dhcp6', cls=TrellisHost,
391 mac="00:dd:00:00:00:01", ips=["2000::3fd/120"],
392 gateway="2000::3ff", dhcpServer=True, ipv6=True)
393 self.addLink(dhcp6, cs1, **linkopts)
394
395
396def config( opts ):
397
398 dhcp = bool(opts.dhcp)
399 routers = bool(opts.routers)
400 ipv6 = bool(opts.ipv6)
401 ipv4 = bool(opts.ipv4)
402
403 if opts.onosIp != '':
404 controllers = opts.onosIp.split( ',' )
405 else:
406 controllers = ['127.0.0.1']
407 topo = ComcastLeafSpineFabric(dhcp=dhcp, routers=routers, ipv6=ipv6,
408 ipv4=ipv4)
409
410 net = Mininet( topo=topo, link=TCLink, build=False,
411 controller=None, autoSetMacs=True )
412 i = 0
413 for ip in controllers:
414 net.addController( "c%s" % ( i ), controller=RemoteController, ip=ip )
415 i += 1
416
417 net.build()
418 net.start()
419 CLI( net )
420 net.stop()
421
422
423if __name__ == '__main__':
424 setLogLevel('info')
425 config(opts)
426 os.system('sudo mn -c')
427