From b6f4352df3ded2d3dba9cd1a340641a7edae3bb5 Mon Sep 17 00:00:00 2001 From: Willian Galvani Date: Thu, 23 Jan 2025 14:57:34 -0300 Subject: [PATCH] cable-guy: update interface metrics live on dhcpcd --- core/services/cable_guy/networksetup.py | 147 +++++++++++++----------- 1 file changed, 81 insertions(+), 66 deletions(-) diff --git a/core/services/cable_guy/networksetup.py b/core/services/cable_guy/networksetup.py index c50a006ffc..20405e9166 100755 --- a/core/services/cable_guy/networksetup.py +++ b/core/services/cable_guy/networksetup.py @@ -1,7 +1,7 @@ import os import re import socket -from typing import Dict, List +from typing import Any, Dict, List import sdbus from loguru import logger @@ -49,6 +49,83 @@ def trigger_dynamic_ip_acquisition(self, interface_name: str) -> None: def cleanup_interface_connections(self, interface_name: str) -> None: pass + def set_interfaces_priority_on_ipr(self, interfaces: List[NetworkInterfaceMetricApi]) -> None: + if not interfaces: + logger.info("No interfaces to set priority for") + return + + # If there's only one interface and no priority specified, set it to highest priority (0) + if len(interfaces) == 1 and interfaces[0].priority is None: + interfaces[0].priority = 0 + + current_priority = 1000 + for interface in interfaces: + current_priority = self._update_interface_routes(interface, current_priority) + + def _update_single_route(self, interface_index: int, route: Any, priority: int) -> None: + # Delete the existing route first + self._delete_route(interface_index, route) + # Add the new route with updated priority + self._add_route(interface_index, route, priority) + logger.info(f"Updated route for interface index {interface_index} with priority {priority}") + + def _delete_route(self, interface_index: int, route: Any) -> None: + self.ipr.route( + "del", + oif=interface_index, + family=socket.AF_INET, + dst=route.get_attr("RTA_DST") if route.get_attr("RTA_DST") else "0.0.0.0/0", + gateway=route.get_attr("RTA_GATEWAY"), + table=route.get_attr("RTA_TABLE", 254), # Default to main table if not specified + scope=route["scope"], + proto=route["proto"], + type=route["type"], + ) + + def _add_route(self, interface_index: int, route: Any, priority: int) -> None: + route_data = { + "priority": priority, + "oif": interface_index, + "family": socket.AF_INET, + "scope": route["scope"], + "proto": route["proto"], + "type": route["type"], + } + + # Copy existing route attributes + for attr in ["RTA_DST", "RTA_GATEWAY", "RTA_TABLE", "RTA_PREFSRC"]: + value = route.get_attr(attr) + if value: + # Convert attribute name to lowercase and remove RTA_ prefix + key = attr.lower().replace("rta_", "") + route_data[key] = value + + # Add the new route + self.ipr.route("add", **route_data) + + def _update_interface_routes(self, interface: NetworkInterfaceMetricApi, current_priority: int) -> int: + try: + # Use specified priority or increment current_priority + priority = interface.priority if interface.priority is not None else current_priority + + # Get interface index + interface_index = self.ipr.link_lookup(ifname=interface.name)[0] + + # Get all routes for this interface + routes = self.ipr.get_routes(oif=interface_index, family=socket.AF_INET) + + # Update existing routes + for route in routes: + try: + self._update_single_route(interface_index, route, priority) + except Exception as e: + logger.error(f"Failed to update route for {interface.name}: {e}") + + return current_priority + 1000 + except Exception as e: + logger.error(f"Failed to set priority for interface {interface.name}: {e}") + return current_priority + class BookwormHandler(AbstractNetworkHandler): """ @@ -148,71 +225,7 @@ def set_interfaces_priority(self, interfaces: List[NetworkInterfaceMetricApi]) - interfaces (List[NetworkInterfaceMetricApi]): A list of interfaces and their priority metrics, sorted by priority to set. """ - if not interfaces: - logger.info("No interfaces to set priority for") - return - - # If there's only one interface and no priority specified, set it to highest priority (0) - if len(interfaces) == 1 and interfaces[0].priority is None: - interfaces[0].priority = 0 - - current_priority = 1000 - for interface in interfaces: - try: - # Use specified priority or increment current_priority - priority = interface.priority if interface.priority is not None else current_priority - - # Get interface index - interface_index = self.ipr.link_lookup(ifname=interface.name)[0] - - # Get all routes for this interface - routes = self.ipr.get_routes(oif=interface_index, family=socket.AF_INET) - - # Update existing routes - for route in routes: - try: - # Delete the existing route first - self.ipr.route( - "del", - oif=interface_index, - family=socket.AF_INET, - dst=route.get_attr("RTA_DST") if route.get_attr("RTA_DST") else "0.0.0.0/0", - gateway=route.get_attr("RTA_GATEWAY"), - table=route.get_attr("RTA_TABLE", 254), # Default to main table if not specified - scope=route["scope"], - proto=route["proto"], - type=route["type"], - ) - - # Add the new route with updated priority - route_data = { - "priority": priority, - "oif": interface_index, - "family": socket.AF_INET, - "scope": route["scope"], - "proto": route["proto"], - "type": route["type"], - } - - # Copy existing route attributes - for attr in ["RTA_DST", "RTA_GATEWAY", "RTA_TABLE", "RTA_PREFSRC"]: - value = route.get_attr(attr) - if value: - # Convert attribute name to lowercase and remove RTA_ prefix - key = attr.lower().replace("rta_", "") - route_data[key] = value - - # Add the new route - self.ipr.route("add", **route_data) - logger.info(f"Updated route for {interface.name} with priority {priority}") - except Exception as e: - logger.error(f"Failed to update route for {interface.name}: {e}") - continue - - current_priority += 1000 - except Exception as e: - logger.error(f"Failed to set priority for interface {interface.name}: {e}") - continue + self.set_interfaces_priority_on_ipr(interfaces) def _get_dhcp_address_using_dhclient(self, interface_name: str) -> str | None: """Run dhclient to get a new IP address and return it. @@ -402,6 +415,8 @@ def set_interfaces_priority(self, interfaces: List[NetworkInterfaceMetricApi]) - interfaces (List[NetworkInterfaceMetricApi]): A list of interfaces and their priority metrics. """ + # first let's apply them live, then we can save + self.set_interfaces_priority_on_ipr(interfaces) # Note: With DHCPCD, lower priority wins! self._remove_dhcpcd_configuration()