2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2017-05-03 21:20:56 +01:00
|
|
|
virtual ethernet classes that implement the interfaces available under Linux.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
import subprocess
|
|
|
|
import time
|
|
|
|
|
|
|
|
from core import constants
|
|
|
|
from core.coreobj import PyCoreNetIf
|
2017-04-27 21:34:23 +01:00
|
|
|
from core.enumerations import NodeTypes
|
2017-04-25 16:45:34 +01:00
|
|
|
from core.misc import log
|
2017-04-27 21:34:23 +01:00
|
|
|
from core.misc import nodeutils
|
2017-04-25 16:45:34 +01:00
|
|
|
from core.misc import utils
|
|
|
|
|
|
|
|
logger = log.get_logger(__name__)
|
|
|
|
|
|
|
|
utils.check_executables([constants.IP_BIN])
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
class VEth(PyCoreNetIf):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Provides virtual ethernet functionality for core nodes.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# TODO: network is not used, why was it needed?
|
2017-04-25 16:45:34 +01:00
|
|
|
def __init__(self, node, name, localname, mtu=1500, net=None, start=True):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Creates a VEth instance.
|
|
|
|
|
|
|
|
:param core.netns.nodes.CoreNode node: related core node
|
|
|
|
:param str name: interface name
|
|
|
|
:param str localname: interface local name
|
|
|
|
:param mtu: interface mtu
|
|
|
|
:param net: network
|
|
|
|
:param bool start: start flag
|
|
|
|
:return:
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
# note that net arg is ignored
|
2017-04-25 16:45:34 +01:00
|
|
|
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.localname = localname
|
|
|
|
self.up = False
|
|
|
|
if start:
|
|
|
|
self.startup()
|
|
|
|
|
|
|
|
def startup(self):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Interface startup logic.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
subprocess.check_call([constants.IP_BIN, "link", "add", "name", self.localname,
|
|
|
|
"type", "veth", "peer", "name", self.name])
|
|
|
|
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "up"])
|
2013-08-29 15:21:13 +01:00
|
|
|
self.up = True
|
|
|
|
|
|
|
|
def shutdown(self):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Interface shutdown logic.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if not self.up:
|
|
|
|
return
|
|
|
|
if self.node:
|
2017-04-25 16:45:34 +01:00
|
|
|
self.node.cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.localname:
|
2017-04-25 16:45:34 +01:00
|
|
|
utils.mutedetach([constants.IP_BIN, "link", "delete", self.localname])
|
2013-08-29 15:21:13 +01:00
|
|
|
self.up = False
|
|
|
|
|
|
|
|
|
|
|
|
class TunTap(PyCoreNetIf):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
TUN/TAP virtual device in TAP mode
|
|
|
|
"""
|
|
|
|
|
2017-05-03 21:20:56 +01:00
|
|
|
# TODO: network is not used, why was it needed?
|
2017-04-25 16:45:34 +01:00
|
|
|
def __init__(self, node, name, localname, mtu=1500, net=None, start=True):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Create a TunTap instance.
|
|
|
|
|
|
|
|
:param core.netns.nodes.CoreNode node: related core node
|
|
|
|
:param str name: interface name
|
|
|
|
:param str localname: local interface name
|
|
|
|
:param mtu: interface mtu
|
|
|
|
:param net: related network
|
|
|
|
:param bool start: start flag
|
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.localname = localname
|
|
|
|
self.up = False
|
|
|
|
self.transport_type = "virtual"
|
|
|
|
if start:
|
|
|
|
self.startup()
|
|
|
|
|
|
|
|
def startup(self):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Startup logic for a tunnel tap.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
# TODO: more sophisticated TAP creation here
|
|
|
|
# Debian does not support -p (tap) option, RedHat does.
|
2017-04-25 16:45:34 +01:00
|
|
|
# For now, this is disabled to allow the TAP to be created by another
|
2013-08-29 15:21:13 +01:00
|
|
|
# system (e.g. EMANE's emanetransportd)
|
2017-04-25 16:45:34 +01:00
|
|
|
# check_call(["tunctl", "-t", self.name])
|
2013-08-29 15:21:13 +01:00
|
|
|
# self.install()
|
|
|
|
self.up = True
|
|
|
|
|
|
|
|
def shutdown(self):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Shutdown functionality for a tunnel tap.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if not self.up:
|
|
|
|
return
|
2017-04-25 16:45:34 +01:00
|
|
|
self.node.cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
|
|
|
|
# if self.name:
|
2013-08-29 15:21:13 +01:00
|
|
|
# mutedetach(["tunctl", "-d", self.localname])
|
|
|
|
self.up = False
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
def waitfor(self, func, attempts=10, maxretrydelay=0.25):
|
|
|
|
"""
|
2017-05-03 21:20:56 +01:00
|
|
|
Wait for func() to return zero with exponential backoff.
|
|
|
|
|
|
|
|
:param func: function to wait for a result of zero
|
|
|
|
:param int attempts: number of attempts to wait for a zero result
|
|
|
|
:param float maxretrydelay: maximum retry delay
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2015-01-21 21:38:10 +00:00
|
|
|
delay = 0.01
|
|
|
|
for i in xrange(1, attempts + 1):
|
|
|
|
r = func()
|
|
|
|
if r == 0:
|
|
|
|
return
|
|
|
|
msg = 'attempt %s failed with nonzero exit status %s' % (i, r)
|
|
|
|
if i < attempts + 1:
|
|
|
|
msg += ', retrying...'
|
2017-04-25 16:45:34 +01:00
|
|
|
logger.info(msg)
|
2015-01-21 21:38:10 +00:00
|
|
|
time.sleep(delay)
|
|
|
|
delay = delay + delay
|
|
|
|
if delay > maxretrydelay:
|
|
|
|
delay = maxretrydelay
|
|
|
|
else:
|
|
|
|
msg += ', giving up'
|
2017-04-25 16:45:34 +01:00
|
|
|
logger.info(msg)
|
|
|
|
|
|
|
|
raise RuntimeError('command failed after %s attempts' % attempts)
|
2015-01-21 21:38:10 +00:00
|
|
|
|
|
|
|
def waitfordevicelocal(self):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2015-01-21 21:38:10 +00:00
|
|
|
Check for presence of a local device - tap device may not
|
|
|
|
appear right away waits
|
2017-05-03 21:20:56 +01:00
|
|
|
|
|
|
|
:return: wait for device local response
|
|
|
|
:rtype: int
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
|
2015-01-21 21:38:10 +00:00
|
|
|
def localdevexists():
|
2017-04-25 16:45:34 +01:00
|
|
|
cmd = (constants.IP_BIN, 'link', 'show', self.localname)
|
|
|
|
return utils.mutecall(cmd)
|
|
|
|
|
2015-01-21 21:38:10 +00:00
|
|
|
self.waitfor(localdevexists)
|
|
|
|
|
|
|
|
def waitfordevicenode(self):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2017-05-03 21:20:56 +01:00
|
|
|
Check for presence of a node device - tap device may not appear right away waits.
|
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
|
2015-01-21 21:38:10 +00:00
|
|
|
def nodedevexists():
|
2017-04-25 16:45:34 +01:00
|
|
|
cmd = (constants.IP_BIN, 'link', 'show', self.name)
|
2015-01-21 21:38:10 +00:00
|
|
|
return self.node.cmd(cmd)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2017-04-27 21:34:23 +01:00
|
|
|
count = 0
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
self.waitfor(nodedevexists)
|
|
|
|
break
|
|
|
|
except RuntimeError as e:
|
|
|
|
# check if this is an EMANE interface; if so, continue
|
|
|
|
# waiting if EMANE is still running
|
|
|
|
# TODO: remove emane code
|
|
|
|
if count < 5 and nodeutils.is_node(self.net, NodeTypes.EMANE) and \
|
|
|
|
self.node.session.emane.emanerunning(self.node):
|
|
|
|
count += 1
|
|
|
|
else:
|
|
|
|
raise e
|
2014-12-15 22:24:17 +00:00
|
|
|
|
|
|
|
def install(self):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Install this TAP into its namespace. This is not done from the
|
|
|
|
startup() method but called at a later time when a userspace
|
|
|
|
program (running on the host) has had a chance to open the socket
|
|
|
|
end of the TAP.
|
2017-05-03 21:20:56 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2015-01-21 21:38:10 +00:00
|
|
|
self.waitfordevicelocal()
|
2014-12-15 22:24:17 +00:00
|
|
|
netns = str(self.node.pid)
|
2017-05-03 21:20:56 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
try:
|
2017-04-25 16:45:34 +01:00
|
|
|
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "netns", netns])
|
|
|
|
except subprocess.CalledProcessError:
|
2013-08-29 15:21:13 +01:00
|
|
|
msg = "error installing TAP interface %s, command:" % self.localname
|
|
|
|
msg += "ip link set %s netns %s" % (self.localname, netns)
|
2017-04-25 16:45:34 +01:00
|
|
|
logger.exception(msg)
|
2013-08-29 15:21:13 +01:00
|
|
|
return
|
2017-05-03 21:20:56 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
self.node.cmd([constants.IP_BIN, "link", "set", self.localname, "name", self.name])
|
|
|
|
self.node.cmd([constants.IP_BIN, "link", "set", self.name, "up"])
|
|
|
|
|
2014-10-28 17:24:31 +00:00
|
|
|
def setaddrs(self):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Set interface addresses based on self.addrlist.
|
2017-05-03 21:20:56 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2015-01-21 21:38:10 +00:00
|
|
|
self.waitfordevicenode()
|
2013-08-29 15:21:13 +01:00
|
|
|
for addr in self.addrlist:
|
2017-04-25 16:45:34 +01:00
|
|
|
self.node.cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
class GreTap(PyCoreNetIf):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
GRE TAP device for tunneling between emulation servers.
|
|
|
|
Uses the "gretap" tunnel device type from Linux which is a GRE device
|
|
|
|
having a MAC address. The MAC address is required for bridging.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, node=None, name=None, session=None, mtu=1458,
|
|
|
|
remoteip=None, objid=None, localip=None, ttl=255,
|
|
|
|
key=None, start=True):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Creates a GreTap instance.
|
|
|
|
|
|
|
|
:param core.netns.nodes.CoreNode node: related core node
|
|
|
|
:param str name: interface name
|
|
|
|
:param core.session.Session session: core session instance
|
|
|
|
:param mtu: interface mtu
|
|
|
|
:param str remoteip: remote address
|
|
|
|
:param int objid: object id
|
|
|
|
:param str localip: local address
|
|
|
|
:param ttl: ttl value
|
|
|
|
:param key: gre tap key
|
|
|
|
:param bool start: start flag
|
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.session = session
|
|
|
|
if objid is None:
|
|
|
|
# from PyCoreObj
|
2017-04-25 16:45:34 +01:00
|
|
|
objid = ((id(self) >> 16) ^ (id(self) & 0xffff)) & 0xffff
|
2013-08-29 15:21:13 +01:00
|
|
|
self.objid = objid
|
2017-04-25 16:45:34 +01:00
|
|
|
sessionid = self.session.short_session_id()
|
2013-08-29 15:21:13 +01:00
|
|
|
# interface name on the local host machine
|
|
|
|
self.localname = "gt.%s.%s" % (self.objid, sessionid)
|
|
|
|
self.transport_type = "raw"
|
|
|
|
if not start:
|
|
|
|
self.up = False
|
|
|
|
return
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if remoteip is None:
|
|
|
|
raise ValueError, "missing remote IP required for GRE TAP device"
|
|
|
|
cmd = ("ip", "link", "add", self.localname, "type", "gretap",
|
|
|
|
"remote", str(remoteip))
|
|
|
|
if localip:
|
|
|
|
cmd += ("local", str(localip))
|
|
|
|
if ttl:
|
|
|
|
cmd += ("ttl", str(ttl))
|
|
|
|
if key:
|
|
|
|
cmd += ("key", str(key))
|
2017-04-25 16:45:34 +01:00
|
|
|
subprocess.check_call(cmd)
|
2013-08-29 15:21:13 +01:00
|
|
|
cmd = ("ip", "link", "set", self.localname, "up")
|
2017-04-25 16:45:34 +01:00
|
|
|
subprocess.check_call(cmd)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.up = True
|
|
|
|
|
|
|
|
def shutdown(self):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Shutdown logic for a GreTap.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.localname:
|
|
|
|
cmd = ("ip", "link", "set", self.localname, "down")
|
2017-04-25 16:45:34 +01:00
|
|
|
subprocess.check_call(cmd)
|
2013-08-29 15:21:13 +01:00
|
|
|
cmd = ("ip", "link", "del", self.localname)
|
2017-04-25 16:45:34 +01:00
|
|
|
subprocess.check_call(cmd)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.localname = None
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
def data(self, message_type):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Data for a gre tap.
|
|
|
|
|
|
|
|
:param message_type: message type for data
|
|
|
|
:return: None
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
return None
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
def all_link_data(self, flags):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Retrieve link data.
|
|
|
|
|
|
|
|
:param flags: link flags
|
|
|
|
:return: link data
|
|
|
|
:rtype: list[core.data.LinkData]
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
return []
|