2013-08-29 15:21:13 +01:00
|
|
|
#
|
|
|
|
# CORE
|
2014-10-28 17:24:31 +00:00
|
|
|
# Copyright (c)2011-2014 the Boeing Company.
|
2013-08-29 15:21:13 +01:00
|
|
|
# See the LICENSE file included in this distribution.
|
|
|
|
#
|
|
|
|
# authors: Tom Goff <thomas.goff@boeing.com>
|
|
|
|
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
|
|
|
#
|
|
|
|
'''
|
|
|
|
vif.py: PyCoreNetIf classes that implement the interfaces available
|
|
|
|
under Linux.
|
|
|
|
'''
|
|
|
|
|
|
|
|
import os, signal, shutil, sys, subprocess, vnodeclient, threading, string
|
|
|
|
import random, time
|
|
|
|
from core.api import coreapi
|
|
|
|
from core.misc.utils import *
|
|
|
|
from core.constants import *
|
|
|
|
from core.coreobj import PyCoreObj, PyCoreNode, PyCoreNetIf, Position
|
|
|
|
from core.emane.nodes import EmaneNode
|
|
|
|
|
|
|
|
checkexec([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):
|
|
|
|
check_call([IP_BIN, "link", "add", "name", self.localname,
|
|
|
|
"type", "veth", "peer", "name", self.name])
|
|
|
|
check_call([IP_BIN, "link", "set", self.localname, "up"])
|
|
|
|
self.up = True
|
|
|
|
|
|
|
|
def shutdown(self):
|
|
|
|
if not self.up:
|
|
|
|
return
|
|
|
|
if self.node:
|
|
|
|
self.node.cmd([IP_BIN, "-6", "addr", "flush", "dev", self.name])
|
|
|
|
if self.localname:
|
|
|
|
mutedetach([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([IP_BIN, "-6", "addr", "flush", "dev", self.name])
|
|
|
|
#if self.name:
|
|
|
|
# mutedetach(["tunctl", "-d", self.localname])
|
|
|
|
self.up = False
|
|
|
|
|
2015-01-21 21:38:10 +00:00
|
|
|
def waitfor(self, func, attempts = 10, maxretrydelay = 0.25):
|
2014-12-15 22:24:17 +00:00
|
|
|
'''\
|
2015-01-21 21:38:10 +00:00
|
|
|
Wait for func() to return zero with exponential backoff
|
2013-08-29 15:21:13 +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...'
|
2013-08-29 15:21:13 +01:00
|
|
|
self.node.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'
|
|
|
|
self.node.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 = (IP_BIN, 'link', 'show', self.localname)
|
|
|
|
return 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 = (IP_BIN, 'link', 'show', self.name)
|
|
|
|
return self.node.cmd(cmd)
|
2016-10-20 00:21:06 +01:00
|
|
|
count = 0
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
self.waitfor(nodedevexists)
|
|
|
|
break
|
|
|
|
except RuntimeError:
|
|
|
|
# check if this is an EMANE interface; if so, continue
|
|
|
|
# waiting if EMANE is still running
|
|
|
|
if count < 5 and isinstance(self.net, EmaneNode) and \
|
|
|
|
self.node.session.emane.emanerunning(self.node):
|
|
|
|
count += 1
|
|
|
|
else:
|
|
|
|
raise
|
2014-12-15 22:24:17 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
'''
|
2015-01-21 21:38:10 +00:00
|
|
|
self.waitfordevicelocal()
|
2014-12-15 22:24:17 +00:00
|
|
|
netns = str(self.node.pid)
|
2013-08-29 15:21:13 +01:00
|
|
|
try:
|
|
|
|
check_call([IP_BIN, "link", "set", self.localname, "netns", netns])
|
|
|
|
except Exception, e:
|
|
|
|
msg = "error installing TAP interface %s, command:" % self.localname
|
|
|
|
msg += "ip link set %s netns %s" % (self.localname, netns)
|
|
|
|
self.node.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.localname, msg)
|
|
|
|
self.node.warn(msg)
|
|
|
|
return
|
|
|
|
self.node.cmd([IP_BIN, "link", "set", self.localname,
|
|
|
|
"name", self.name])
|
2014-10-28 17:24:31 +00:00
|
|
|
self.node.cmd([IP_BIN, "link", "set", self.name, "up"])
|
|
|
|
|
|
|
|
def setaddrs(self):
|
|
|
|
''' Set interface addresses based on self.addrlist.
|
|
|
|
'''
|
2015-01-21 21:38:10 +00:00
|
|
|
self.waitfordevicenode()
|
2013-08-29 15:21:13 +01:00
|
|
|
for addr in self.addrlist:
|
|
|
|
self.node.cmd([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.shortsessionid()
|
|
|
|
# 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))
|
|
|
|
check_call(cmd)
|
|
|
|
cmd = ("ip", "link", "set", self.localname, "up")
|
|
|
|
check_call(cmd)
|
|
|
|
self.up = True
|
|
|
|
|
|
|
|
def shutdown(self):
|
|
|
|
if self.localname:
|
|
|
|
cmd = ("ip", "link", "set", self.localname, "down")
|
|
|
|
check_call(cmd)
|
|
|
|
cmd = ("ip", "link", "del", self.localname)
|
|
|
|
check_call(cmd)
|
|
|
|
self.localname = None
|
|
|
|
|
|
|
|
def tonodemsg(self, flags):
|
|
|
|
return None
|
|
|
|
|
|
|
|
def tolinkmsgs(self, flags):
|
|
|
|
return []
|