daemon/grpc/gui: further updates for a new working wireless node, added grpc support for control and additions to gui for creation

This commit is contained in:
Blake Harnden 2022-03-30 21:13:28 -07:00
parent e4a6ecf3c2
commit 84acb82c18
19 changed files with 399 additions and 90 deletions

View file

@ -16,7 +16,13 @@ from core.api.grpc.configservices_pb2 import (
GetConfigServiceDefaultsRequest,
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 (
EmaneLinkRequest,
GetEmaneEventChannelRequest,
@ -43,6 +49,7 @@ from core.api.grpc.wlan_pb2 import (
WlanConfig,
WlanLinkRequest,
)
from core.api.grpc.wrappers import LinkOptions
from core.emulator.data import IpPrefixes
from core.errors import CoreError
@ -1079,6 +1086,42 @@ class CoreGrpcClient:
)
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:
"""
Open connection to server, must be closed manually.

View file

@ -26,7 +26,15 @@ from core.api.grpc.configservices_pb2 import (
GetNodeConfigServiceRequest,
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 (
EmaneLinkRequest,
EmaneLinkResponse,
@ -82,6 +90,7 @@ from core.errors import CoreCommandError, CoreError
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
from core.nodes.base import CoreNode, NodeBase
from core.nodes.network import CoreNetwork, WlanNode
from core.nodes.wireless import WirelessNode
from core.services.coreservices import ServiceManager
logger = logging.getLogger(__name__)
@ -1329,3 +1338,23 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
request.linked,
)
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()

View file

@ -67,6 +67,7 @@ class NodeType(Enum):
CONTROL_NET = 13
DOCKER = 15
LXC = 16
WIRELESS = 17
class LinkType(Enum):

View file

@ -4,14 +4,25 @@ from typing import Any, Dict, List
from core.config import Configuration
from core.configservice.base import ConfigService, ConfigServiceMode
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.network import WlanNode
from core.nodes.wireless import WirelessNode
GROUP: str = "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:
"""
Helper to detect MTU mismatch and add the appropriate FRR
@ -324,7 +335,7 @@ class FRRBabel(FrrService, ConfigService):
return self.render_text(text, data)
def frr_iface_config(self, iface: CoreInterface) -> str:
if isinstance(iface.net, (WlanNode, EmaneNet)):
if is_wireless(iface.net):
text = """
babel wireless
no babel split-horizon

View file

@ -5,16 +5,27 @@ from typing import Any, Dict, List
from core.config import Configuration
from core.configservice.base import ConfigService, ConfigServiceMode
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.network import PtpNet, WlanNode
from core.nodes.physical import Rj45Node
from core.nodes.wireless import WirelessNode
logger = logging.getLogger(__name__)
GROUP: str = "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:
"""
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:
config = super().quagga_iface_config(iface)
if isinstance(iface.net, (WlanNode, EmaneNet)):
if is_wireless(iface.net):
config = self.clean_text(
f"""
{config}
@ -390,7 +401,7 @@ class Babel(QuaggaService, ConfigService):
return self.render_text(text, data)
def quagga_iface_config(self, iface: CoreInterface) -> str:
if isinstance(iface.net, (WlanNode, EmaneNet)):
if is_wireless(iface.net):
text = """
babel wireless
no babel split-horizon

View file

@ -49,6 +49,7 @@ class NodeTypes(Enum):
CONTROL_NET = 13
DOCKER = 15
LXC = 16
WIRELESS = 17
class LinkTypes(Enum):

View file

@ -81,13 +81,18 @@ NODES: Dict[NodeTypes, Type[NodeBase]] = {
NodeTypes.CONTROL_NET: CtrlNet,
NodeTypes.DOCKER: DockerNode,
NodeTypes.LXC: LxcNode,
NodeTypes.WIRELESS: WirelessNode,
}
NODES_TYPE: Dict[Type[NodeBase], NodeTypes] = {NODES[x]: x for x in NODES}
CONTAINER_NODES: Set[Type[NodeBase]] = {DockerNode, LxcNode}
CTRL_NET_ID: int = 9001
LINK_COLORS: List[str] = ["green", "blue", "orange", "purple", "turquoise"]
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:
@ -299,7 +304,10 @@ class Session:
return iface1, iface2
def _add_wlan_link(
self, node: NodeBase, iface_data: InterfaceData, net: WlanNode
self,
node: NodeBase,
iface_data: InterfaceData,
net: Union[WlanNode, WirelessNode],
) -> CoreInterface:
"""
Create a wlan link.
@ -393,10 +401,10 @@ class Session:
)
iface1 = None
iface2 = None
if isinstance(node1, WlanNode):
if isinstance(node1, (WlanNode, WirelessNode)):
iface2 = node2.delete_iface(iface2_id)
node1.detach(iface2)
elif isinstance(node2, WlanNode):
elif isinstance(node2, (WlanNode, WirelessNode)):
iface1 = node1.delete_iface(iface1_id)
node2.detach(iface1)
elif isinstance(node1, EmaneNet):

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -53,6 +53,7 @@ class ImageEnum(Enum):
LINK = "link"
HUB = "hub"
WLAN = "wlan"
WIRELESS = "wireless"
EMANE = "emane"
RJ45 = "rj45"
TUNNEL = "tunnel"

View file

@ -18,7 +18,11 @@ NETWORK_NODES: List["NodeDraw"] = []
NODE_ICONS = {}
CONTAINER_NODES: Set[NodeType] = {NodeType.DEFAULT, 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}
BRIDGE_NODES: Set[NodeType] = {NodeType.HUB, NodeType.SWITCH}
IGNORE_NODES: Set[NodeType] = {NodeType.CONTROL_NET}
@ -46,6 +50,7 @@ def setup() -> None:
(ImageEnum.HUB, NodeType.HUB, "Hub"),
(ImageEnum.SWITCH, NodeType.SWITCH, "Switch"),
(ImageEnum.WLAN, NodeType.WIRELESS_LAN, "WLAN"),
(ImageEnum.WIRELESS, NodeType.WIRELESS, "Wireless"),
(ImageEnum.EMANE, NodeType.EMANE, "EMANE"),
(ImageEnum.RJ45, NodeType.RJ45, "RJ45"),
(ImageEnum.TUNNEL, NodeType.TUNNEL, "Tunnel"),

View file

@ -766,8 +766,9 @@ class CoreNode(CoreNodeBase):
raise CoreError(f"adopting unknown iface({iface.name})")
# add iface to container namespace
self.net_client.device_ns(iface.name, str(self.pid))
# update iface name to container name
name = name if name else f"eth{iface_id}"
# use default iface name for container, if a unique name was not provided
if iface.name == name:
name = f"eth{iface_id}"
self.node_net_client.device_name(iface.name, name)
iface.name = name
# turn checksums off

View file

@ -1,13 +1,19 @@
"""
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 LinkOptions
from core.emulator.links import CoreLink
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
from core.nodes.network import PtpNet
if TYPE_CHECKING:
from core.emulator.session import Session
@ -16,6 +22,24 @@ if TYPE_CHECKING:
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,
@ -26,7 +50,8 @@ class WirelessNode(CoreNetworkBase):
):
super().__init__(session, _id, name, server)
self.bridges: Dict[int, Tuple[CoreInterface, str]] = {}
self.links: Dict[Tuple[int, int], CoreLink] = {}
self.links: Dict[Tuple[int, int], WirelessLink] = {}
self.labels: Dict[Tuple[int, int], str] = {}
def startup(self) -> None:
if self.up:
@ -39,10 +64,8 @@ class WirelessNode(CoreNetworkBase):
self.net_client.delete_bridge(bridge_name)
self.host_cmd(f"{NFTABLES} delete table bridge {bridge_name}")
while self.links:
key, core_link = self.links.popitem()
core_link.iface1.shutdown()
core_link.iface2.shutdown()
core_link.ptp.shutdown()
_, link = self.links.popitem()
link.iface.shutdown()
self.up = False
def attach(self, iface: CoreInterface) -> None:
@ -90,26 +113,20 @@ class WirelessNode(CoreNetworkBase):
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}"
iface1 = CoreInterface(0, name1, f"{name1}p", self.session.use_ovs())
iface1.startup()
iface2 = CoreInterface(0, name2, f"{name2}p", self.session.use_ovs())
iface2.startup()
link_name = f"wl{node1.id}.{node2.id}.{self.session.id}"
ptp = PtpNet(self.session)
ptp.brname = link_name
ptp.startup()
ptp.attach(iface1)
ptp.attach(iface2)
core_link = CoreLink(node1, iface1, node2, iface2, ptp)
self.links[key] = core_link
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, iface1.name)
self.net_client.set_iface_master(bridge2, iface2.name)
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)
@ -130,41 +147,94 @@ class WirelessNode(CoreNetworkBase):
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)
core_link = self.links.get(key)
if not core_link:
link = self.links.get(key)
if not link:
raise CoreError(f"invalid node links node1({node1_id}) node2({node2_id})")
ptp = core_link.ptp
iface1, iface2 = core_link.iface1, core_link.iface2
bridge1, bridge2 = link.bridge1, link.bridge2
iface = link.iface
if linked:
ptp.attach(iface1)
ptp.attach(iface2)
self.net_client.set_iface_master(bridge1, iface.name)
self.net_client.set_iface_master(bridge2, iface.localname)
message_type = MessageFlags.ADD
else:
ptp.detach(iface1)
ptp.detach(iface2)
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,
iface1_options: LinkOptions,
iface2_options: LinkOptions,
message_type: MessageFlags,
label: str = None,
) -> None:
key = (node1_id, node2_id) if node1_id < node2_id else (node2_id, node1_id)
core_link = self.links.get(key)
if not core_link:
raise CoreError(f"invalid node links node1({node1_id}) node2({node2_id})")
iface1, iface2 = core_link.iface1, core_link.iface2
iface1.options.update(iface1_options)
iface1.set_config()
iface2.options.update(iface2_options)
iface2.set_config()
"""
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(
"received position callback for node(%s) iface(%s)",
iface.node.name,
iface.name,
"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")

View file

@ -4,7 +4,7 @@ sdt.py: Scripted Display Tool (SDT3D) helper
import logging
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 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.enumerations import EventTypes, MessageFlags
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.wireless import WirelessNode
logger = logging.getLogger(__name__)
if TYPE_CHECKING:
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"
NODE_LAYER: str = "CORE::Nodes"
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"
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:
"""
Helper class for exporting session objects to NRL"s SDT3D.
@ -67,7 +71,7 @@ class Sdt:
:param session: session this manager is tied to
"""
self.session: "Session" = session
self.sock: Optional[IO] = None
self.sock: Optional[socket.socket] = None
self.connected: bool = False
self.url: str = self.DEFAULT_SDT_URL
self.address: Optional[Tuple[Optional[str], Optional[int]]] = None
@ -210,26 +214,23 @@ class Sdt:
:return: nothing
"""
nets = []
# create layers
for layer in CORE_LAYERS:
self.cmd(f"layer {layer}")
with self.session.nodes_lock:
for node_id in self.session.nodes:
node = self.session.nodes[node_id]
if isinstance(node, CoreNetworkBase):
nets = []
for node in self.session.nodes.values():
if isinstance(node, (EmaneNet, WlanNode)):
nets.append(node)
if not isinstance(node, NodeBase):
continue
self.add_node(node)
for net in nets:
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:
for link in self.session.link_manager.links():
if is_wireless(link.node1) or is_wireless(link.node2):
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)
def get_node_position(self, node: NodeBase) -> Optional[str]:
@ -341,7 +342,7 @@ class Sdt:
result = False
try:
node = self.session.get_node(node_id, NodeBase)
result = isinstance(node, (WlanNode, EmaneNet))
result = isinstance(node, (WlanNode, EmaneNet, WirelessNode))
except CoreError:
pass
return result

View file

@ -7,15 +7,26 @@ from typing import Optional, Tuple
import netaddr
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.network import PtpNet, WlanNode
from core.nodes.physical import Rj45Node
from core.nodes.wireless import WirelessNode
from core.services.coreservices import CoreService
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):
name: str = "FRRzebra"
group: str = "FRR"
@ -593,7 +604,7 @@ class FRRBabel(FrrService):
@classmethod
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"
else:
return " babel wired\n babel split-horizon\n"

View file

@ -7,15 +7,26 @@ import netaddr
from core.emane.nodes import EmaneNet
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.network import PtpNet, WlanNode
from core.nodes.physical import Rj45Node
from core.nodes.wireless import WirelessNode
from core.services.coreservices import CoreService
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):
name: str = "zebra"
group: str = "Quagga"
@ -431,7 +442,7 @@ class Ospfv3mdr(Ospfv3):
@classmethod
def generate_quagga_iface_config(cls, node: CoreNode, iface: CoreInterface) -> str:
cfg = cls.mtu_check(iface)
if iface.net is not None and isinstance(iface.net, (WlanNode, EmaneNet)):
if is_wireless(iface.net):
return (
cfg
+ """\

View file

@ -16,6 +16,7 @@ from core.nodes.docker import DockerNode
from core.nodes.interface import CoreInterface
from core.nodes.lxd import LxcNode
from core.nodes.network import CtrlNet, GreTapBridge, WlanNode
from core.nodes.wireless import WirelessNode
from core.services.coreservices import CoreService
logger = logging.getLogger(__name__)
@ -542,8 +543,8 @@ class CoreXmlWriter:
iface2 = self.create_iface_element("iface2", iface2)
link_element.append(iface2)
# check for options, don't write for emane/wlan links
is_node1_wireless = isinstance(node1, (WlanNode, EmaneNet))
is_node2_wireless = isinstance(node2, (WlanNode, EmaneNet))
is_node1_wireless = isinstance(node1, (WlanNode, EmaneNet, WirelessNode))
is_node2_wireless = isinstance(node2, (WlanNode, EmaneNet, WirelessNode))
if not (is_node1_wireless or is_node2_wireless):
unidirectional = 1 if unidirectional else 0
options_element = etree.Element("options")

View 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)

View 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()

View file

@ -100,6 +100,12 @@ service CoreApi {
rpc WlanLink (wlan.WlanLinkRequest) returns (wlan.WlanLinkResponse) {
}
// wireless rpc
rpc WirelessLinked (WirelessLinkedRequest) returns (WirelessLinkedResponse) {
}
rpc WirelessConfig (WirelessConfigRequest) returns (WirelessConfigResponse) {
}
// emane rpc
rpc GetEmaneModelConfig (emane.GetEmaneModelConfigRequest) returns (emane.GetEmaneModelConfigResponse) {
}
@ -697,3 +703,26 @@ message LinkedRequest {
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 {
}