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
|
|
|
|
2019-02-16 17:50:19 +00:00
|
|
|
import logging
|
2017-04-25 16:45:34 +01:00
|
|
|
import time
|
2020-01-13 22:08:49 +00:00
|
|
|
from typing import TYPE_CHECKING, Callable, Dict, List, Tuple
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2019-10-01 20:14:37 +01:00
|
|
|
from core import utils
|
2019-09-28 07:29:15 +01:00
|
|
|
from core.errors import CoreCommandError
|
2019-10-16 18:14:36 +01:00
|
|
|
from core.nodes.netclient import get_net_client
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from core.emulator.distributed import DistributedServer
|
|
|
|
from core.emulator.session import Session
|
|
|
|
from core.nodes.base import CoreNetworkBase, CoreNode
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2019-10-23 17:31:07 +01:00
|
|
|
class CoreInterface:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Base class for network interfaces.
|
|
|
|
"""
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
session: "Session",
|
|
|
|
node: "CoreNode",
|
|
|
|
name: str,
|
|
|
|
mtu: int,
|
|
|
|
server: "DistributedServer" = None,
|
|
|
|
) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2019-10-19 07:28:09 +01:00
|
|
|
Creates a CoreInterface instance.
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2019-10-16 18:14:36 +01:00
|
|
|
:param core.emulator.session.Session session: core session instance
|
2019-06-07 16:59:16 +01:00
|
|
|
:param core.nodes.base.CoreNode node: node for interface
|
2019-04-30 07:31:47 +01:00
|
|
|
:param str name: interface name
|
2019-10-16 18:14:36 +01:00
|
|
|
:param int mtu: mtu value
|
2019-10-14 23:43:57 +01:00
|
|
|
:param core.emulator.distributed.DistributedServer server: remote server node
|
|
|
|
will run on, default is None for localhost
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
2019-10-16 18:14:36 +01:00
|
|
|
self.session = session
|
2019-04-30 07:31:47 +01:00
|
|
|
self.node = node
|
|
|
|
self.name = name
|
2019-06-03 03:06:25 +01:00
|
|
|
if not isinstance(mtu, int):
|
2019-04-30 07:31:47 +01:00
|
|
|
raise ValueError
|
|
|
|
self.mtu = mtu
|
|
|
|
self.net = None
|
|
|
|
self._params = {}
|
|
|
|
self.addrlist = []
|
|
|
|
self.hwaddr = None
|
|
|
|
# placeholder position hook
|
|
|
|
self.poshook = lambda a, b, c, d: None
|
|
|
|
# used with EMANE
|
|
|
|
self.transport_type = None
|
2019-10-23 07:03:03 +01:00
|
|
|
# node interface index
|
2019-04-30 07:31:47 +01:00
|
|
|
self.netindex = None
|
2019-10-23 07:03:03 +01:00
|
|
|
# net interface index
|
|
|
|
self.netifi = None
|
2019-04-30 07:31:47 +01:00
|
|
|
# index used to find flow data
|
|
|
|
self.flow_id = None
|
2019-10-08 23:09:26 +01:00
|
|
|
self.server = server
|
2019-10-16 18:14:36 +01:00
|
|
|
use_ovs = session.options.get_config("ovs") == "True"
|
2019-10-19 07:28:09 +01:00
|
|
|
self.net_client = get_net_client(use_ovs, self.host_cmd)
|
2019-10-08 23:09:26 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def host_cmd(
|
|
|
|
self,
|
|
|
|
args: str,
|
|
|
|
env: Dict[str, str] = None,
|
|
|
|
cwd: str = None,
|
|
|
|
wait: bool = True,
|
|
|
|
shell: bool = False,
|
|
|
|
) -> str:
|
2019-10-11 20:57:37 +01:00
|
|
|
"""
|
2019-10-19 07:28:09 +01:00
|
|
|
Runs a command on the host system or distributed server.
|
2019-10-11 20:57:37 +01:00
|
|
|
|
2019-10-12 00:36:57 +01:00
|
|
|
:param str args: command to run
|
2019-10-11 20:57:37 +01:00
|
|
|
:param dict env: environment to run command with
|
|
|
|
:param str cwd: directory to run command in
|
|
|
|
:param bool wait: True to wait for status, False otherwise
|
2019-10-21 18:32:42 +01:00
|
|
|
:param bool shell: True to use shell, False otherwise
|
2019-10-11 20:57:37 +01:00
|
|
|
:return: combined stdout and stderr
|
|
|
|
:rtype: str
|
|
|
|
:raises CoreCommandError: when a non-zero exit status occurs
|
|
|
|
"""
|
2019-10-08 23:09:26 +01:00
|
|
|
if self.server is None:
|
2019-10-21 18:32:42 +01:00
|
|
|
return utils.cmd(args, env, cwd, wait, shell)
|
2019-10-08 23:09:26 +01:00
|
|
|
else:
|
2019-10-14 23:43:57 +01:00
|
|
|
return self.server.remote_cmd(args, env, cwd, wait)
|
2019-04-30 07:31:47 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def startup(self) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Startup method for the interface.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def shutdown(self) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Shutdown method for the interface.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def attachnet(self, net: "CoreNetworkBase") -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Attach network.
|
|
|
|
|
2019-06-07 16:59:16 +01:00
|
|
|
:param core.nodes.base.CoreNetworkBase net: network to attach
|
2019-04-30 07:31:47 +01:00
|
|
|
:return: nothing
|
|
|
|
"""
|
|
|
|
if self.net:
|
|
|
|
self.detachnet()
|
|
|
|
self.net = None
|
|
|
|
|
|
|
|
net.attach(self)
|
|
|
|
self.net = net
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def detachnet(self) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Detach from a network.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
|
|
|
if self.net is not None:
|
|
|
|
self.net.detach(self)
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def addaddr(self, addr: str) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Add address.
|
|
|
|
|
|
|
|
:param str addr: address to add
|
|
|
|
:return: nothing
|
|
|
|
"""
|
2020-01-09 04:44:15 +00:00
|
|
|
addr = utils.validate_ip(addr)
|
2019-04-30 07:31:47 +01:00
|
|
|
self.addrlist.append(addr)
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def deladdr(self, addr: str) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Delete address.
|
|
|
|
|
|
|
|
:param str addr: address to delete
|
|
|
|
:return: nothing
|
|
|
|
"""
|
|
|
|
self.addrlist.remove(addr)
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def sethwaddr(self, addr: str) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Set hardware address.
|
|
|
|
|
2020-01-09 01:33:49 +00:00
|
|
|
:param str addr: hardware address to set to.
|
2019-04-30 07:31:47 +01:00
|
|
|
:return: nothing
|
|
|
|
"""
|
2020-01-09 04:44:15 +00:00
|
|
|
addr = utils.validate_mac(addr)
|
2019-04-30 07:31:47 +01:00
|
|
|
self.hwaddr = addr
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def getparam(self, key: str) -> float:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Retrieve a parameter from the, or None if the parameter does not exist.
|
|
|
|
|
|
|
|
:param key: parameter to get value for
|
|
|
|
:return: parameter value
|
|
|
|
"""
|
|
|
|
return self._params.get(key)
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def getparams(self) -> List[Tuple[str, float]]:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Return (key, value) pairs for parameters.
|
|
|
|
"""
|
|
|
|
parameters = []
|
|
|
|
for k in sorted(self._params.keys()):
|
|
|
|
parameters.append((k, self._params[k]))
|
|
|
|
return parameters
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def setparam(self, key: str, value: float) -> bool:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Set a parameter value, returns True if the parameter has changed.
|
|
|
|
|
|
|
|
:param key: parameter name to set
|
|
|
|
:param value: parameter value
|
|
|
|
:return: True if parameter changed, False otherwise
|
|
|
|
"""
|
|
|
|
# treat None and 0 as unchanged values
|
2019-06-05 17:54:11 +01:00
|
|
|
logging.debug("setting param: %s - %s", key, value)
|
2019-07-06 03:06:27 +01:00
|
|
|
if value is None or value < 0:
|
2019-06-03 06:53:39 +01:00
|
|
|
return False
|
|
|
|
|
2019-04-30 07:31:47 +01:00
|
|
|
current_value = self._params.get(key)
|
2019-06-03 06:53:39 +01:00
|
|
|
if current_value is not None and current_value == value:
|
2019-04-30 07:31:47 +01:00
|
|
|
return False
|
|
|
|
|
|
|
|
self._params[key] = value
|
|
|
|
return True
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def swapparams(self, name: str) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Swap out parameters dict for name. If name does not exist,
|
|
|
|
intialize it. This is for supporting separate upstream/downstream
|
|
|
|
parameters when two layer-2 nodes are linked together.
|
|
|
|
|
|
|
|
:param str name: name of parameter to swap
|
|
|
|
:return: nothing
|
|
|
|
"""
|
|
|
|
tmp = self._params
|
|
|
|
if not hasattr(self, name):
|
|
|
|
setattr(self, name, {})
|
|
|
|
self._params = getattr(self, name)
|
|
|
|
setattr(self, name, tmp)
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def setposition(self, x: float, y: float, z: float) -> None:
|
2019-04-30 07:31:47 +01:00
|
|
|
"""
|
|
|
|
Dispatch position hook handler.
|
|
|
|
|
|
|
|
:param x: x position
|
|
|
|
:param y: y position
|
|
|
|
:param z: z position
|
|
|
|
:return: nothing
|
|
|
|
"""
|
|
|
|
self.poshook(self, x, y, z)
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def __lt__(self, other: "CoreInterface") -> bool:
|
2019-06-03 03:06:25 +01:00
|
|
|
"""
|
|
|
|
Used for comparisons of this object.
|
|
|
|
|
|
|
|
:param other: other interface
|
|
|
|
:return: true if less than, false otherwise
|
|
|
|
:rtype: bool
|
|
|
|
"""
|
|
|
|
return id(self) < id(other)
|
|
|
|
|
2019-04-30 07:31:47 +01:00
|
|
|
|
|
|
|
class Veth(CoreInterface):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Provides virtual ethernet functionality for core nodes.
|
|
|
|
"""
|
|
|
|
|
2019-10-16 18:14:36 +01:00
|
|
|
def __init__(
|
2020-01-13 22:08:49 +00:00
|
|
|
self,
|
|
|
|
session: "Session",
|
|
|
|
node: "CoreNode",
|
|
|
|
name: str,
|
|
|
|
localname: str,
|
|
|
|
mtu: int = 1500,
|
|
|
|
server: "DistributedServer" = None,
|
|
|
|
start: bool = True,
|
|
|
|
) -> None:
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Creates a VEth instance.
|
|
|
|
|
2019-10-16 18:14:36 +01:00
|
|
|
:param core.emulator.session.Session session: core session instance
|
2019-06-07 16:59:16 +01:00
|
|
|
:param core.nodes.base.CoreNode node: related core node
|
2017-05-03 21:20:56 +01:00
|
|
|
:param str name: interface name
|
|
|
|
:param str localname: interface local name
|
2019-10-16 18:14:36 +01:00
|
|
|
:param int mtu: interface mtu
|
2019-10-14 23:43:57 +01:00
|
|
|
:param core.emulator.distributed.DistributedServer server: remote server node
|
|
|
|
will run on, default is None for localhost
|
2017-05-03 21:20:56 +01:00
|
|
|
:param bool start: start flag
|
2018-03-03 00:22:20 +00:00
|
|
|
:raises CoreCommandError: when there is a command exception
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
# note that net arg is ignored
|
2019-10-23 18:40:40 +01:00
|
|
|
super().__init__(session, node, name, mtu, server)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.localname = localname
|
|
|
|
self.up = False
|
|
|
|
if start:
|
|
|
|
self.startup()
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def startup(self) -> None:
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Interface startup logic.
|
|
|
|
|
|
|
|
:return: nothing
|
2018-03-03 00:22:20 +00:00
|
|
|
:raises CoreCommandError: when there is a command exception
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
2019-10-01 20:14:37 +01:00
|
|
|
self.net_client.create_veth(self.localname, self.name)
|
|
|
|
self.net_client.device_up(self.localname)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.up = True
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def shutdown(self) -> None:
|
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
|
2018-03-01 21:21:25 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.node:
|
2018-03-01 21:21:25 +00:00
|
|
|
try:
|
2019-10-01 20:14:37 +01:00
|
|
|
self.node.node_net_client.device_flush(self.name)
|
2018-03-03 00:22:20 +00:00
|
|
|
except CoreCommandError:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.exception("error shutting down interface")
|
2018-03-01 21:21:25 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.localname:
|
2018-03-02 21:39:44 +00:00
|
|
|
try:
|
2019-10-01 20:14:37 +01:00
|
|
|
self.net_client.delete_device(self.localname)
|
2018-03-03 00:22:20 +00:00
|
|
|
except CoreCommandError:
|
2019-07-03 00:05:45 +01:00
|
|
|
logging.info("link already removed: %s", self.localname)
|
2018-03-01 21:21:25 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
self.up = False
|
|
|
|
|
|
|
|
|
2019-04-30 07:31:47 +01:00
|
|
|
class TunTap(CoreInterface):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
TUN/TAP virtual device in TAP mode
|
|
|
|
"""
|
|
|
|
|
2019-10-16 18:14:36 +01:00
|
|
|
def __init__(
|
2020-01-13 22:08:49 +00:00
|
|
|
self,
|
|
|
|
session: "Session",
|
|
|
|
node: "CoreNode",
|
|
|
|
name: str,
|
|
|
|
localname: str,
|
|
|
|
mtu: int = 1500,
|
|
|
|
server: "DistributedServer" = None,
|
|
|
|
start: bool = True,
|
|
|
|
) -> None:
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Create a TunTap instance.
|
|
|
|
|
2019-10-16 18:14:36 +01:00
|
|
|
:param core.emulator.session.Session session: core session instance
|
2019-06-07 16:59:16 +01:00
|
|
|
:param core.nodes.base.CoreNode node: related core node
|
2017-05-03 21:20:56 +01:00
|
|
|
:param str name: interface name
|
|
|
|
:param str localname: local interface name
|
2019-10-16 18:14:36 +01:00
|
|
|
:param int mtu: interface mtu
|
2019-10-14 23:43:57 +01:00
|
|
|
:param core.emulator.distributed.DistributedServer server: remote server node
|
|
|
|
will run on, default is None for localhost
|
2017-05-03 21:20:56 +01:00
|
|
|
:param bool start: start flag
|
|
|
|
"""
|
2019-10-23 18:40:40 +01:00
|
|
|
super().__init__(session, node, name, mtu, server)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.localname = localname
|
|
|
|
self.up = False
|
|
|
|
self.transport_type = "virtual"
|
|
|
|
if start:
|
|
|
|
self.startup()
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def startup(self) -> None:
|
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.
|
2018-03-01 21:21:25 +00:00
|
|
|
# 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()
|
2013-08-29 15:21:13 +01:00
|
|
|
self.up = True
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def shutdown(self) -> None:
|
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
|
2018-03-01 21:21:25 +00:00
|
|
|
|
|
|
|
try:
|
2019-10-01 20:14:37 +01:00
|
|
|
self.node.node_net_client.device_flush(self.name)
|
2018-03-03 00:22:20 +00:00
|
|
|
except CoreCommandError:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.exception("error shutting down tunnel tap")
|
2018-03-01 21:21:25 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
self.up = False
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def waitfor(
|
2020-01-15 19:56:23 +00:00
|
|
|
self, func: Callable[[], int], attempts: int = 10, maxretrydelay: float = 0.25
|
2020-01-13 22:08:49 +00:00
|
|
|
) -> bool:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
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
|
2018-03-02 21:39:44 +00:00
|
|
|
:return: True if wait succeeded, False otherwise
|
|
|
|
:rtype: bool
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2015-01-21 21:38:10 +00:00
|
|
|
delay = 0.01
|
2018-03-02 21:39:44 +00:00
|
|
|
result = False
|
2019-05-06 00:19:12 +01:00
|
|
|
for i in range(1, attempts + 1):
|
2015-01-21 21:38:10 +00:00
|
|
|
r = func()
|
|
|
|
if r == 0:
|
2018-03-02 21:39:44 +00:00
|
|
|
result = True
|
|
|
|
break
|
2019-10-18 02:59:50 +01:00
|
|
|
msg = f"attempt {i} failed with nonzero exit status {r}"
|
2015-01-21 21:38:10 +00:00
|
|
|
if i < attempts + 1:
|
2017-08-04 22:51:25 +01:00
|
|
|
msg += ", retrying..."
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.info(msg)
|
2015-01-21 21:38:10 +00:00
|
|
|
time.sleep(delay)
|
2018-02-28 00:28:28 +00:00
|
|
|
delay += delay
|
2015-01-21 21:38:10 +00:00
|
|
|
if delay > maxretrydelay:
|
|
|
|
delay = maxretrydelay
|
|
|
|
else:
|
2017-08-04 22:51:25 +01:00
|
|
|
msg += ", giving up"
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.info(msg)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2018-03-02 21:39:44 +00:00
|
|
|
return result
|
2015-01-21 21:38:10 +00:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def waitfordevicelocal(self) -> None:
|
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
|
|
|
"""
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.debug("waiting for device local: %s", self.localname)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2015-01-21 21:38:10 +00:00
|
|
|
def localdevexists():
|
2019-10-01 20:14:37 +01:00
|
|
|
try:
|
|
|
|
self.net_client.device_show(self.localname)
|
|
|
|
return 0
|
|
|
|
except CoreCommandError:
|
|
|
|
return 1
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2015-01-21 21:38:10 +00:00
|
|
|
self.waitfor(localdevexists)
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def waitfordevicenode(self) -> None:
|
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
|
|
|
"""
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.debug("waiting for device node: %s", self.name)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2015-01-21 21:38:10 +00:00
|
|
|
def nodedevexists():
|
2019-07-03 00:05:45 +01:00
|
|
|
try:
|
2019-10-01 20:14:37 +01:00
|
|
|
self.node.node_net_client.device_show(self.name)
|
2019-07-03 00:05:45 +01:00
|
|
|
return 0
|
|
|
|
except CoreCommandError:
|
|
|
|
return 1
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2017-04-27 21:34:23 +01:00
|
|
|
count = 0
|
|
|
|
while True:
|
2018-03-02 21:39:44 +00:00
|
|
|
result = self.waitfor(nodedevexists)
|
|
|
|
if result:
|
2017-04-27 21:34:23 +01:00
|
|
|
break
|
2018-03-02 21:39:44 +00:00
|
|
|
|
2019-09-26 21:00:12 +01:00
|
|
|
# TODO: emane specific code
|
2018-03-02 21:39:44 +00:00
|
|
|
# check if this is an EMANE interface; if so, continue
|
|
|
|
# waiting if EMANE is still running
|
|
|
|
should_retry = count < 5
|
|
|
|
is_emane_running = self.node.session.emane.emanerunning(self.node)
|
2019-09-26 21:00:12 +01:00
|
|
|
if all([should_retry, self.net.is_emane, is_emane_running]):
|
2018-03-02 21:39:44 +00:00
|
|
|
count += 1
|
|
|
|
else:
|
|
|
|
raise RuntimeError("node device failed to exist")
|
2014-12-15 22:24:17 +00:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def install(self) -> None:
|
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
|
2018-03-03 00:22:20 +00:00
|
|
|
:raises CoreCommandError: when there is a command exception
|
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)
|
2019-10-01 20:14:37 +01:00
|
|
|
self.net_client.device_ns(self.localname, netns)
|
|
|
|
self.node.node_net_client.device_name(self.localname, self.name)
|
|
|
|
self.node.node_net_client.device_up(self.name)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def setaddrs(self) -> None:
|
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:
|
2019-10-01 20:14:37 +01:00
|
|
|
self.node.node_net_client.create_address(self.name, str(addr))
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2019-04-30 07:31:47 +01:00
|
|
|
class GreTap(CoreInterface):
|
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.
|
|
|
|
"""
|
|
|
|
|
2019-09-10 23:10:24 +01:00
|
|
|
def __init__(
|
|
|
|
self,
|
2020-01-13 22:08:49 +00:00
|
|
|
node: "CoreNode" = None,
|
|
|
|
name: str = None,
|
|
|
|
session: "Session" = None,
|
|
|
|
mtu: int = 1458,
|
|
|
|
remoteip: str = None,
|
|
|
|
_id: int = None,
|
|
|
|
localip: str = None,
|
|
|
|
ttl: int = 255,
|
|
|
|
key: int = None,
|
|
|
|
start: bool = True,
|
|
|
|
server: "DistributedServer" = None,
|
|
|
|
) -> None:
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Creates a GreTap instance.
|
|
|
|
|
2019-06-07 16:59:16 +01:00
|
|
|
:param core.nodes.base.CoreNode node: related core node
|
2017-05-03 21:20:56 +01:00
|
|
|
:param str name: interface name
|
2019-06-07 16:59:16 +01:00
|
|
|
:param core.emulator.session.Session session: core session instance
|
2019-10-14 20:31:41 +01:00
|
|
|
:param int mtu: interface mtu
|
2017-05-03 21:20:56 +01:00
|
|
|
:param str remoteip: remote address
|
2019-04-27 06:07:51 +01:00
|
|
|
:param int _id: object id
|
2017-05-03 21:20:56 +01:00
|
|
|
:param str localip: local address
|
2019-10-14 20:31:41 +01:00
|
|
|
:param int ttl: ttl value
|
|
|
|
:param int key: gre tap key
|
2017-05-03 21:20:56 +01:00
|
|
|
:param bool start: start flag
|
2019-10-14 23:43:57 +01:00
|
|
|
:param core.emulator.distributed.DistributedServer server: remote server node
|
|
|
|
will run on, default is None for localhost
|
2018-03-03 00:22:20 +00:00
|
|
|
:raises CoreCommandError: when there is a command exception
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
2019-10-23 18:40:40 +01:00
|
|
|
super().__init__(session, node, name, mtu, server)
|
2019-04-27 06:07:51 +01:00
|
|
|
if _id is None:
|
2013-08-29 15:21:13 +01:00
|
|
|
# from PyCoreObj
|
2019-09-10 23:10:24 +01:00
|
|
|
_id = ((id(self) >> 16) ^ (id(self) & 0xFFFF)) & 0xFFFF
|
2019-04-27 06:07:51 +01:00
|
|
|
self.id = _id
|
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
|
2019-10-18 02:59:50 +01:00
|
|
|
self.localname = f"gt.{self.id}.{sessionid}"
|
2013-08-29 15:21:13 +01:00
|
|
|
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:
|
2019-05-06 00:56:18 +01:00
|
|
|
raise ValueError("missing remote IP required for GRE TAP device")
|
2019-10-01 20:14:37 +01:00
|
|
|
|
2019-10-14 20:31:41 +01:00
|
|
|
self.net_client.create_gretap(self.localname, remoteip, localip, ttl, key)
|
2019-10-01 20:14:37 +01:00
|
|
|
self.net_client.device_up(self.localname)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.up = True
|
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def shutdown(self) -> None:
|
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:
|
2018-03-02 21:39:44 +00:00
|
|
|
try:
|
2019-10-01 20:14:37 +01:00
|
|
|
self.net_client.device_down(self.localname)
|
|
|
|
self.net_client.delete_device(self.localname)
|
2018-03-03 00:22:20 +00:00
|
|
|
except CoreCommandError:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.exception("error during shutdown")
|
2018-03-02 21:39:44 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
self.localname = None
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def data(self, message_type: int) -> None:
|
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
|
|
|
|
2020-01-13 22:08:49 +00:00
|
|
|
def all_link_data(self, flags: int) -> List:
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Retrieve link data.
|
|
|
|
|
|
|
|
:param flags: link flags
|
|
|
|
:return: link data
|
2019-06-07 16:59:16 +01:00
|
|
|
:rtype: list[core.emulator.data.LinkData]
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
return []
|