IPv6 support for RoutedHost and Router

Also remove traling spaces

Change-Id: Ie27b4c1136f9738ea12ab6eab3972043170c4d06
diff --git a/routinglib.py b/routinglib.py
index 7285fc3..b7424ca 100644
--- a/routinglib.py
+++ b/routinglib.py
@@ -22,47 +22,48 @@
     def config(self, **kwargs):
         Host.config(self, **kwargs)
 
-        self.cmd('ip addr flush dev %s' % self.defaultIntf())
+        self.cmd('ip -4 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')
+        self.cmd('sysctl net.ipv6.conf.all.forwarding=1')
 
         for intf, configs in self.interfaces.items():
-            self.cmd('ip addr flush dev %s' % intf)
+            self.cmd('ip -4 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    
+                # 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' % 
+                    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)
@@ -72,12 +73,12 @@
                     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,
@@ -85,16 +86,16 @@
                  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)
@@ -109,9 +110,9 @@
         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):
@@ -128,59 +129,59 @@
 
         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.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
@@ -188,19 +189,19 @@
                 # 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' % 'quagga')
         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'])
@@ -209,32 +210,32 @@
             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
@@ -242,72 +243,72 @@
                 # 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,
@@ -315,64 +316,64 @@
                  zebraConfFile=None,
                  *args, **kwargs):
         bgp = BgpProtocol(configFile=quaggaConfFile, asNum=asNum, neighbors=neighbors, routes=routes)
-        
-        super(BgpRouter, self).__init__(name, interfaces, 
+
+        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)
@@ -380,27 +381,27 @@
         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)
 
@@ -411,24 +412,24 @@
     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))
@@ -442,7 +443,7 @@
         super(BasicAutonomousSystem, self).__init__(65000+num, numRouters)
         self.num = num
         self.routes = routes
-        
+
     def addLink(self, switch, router=1):
         self.routers[router].setSwitch(switch)
 
@@ -450,49 +451,49 @@
         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,  
+
+            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:
@@ -501,16 +502,16 @@
             # 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)
 
@@ -536,14 +537,14 @@
 
         topology.addLink(routeServer, switch)
         topology.addLink(switch, connectAtSwitch)
-        
+
 class SdnAutonomousSystem(AutonomousSystem):
-    
+
     """Runs the internal BGP speakers needed for ONOS routing apps like
     SDN-IP."""
-    
+
     routerIdx = 1
-    
+
     def __init__(self, onosIps, num=1, numBgpSpeakers=1, asNum=65000, externalOnos=True,
                  peerIntfConfig=None, withFpm=False):
         super(SdnAutonomousSystem, self).__init__(asNum, numBgpSpeakers)
@@ -554,12 +555,12 @@
         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:
@@ -567,83 +568,83 @@
                 cpIpBase = self.num*10
                 ip = AutonomousSystem.getIthAddress(self.internalPeeringSubnet, cpIpBase+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():
             num = SdnAutonomousSystem.routerIdx
             SdnAutonomousSystem.routerIdx += 1
             name = 'bgp%s' % num
-            
+
             cpIpBase = self.num*10
             ip = AutonomousSystem.getIthAddress(self.internalPeeringSubnet, cpIpBase+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' % num, 
+                eth1 = [{ 'mac':'00:00:00:00:00:%02x' % num,
                          'ipAddrs' : nativeAddresses }]
-                
+
                 for vlan, addresses in router.interfaces[1].addressesByVlan.items():
                     eth1.append({'vlan':vlan,
                                 'mac':'00:00:00:%02x:%02x:00' % (num, vlan),
                                 'ipAddrs':addresses})
-            
-            
+
+
             intfs = { '%s-eth0' % name : eth0,
                       '%s-eth1' % name : eth1 }
-            
-            bgp = topology.addHost( name, cls=BgpRouter, asNum=self.asNum, 
+
+            bgp = topology.addHost( name, cls=BgpRouter, asNum=self.asNum,
                                     neighbors=router.neighbors,
-                                    interfaces=intfs, 
+                                    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), 
+            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: