diff --git a/examples/nat.py b/examples/nat.py index 3c1f1cfb..df11fc20 100755 --- a/examples/nat.py +++ b/examples/nat.py @@ -6,7 +6,6 @@ from mininet.cli import CLI from mininet.log import lg -from mininet.node import Node from mininet.topolib import TreeNet if __name__ == '__main__': diff --git a/mininet/cli.py b/mininet/cli.py index a7fab397..9d6968e7 100644 --- a/mininet/cli.py +++ b/mininet/cli.py @@ -411,9 +411,14 @@ def default( self, line ): rest = args.split( ' ' ) # Substitute IP addresses for node names in command # If updateIP() returns None, then use node name - rest = [ self.mn[ arg ].defaultIntf().updateIP() or arg - if arg in self.mn else arg - for arg in rest ] + if "ping6" in args: + rest = [ self.mn[ arg ].defaultIntf().updateIP6() or arg + if arg in self.mn else arg + for arg in rest ] + else: + rest = [ self.mn[ arg ].defaultIntf().updateIP() or arg + if arg in self.mn else arg + for arg in rest ] rest = ' '.join( rest ) # Run cmd on node: node.sendCmd( rest ) diff --git a/mininet/link.py b/mininet/link.py index 9703ce7b..c4438e94 100644 --- a/mininet/link.py +++ b/mininet/link.py @@ -44,6 +44,7 @@ def __init__( self, name, node=None, port=None, link=None, self.link = link self.mac = mac self.ip, self.prefixLen = None, None + self.ip6, self.prefixLen6 = None, None # if interface is lo, we know the ip is 127.0.0.1. # This saves an ifconfig command per node @@ -81,6 +82,15 @@ def setIP( self, ipstr, prefixLen=None ): self.ip, self.prefixLen = ipstr, prefixLen return self.ifconfig( '%s/%s' % ( ipstr, prefixLen ) ) + def setIP6( self, ipstr, prefixLen=None ): + """Set IPv6 address""" + if '/' in ipstr: + self.ip6, self.prefixLen6 = ipstr.split( '/' ) + return self.ifconfig( "inet6 add %s" % ipstr ) + else: + self.ip6, self.prefixLen6 = ipstr, prefixLen + return self.ifconfig( 'inet6 add %s/%s' % ( ipstr, prefixLen ) ) + def setMAC( self, macstr ): """Set the MAC address for an interface. macstr: MAC address as string""" @@ -91,6 +101,8 @@ def setMAC( self, macstr ): _ipMatchRegex = re.compile( r'\d+\.\d+\.\d+\.\d+' ) _macMatchRegex = re.compile( r'..:..:..:..:..:..' ) + _ip6MatchRegex = re.compile( + r'inet6\s+addr:\s+(?:[0-9A-Fa-f]{0,4}:{0,1}){8}\/\d+\s+Scope:Global' ) def updateIP( self ): "Return updated IP address based on ifconfig" @@ -102,6 +114,17 @@ def updateIP( self ): self.ip = ips[ 0 ] if ips else None return self.ip + def updateIP6( self ): + "Return updated IPv6 address based on ifconfig" + ip6addr, _err, _exitCode = self.node.pexec( + 'ifconfig %s' % self.name ) + # get the ipv6 address + ipv6s = self._ip6MatchRegex.findall( ip6addr ) + self.ip6 = ipv6s[ 0 ] if ipv6s else None + if self.ip6 is not None: + self.ip6 = self.ip6.split()[2].split('/')[0] + return self.ip6 + def updateMAC( self ): "Return updated MAC address based on ifconfig" ifconfig = self.ifconfig() @@ -126,6 +149,10 @@ def IP( self ): "Return IP address" return self.ip + def IP6( self ): + "Return IPv6 address" + return self.ip6 + def MAC( self ): "Return MAC address" return self.mac diff --git a/mininet/net.py b/mininet/net.py index 917c2b75..1f9c8e4d 100755 --- a/mininet/net.py +++ b/mininet/net.py @@ -629,6 +629,50 @@ def ping( self, hosts=None, timeout=None ): output( "*** Warning: No packets sent\n" ) return ploss + def ping6( self, hosts=None, timeout=None ): + """Ping between all specified hosts. + hosts: list of hosts + timeout: time to wait for a response, as string + returns: ploss packet loss percentage""" + # should we check if running? + packets = 0 + lost = 0 + ploss = None + if not hosts: + hosts = self.hosts + output( '*** Ping6: testing ping reachability\n' ) + for node in hosts: + output( '%s -> ' % node.name ) + for dest in hosts: + if node != dest: + opts = '' + if timeout: + opts = '-W %s' % timeout + if dest.intfs: + result = node.cmd( 'ping6 -c1 %s %s' % + (opts, dest.IP6()) ) + sent, received = self._parsePing( result ) + else: + sent, received = 0, 0 + packets += sent + if received > sent: + error( '*** Error: received too many packets' ) + error( '%s' % result ) + node.cmdPrint( 'route' ) + exit( 1 ) + lost += sent - received + output( ( '%s ' % dest.name ) if received else 'X ' ) + output( '\n' ) + if packets > 0: + ploss = 100.0 * lost / packets + received = packets - lost + output( "*** Results: %i%% dropped (%d/%d received)\n" % + ( ploss, received, packets ) ) + else: + ploss = 0 + output( "*** Warning: No packets sent\n" ) + return ploss + @staticmethod def _parsePingFull( pingOutput ): "Parse ping output and return all data." diff --git a/mininet/node.py b/mininet/node.py index c93fc1b8..5c203667 100644 --- a/mininet/node.py +++ b/mininet/node.py @@ -496,7 +496,7 @@ def setHostRoute( self, ip, intf ): intf: string, interface name""" return self.cmd( 'route add -host', ip, 'dev', intf ) - def setDefaultRoute( self, intf=None ): + def setDefaultRoute( self, intf=None, ip6=False ): """Set the default route to go through intf. intf: Intf or {dev via ...}""" # Note setParam won't call us if intf is none @@ -505,7 +505,10 @@ def setDefaultRoute( self, intf=None ): else: params = 'dev %s' % intf # Do this in one line in case we're messing with the root namespace - self.cmd( 'ip route del default; ip route add default', params ) + if ip6: + self.cmd( 'ip -6 route add default', params ) + else: + self.cmd( 'ip route del default; ip route add default', params ) # Convenience and configuration methods @@ -523,10 +526,23 @@ def setIP( self, ip, prefixLen=8, intf=None, **kwargs ): kwargs: any additional arguments for intf.setIP""" return self.intf( intf ).setIP( ip, prefixLen, **kwargs ) + def setIP6( self, ip6, prefixLen=64, intf=None): + """Set the IPv6 address for an interface. + intf: intf or intf name + ip6: IPv6 address as a string + prefixLen: prefix length""" + if '/' not in ip6: + ip6 = '%s/%s' % ( ip6, prefixLen ) + return self.intf( intf ).setIP6( ip6 ) + def IP( self, intf=None ): "Return IP address of a node or specific interface." return self.intf( intf ).IP() + def IP6( self, intf=None ): + "Return IPv6 address of a node or specific interface." + return self.intf( intf ).IP6() + def MAC( self, intf=None ): "Return MAC address of a node or specific interface." return self.intf( intf ).MAC() @@ -562,7 +578,7 @@ def setParam( self, results, method, **param ): results[ name ] = result return result - def config( self, mac=None, ip=None, + def config( self, mac=None, ip=None, ip6=None, defaultRoute=None, lo='up', **_params ): """Configure Node according to (optional) parameters: mac: MAC address for default interface @@ -576,6 +592,7 @@ def config( self, mac=None, ip=None, r = {} self.setParam( r, 'setMAC', mac=mac ) self.setParam( r, 'setIP', ip=ip ) + self.setParam( r, 'setIP6', ip6=ip6 ) self.setParam( r, 'setDefaultRoute', defaultRoute=defaultRoute ) # This should be examined self.cmd( 'ifconfig lo ' + lo ) @@ -683,7 +700,7 @@ def cgroupDel( self ): _out, _err, exitcode = errRun( 'cgdelete -r ' + self.cgroup ) # Sometimes cgdelete returns a resource busy error but still # deletes the group; next attempt will give "no such file" - return exitcode == 0 or ( 'no such file' in _err.lower() ) + return exitcode == 0 or ( 'no such file' in _err.lower() ) def popen( self, *args, **kwargs ): """Return a Popen() object in node's namespace diff --git a/mininet/nodelib.py b/mininet/nodelib.py index 682b608a..7d87dbd4 100644 --- a/mininet/nodelib.py +++ b/mininet/nodelib.py @@ -9,7 +9,6 @@ from mininet.moduledeps import pathCheck from mininet.util import quietRun -import re class LinuxBridge( Switch ): "Linux Bridge (with optional spanning tree)" @@ -107,9 +106,10 @@ def config( self, **params ): self.cmd( 'iptables -A FORWARD', '-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' ) self.cmd( 'iptables -A FORWARD', - '-o', self.localIntf, '-d', self.subnet,'-j ACCEPT' ) + '-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' ) self.cmd( 'iptables -t nat -A POSTROUTING', - '-s', self.subnet, "'!'", '-d', self.subnet, '-j MASQUERADE' ) + '-s', self.subnet, "'!'", '-d', self.subnet, + '-j MASQUERADE' ) # Instruct the kernel to perform forwarding self.cmd( 'sysctl net.ipv4.ip_forward=1' ) @@ -136,9 +136,10 @@ def terminate( self ): self.cmd( 'iptables -D FORWARD', '-i', self.localIntf, '-s', self.subnet, '-j ACCEPT' ) self.cmd( 'iptables -D FORWARD', - '-o', self.localIntf, '-d', self.subnet,'-j ACCEPT' ) + '-o', self.localIntf, '-d', self.subnet, '-j ACCEPT' ) self.cmd( 'iptables -t nat -D POSTROUTING', - '-s', self.subnet, '\'!\'', '-d', self.subnet, '-j MASQUERADE' ) + '-s', self.subnet, '\'!\'', '-d', self.subnet, + '-j MASQUERADE' ) # Put the forwarding state back to what it was self.cmd( 'sysctl net.ipv4.ip_forward=%s' % self.forwardState ) super( NAT, self ).terminate() diff --git a/mininet/topolib.py b/mininet/topolib.py index 8a2542dd..a3d8a092 100644 --- a/mininet/topolib.py +++ b/mininet/topolib.py @@ -67,7 +67,9 @@ def build( self, x, y, n=1 ): switch = switches[ i, j ] = self.addSwitch( 's' + loc, dpid='%x' % dpid ) for k in range( 0, n ): - host = hosts[ i, j, k ] = self.addHost( genHostName( loc, k + 1 ) ) + host = hosts[ i, j, k ] = self.addHost( genHostName( loc, + k + 1 + ) ) self.addLink( host, switch ) # Connect switches for i in range( 0, x ): diff --git a/mininet/util.py b/mininet/util.py index 6eff84d2..c55fddbe 100644 --- a/mininet/util.py +++ b/mininet/util.py @@ -336,6 +336,19 @@ def netParse( ipstr ): prefixLen = 24 return ipParse( ip ), prefixLen +def netParse6( ipstr ): + """Parse an IP network specification, returning + address and prefix len""" + prefixLen = 0 + if '/' in ipstr: + ip, pf = ipstr.split( '/' ) + prefixLen = int( pf ) + #if no prefix is specified, set the prefix to 64 + else: + ip = ipstr + prefixLen = 64 + return ip, prefixLen + def checkInt( s ): "Check if input string is an int" try: