diff --git a/daemon/core/gui/interface.py b/daemon/core/gui/interface.py index 359dba8e..b2e037c9 100644 --- a/daemon/core/gui/interface.py +++ b/daemon/core/gui/interface.py @@ -59,6 +59,7 @@ class InterfaceManager: def next_mac(self) -> str: mac = str(self.current_mac) + logging.info("mac(%s) value(%s)", self.current_mac, self.current_mac.value) value = self.current_mac.value + 1 self.current_mac = EUI(value) self.current_mac.dialect = netaddr.mac_unix_expanded diff --git a/daemon/core/location/mobility.py b/daemon/core/location/mobility.py index 05a6ac3e..ddbbdce8 100644 --- a/daemon/core/location/mobility.py +++ b/daemon/core/location/mobility.py @@ -363,14 +363,12 @@ class BasicRangeModel(WirelessModel): :return: nothing """ x, y, z = netif.node.position.get() - self._netifslock.acquire() - self._netifs[netif] = (x, y, z) - if x is None or y is None: - self._netifslock.release() - return - for netif2 in self._netifs: - self.calclink(netif, netif2) - self._netifslock.release() + with self._netifslock: + self._netifs[netif] = (x, y, z) + if x is None or y is None: + return + for netif2 in self._netifs: + self.calclink(netif, netif2) position_callback = set_position @@ -407,35 +405,43 @@ class BasicRangeModel(WirelessModel): :param netif2: interface two :return: nothing """ - if netif == netif2: + if not self.range: return + if netif == netif2: + return try: x, y, z = self._netifs[netif] x2, y2, z2 = self._netifs[netif2] - if x2 is None or y2 is None: return - d = self.calcdistance((x, y, z), (x2, y2, z2)) - # ordering is important, to keep the wlan._linked dict organized + # calculate loss + start_point = self.range / 2 + start_value = max(d - start_point, 0) + loss = max(start_value / start_point, 0.001) + loss = min(loss, 1.0) * 100 + self.wlan.change_loss(netif, netif2, loss) + self.wlan.change_loss(netif2, netif, loss) + + # # ordering is important, to keep the wlan._linked dict organized a = min(netif, netif2) b = max(netif, netif2) - with self.wlan._linked_lock: linked = self.wlan.linked(a, b) - if d > self.range: if linked: logging.debug("was linked, unlinking") self.wlan.unlink(a, b) - self.sendlinkmsg(a, b, unlink=True) + self.sendlinkmsg(MessageFlags.DELETE, a, b) else: if not linked: logging.debug("was not linked, linking") self.wlan.link(a, b) - self.sendlinkmsg(a, b) + self.sendlinkmsg(MessageFlags.ADD, a, b, loss=loss) + else: + self.sendlinkmsg(MessageFlags.NONE, a, b, loss=loss) except KeyError: logging.exception("error getting interfaces during calclinkS") @@ -472,7 +478,6 @@ class BasicRangeModel(WirelessModel): self.delay = self._get_config(self.delay, config, "delay") self.loss = self._get_config(self.loss, config, "error") self.jitter = self._get_config(self.jitter, config, "jitter") - self.setlinkparams() def create_link_data( self, @@ -499,22 +504,26 @@ class BasicRangeModel(WirelessModel): ) def sendlinkmsg( - self, netif: CoreInterface, netif2: CoreInterface, unlink: bool = False + self, + message_type: MessageFlags, + netif: CoreInterface, + netif2: CoreInterface, + loss: float = None, ) -> None: """ Send a wireless link/unlink API message to the GUI. + :param message_type: type of link message to send :param netif: interface one :param netif2: interface two - :param unlink: unlink or not + :param loss: link loss value :return: nothing """ - if unlink: - message_type = MessageFlags.DELETE - else: - message_type = MessageFlags.ADD - + label = None + if loss is not None: + label = f"{loss:.2f}%" link_data = self.create_link_data(netif, netif2, message_type) + link_data.label = label self.session.broadcast_link(link_data) def all_link_data(self, flags: MessageFlags = MessageFlags.NONE) -> List[LinkData]: diff --git a/daemon/core/nodes/network.py b/daemon/core/nodes/network.py index f2c16bd0..6ed62a36 100644 --- a/daemon/core/nodes/network.py +++ b/daemon/core/nodes/network.py @@ -288,7 +288,6 @@ class CoreNetwork(CoreNetworkBase): self.has_ebtables_chain = False if start: self.startup() - ebq.startupdateloop(self) def host_cmd( self, @@ -335,16 +334,8 @@ class CoreNetwork(CoreNetworkBase): if not self.up: return - ebq.stopupdateloop(self) - try: self.net_client.delete_bridge(self.brname) - if self.has_ebtables_chain: - cmds = [ - f"{EBTABLES_BIN} -D FORWARD --logical-in {self.brname} -j {self.brname}", - f"{EBTABLES_BIN} -X {self.brname}", - ] - ebtablescmds(self.host_cmd, cmds) except CoreCommandError: logging.exception("error during shutdown") @@ -421,8 +412,6 @@ class CoreNetwork(CoreNetworkBase): return self._linked[netif1][netif2] = False - ebq.ebchange(self) - def link(self, netif1: CoreInterface, netif2: CoreInterface) -> None: """ Link two interfaces together, resulting in adding or removing @@ -437,8 +426,6 @@ class CoreNetwork(CoreNetworkBase): return self._linked[netif1][netif2] = True - ebq.ebchange(self) - def linkconfig( self, netif: CoreInterface, @@ -1050,8 +1037,11 @@ class WlanNode(CoreNetwork): """ super().__init__(session, _id, name, start, server, policy) # wireless and mobility models (BasicRangeModel, Ns2WaypointMobility) + self.initialized = False self.model = None self.mobility = None + self.loss_maps = {} + self.bands = None def startup(self) -> None: """ @@ -1061,7 +1051,99 @@ class WlanNode(CoreNetwork): """ super().startup() self.net_client.disable_mac_learning(self.brname) - ebq.ebchange(self) + + def _initialize_tc(self) -> None: + logging.info("setting wlan configuration: %s", self.model.bw) + + # initial settings + self.bands = len(self.netifs()) + self.loss_maps.clear() + + # initialize loss rules + for netif in self.netifs(): + node = netif.node + name = netif.localname + logging.info("connected nodes: %s - %s", node.name, name) + + # setup root handler for wlan interface + self.host_cmd( + f"tc qdisc add dev {name} root handle 1: htb default {self.bands}" + ) + + # setup filter rules and qdisc for each other node + index = 2 + for other_netif in self.netifs(): + if netif == other_netif: + continue + other_name = other_netif.localname + mac = other_netif.hwaddr + logging.info( + "tc filter from(%s) to(%s) for src mac(%s) index(%s)", + node.name, + other_netif.node.name, + mac, + index, + ) + # save loss map + loss_map = self.loss_maps.setdefault(name, {}) + loss_map[other_name] = index + self._tc_filter(name, index, mac) + index += 1 + + # setup catch all + loss_map = self.loss_maps.setdefault(name, {}) + loss_map["catch"] = index + self._tc_catch_all(name, index) + + import pprint + + pretty_map = pprint.pformat(self.loss_maps, indent=4, compact=False) + logging.info("wlan loss map:\n%s", pretty_map) + + def _tc_update_rate(self): + for name, loss_map in self.loss_maps.items(): + for index in loss_map.values(): + self.host_cmd( + f"tc class change dev {name} parent 1: classid 1:{index} " + f"htb rate {self.model.bw}" + ) + + def _tc_catch_all(self, name: str, index: int) -> None: + self.host_cmd( + f"tc class add dev {name} parent 1: classid 1:{index} " + f"htb rate {self.model.bw}" + ) + self.host_cmd( + f"tc filter add dev {name} protocol all parent 1: " + f"prio 0 u32 match u32 0 0 flowid 1:{index}" + ) + self.host_cmd(f"tc qdisc add dev {name} parent 1:{index} handle {index}: sfq") + + def _tc_filter(self, name: str, index: int, mac: str) -> None: + self.host_cmd( + f"tc class add dev {name} parent 1: classid 1:{index} " + f"htb rate {self.model.bw}" + ) + self.host_cmd( + f"tc filter add dev {name} protocol all parent 1: " + f"prio 1 u32 match eth src {mac} flowid 1:{index}" + ) + self.host_cmd( + f"tc qdisc add dev {name} parent 1:{index} " + f"handle {index}: netem loss 0.01%" + ) + + def change_loss( + self, netif: CoreInterface, netif2: CoreInterface, loss: float + ) -> None: + name = netif.localname + other_name = netif2.localname + index = self.loss_maps[name][other_name] + self.host_cmd( + f"tc qdisc change dev {name} parent 1:{index}" + f" handle {index}: netem loss {loss}%" + f" delay {self.model.delay}us {self.model.jitter}us 25%" + ) def attach(self, netif: CoreInterface) -> None: """ @@ -1106,6 +1188,11 @@ class WlanNode(CoreNetwork): "node(%s) updating model(%s): %s", self.id, self.model.name, config ) self.model.update_config(config) + if not self.initialized: + self.initialized = True + self._initialize_tc() + else: + self._tc_update_rate() for netif in self.netifs(): netif.setposition()