mirror of
				https://github.com/opennetworkinglab/onos.git
				synced 2025-10-30 15:52:08 +01:00 
			
		
		
		
	Updated to latest routinglib version. Change-Id: I939f5562cc88c30671f8f2f4b074699bd5367a79
		
			
				
	
	
		
			655 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			655 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/python
 | |
| 
 | |
| """
 | |
| Libraries for creating L3 topologies with routing protocols.
 | |
| """
 | |
| 
 | |
| from mininet.node import Host, OVSBridge
 | |
| from mininet.nodelib import NAT
 | |
| from mininet.log import info, debug, error
 | |
| from mininet.cli import CLI
 | |
| from ipaddress import ip_network, ip_address, ip_interface
 | |
| import os
 | |
| 
 | |
| class RoutedHost(Host):
 | |
|     """Host that can be configured with multiple IP addresses."""
 | |
|     def __init__(self, name, ips, gateway, *args, **kwargs):
 | |
|         super(RoutedHost, self).__init__(name, *args, **kwargs)
 | |
| 
 | |
|         self.ips = ips
 | |
|         self.gateway = gateway
 | |
| 
 | |
|     def config(self, **kwargs):
 | |
|         Host.config(self, **kwargs)
 | |
| 
 | |
|         self.cmd('ip addr flush dev %s' % self.defaultIntf())
 | |
|         for ip in self.ips:
 | |
|             self.cmd('ip addr add %s dev %s' % (ip, self.defaultIntf()))
 | |
| 
 | |
|         self.cmd('ip route add default via %s' % self.gateway)
 | |
| 
 | |
| class Router(Host):
 | |
|     
 | |
|     """An L3 router.
 | |
|     Configures the Linux kernel for L3 forwarding and supports rich interface
 | |
|     configuration of IP addresses, MAC addresses and VLANs."""
 | |
|     
 | |
|     def __init__(self, name, interfaces, *args, **kwargs):
 | |
|         super(Router, self).__init__(name, **kwargs)
 | |
| 
 | |
|         self.interfaces = interfaces
 | |
|         
 | |
|     def config(self, **kwargs):
 | |
|         super(Host, self).config(**kwargs)
 | |
|         
 | |
|         self.cmd('sysctl net.ipv4.ip_forward=1')
 | |
|         self.cmd('sysctl net.ipv4.conf.all.rp_filter=0')
 | |
| 
 | |
|         for intf, configs in self.interfaces.items():
 | |
|             self.cmd('ip addr flush dev %s' % intf)
 | |
|             self.cmd( 'sysctl net.ipv4.conf.%s.rp_filter=0' % intf )
 | |
|             
 | |
|             if not isinstance(configs, list):
 | |
|                 configs = [configs]
 | |
|                 
 | |
|             for attrs in configs:
 | |
|                 # Configure the vlan if there is one    
 | |
|                 if 'vlan' in attrs:
 | |
|                     vlanName = '%s.%s' % (intf, attrs['vlan'])
 | |
|                     self.cmd('ip link add link %s name %s type vlan id %s' % 
 | |
|                              (intf, vlanName, attrs['vlan']))
 | |
|                     self.cmd('ip link set %s up' % vlanName)
 | |
|                     addrIntf = vlanName
 | |
|                 else:
 | |
|                     addrIntf = intf
 | |
|                     
 | |
|                 # Now configure the addresses on the vlan/native interface
 | |
|                 if 'mac' in attrs:
 | |
|                     self.cmd('ip link set %s down' % addrIntf)
 | |
|                     self.cmd('ip link set %s address %s' % (addrIntf, attrs['mac']))
 | |
|                     self.cmd('ip link set %s up' % addrIntf)
 | |
|                 for addr in attrs['ipAddrs']:
 | |
|                     self.cmd('ip addr add %s dev %s' % (addr, addrIntf))
 | |
| 
 | |
| class QuaggaRouter(Router):
 | |
|     
 | |
|     """Runs Quagga to create a router that can speak routing protocols."""
 | |
|     
 | |
|     binDir = '/usr/lib/quagga'
 | |
|     logDir = '/var/log/quagga'
 | |
|     
 | |
|     def __init__(self, name, interfaces,
 | |
|                  defaultRoute=None,
 | |
|                  zebraConfFile=None,
 | |
|                  protocols=[],
 | |
|                  fpm=None,
 | |
|                  runDir='/var/run/quagga', *args, **kwargs):
 | |
|         super(QuaggaRouter, self).__init__(name, interfaces, **kwargs)
 | |
|         
 | |
|         self.protocols = protocols
 | |
|         self.fpm = fpm
 | |
|         
 | |
|         for p in self.protocols:
 | |
|             p.setQuaggaRouter(self)
 | |
|         
 | |
|         self.runDir = runDir
 | |
|         self.defaultRoute = defaultRoute
 | |
|         
 | |
|         # Ensure required directories exist
 | |
|         try:
 | |
|             original_umask = os.umask(0)
 | |
|             if (not os.path.isdir(QuaggaRouter.logDir)):
 | |
|                 os.makedirs(QuaggaRouter.logDir, 0777)
 | |
|             if (not os.path.isdir(self.runDir)):
 | |
|                 os.makedirs(self.runDir, 0777)
 | |
|         finally:
 | |
|             os.umask(original_umask)
 | |
| 
 | |
|         self.zebraConfFile = zebraConfFile
 | |
|         if (self.zebraConfFile is None):
 | |
|             self.zebraConfFile = '%s/zebrad%s.conf' % (self.runDir, self.name)
 | |
|             self.generateZebra()
 | |
|             
 | |
|         self.socket = '%s/zebra%s.api' % (self.runDir, self.name)
 | |
|         
 | |
|         self.zebraPidFile = '%s/zebra%s.pid' % (self.runDir, self.name)
 | |
| 
 | |
|     def generateZebra(self):
 | |
|         configFile = open(self.zebraConfFile, 'w+')
 | |
|         configFile.write('log file %s/zebrad%s.log\n' % (QuaggaRouter.logDir, self.name))
 | |
|         configFile.write('hostname zebra-%s\n' % self.name)
 | |
|         configFile.write('password %s\n' % 'hello')
 | |
|         if (self.fpm is not None):
 | |
|             configFile.write('fpm connection ip %s port 2620' % self.fpm)
 | |
|         configFile.close()
 | |
| 
 | |
|     def config(self, **kwargs):
 | |
|         super(QuaggaRouter, self).config(**kwargs)
 | |
| 
 | |
|         self.cmd('%s/zebra -d -f %s -z %s -i %s'
 | |
|                  % (QuaggaRouter.binDir, self.zebraConfFile, self.socket, self.zebraPidFile))
 | |
|         
 | |
|         for p in self.protocols:
 | |
|             p.config(**kwargs)
 | |
|         
 | |
|         if self.defaultRoute:
 | |
|             self.cmd('ip route add default via %s' % self.defaultRoute)
 | |
|         
 | |
|     def terminate(self, **kwargs):
 | |
|         self.cmd("ps ax | grep '%s' | awk '{print $1}' | xargs kill" 
 | |
|                  % (self.socket))
 | |
|         
 | |
|         for p in self.protocols:
 | |
|             p.terminate(**kwargs)
 | |
| 
 | |
|         super(QuaggaRouter, self).terminate()
 | |
|         
 | |
| class Protocol(object):
 | |
|     
 | |
|     """Base abstraction of a protocol that the QuaggaRouter can run."""
 | |
|         
 | |
|     def setQuaggaRouter(self, qr):
 | |
|         self.qr = qr
 | |
|         
 | |
|     def config(self, **kwargs):
 | |
|         pass
 | |
|     
 | |
|     def terminate(self, **kwargs):
 | |
|         pass
 | |
|         
 | |
| class BgpProtocol(Protocol):
 | |
|     
 | |
|     """Configures and runs the BGP protocol in Quagga."""
 | |
|     
 | |
|     def __init__(self, configFile=None, asNum=None, neighbors=[], routes=[], *args, **kwargs):
 | |
|         self.configFile = configFile
 | |
|         
 | |
|         self.asNum = asNum
 | |
|         self.neighbors = neighbors
 | |
|         self.routes = routes
 | |
|             
 | |
|     def config(self, **kwargs):
 | |
|         if self.configFile is None:
 | |
|             self.configFile = '%s/bgpd%s.conf' % (self.qr.runDir, self.qr.name)
 | |
|             self.generateConfig()
 | |
|         
 | |
|         bgpdPidFile = '%s/bgpd%s.pid' % (self.qr.runDir, self.qr.name)
 | |
|         
 | |
|         self.qr.cmd('%s/bgpd -d -f %s -z %s -i %s'
 | |
|                      % (QuaggaRouter.binDir, self.configFile, self.qr.socket, bgpdPidFile))
 | |
|         
 | |
|     def generateConfig(self):
 | |
|         conf = ConfigurationWriter(self.configFile)
 | |
|                     
 | |
|         def getRouterId(interfaces):
 | |
|             intfAttributes = interfaces.itervalues().next()
 | |
|             print intfAttributes
 | |
|             if isinstance(intfAttributes, list):
 | |
|                 # Try use the first set of attributes, but if using vlans they might not have addresses
 | |
|                 intfAttributes = intfAttributes[1] if not intfAttributes[0]['ipAddrs'] else intfAttributes[0]
 | |
|             return intfAttributes['ipAddrs'][0].split('/')[0]
 | |
|         
 | |
|         conf.writeLine('log file %s/bgpd%s.log' % (QuaggaRouter.logDir, self.qr.name))
 | |
|         conf.writeLine('hostname bgp-%s' % self.qr.name);
 | |
|         conf.writeLine('password %s' % 'sdnip')
 | |
|         conf.writeLine('!')
 | |
|         conf.writeLine('router bgp %s' % self.asNum)
 | |
|         
 | |
|         conf.indent()
 | |
|         
 | |
|         conf.writeLine('bgp router-id %s' % getRouterId(self.qr.interfaces))
 | |
|         conf.writeLine('timers bgp %s' % '3 9')
 | |
|         conf.writeLine('!')
 | |
|         
 | |
|         for neighbor in self.neighbors:
 | |
|             conf.writeLine('neighbor %s remote-as %s' % (neighbor['address'], neighbor['as']))
 | |
|             conf.writeLine('neighbor %s ebgp-multihop' % neighbor['address'])
 | |
|             conf.writeLine('neighbor %s timers connect %s' % (neighbor['address'], '5'))
 | |
|             conf.writeLine('neighbor %s advertisement-interval %s' % (neighbor['address'], '5'))
 | |
|             if 'port' in neighbor:
 | |
|                 conf.writeLine('neighbor %s port %s' % (neighbor['address'], neighbor['port']))
 | |
|             conf.writeLine('!')
 | |
|             
 | |
|         for route in self.routes:
 | |
|             conf.writeLine('network %s' % route)
 | |
|         
 | |
|         conf.close()
 | |
|     
 | |
| class OspfProtocol(Protocol):
 | |
|     
 | |
|     """Configures and runs the OSPF protocol in Quagga."""
 | |
|     
 | |
|     def __init__(self, configFile=None, *args, **kwargs):
 | |
|         self.configFile = configFile
 | |
|             
 | |
|     def config(self, **kwargs):
 | |
|         if self.configFile is None:
 | |
|             self.configFile = '%s/ospfd%s.conf' % (self.qr.runDir, self.qr.name)
 | |
|             self.generateConfig()
 | |
|         
 | |
|         ospfPidFile = '%s/ospf%s.pid' % (self.qr.runDir, self.qr.name)
 | |
|         
 | |
|         self.qr.cmd('%s/ospfd -d -f %s -z %s -i %s'
 | |
|                      % (QuaggaRouter.binDir, self.configFile, self.qr.socket, ospfPidFile))
 | |
|         
 | |
|     def generateConfig(self):
 | |
|         conf = ConfigurationWriter(self.configFile)
 | |
|             
 | |
|         def getRouterId(interfaces):
 | |
|             intfAttributes = interfaces.itervalues().next()
 | |
|             print intfAttributes
 | |
|             if isinstance(intfAttributes, list):
 | |
|                 # Try use the first set of attributes, but if using vlans they might not have addresses
 | |
|                 intfAttributes = intfAttributes[1] if not intfAttributes[0]['ipAddrs'] else intfAttributes[0]
 | |
|             return intfAttributes['ipAddrs'][0].split('/')[0]
 | |
|         
 | |
|         conf.writeLine('hostname ospf-%s' % self.qr.name);
 | |
|         conf.writeLine('password %s' % 'hello')
 | |
|         conf.writeLine('!')
 | |
|         conf.writeLine('router ospf')
 | |
|         
 | |
|         conf.indent()
 | |
|         
 | |
|         conf.writeLine('ospf router-id %s' % getRouterId(self.qr.interfaces))
 | |
|         conf.writeLine('!')
 | |
|         
 | |
|         for name, intf in self.qr.interfaces.items():
 | |
|             for ip in intf['ipAddrs']:
 | |
|                 conf.writeLine('network %s area 0' % ip)
 | |
|             #if intf['ipAddrs'][0].startswith('192.168'):
 | |
|             #    writeLine(1, 'passive-interface %s' % name)
 | |
|             
 | |
|         conf.close()
 | |
|         
 | |
| class PimProtocol(Protocol):
 | |
|     
 | |
|     """Configures and runs the PIM protcol in Quagga."""
 | |
|     
 | |
|     def __init__(self, configFile=None, *args, **kwargs):
 | |
|         self.configFile = configFile
 | |
|         
 | |
|     def config(self, **kwargs):
 | |
|         pimPidFile = '%s/pim%s.pid' % (self.qr.runDir, self.qr.name)
 | |
|                 
 | |
|         self.qr.cmd('%s/pimd -Z -d -f %s -z %s -i %s'
 | |
|                      % (QuaggaRouter.binDir, self.configFile, self.qr.socket, pimPidFile))
 | |
|         
 | |
| class ConfigurationWriter(object):
 | |
|     
 | |
|     """Utility class for writing a configuration file."""
 | |
|     
 | |
|     def __init__(self, filename):
 | |
|         self.filename = filename
 | |
|         self.indentValue = 0;
 | |
|         
 | |
|         self.configFile = open(self.filename, 'w+')
 | |
|     
 | |
|     def indent(self):
 | |
|         self.indentValue += 1
 | |
|         
 | |
|     def unindent(self):
 | |
|         if (self.indentValue > 0):
 | |
|             self.indentValue -= 1
 | |
|             
 | |
|     def write(self, string):
 | |
|         self.configFile.write(string)
 | |
|     
 | |
|     def writeLine(self, string):
 | |
|         intentStr = ''
 | |
|         for _ in range(0, self.indentValue):
 | |
|             intentStr += '  '
 | |
|         self.write('%s%s\n' % (intentStr, string))
 | |
|         
 | |
|     def close(self):
 | |
|         self.configFile.close()
 | |
| 
 | |
| #Backward compatibility for BGP-only use case
 | |
| class BgpRouter(QuaggaRouter):
 | |
|     
 | |
|     """Quagga router running the BGP protocol."""
 | |
|     
 | |
|     def __init__(self, name, interfaces,
 | |
|                  asNum, neighbors, routes=[],
 | |
|                  defaultRoute=None,
 | |
|                  quaggaConfFile=None,
 | |
|                  zebraConfFile=None,
 | |
|                  *args, **kwargs):
 | |
|         bgp = BgpProtocol(configFile=quaggaConfFile, asNum=asNum, neighbors=neighbors, routes=routes)
 | |
|         
 | |
|         super(BgpRouter, self).__init__(name, interfaces, 
 | |
|                                         zebraConfFile=zebraConfFile,
 | |
|                                         defaultRoute=defaultRoute,
 | |
|                                         protocols=[bgp],
 | |
|                                         *args, **kwargs)
 | |
|         
 | |
| class RouterData(object):
 | |
|     
 | |
|     """Internal data structure storing information about a router."""
 | |
|     
 | |
|     def __init__(self, index):
 | |
|         self.index = index;
 | |
|         self.neighbors = []
 | |
|         self.interfaces = {}
 | |
|         self.switches = []
 | |
|         
 | |
|     def addNeighbor(self, theirAddress, theirAsNum):
 | |
|         self.neighbors.append({'address':theirAddress.ip, 'as':theirAsNum})
 | |
|     
 | |
|     def addInterface(self, intf, vlan, address):
 | |
|         if not intf in self.interfaces:
 | |
|             self.interfaces[intf] = InterfaceData(intf)
 | |
|             
 | |
|         self.interfaces[intf].addAddress(vlan, address)
 | |
|         
 | |
|     def setSwitch(self, switch):
 | |
|         self.switches.append(switch)
 | |
|         
 | |
| class InterfaceData(object):
 | |
|     
 | |
|     """Internal data structure storing information about an interface."""
 | |
|     
 | |
|     def __init__(self, number):
 | |
|         self.number = number
 | |
|         self.addressesByVlan = {}
 | |
|         
 | |
|     def addAddress(self, vlan, address):
 | |
|         if not vlan in self.addressesByVlan:
 | |
|             self.addressesByVlan[vlan] = []
 | |
|             
 | |
|         self.addressesByVlan[vlan].append(address.with_prefixlen)
 | |
|         
 | |
| class RoutedNetwork(object):
 | |
|     
 | |
|     """Creates a host behind a router. This is common boilerplate topology
 | |
|     segment in routed networks."""
 | |
|     
 | |
|     @staticmethod
 | |
|     def build(topology, router, hostName, networks):
 | |
|         # There's a convention that the router's addresses are already set up,
 | |
|         # and it has the last address in the network.
 | |
|         
 | |
|         def getFirstAddress(network):
 | |
|             return '%s/%s' % (network[1], network.prefixlen)
 | |
|         
 | |
|         defaultRoute = AutonomousSystem.getLastAddress(networks[0]).ip
 | |
|         
 | |
|         host = topology.addHost(hostName, cls=RoutedHost,
 | |
|                                 ips=[getFirstAddress(network) for network in networks],
 | |
|                                 gateway=defaultRoute)
 | |
| 
 | |
|         topology.addLink(router, host)
 | |
| 
 | |
| class AutonomousSystem(object):
 | |
|     
 | |
|     """Base abstraction of an autonomous system, which implies some internal
 | |
|     topology and connections to other topology elements (switches/other ASes)."""
 | |
|     
 | |
|     psIdx = 1
 | |
|     
 | |
|     def __init__(self, asNum, numRouters):
 | |
|         self.asNum = asNum
 | |
|         self.numRouters = numRouters
 | |
|         self.routers = {}
 | |
|         for i in range(1, numRouters + 1):
 | |
|             self.routers[i] = RouterData(i)
 | |
|             
 | |
|         self.routerNodes={}
 | |
|             
 | |
|         self.neighbors=[]
 | |
|         self.vlanAddresses={}
 | |
|         
 | |
|     def peerWith(self, myRouter, myAddress, theirAddress, theirAsNum, intf=1, vlan=None):
 | |
|         router = self.routers[myRouter]
 | |
|         
 | |
|         router.addInterface(intf, vlan, myAddress)
 | |
|         router.addNeighbor(theirAddress, theirAsNum)
 | |
| 
 | |
|     def getRouter(self, i):
 | |
|         return self.routerNodes[i]
 | |
| 
 | |
|     @staticmethod
 | |
|     def generatePeeringAddresses():
 | |
|         network = ip_network(u'10.0.%s.0/24' % AutonomousSystem.psIdx)
 | |
|         AutonomousSystem.psIdx += 1
 | |
|         
 | |
|         return ip_interface('%s/%s' % (network[1], network.prefixlen)), \
 | |
|             ip_interface('%s/%s' % (network[2], network.prefixlen))
 | |
|         
 | |
|     @staticmethod
 | |
|     def addPeering(as1, as2, router1=1, router2=1, intf1=1, intf2=1, address1=None, address2=None, useVlans=False):
 | |
|         vlan = AutonomousSystem.psIdx if useVlans else None
 | |
|         
 | |
|         if address1 is None or address2 is None:
 | |
|             (address1, address2) = AutonomousSystem.generatePeeringAddresses()
 | |
|             
 | |
|         as1.peerWith(router1, address1, address2, as2.asNum, intf=intf1, vlan=vlan)
 | |
|         as2.peerWith(router2, address2, address1, as1.asNum, intf=intf2, vlan=vlan)
 | |
|     
 | |
|     @staticmethod
 | |
|     def getLastAddress(network):
 | |
|         return ip_interface(network.network_address + network.num_addresses - 2)
 | |
|     
 | |
|     @staticmethod
 | |
|     def getIthAddress(network, i):
 | |
|         return ip_interface('%s/%s' % (network[i], network.prefixlen))
 | |
| 
 | |
| class BasicAutonomousSystem(AutonomousSystem):
 | |
| 
 | |
|     """Basic autonomous system containing one host and one or more routers
 | |
|     which peer with other ASes."""
 | |
| 
 | |
|     def __init__(self, num, routes, numRouters=1):
 | |
|         super(BasicAutonomousSystem, self).__init__(65000+num, numRouters)
 | |
|         self.num = num
 | |
|         self.routes = routes
 | |
|         
 | |
|     def addLink(self, switch, router=1):
 | |
|         self.routers[router].setSwitch(switch)
 | |
| 
 | |
|     def build(self, topology):
 | |
|         self.addRouterAndHost(topology)
 | |
| 
 | |
|     def addRouterAndHost(self, topology):
 | |
|         
 | |
|         # TODO implementation is messy and needs to be cleaned up
 | |
|         
 | |
|         intfs = {}
 | |
|         
 | |
|         router = self.routers[1]
 | |
|         for i, router in self.routers.items():
 | |
|         
 | |
|             #routerName = 'r%i%i' % (self.num, i)
 | |
|             routerName = 'r%i' % self.num
 | |
|             if not i==1:
 | |
|                 routerName += ('%i' % i)
 | |
|                 
 | |
|             hostName = 'h%i' % self.num
 | |
|         
 | |
|             for j, interface in router.interfaces.items():
 | |
|                 nativeAddresses = interface.addressesByVlan.pop(None, [])
 | |
|                 peeringIntf = [{'mac' : '00:00:%02x:00:%02x:%02x' % (self.num, i, j),
 | |
|                                'ipAddrs' : nativeAddresses}]
 | |
|                 
 | |
|                 for vlan, addresses in interface.addressesByVlan.items():
 | |
|                     peeringIntf.append({'vlan':vlan,
 | |
|                                         'mac':'00:00:%02x:%02x:%02x:%02x' % (self.num, vlan, i, j),
 | |
|                                         'ipAddrs':addresses})
 | |
|                     
 | |
|                 intfs.update({'%s-eth%s' % (routerName, j-1) : peeringIntf})
 | |
|             
 | |
|             # Only add the host to the first router for now
 | |
|             if i==1:
 | |
|                 internalAddresses=[]
 | |
|                 for route in self.routes:
 | |
|                     internalAddresses.append('%s/%s' % (AutonomousSystem.getLastAddress(route).ip, route.prefixlen))
 | |
|         
 | |
|                 internalIntf = {'ipAddrs' : internalAddresses}
 | |
|         
 | |
|                 # This is the configuration of the next interface after all the peering interfaces
 | |
|                 intfs.update({'%s-eth%s' % (routerName, len(router.interfaces.keys())) : internalIntf})
 | |
|     
 | |
|             routerNode = topology.addHost(routerName,  
 | |
|                                   asNum=self.asNum, neighbors=router.neighbors,
 | |
|                                   routes=self.routes,
 | |
|                                   cls=BgpRouter, interfaces=intfs)
 | |
|             
 | |
|             self.routerNodes[i] = routerNode
 | |
| 
 | |
|             for switch in router.switches:
 | |
|                 topology.addLink(switch, routerNode)
 | |
| 
 | |
|             # Only add the host to the first router for now
 | |
|             if i==1:
 | |
|                 defaultRoute = internalAddresses[0].split('/')[0]
 | |
|         
 | |
|                 host = topology.addHost(hostName, cls=RoutedHost,
 | |
|                                         ips=[self.getFirstAddress(route) for route in self.routes],
 | |
|                                         gateway=defaultRoute)
 | |
|         
 | |
|                 topology.addLink(routerNode, host)
 | |
| 
 | |
|     #def getLastAddress(self, network):
 | |
|     #    return ip_address(network.network_address + network.num_addresses - 2)
 | |
|     
 | |
|     def getFirstAddress(self, network):
 | |
|         return '%s/%s' % (network[1], network.prefixlen)
 | |
| 
 | |
| # TODO fix this AS - doesn't currently work
 | |
| class RouteServerAutonomousSystem(BasicAutonomousSystem):
 | |
| 
 | |
|     def __init__(self, routerAddress, *args, **kwargs):
 | |
|         BasicAutonomousSystem.__init__(self, *args, **kwargs)
 | |
| 
 | |
|         self.routerAddress = routerAddress
 | |
| 
 | |
|     def build(self, topology, connectAtSwitch):
 | |
| 
 | |
|         switch = topology.addSwitch('as%isw' % self.num, cls=OVSBridge)
 | |
| 
 | |
|         self.addRouterAndHost(topology, self.routerAddress, switch)
 | |
| 
 | |
|         rsName = 'rs%i' % self.num
 | |
|         routeServer = topology.addHost(rsName,
 | |
|                                        self.asnum, self.neighbors,
 | |
|                                        cls=BgpRouter,
 | |
|                                        interfaces={'%s-eth0' % rsName : {'ipAddrs':[self.peeringAddress]}})
 | |
| 
 | |
|         topology.addLink(routeServer, switch)
 | |
|         topology.addLink(switch, connectAtSwitch)
 | |
|         
 | |
| class SdnAutonomousSystem(AutonomousSystem):
 | |
|     
 | |
|     """Runs the internal BGP speakers needed for ONOS routing apps like
 | |
|     SDN-IP."""
 | |
|     
 | |
|     def __init__(self, onosIps, numBgpSpeakers=1, asNum=65000, externalOnos=True,
 | |
|                  peerIntfConfig=None, withFpm=False):
 | |
|         super(SdnAutonomousSystem, self).__init__(asNum, numBgpSpeakers)
 | |
|         self.onosIps = onosIps
 | |
|         self.numBgpSpeakers = numBgpSpeakers
 | |
|         self.peerIntfConfig = peerIntfConfig
 | |
|         self.withFpm = withFpm
 | |
|         self.externalOnos= externalOnos
 | |
|         self.internalPeeringSubnet = ip_network(u'1.1.1.0/24')
 | |
|         
 | |
|         for router in self.routers.values():
 | |
|             # Add iBGP sessions to ONOS nodes
 | |
|             for onosIp in onosIps:
 | |
|                 router.neighbors.append({'address':onosIp, 'as':asNum, 'port':2000})
 | |
|                 
 | |
|             # Add iBGP sessions to other BGP speakers
 | |
|             for i, router2 in self.routers.items():
 | |
|                 if router == router2:
 | |
|                     continue
 | |
|                 ip = AutonomousSystem.getIthAddress(self.internalPeeringSubnet, 10+i)
 | |
|                 router.neighbors.append({'address':ip.ip, 'as':asNum})
 | |
|         
 | |
|     def build(self, topology, connectAtSwitch, controlSwitch):
 | |
|         
 | |
|         natIp = AutonomousSystem.getLastAddress(self.internalPeeringSubnet)
 | |
|         
 | |
|         for i, router in self.routers.items():
 | |
|             name = 'bgp%s' % i
 | |
|             
 | |
|             ip = AutonomousSystem.getIthAddress(self.internalPeeringSubnet, 10+i)
 | |
|             eth0 = { 'ipAddrs' : [ str(ip) ] }
 | |
|             if self.peerIntfConfig is not None:
 | |
|                 eth1 = self.peerIntfConfig
 | |
|             else:
 | |
|                 nativeAddresses = router.interfaces[1].addressesByVlan.pop(None, [])
 | |
|                 eth1 = [{ 'mac':'00:00:00:00:00:%02x' % i, 
 | |
|                          'ipAddrs' : nativeAddresses }]
 | |
|                 
 | |
|                 for vlan, addresses in router.interfaces[1].addressesByVlan.items():
 | |
|                     eth1.append({'vlan':vlan,
 | |
|                                 'mac':'00:00:00:%02x:%02x:00' % (i, vlan),
 | |
|                                 'ipAddrs':addresses})
 | |
|             
 | |
|             
 | |
|             intfs = { '%s-eth0' % name : eth0,
 | |
|                       '%s-eth1' % name : eth1 }
 | |
|             
 | |
|             bgp = topology.addHost( name, cls=BgpRouter, asNum=self.asNum, 
 | |
|                                     neighbors=router.neighbors,
 | |
|                                     interfaces=intfs, 
 | |
|                                     defaultRoute=str(natIp.ip),
 | |
|                                     fpm=self.onosIps[0] if self.withFpm else None )
 | |
|             
 | |
|             topology.addLink( bgp, controlSwitch )
 | |
|             topology.addLink( bgp, connectAtSwitch )
 | |
|             
 | |
|             
 | |
|         if self.externalOnos:
 | |
|             nat = topology.addHost('nat', cls=NAT, 
 | |
|                                    ip='%s/%s' % (natIp.ip, self.internalPeeringSubnet.prefixlen), 
 | |
|                                    subnet=str(self.internalPeeringSubnet), inNamespace=False);
 | |
|             topology.addLink(controlSwitch, nat)
 | |
| 
 | |
|         
 | |
| def generateRoutes(baseRange, numRoutes, subnetSize=None):
 | |
|     baseNetwork = ip_network(baseRange)
 | |
|     
 | |
|     # We need to get at least 2 addresses out of each subnet, so the biggest
 | |
|     # prefix length we can have is /30
 | |
|     maxPrefixLength = baseNetwork.max_prefixlen - 2
 | |
|     
 | |
|     if subnetSize is not None:
 | |
|         return list(baseNetwork.subnets(new_prefix=subnetSize))
 | |
|     
 | |
|     trySubnetSize = baseNetwork.prefixlen + 1
 | |
|     while trySubnetSize <= maxPrefixLength and \
 | |
|             len(list(baseNetwork.subnets(new_prefix=trySubnetSize))) < numRoutes:
 | |
|         trySubnetSize += 1
 | |
|         
 | |
|     if trySubnetSize > maxPrefixLength:
 | |
|         raise Exception("Can't get enough routes from input parameters")
 | |
|     
 | |
|     return list(baseNetwork.subnets(new_prefix=trySubnetSize))[:numRoutes]
 | |
|     
 | |
| class RoutingCli( CLI ):
 | |
|     
 | |
|     """CLI command that can bring a host up or down. Useful for simulating router failure."""
 | |
|     
 | |
|     def do_host( self, line ):
 | |
|         args = line.split()
 | |
|         if len(args) != 2:
 | |
|             error( 'invalid number of args: host <host name> {up, down}\n' )
 | |
|             return
 | |
|         
 | |
|         host = args[ 0 ]
 | |
|         command = args[ 1 ]
 | |
|         if host not in self.mn or self.mn.get( host ) not in self.mn.hosts:
 | |
|             error( 'invalid host: %s\n' % args[ 1 ] )
 | |
|         else:
 | |
|             if command == 'up':
 | |
|                 op = 'up'
 | |
|             elif command == 'down':
 | |
|                 op = 'down'
 | |
|             else:
 | |
|                 error( 'invalid command: host <host name> {up, down}\n' )
 | |
|                 return
 | |
| 
 | |
|             for intf in self.mn.get( host ).intfList( ):
 | |
|                 intf.link.intf1.ifconfig( op )
 | |
|                 intf.link.intf2.ifconfig( op )
 |