Merge branch 'develop' into coretk-create-node

This commit is contained in:
Huy Pham 2019-09-27 16:19:03 -07:00
commit 130c2a9b8d
42 changed files with 478 additions and 1251 deletions

View file

@ -11,6 +11,7 @@ insert_final_newline = true
[*.py]
indent_style = space
indent_size = 4
max_line_length = 88
[*.am]
indent_style = tab

View file

@ -1,4 +1,4 @@
# CORE [![Codacy Badge](https://api.codacy.com/project/badge/Grade/d94eb0244ade4510a106b4af76077a92)](https://www.codacy.com/app/blakeharnden/core?utm_source=github.com&utm_medium=referral&utm_content=coreemu/core&utm_campaign=Badge_Grade)
# CORE
CORE: Common Open Research Emulator

View file

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
# this defines the CORE version number, must be static for AC_INIT
AC_INIT(core, 5.3.1)
AC_INIT(core, 5.4.0)
# autoconf and automake initialization
AC_CONFIG_SRCDIR([netns/version.h.in])
@ -54,6 +54,13 @@ if test "x$enable_python" = "xyes" ; then
else
want_python=no
fi
AC_ARG_ENABLE([python3],
[AS_HELP_STRING([--enable-python3],
[sets python3 flag for building packages])],
[enable_python3=yes], [enable_python3=no])
AM_CONDITIONAL([PYTHON3], [test "x$enable_python3" == "xyes"])
AC_ARG_ENABLE([daemon],
[AS_HELP_STRING([--enable-daemon[=ARG]],
[build and install the daemon with Python modules
@ -110,7 +117,6 @@ if test "x$enable_daemon" = "xyes"; then
AC_CHECK_FUNCS([atexit dup2 gettimeofday memset socket strerror uname])
AM_PATH_PYTHON(2.7)
AM_CONDITIONAL([PYTHON3], [test "x$PYTHON" == "xpython3"])
AS_IF([$PYTHON -m grpc_tools.protoc -h &> /dev/null], [], [AC_MSG_ERROR([please install python grpcio-tools])])
AC_CHECK_PROG(brctl_path, brctl, $as_dir, no, $SEARCHPATH)
@ -157,6 +163,7 @@ if test "x$enable_daemon" = "xyes"; then
CFLAGS=$CFLAGS_save
CPPFLAGS=$CPPFLAGS_save
fi
if [ test "x$enable_daemon" = "xyes" || test "x$enable_vnodedonly" = "xyes" ] ; then
want_linux_netns=yes
PKG_CHECK_MODULES(libev, libev,

View file

@ -13,7 +13,7 @@
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<jung.version>2.1.1</jung.version>
<jackson.version>2.9.9</jackson.version>
<jackson.version>2.10.0.pr2</jackson.version>
<grpc.version>1.20.0</grpc.version>
<log4j.version>2.9.0</log4j.version>
</properties>

View file

@ -14,11 +14,7 @@ if WANT_DOCS
DOCS = doc
endif
if PYTHON3
PYTHONLIBDIR=$(libdir)/python3/dist-packages
else
PYTHONLIBDIR=$(pythondir)
endif
PYTHONLIBDIR=$(subst site-packages,dist-packages,$(pythondir))
SUBDIRS = proto $(DOCS)

View file

@ -4,8 +4,9 @@ url = "https://pypi.org/simple"
verify_ssl = true
[scripts]
coredev = "python scripts/core-daemon -f data/core.conf -l data/logging.conf"
coretest = "python -m pytest -v tests"
core = "python scripts/core-daemon -f data/core.conf -l data/logging.conf"
test = "pytest -v tests"
test_emane = "pytest -v tests/emane"
[dev-packages]
grpcio-tools = "*"

View file

@ -12,6 +12,7 @@ import grpc
from core import CoreError
from core.api.grpc import core_pb2, core_pb2_grpc
from core.emane.nodes import EmaneNode
from core.emulator.data import (
ConfigData,
EventData,
@ -23,7 +24,6 @@ from core.emulator.data import (
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
from core.emulator.enumerations import EventTypes, LinkTypes, NodeTypes
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
from core.nodes import nodeutils
from core.nodes.base import CoreNetworkBase
from core.nodes.docker import DockerNode
from core.nodes.ipaddress import MacAddress
@ -444,7 +444,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
if not isinstance(node.id, int):
continue
node_type = nodeutils.get_node_type(node.__class__).value
node_type = session.get_node_type(node.__class__)
model = getattr(node, "type", None)
position = core_pb2.Position(
x=node.position.x, y=node.position.y, z=node.position.z
@ -456,7 +456,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
services = [x.name for x in services]
emane_model = None
if nodeutils.is_node(node, NodeTypes.EMANE):
if isinstance(node, EmaneNode):
emane_model = node.model.name
node_proto = core_pb2.Node(
@ -464,7 +464,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
name=node.name,
emane=emane_model,
model=model,
type=node_type,
type=node_type.value,
position=position,
services=services,
)
@ -809,18 +809,18 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
interfaces.append(interface_proto)
emane_model = None
if nodeutils.is_node(node, NodeTypes.EMANE):
if isinstance(node, EmaneNode):
emane_model = node.model.name
services = [x.name for x in getattr(node, "services", [])]
position = core_pb2.Position(
x=node.position.x, y=node.position.y, z=node.position.z
)
node_type = nodeutils.get_node_type(node.__class__).value
node_type = session.get_node_type(node.__class__)
node_proto = core_pb2.Node(
id=node.id,
name=node.name,
type=node_type,
type=node_type.value,
emane=emane_model,
model=node.type,
position=position,

View file

@ -12,6 +12,7 @@ import threading
from core import utils
from core.api.tlv import coreapi
from core.emane.nodes import EmaneNet
from core.emulator.enumerations import (
ConfigDataTypes,
ConfigFlags,
@ -27,11 +28,10 @@ from core.emulator.enumerations import (
NodeTypes,
RegisterTlvs,
)
from core.nodes import nodeutils
from core.nodes.base import CoreNetworkBase, CoreNodeBase
from core.nodes.interface import GreTap
from core.nodes.ipaddress import IpAddress
from core.nodes.network import GreTapBridge
from core.nodes.network import CtrlNet, GreTapBridge
from core.nodes.physical import PhysicalNode
@ -318,13 +318,13 @@ class CoreBroker(object):
# leave this socket connected
return
logging.info(
logging.debug(
"closing connection with %s @ %s:%s", name, server.host, server.port
)
server.close()
del self.servers[name]
logging.info("adding broker server(%s): %s:%s", name, host, port)
logging.debug("adding broker server(%s): %s:%s", name, host, port)
server = CoreDistributedServer(name, host, port)
if host is not None and port is not None:
try:
@ -492,33 +492,30 @@ class CoreBroker(object):
:raises core.CoreError: when node to add net tunnel to does not exist
"""
net = self.session.get_node(node_id)
logging.info("adding net tunnel for: id(%s) %s", node_id, net)
logging.debug("adding net tunnel for: id(%s) %s", node_id, net.name)
# add other nets here that do not require tunnels
if nodeutils.is_node(net, NodeTypes.EMANE_NET):
if isinstance(net, EmaneNet):
logging.warning("emane network does not require a tunnel")
return None
server_interface = getattr(net, "serverintf", None)
if (
nodeutils.is_node(net, NodeTypes.CONTROL_NET)
and server_interface is not None
):
logging.warning(
if isinstance(net, CtrlNet) and server_interface is not None:
logging.debug(
"control networks with server interfaces do not need a tunnel"
)
return None
servers = self.getserversbynode(node_id)
if len(servers) < 2:
logging.warning("not enough servers to create a tunnel: %s", servers)
logging.debug("not enough servers to create a tunnel for node: %s", node_id)
return None
hosts = []
for server in servers:
if server.host is None:
continue
logging.info("adding server host for net tunnel: %s", server.host)
logging.debug("adding server host for net tunnel: %s", server.host)
hosts.append(server.host)
if len(hosts) == 0:
@ -526,7 +523,7 @@ class CoreBroker(object):
# get IP address from API message sender (master)
if session_client.client_address != "":
address = session_client.client_address[0]
logging.info("adding session_client host: %s", address)
logging.debug("adding session_client host: %s", address)
hosts.append(address)
r = []
@ -539,7 +536,7 @@ class CoreBroker(object):
myip = host
key = self.tunnelkey(node_id, IpAddress.to_int(myip))
if key in self.tunnels.keys():
logging.info(
logging.debug(
"tunnel already exists, returning existing tunnel: %s", key
)
gt = self.tunnels[key]
@ -658,9 +655,9 @@ class CoreBroker(object):
:param int nodenum: node id to add
:return: nothing
"""
logging.info("adding net to broker: %s", nodenum)
logging.debug("adding net to broker: %s", nodenum)
self.network_nodes.add(nodenum)
logging.info("broker network nodes: %s", self.network_nodes)
logging.debug("broker network nodes: %s", self.network_nodes)
def addphys(self, nodenum):
"""
@ -824,7 +821,8 @@ class CoreBroker(object):
nodetype = message.get_tlv(NodeTlvs.TYPE.value)
if nodetype is not None:
try:
nodecls = nodeutils.get_node_class(NodeTypes(nodetype))
nodetype = NodeTypes(nodetype)
nodecls = self.session.get_node_class(nodetype)
except KeyError:
logging.warning("broker invalid node type %s", nodetype)
return handle_locally, servers

View file

@ -38,7 +38,7 @@ from core.emulator.enumerations import (
SessionTlvs,
)
from core.location.mobility import BasicRangeModel
from core.nodes import nodeutils
from core.nodes.network import WlanNode
from core.services.coreservices import ServiceManager, ServiceShim
@ -1603,8 +1603,8 @@ class CoreHandler(socketserver.BaseRequestHandler):
node = self.session.get_node(node_id)
# configure mobility models for WLAN added during runtime
if event_type == EventTypes.INSTANTIATION_STATE and nodeutils.is_node(
node, NodeTypes.WIRELESS_LAN
if event_type == EventTypes.INSTANTIATION_STATE and isinstance(
node, WlanNode
):
self.session.start_mobility(node_ids=(node.id,))
return ()

View file

@ -15,6 +15,7 @@ from core.emane.bypass import EmaneBypassModel
from core.emane.commeffect import EmaneCommEffectModel
from core.emane.emanemodel import EmaneModel
from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emane.nodes import EmaneNode
from core.emane.rfpipe import EmaneRfPipeModel
from core.emane.tdma import EmaneTdmaModel
from core.emulator.enumerations import (
@ -23,10 +24,8 @@ from core.emulator.enumerations import (
ConfigTlvs,
MessageFlags,
MessageTypes,
NodeTypes,
RegisterTlvs,
)
from core.nodes import nodeutils
from core.xml import emanexml
try:
@ -266,7 +265,7 @@ class EmaneManager(ModelManager):
with self.session._nodes_lock:
for node_id in self.session.nodes:
node = self.session.nodes[node_id]
if nodeutils.is_node(node, NodeTypes.EMANE):
if isinstance(node, EmaneNode):
logging.debug(
"adding emane node: id(%s) name(%s)", node.id, node.name
)

View file

@ -25,7 +25,6 @@ class EmaneNet(CoreNetworkBase):
apitype = NodeTypes.EMANE.value
linktype = LinkTypes.WIRELESS.value
# icon used
type = "wlan"
@ -36,6 +35,8 @@ class EmaneNode(EmaneNet):
Emane controller object that exists in a session.
"""
is_emane = True
def __init__(self, session, _id=None, name=None, start=True):
super(EmaneNode, self).__init__(session, _id, name, start)
self.conf = ""

View file

@ -7,7 +7,6 @@ import sys
import core.services
from core.emulator.emudata import IdGen
from core.emulator.session import Session
from core.nodes import nodemaps, nodeutils
from core.services.coreservices import ServiceManager
@ -45,7 +44,7 @@ class CoreEmu(object):
os.umask(0)
# configuration
if not config:
if config is None:
config = {}
self.config = config
@ -53,10 +52,6 @@ class CoreEmu(object):
self.session_id_gen = IdGen(_id=0)
self.sessions = {}
# set default nodes
node_map = nodemaps.NODES
nodeutils.set_node_map(node_map)
# load services
self.service_errors = []
self.load_services()
@ -77,15 +72,6 @@ class CoreEmu(object):
custom_service_errors = ServiceManager.add_services(service_path)
self.service_errors.extend(custom_service_errors)
def update_nodes(self, node_map):
"""
Updates node map used by core.
:param dict node_map: node map to update existing node map with
:return: nothing
"""
nodeutils.update_node_map(node_map)
def shutdown(self):
"""
Shutdown all CORE session.

View file

@ -1,7 +1,7 @@
from core.emulator.enumerations import LinkTypes, NodeTypes
from core.nodes import nodeutils
from core.nodes.base import CoreNetworkBase
from core.emane.nodes import EmaneNode
from core.emulator.enumerations import LinkTypes
from core.nodes.ipaddress import Ipv4Prefix, Ipv6Prefix, MacAddress
from core.nodes.physical import PhysicalNode
class IdGen(object):
@ -13,17 +13,6 @@ class IdGen(object):
return self.id
def is_net_node(node):
"""
Convenience method for testing if a legacy core node is considered a network node.
:param object node: object to test against
:return: True if object is an instance of a network node, False otherwise
:rtype: bool
"""
return isinstance(node, CoreNetworkBase)
def create_interface(node, network, interface_data):
"""
Create an interface for a node on a network using provided interface data.
@ -65,7 +54,7 @@ def link_config(network, interface, link_options, devname=None, interface_two=No
}
# hacky check here, because physical and emane nodes do not conform to the same linkconfig interface
if not nodeutils.is_node(network, [NodeTypes.EMANE, NodeTypes.PHYSICAL]):
if not isinstance(network, (EmaneNode, PhysicalNode)):
config["devname"] = devname
network.linkconfig(**config)

View file

@ -14,18 +14,17 @@ import threading
import time
from multiprocessing.pool import ThreadPool
import core.nodes.base
from core import CoreError, constants, utils
from core.api.tlv import coreapi
from core.api.tlv.broker import CoreBroker
from core.emane.emanemanager import EmaneManager
from core.emane.nodes import EmaneNet, EmaneNode
from core.emulator.data import EventData, ExceptionData, NodeData
from core.emulator.emudata import (
IdGen,
LinkOptions,
NodeOptions,
create_interface,
is_net_node,
link_config,
)
from core.emulator.enumerations import EventTypes, ExceptionLevels, LinkTypes, NodeTypes
@ -33,14 +32,46 @@ from core.emulator.sessionconfig import SessionConfig, SessionMetaData
from core.location.corelocation import CoreLocation
from core.location.event import EventLoop
from core.location.mobility import MobilityManager
from core.nodes import nodeutils
from core.nodes.base import CoreNodeBase
from core.nodes.base import CoreNetworkBase, CoreNode, CoreNodeBase
from core.nodes.docker import DockerNode
from core.nodes.ipaddress import MacAddress
from core.nodes.lxd import LxcNode
from core.nodes.network import (
CtrlNet,
GreTapBridge,
HubNode,
PtpNet,
SwitchNode,
TunnelNode,
WlanNode,
)
from core.nodes.physical import PhysicalNode, Rj45Node
from core.plugins.sdt import Sdt
from core.services.coreservices import CoreServices
from core.xml import corexml, corexmldeployment
from core.xml.corexml import CoreXmlReader, CoreXmlWriter
# maps for converting from API call node type values to classes and vice versa
NODES = {
NodeTypes.DEFAULT: CoreNode,
NodeTypes.PHYSICAL: PhysicalNode,
NodeTypes.TBD: None,
NodeTypes.SWITCH: SwitchNode,
NodeTypes.HUB: HubNode,
NodeTypes.WIRELESS_LAN: WlanNode,
NodeTypes.RJ45: Rj45Node,
NodeTypes.TUNNEL: TunnelNode,
NodeTypes.KTUNNEL: None,
NodeTypes.EMANE: EmaneNode,
NodeTypes.EMANE_NET: EmaneNet,
NodeTypes.TAP_BRIDGE: GreTapBridge,
NodeTypes.PEER_TO_PEER: PtpNet,
NodeTypes.CONTROL_NET: CtrlNet,
NodeTypes.DOCKER: DockerNode,
NodeTypes.LXC: LxcNode,
}
NODES_TYPE = {NODES[x]: x for x in NODES}
class Session(object):
"""
@ -121,6 +152,33 @@ class Session(object):
"host": ("DefaultRoute", "SSH"),
}
@classmethod
def get_node_class(cls, _type):
"""
Retrieve the class for a given node type.
:param core.emulator.enumerations.NodeTypes _type: node type to get class for
:return: node class
"""
node_class = NODES.get(_type)
if node_class is None:
raise CoreError("invalid node type: %s" % _type)
return node_class
@classmethod
def get_node_type(cls, _class):
"""
Retrieve node type for a given node class.
:param _class: node class to get a node type for
:return: node type
:rtype: core.emulator.enumerations.NodeTypes
"""
node_type = NODES_TYPE.get(_class)
if node_type is None:
raise CoreError("invalid node class: %s" % _class)
return node_type
def _link_nodes(self, node_one_id, node_two_id):
"""
Convenience method for retrieving nodes within link data.
@ -145,7 +203,7 @@ class Session(object):
# both node ids are provided
tunnel = self.broker.gettunnel(node_one_id, node_two_id)
logging.debug("tunnel between nodes: %s", tunnel)
if nodeutils.is_node(tunnel, NodeTypes.TAP_BRIDGE):
if isinstance(tunnel, GreTapBridge):
net_one = tunnel
if tunnel.remotenum == node_one_id:
node_one = None
@ -158,14 +216,14 @@ class Session(object):
else:
node_two = None
if is_net_node(node_one):
if isinstance(node_one, CoreNetworkBase):
if not net_one:
net_one = node_one
else:
net_two = node_one
node_one = None
if is_net_node(node_two):
if isinstance(node_two, CoreNetworkBase):
if not net_one:
net_one = node_two
else:
@ -203,9 +261,7 @@ class Session(object):
raise CoreError("no common network found for wireless link/unlink")
for common_network, interface_one, interface_two in common_networks:
if not nodeutils.is_node(
common_network, [NodeTypes.WIRELESS_LAN, NodeTypes.EMANE]
):
if not isinstance(common_network, (WlanNode, EmaneNode)):
logging.info(
"skipping common network that is not wireless/emane: %s",
common_network,
@ -268,9 +324,8 @@ class Session(object):
node_one.name,
node_two.name,
)
ptp_class = nodeutils.get_node_class(NodeTypes.PEER_TO_PEER)
start = self.state > EventTypes.DEFINITION_STATE.value
net_one = self.create_node(cls=ptp_class, start=start)
net_one = self.create_node(cls=PtpNet, start=start)
# node to network
if node_one and net_one:
@ -300,7 +355,7 @@ class Session(object):
net_one.name,
net_two.name,
)
if nodeutils.is_node(net_two, NodeTypes.RJ45):
if isinstance(net_two, Rj45Node):
interface = net_two.linknet(net_one)
else:
interface = net_one.linknet(net_two)
@ -324,12 +379,12 @@ class Session(object):
# tunnel node logic
key = link_options.key
if key and nodeutils.is_node(net_one, NodeTypes.TUNNEL):
if key and isinstance(net_one, TunnelNode):
logging.info("setting tunnel key for: %s", net_one.name)
net_one.setkey(key)
if addresses:
net_one.addrconfig(addresses)
if key and nodeutils.is_node(net_two, NodeTypes.TUNNEL):
if key and isinstance(net_two, TunnelNode):
logging.info("setting tunnel key for: %s", net_two.name)
net_two.setkey(key)
if addresses:
@ -337,14 +392,14 @@ class Session(object):
# physical node connected with tunnel
if not net_one and not net_two and (node_one or node_two):
if node_one and nodeutils.is_node(node_one, NodeTypes.PHYSICAL):
if node_one and isinstance(node_one, PhysicalNode):
logging.info("adding link for physical node: %s", node_one.name)
addresses = interface_one.get_addresses()
node_one.adoptnetif(
tunnel, interface_one.id, interface_one.mac, addresses
)
link_config(node_one, tunnel, link_options)
elif node_two and nodeutils.is_node(node_two, NodeTypes.PHYSICAL):
elif node_two and isinstance(node_two, PhysicalNode):
logging.info("adding link for physical node: %s", node_two.name)
addresses = interface_two.get_addresses()
node_two.adoptnetif(
@ -584,14 +639,11 @@ class Session(object):
:param int _id: id for node, defaults to None for generated id
:param core.emulator.emudata.NodeOptions node_options: data to create node with
:return: created node
:raises core.CoreError: when an invalid node type is given
"""
# retrieve node class for given node type
try:
node_class = nodeutils.get_node_class(_type)
except KeyError:
logging.error("invalid node type to create: %s", _type)
return None
# validate node type, get class, or throw error
node_class = self.get_node_class(_type)
# set node start based on current session state, override and check when rj45
start = self.state > EventTypes.DEFINITION_STATE.value
@ -651,10 +703,8 @@ class Session(object):
logging.debug("set node type: %s", node.type)
self.services.add_services(node, node.type, node_options.services)
# boot nodes if created after runtime, LcxNodes, Physical, and RJ45 are all PyCoreNodes
is_boot_node = isinstance(node, CoreNodeBase) and not nodeutils.is_node(
node, NodeTypes.RJ45
)
# boot nodes if created after runtime, CoreNodes, Physical, and RJ45 are all nodes
is_boot_node = isinstance(node, CoreNodeBase) and not isinstance(node, Rj45Node)
if self.state == EventTypes.RUNTIME_STATE.value and is_boot_node:
self.write_nodes()
self.add_remove_control_interface(node=node, remove=False)
@ -1441,12 +1491,10 @@ class Session(object):
count = 0
for node_id in self.nodes:
node = self.nodes[node_id]
is_p2p_ctrlnet = nodeutils.is_node(
node, (NodeTypes.PEER_TO_PEER, NodeTypes.CONTROL_NET)
is_p2p_ctrlnet = isinstance(node, (PtpNet, CtrlNet))
is_tap = isinstance(node, GreTapBridge) and not isinstance(
node, TunnelNode
)
is_tap = nodeutils.is_node(
node, NodeTypes.TAP_BRIDGE
) and not nodeutils.is_node(node, NodeTypes.TUNNEL)
if is_p2p_ctrlnet or is_tap:
continue
@ -1493,7 +1541,7 @@ class Session(object):
for node_id in self.nodes:
node = self.nodes[node_id]
# TODO: determine if checking for CoreNode alone is ok
if isinstance(node, core.nodes.base.CoreNodeBase):
if isinstance(node, CoreNodeBase):
self.services.stop_services(node)
# shutdown emane
@ -1546,10 +1594,7 @@ class Session(object):
start = time.time()
for _id in self.nodes:
node = self.nodes[_id]
# TODO: PyCoreNode is not the type to check
if isinstance(node, CoreNodeBase) and not nodeutils.is_node(
node, NodeTypes.RJ45
):
if isinstance(node, CoreNodeBase) and not isinstance(node, Rj45Node):
# add a control interface if configured
logging.info(
"booting node(%s): %s",
@ -1648,8 +1693,7 @@ class Session(object):
# no controlnet needed
return None
else:
control_net_class = nodeutils.get_node_class(NodeTypes.CONTROL_NET)
prefix_spec = control_net_class.DEFAULT_PREFIX_LIST[net_index]
prefix_spec = CtrlNet.DEFAULT_PREFIX_LIST[net_index]
logging.debug("prefix spec: %s", prefix_spec)
server_interface = self.get_control_net_server_interfaces()[net_index]
@ -1725,9 +1769,8 @@ class Session(object):
prefix = prefixes[0]
logging.info("controlnet prefix: %s - %s", type(prefix), prefix)
control_net_class = nodeutils.get_node_class(NodeTypes.CONTROL_NET)
control_net = self.create_node(
cls=control_net_class,
cls=CtrlNet,
_id=_id,
prefix=prefix,
assign_address=assign_address,

View file

@ -17,8 +17,9 @@ from socket import AF_INET, AF_INET6
from core import CoreCommandError, constants, utils
from core.emulator.data import LinkData, NodeData
from core.emulator.enumerations import LinkTypes, NodeTypes
from core.nodes import client, ipaddress, nodeutils
from core.nodes import client, ipaddress
from core.nodes.interface import CoreInterface, TunTap, Veth
from core.nodes.netclient import LinuxNetClient, OvsNetClient
_DEFAULT_MTU = 1500
@ -886,8 +887,8 @@ class CoreNode(CoreNodeBase):
addrlist = []
with self.lock:
# TODO: see if you can move this to emane specific code
if nodeutils.is_node(net, NodeTypes.EMANE):
# TODO: emane specific code
if net.is_emane is True:
ifindex = self.newtuntap(ifindex=ifindex, ifname=ifname, net=net)
# TUN/TAP is not ready for addressing yet; the device may
# take some time to appear, and installing it into a
@ -1050,6 +1051,7 @@ class CoreNetworkBase(NodeBase):
"""
linktype = LinkTypes.WIRED.value
is_emane = False
def __init__(self, session, _id, name, start=True):
"""
@ -1063,6 +1065,10 @@ class CoreNetworkBase(NodeBase):
super(CoreNetworkBase, self).__init__(session, _id, name, start=start)
self._linked = {}
self._linked_lock = threading.Lock()
if session.options.get_config("ovs") == "True":
self.net_client = OvsNetClient()
else:
self.net_client = LinuxNetClient()
def startup(self):
"""

View file

@ -7,8 +7,6 @@ import time
from builtins import int, range
from core import CoreCommandError, constants, utils
from core.emulator.enumerations import NodeTypes
from core.nodes import nodeutils
utils.check_executables([constants.IP_BIN])
@ -387,13 +385,12 @@ class TunTap(CoreInterface):
if result:
break
# TODO: emane specific code
# check if this is an EMANE interface; if so, continue
# waiting if EMANE is still running
# TODO: remove emane code
should_retry = count < 5
is_emane_node = nodeutils.is_node(self.net, NodeTypes.EMANE)
is_emane_running = self.node.session.emane.emanerunning(self.node)
if all([should_retry, is_emane_node, is_emane_running]):
if all([should_retry, self.net.is_emane, is_emane_running]):
count += 1
else:
raise RuntimeError("node device failed to exist")

View file

@ -0,0 +1,233 @@
"""
Clients for dealing with bridge/interface commands.
"""
import abc
import os
from future.utils import with_metaclass
from core.constants import BRCTL_BIN, IP_BIN, OVS_BIN
from core.utils import check_cmd
class NetClientBase(with_metaclass(abc.ABCMeta)):
"""
Base client for running command line bridge/interface commands.
"""
@abc.abstractmethod
def create_bridge(self, name):
"""
Create a network bridge to connect interfaces to.
:param str name: bridge name
:return: nothing
"""
pass
@abc.abstractmethod
def delete_bridge(self, name):
"""
Delete a network bridge.
:param str name: bridge name
:return: nothing
"""
pass
@abc.abstractmethod
def create_interface(self, bridge_name, interface_name):
"""
Create an interface associated with a network bridge.
:param str bridge_name: bridge name
:param str interface_name: interface name
:return: nothing
"""
pass
@abc.abstractmethod
def delete_interface(self, bridge_name, interface_name):
"""
Delete an interface associated with a network bridge.
:param str bridge_name: bridge name
:param str interface_name: interface name
:return: nothing
"""
pass
@abc.abstractmethod
def existing_bridges(self, _id):
"""
Checks if there are any existing bridges for a node.
:param _id: node id to check bridges for
"""
pass
@abc.abstractmethod
def disable_mac_learning(self, name):
"""
Disable mac learning for a bridge.
:param str name: bridge name
:return: nothing
"""
pass
class LinuxNetClient(NetClientBase):
"""
Client for creating Linux bridges and ip interfaces for nodes.
"""
def create_bridge(self, name):
"""
Create a Linux bridge and bring it up.
:param str name: bridge name
:return: nothing
"""
check_cmd([BRCTL_BIN, "addbr", name])
check_cmd([BRCTL_BIN, "stp", name, "off"])
check_cmd([BRCTL_BIN, "setfd", name, "0"])
check_cmd([IP_BIN, "link", "set", name, "up"])
# turn off multicast snooping so forwarding occurs w/o IGMP joins
snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % name
if os.path.exists(snoop):
with open(snoop, "w") as f:
f.write("0")
def delete_bridge(self, name):
"""
Bring down and delete a Linux bridge.
:param str name: bridge name
:return: nothing
"""
check_cmd([IP_BIN, "link", "set", name, "down"])
check_cmd([BRCTL_BIN, "delbr", name])
def create_interface(self, bridge_name, interface_name):
"""
Create an interface associated with a Linux bridge.
:param str bridge_name: bridge name
:param str interface_name: interface name
:return: nothing
"""
check_cmd([BRCTL_BIN, "addif", bridge_name, interface_name])
check_cmd([IP_BIN, "link", "set", interface_name, "up"])
def delete_interface(self, bridge_name, interface_name):
"""
Delete an interface associated with a Linux bridge.
:param str bridge_name: bridge name
:param str interface_name: interface name
:return: nothing
"""
check_cmd([BRCTL_BIN, "delif", bridge_name, interface_name])
def existing_bridges(self, _id):
"""
Checks if there are any existing Linux bridges for a node.
:param _id: node id to check bridges for
"""
output = check_cmd([BRCTL_BIN, "show"])
lines = output.split("\n")
for line in lines[1:]:
columns = line.split()
name = columns[0]
fields = name.split(".")
if len(fields) != 3:
continue
if fields[0] == "b" and fields[1] == _id:
return True
return False
def disable_mac_learning(self, name):
"""
Disable mac learning for a Linux bridge.
:param str name: bridge name
:return: nothing
"""
check_cmd([BRCTL_BIN, "setageing", name, "0"])
class OvsNetClient(NetClientBase):
"""
Client for creating OVS bridges and ip interfaces for nodes.
"""
def create_bridge(self, name):
"""
Create a OVS bridge and bring it up.
:param str name: bridge name
:return: nothing
"""
check_cmd([OVS_BIN, "add-br", name])
check_cmd([OVS_BIN, "set", "bridge", name, "stp_enable=false"])
check_cmd([OVS_BIN, "set", "bridge", name, "other_config:stp-max-age=6"])
check_cmd([OVS_BIN, "set", "bridge", name, "other_config:stp-forward-delay=4"])
check_cmd([IP_BIN, "link", "set", name, "up"])
def delete_bridge(self, name):
"""
Bring down and delete a OVS bridge.
:param str name: bridge name
:return: nothing
"""
check_cmd([IP_BIN, "link", "set", name, "down"])
check_cmd([OVS_BIN, "del-br", name])
def create_interface(self, bridge_name, interface_name):
"""
Create an interface associated with a network bridge.
:param str bridge_name: bridge name
:param str interface_name: interface name
:return: nothing
"""
check_cmd([OVS_BIN, "add-port", bridge_name, interface_name])
check_cmd([IP_BIN, "link", "set", interface_name, "up"])
def delete_interface(self, bridge_name, interface_name):
"""
Delete an interface associated with a OVS bridge.
:param str bridge_name: bridge name
:param str interface_name: interface name
:return: nothing
"""
check_cmd([OVS_BIN, "del-port", bridge_name, interface_name])
def existing_bridges(self, _id):
"""
Checks if there are any existing OVS bridges for a node.
:param _id: node id to check bridges for
"""
output = check_cmd([OVS_BIN, "list-br"])
if output:
for line in output.split("\n"):
fields = line.split(".")
if fields[0] == "b" and fields[1] == _id:
return True
return False
def disable_mac_learning(self, name):
"""
Disable mac learning for a OVS bridge.
:param str name: bridge name
:return: nothing
"""
check_cmd([OVS_BIN, "set", "bridge", name, "other_config:mac-aging-time=0"])

View file

@ -9,7 +9,7 @@ import threading
import time
from socket import AF_INET, AF_INET6
from core import CoreCommandError, constants, utils
from core import CoreCommandError, CoreError, constants, utils
from core.emulator.data import LinkData
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
from core.nodes import ipaddress
@ -314,12 +314,8 @@ class CoreNetwork(CoreNetworkBase):
:return: nothing
:raises CoreCommandError: when there is a command exception
"""
utils.check_cmd([constants.BRCTL_BIN, "addbr", self.brname])
self.net_client.create_bridge(self.brname)
# turn off spanning tree protocol and forwarding delay
utils.check_cmd([constants.BRCTL_BIN, "stp", self.brname, "off"])
utils.check_cmd([constants.BRCTL_BIN, "setfd", self.brname, "0"])
utils.check_cmd([constants.IP_BIN, "link", "set", self.brname, "up"])
# create a new ebtables chain for this bridge
ebtablescmds(
utils.check_cmd,
@ -336,11 +332,6 @@ class CoreNetwork(CoreNetworkBase):
],
],
)
# turn off multicast snooping so mcast forwarding occurs w/o IGMP joins
snoop = "/sys/devices/virtual/net/%s/bridge/multicast_snooping" % self.brname
if os.path.exists(snoop):
with open(snoop, "w") as snoop_file:
snoop_file.write("0")
self.up = True
@ -356,8 +347,7 @@ class CoreNetwork(CoreNetworkBase):
ebq.stopupdateloop(self)
try:
utils.check_cmd([constants.IP_BIN, "link", "set", self.brname, "down"])
utils.check_cmd([constants.BRCTL_BIN, "delbr", self.brname])
self.net_client.delete_bridge(self.brname)
ebtablescmds(
utils.check_cmd,
[
@ -385,7 +375,8 @@ class CoreNetwork(CoreNetworkBase):
del self.session
self.up = False
# TODO: this depends on a subtype with localname defined, seems like the wrong place for this to live
# TODO: this depends on a subtype with localname defined, seems like the
# wrong place for this to live
def attach(self, netif):
"""
Attach a network interface.
@ -394,10 +385,7 @@ class CoreNetwork(CoreNetworkBase):
:return: nothing
"""
if self.up:
utils.check_cmd(
[constants.BRCTL_BIN, "addif", self.brname, netif.localname]
)
utils.check_cmd([constants.IP_BIN, "link", "set", netif.localname, "up"])
self.net_client.create_interface(self.brname, netif.localname)
CoreNetworkBase.attach(self, netif)
@ -409,9 +397,7 @@ class CoreNetwork(CoreNetworkBase):
:return: nothing
"""
if self.up:
utils.check_cmd(
[constants.BRCTL_BIN, "delif", self.brname, netif.localname]
)
self.net_client.delete_interface(self.brname, netif.localname)
CoreNetworkBase.detach(self, netif)
@ -610,10 +596,8 @@ class CoreNetwork(CoreNetworkBase):
)
self.attach(netif)
if net.up:
# this is similar to net.attach() but uses netif.name instead
# of localname
utils.check_cmd([constants.BRCTL_BIN, "addif", net.brname, netif.name])
utils.check_cmd([constants.IP_BIN, "link", "set", netif.name, "up"])
# this is similar to net.attach() but uses netif.name instead of localname
self.net_client.create_interface(net.brname, netif.name)
i = net.newifindex()
net._netif[i] = netif
with net._linked_lock:
@ -822,8 +806,8 @@ class CtrlNet(CoreNetwork):
:return: nothing
:raises CoreCommandError: when there is a command exception
"""
if self.detectoldbridge():
return
if self.net_client.existing_bridges(self.id):
raise CoreError("old bridges exist for node: %s" % self.id)
CoreNetwork.startup(self)
@ -848,42 +832,7 @@ class CtrlNet(CoreNetwork):
utils.check_cmd([self.updown_script, self.brname, "startup"])
if self.serverintf:
# sets the interface as a port of the bridge
utils.check_cmd(
[constants.BRCTL_BIN, "addif", self.brname, self.serverintf]
)
# bring interface up
utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
def detectoldbridge(self):
"""
Occasionally, control net bridges from previously closed sessions are not cleaned up.
Check if there are old control net bridges and delete them
:return: True if an old bridge was detected, False otherwise
:rtype: bool
"""
status, output = utils.cmd_output([constants.BRCTL_BIN, "show"])
if status != 0:
logging.error("Unable to retrieve list of installed bridges")
else:
lines = output.split("\n")
for line in lines[1:]:
cols = line.split("\t")
oldbr = cols[0]
flds = cols[0].split(".")
if len(flds) == 3:
if flds[0] == "b" and flds[1] == self.id:
logging.error(
"error: An active control net bridge (%s) found. "
"An older session might still be running. "
"Stop all sessions and, if needed, delete %s to continue.",
oldbr,
oldbr,
)
return True
return False
self.net_client.create_interface(self.brname, self.serverintf)
def shutdown(self):
"""
@ -893,9 +842,7 @@ class CtrlNet(CoreNetwork):
"""
if self.serverintf is not None:
try:
utils.check_cmd(
[constants.BRCTL_BIN, "delif", self.brname, self.serverintf]
)
self.net_client.delete_interface(self.brname, self.serverintf)
except CoreCommandError:
logging.exception(
"error deleting server interface %s from bridge %s",
@ -1100,7 +1047,7 @@ class HubNode(CoreNetwork):
# TODO: move to startup method
if start:
utils.check_cmd([constants.BRCTL_BIN, "setageing", self.brname, "0"])
self.net_client.disable_mac_learning(self.brname)
class WlanNode(CoreNetwork):
@ -1131,7 +1078,7 @@ class WlanNode(CoreNetwork):
# TODO: move to startup method
if start:
utils.check_cmd([constants.BRCTL_BIN, "setageing", self.brname, "0"])
self.net_client.disable_mac_learning(self.brname)
def attach(self, netif):
"""

View file

@ -1,32 +0,0 @@
"""
Provides default node maps that can be used to run core with.
"""
import core.nodes.base
import core.nodes.docker
import core.nodes.lxd
import core.nodes.network
import core.nodes.physical
from core.emane.nodes import EmaneNet, EmaneNode
from core.emulator.enumerations import NodeTypes
from core.nodes import physical
from core.nodes.network import GreTapBridge
# legacy core nodes, that leverage linux bridges
NODES = {
NodeTypes.DEFAULT: core.nodes.base.CoreNode,
NodeTypes.PHYSICAL: physical.PhysicalNode,
NodeTypes.TBD: None,
NodeTypes.SWITCH: core.nodes.network.SwitchNode,
NodeTypes.HUB: core.nodes.network.HubNode,
NodeTypes.WIRELESS_LAN: core.nodes.network.WlanNode,
NodeTypes.RJ45: core.nodes.physical.Rj45Node,
NodeTypes.TUNNEL: core.nodes.network.TunnelNode,
NodeTypes.KTUNNEL: None,
NodeTypes.EMANE: EmaneNode,
NodeTypes.EMANE_NET: EmaneNet,
NodeTypes.TAP_BRIDGE: GreTapBridge,
NodeTypes.PEER_TO_PEER: core.nodes.network.PtpNet,
NodeTypes.CONTROL_NET: core.nodes.network.CtrlNet,
NodeTypes.DOCKER: core.nodes.docker.DockerNode,
NodeTypes.LXC: core.nodes.lxd.LxcNode,
}

View file

@ -1,97 +0,0 @@
"""
Serves as a global point for storing and retrieving node types needed during simulation.
"""
import logging
_NODE_MAP = None
def _log_map():
global _NODE_MAP
for key in _NODE_MAP:
value = _NODE_MAP[key]
name = None
if value:
name = value.__name__
logging.debug("node type (%s) - class (%s)", key.name, name)
def _convert_map(x, y):
"""
Convenience method to create a human readable version of the node map to log.
:param dict x: dictionary to reduce node items into
:param tuple y: current node item
:return: human readable name mapping of the node map
"""
x[y[0].name] = y[1]
return x
def update_node_map(node_map):
"""
Update the current node map with the provided node map values.
:param dict node_map: node map to update with
"""
global _NODE_MAP
_NODE_MAP.update(node_map)
_log_map()
def set_node_map(node_map):
"""
Set the global node map that proides a consistent way to retrieve differently configured nodes.
:param dict node_map: node map to set to
:return: nothing
"""
global _NODE_MAP
_NODE_MAP = node_map
_log_map()
def get_node_class(node_type):
"""
Retrieve the node class for a given node type.
:param int node_type: node type to retrieve class for
:return: node class
"""
global _NODE_MAP
return _NODE_MAP[node_type]
def get_node_type(node_class):
"""
Retrieve the node type given a node class.
:param class node_class: node class to get type for
:return: node type
:rtype: core.emulator.enumerations.NodeTypes
"""
global _NODE_MAP
node_type_map = {_NODE_MAP[x]: x for x in _NODE_MAP}
return node_type_map.get(node_class)
def is_node(obj, node_types):
"""
Validates if an object is one of the provided node types.
:param obj: object to check type for
:param int|tuple|list node_types: node type(s) to check against
:return: True if the object is one of the node types, False otherwise
:rtype: bool
"""
type_classes = []
if isinstance(node_types, (tuple, list)):
for node_type in node_types:
type_class = get_node_class(node_type)
type_classes.append(type_class)
else:
type_class = get_node_class(node_types)
type_classes.append(type_class)
return isinstance(obj, tuple(type_classes))

View file

@ -1,825 +0,0 @@
"""
TODO: probably goes away, or implement the usage of "unshare", or docker formal.
"""
import logging
import socket
import threading
from socket import AF_INET, AF_INET6
from core import CoreCommandError, constants, utils
from core.emulator.data import LinkData
from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs
from core.nodes import ipaddress
from core.nodes.base import CoreNetworkBase
from core.nodes.interface import GreTap, Veth
from core.nodes.network import EbtablesQueue, GreTapBridge
# a global object because all WLANs share the same queue
# cannot have multiple threads invoking the ebtables commnd
ebtables_queue = EbtablesQueue()
ebtables_lock = threading.Lock()
utils.check_executables([constants.IP_BIN, constants.EBTABLES_BIN, constants.TC_BIN])
def ebtables_commands(call, commands):
with ebtables_lock:
for command in commands:
call(command)
class OvsNet(CoreNetworkBase):
"""
Used to be LxBrNet.
Base class for providing Openvswitch functionality to objects that create bridges.
"""
policy = "DROP"
def __init__(self, session, _id=None, name=None, start=True, policy=None):
"""
Creates an OvsNet instance.
:param core.emulator.session.Session session: session this object is a part of
:param int _id: object id
:param str name: object name
:param bool start: start flag
:param policy: network policy
"""
CoreNetworkBase.__init__(self, session, _id, name, start)
if policy:
self.policy = policy
else:
self.policy = self.__class__.policy
session_id = self.session.short_session_id()
self.bridge_name = "b.%s.%s" % (str(self.id), session_id)
self.up = False
if start:
self.startup()
ebtables_queue.startupdateloop(self)
def startup(self):
"""
:return:
:raises CoreCommandError: when there is a command exception
"""
utils.check_cmd([constants.OVS_BIN, "add-br", self.bridge_name])
# turn off spanning tree protocol and forwarding delay
# TODO: appears stp and rstp are off by default, make sure this always holds true
# TODO: apears ovs only supports rstp forward delay and again it's off by default
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "up"])
# create a new ebtables chain for this bridge
ebtables_commands(
utils.check_cmd,
[
[constants.EBTABLES_BIN, "-N", self.bridge_name, "-P", self.policy],
[
constants.EBTABLES_BIN,
"-A",
"FORWARD",
"--logical-in",
self.bridge_name,
"-j",
self.bridge_name,
],
],
)
self.up = True
def shutdown(self):
if not self.up:
logging.info("exiting shutdown, object is not up")
return
ebtables_queue.stopupdateloop(self)
try:
utils.check_cmd([constants.IP_BIN, "link", "set", self.bridge_name, "down"])
utils.check_cmd([constants.OVS_BIN, "del-br", self.bridge_name])
ebtables_commands(
utils.check_cmd,
[
[
constants.EBTABLES_BIN,
"-D",
"FORWARD",
"--logical-in",
self.bridge_name,
"-j",
self.bridge_name,
],
[constants.EBTABLES_BIN, "-X", self.bridge_name],
],
)
except CoreCommandError:
logging.exception("error bringing bridge down and removing it")
# removes veth pairs used for bridge-to-bridge connections
for interface in self.netifs():
interface.shutdown()
self._netif.clear()
self._linked.clear()
del self.session
self.up = False
def attach(self, interface):
if self.up:
utils.check_cmd(
[constants.OVS_BIN, "add-port", self.bridge_name, interface.localname]
)
utils.check_cmd(
[constants.IP_BIN, "link", "set", interface.localname, "up"]
)
CoreNetworkBase.attach(self, interface)
def detach(self, interface):
if self.up:
utils.check_cmd(
[constants.OVS_BIN, "del-port", self.bridge_name, interface.localname]
)
CoreNetworkBase.detach(self, interface)
def linked(self, interface_one, interface_two):
# check if the network interfaces are attached to this network
if self._netif[interface_one.netifi] != interface_one:
raise ValueError("inconsistency for interface %s" % interface_one.name)
if self._netif[interface_two.netifi] != interface_two:
raise ValueError("inconsistency for interface %s" % interface_two.name)
try:
linked = self._linked[interface_one][interface_two]
except KeyError:
if self.policy == "ACCEPT":
linked = True
elif self.policy == "DROP":
linked = False
else:
raise ValueError("unknown policy: %s" % self.policy)
self._linked[interface_one][interface_two] = linked
return linked
def unlink(self, interface_one, interface_two):
"""
Unlink two PyCoreNetIfs, resulting in adding or removing ebtables
filtering rules.
"""
with self._linked_lock:
if not self.linked(interface_one, interface_two):
return
self._linked[interface_one][interface_two] = False
ebtables_queue.ebchange(self)
def link(self, interface_one, interface_two):
"""
Link two interfaces together, resulting in adding or removing
ebtables filtering rules.
"""
with self._linked_lock:
if self.linked(interface_one, interface_two):
return
self._linked[interface_one][interface_two] = True
ebtables_queue.ebchange(self)
def linkconfig(
self,
netif,
bw=None,
delay=None,
loss=None,
duplicate=None,
jitter=None,
netif2=None,
devname=None,
):
"""
Configure link parameters by applying tc queuing disciplines on the
interface.
"""
if not devname:
devname = netif.localname
tc = [constants.TC_BIN, "qdisc", "replace", "dev", devname]
parent = ["root"]
# attempt to set bandwidth and update as needed if value changed
bandwidth_changed = netif.setparam("bw", bw)
if bandwidth_changed:
# from tc-tbf(8): minimum value for burst is rate / kernel_hz
if bw > 0:
if self.up:
burst = max(2 * netif.mtu, bw / 1000)
limit = 0xFFFF # max IP payload
tbf = [
"tbf",
"rate",
str(bw),
"burst",
str(burst),
"limit",
str(limit),
]
logging.info(
"linkconfig: %s" % [tc + parent + ["handle", "1:"] + tbf]
)
utils.check_cmd(tc + parent + ["handle", "1:"] + tbf)
netif.setparam("has_tbf", True)
elif netif.getparam("has_tbf") and bw <= 0:
tcd = [] + tc
tcd[2] = "delete"
if self.up:
utils.check_cmd(tcd + parent)
netif.setparam("has_tbf", False)
# removing the parent removes the child
netif.setparam("has_netem", False)
if netif.getparam("has_tbf"):
parent = ["parent", "1:1"]
netem = ["netem"]
delay_changed = netif.setparam("delay", delay)
if loss is not None:
loss = float(loss)
loss_changed = netif.setparam("loss", loss)
if duplicate is not None:
duplicate = int(duplicate)
duplicate_changed = netif.setparam("duplicate", duplicate)
jitter_changed = netif.setparam("jitter", jitter)
# if nothing changed return
if not any(
[
bandwidth_changed,
delay_changed,
loss_changed,
duplicate_changed,
jitter_changed,
]
):
return
# jitter and delay use the same delay statement
if delay is not None:
netem += ["delay", "%sus" % delay]
else:
netem += ["delay", "0us"]
if jitter is not None:
netem += ["%sus" % jitter, "25%"]
if loss is not None and loss > 0:
netem += ["loss", "%s%%" % min(loss, 100)]
if duplicate is not None and duplicate > 0:
netem += ["duplicate", "%s%%" % min(duplicate, 100)]
if delay <= 0 and jitter <= 0 and loss <= 0 and duplicate <= 0:
# possibly remove netem if it exists and parent queue wasn"t removed
if not netif.getparam("has_netem"):
return
tc[2] = "delete"
if self.up:
logging.info("linkconfig: %s" % ([tc + parent + ["handle", "10:"]],))
utils.check_cmd(tc + parent + ["handle", "10:"])
netif.setparam("has_netem", False)
elif len(netem) > 1:
if self.up:
logging.info(
"linkconfig: %s" % ([tc + parent + ["handle", "10:"] + netem],)
)
utils.check_cmd(tc + parent + ["handle", "10:"] + netem)
netif.setparam("has_netem", True)
def linknet(self, network):
"""
Link this bridge with another by creating a veth pair and installing
each device into each bridge.
"""
session_id = self.session.short_session_id()
try:
_id = "%x" % self.id
except TypeError:
_id = "%s" % self.id
try:
network_id = "%x" % network.id
except TypeError:
network_id = "%s" % network.id
localname = "veth%s.%s.%s" % (_id, network_id, session_id)
if len(localname) >= 16:
raise ValueError("interface local name %s too long" % localname)
name = "veth%s.%s.%s" % (network_id, _id, session_id)
if len(name) >= 16:
raise ValueError("interface name %s too long" % name)
interface = Veth(
node=None, name=name, localname=localname, mtu=1500, net=self, start=self.up
)
self.attach(interface)
if network.up:
# this is similar to net.attach() but uses netif.name instead of localname
utils.check_cmd(
[constants.OVS_BIN, "add-port", network.bridge_name, interface.name]
)
utils.check_cmd([constants.IP_BIN, "link", "set", interface.name, "up"])
network.attach(interface)
interface.net = self
interface.othernet = network
return interface
def getlinknetif(self, network):
"""
Return the interface of that links this net with another net
(that were linked using linknet()).
"""
for interface in self.netifs():
if hasattr(interface, "othernet") and interface.othernet == network:
return interface
return None
def addrconfig(self, addresses):
"""
Set addresses on the bridge.
"""
if not self.up:
return
for address in addresses:
utils.check_cmd(
[constants.IP_BIN, "addr", "add", str(address), "dev", self.bridge_name]
)
class OvsCtrlNet(OvsNet):
policy = "ACCEPT"
CTRLIF_IDX_BASE = 99 # base control interface index
DEFAULT_PREFIX_LIST = [
"172.16.0.0/24 172.16.1.0/24 172.16.2.0/24 172.16.3.0/24 172.16.4.0/24",
"172.17.0.0/24 172.17.1.0/24 172.17.2.0/24 172.17.3.0/24 172.17.4.0/24",
"172.18.0.0/24 172.18.1.0/24 172.18.2.0/24 172.18.3.0/24 172.18.4.0/24",
"172.19.0.0/24 172.19.1.0/24 172.19.2.0/24 172.19.3.0/24 172.19.4.0/24",
]
def __init__(
self,
session,
_id="ctrlnet",
name=None,
prefix=None,
hostid=None,
start=True,
assign_address=True,
updown_script=None,
serverintf=None,
):
self.prefix = ipaddress.Ipv4Prefix(prefix)
self.hostid = hostid
self.assign_address = assign_address
self.updown_script = updown_script
self.serverintf = serverintf
OvsNet.__init__(self, session, _id=_id, name=name, start=start)
def startup(self):
if self.detectoldbridge():
return
OvsNet.startup(self)
if self.hostid:
addr = self.prefix.addr(self.hostid)
else:
addr = self.prefix.max_addr()
message = "Added control network bridge: %s %s" % (
self.bridge_name,
self.prefix,
)
addresses = ["%s/%s" % (addr, self.prefix.prefixlen)]
if self.assign_address:
self.addrconfig(addresses=addresses)
message += " address %s" % addr
logging.info(message)
if self.updown_script:
logging.info(
"interface %s updown script %s startup called"
% (self.bridge_name, self.updown_script)
)
utils.check_cmd([self.updown_script, self.bridge_name, "startup"])
if self.serverintf:
utils.check_cmd(
[constants.OVS_BIN, "add-port", self.bridge_name, self.serverintf]
)
utils.check_cmd([constants.IP_BIN, "link", "set", self.serverintf, "up"])
def detectoldbridge(self):
"""
Occasionally, control net bridges from previously closed sessions are not cleaned up.
Check if there are old control net bridges and delete them
"""
output = utils.check_cmd([constants.OVS_BIN, "list-br"])
output = output.strip()
if output:
for line in output.split("\n"):
bride_name = line.split(".")
if bride_name[0] == "b" and bride_name[1] == self.id:
logging.error(
"older session may still be running with conflicting id for bridge: %s",
line,
)
return True
return False
def shutdown(self):
if self.serverintf:
try:
utils.check_cmd(
[constants.OVS_BIN, "del-port", self.bridge_name, self.serverintf]
)
except CoreCommandError:
logging.exception(
"error deleting server interface %s to controlnet bridge %s",
self.serverintf,
self.bridge_name,
)
if self.updown_script:
try:
logging.info(
"interface %s updown script (%s shutdown) called",
self.bridge_name,
self.updown_script,
)
utils.check_cmd([self.updown_script, self.bridge_name, "shutdown"])
except CoreCommandError:
logging.exception("error during updown script shutdown")
OvsNet.shutdown(self)
def all_link_data(self, flags):
"""
Do not include CtrlNet in link messages describing this session.
"""
return []
class OvsPtpNet(OvsNet):
policy = "ACCEPT"
def attach(self, interface):
if len(self._netif) >= 2:
raise ValueError(
"point-to-point links support at most 2 network interfaces"
)
OvsNet.attach(self, interface)
def data(self, message_type, lat=None, lon=None, alt=None):
"""
Do not generate a Node Message for point-to-point links. They are
built using a link message instead.
"""
return None
def all_link_data(self, flags):
"""
Build CORE API TLVs for a point-to-point link. One Link message describes this network.
"""
all_links = []
if len(self._netif) != 2:
return all_links
if1, if2 = self._netif.values()
unidirectional = 0
if if1.getparams() != if2.getparams():
unidirectional = 1
interface1_ip4 = None
interface1_ip4_mask = None
interface1_ip6 = None
interface1_ip6_mask = None
for address in if1.addrlist:
ip, _sep, mask = address.partition("/")
mask = int(mask)
if ipaddress.is_ipv4_address(ip):
family = AF_INET
ipl = socket.inet_pton(family, ip)
interface1_ip4 = ipaddress.IpAddress(af=family, address=ipl)
interface1_ip4_mask = mask
else:
family = AF_INET6
ipl = socket.inet_pton(family, ip)
interface1_ip6 = ipaddress.IpAddress(af=family, address=ipl)
interface1_ip6_mask = mask
interface2_ip4 = None
interface2_ip4_mask = None
interface2_ip6 = None
interface2_ip6_mask = None
for address in if2.addrlist:
ip, _sep, mask = address.partition("/")
mask = int(mask)
if ipaddress.is_ipv4_address(ip):
family = AF_INET
ipl = socket.inet_pton(family, ip)
interface2_ip4 = ipaddress.IpAddress(af=family, address=ipl)
interface2_ip4_mask = mask
else:
family = AF_INET6
ipl = socket.inet_pton(family, ip)
interface2_ip6 = ipaddress.IpAddress(af=family, address=ipl)
interface2_ip6_mask = mask
# TODO: not currently used
# loss=netif.getparam("loss")
link_data = LinkData(
message_type=flags,
node1_id=if1.node.id,
node2_id=if2.node.id,
link_type=self.linktype,
unidirectional=unidirectional,
delay=if1.getparam("delay"),
bandwidth=if1.getparam("bw"),
dup=if1.getparam("duplicate"),
jitter=if1.getparam("jitter"),
interface1_id=if1.node.getifindex(if1),
interface1_mac=if1.hwaddr,
interface1_ip4=interface1_ip4,
interface1_ip4_mask=interface1_ip4_mask,
interface1_ip6=interface1_ip6,
interface1_ip6_mask=interface1_ip6_mask,
interface2_id=if2.node.getifindex(if2),
interface2_mac=if2.hwaddr,
interface2_ip4=interface2_ip4,
interface2_ip4_mask=interface2_ip4_mask,
interface2_ip6=interface2_ip6,
interface2_ip6_mask=interface2_ip6_mask,
)
all_links.append(link_data)
# build a 2nd link message for the upstream link parameters
# (swap if1 and if2)
if unidirectional:
link_data = LinkData(
message_type=0,
node1_id=if2.node.id,
node2_id=if1.node.id,
delay=if1.getparam("delay"),
bandwidth=if1.getparam("bw"),
dup=if1.getparam("duplicate"),
jitter=if1.getparam("jitter"),
unidirectional=1,
interface1_id=if2.node.getifindex(if2),
interface2_id=if1.node.getifindex(if1),
)
all_links.append(link_data)
return all_links
class OvsSwitchNode(OvsNet):
apitype = NodeTypes.SWITCH.value
policy = "ACCEPT"
type = "lanswitch"
class OvsHubNode(OvsNet):
apitype = NodeTypes.HUB.value
policy = "ACCEPT"
type = "hub"
def __init__(self, session, _id=None, name=None, start=True):
"""
the Hub node forwards packets to all bridge ports by turning off
the MAC address learning
"""
OvsNet.__init__(self, session, _id, name, start)
if start:
# TODO: verify that the below flow accomplishes what is desired for a "HUB"
# TODO: replace "brctl setageing 0"
utils.check_cmd(
[constants.OVS_FLOW_BIN, "add-flow", self.bridge_name, "action=flood"]
)
class OvsWlanNode(OvsNet):
apitype = NodeTypes.WIRELESS_LAN.value
linktype = LinkTypes.WIRELESS.value
policy = "DROP"
type = "wlan"
def __init__(self, session, _id=None, name=None, start=True, policy=None):
OvsNet.__init__(self, session, _id, name, start, policy)
# wireless model such as basic range
self.model = None
# mobility model such as scripted
self.mobility = None
def attach(self, interface):
OvsNet.attach(self, interface)
if self.model:
interface.poshook = self.model.position_callback
if interface.node is None:
return
x, y, z = interface.node.position.get()
# invokes any netif.poshook
interface.setposition(x, y, z)
# self.model.setlinkparams()
def setmodel(self, model, config=None):
"""
Mobility and wireless model.
"""
logging.info("adding model %s", model.name)
if model.type == RegisterTlvs.WIRELESS.value:
self.model = model(session=self.session, _id=self.id, config=config)
if self.model.position_callback:
for interface in self.netifs():
interface.poshook = self.model.position_callback
if interface.node is not None:
x, y, z = interface.node.position.get()
interface.poshook(interface, x, y, z)
self.model.setlinkparams()
elif model.type == RegisterTlvs.MOBILITY.value:
self.mobility = model(session=self.session, _id=self.id, config=config)
def updatemodel(self, config):
if not self.model:
raise ValueError("no model set to update for node(%s)", self.id)
logging.info(
"node(%s) updating model(%s): %s", self.id, self.model.name, config
)
self.model.set_configs(config, node_id=self.id)
if self.model.position_callback:
for netif in self.netifs():
netif.poshook = self.model.position_callback
if netif.node is not None:
x, y, z = netif.node.position.get()
netif.poshook(netif, x, y, z)
self.model.updateconfig()
def all_link_data(self, flags):
all_links = OvsNet.all_link_data(self, flags)
if self.model:
all_links.extend(self.model.all_link_data(flags))
return all_links
class OvsTunnelNode(GreTapBridge):
apitype = NodeTypes.TUNNEL.value
policy = "ACCEPT"
type = "tunnel"
class OvsGreTapBridge(OvsNet):
"""
A network consisting of a bridge with a gretap device for tunneling to
another system.
"""
def __init__(
self,
session,
remoteip=None,
_id=None,
name=None,
policy="ACCEPT",
localip=None,
ttl=255,
key=None,
start=True,
):
OvsNet.__init__(
self, session=session, _id=_id, name=name, policy=policy, start=False
)
self.grekey = key
if self.grekey is None:
self.grekey = self.session.id ^ self.id
self.localnum = None
self.remotenum = None
self.remoteip = remoteip
self.localip = localip
self.ttl = ttl
if remoteip is None:
self.gretap = None
else:
self.gretap = GreTap(
node=self,
session=session,
remoteip=remoteip,
localip=localip,
ttl=ttl,
key=self.grekey,
)
if start:
self.startup()
def startup(self):
"""
Creates a bridge and adds the gretap device to it.
"""
OvsNet.startup(self)
if self.gretap:
self.attach(self.gretap)
def shutdown(self):
"""
Detach the gretap device and remove the bridge.
"""
if self.gretap:
self.detach(self.gretap)
self.gretap.shutdown()
self.gretap = None
OvsNet.shutdown(self)
def addrconfig(self, addresses):
"""
Set the remote tunnel endpoint. This is a one-time method for
creating the GreTap device, which requires the remoteip at startup.
The 1st address in the provided list is remoteip, 2nd optionally
specifies localip.
"""
if self.gretap:
raise ValueError("gretap already exists for %s" % self.name)
remoteip = addresses[0].split("/")[0]
localip = None
if len(addresses) > 1:
localip = addresses[1].split("/")[0]
self.gretap = GreTap(
session=self.session,
remoteip=remoteip,
localip=localip,
ttl=self.ttl,
key=self.grekey,
)
self.attach(self.gretap)
def setkey(self, key):
"""
Set the GRE key used for the GreTap device. This needs to be set
prior to instantiating the GreTap device (before addrconfig).
"""
self.grekey = key
OVS_NODES = {
NodeTypes.SWITCH: OvsSwitchNode,
NodeTypes.HUB: OvsHubNode,
NodeTypes.WIRELESS_LAN: OvsWlanNode,
NodeTypes.TUNNEL: OvsTunnelNode,
NodeTypes.TAP_BRIDGE: OvsGreTapBridge,
NodeTypes.PEER_TO_PEER: OvsPtpNet,
NodeTypes.CONTROL_NET: OvsCtrlNet,
}

View file

@ -8,6 +8,7 @@ import socket
from future.moves.urllib.parse import urlparse
from core import CoreError, constants
from core.emane.nodes import EmaneNode
from core.emulator.enumerations import (
EventTypes,
LinkTlvs,
@ -17,8 +18,8 @@ from core.emulator.enumerations import (
NodeTlvs,
NodeTypes,
)
from core.nodes import nodeutils
from core.nodes.base import CoreNetworkBase, NodeBase
from core.nodes.network import WlanNode
# TODO: A named tuple may be more appropriate, than abusing a class dict like this
@ -27,14 +28,13 @@ class Bunch(object):
Helper class for recording a collection of attributes.
"""
def __init__(self, **kwds):
def __init__(self, **kwargs):
"""
Create a Bunch instance.
:param dict kwds: keyword arguments
:return:
:param dict kwargs: keyword arguments
"""
self.__dict__.update(kwds)
self.__dict__.update(kwargs)
class Sdt(object):
@ -365,9 +365,7 @@ class Sdt(object):
for net in nets:
all_links = net.all_link_data(flags=MessageFlags.ADD.value)
for link_data in all_links:
is_wireless = nodeutils.is_node(
net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)
)
is_wireless = isinstance(net, (WlanNode, EmaneNode))
wireless_link = link_data.message_type == LinkTypes.WIRELESS.value
if is_wireless and link_data.node1_id == net.id:
continue
@ -401,7 +399,7 @@ class Sdt(object):
def handlenodemsg(self, msg):
"""
Process a Node Message to add/delete or move a node on
the SDT display. Node properties are found in session._objs or
the SDT display. Node properties are found in a session or
self.remotes for remote nodes (or those not yet instantiated).
:param msg: node message to handle
@ -430,7 +428,8 @@ class Sdt(object):
model = "router"
nodetype = model
elif nodetype is not None:
nodetype = nodeutils.get_node_class(NodeTypes(nodetype)).type
nodetype = NodeTypes(nodetype)
nodetype = self.session.get_node_class(nodetype).type
net = True
else:
nodetype = None
@ -509,6 +508,6 @@ class Sdt(object):
n = self.session.get_node(nodenum)
except CoreError:
return False
if nodeutils.is_node(n, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)):
if isinstance(n, (WlanNode, EmaneNode)):
return True
return False

View file

@ -1,5 +1,4 @@
from core.emulator.enumerations import NodeTypes
from core.nodes import nodeutils
from core.emane.nodes import EmaneNode
from core.services.coreservices import CoreService
from core.xml import emanexml
@ -22,7 +21,7 @@ class EmaneTransportService(CoreService):
transport_commands = []
for interface in node.netifs(sort=True):
network_node = node.session.get_node(interface.net.id)
if nodeutils.is_node(network_node, NodeTypes.EMANE):
if isinstance(network_node, EmaneNode):
config = node.session.emane.get_configs(
network_node.id, network_node.model.name
)

View file

@ -4,8 +4,10 @@ Assumes installation of FRR via https://deb.frrouting.org/
"""
from core import constants
from core.emulator.enumerations import LinkTypes, NodeTypes
from core.nodes import ipaddress, nodeutils
from core.emulator.enumerations import LinkTypes
from core.nodes import ipaddress
from core.nodes.network import PtpNet
from core.nodes.physical import Rj45Node
from core.services.coreservices import CoreService
@ -341,7 +343,7 @@ class FrrService(CoreService):
for peerifc in ifc.net.netifs():
if peerifc == ifc:
continue
if nodeutils.is_node(peerifc, NodeTypes.RJ45):
if isinstance(peerifc, Rj45Node):
return True
return False
@ -395,7 +397,7 @@ class FRROspfv2(FrrService):
Helper to detect whether interface is connected to a notional
point-to-point link.
"""
if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER):
if isinstance(ifc.net, PtpNet):
return " ip ospf network point-to-point\n"
return ""
@ -481,7 +483,7 @@ class FRROspfv3(FrrService):
Helper to detect whether interface is connected to a notional
point-to-point link.
"""
if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER):
if isinstance(ifc.net, PtpNet):
return " ipv6 ospf6 network point-to-point\n"
return ""

View file

@ -3,8 +3,11 @@ quagga.py: defines routing services provided by Quagga.
"""
from core import constants
from core.emulator.enumerations import LinkTypes, NodeTypes
from core.nodes import ipaddress, nodeutils
from core.emane.nodes import EmaneNode
from core.emulator.enumerations import LinkTypes
from core.nodes import ipaddress
from core.nodes.network import PtpNet, WlanNode
from core.nodes.physical import Rj45Node
from core.services.coreservices import CoreService
@ -267,7 +270,7 @@ class QuaggaService(CoreService):
for peerifc in ifc.net.netifs():
if peerifc == ifc:
continue
if nodeutils.is_node(peerifc, NodeTypes.RJ45):
if isinstance(peerifc, Rj45Node):
return True
return False
@ -321,7 +324,7 @@ class Ospfv2(QuaggaService):
Helper to detect whether interface is connected to a notional
point-to-point link.
"""
if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER):
if isinstance(ifc.net, PtpNet):
return " ip ospf network point-to-point\n"
return ""
@ -407,7 +410,7 @@ class Ospfv3(QuaggaService):
Helper to detect whether interface is connected to a notional
point-to-point link.
"""
if nodeutils.is_node(ifc.net, NodeTypes.PEER_TO_PEER):
if isinstance(ifc.net, PtpNet):
return " ipv6 ospf6 network point-to-point\n"
return ""
@ -457,9 +460,7 @@ class Ospfv3mdr(Ospfv3):
cfg = cls.mtucheck(ifc)
# Uncomment the following line to use Address Family Translation for IPv4
cfg += " ipv6 ospf6 instance-id 65\n"
if ifc.net is not None and nodeutils.is_node(
ifc.net, (NodeTypes.WIRELESS_LAN, NodeTypes.EMANE)
):
if ifc.net is not None and isinstance(ifc.net, (WlanNode, EmaneNode)):
return (
cfg
+ """\

View file

@ -4,11 +4,12 @@ from lxml import etree
import core.nodes.base
import core.nodes.physical
from core.emane.nodes import EmaneNode
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
from core.emulator.enumerations import NodeTypes
from core.nodes import nodeutils
from core.nodes.base import CoreNetworkBase
from core.nodes.ipaddress import MacAddress
from core.nodes.network import CtrlNet
def write_xml_file(xml_element, file_path, doctype=None):
@ -408,7 +409,7 @@ class CoreXmlWriter(object):
is_network_or_rj45 = isinstance(
node, (core.nodes.base.CoreNetworkBase, core.nodes.physical.Rj45Node)
)
is_controlnet = nodeutils.is_node(node, NodeTypes.CONTROL_NET)
is_controlnet = isinstance(node, CtrlNet)
if is_network_or_rj45 and not is_controlnet:
self.write_network(node)
# device node
@ -457,7 +458,7 @@ class CoreXmlWriter(object):
interface_name = node_interface.name
# check if emane interface
if nodeutils.is_node(node_interface.net, NodeTypes.EMANE):
if isinstance(node_interface.net, EmaneNode):
nem = node_interface.net.getnemid(node_interface)
add_attribute(interface, "nem", nem)

View file

@ -4,8 +4,8 @@ import socket
from lxml import etree
from core import constants, utils
from core.emulator.enumerations import NodeTypes
from core.nodes import ipaddress, nodeutils
from core.emane.nodes import EmaneNode
from core.nodes import ipaddress
from core.nodes.base import CoreNodeBase
@ -144,7 +144,7 @@ class CoreXmlDeployment(object):
for netif in node.netifs():
emane_element = None
if nodeutils.is_node(netif.net, NodeTypes.EMANE):
if isinstance(netif.net, EmaneNode):
emane_element = add_emane_interface(host_element, netif)
parent_element = host_element

View file

@ -1,4 +1,4 @@
configparser==4.0.1
configparser==4.0.2
future==0.17.1
grpcio==1.23.0
grpcio-tools==1.21.1

View file

@ -44,12 +44,11 @@ def start_udp(mainserver, server_address):
mainserver.udpthread.start()
def cored(cfg, use_ovs):
def cored(cfg):
"""
Start the CoreServer object and enter the server loop.
:param dict cfg: core configuration
:param bool use_ovs: flag to determine if ovs nodes should be used
:return: nothing
"""
host = cfg["listenaddr"]
@ -60,9 +59,6 @@ def cored(cfg, use_ovs):
try:
address = (host, port)
server = CoreServer(address, CoreHandler, cfg)
if use_ovs:
from core.nodes.openvswitch import OVS_NODES
server.coreemu.update_nodes(OVS_NODES)
except:
logging.exception("error starting main server on: %s:%s", host, port)
sys.exit(1)
@ -156,11 +152,8 @@ def main():
cfg = get_merged_config("%s/core.conf" % constants.CORE_CONF_DIR)
banner()
# check if ovs flag was provided
use_ovs = len(sys.argv) == 2 and sys.argv[1] == "ovs"
try:
cored(cfg, use_ovs)
cored(cfg)
except KeyboardInterrupt:
logging.info("keyboard interrupt, stopping core daemon")

View file

@ -11,7 +11,7 @@ line_length=88
[flake8]
ignore=E501,W503,E203
max-line-length=100
max-line-length=88
max-complexity=26
select=B,C,E,F,W,T4
exclude=*_pb2*.py,utm.py,doc,build

View file

@ -32,6 +32,9 @@ To leverage the dev environment you need python 3.6+.
# change to daemon directory
cd $REPO/daemon
# copy setup.py for installation
cp setup.py.in setup.py
# install pipenv
pip3 install pipenv

View file

@ -230,26 +230,26 @@ pip3 install grpcio-tools
### Ubuntu 18.04 Requirements
```shell
sudo apt install automake pkg-config gcc libev-dev bridge-utils ebtables python-dev python-setuptools tk libtk-img
sudo apt install automake pkg-config gcc libev-dev bridge-utils ebtables python-dev python-setuptools tk libtk-img ethtool
```
### Ubuntu 16.04 Requirements
```shell
sudo apt-get install automake bridge-utils ebtables python-dev libev-dev python-setuptools libtk-img
sudo apt-get install automake bridge-utils ebtables python-dev libev-dev python-setuptools libtk-img ethtool
```
### CentOS 7 with Gnome Desktop Requirements
```shell
sudo yum -y install automake gcc python-devel libev-devel tk
sudo yum -y install automake gcc python-devel libev-devel tk ethtool
```
## Build and Install
```shell
./bootstrap.sh
# use python2 or python3 depending on desired version
# $VERSION should be path to python2/3
PYTHON=$VERSION ./configure
make
sudo make install
@ -268,7 +268,7 @@ sudo apt install python3-sphinx
sudo yum install python3-sphinx
./bootstrap.sh
# use python2 or python3 depending on desired version
# $VERSION should be path to python2/3
PYTHON=$VERSION ./configure
make doc
```
@ -282,8 +282,10 @@ Build package commands, DESTDIR is used to make install into and then for packag
```shell
./bootstrap.sh
# use python2 or python3 depending on desired version
PYTHON=$VERSION ./configure
# for python2
PYTHON=python2 ./configure
# for python3
PYTHON=python3 ./configure --enable-python3
make
mkdir /tmp/core-build
make fpm DESTDIR=/tmp/core-build

View file

@ -2706,7 +2706,7 @@ proc sendNodeTypeInfo { sock reset } {
set typesinuse ""
foreach node $node_list {
set type [nodeType $node]
if { $type != "router" && $type != "OVS" } { continue }
if { $type != "router" } { continue }
set model [getNodeModel $node]
if { [lsearch $typesinuse $model] < 0 } { lappend typesinuse $model }
}
@ -2910,7 +2910,6 @@ proc getNodeTypeAPI { node } {
router { return 0x0 }
netns { return 0x0 }
jail { return 0x0 }
OVS { return 0x0 }
physical { return 0x1 }
tbd { return 0x3 }
lanswitch { return 0x4 }

View file

@ -333,7 +333,7 @@ proc redrawAll {} {
proc drawNode { c node } {
global showNodeLabels
global router pc host lanswitch rj45 hub pseudo OVS
global router pc host lanswitch rj45 hub pseudo
global curcanvas zoom
global wlan
if { $c == "" } { set c .c } ;# default canvas
@ -348,7 +348,7 @@ proc drawNode { c node } {
set cimg ""
set imgzoom $zoom
if { $zoom == 0.75 || $zoom == 1.5 } { set imgzoom 1.0 }
if { $type == "router" || $type == "OVS" } {
if { $type == "router" } {
set model [getNodeModel $node]
set cimg [getNodeTypeImage $model normal]
}
@ -1535,7 +1535,7 @@ proc raiseAll {c} {
proc button1 { c x y button } {
global node_list plot_list curcanvas zoom
global activetool activetoolp newlink curobj changed def_router_model
global router pc host lanswitch rj45 hub OVS
global router pc host lanswitch rj45 hub
global oval rectangle text
global lastX lastY
global background selectbox
@ -1607,10 +1607,7 @@ proc button1 { c x y button } {
rectangle text} $activetool] < 0 } {
if { $g_view_locked == 1 } { return }
if { $activetoolp == "routers" } {
if {$activetool != "OVS"} {
set node [newNode router]
} else {
set node [newNode OVS]}
set node [newNode router]
setNodeModel $node $activetool
} else {
set node [newNode $activetool]
@ -2550,7 +2547,7 @@ proc popupConfigDialog { c } {
-side right -padx 4 -pady 4
# end Boeing
pack $wi.ftop -side top
if { $type == "router" || $type == "OVS"} {
if { $type == "router" } {
ttk::frame $wi.model -borderwidth 4
ttk::label $wi.model.label -text "Type:"

View file

@ -108,7 +108,7 @@ proc autoIPv4addr { node iface } {
set peer_node [logicalPeerByIfc $node $iface]
# find addresses of NETWORK layer peer nodes
if { [[typemodel $peer_node].layer] == "LINK" || [nodeType $peer_node] == "OVS" } {
if { [[typemodel $peer_node].layer] == "LINK" } {
foreach l2node [listLANnodes $peer_node {}] {
foreach ifc [ifcList $l2node] {
set peer [logicalPeerByIfc $l2node $ifc]

View file

@ -747,15 +747,12 @@ proc newLink { lnode1 lnode2 } {
global defLinkColor defLinkWidth
global curcanvas
global systype
if { ([nodeType $lnode1] == "lanswitch" ||[nodeType $lnode1] == "OVS") && \
if { [nodeType $lnode1] == "lanswitch" && \
[nodeType $lnode2] != "router" && \
([nodeType $lnode2] != "lanswitch" || [nodeType $lnode2] != "OVS") } {
set regular no }
if { ([nodeType $lnode2] == "lanswitch" || [nodeType $lnode2] == "OVS") && \
[nodeType $lnode2] != "lanswitch" } { set regular no }
if { [nodeType $lnode2] == "lanswitch" && \
[nodeType $lnode1] != "router" && \
([nodeType $lnode1] != "lanswitch" || [nodeType $lnode1] != "OVS" )} {
#Khaled: puts "connecting '$lnode1' (type: '[nodeType $lnode1]') to '$lnode2' (type: '[nodeType $lnode2]') "
set regular no }
[nodeType $lnode1] != "lanswitch" } { set regular no }
if { [nodeType $lnode1] == "hub" && \
[nodeType $lnode2] == "hub" } { set regular no }
# Boeing: added tunnel, ktunnel types to behave as rj45
@ -771,7 +768,7 @@ proc newLink { lnode1 lnode2 } {
set othernode $lnode1
}
# only allowed to link with certain types
if { [lsearch {router lanswitch hub pc host wlan OVS} \
if { [lsearch {router lanswitch hub pc host wlan} \
[nodeType $othernode]] < 0} {
return
}
@ -836,18 +833,13 @@ proc newLink { lnode1 lnode2 } {
} elseif {$delay != ""} {
lappend $link "delay $delay"
}
# Exclude OVS from network layer nodes IP address asignments
if { ([[typemodel $lnode2].layer] == "NETWORK") && ([nodeType $lnode2] != "OVS") } {
#Khaled: puts "Assigning '$lnode2' (type: '[nodeType $lnode2]') an automatic IP address"
if { [[typemodel $lnode2].layer] == "NETWORK" } {
if { $ipv4_addr2 == "" } { autoIPv4addr $lnode2 $ifname2 }
if { $ipv6_addr2 == "" } { autoIPv6addr $lnode2 $ifname2 }
}
# tunnels also excluded from link settings
# OVS and Lanswitch should go side by side
} elseif { (([nodeType $lnode1] == "lanswitch" || [nodeType $lnode1] == "OVS" )|| \
([nodeType $lnode2] == "lanswitch"|| [nodeType $lnode2] == "OVS") || \
} elseif { ([nodeType $lnode1] == "lanswitch" || \
[nodeType $lnode2] == "lanswitch" || \
[string first eth "$ifname1 $ifname2"] != -1) && \
[nodeType $lnode1] != "rj45" && [nodeType $lnode2] != "rj45" && \
[nodeType $lnode1] != "tunnel" && [nodeType $lnode2] != "tunnel" && \
@ -859,13 +851,11 @@ proc newLink { lnode1 lnode2 } {
}
lappend link_list $link
# Exclude OVS from Network layer node configs
if { [nodeType $lnode2] != "pseudo" &&
[nodeType $lnode1] != "wlan" &&
([[typemodel $lnode1].layer] == "NETWORK" && [nodeType $lnode1] != "OVS") } {
[[typemodel $lnode1].layer] == "NETWORK" } {
if { $ipv4_addr1 == "" && $do_auto_addressing } {
autoIPv4addr $lnode1 $ifname1
autoIPv4addr $lnode1 $ifname1
}
if { $ipv6_addr1 == "" && $do_auto_addressing } {
autoIPv6addr $lnode1 $ifname1
@ -874,8 +864,7 @@ proc newLink { lnode1 lnode2 } {
# assume wlan is always lnode1
if { [nodeType $lnode1] != "pseudo" &&
[nodeType $lnode1] != "wlan" &&
([[typemodel $lnode2].layer] == "NETWORK" && [nodeType $lnode2] != "OVS") } {
[[typemodel $lnode2].layer] == "NETWORK" } {
if { $ipv4_addr2 == "" && $do_auto_addressing } {
autoIPv4addr $lnode2 $ifname2
}

View file

@ -21,12 +21,10 @@ array set g_node_types_default {
5 {prouter router_green.gif router_green.gif \
{zebra OSPFv2 OSPFv3 IPForward} \
physical {built-in type for physical nodes}}
6 {OVS lanswitch.gif lanswitch.gif {DefaultRoute SSH OvsService} OVS {} }
}
# possible machine types for nodes
set MACHINE_TYPES "netns physical OVS"
set MACHINE_TYPES "netns physical"
# array populated from nodes.conf file
array set g_node_types { }
@ -184,7 +182,7 @@ proc getNodeTypeServices { type } {
# node type from the toolbar
proc getNodeTypeMachineType { type } {
global MACHINE_TYPES g_node_types
set default_machine_type [lindex $MACHINE_TYPES 3]
set default_machine_type [lindex $MACHINE_TYPES 0]
set i [getNodeTypeIndex $type]
if { $i < 0 } { return $default_machine_type }; # failsafe
return [lindex $g_node_types($i) 4]
@ -208,7 +206,7 @@ proc getNodeTypeProfile { type } {
# node type from the toolbar
proc getNodeTypeMachineType { type } {
global MACHINE_TYPES g_node_types
set default_machine_type [lindex $MACHINE_TYPES 3]
set default_machine_type [lindex $MACHINE_TYPES 0]
set i [getNodeTypeIndex $type]
if { $i < 0 } { return $default_machine_type }; # failsafe
return [lindex $g_node_types($i) 4]
@ -714,7 +712,6 @@ proc lanswitch.layer {} { return LINK }
proc hub.layer {} { return LINK }
proc tunnel.layer {} { return LINK }
proc wlan.layer {} { return LINK }
proc OVS.layer {} { return NETWORK }
proc router.layer {} { return NETWORK }
proc router.shellcmd { n } { return "vtysh" }

View file

@ -9,11 +9,7 @@
if WANT_PYTHON
if PYTHON3
PYTHONLIBDIR=$(libdir)/python3/dist-packages
else
PYTHONLIBDIR=$(pythondir)
endif
PYTHONLIBDIR=$(subst site-packages,dist-packages,$(pythondir))
SETUPPY = setup.py
SETUPPYFLAGS = -v

View file

@ -10,17 +10,16 @@ import sys
import ns.core
import ns.mobility
from core.nodes import nodeutils, nodemaps, ipaddress
from corens3.obj import Ns3LteNet
from corens3.obj import Ns3Session
from core.nodes import ipaddress
def ltesession(opt):
"""
Run a test LTE session.
"""
nodeutils.set_node_map(nodemaps.NODES)
session = Ns3Session(1, persistent=True, duration=opt.duration)
lte = session.create_node(cls=Ns3LteNet, name="wlan1")
lte.setsubchannels(range(25), range(50, 100))

View file

@ -27,11 +27,11 @@ import optparse
import sys
import ns.core
from core.nodes import nodeutils, nodemaps, ipaddress
from corens3.obj import Ns3Session
from corens3.obj import Ns3WifiNet
from core.nodes import ipaddress
def add_to_server(session):
"""
@ -50,7 +50,6 @@ def wifisession(opt):
"""
Run a test wifi session.
"""
nodeutils.set_node_map(nodemaps.NODES)
session = Ns3Session(1, persistent=True, duration=opt.duration)
session.name = "ns3wifi"
session.filename = session.name + ".py"

View file

@ -14,13 +14,14 @@ How to run this:
import logging
import optparse
import sys
from builtins import range
import ns.core
import ns.network
from corens3.obj import Ns3Session
from corens3.obj import Ns3WifiNet
from core.nodes import nodeutils, nodemaps, ipaddress
from core.nodes import ipaddress
def add_to_server(session):
@ -40,7 +41,6 @@ def wifisession(opt):
"""
Run a random walk wifi session.
"""
nodeutils.set_node_map(nodemaps.NODES)
session = Ns3Session(1, persistent=True, duration=opt.duration)
session.name = "ns3wifirandomwalk"
session.filename = session.name + ".py"
@ -54,7 +54,7 @@ def wifisession(opt):
prefix = ipaddress.Ipv4Prefix("10.0.0.0/16")
services_str = "zebra|OSPFv3MDR|IPForward"
nodes = []
for i in xrange(1, opt.numnodes + 1):
for i in range(1, opt.numnodes + 1):
node = session.addnode(name="n%d" % i)
node.newnetif(wifi, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
nodes.append(node)

View file

@ -12,17 +12,18 @@ Current issues:
import logging
import optparse
import sys
from builtins import range
from core.nodes import nodeutils, nodemaps, ipaddress
from corens3.obj import Ns3Session
from corens3.obj import Ns3WimaxNet
from core.nodes import ipaddress
def wimaxsession(opt):
"""
Run a test wimax session.
"""
nodeutils.set_node_map(nodemaps.NODES)
session = Ns3Session(1, persistent=True, duration=opt.duration)
wimax = session.create_node(cls=Ns3WimaxNet, name="wlan1")
# wimax.wimax.EnableLogComponents()
@ -33,7 +34,7 @@ def wimaxsession(opt):
# classifier = (0, 65000, 0, 65000, 1, 1)
classifier = (0, 65000, 0, 65000, 17, 1)
nodes = []
for i in xrange(1, opt.numnodes + 1):
for i in range(1, opt.numnodes + 1):
node = session.addnode(name="n%d" % i)
if i == 1:
wimax.setbasestation(node)