2020-01-11 17:37:26 +00:00
|
|
|
from typing import List, Optional
|
|
|
|
|
2020-01-08 21:25:00 +00:00
|
|
|
import netaddr
|
|
|
|
|
2020-01-09 01:33:49 +00:00
|
|
|
from core import utils
|
2020-01-11 17:37:26 +00:00
|
|
|
from core.api.grpc.core_pb2 import LinkOptions
|
2019-09-28 06:31:56 +01:00
|
|
|
from core.emane.nodes import EmaneNet
|
2019-09-26 21:00:12 +01:00
|
|
|
from core.emulator.enumerations import LinkTypes
|
2020-01-11 17:37:26 +00:00
|
|
|
from core.nodes.base import CoreNetworkBase, CoreNode
|
|
|
|
from core.nodes.interface import CoreInterface
|
2019-09-26 21:00:12 +01:00
|
|
|
from core.nodes.physical import PhysicalNode
|
2018-04-25 00:24:54 +01:00
|
|
|
|
|
|
|
|
2019-10-23 17:31:07 +01:00
|
|
|
class IdGen:
|
2020-01-11 17:37:26 +00:00
|
|
|
def __init__(self, _id: int = 0) -> None:
|
2019-05-02 07:17:46 +01:00
|
|
|
self.id = _id
|
|
|
|
|
2020-01-11 17:37:26 +00:00
|
|
|
def next(self) -> int:
|
2019-05-02 07:17:46 +01:00
|
|
|
self.id += 1
|
|
|
|
return self.id
|
|
|
|
|
|
|
|
|
2020-01-11 17:37:26 +00:00
|
|
|
def link_config(
|
|
|
|
network: CoreNetworkBase,
|
|
|
|
interface: CoreInterface,
|
|
|
|
link_options: LinkOptions,
|
|
|
|
devname: str = None,
|
|
|
|
interface_two: CoreInterface = None,
|
|
|
|
) -> None:
|
2019-05-02 07:17:46 +01:00
|
|
|
"""
|
|
|
|
Convenience method for configuring a link,
|
|
|
|
|
|
|
|
:param network: network to configure link for
|
|
|
|
:param interface: interface to configure
|
2020-01-16 19:00:57 +00:00
|
|
|
:param link_options: data to configure link with
|
|
|
|
:param devname: device name, default is None
|
2019-05-02 07:17:46 +01:00
|
|
|
:param interface_two: other interface associated, default is None
|
|
|
|
:return: nothing
|
|
|
|
"""
|
|
|
|
config = {
|
|
|
|
"netif": interface,
|
|
|
|
"bw": link_options.bandwidth,
|
|
|
|
"delay": link_options.delay,
|
|
|
|
"loss": link_options.per,
|
|
|
|
"duplicate": link_options.dup,
|
|
|
|
"jitter": link_options.jitter,
|
2019-09-10 23:10:24 +01:00
|
|
|
"netif2": interface_two,
|
2019-05-02 07:17:46 +01:00
|
|
|
}
|
|
|
|
|
2019-09-28 06:31:56 +01:00
|
|
|
# hacky check here, because physical and emane nodes do not conform to the same
|
|
|
|
# linkconfig interface
|
|
|
|
if not isinstance(network, (EmaneNet, PhysicalNode)):
|
2019-05-02 07:17:46 +01:00
|
|
|
config["devname"] = devname
|
|
|
|
|
|
|
|
network.linkconfig(**config)
|
|
|
|
|
|
|
|
|
2019-10-23 17:31:07 +01:00
|
|
|
class NodeOptions:
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
|
|
|
Options for creating and updating nodes within core.
|
|
|
|
"""
|
|
|
|
|
2020-01-11 17:37:26 +00:00
|
|
|
def __init__(self, name: str = None, model: str = "PC", image: str = None) -> None:
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
|
|
|
Create a NodeOptions object.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param name: name of node, defaults to node class name postfix with its id
|
|
|
|
:param model: defines services for default and physical nodes, defaults to
|
2019-09-28 06:31:56 +01:00
|
|
|
"router"
|
2020-01-16 19:00:57 +00:00
|
|
|
:param image: image to use for docker nodes
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
|
|
|
self.name = name
|
|
|
|
self.model = model
|
|
|
|
self.canvas = None
|
|
|
|
self.icon = None
|
|
|
|
self.opaque = None
|
2020-04-16 20:31:48 +01:00
|
|
|
self.services = []
|
2020-01-18 05:09:51 +00:00
|
|
|
self.config_services = []
|
2018-04-25 00:24:54 +01:00
|
|
|
self.x = None
|
|
|
|
self.y = None
|
|
|
|
self.lat = None
|
|
|
|
self.lon = None
|
|
|
|
self.alt = None
|
|
|
|
self.emulation_id = None
|
2019-10-23 04:50:01 +01:00
|
|
|
self.server = None
|
2019-06-28 23:41:55 +01:00
|
|
|
self.image = image
|
2019-11-21 20:29:33 +00:00
|
|
|
self.emane = None
|
2018-04-25 00:24:54 +01:00
|
|
|
|
2020-01-11 17:37:26 +00:00
|
|
|
def set_position(self, x: float, y: float) -> None:
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
|
|
|
Convenience method for setting position.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param x: x position
|
|
|
|
:param y: y position
|
2018-04-25 00:24:54 +01:00
|
|
|
:return: nothing
|
|
|
|
"""
|
|
|
|
self.x = x
|
|
|
|
self.y = y
|
|
|
|
|
2020-01-11 17:37:26 +00:00
|
|
|
def set_location(self, lat: float, lon: float, alt: float) -> None:
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
|
|
|
Convenience method for setting location.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param lat: latitude
|
|
|
|
:param lon: longitude
|
|
|
|
:param alt: altitude
|
2018-04-25 00:24:54 +01:00
|
|
|
:return: nothing
|
|
|
|
"""
|
|
|
|
self.lat = lat
|
|
|
|
self.lon = lon
|
|
|
|
self.alt = alt
|
|
|
|
|
|
|
|
|
2019-10-23 17:31:07 +01:00
|
|
|
class LinkOptions:
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
|
|
|
Options for creating and updating links within core.
|
|
|
|
"""
|
|
|
|
|
2020-01-11 17:37:26 +00:00
|
|
|
def __init__(self, _type: LinkTypes = LinkTypes.WIRED) -> None:
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
|
|
|
Create a LinkOptions object.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param _type: type of link, defaults to
|
2019-09-28 06:31:56 +01:00
|
|
|
wired
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
|
|
|
self.type = _type
|
|
|
|
self.session = None
|
|
|
|
self.delay = None
|
|
|
|
self.bandwidth = None
|
|
|
|
self.per = None
|
|
|
|
self.dup = None
|
|
|
|
self.jitter = None
|
|
|
|
self.mer = None
|
|
|
|
self.burst = None
|
|
|
|
self.mburst = None
|
|
|
|
self.gui_attributes = None
|
|
|
|
self.unidirectional = None
|
|
|
|
self.emulation_id = None
|
|
|
|
self.network_id = None
|
|
|
|
self.key = None
|
|
|
|
self.opaque = None
|
|
|
|
|
|
|
|
|
2020-01-11 17:37:26 +00:00
|
|
|
class InterfaceData:
|
|
|
|
"""
|
|
|
|
Convenience class for storing interface data.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
_id: int,
|
|
|
|
name: str,
|
|
|
|
mac: str,
|
|
|
|
ip4: str,
|
|
|
|
ip4_mask: int,
|
|
|
|
ip6: str,
|
|
|
|
ip6_mask: int,
|
|
|
|
) -> None:
|
|
|
|
"""
|
|
|
|
Creates an InterfaceData object.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param _id: interface id
|
|
|
|
:param name: name for interface
|
|
|
|
:param mac: mac address
|
|
|
|
:param ip4: ipv4 address
|
|
|
|
:param ip4_mask: ipv4 bit mask
|
|
|
|
:param ip6: ipv6 address
|
|
|
|
:param ip6_mask: ipv6 bit mask
|
2020-01-11 17:37:26 +00:00
|
|
|
"""
|
|
|
|
self.id = _id
|
|
|
|
self.name = name
|
|
|
|
self.mac = mac
|
|
|
|
self.ip4 = ip4
|
|
|
|
self.ip4_mask = ip4_mask
|
|
|
|
self.ip6 = ip6
|
|
|
|
self.ip6_mask = ip6_mask
|
|
|
|
|
|
|
|
def has_ip4(self) -> bool:
|
|
|
|
"""
|
|
|
|
Determines if interface has an ip4 address.
|
|
|
|
|
|
|
|
:return: True if has ip4, False otherwise
|
|
|
|
"""
|
|
|
|
return all([self.ip4, self.ip4_mask])
|
|
|
|
|
|
|
|
def has_ip6(self) -> bool:
|
|
|
|
"""
|
|
|
|
Determines if interface has an ip6 address.
|
|
|
|
|
|
|
|
:return: True if has ip6, False otherwise
|
|
|
|
"""
|
|
|
|
return all([self.ip6, self.ip6_mask])
|
|
|
|
|
|
|
|
def ip4_address(self) -> Optional[str]:
|
|
|
|
"""
|
|
|
|
Retrieve a string representation of the ip4 address and netmask.
|
|
|
|
|
|
|
|
:return: ip4 string or None
|
|
|
|
"""
|
|
|
|
if self.has_ip4():
|
|
|
|
return f"{self.ip4}/{self.ip4_mask}"
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def ip6_address(self) -> Optional[str]:
|
|
|
|
"""
|
|
|
|
Retrieve a string representation of the ip6 address and netmask.
|
|
|
|
|
|
|
|
:return: ip4 string or None
|
|
|
|
"""
|
|
|
|
if self.has_ip6():
|
|
|
|
return f"{self.ip6}/{self.ip6_mask}"
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def get_addresses(self) -> List[str]:
|
|
|
|
"""
|
|
|
|
Returns a list of ip4 and ip6 address when present.
|
|
|
|
|
|
|
|
:return: list of addresses
|
2020-01-17 00:12:01 +00:00
|
|
|
"""
|
2020-01-11 17:37:26 +00:00
|
|
|
ip4 = self.ip4_address()
|
|
|
|
ip6 = self.ip6_address()
|
|
|
|
return [i for i in [ip4, ip6] if i]
|
|
|
|
|
|
|
|
|
2019-10-23 17:31:07 +01:00
|
|
|
class IpPrefixes:
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
|
|
|
Convenience class to help generate IP4 and IP6 addresses for nodes within CORE.
|
|
|
|
"""
|
|
|
|
|
2020-01-11 17:37:26 +00:00
|
|
|
def __init__(self, ip4_prefix: str = None, ip6_prefix: str = None) -> None:
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
|
|
|
Creates an IpPrefixes object.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param ip4_prefix: ip4 prefix to use for generation
|
|
|
|
:param ip6_prefix: ip6 prefix to use for generation
|
2018-04-25 00:24:54 +01:00
|
|
|
:raises ValueError: when both ip4 and ip6 prefixes have not been provided
|
|
|
|
"""
|
|
|
|
if not ip4_prefix and not ip6_prefix:
|
|
|
|
raise ValueError("ip4 or ip6 must be provided")
|
|
|
|
|
|
|
|
self.ip4 = None
|
|
|
|
if ip4_prefix:
|
2020-01-08 21:25:00 +00:00
|
|
|
self.ip4 = netaddr.IPNetwork(ip4_prefix)
|
2018-04-25 00:24:54 +01:00
|
|
|
self.ip6 = None
|
|
|
|
if ip6_prefix:
|
2020-01-08 21:25:00 +00:00
|
|
|
self.ip6 = netaddr.IPNetwork(ip6_prefix)
|
2018-04-25 00:24:54 +01:00
|
|
|
|
2020-01-11 17:37:26 +00:00
|
|
|
def ip4_address(self, node: CoreNode) -> str:
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
|
|
|
Convenience method to return the IP4 address for a node.
|
|
|
|
|
|
|
|
:param node: node to get IP4 address for
|
|
|
|
:return: IP4 address or None
|
2020-01-17 00:12:01 +00:00
|
|
|
"""
|
2018-04-25 00:24:54 +01:00
|
|
|
if not self.ip4:
|
|
|
|
raise ValueError("ip4 prefixes have not been set")
|
2020-01-08 21:25:00 +00:00
|
|
|
return str(self.ip4[node.id])
|
2018-04-25 00:24:54 +01:00
|
|
|
|
2020-01-11 17:37:26 +00:00
|
|
|
def ip6_address(self, node: CoreNode) -> str:
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
|
|
|
Convenience method to return the IP6 address for a node.
|
|
|
|
|
|
|
|
:param node: node to get IP6 address for
|
|
|
|
:return: IP4 address or None
|
2020-01-17 00:12:01 +00:00
|
|
|
"""
|
2018-04-25 00:24:54 +01:00
|
|
|
if not self.ip6:
|
|
|
|
raise ValueError("ip6 prefixes have not been set")
|
2020-01-08 21:25:00 +00:00
|
|
|
return str(self.ip6[node.id])
|
2018-04-25 00:24:54 +01:00
|
|
|
|
2020-01-11 17:37:26 +00:00
|
|
|
def create_interface(
|
|
|
|
self, node: CoreNode, name: str = None, mac: str = None
|
|
|
|
) -> InterfaceData:
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
2019-09-28 06:31:56 +01:00
|
|
|
Creates interface data for linking nodes, using the nodes unique id for
|
|
|
|
generation, along with a random mac address, unless provided.
|
2018-04-25 00:24:54 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param node: node to create interface for
|
|
|
|
:param name: name to set for interface, default is eth{id}
|
|
|
|
:param mac: mac address to use for this interface, default is random
|
2019-09-28 06:31:56 +01:00
|
|
|
generation
|
2018-04-25 00:24:54 +01:00
|
|
|
:return: new interface data for the provided node
|
2020-01-17 00:12:01 +00:00
|
|
|
"""
|
2018-04-25 00:24:54 +01:00
|
|
|
# interface id
|
|
|
|
inteface_id = node.newifindex()
|
|
|
|
|
|
|
|
# generate ip4 data
|
|
|
|
ip4 = None
|
|
|
|
ip4_mask = None
|
|
|
|
if self.ip4:
|
2020-01-08 21:25:00 +00:00
|
|
|
ip4 = self.ip4_address(node)
|
2018-04-25 00:24:54 +01:00
|
|
|
ip4_mask = self.ip4.prefixlen
|
|
|
|
|
|
|
|
# generate ip6 data
|
|
|
|
ip6 = None
|
|
|
|
ip6_mask = None
|
|
|
|
if self.ip6:
|
2020-01-08 21:25:00 +00:00
|
|
|
ip6 = self.ip6_address(node)
|
2018-04-25 00:24:54 +01:00
|
|
|
ip6_mask = self.ip6.prefixlen
|
|
|
|
|
|
|
|
# random mac
|
|
|
|
if not mac:
|
2020-01-09 01:33:49 +00:00
|
|
|
mac = utils.random_mac()
|
2018-04-25 00:24:54 +01:00
|
|
|
|
|
|
|
return InterfaceData(
|
|
|
|
_id=inteface_id,
|
|
|
|
name=name,
|
|
|
|
ip4=ip4,
|
|
|
|
ip4_mask=ip4_mask,
|
|
|
|
ip6=ip6,
|
|
|
|
ip6_mask=ip6_mask,
|
2019-09-10 23:10:24 +01:00
|
|
|
mac=mac,
|
2018-04-25 00:24:54 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2020-01-11 17:37:26 +00:00
|
|
|
def create_interface(
|
|
|
|
node: CoreNode, network: CoreNetworkBase, interface_data: InterfaceData
|
|
|
|
):
|
2018-04-25 00:24:54 +01:00
|
|
|
"""
|
2020-01-11 17:37:26 +00:00
|
|
|
Create an interface for a node on a network using provided interface data.
|
2019-06-07 00:34:26 +01:00
|
|
|
|
2020-01-11 17:37:26 +00:00
|
|
|
:param node: node to create interface for
|
2020-01-16 19:00:57 +00:00
|
|
|
:param network: network to associate interface with
|
|
|
|
:param interface_data: interface data
|
2020-01-11 17:37:26 +00:00
|
|
|
:return: created interface
|
|
|
|
"""
|
|
|
|
node.newnetif(
|
|
|
|
network,
|
|
|
|
addrlist=interface_data.get_addresses(),
|
|
|
|
hwaddr=interface_data.mac,
|
|
|
|
ifindex=interface_data.id,
|
|
|
|
ifname=interface_data.name,
|
|
|
|
)
|
|
|
|
return node.netif(interface_data.id)
|