blob: 13a75e2007899b1ffcc456151f839290f6cdb0fe [file] [log] [blame]
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -07001#!/usr/bin/python
2
3"""
4Libraries for creating L3 topologies with routing protocols.
5"""
6
7from mininet.node import Host, OVSBridge
8from mininet.nodelib import NAT
9from mininet.log import info, debug, error
10from mininet.cli import CLI
11from ipaddress import ip_network, ip_address, ip_interface
12import os
13
14class RoutedHost(Host):
15 """Host that can be configured with multiple IP addresses."""
16 def __init__(self, name, ips, gateway, *args, **kwargs):
17 super(RoutedHost, self).__init__(name, *args, **kwargs)
18
19 self.ips = ips
20 self.gateway = gateway
21
22 def config(self, **kwargs):
23 Host.config(self, **kwargs)
24
25 self.cmd('ip -4 addr flush dev %s' % self.defaultIntf())
26 for ip in self.ips:
27 self.cmd('ip addr add %s dev %s' % (ip, self.defaultIntf()))
28
29 self.cmd('ip route add default via %s' % self.gateway)
30
31class RoutedHost6(Host):
32 """Host that can be configured with multiple IP addresses."""
33 def __init__(self, name, ips, gateway, *args, **kwargs):
34 super(RoutedHost6, self).__init__(name, *args, **kwargs)
35
36 self.ips = ips
37 self.gateway = gateway
38
39 def config(self, **kwargs):
40 Host.config(self, **kwargs)
41
42 self.cmd('ip -6 addr flush dev %s' % self.defaultIntf())
43 for ip in self.ips:
44 self.cmd('ip -6 addr add %s dev %s' % (ip, self.defaultIntf()))
45
46 self.cmd('ip -6 route add default via %s' % self.gateway)
47
48class Router(Host):
49
50 """An L3 router.
51 Configures the Linux kernel for L3 forwarding and supports rich interface
52 configuration of IP addresses, MAC addresses and VLANs."""
53
54 def __init__(self, name, interfaces, *args, **kwargs):
55 super(Router, self).__init__(name, **kwargs)
56
57 self.interfaces = interfaces
58
59 def config(self, **kwargs):
60 super(Host, self).config(**kwargs)
61
62 self.cmd('sysctl net.ipv4.ip_forward=1')
63 self.cmd('sysctl net.ipv4.conf.all.rp_filter=0')
64 self.cmd('sysctl net.ipv6.conf.all.forwarding=1')
65
66 for intf, configs in self.interfaces.items():
67 self.cmd('ip -4 addr flush dev %s' % intf)
68 self.cmd( 'sysctl net.ipv4.conf.%s.rp_filter=0' % intf )
69
70 if not isinstance(configs, list):
71 configs = [configs]
72
73 for attrs in configs:
74 # Configure the vlan if there is one
75 if 'vlan' in attrs:
76 vlanName = '%s.%s' % (intf, attrs['vlan'])
77 self.cmd('ip link add link %s name %s type vlan id %s' %
78 (intf, vlanName, attrs['vlan']))
79 self.cmd('ip link set %s up' % vlanName)
80 addrIntf = vlanName
81 else:
82 addrIntf = intf
83
84 # Now configure the addresses on the vlan/native interface
85 if 'mac' in attrs:
86 self.cmd('ip link set %s down' % addrIntf)
87 self.cmd('ip link set %s address %s' % (addrIntf, attrs['mac']))
88 self.cmd('ip link set %s up' % addrIntf)
89 for addr in attrs['ipAddrs']:
90 self.cmd('ip addr add %s dev %s' % (addr, addrIntf))
91
92class QuaggaRouter(Router):
93
94 """Runs Quagga to create a router that can speak routing protocols."""
95
96 binDir = '/usr/lib/quagga'
97 logDir = '/var/log/quagga'
98
99 def __init__(self, name, interfaces,
100 defaultRoute=None,
101 zebraConfFile=None,
102 protocols= [],
103 fpm=None,
104 runDir='/var/run/quagga', *args, **kwargs):
105 super(QuaggaRouter, self).__init__(name, interfaces, **kwargs)
106
107 self.protocols = protocols
108 self.fpm = fpm
109
110 for p in self.protocols:
111 p.setQuaggaRouter(self)
112
113 self.runDir = runDir
114 self.defaultRoute = defaultRoute
115
116 # Ensure required directories exist
117 try:
118 original_umask = os.umask(0)
119 if (not os.path.isdir(QuaggaRouter.logDir)):
120 os.makedirs(QuaggaRouter.logDir, 0777)
121 if (not os.path.isdir(self.runDir)):
122 os.makedirs(self.runDir, 0777)
123 finally:
124 os.umask(original_umask)
125
126 self.zebraConfFile = zebraConfFile
127 if (self.zebraConfFile is None):
128 self.zebraConfFile = '%s/zebrad%s.conf' % (self.runDir, self.name)
129 self.generateZebra()
130
131 self.socket = '%s/zebra%s.api' % (self.runDir, self.name)
132
133 self.zebraPidFile = '%s/zebra%s.pid' % (self.runDir, self.name)
134
135 def generateZebra(self):
136 configFile = open(self.zebraConfFile, 'w+')
137 configFile.write('log file %s/zebrad%s.log\n' % (QuaggaRouter.logDir, self.name))
138 configFile.write('hostname zebra-%s\n' % self.name)
139 configFile.write('password %s\n' % 'quagga')
140 if (self.fpm is not None):
141 configFile.write('fpm connection ip %s port 2620' % self.fpm)
142 configFile.close()
143
144 def config(self, **kwargs):
145 super(QuaggaRouter, self).config(**kwargs)
146
147 self.cmd('%s/zebra -d -f %s -z %s -i %s'
148 % (QuaggaRouter.binDir, self.zebraConfFile, self.socket, self.zebraPidFile))
Andreas Pantelopoulos971c91d2018-02-12 11:28:10 -0800149 print("\n")
150 print('%s/zebra -d -f %s -z %s -i %s'
151 % (QuaggaRouter.binDir, self.zebraConfFile, self.socket, self.zebraPidFile))
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700152
153 for p in self.protocols:
154 p.config(**kwargs)
155
156 if self.defaultRoute:
157 self.cmd('ip route add default via %s' % self.defaultRoute)
158
You Wang0f745de2018-07-27 15:49:22 -0700159 def stopProtocols(self, **kwargs):
160 for p in self.protocols:
161 p.stop(**kwargs)
162
163 def startProtocols(self, **kwargs):
164 for p in self.protocols:
165 p.start(**kwargs)
166
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700167 def terminate(self, **kwargs):
168 self.cmd("ps ax | grep '%s' | awk '{print $1}' | xargs kill"
169 % (self.socket))
170
171 for p in self.protocols:
172 p.terminate(**kwargs)
173
174 super(QuaggaRouter, self).terminate()
175
176class Protocol(object):
177
178 """Base abstraction of a protocol that the QuaggaRouter can run."""
179
180 def setQuaggaRouter(self, qr):
181 self.qr = qr
182
183 def config(self, **kwargs):
184 pass
185
You Wang0f745de2018-07-27 15:49:22 -0700186 def stop(self, **kwargs):
187 pass
188
189 def start(self, **kwargs):
190 pass
191
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700192 def terminate(self, **kwargs):
193 pass
194
195class BgpProtocol(Protocol):
196
197 """Configures and runs the BGP protocol in Quagga."""
198
199 def __init__(self, configFile=None, asNum=None, neighbors=[], routes=[], *args, **kwargs):
200 self.configFile = configFile
201
202 self.asNum = asNum
203 self.neighbors = neighbors
204 self.routes = routes
205
206 def config(self, **kwargs):
207 if self.configFile is None:
208 self.configFile = '%s/bgpd%s.conf' % (self.qr.runDir, self.qr.name)
209 self.generateConfig()
210
211 bgpdPidFile = '%s/bgpd%s.pid' % (self.qr.runDir, self.qr.name)
212
213 self.qr.cmd('%s/bgpd -d -f %s -z %s -i %s'
214 % (QuaggaRouter.binDir, self.configFile, self.qr.socket, bgpdPidFile))
215
You Wang0f745de2018-07-27 15:49:22 -0700216 def stop(self, **kwargs):
217 self.qr.cmd('pkill -f %s' % self.configFile)
218
219 def start(self, **kwargs):
220 bgpdPidFile = '%s/bgpd%s.pid' % (self.qr.runDir, self.qr.name)
221 self.qr.cmd('%s/bgpd -d -f %s -z %s -i %s'
222 % (QuaggaRouter.binDir, self.configFile, self.qr.socket, bgpdPidFile))
223
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700224 def generateConfig(self):
225 conf = ConfigurationWriter(self.configFile)
226
227 def getRouterId(interfaces):
228 intfAttributes = interfaces.itervalues().next()
229 print intfAttributes
230 if isinstance(intfAttributes, list):
231 # Try use the first set of attributes, but if using vlans they might not have addresses
232 intfAttributes = intfAttributes[1] if not intfAttributes[0]['ipAddrs'] else intfAttributes[0]
233 return intfAttributes['ipAddrs'][0].split('/')[0]
234
235 conf.writeLine('log file %s/bgpd%s.log' % (QuaggaRouter.logDir, self.qr.name))
236 conf.writeLine('hostname bgp-%s' % self.qr.name)
237 conf.writeLine('password %s' % 'quagga')
238 conf.writeLine('!')
239 conf.writeLine('router bgp %s' % self.asNum)
240
241 conf.indent()
242
243 conf.writeLine('bgp router-id %s' % getRouterId(self.qr.interfaces))
244 conf.writeLine('timers bgp %s' % '3 9')
245 conf.writeLine('!')
246
247 for neighbor in self.neighbors:
248 conf.writeLine('neighbor %s remote-as %s' % (neighbor['address'], neighbor['as']))
249 conf.writeLine('neighbor %s ebgp-multihop' % neighbor['address'])
250 conf.writeLine('neighbor %s timers connect %s' % (neighbor['address'], '5'))
251 conf.writeLine('neighbor %s advertisement-interval %s' % (neighbor['address'], '5'))
252 if 'port' in neighbor:
253 conf.writeLine('neighbor %s port %s' % (neighbor['address'], neighbor['port']))
254 conf.writeLine('!')
255
256 for route in self.routes:
257 conf.writeLine('network %s' % route)
258
259 conf.close()
260
261class OspfProtocol(Protocol):
262
263 """Configures and runs the OSPF protocol in Quagga."""
264
265 def __init__(self, configFile=None, *args, **kwargs):
266 self.configFile = configFile
267
268 def config(self, **kwargs):
269 if self.configFile is None:
270 self.configFile = '%s/ospfd%s.conf' % (self.qr.runDir, self.qr.name)
271 self.generateConfig()
272
273 ospfPidFile = '%s/ospf%s.pid' % (self.qr.runDir, self.qr.name)
274
275 self.qr.cmd('%s/ospfd -d -f %s -z %s -i %s'
276 % (QuaggaRouter.binDir, self.configFile, self.qr.socket, ospfPidFile))
277
278 def generateConfig(self):
279 conf = ConfigurationWriter(self.configFile)
280
281 def getRouterId(interfaces):
282 intfAttributes = interfaces.itervalues().next()
283 print intfAttributes
284 if isinstance(intfAttributes, list):
285 # Try use the first set of attributes, but if using vlans they might not have addresses
286 intfAttributes = intfAttributes[1] if not intfAttributes[0]['ipAddrs'] else intfAttributes[0]
287 return intfAttributes['ipAddrs'][0].split('/')[0]
288
289 conf.writeLine('hostname ospf-%s' % self.qr.name)
290 conf.writeLine('password %s' % 'hello')
291 conf.writeLine('!')
292 conf.writeLine('router ospf')
293
294 conf.indent()
295
296 conf.writeLine('ospf router-id %s' % getRouterId(self.qr.interfaces))
297 conf.writeLine('!')
298
299 for name, intf in self.qr.interfaces.items():
300 for ip in intf['ipAddrs']:
301 conf.writeLine('network %s area 0' % ip)
302 # if intf['ipAddrs'][0].startswith('192.168'):
303 # writeLine(1, 'passive-interface %s' % name)
304
305 conf.close()
306
307class PimProtocol(Protocol):
308
309 """Configures and runs the PIM protcol in Quagga."""
310
311 def __init__(self, configFile=None, *args, **kwargs):
312 self.configFile = configFile
313
314 def config(self, **kwargs):
315 pimPidFile = '%s/pim%s.pid' % (self.qr.runDir, self.qr.name)
316
317 self.qr.cmd('%s/pimd -Z -d -f %s -z %s -i %s'
318 % (QuaggaRouter.binDir, self.configFile, self.qr.socket, pimPidFile))
319
320class ConfigurationWriter(object):
321
322 """Utility class for writing a configuration file."""
323
324 def __init__(self, filename):
325 self.filename = filename
326 self.indentValue = 0
327
328 self.configFile = open(self.filename, 'w+')
329
330 def indent(self):
331 self.indentValue += 1
332
333 def unindent(self):
334 if (self.indentValue > 0):
335 self.indentValue -= 1
336
337 def write(self, string):
338 self.configFile.write(string)
339
340 def writeLine(self, string):
341 intentStr = ''
342 for _ in range(0, self.indentValue):
343 intentStr += ' '
344 self.write('%s%s\n' % (intentStr, string))
345
346 def close(self):
347 self.configFile.close()
348
349# Backward compatibility for BGP-only use case
350class BgpRouter(QuaggaRouter):
351
352 """Quagga router running the BGP protocol."""
353
354 def __init__(self, name, interfaces,
355 asNum=0, neighbors=[], routes=[],
356 defaultRoute=None,
357 quaggaConfFile=None,
358 zebraConfFile=None,
359 *args, **kwargs):
360 bgp = BgpProtocol(configFile=quaggaConfFile, asNum=asNum, neighbors=neighbors, routes=routes)
361
362 super(BgpRouter, self).__init__(name, interfaces,
363 zebraConfFile=zebraConfFile,
364 defaultRoute=defaultRoute,
365 protocols=[bgp],
366 *args, **kwargs)
367
368class RouterData(object):
369
370 """Internal data structure storing information about a router."""
371
372 def __init__(self, index):
373 self.index = index
374 self.neighbors = []
375 self.interfaces = {}
376 self.switches = []
377
378 def addNeighbor(self, theirAddress, theirAsNum):
379 self.neighbors.append({'address': theirAddress.ip, 'as': theirAsNum})
380
381 def addInterface(self, intf, vlan, address):
382 if intf not in self.interfaces:
383 self.interfaces[intf] = InterfaceData(intf)
384
385 self.interfaces[intf].addAddress(vlan, address)
386
387 def setSwitch(self, switch):
388 self.switches.append(switch)
389
390class InterfaceData(object):
391
392 """Internal data structure storing information about an interface."""
393
394 def __init__(self, number):
395 self.number = number
396 self.addressesByVlan = {}
397
398 def addAddress(self, vlan, address):
399 if vlan not in self.addressesByVlan:
400 self.addressesByVlan[vlan] = []
401
402 self.addressesByVlan[vlan].append(address.with_prefixlen)
403
404class RoutedNetwork(object):
405
406 """Creates a host behind a router. This is common boilerplate topology
407 segment in routed networks."""
408
409 @staticmethod
410 def build(topology, router, hostName, networks):
411 # There's a convention that the router's addresses are already set up,
412 # and it has the last address in the network.
413
414 def getFirstAddress(network):
415 return '%s/%s' % (network[1], network.prefixlen)
416
417 defaultRoute = AutonomousSystem.getLastAddress(networks[0]).ip
418
419 host = topology.addHost(hostName, cls=RoutedHost,
420 ips=[getFirstAddress(network) for network in networks],
421 gateway=defaultRoute)
422
423 topology.addLink(router, host)
424
425class AutonomousSystem(object):
426
427 """Base abstraction of an autonomous system, which implies some internal
428 topology and connections to other topology elements (switches/other ASes)."""
429
430 psIdx = 1
431
432 def __init__(self, asNum, numRouters):
433 self.asNum = asNum
434 self.numRouters = numRouters
435 self.routers = {}
436 for i in range(1, numRouters + 1):
437 self.routers[i] = RouterData(i)
438
439 self.routerNodes = {}
440
441 self.neighbors = []
442 self.vlanAddresses = {}
443
444 def peerWith(self, myRouter, myAddress, theirAddress, theirAsNum, intf=1, vlan=None):
445 router = self.routers[myRouter]
446
447 router.addInterface(intf, vlan, myAddress)
448 router.addNeighbor(theirAddress, theirAsNum)
449
450 def getRouter(self, i):
451 return self.routerNodes[i]
452
453 @staticmethod
454 def generatePeeringAddresses():
455 network = ip_network(u'10.0.%s.0/24' % AutonomousSystem.psIdx)
456 AutonomousSystem.psIdx += 1
457
458 return ip_interface('%s/%s' % (network[1], network.prefixlen)), \
459 ip_interface('%s/%s' % (network[2], network.prefixlen))
460
461 @staticmethod
462 def addPeering(as1, as2, router1=1, router2=1, intf1=1, intf2=1, address1=None, address2=None, useVlans=False):
463 vlan = AutonomousSystem.psIdx if useVlans else None
464
465 if address1 is None or address2 is None:
466 (address1, address2) = AutonomousSystem.generatePeeringAddresses()
467
468 as1.peerWith(router1, address1, address2, as2.asNum, intf=intf1, vlan=vlan)
469 as2.peerWith(router2, address2, address1, as1.asNum, intf=intf2, vlan=vlan)
470
471 @staticmethod
472 def getLastAddress(network):
473 return ip_interface(network.network_address + network.num_addresses - 2)
474
475 @staticmethod
476 def getIthAddress(network, i):
477 return ip_interface('%s/%s' % (network[i], network.prefixlen))
478
479class BasicAutonomousSystem(AutonomousSystem):
480
481 """Basic autonomous system containing one host and one or more routers
482 which peer with other ASes."""
483
484 def __init__(self, num, routes, numRouters=1):
485 super(BasicAutonomousSystem, self).__init__(65000+num, numRouters)
486 self.num = num
487 self.routes = routes
488
489 def addLink(self, switch, router=1):
490 self.routers[router].setSwitch(switch)
491
492 def build(self, topology):
493 self.addRouterAndHost(topology)
494
495 def addRouterAndHost(self, topology):
496
497 # TODO implementation is messy and needs to be cleaned up
498
499 intfs = {}
500
501 router = self.routers[1]
502 for i, router in self.routers.items():
503
504 # routerName = 'r%i%i' % (self.num, i)
505 routerName = 'r%i' % self.num
506 if not i == 1:
507 routerName += ('%i' % i)
508
509 hostName = 'h%i' % self.num
510
511 for j, interface in router.interfaces.items():
512 nativeAddresses = interface.addressesByVlan.pop(None, [])
513 peeringIntf = [{'mac' : '00:00:%02x:00:%02x:%02x' % (self.num, i, j),
514 'ipAddrs' : nativeAddresses}]
515
516 for vlan, addresses in interface.addressesByVlan.items():
517 peeringIntf.append({'vlan': vlan,
518 'mac': '00:00:%02x:%02x:%02x:%02x' % (self.num, vlan, i, j),
519 'ipAddrs': addresses})
520
521 intfs.update({'%s-eth%s' % (routerName, j-1) : peeringIntf})
522
523 # Only add the host to the first router for now
524 if i == 1:
525 internalAddresses = []
526 for route in self.routes:
527 internalAddresses.append('%s/%s' % (AutonomousSystem.getLastAddress(route).ip, route.prefixlen))
528
529 internalIntf = {'ipAddrs' : internalAddresses}
530
531 # This is the configuration of the next interface after all the peering interfaces
532 intfs.update({'%s-eth%s' % (routerName, len(router.interfaces.keys())) : internalIntf})
533
534 routerNode = topology.addHost(routerName,
535 asNum=self.asNum, neighbors=router.neighbors,
536 routes=self.routes,
537 cls=BgpRouter, interfaces=intfs)
538
539 self.routerNodes[i] = routerNode
540
541 for switch in router.switches:
542 topology.addLink(switch, routerNode)
543
544 # Only add the host to the first router for now
545 if i == 1:
546 defaultRoute = internalAddresses[0].split('/')[0]
547
548 host = topology.addHost(hostName, cls=RoutedHost,
549 ips=[self.getFirstAddress(route) for route in self.routes],
550 gateway=defaultRoute)
551
552 topology.addLink(routerNode, host)
553
554 # def getLastAddress(self, network):
555 # return ip_address(network.network_address + network.num_addresses - 2)
556
557 def getFirstAddress(self, network):
558 return '%s/%s' % (network[1], network.prefixlen)
559
560# TODO fix this AS - doesn't currently work
561class RouteServerAutonomousSystem(BasicAutonomousSystem):
562
563 def __init__(self, routerAddress, *args, **kwargs):
564 BasicAutonomousSystem.__init__(self, *args, **kwargs)
565
566 self.routerAddress = routerAddress
567
568 def build(self, topology, connectAtSwitch):
569
570 switch = topology.addSwitch('as%isw' % self.num, cls=OVSBridge)
571
572 self.addRouterAndHost(topology, self.routerAddress, switch)
573
574 rsName = 'rs%i' % self.num
575 routeServer = topology.addHost(rsName,
576 self.asnum, self.neighbors,
577 cls=BgpRouter,
578 interfaces={'%s-eth0' % rsName : {'ipAddrs': [self.peeringAddress]}})
579
580 topology.addLink(routeServer, switch)
581 topology.addLink(switch, connectAtSwitch)
582
583class SdnAutonomousSystem(AutonomousSystem):
584
585 """Runs the internal BGP speakers needed for ONOS routing apps like
586 SDN-IP."""
587
588 routerIdx = 1
589
590 def __init__(self, onosIps, num=1, numBgpSpeakers=1, asNum=65000, externalOnos=True,
591 peerIntfConfig=None, withFpm=False):
592 super(SdnAutonomousSystem, self).__init__(asNum, numBgpSpeakers)
593 self.onosIps = onosIps
594 self.num = num
595 self.numBgpSpeakers = numBgpSpeakers
596 self.peerIntfConfig = peerIntfConfig
597 self.withFpm = withFpm
598 self.externalOnos = externalOnos
599 self.internalPeeringSubnet = ip_network(u'1.1.1.0/24')
600
601 for router in self.routers.values():
602 # Add iBGP sessions to ONOS nodes
603 for onosIp in onosIps:
604 router.neighbors.append({'address': onosIp, 'as': asNum, 'port': 2000})
605
606 # Add iBGP sessions to other BGP speakers
607 for i, router2 in self.routers.items():
608 if router == router2:
609 continue
610 cpIpBase = self.num*10
611 ip = AutonomousSystem.getIthAddress(self.internalPeeringSubnet, cpIpBase+i)
612 router.neighbors.append({'address': ip.ip, 'as': asNum})
613
614 def build(self, topology, connectAtSwitch, controlSwitch):
615
616 natIp = AutonomousSystem.getLastAddress(self.internalPeeringSubnet)
617
618 for i, router in self.routers.items():
619 num = SdnAutonomousSystem.routerIdx
620 SdnAutonomousSystem.routerIdx += 1
621 name = 'bgp%s' % num
622
623 cpIpBase = self.num*10
624 ip = AutonomousSystem.getIthAddress(self.internalPeeringSubnet, cpIpBase+i)
625
626 eth0 = { 'ipAddrs' : [ str(ip) ] }
627 if self.peerIntfConfig is not None:
628 eth1 = self.peerIntfConfig
629 else:
630 nativeAddresses = router.interfaces[1].addressesByVlan.pop(None, [])
631 eth1 = [{ 'mac': '00:00:00:00:00:%02x' % num,
632 'ipAddrs' : nativeAddresses }]
633
634 for vlan, addresses in router.interfaces[1].addressesByVlan.items():
635 eth1.append({'vlan': vlan,
636 'mac': '00:00:00:%02x:%02x:00' % (num, vlan),
637 'ipAddrs': addresses})
638
639 intfs = { '%s-eth0' % name : eth0,
640 '%s-eth1' % name : eth1 }
641
642 bgp = topology.addHost( name, cls=BgpRouter, asNum=self.asNum,
643 neighbors=router.neighbors,
644 interfaces=intfs,
645 defaultRoute=str(natIp.ip),
646 fpm=self.onosIps[0] if self.withFpm else None )
647
648 topology.addLink( bgp, controlSwitch )
649 topology.addLink( bgp, connectAtSwitch )
650
651 if self.externalOnos:
652 nat = topology.addHost('nat', cls=NAT,
653 ip='%s/%s' % (natIp.ip, self.internalPeeringSubnet.prefixlen),
654 subnet=str(self.internalPeeringSubnet), inNamespace=False)
655 topology.addLink(controlSwitch, nat)
656
657def generateRoutes(baseRange, numRoutes, subnetSize=None):
658 baseNetwork = ip_network(baseRange)
659
660 # We need to get at least 2 addresses out of each subnet, so the biggest
661 # prefix length we can have is /30
662 maxPrefixLength = baseNetwork.max_prefixlen - 2
663
664 if subnetSize is not None:
665 return list(baseNetwork.subnets(new_prefix=subnetSize))
666
667 trySubnetSize = baseNetwork.prefixlen + 1
668 while trySubnetSize <= maxPrefixLength and \
669 len(list(baseNetwork.subnets(new_prefix=trySubnetSize))) < numRoutes:
670 trySubnetSize += 1
671
672 if trySubnetSize > maxPrefixLength:
673 raise Exception("Can't get enough routes from input parameters")
674
675 return list(baseNetwork.subnets(new_prefix=trySubnetSize))[:numRoutes]
676
677class RoutingCli( CLI ):
678
679 """CLI command that can bring a host up or down. Useful for simulating router failure."""
680
681 def do_host( self, line ):
682 args = line.split()
683 if len(args) != 2:
684 error( 'invalid number of args: host <host name> {up, down}\n' )
685 return
686
687 host = args[ 0 ]
688 command = args[ 1 ]
689 if host not in self.mn or self.mn.get( host ) not in self.mn.hosts:
690 error( 'invalid host: %s\n' % args[ 1 ] )
691 else:
692 if command == 'up':
693 op = 'up'
694 elif command == 'down':
695 op = 'down'
696 else:
697 error( 'invalid command: host <host name> {up, down}\n' )
698 return
699
700 for intf in self.mn.get( host ).intfList( ):
701 intf.link.intf1.ifconfig( op )
702 intf.link.intf2.ifconfig( op )