Merge pull request #681 from coreemu/enhancement/wireless-node
Enhancement/wireless node
This commit is contained in:
commit
ace0183db5
19 changed files with 530 additions and 46 deletions
|
@ -16,7 +16,13 @@ from core.api.grpc.configservices_pb2 import (
|
||||||
GetConfigServiceDefaultsRequest,
|
GetConfigServiceDefaultsRequest,
|
||||||
GetNodeConfigServiceRequest,
|
GetNodeConfigServiceRequest,
|
||||||
)
|
)
|
||||||
from core.api.grpc.core_pb2 import ExecuteScriptRequest, GetConfigRequest, LinkedRequest
|
from core.api.grpc.core_pb2 import (
|
||||||
|
ExecuteScriptRequest,
|
||||||
|
GetConfigRequest,
|
||||||
|
LinkedRequest,
|
||||||
|
WirelessConfigRequest,
|
||||||
|
WirelessLinkedRequest,
|
||||||
|
)
|
||||||
from core.api.grpc.emane_pb2 import (
|
from core.api.grpc.emane_pb2 import (
|
||||||
EmaneLinkRequest,
|
EmaneLinkRequest,
|
||||||
GetEmaneEventChannelRequest,
|
GetEmaneEventChannelRequest,
|
||||||
|
@ -43,6 +49,7 @@ from core.api.grpc.wlan_pb2 import (
|
||||||
WlanConfig,
|
WlanConfig,
|
||||||
WlanLinkRequest,
|
WlanLinkRequest,
|
||||||
)
|
)
|
||||||
|
from core.api.grpc.wrappers import LinkOptions
|
||||||
from core.emulator.data import IpPrefixes
|
from core.emulator.data import IpPrefixes
|
||||||
from core.errors import CoreError
|
from core.errors import CoreError
|
||||||
|
|
||||||
|
@ -1079,6 +1086,42 @@ class CoreGrpcClient:
|
||||||
)
|
)
|
||||||
self.stub.Linked(request)
|
self.stub.Linked(request)
|
||||||
|
|
||||||
|
def wireless_linked(
|
||||||
|
self,
|
||||||
|
session_id: int,
|
||||||
|
wireless_id: int,
|
||||||
|
node1_id: int,
|
||||||
|
node2_id: int,
|
||||||
|
linked: bool,
|
||||||
|
) -> None:
|
||||||
|
request = WirelessLinkedRequest(
|
||||||
|
session_id=session_id,
|
||||||
|
wireless_id=wireless_id,
|
||||||
|
node1_id=node1_id,
|
||||||
|
node2_id=node2_id,
|
||||||
|
linked=linked,
|
||||||
|
)
|
||||||
|
self.stub.WirelessLinked(request)
|
||||||
|
|
||||||
|
def wireless_config(
|
||||||
|
self,
|
||||||
|
session_id: int,
|
||||||
|
wireless_id: int,
|
||||||
|
node1_id: int,
|
||||||
|
node2_id: int,
|
||||||
|
options1: LinkOptions,
|
||||||
|
options2: LinkOptions = None,
|
||||||
|
) -> None:
|
||||||
|
request = WirelessConfigRequest(
|
||||||
|
session_id=session_id,
|
||||||
|
wireless_id=wireless_id,
|
||||||
|
node1_id=node1_id,
|
||||||
|
node2_id=node2_id,
|
||||||
|
options1=options1.to_proto(),
|
||||||
|
options2=options2.to_proto(),
|
||||||
|
)
|
||||||
|
self.stub.WirelessConfig(request)
|
||||||
|
|
||||||
def connect(self) -> None:
|
def connect(self) -> None:
|
||||||
"""
|
"""
|
||||||
Open connection to server, must be closed manually.
|
Open connection to server, must be closed manually.
|
||||||
|
|
|
@ -26,7 +26,15 @@ from core.api.grpc.configservices_pb2 import (
|
||||||
GetNodeConfigServiceRequest,
|
GetNodeConfigServiceRequest,
|
||||||
GetNodeConfigServiceResponse,
|
GetNodeConfigServiceResponse,
|
||||||
)
|
)
|
||||||
from core.api.grpc.core_pb2 import ExecuteScriptResponse, LinkedRequest, LinkedResponse
|
from core.api.grpc.core_pb2 import (
|
||||||
|
ExecuteScriptResponse,
|
||||||
|
LinkedRequest,
|
||||||
|
LinkedResponse,
|
||||||
|
WirelessConfigRequest,
|
||||||
|
WirelessConfigResponse,
|
||||||
|
WirelessLinkedRequest,
|
||||||
|
WirelessLinkedResponse,
|
||||||
|
)
|
||||||
from core.api.grpc.emane_pb2 import (
|
from core.api.grpc.emane_pb2 import (
|
||||||
EmaneLinkRequest,
|
EmaneLinkRequest,
|
||||||
EmaneLinkResponse,
|
EmaneLinkResponse,
|
||||||
|
@ -82,6 +90,7 @@ from core.errors import CoreCommandError, CoreError
|
||||||
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
|
||||||
from core.nodes.base import CoreNode, NodeBase
|
from core.nodes.base import CoreNode, NodeBase
|
||||||
from core.nodes.network import CoreNetwork, WlanNode
|
from core.nodes.network import CoreNetwork, WlanNode
|
||||||
|
from core.nodes.wireless import WirelessNode
|
||||||
from core.services.coreservices import ServiceManager
|
from core.services.coreservices import ServiceManager
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -1329,3 +1338,23 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
request.linked,
|
request.linked,
|
||||||
)
|
)
|
||||||
return LinkedResponse()
|
return LinkedResponse()
|
||||||
|
|
||||||
|
def WirelessLinked(
|
||||||
|
self, request: WirelessLinkedRequest, context: ServicerContext
|
||||||
|
) -> WirelessLinkedResponse:
|
||||||
|
session = self.get_session(request.session_id, context)
|
||||||
|
wireless = self.get_node(session, request.wireless_id, context, WirelessNode)
|
||||||
|
wireless.link_control(request.node1_id, request.node2_id, request.linked)
|
||||||
|
return WirelessLinkedResponse()
|
||||||
|
|
||||||
|
def WirelessConfig(
|
||||||
|
self, request: WirelessConfigRequest, context: ServicerContext
|
||||||
|
) -> WirelessConfigResponse:
|
||||||
|
session = self.get_session(request.session_id, context)
|
||||||
|
wireless = self.get_node(session, request.wireless_id, context, WirelessNode)
|
||||||
|
options1 = request.options1
|
||||||
|
options2 = options1
|
||||||
|
if request.HasField("options2"):
|
||||||
|
options2 = request.options2
|
||||||
|
wireless.link_config(request.node1_id, request.node2_id, options1, options2)
|
||||||
|
return WirelessConfigResponse()
|
||||||
|
|
|
@ -67,6 +67,7 @@ class NodeType(Enum):
|
||||||
CONTROL_NET = 13
|
CONTROL_NET = 13
|
||||||
DOCKER = 15
|
DOCKER = 15
|
||||||
LXC = 16
|
LXC = 16
|
||||||
|
WIRELESS = 17
|
||||||
|
|
||||||
|
|
||||||
class LinkType(Enum):
|
class LinkType(Enum):
|
||||||
|
|
|
@ -4,14 +4,25 @@ from typing import Any, Dict, List
|
||||||
from core.config import Configuration
|
from core.config import Configuration
|
||||||
from core.configservice.base import ConfigService, ConfigServiceMode
|
from core.configservice.base import ConfigService, ConfigServiceMode
|
||||||
from core.emane.nodes import EmaneNet
|
from core.emane.nodes import EmaneNet
|
||||||
from core.nodes.base import CoreNodeBase
|
from core.nodes.base import CoreNodeBase, NodeBase
|
||||||
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||||
from core.nodes.network import WlanNode
|
from core.nodes.network import WlanNode
|
||||||
|
from core.nodes.wireless import WirelessNode
|
||||||
|
|
||||||
GROUP: str = "FRR"
|
GROUP: str = "FRR"
|
||||||
FRR_STATE_DIR: str = "/var/run/frr"
|
FRR_STATE_DIR: str = "/var/run/frr"
|
||||||
|
|
||||||
|
|
||||||
|
def is_wireless(node: NodeBase) -> bool:
|
||||||
|
"""
|
||||||
|
Check if the node is a wireless type node.
|
||||||
|
|
||||||
|
:param node: node to check type for
|
||||||
|
:return: True if wireless type, False otherwise
|
||||||
|
"""
|
||||||
|
return isinstance(node, (WlanNode, EmaneNet, WirelessNode))
|
||||||
|
|
||||||
|
|
||||||
def has_mtu_mismatch(iface: CoreInterface) -> bool:
|
def has_mtu_mismatch(iface: CoreInterface) -> bool:
|
||||||
"""
|
"""
|
||||||
Helper to detect MTU mismatch and add the appropriate FRR
|
Helper to detect MTU mismatch and add the appropriate FRR
|
||||||
|
@ -324,7 +335,7 @@ class FRRBabel(FrrService, ConfigService):
|
||||||
return self.render_text(text, data)
|
return self.render_text(text, data)
|
||||||
|
|
||||||
def frr_iface_config(self, iface: CoreInterface) -> str:
|
def frr_iface_config(self, iface: CoreInterface) -> str:
|
||||||
if isinstance(iface.net, (WlanNode, EmaneNet)):
|
if is_wireless(iface.net):
|
||||||
text = """
|
text = """
|
||||||
babel wireless
|
babel wireless
|
||||||
no babel split-horizon
|
no babel split-horizon
|
||||||
|
|
|
@ -5,16 +5,27 @@ from typing import Any, Dict, List
|
||||||
from core.config import Configuration
|
from core.config import Configuration
|
||||||
from core.configservice.base import ConfigService, ConfigServiceMode
|
from core.configservice.base import ConfigService, ConfigServiceMode
|
||||||
from core.emane.nodes import EmaneNet
|
from core.emane.nodes import EmaneNet
|
||||||
from core.nodes.base import CoreNodeBase
|
from core.nodes.base import CoreNodeBase, NodeBase
|
||||||
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||||
from core.nodes.network import PtpNet, WlanNode
|
from core.nodes.network import PtpNet, WlanNode
|
||||||
from core.nodes.physical import Rj45Node
|
from core.nodes.physical import Rj45Node
|
||||||
|
from core.nodes.wireless import WirelessNode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
GROUP: str = "Quagga"
|
GROUP: str = "Quagga"
|
||||||
QUAGGA_STATE_DIR: str = "/var/run/quagga"
|
QUAGGA_STATE_DIR: str = "/var/run/quagga"
|
||||||
|
|
||||||
|
|
||||||
|
def is_wireless(node: NodeBase) -> bool:
|
||||||
|
"""
|
||||||
|
Check if the node is a wireless type node.
|
||||||
|
|
||||||
|
:param node: node to check type for
|
||||||
|
:return: True if wireless type, False otherwise
|
||||||
|
"""
|
||||||
|
return isinstance(node, (WlanNode, EmaneNet, WirelessNode))
|
||||||
|
|
||||||
|
|
||||||
def has_mtu_mismatch(iface: CoreInterface) -> bool:
|
def has_mtu_mismatch(iface: CoreInterface) -> bool:
|
||||||
"""
|
"""
|
||||||
Helper to detect MTU mismatch and add the appropriate OSPF
|
Helper to detect MTU mismatch and add the appropriate OSPF
|
||||||
|
@ -265,7 +276,7 @@ class Ospfv3mdr(Ospfv3):
|
||||||
|
|
||||||
def quagga_iface_config(self, iface: CoreInterface) -> str:
|
def quagga_iface_config(self, iface: CoreInterface) -> str:
|
||||||
config = super().quagga_iface_config(iface)
|
config = super().quagga_iface_config(iface)
|
||||||
if isinstance(iface.net, (WlanNode, EmaneNet)):
|
if is_wireless(iface.net):
|
||||||
config = self.clean_text(
|
config = self.clean_text(
|
||||||
f"""
|
f"""
|
||||||
{config}
|
{config}
|
||||||
|
@ -390,7 +401,7 @@ class Babel(QuaggaService, ConfigService):
|
||||||
return self.render_text(text, data)
|
return self.render_text(text, data)
|
||||||
|
|
||||||
def quagga_iface_config(self, iface: CoreInterface) -> str:
|
def quagga_iface_config(self, iface: CoreInterface) -> str:
|
||||||
if isinstance(iface.net, (WlanNode, EmaneNet)):
|
if is_wireless(iface.net):
|
||||||
text = """
|
text = """
|
||||||
babel wireless
|
babel wireless
|
||||||
no babel split-horizon
|
no babel split-horizon
|
||||||
|
|
|
@ -49,6 +49,7 @@ class NodeTypes(Enum):
|
||||||
CONTROL_NET = 13
|
CONTROL_NET = 13
|
||||||
DOCKER = 15
|
DOCKER = 15
|
||||||
LXC = 16
|
LXC = 16
|
||||||
|
WIRELESS = 17
|
||||||
|
|
||||||
|
|
||||||
class LinkTypes(Enum):
|
class LinkTypes(Enum):
|
||||||
|
|
|
@ -58,6 +58,7 @@ from core.nodes.network import (
|
||||||
WlanNode,
|
WlanNode,
|
||||||
)
|
)
|
||||||
from core.nodes.physical import PhysicalNode, Rj45Node
|
from core.nodes.physical import PhysicalNode, Rj45Node
|
||||||
|
from core.nodes.wireless import WirelessNode
|
||||||
from core.plugins.sdt import Sdt
|
from core.plugins.sdt import Sdt
|
||||||
from core.services.coreservices import CoreServices
|
from core.services.coreservices import CoreServices
|
||||||
from core.xml import corexml, corexmldeployment
|
from core.xml import corexml, corexmldeployment
|
||||||
|
@ -80,13 +81,18 @@ NODES: Dict[NodeTypes, Type[NodeBase]] = {
|
||||||
NodeTypes.CONTROL_NET: CtrlNet,
|
NodeTypes.CONTROL_NET: CtrlNet,
|
||||||
NodeTypes.DOCKER: DockerNode,
|
NodeTypes.DOCKER: DockerNode,
|
||||||
NodeTypes.LXC: LxcNode,
|
NodeTypes.LXC: LxcNode,
|
||||||
|
NodeTypes.WIRELESS: WirelessNode,
|
||||||
}
|
}
|
||||||
NODES_TYPE: Dict[Type[NodeBase], NodeTypes] = {NODES[x]: x for x in NODES}
|
NODES_TYPE: Dict[Type[NodeBase], NodeTypes] = {NODES[x]: x for x in NODES}
|
||||||
CONTAINER_NODES: Set[Type[NodeBase]] = {DockerNode, LxcNode}
|
CONTAINER_NODES: Set[Type[NodeBase]] = {DockerNode, LxcNode}
|
||||||
CTRL_NET_ID: int = 9001
|
CTRL_NET_ID: int = 9001
|
||||||
LINK_COLORS: List[str] = ["green", "blue", "orange", "purple", "turquoise"]
|
LINK_COLORS: List[str] = ["green", "blue", "orange", "purple", "turquoise"]
|
||||||
NT: TypeVar = TypeVar("NT", bound=NodeBase)
|
NT: TypeVar = TypeVar("NT", bound=NodeBase)
|
||||||
WIRELESS_TYPE: Tuple[Type[WlanNode], Type[EmaneNet]] = (WlanNode, EmaneNet)
|
WIRELESS_TYPE: Tuple[Type[WlanNode], Type[EmaneNet], Type[WirelessNode]] = (
|
||||||
|
WlanNode,
|
||||||
|
EmaneNet,
|
||||||
|
WirelessNode,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Session:
|
class Session:
|
||||||
|
@ -274,9 +280,9 @@ class Session:
|
||||||
# custom links
|
# custom links
|
||||||
iface1 = None
|
iface1 = None
|
||||||
iface2 = None
|
iface2 = None
|
||||||
if isinstance(node1, WlanNode):
|
if isinstance(node1, (WlanNode, WirelessNode)):
|
||||||
iface2 = self._add_wlan_link(node2, iface2_data, node1)
|
iface2 = self._add_wlan_link(node2, iface2_data, node1)
|
||||||
elif isinstance(node2, WlanNode):
|
elif isinstance(node2, (WlanNode, WirelessNode)):
|
||||||
iface1 = self._add_wlan_link(node1, iface1_data, node2)
|
iface1 = self._add_wlan_link(node1, iface1_data, node2)
|
||||||
elif isinstance(node1, EmaneNet) and isinstance(node2, CoreNode):
|
elif isinstance(node1, EmaneNet) and isinstance(node2, CoreNode):
|
||||||
iface2 = self._add_emane_link(node2, iface2_data, node1)
|
iface2 = self._add_emane_link(node2, iface2_data, node1)
|
||||||
|
@ -298,7 +304,10 @@ class Session:
|
||||||
return iface1, iface2
|
return iface1, iface2
|
||||||
|
|
||||||
def _add_wlan_link(
|
def _add_wlan_link(
|
||||||
self, node: NodeBase, iface_data: InterfaceData, net: WlanNode
|
self,
|
||||||
|
node: NodeBase,
|
||||||
|
iface_data: InterfaceData,
|
||||||
|
net: Union[WlanNode, WirelessNode],
|
||||||
) -> CoreInterface:
|
) -> CoreInterface:
|
||||||
"""
|
"""
|
||||||
Create a wlan link.
|
Create a wlan link.
|
||||||
|
@ -392,10 +401,10 @@ class Session:
|
||||||
)
|
)
|
||||||
iface1 = None
|
iface1 = None
|
||||||
iface2 = None
|
iface2 = None
|
||||||
if isinstance(node1, WlanNode):
|
if isinstance(node1, (WlanNode, WirelessNode)):
|
||||||
iface2 = node2.delete_iface(iface2_id)
|
iface2 = node2.delete_iface(iface2_id)
|
||||||
node1.detach(iface2)
|
node1.detach(iface2)
|
||||||
elif isinstance(node2, WlanNode):
|
elif isinstance(node2, (WlanNode, WirelessNode)):
|
||||||
iface1 = node1.delete_iface(iface1_id)
|
iface1 = node1.delete_iface(iface1_id)
|
||||||
node2.detach(iface1)
|
node2.detach(iface1)
|
||||||
elif isinstance(node1, EmaneNet):
|
elif isinstance(node1, EmaneNet):
|
||||||
|
@ -1111,6 +1120,10 @@ class Session:
|
||||||
# boot node services and then start mobility
|
# boot node services and then start mobility
|
||||||
exceptions = self.boot_nodes()
|
exceptions = self.boot_nodes()
|
||||||
if not exceptions:
|
if not exceptions:
|
||||||
|
# complete wireless node
|
||||||
|
for node in self.nodes.values():
|
||||||
|
if isinstance(node, WirelessNode):
|
||||||
|
node.post_startup()
|
||||||
self.mobility.startup()
|
self.mobility.startup()
|
||||||
# notify listeners that instantiation is complete
|
# notify listeners that instantiation is complete
|
||||||
event = EventData(event_type=EventTypes.INSTANTIATION_COMPLETE)
|
event = EventData(event_type=EventTypes.INSTANTIATION_COMPLETE)
|
||||||
|
|
BIN
daemon/core/gui/data/icons/wireless.png
Normal file
BIN
daemon/core/gui/data/icons/wireless.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
|
@ -53,6 +53,7 @@ class ImageEnum(Enum):
|
||||||
LINK = "link"
|
LINK = "link"
|
||||||
HUB = "hub"
|
HUB = "hub"
|
||||||
WLAN = "wlan"
|
WLAN = "wlan"
|
||||||
|
WIRELESS = "wireless"
|
||||||
EMANE = "emane"
|
EMANE = "emane"
|
||||||
RJ45 = "rj45"
|
RJ45 = "rj45"
|
||||||
TUNNEL = "tunnel"
|
TUNNEL = "tunnel"
|
||||||
|
|
|
@ -18,7 +18,11 @@ NETWORK_NODES: List["NodeDraw"] = []
|
||||||
NODE_ICONS = {}
|
NODE_ICONS = {}
|
||||||
CONTAINER_NODES: Set[NodeType] = {NodeType.DEFAULT, NodeType.DOCKER, NodeType.LXC}
|
CONTAINER_NODES: Set[NodeType] = {NodeType.DEFAULT, NodeType.DOCKER, NodeType.LXC}
|
||||||
IMAGE_NODES: Set[NodeType] = {NodeType.DOCKER, NodeType.LXC}
|
IMAGE_NODES: Set[NodeType] = {NodeType.DOCKER, NodeType.LXC}
|
||||||
WIRELESS_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE}
|
WIRELESS_NODES: Set[NodeType] = {
|
||||||
|
NodeType.WIRELESS_LAN,
|
||||||
|
NodeType.EMANE,
|
||||||
|
NodeType.WIRELESS,
|
||||||
|
}
|
||||||
RJ45_NODES: Set[NodeType] = {NodeType.RJ45}
|
RJ45_NODES: Set[NodeType] = {NodeType.RJ45}
|
||||||
BRIDGE_NODES: Set[NodeType] = {NodeType.HUB, NodeType.SWITCH}
|
BRIDGE_NODES: Set[NodeType] = {NodeType.HUB, NodeType.SWITCH}
|
||||||
IGNORE_NODES: Set[NodeType] = {NodeType.CONTROL_NET}
|
IGNORE_NODES: Set[NodeType] = {NodeType.CONTROL_NET}
|
||||||
|
@ -46,6 +50,7 @@ def setup() -> None:
|
||||||
(ImageEnum.HUB, NodeType.HUB, "Hub"),
|
(ImageEnum.HUB, NodeType.HUB, "Hub"),
|
||||||
(ImageEnum.SWITCH, NodeType.SWITCH, "Switch"),
|
(ImageEnum.SWITCH, NodeType.SWITCH, "Switch"),
|
||||||
(ImageEnum.WLAN, NodeType.WIRELESS_LAN, "WLAN"),
|
(ImageEnum.WLAN, NodeType.WIRELESS_LAN, "WLAN"),
|
||||||
|
(ImageEnum.WIRELESS, NodeType.WIRELESS, "Wireless"),
|
||||||
(ImageEnum.EMANE, NodeType.EMANE, "EMANE"),
|
(ImageEnum.EMANE, NodeType.EMANE, "EMANE"),
|
||||||
(ImageEnum.RJ45, NodeType.RJ45, "RJ45"),
|
(ImageEnum.RJ45, NodeType.RJ45, "RJ45"),
|
||||||
(ImageEnum.TUNNEL, NodeType.TUNNEL, "Tunnel"),
|
(ImageEnum.TUNNEL, NodeType.TUNNEL, "Tunnel"),
|
||||||
|
|
|
@ -766,8 +766,9 @@ class CoreNode(CoreNodeBase):
|
||||||
raise CoreError(f"adopting unknown iface({iface.name})")
|
raise CoreError(f"adopting unknown iface({iface.name})")
|
||||||
# add iface to container namespace
|
# add iface to container namespace
|
||||||
self.net_client.device_ns(iface.name, str(self.pid))
|
self.net_client.device_ns(iface.name, str(self.pid))
|
||||||
# update iface name to container name
|
# use default iface name for container, if a unique name was not provided
|
||||||
name = name if name else f"eth{iface_id}"
|
if iface.name == name:
|
||||||
|
name = f"eth{iface_id}"
|
||||||
self.node_net_client.device_name(iface.name, name)
|
self.node_net_client.device_name(iface.name, name)
|
||||||
iface.name = name
|
iface.name = name
|
||||||
# turn checksums off
|
# turn checksums off
|
||||||
|
|
240
daemon/core/nodes/wireless.py
Normal file
240
daemon/core/nodes/wireless.py
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
"""
|
||||||
|
Defines a wireless node that allows programmatic link connectivity and
|
||||||
|
configuration between pairs of nodes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import TYPE_CHECKING, Dict, Tuple
|
||||||
|
|
||||||
|
from core.emulator.data import LinkData, LinkOptions
|
||||||
|
from core.emulator.enumerations import LinkTypes, MessageFlags
|
||||||
|
from core.errors import CoreError
|
||||||
|
from core.executables import NFTABLES
|
||||||
|
from core.nodes.base import CoreNetworkBase
|
||||||
|
from core.nodes.interface import CoreInterface
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.emulator.session import Session
|
||||||
|
from core.emulator.distributed import DistributedServer
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def calc_distance(
|
||||||
|
point1: Tuple[float, float, float], point2: Tuple[float, float, float]
|
||||||
|
) -> float:
|
||||||
|
a = point1[0] - point2[0]
|
||||||
|
b = point1[1] - point2[1]
|
||||||
|
c = 0
|
||||||
|
if point1[2] is not None and point2[2] is not None:
|
||||||
|
c = point1[2] - point2[2]
|
||||||
|
return math.hypot(math.hypot(a, b), c)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class WirelessLink:
|
||||||
|
bridge1: str
|
||||||
|
bridge2: str
|
||||||
|
iface: CoreInterface
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessNode(CoreNetworkBase):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
session: "Session",
|
||||||
|
_id: int,
|
||||||
|
name: str,
|
||||||
|
server: "DistributedServer" = None,
|
||||||
|
):
|
||||||
|
super().__init__(session, _id, name, server)
|
||||||
|
self.bridges: Dict[int, Tuple[CoreInterface, str]] = {}
|
||||||
|
self.links: Dict[Tuple[int, int], WirelessLink] = {}
|
||||||
|
self.labels: Dict[Tuple[int, int], str] = {}
|
||||||
|
|
||||||
|
def startup(self) -> None:
|
||||||
|
if self.up:
|
||||||
|
return
|
||||||
|
self.up = True
|
||||||
|
|
||||||
|
def shutdown(self) -> None:
|
||||||
|
while self.bridges:
|
||||||
|
_, (_, bridge_name) = self.bridges.popitem()
|
||||||
|
self.net_client.delete_bridge(bridge_name)
|
||||||
|
self.host_cmd(f"{NFTABLES} delete table bridge {bridge_name}")
|
||||||
|
while self.links:
|
||||||
|
_, link = self.links.popitem()
|
||||||
|
link.iface.shutdown()
|
||||||
|
self.up = False
|
||||||
|
|
||||||
|
def attach(self, iface: CoreInterface) -> None:
|
||||||
|
super().attach(iface)
|
||||||
|
logging.info("attaching node(%s) iface(%s)", iface.node.name, iface.name)
|
||||||
|
if self.up:
|
||||||
|
# create node unique bridge
|
||||||
|
bridge_name = f"wb{iface.node.id}.{self.id}.{self.session.id}"
|
||||||
|
self.net_client.create_bridge(bridge_name)
|
||||||
|
# setup initial bridge rules
|
||||||
|
self.host_cmd(f'{NFTABLES} "add table bridge {bridge_name}"')
|
||||||
|
self.host_cmd(
|
||||||
|
f"{NFTABLES} "
|
||||||
|
f"'add chain bridge {bridge_name} forward {{type filter hook "
|
||||||
|
f"forward priority -1; policy drop;}}'"
|
||||||
|
)
|
||||||
|
self.host_cmd(
|
||||||
|
f"{NFTABLES} "
|
||||||
|
f"'add rule bridge {bridge_name} forward "
|
||||||
|
f"ibriport != {bridge_name} accept'"
|
||||||
|
)
|
||||||
|
# associate node iface with bridge
|
||||||
|
iface.net_client.set_iface_master(bridge_name, iface.localname)
|
||||||
|
# assign position callback
|
||||||
|
iface.poshook = self.position_callback
|
||||||
|
# save created bridge
|
||||||
|
self.bridges[iface.node.id] = (iface, bridge_name)
|
||||||
|
|
||||||
|
def post_startup(self) -> None:
|
||||||
|
routes = {}
|
||||||
|
for node_id, (iface, bridge_name) in self.bridges.items():
|
||||||
|
for onode_id, (oiface, obridge_name) in self.bridges.items():
|
||||||
|
if node_id == onode_id:
|
||||||
|
continue
|
||||||
|
if node_id < onode_id:
|
||||||
|
node1, node2 = iface.node, oiface.node
|
||||||
|
bridge1, bridge2 = bridge_name, obridge_name
|
||||||
|
else:
|
||||||
|
node1, node2 = oiface.node, iface.node
|
||||||
|
bridge1, bridge2 = obridge_name, bridge_name
|
||||||
|
key = (node1.id, node2.id)
|
||||||
|
if key in self.links:
|
||||||
|
continue
|
||||||
|
# create node to node link
|
||||||
|
session_id = self.session.short_session_id()
|
||||||
|
name1 = f"we{self.id}.{node1.id}.{node2.id}.{session_id}"
|
||||||
|
name2 = f"we{self.id}.{node2.id}.{node1.id}.{session_id}"
|
||||||
|
link_iface = CoreInterface(0, name1, name2, self.session.use_ovs())
|
||||||
|
link_iface.startup()
|
||||||
|
link = WirelessLink(bridge1, bridge2, link_iface)
|
||||||
|
self.links[key] = link
|
||||||
|
# assign ifaces to respective bridges
|
||||||
|
self.net_client.set_iface_master(bridge1, link_iface.name)
|
||||||
|
self.net_client.set_iface_master(bridge2, link_iface.localname)
|
||||||
|
# track bridge routes
|
||||||
|
node1_routes = routes.setdefault(node1.id, set())
|
||||||
|
node1_routes.add(name1)
|
||||||
|
node2_routes = routes.setdefault(node2.id, set())
|
||||||
|
node2_routes.add(name2)
|
||||||
|
# send link
|
||||||
|
self.send_link(node1.id, node2.id, MessageFlags.ADD)
|
||||||
|
for node_id, ifaces in routes.items():
|
||||||
|
iface, bridge_name = self.bridges[node_id]
|
||||||
|
ifaces = ",".join(ifaces)
|
||||||
|
# out routes
|
||||||
|
self.host_cmd(
|
||||||
|
f"{NFTABLES} "
|
||||||
|
f'"add rule bridge {bridge_name} forward '
|
||||||
|
f"iif {iface.localname} oif {{{ifaces}}} "
|
||||||
|
f'accept"'
|
||||||
|
)
|
||||||
|
# in routes
|
||||||
|
self.host_cmd(
|
||||||
|
f"{NFTABLES} "
|
||||||
|
f'"add rule bridge {bridge_name} forward '
|
||||||
|
f"iif {{{ifaces}}} oif {iface.localname} "
|
||||||
|
f'accept"'
|
||||||
|
)
|
||||||
|
|
||||||
|
def link_control(self, node1_id: int, node2_id: int, linked: bool) -> None:
|
||||||
|
key = (node1_id, node2_id) if node1_id < node2_id else (node2_id, node1_id)
|
||||||
|
link = self.links.get(key)
|
||||||
|
if not link:
|
||||||
|
raise CoreError(f"invalid node links node1({node1_id}) node2({node2_id})")
|
||||||
|
bridge1, bridge2 = link.bridge1, link.bridge2
|
||||||
|
iface = link.iface
|
||||||
|
if linked:
|
||||||
|
self.net_client.set_iface_master(bridge1, iface.name)
|
||||||
|
self.net_client.set_iface_master(bridge2, iface.localname)
|
||||||
|
message_type = MessageFlags.ADD
|
||||||
|
else:
|
||||||
|
self.net_client.delete_iface(bridge1, iface.name)
|
||||||
|
self.net_client.delete_iface(bridge2, iface.localname)
|
||||||
|
message_type = MessageFlags.DELETE
|
||||||
|
label = self.labels.get(key)
|
||||||
|
self.send_link(key[0], key[1], message_type, label)
|
||||||
|
|
||||||
|
def link_config(
|
||||||
|
self, node1_id: int, node2_id: int, options1: LinkOptions, options2: LinkOptions
|
||||||
|
) -> None:
|
||||||
|
key = (node1_id, node2_id) if node1_id < node2_id else (node2_id, node1_id)
|
||||||
|
link = self.links.get(key)
|
||||||
|
if not link:
|
||||||
|
raise CoreError(f"invalid node links node1({node1_id}) node2({node2_id})")
|
||||||
|
iface = link.iface
|
||||||
|
iface.options.update(options1)
|
||||||
|
iface.set_config()
|
||||||
|
name, localname = iface.name, iface.localname
|
||||||
|
iface.name, iface.localname = localname, name
|
||||||
|
iface.options.update(options2)
|
||||||
|
iface.set_config()
|
||||||
|
iface.name, iface.localname = name, localname
|
||||||
|
if options1 == options2:
|
||||||
|
if options1.is_clear():
|
||||||
|
label = ""
|
||||||
|
else:
|
||||||
|
label = f"{options1.loss:.2f}%/{options1.delay}us"
|
||||||
|
else:
|
||||||
|
label = (
|
||||||
|
f"({options1.loss:.2f}%/{options1.delay}us) "
|
||||||
|
f"({options2.loss:.2f}%/{options2.delay}us)"
|
||||||
|
)
|
||||||
|
self.labels[key] = label
|
||||||
|
self.send_link(key[0], key[1], MessageFlags.NONE, label)
|
||||||
|
|
||||||
|
def send_link(
|
||||||
|
self,
|
||||||
|
node1_id: int,
|
||||||
|
node2_id: int,
|
||||||
|
message_type: MessageFlags,
|
||||||
|
label: str = None,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Broadcasts out a wireless link/unlink message.
|
||||||
|
|
||||||
|
:param node1_id: first node in link
|
||||||
|
:param node2_id: second node in link
|
||||||
|
:param message_type: type of link message to send
|
||||||
|
:param label: label to display for link
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
color = self.session.get_link_color(self.id)
|
||||||
|
link_data = LinkData(
|
||||||
|
message_type=message_type,
|
||||||
|
type=LinkTypes.WIRELESS,
|
||||||
|
node1_id=node1_id,
|
||||||
|
node2_id=node2_id,
|
||||||
|
network_id=self.id,
|
||||||
|
color=color,
|
||||||
|
label=label,
|
||||||
|
)
|
||||||
|
self.session.broadcast_link(link_data)
|
||||||
|
|
||||||
|
def position_callback(self, iface: CoreInterface) -> None:
|
||||||
|
for oiface, bridge_name in self.bridges.values():
|
||||||
|
if iface == oiface:
|
||||||
|
continue
|
||||||
|
point1 = iface.node.position.get()
|
||||||
|
point2 = oiface.node.position.get()
|
||||||
|
distance = calc_distance(point1, point2) - 250
|
||||||
|
distance = max(distance, 0.0)
|
||||||
|
logger.info(
|
||||||
|
"n1(%s) n2(%s) d(%s)", iface.node.name, oiface.node.name, distance
|
||||||
|
)
|
||||||
|
loss = min((distance / 500) * 100.0, 100.0)
|
||||||
|
node1_id = iface.node.id
|
||||||
|
node2_id = oiface.node.id
|
||||||
|
options = LinkOptions(loss=loss, delay=0)
|
||||||
|
self.link_config(node1_id, node2_id, options, options)
|
||||||
|
|
||||||
|
def adopt_iface(self, iface: CoreInterface, name: str) -> None:
|
||||||
|
raise CoreError(f"{type(self)} does not support adopt interface")
|
|
@ -4,7 +4,7 @@ sdt.py: Scripted Display Tool (SDT3D) helper
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
from typing import IO, TYPE_CHECKING, Dict, List, Optional, Set, Tuple
|
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from core.constants import CORE_CONF_DIR, CORE_DATA_DIR
|
from core.constants import CORE_CONF_DIR, CORE_DATA_DIR
|
||||||
|
@ -12,22 +12,15 @@ from core.emane.nodes import EmaneNet
|
||||||
from core.emulator.data import LinkData, NodeData
|
from core.emulator.data import LinkData, NodeData
|
||||||
from core.emulator.enumerations import EventTypes, MessageFlags
|
from core.emulator.enumerations import EventTypes, MessageFlags
|
||||||
from core.errors import CoreError
|
from core.errors import CoreError
|
||||||
from core.nodes.base import CoreNetworkBase, NodeBase
|
from core.nodes.base import NodeBase
|
||||||
from core.nodes.network import WlanNode
|
from core.nodes.network import WlanNode
|
||||||
|
from core.nodes.wireless import WirelessNode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.emulator.session import Session
|
from core.emulator.session import Session
|
||||||
|
|
||||||
|
|
||||||
def get_link_id(node1_id: int, node2_id: int, network_id: int) -> str:
|
|
||||||
link_id = f"{node1_id}-{node2_id}"
|
|
||||||
if network_id is not None:
|
|
||||||
link_id = f"{link_id}-{network_id}"
|
|
||||||
return link_id
|
|
||||||
|
|
||||||
|
|
||||||
CORE_LAYER: str = "CORE"
|
CORE_LAYER: str = "CORE"
|
||||||
NODE_LAYER: str = "CORE::Nodes"
|
NODE_LAYER: str = "CORE::Nodes"
|
||||||
LINK_LAYER: str = "CORE::Links"
|
LINK_LAYER: str = "CORE::Links"
|
||||||
|
@ -36,6 +29,17 @@ CORE_LAYERS: List[str] = [CORE_LAYER, LINK_LAYER, NODE_LAYER, WIRED_LINK_LAYER]
|
||||||
DEFAULT_LINK_COLOR: str = "red"
|
DEFAULT_LINK_COLOR: str = "red"
|
||||||
|
|
||||||
|
|
||||||
|
def is_wireless(node: NodeBase) -> bool:
|
||||||
|
return isinstance(node, (WlanNode, EmaneNet, WirelessNode))
|
||||||
|
|
||||||
|
|
||||||
|
def get_link_id(node1_id: int, node2_id: int, network_id: int) -> str:
|
||||||
|
link_id = f"{node1_id}-{node2_id}"
|
||||||
|
if network_id is not None:
|
||||||
|
link_id = f"{link_id}-{network_id}"
|
||||||
|
return link_id
|
||||||
|
|
||||||
|
|
||||||
class Sdt:
|
class Sdt:
|
||||||
"""
|
"""
|
||||||
Helper class for exporting session objects to NRL"s SDT3D.
|
Helper class for exporting session objects to NRL"s SDT3D.
|
||||||
|
@ -67,7 +71,7 @@ class Sdt:
|
||||||
:param session: session this manager is tied to
|
:param session: session this manager is tied to
|
||||||
"""
|
"""
|
||||||
self.session: "Session" = session
|
self.session: "Session" = session
|
||||||
self.sock: Optional[IO] = None
|
self.sock: Optional[socket.socket] = None
|
||||||
self.connected: bool = False
|
self.connected: bool = False
|
||||||
self.url: str = self.DEFAULT_SDT_URL
|
self.url: str = self.DEFAULT_SDT_URL
|
||||||
self.address: Optional[Tuple[Optional[str], Optional[int]]] = None
|
self.address: Optional[Tuple[Optional[str], Optional[int]]] = None
|
||||||
|
@ -210,26 +214,23 @@ class Sdt:
|
||||||
|
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
nets = []
|
|
||||||
# create layers
|
|
||||||
for layer in CORE_LAYERS:
|
for layer in CORE_LAYERS:
|
||||||
self.cmd(f"layer {layer}")
|
self.cmd(f"layer {layer}")
|
||||||
|
|
||||||
with self.session.nodes_lock:
|
with self.session.nodes_lock:
|
||||||
for node_id in self.session.nodes:
|
nets = []
|
||||||
node = self.session.nodes[node_id]
|
for node in self.session.nodes.values():
|
||||||
if isinstance(node, CoreNetworkBase):
|
if isinstance(node, (EmaneNet, WlanNode)):
|
||||||
nets.append(node)
|
nets.append(node)
|
||||||
if not isinstance(node, NodeBase):
|
if not isinstance(node, NodeBase):
|
||||||
continue
|
continue
|
||||||
self.add_node(node)
|
self.add_node(node)
|
||||||
|
for link in self.session.link_manager.links():
|
||||||
for net in nets:
|
if is_wireless(link.node1) or is_wireless(link.node2):
|
||||||
all_links = net.links(flags=MessageFlags.ADD)
|
|
||||||
for link_data in all_links:
|
|
||||||
is_wireless = isinstance(net, (WlanNode, EmaneNet))
|
|
||||||
if is_wireless and link_data.node1_id == net.id:
|
|
||||||
continue
|
continue
|
||||||
|
link_data = link.get_data(MessageFlags.ADD)
|
||||||
|
self.handle_link_update(link_data)
|
||||||
|
for net in nets:
|
||||||
|
for link_data in net.links(MessageFlags.ADD):
|
||||||
self.handle_link_update(link_data)
|
self.handle_link_update(link_data)
|
||||||
|
|
||||||
def get_node_position(self, node: NodeBase) -> Optional[str]:
|
def get_node_position(self, node: NodeBase) -> Optional[str]:
|
||||||
|
@ -341,7 +342,7 @@ class Sdt:
|
||||||
result = False
|
result = False
|
||||||
try:
|
try:
|
||||||
node = self.session.get_node(node_id, NodeBase)
|
node = self.session.get_node(node_id, NodeBase)
|
||||||
result = isinstance(node, (WlanNode, EmaneNet))
|
result = isinstance(node, (WlanNode, EmaneNet, WirelessNode))
|
||||||
except CoreError:
|
except CoreError:
|
||||||
pass
|
pass
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -7,15 +7,26 @@ from typing import Optional, Tuple
|
||||||
import netaddr
|
import netaddr
|
||||||
|
|
||||||
from core.emane.nodes import EmaneNet
|
from core.emane.nodes import EmaneNet
|
||||||
from core.nodes.base import CoreNode
|
from core.nodes.base import CoreNode, NodeBase
|
||||||
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||||
from core.nodes.network import PtpNet, WlanNode
|
from core.nodes.network import PtpNet, WlanNode
|
||||||
from core.nodes.physical import Rj45Node
|
from core.nodes.physical import Rj45Node
|
||||||
|
from core.nodes.wireless import WirelessNode
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
|
|
||||||
FRR_STATE_DIR: str = "/var/run/frr"
|
FRR_STATE_DIR: str = "/var/run/frr"
|
||||||
|
|
||||||
|
|
||||||
|
def is_wireless(node: NodeBase) -> bool:
|
||||||
|
"""
|
||||||
|
Check if the node is a wireless type node.
|
||||||
|
|
||||||
|
:param node: node to check type for
|
||||||
|
:return: True if wireless type, False otherwise
|
||||||
|
"""
|
||||||
|
return isinstance(node, (WlanNode, EmaneNet, WirelessNode))
|
||||||
|
|
||||||
|
|
||||||
class FRRZebra(CoreService):
|
class FRRZebra(CoreService):
|
||||||
name: str = "FRRzebra"
|
name: str = "FRRzebra"
|
||||||
group: str = "FRR"
|
group: str = "FRR"
|
||||||
|
@ -593,7 +604,7 @@ class FRRBabel(FrrService):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate_frr_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
|
def generate_frr_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
|
||||||
if iface.net and isinstance(iface.net, (EmaneNet, WlanNode)):
|
if is_wireless(iface.net):
|
||||||
return " babel wireless\n no babel split-horizon\n"
|
return " babel wireless\n no babel split-horizon\n"
|
||||||
else:
|
else:
|
||||||
return " babel wired\n babel split-horizon\n"
|
return " babel wired\n babel split-horizon\n"
|
||||||
|
|
|
@ -7,15 +7,26 @@ import netaddr
|
||||||
|
|
||||||
from core.emane.nodes import EmaneNet
|
from core.emane.nodes import EmaneNet
|
||||||
from core.emulator.enumerations import LinkTypes
|
from core.emulator.enumerations import LinkTypes
|
||||||
from core.nodes.base import CoreNode
|
from core.nodes.base import CoreNode, NodeBase
|
||||||
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
from core.nodes.interface import DEFAULT_MTU, CoreInterface
|
||||||
from core.nodes.network import PtpNet, WlanNode
|
from core.nodes.network import PtpNet, WlanNode
|
||||||
from core.nodes.physical import Rj45Node
|
from core.nodes.physical import Rj45Node
|
||||||
|
from core.nodes.wireless import WirelessNode
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
|
|
||||||
QUAGGA_STATE_DIR: str = "/var/run/quagga"
|
QUAGGA_STATE_DIR: str = "/var/run/quagga"
|
||||||
|
|
||||||
|
|
||||||
|
def is_wireless(node: NodeBase) -> bool:
|
||||||
|
"""
|
||||||
|
Check if the node is a wireless type node.
|
||||||
|
|
||||||
|
:param node: node to check type for
|
||||||
|
:return: True if wireless type, False otherwise
|
||||||
|
"""
|
||||||
|
return isinstance(node, (WlanNode, EmaneNet, WirelessNode))
|
||||||
|
|
||||||
|
|
||||||
class Zebra(CoreService):
|
class Zebra(CoreService):
|
||||||
name: str = "zebra"
|
name: str = "zebra"
|
||||||
group: str = "Quagga"
|
group: str = "Quagga"
|
||||||
|
@ -431,7 +442,7 @@ class Ospfv3mdr(Ospfv3):
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate_quagga_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
|
def generate_quagga_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
|
||||||
cfg = cls.mtu_check(iface)
|
cfg = cls.mtu_check(iface)
|
||||||
if iface.net is not None and isinstance(iface.net, (WlanNode, EmaneNet)):
|
if is_wireless(iface.net):
|
||||||
return (
|
return (
|
||||||
cfg
|
cfg
|
||||||
+ """\
|
+ """\
|
||||||
|
|
|
@ -16,6 +16,7 @@ from core.nodes.docker import DockerNode
|
||||||
from core.nodes.interface import CoreInterface
|
from core.nodes.interface import CoreInterface
|
||||||
from core.nodes.lxd import LxcNode
|
from core.nodes.lxd import LxcNode
|
||||||
from core.nodes.network import CtrlNet, GreTapBridge, WlanNode
|
from core.nodes.network import CtrlNet, GreTapBridge, WlanNode
|
||||||
|
from core.nodes.wireless import WirelessNode
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -542,8 +543,8 @@ class CoreXmlWriter:
|
||||||
iface2 = self.create_iface_element("iface2", iface2)
|
iface2 = self.create_iface_element("iface2", iface2)
|
||||||
link_element.append(iface2)
|
link_element.append(iface2)
|
||||||
# check for options, don't write for emane/wlan links
|
# check for options, don't write for emane/wlan links
|
||||||
is_node1_wireless = isinstance(node1, (WlanNode, EmaneNet))
|
is_node1_wireless = isinstance(node1, (WlanNode, EmaneNet, WirelessNode))
|
||||||
is_node2_wireless = isinstance(node2, (WlanNode, EmaneNet))
|
is_node2_wireless = isinstance(node2, (WlanNode, EmaneNet, WirelessNode))
|
||||||
if not (is_node1_wireless or is_node2_wireless):
|
if not (is_node1_wireless or is_node2_wireless):
|
||||||
unidirectional = 1 if unidirectional else 0
|
unidirectional = 1 if unidirectional else 0
|
||||||
options_element = etree.Element("options")
|
options_element = etree.Element("options")
|
||||||
|
|
29
daemon/examples/grpc/wireless.py
Normal file
29
daemon/examples/grpc/wireless.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
from core.api.grpc import client
|
||||||
|
from core.api.grpc.wrappers import NodeType, Position
|
||||||
|
|
||||||
|
# interface helper
|
||||||
|
iface_helper = client.InterfaceHelper(ip4_prefix="10.0.0.0/24", ip6_prefix="2001::/64")
|
||||||
|
|
||||||
|
# create grpc client and connect
|
||||||
|
core = client.CoreGrpcClient()
|
||||||
|
core.connect()
|
||||||
|
|
||||||
|
# add session
|
||||||
|
session = core.create_session()
|
||||||
|
|
||||||
|
# create nodes
|
||||||
|
position = Position(x=200, y=200)
|
||||||
|
wlan = session.add_node(1, _type=NodeType.WIRELESS, position=position)
|
||||||
|
position = Position(x=100, y=100)
|
||||||
|
node1 = session.add_node(2, model="mdr", position=position)
|
||||||
|
position = Position(x=300, y=100)
|
||||||
|
node2 = session.add_node(3, model="mdr", position=position)
|
||||||
|
|
||||||
|
# create links
|
||||||
|
iface1 = iface_helper.create_iface(node1.id, 0)
|
||||||
|
session.add_link(node1=node1, node2=wlan, iface1=iface1)
|
||||||
|
iface1 = iface_helper.create_iface(node2.id, 0)
|
||||||
|
session.add_link(node1=node2, node2=wlan, iface1=iface1)
|
||||||
|
|
||||||
|
# start session
|
||||||
|
core.start_session(session)
|
46
daemon/examples/python/wireless.py
Normal file
46
daemon/examples/python/wireless.py
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# required imports
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from core.emulator.coreemu import CoreEmu
|
||||||
|
from core.emulator.data import IpPrefixes, NodeOptions
|
||||||
|
from core.emulator.enumerations import EventTypes
|
||||||
|
from core.nodes.base import CoreNode
|
||||||
|
from core.nodes.network import WlanNode
|
||||||
|
|
||||||
|
# enable info logging
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
# ip nerator for example
|
||||||
|
ip_prefixes = IpPrefixes(ip4_prefix="10.0.0.0/24")
|
||||||
|
|
||||||
|
# create emulator instance for creating sessions and utility methods
|
||||||
|
coreemu = CoreEmu()
|
||||||
|
session = coreemu.create_session()
|
||||||
|
|
||||||
|
# must be in configuration state for nodes to start
|
||||||
|
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||||
|
|
||||||
|
# create wireless
|
||||||
|
options = NodeOptions(x=200, y=200)
|
||||||
|
wireless = session.add_node(WlanNode, options=options)
|
||||||
|
|
||||||
|
# create nodes
|
||||||
|
options = NodeOptions(model="mdr", x=100, y=100)
|
||||||
|
n1 = session.add_node(CoreNode, options=options)
|
||||||
|
options = NodeOptions(model="mdr", x=300, y=100)
|
||||||
|
n2 = session.add_node(CoreNode, options=options)
|
||||||
|
|
||||||
|
# link nodes to wireless
|
||||||
|
iface1 = ip_prefixes.create_iface(n1)
|
||||||
|
session.add_link(n1.id, wireless.id, iface1)
|
||||||
|
iface1 = ip_prefixes.create_iface(n2)
|
||||||
|
session.add_link(n2.id, wireless.id, iface1)
|
||||||
|
|
||||||
|
# start session
|
||||||
|
session.instantiate()
|
||||||
|
|
||||||
|
# do whatever you like here
|
||||||
|
input("press enter to shutdown")
|
||||||
|
|
||||||
|
# stop session
|
||||||
|
session.shutdown()
|
|
@ -100,6 +100,12 @@ service CoreApi {
|
||||||
rpc WlanLink (wlan.WlanLinkRequest) returns (wlan.WlanLinkResponse) {
|
rpc WlanLink (wlan.WlanLinkRequest) returns (wlan.WlanLinkResponse) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wireless rpc
|
||||||
|
rpc WirelessLinked (WirelessLinkedRequest) returns (WirelessLinkedResponse) {
|
||||||
|
}
|
||||||
|
rpc WirelessConfig (WirelessConfigRequest) returns (WirelessConfigResponse) {
|
||||||
|
}
|
||||||
|
|
||||||
// emane rpc
|
// emane rpc
|
||||||
rpc GetEmaneModelConfig (emane.GetEmaneModelConfigRequest) returns (emane.GetEmaneModelConfigResponse) {
|
rpc GetEmaneModelConfig (emane.GetEmaneModelConfigRequest) returns (emane.GetEmaneModelConfigResponse) {
|
||||||
}
|
}
|
||||||
|
@ -697,3 +703,26 @@ message LinkedRequest {
|
||||||
|
|
||||||
message LinkedResponse {
|
message LinkedResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message WirelessLinkedRequest {
|
||||||
|
int32 session_id = 1;
|
||||||
|
int32 wireless_id = 2;
|
||||||
|
int32 node1_id = 3;
|
||||||
|
int32 node2_id = 4;
|
||||||
|
bool linked = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WirelessLinkedResponse {
|
||||||
|
}
|
||||||
|
|
||||||
|
message WirelessConfigRequest {
|
||||||
|
int32 session_id = 1;
|
||||||
|
int32 wireless_id = 2;
|
||||||
|
int32 node1_id = 3;
|
||||||
|
int32 node2_id = 4;
|
||||||
|
LinkOptions options1 = 5;
|
||||||
|
LinkOptions options2 = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WirelessConfigResponse {
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue