2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2017-05-03 21:20:56 +01:00
|
|
|
Definition of LxcNode, CoreNode, and other node classes that inherit from the CoreNode,
|
|
|
|
implementing specific node types.
|
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 socket
|
|
|
|
import threading
|
|
|
|
from socket import AF_INET
|
|
|
|
from socket import AF_INET6
|
|
|
|
|
2018-03-03 00:22:20 +00:00
|
|
|
from core import CoreCommandError
|
2017-04-25 16:45:34 +01:00
|
|
|
from core import constants
|
|
|
|
from core.coreobj import PyCoreNetIf
|
2013-08-29 15:21:13 +01:00
|
|
|
from core.coreobj import PyCoreNode
|
2017-04-25 16:45:34 +01:00
|
|
|
from core.coreobj import PyCoreObj
|
|
|
|
from core.data import LinkData
|
|
|
|
from core.enumerations import LinkTypes
|
|
|
|
from core.enumerations import NodeTypes
|
|
|
|
from core.enumerations import RegisterTlvs
|
|
|
|
from core.misc import ipaddress
|
|
|
|
from core.misc import utils
|
|
|
|
from core.netns.vnet import GreTapBridge
|
|
|
|
from core.netns.vnet import LxBrNet
|
|
|
|
from core.netns.vnode import LxcNode
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
class CtrlNet(LxBrNet):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Control network functionality.
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
policy = "ACCEPT"
|
2017-04-25 16:45:34 +01:00
|
|
|
# base control interface index
|
|
|
|
CTRLIF_IDX_BASE = 99
|
|
|
|
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, objid="ctrlnet", name=None, prefix=None,
|
|
|
|
hostid=None, start=True, assign_address=True,
|
|
|
|
updown_script=None, serverintf=None):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Creates a CtrlNet instance.
|
|
|
|
|
|
|
|
:param core.session.Session session: core session instance
|
|
|
|
:param int objid: node id
|
|
|
|
:param str name: node namee
|
|
|
|
:param prefix: control network ipv4 prefix
|
|
|
|
:param hostid: host id
|
|
|
|
:param bool start: start flag
|
|
|
|
:param str assign_address: assigned address
|
|
|
|
:param str updown_script: updown script
|
|
|
|
:param serverintf: server interface
|
|
|
|
:return:
|
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
self.prefix = ipaddress.Ipv4Prefix(prefix)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.hostid = hostid
|
|
|
|
self.assign_address = assign_address
|
|
|
|
self.updown_script = updown_script
|
2015-05-22 01:53:33 +01:00
|
|
|
self.serverintf = serverintf
|
2017-04-25 16:45:34 +01:00
|
|
|
LxBrNet.__init__(self, session, objid=objid, name=name, start=start)
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
def startup(self):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Startup functionality for the control network.
|
|
|
|
|
|
|
|
: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
|
|
|
"""
|
2015-05-22 01:54:23 +01:00
|
|
|
if self.detectoldbridge():
|
|
|
|
return
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
LxBrNet.startup(self)
|
2018-03-02 00:23:58 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.hostid:
|
|
|
|
addr = self.prefix.addr(self.hostid)
|
|
|
|
else:
|
2017-04-25 16:45:34 +01:00
|
|
|
addr = self.prefix.max_addr()
|
2018-03-02 00:23:58 +00:00
|
|
|
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.info("added control network bridge: %s %s", self.brname, self.prefix)
|
2018-03-02 00:23:58 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.assign_address:
|
2018-03-02 21:39:44 +00:00
|
|
|
addrlist = ["%s/%s" % (addr, self.prefix.prefixlen)]
|
2017-04-25 16:45:34 +01:00
|
|
|
self.addrconfig(addrlist=addrlist)
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.info("address %s", addr)
|
2018-03-02 00:23:58 +00:00
|
|
|
|
2018-03-02 21:39:44 +00:00
|
|
|
if self.updown_script:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.info("interface %s updown script (%s startup) called", self.brname, self.updown_script)
|
2018-03-02 00:23:58 +00:00
|
|
|
utils.check_cmd([self.updown_script, self.brname, "startup"])
|
|
|
|
|
2018-03-02 21:39:44 +00:00
|
|
|
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"])
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2015-05-22 01:54:23 +01:00
|
|
|
def detectoldbridge(self):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Occassionally, control net bridges from previously closed sessions are not cleaned up.
|
2015-05-22 01:54:23 +01:00
|
|
|
Check if there are old control net bridges and delete them
|
2017-05-03 21:20:56 +01:00
|
|
|
|
|
|
|
:return: True if an old bridge was detected, False otherwise
|
|
|
|
:rtype: bool
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2018-03-02 00:23:58 +00:00
|
|
|
status, output = utils.cmd_output([constants.BRCTL_BIN, "show"])
|
|
|
|
if status != 0:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.error("Unable to retrieve list of installed bridges")
|
2018-03-02 00:23:58 +00:00
|
|
|
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.objid:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.error(
|
2018-03-02 21:39:44 +00:00
|
|
|
"error: An active control net bridge (%s) found. "
|
2018-03-02 00:23:58 +00:00
|
|
|
"An older session might still be running. "
|
2018-03-02 21:39:44 +00:00
|
|
|
"Stop all sessions and, if needed, delete %s to continue.", oldbr, oldbr
|
2018-03-02 00:23:58 +00:00
|
|
|
)
|
|
|
|
return True
|
2015-05-22 01:54:23 +01:00
|
|
|
return False
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def shutdown(self):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Control network shutdown.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
2015-05-22 01:53:33 +01:00
|
|
|
if self.serverintf is not None:
|
|
|
|
try:
|
2018-03-02 00:23:58 +00:00
|
|
|
utils.check_cmd([constants.BRCTL_BIN, "delif", self.brname, self.serverintf])
|
2018-03-03 00:22:20 +00:00
|
|
|
except CoreCommandError:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.exception("error deleting server interface %s from bridge %s", self.serverintf, self.brname)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.updown_script is not None:
|
2018-03-02 21:39:44 +00:00
|
|
|
try:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.info("interface %s updown script (%s shutdown) called", self.brname, self.updown_script)
|
2018-03-02 21:39:44 +00:00
|
|
|
utils.check_cmd([self.updown_script, self.brname, "shutdown"])
|
2018-03-03 00:22:20 +00:00
|
|
|
except CoreCommandError:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.exception("error issuing shutdown script shutdown")
|
2018-03-02 21:39:44 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
LxBrNet.shutdown(self)
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
def all_link_data(self, flags):
|
|
|
|
"""
|
|
|
|
Do not include CtrlNet in link messages describing this session.
|
2017-05-03 21:20:56 +01:00
|
|
|
|
2018-03-02 21:39:44 +00:00
|
|
|
:param flags: message flags
|
|
|
|
:return: list of link data
|
|
|
|
:rtype: list[core.data.LinkData]
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
return []
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
class CoreNode(LxcNode):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Basic core node class for nodes to extend.
|
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
apitype = NodeTypes.DEFAULT.value
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
class PtpNet(LxBrNet):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Peer to peer network node.
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
policy = "ACCEPT"
|
|
|
|
|
|
|
|
def attach(self, netif):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Attach a network interface, but limit attachment to two interfaces.
|
|
|
|
|
2018-03-02 21:39:44 +00:00
|
|
|
:param core.netns.vif.VEth netif: network interface
|
2017-05-03 21:20:56 +01:00
|
|
|
:return: nothing
|
|
|
|
"""
|
|
|
|
if len(self._netif) >= 2:
|
|
|
|
raise ValueError("Point-to-point links support at most 2 network interfaces")
|
2018-03-02 21:39:44 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
LxBrNet.attach(self, netif)
|
|
|
|
|
2018-03-02 21:39:44 +00:00
|
|
|
def data(self, message_type, lat=None, lon=None, alt=None):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Do not generate a Node Message for point-to-point links. They are
|
|
|
|
built using a link message instead.
|
2017-05-03 21:20:56 +01:00
|
|
|
|
2018-03-02 21:39:44 +00:00
|
|
|
:param message_type: purpose for the data object we are creating
|
|
|
|
:param float lat: latitude
|
|
|
|
:param float lon: longitude
|
|
|
|
:param float alt: altitude
|
|
|
|
:return: node data object
|
|
|
|
:rtype: core.data.NodeData
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2018-03-02 21:39:44 +00:00
|
|
|
return None
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
def all_link_data(self, flags):
|
|
|
|
"""
|
|
|
|
Build CORE API TLVs for a point-to-point link. One Link message
|
|
|
|
describes this network.
|
2017-05-03 21:20:56 +01:00
|
|
|
|
2018-03-02 21:39:44 +00:00
|
|
|
:param flags: message flags
|
|
|
|
:return: list of link data
|
|
|
|
:rtype: list[core.data.LinkData]
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
all_links = []
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if len(self._netif) != 2:
|
2017-04-25 16:45:34 +01:00
|
|
|
return all_links
|
|
|
|
|
2018-10-12 00:01:17 +01:00
|
|
|
if1, if2 = self._netif.values()
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
unidirectional = 0
|
2013-12-02 21:20:52 +00:00
|
|
|
if if1.getparams() != if2.getparams():
|
2017-04-25 16:45:34 +01:00
|
|
|
unidirectional = 1
|
|
|
|
|
|
|
|
interface1_ip4 = None
|
|
|
|
interface1_ip4_mask = None
|
|
|
|
interface1_ip6 = None
|
|
|
|
interface1_ip6_mask = None
|
|
|
|
for address in if1.addrlist:
|
2018-10-12 00:01:17 +01:00
|
|
|
ip, _sep, mask = address.partition("/")
|
2013-08-29 15:21:13 +01:00
|
|
|
mask = int(mask)
|
2017-04-25 16:45:34 +01:00
|
|
|
if ipaddress.is_ipv4_address(ip):
|
2013-08-29 15:21:13 +01:00
|
|
|
family = AF_INET
|
2017-04-25 16:45:34 +01:00
|
|
|
ipl = socket.inet_pton(family, ip)
|
|
|
|
interface1_ip4 = ipaddress.IpAddress(af=family, address=ipl)
|
|
|
|
interface1_ip4_mask = mask
|
2013-08-29 15:21:13 +01:00
|
|
|
else:
|
|
|
|
family = AF_INET6
|
2017-04-25 16:45:34 +01:00
|
|
|
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:
|
2018-10-12 00:01:17 +01:00
|
|
|
ip, _sep, mask = address.partition("/")
|
2013-08-29 15:21:13 +01:00
|
|
|
mask = int(mask)
|
2017-04-25 16:45:34 +01:00
|
|
|
if ipaddress.is_ipv4_address(ip):
|
2013-08-29 15:21:13 +01:00
|
|
|
family = AF_INET
|
2017-04-25 16:45:34 +01:00
|
|
|
ipl = socket.inet_pton(family, ip)
|
|
|
|
interface2_ip4 = ipaddress.IpAddress(af=family, address=ipl)
|
|
|
|
interface2_ip4_mask = mask
|
2013-08-29 15:21:13 +01:00
|
|
|
else:
|
|
|
|
family = AF_INET6
|
2017-04-25 16:45:34 +01:00
|
|
|
ipl = socket.inet_pton(family, ip)
|
|
|
|
interface2_ip6 = ipaddress.IpAddress(af=family, address=ipl)
|
|
|
|
interface2_ip6_mask = mask
|
|
|
|
|
|
|
|
link_data = LinkData(
|
|
|
|
message_type=flags,
|
|
|
|
node1_id=if1.node.objid,
|
|
|
|
node2_id=if2.node.objid,
|
|
|
|
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)
|
|
|
|
|
2013-12-02 21:20:52 +00:00
|
|
|
# build a 2nd link message for the upstream link parameters
|
|
|
|
# (swap if1 and if2)
|
2017-04-25 16:45:34 +01:00
|
|
|
if unidirectional:
|
|
|
|
link_data = LinkData(
|
|
|
|
message_type=0,
|
|
|
|
node1_id=if2.node.objid,
|
|
|
|
node2_id=if1.node.objid,
|
|
|
|
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
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
class SwitchNode(LxBrNet):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Provides switch functionality within a core node.
|
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
apitype = NodeTypes.SWITCH.value
|
2013-08-29 15:21:13 +01:00
|
|
|
policy = "ACCEPT"
|
|
|
|
type = "lanswitch"
|
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
class HubNode(LxBrNet):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Provides hub functionality within a core node, forwards packets to all bridge
|
|
|
|
ports by turning off MAC address learning.
|
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
apitype = NodeTypes.HUB.value
|
2013-08-29 15:21:13 +01:00
|
|
|
policy = "ACCEPT"
|
|
|
|
type = "hub"
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
def __init__(self, session, objid=None, name=None, start=True):
|
|
|
|
"""
|
2017-05-03 21:20:56 +01:00
|
|
|
Creates a HubNode instance.
|
|
|
|
|
|
|
|
:param core.session.Session session: core session instance
|
|
|
|
:param int objid: node id
|
|
|
|
:param str name: node namee
|
|
|
|
:param bool start: start flag
|
2018-03-03 00:22:20 +00:00
|
|
|
:raises CoreCommandError: when there is a command exception
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
LxBrNet.__init__(self, session, objid, name, start)
|
2018-03-02 21:39:44 +00:00
|
|
|
|
|
|
|
# TODO: move to startup method
|
2013-08-29 15:21:13 +01:00
|
|
|
if start:
|
2018-03-02 00:23:58 +00:00
|
|
|
utils.check_cmd([constants.BRCTL_BIN, "setageing", self.brname, "0"])
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
class WlanNode(LxBrNet):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Provides wireless lan functionality within a core node.
|
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
apitype = NodeTypes.WIRELESS_LAN.value
|
|
|
|
linktype = LinkTypes.WIRELESS.value
|
2013-08-29 15:21:13 +01:00
|
|
|
policy = "DROP"
|
|
|
|
type = "wlan"
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
def __init__(self, session, objid=None, name=None, start=True, policy=None):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Create a WlanNode instance.
|
|
|
|
|
|
|
|
:param core.session.Session session: core session instance
|
|
|
|
:param int objid: node id
|
|
|
|
:param str name: node name
|
|
|
|
:param bool start: start flag
|
|
|
|
:param policy: wlan policy
|
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
LxBrNet.__init__(self, session, objid, name, start, policy)
|
2013-08-29 15:21:13 +01:00
|
|
|
# wireless model such as basic range
|
|
|
|
self.model = None
|
|
|
|
# mobility model such as scripted
|
|
|
|
self.mobility = None
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def attach(self, netif):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Attach a network interface.
|
|
|
|
|
2018-03-02 21:39:44 +00:00
|
|
|
:param core.netns.vif.VEth netif: network interface
|
2017-05-03 21:20:56 +01:00
|
|
|
:return: nothing
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
LxBrNet.attach(self, netif)
|
|
|
|
if self.model:
|
2017-04-25 16:45:34 +01:00
|
|
|
netif.poshook = self.model.position_callback
|
2013-08-29 15:21:13 +01:00
|
|
|
if netif.node is None:
|
|
|
|
return
|
2017-05-03 21:20:56 +01:00
|
|
|
x, y, z = netif.node.position.get()
|
2013-08-29 15:21:13 +01:00
|
|
|
# invokes any netif.poshook
|
|
|
|
netif.setposition(x, y, z)
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2018-06-13 19:59:50 +01:00
|
|
|
def setmodel(self, model, config):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Sets the mobility and wireless model.
|
|
|
|
|
|
|
|
:param core.mobility.WirelessModel.cls model: wireless model to set to
|
2018-06-13 19:59:50 +01:00
|
|
|
:param dict config: configuration for model being set
|
2017-05-03 21:20:56 +01:00
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.info("adding model: %s", model.name)
|
2017-04-25 16:45:34 +01:00
|
|
|
if model.config_type == RegisterTlvs.WIRELESS.value:
|
2018-06-13 19:59:50 +01:00
|
|
|
self.model = model(session=self.session, object_id=self.objid)
|
|
|
|
self.model.update_config(config)
|
2017-04-25 16:45:34 +01:00
|
|
|
if self.model.position_callback:
|
2013-08-29 15:21:13 +01:00
|
|
|
for netif in self.netifs():
|
2017-04-25 16:45:34 +01:00
|
|
|
netif.poshook = self.model.position_callback
|
2013-08-29 15:21:13 +01:00
|
|
|
if netif.node is not None:
|
2017-04-27 21:34:23 +01:00
|
|
|
x, y, z = netif.node.position.get()
|
2013-08-29 15:21:13 +01:00
|
|
|
netif.poshook(netif, x, y, z)
|
|
|
|
self.model.setlinkparams()
|
2017-04-25 16:45:34 +01:00
|
|
|
elif model.config_type == RegisterTlvs.MOBILITY.value:
|
2018-06-13 19:59:50 +01:00
|
|
|
self.mobility = model(session=self.session, object_id=self.objid)
|
|
|
|
self.mobility.update_config(config)
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2018-06-11 20:26:12 +01:00
|
|
|
def update_mobility(self, config):
|
|
|
|
if not self.mobility:
|
|
|
|
raise ValueError("no mobility set to update for node(%s)", self.objid)
|
|
|
|
self.mobility.set_configs(config, node_id=self.objid)
|
|
|
|
|
|
|
|
def updatemodel(self, config):
|
|
|
|
if not self.model:
|
|
|
|
raise ValueError("no model set to update for node(%s)", self.objid)
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.info("node(%s) updating model(%s): %s", self.objid, self.model.name, config)
|
2018-06-11 20:26:12 +01:00
|
|
|
self.model.set_configs(config, node_id=self.objid)
|
|
|
|
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()
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
def all_link_data(self, flags):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Retrieve all link data.
|
|
|
|
|
2018-03-02 21:39:44 +00:00
|
|
|
:param flags: message flags
|
|
|
|
:return: list of link data
|
|
|
|
:rtype: list[core.data.LinkData]
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
all_links = LxBrNet.all_link_data(self, flags)
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.model:
|
2017-04-25 16:45:34 +01:00
|
|
|
all_links.extend(self.model.all_link_data(flags))
|
|
|
|
|
|
|
|
return all_links
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
class RJ45Node(PyCoreNode, PyCoreNetIf):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
RJ45Node is a physical interface on the host linked to the emulated
|
|
|
|
network.
|
|
|
|
"""
|
|
|
|
apitype = NodeTypes.RJ45.value
|
2013-09-05 18:46:12 +01:00
|
|
|
type = "rj45"
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
def __init__(self, session, objid=None, name=None, mtu=1500, start=True):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Create an RJ45Node instance.
|
|
|
|
|
|
|
|
:param core.session.Session session: core session instance
|
|
|
|
:param int objid: node id
|
|
|
|
:param str name: node name
|
|
|
|
:param mtu: rj45 mtu
|
|
|
|
:param bool start: start flag
|
|
|
|
:return:
|
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
PyCoreNode.__init__(self, session, objid, name, start=start)
|
|
|
|
PyCoreNetIf.__init__(self, node=self, name=name, mtu=mtu)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.up = False
|
|
|
|
self.lock = threading.RLock()
|
|
|
|
self.ifindex = None
|
|
|
|
# the following are PyCoreNetIf attributes
|
|
|
|
self.transport_type = "raw"
|
|
|
|
self.localname = name
|
2018-03-02 00:23:58 +00:00
|
|
|
self.old_up = False
|
|
|
|
self.old_addrs = []
|
2018-03-02 21:39:44 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if start:
|
|
|
|
self.startup()
|
|
|
|
|
|
|
|
def startup(self):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Set the interface in the up state.
|
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
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
# interface will also be marked up during net.attach()
|
|
|
|
self.savestate()
|
2018-03-02 21:39:44 +00:00
|
|
|
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "up"])
|
|
|
|
self.up = True
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def shutdown(self):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Bring the interface down. Remove any addresses and queuing
|
|
|
|
disciplines.
|
2017-05-03 21:20:56 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if not self.up:
|
|
|
|
return
|
2018-03-02 00:23:58 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "down"])
|
|
|
|
utils.check_cmd([constants.IP_BIN, "addr", "flush", "dev", self.localname])
|
2018-03-02 17:15:52 +00:00
|
|
|
utils.check_cmd([constants.TC_BIN, "qdisc", "del", "dev", self.localname, "root"])
|
2018-03-03 00:22:20 +00:00
|
|
|
except CoreCommandError:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.exception("error shutting down")
|
2018-03-02 00:23:58 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
self.up = False
|
|
|
|
self.restorestate()
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2017-05-03 21:20:56 +01:00
|
|
|
# TODO: issue in that both classes inherited from provide the same method with different signatures
|
2013-08-29 15:21:13 +01:00
|
|
|
def attachnet(self, net):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Attach a network.
|
|
|
|
|
|
|
|
:param core.coreobj.PyCoreNet net: network to attach
|
|
|
|
:return: nothing
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
PyCoreNetIf.attachnet(self, net)
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2018-03-02 21:39:44 +00:00
|
|
|
# TODO: issue in that both classes inherited from provide the same method with different signatures
|
2013-08-29 15:21:13 +01:00
|
|
|
def detachnet(self):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Detach a network.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
PyCoreNetIf.detachnet(self)
|
|
|
|
|
2017-05-03 21:20:56 +01:00
|
|
|
def newnetif(self, net=None, addrlist=None, hwaddr=None, ifindex=None, ifname=None):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
This is called when linking with another node. Since this node
|
|
|
|
represents an interface, we do not create another object here,
|
|
|
|
but attach ourselves to the given network.
|
2017-05-03 21:20:56 +01:00
|
|
|
|
|
|
|
:param core.coreobj.PyCoreNet net: new network instance
|
|
|
|
:param list[str] addrlist: address list
|
|
|
|
:param str hwaddr: hardware address
|
|
|
|
:param int ifindex: interface index
|
|
|
|
:param str ifname: interface name
|
2018-03-02 21:39:44 +00:00
|
|
|
:return: interface index
|
|
|
|
:rtype: int
|
|
|
|
:raises ValueError: when an interface has already been created, one max
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2017-05-03 21:20:56 +01:00
|
|
|
with self.lock:
|
2013-08-29 15:21:13 +01:00
|
|
|
if ifindex is None:
|
|
|
|
ifindex = 0
|
2017-05-03 21:20:56 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.net is not None:
|
2017-05-03 21:20:56 +01:00
|
|
|
raise ValueError("RJ45 nodes support at most 1 network interface")
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
self._netif[ifindex] = self
|
2017-04-25 16:45:34 +01:00
|
|
|
# PyCoreNetIf.node is self
|
|
|
|
self.node = self
|
2013-08-29 15:21:13 +01:00
|
|
|
self.ifindex = ifindex
|
2017-05-03 21:20:56 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if net is not None:
|
|
|
|
self.attachnet(net)
|
2017-05-03 21:20:56 +01:00
|
|
|
|
|
|
|
if addrlist:
|
2018-03-02 17:15:52 +00:00
|
|
|
for addr in utils.make_tuple(addrlist):
|
2017-05-03 21:20:56 +01:00
|
|
|
self.addaddr(addr)
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
return ifindex
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def delnetif(self, ifindex):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Delete a network interface.
|
|
|
|
|
|
|
|
:param int ifindex: interface index to delete
|
|
|
|
:return: nothing
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if ifindex is None:
|
|
|
|
ifindex = 0
|
2017-05-03 21:20:56 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
self._netif.pop(ifindex)
|
2018-03-02 21:39:44 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if ifindex == self.ifindex:
|
|
|
|
self.shutdown()
|
|
|
|
else:
|
2018-03-02 21:39:44 +00:00
|
|
|
raise ValueError("ifindex %s does not exist" % ifindex)
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
def netif(self, ifindex, net=None):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
This object is considered the network interface, so we only
|
|
|
|
return self here. This keeps the RJ45Node compatible with
|
|
|
|
real nodes.
|
2017-05-03 21:20:56 +01:00
|
|
|
|
|
|
|
:param int ifindex: interface index to retrieve
|
|
|
|
:param net: network to retrieve
|
|
|
|
:return: a network interface
|
|
|
|
:rtype: core.coreobj.PyCoreNetIf
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if net is not None and net == self.net:
|
|
|
|
return self
|
2017-05-03 21:20:56 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if ifindex is None:
|
|
|
|
ifindex = 0
|
2017-05-03 21:20:56 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if ifindex == self.ifindex:
|
|
|
|
return self
|
2017-05-03 21:20:56 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
return None
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def getifindex(self, netif):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Retrieve network interface index.
|
|
|
|
|
|
|
|
:param core.coreobj.PyCoreNetIf netif: network interface to retrieve index for
|
|
|
|
:return: interface index, None otherwise
|
|
|
|
:rtype: int
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if netif != self:
|
|
|
|
return None
|
2017-05-03 21:20:56 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
return self.ifindex
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def addaddr(self, addr):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Add address to to network interface.
|
|
|
|
|
|
|
|
:param str addr: address to add
|
|
|
|
: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
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.up:
|
2018-03-02 00:23:58 +00:00
|
|
|
utils.check_cmd([constants.IP_BIN, "addr", "add", str(addr), "dev", self.name])
|
2018-03-02 21:39:44 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
PyCoreNetIf.addaddr(self, addr)
|
|
|
|
|
|
|
|
def deladdr(self, addr):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Delete address from network interface.
|
|
|
|
|
|
|
|
:param str addr: address to delete
|
|
|
|
: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
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.up:
|
2018-03-02 00:23:58 +00:00
|
|
|
utils.check_cmd([constants.IP_BIN, "addr", "del", str(addr), "dev", self.name])
|
2018-03-02 21:39:44 +00:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
PyCoreNetIf.deladdr(self, addr)
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def savestate(self):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Save the addresses and other interface state before using the
|
2013-08-29 15:21:13 +01:00
|
|
|
interface for emulation purposes. TODO: save/restore the PROMISC flag
|
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
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
self.old_up = False
|
|
|
|
self.old_addrs = []
|
2018-03-02 00:23:58 +00:00
|
|
|
args = [constants.IP_BIN, "addr", "show", "dev", self.localname]
|
2018-03-02 21:57:50 +00:00
|
|
|
output = utils.check_cmd(args)
|
2018-03-02 21:39:44 +00:00
|
|
|
for line in output.split("\n"):
|
|
|
|
items = line.split()
|
|
|
|
if len(items) < 2:
|
|
|
|
continue
|
|
|
|
|
|
|
|
if items[1] == "%s:" % self.localname:
|
|
|
|
flags = items[2][1:-1].split(",")
|
|
|
|
if "UP" in flags:
|
|
|
|
self.old_up = True
|
|
|
|
elif items[0] == "inet":
|
|
|
|
self.old_addrs.append((items[1], items[3]))
|
|
|
|
elif items[0] == "inet6":
|
|
|
|
if items[1][:4] == "fe80":
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
2018-03-02 21:39:44 +00:00
|
|
|
self.old_addrs.append((items[1], None))
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def restorestate(self):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Restore the addresses and other interface state after using it.
|
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
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
for addr in self.old_addrs:
|
|
|
|
if addr[1] is None:
|
2018-03-02 00:23:58 +00:00
|
|
|
utils.check_cmd([constants.IP_BIN, "addr", "add", addr[0], "dev", self.localname])
|
2013-08-29 15:21:13 +01:00
|
|
|
else:
|
2018-03-02 00:23:58 +00:00
|
|
|
utils.check_cmd([constants.IP_BIN, "addr", "add", addr[0], "brd", addr[1], "dev", self.localname])
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.old_up:
|
2018-03-02 00:23:58 +00:00
|
|
|
utils.check_cmd([constants.IP_BIN, "link", "set", self.localname, "up"])
|
2014-09-23 17:26:22 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
def setposition(self, x=None, y=None, z=None):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2018-03-02 21:39:44 +00:00
|
|
|
Uses setposition from both parent classes.
|
2017-05-03 21:20:56 +01:00
|
|
|
|
2018-03-02 21:39:44 +00:00
|
|
|
:param float x: x position
|
|
|
|
:param float y: y position
|
|
|
|
:param float z: z position
|
|
|
|
:return: True if position changed, False otherwise
|
|
|
|
:rtype: bool
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2018-03-02 21:39:44 +00:00
|
|
|
result = PyCoreObj.setposition(self, x, y, z)
|
2013-08-29 15:21:13 +01:00
|
|
|
PyCoreNetIf.setposition(self, x, y, z)
|
2018-03-02 21:39:44 +00:00
|
|
|
return result
|
|
|
|
|
|
|
|
def check_cmd(self, args):
|
|
|
|
"""
|
|
|
|
Runs shell command on node.
|
|
|
|
|
|
|
|
:param list[str]|str args: command to run
|
|
|
|
:return: exist status and combined stdout and stderr
|
|
|
|
:rtype: tuple[int, str]
|
2018-03-03 00:22:20 +00:00
|
|
|
:raises CoreCommandError: when a non-zero exit status occurs
|
2018-03-02 21:39:44 +00:00
|
|
|
"""
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def cmd(self, args, wait=True):
|
|
|
|
"""
|
|
|
|
Runs shell command on node, with option to not wait for a result.
|
|
|
|
|
|
|
|
:param list[str]|str args: command to run
|
|
|
|
:param bool wait: wait for command to exit, defaults to True
|
|
|
|
:return: exit status for command
|
|
|
|
:rtype: int
|
|
|
|
"""
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def cmd_output(self, args):
|
|
|
|
"""
|
|
|
|
Runs shell command on node and get exit status and output.
|
|
|
|
|
|
|
|
:param list[str]|str args: command to run
|
|
|
|
:return: exit status and combined stdout and stderr
|
|
|
|
:rtype: tuple[int, str]
|
|
|
|
"""
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def termcmdstring(self, sh):
|
|
|
|
"""
|
|
|
|
Create a terminal command string.
|
|
|
|
|
|
|
|
:param str sh: shell to execute command in
|
|
|
|
:return: str
|
|
|
|
"""
|
|
|
|
raise NotImplementedError
|
2014-09-23 17:26:22 +01:00
|
|
|
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
class TunnelNode(GreTapBridge):
|
2017-05-03 21:20:56 +01:00
|
|
|
"""
|
|
|
|
Provides tunnel functionality in a core node.
|
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
apitype = NodeTypes.TUNNEL.value
|
2013-08-29 15:21:13 +01:00
|
|
|
policy = "ACCEPT"
|
|
|
|
type = "tunnel"
|