daemon: initial code to support a new style wireless approach
This commit is contained in:
parent
73aaa8ca18
commit
e4a6ecf3c2
2 changed files with 177 additions and 2 deletions
|
@ -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
|
||||||
|
@ -274,9 +275,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)
|
||||||
|
@ -1111,6 +1112,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)
|
||||||
|
|
170
daemon/core/nodes/wireless.py
Normal file
170
daemon/core/nodes/wireless.py
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
import logging
|
||||||
|
from typing import TYPE_CHECKING, Dict, Tuple
|
||||||
|
|
||||||
|
from core.emulator.data import LinkOptions
|
||||||
|
from core.emulator.links import CoreLink
|
||||||
|
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
|
||||||
|
from core.emulator.distributed import DistributedServer
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
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], CoreLink] = {}
|
||||||
|
|
||||||
|
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:
|
||||||
|
key, core_link = self.links.popitem()
|
||||||
|
core_link.iface1.shutdown()
|
||||||
|
core_link.iface2.shutdown()
|
||||||
|
core_link.ptp.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}"
|
||||||
|
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
|
||||||
|
# assign ifaces to respective bridges
|
||||||
|
self.net_client.set_iface_master(bridge1, iface1.name)
|
||||||
|
self.net_client.set_iface_master(bridge2, iface2.name)
|
||||||
|
# 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)
|
||||||
|
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)
|
||||||
|
core_link = self.links.get(key)
|
||||||
|
if not core_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
|
||||||
|
if linked:
|
||||||
|
ptp.attach(iface1)
|
||||||
|
ptp.attach(iface2)
|
||||||
|
else:
|
||||||
|
ptp.detach(iface1)
|
||||||
|
ptp.detach(iface2)
|
||||||
|
|
||||||
|
def link_config(
|
||||||
|
self,
|
||||||
|
node1_id: int,
|
||||||
|
node2_id: int,
|
||||||
|
iface1_options: LinkOptions,
|
||||||
|
iface2_options: LinkOptions,
|
||||||
|
) -> 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()
|
||||||
|
|
||||||
|
def position_callback(self, iface: CoreInterface) -> None:
|
||||||
|
logger.info(
|
||||||
|
"received position callback for node(%s) iface(%s)",
|
||||||
|
iface.node.name,
|
||||||
|
iface.name,
|
||||||
|
)
|
||||||
|
|
||||||
|
def adopt_iface(self, iface: CoreInterface, name: str) -> None:
|
||||||
|
raise CoreError(f"{type(self)} does not support adopt interface")
|
Loading…
Reference in a new issue