core-extra/daemon/core/netns/vif.py

216 lines
7.1 KiB
Python
Raw Normal View History

"""
PyCoreNetIf classes that implement the interfaces available
under Linux.
"""
import subprocess
import time
from core import constants
from core.coreobj import PyCoreNetIf
from core.enumerations import NodeTypes
from core.misc import log
from core.misc import nodeutils
from core.misc import utils
logger = log.get_logger(__name__)
utils.check_executables([constants.IP_BIN])
class VEth(PyCoreNetIf):
def __init__(self, node, name, localname, mtu=1500, net=None, start=True):
# note that net arg is ignored
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
self.localname = localname
self.up = False
if start:
self.startup()
def startup(self):
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"])
self.up = True
def shutdown(self):
if not self.up:
return
if self.node:
self.node.cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
if self.localname:
utils.mutedetach([constants.IP_BIN, "link", "delete", self.localname])
self.up = False
class TunTap(PyCoreNetIf):
"""
TUN/TAP virtual device in TAP mode
"""
def __init__(self, node, name, localname, mtu=1500, net=None, start=True):
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
self.localname = localname
self.up = False
self.transport_type = "virtual"
if start:
self.startup()
def startup(self):
# TODO: more sophisticated TAP creation here
# Debian does not support -p (tap) option, RedHat does.
# For now, this is disabled to allow the TAP to be created by another
# system (e.g. EMANE's emanetransportd)
# check_call(["tunctl", "-t", self.name])
# self.install()
self.up = True
def shutdown(self):
if not self.up:
return
self.node.cmd([constants.IP_BIN, "-6", "addr", "flush", "dev", self.name])
# if self.name:
# mutedetach(["tunctl", "-d", self.localname])
self.up = False
def waitfor(self, func, attempts=10, maxretrydelay=0.25):
"""
Wait for func() to return zero with exponential backoff
"""
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...'
logger.info(msg)
time.sleep(delay)
delay = delay + delay
if delay > maxretrydelay:
delay = maxretrydelay
else:
msg += ', giving up'
logger.info(msg)
raise RuntimeError('command failed after %s attempts' % attempts)
def waitfordevicelocal(self):
"""
Check for presence of a local device - tap device may not
appear right away waits
"""
def localdevexists():
cmd = (constants.IP_BIN, 'link', 'show', self.localname)
return utils.mutecall(cmd)
self.waitfor(localdevexists)
def waitfordevicenode(self):
"""
Check for presence of a node device - tap device may not
appear right away waits
"""
def nodedevexists():
cmd = (constants.IP_BIN, 'link', 'show', self.name)
return self.node.cmd(cmd)
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
def install(self):
"""
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.
"""
self.waitfordevicelocal()
netns = str(self.node.pid)
try:
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "netns", netns])
except subprocess.CalledProcessError:
msg = "error installing TAP interface %s, command:" % self.localname
msg += "ip link set %s netns %s" % (self.localname, netns)
logger.exception(msg)
return
self.node.cmd([constants.IP_BIN, "link", "set", self.localname, "name", self.name])
self.node.cmd([constants.IP_BIN, "link", "set", self.name, "up"])
def setaddrs(self):
"""
Set interface addresses based on self.addrlist.
"""
self.waitfordevicenode()
for addr in self.addrlist:
self.node.cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
class GreTap(PyCoreNetIf):
"""
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):
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
self.session = session
if objid is None:
# from PyCoreObj
objid = ((id(self) >> 16) ^ (id(self) & 0xffff)) & 0xffff
self.objid = objid
sessionid = self.session.short_session_id()
# 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
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))
subprocess.check_call(cmd)
cmd = ("ip", "link", "set", self.localname, "up")
subprocess.check_call(cmd)
self.up = True
def shutdown(self):
if self.localname:
cmd = ("ip", "link", "set", self.localname, "down")
subprocess.check_call(cmd)
cmd = ("ip", "link", "del", self.localname)
subprocess.check_call(cmd)
self.localname = None
def data(self, message_type):
return None
def all_link_data(self, flags):
return []