From aebd21d01a5a710c4f6bce53aef470b93197933f Mon Sep 17 00:00:00 2001 From: Igor Yamolov Date: Wed, 1 Jan 2025 12:52:11 +0000 Subject: [PATCH 1/2] Add network interface settings for mDNS/LLMNR --- supervisor/api/network.py | 11 ++++++++ supervisor/const.py | 2 ++ supervisor/dbus/const.py | 8 ++++++ supervisor/dbus/network/configuration.py | 2 ++ supervisor/dbus/network/setting/__init__.py | 2 ++ supervisor/dbus/network/setting/generate.py | 20 +++++++++++--- supervisor/host/configuration.py | 30 ++++++++++++++++++++- supervisor/host/const.py | 8 ++++++ 8 files changed, 79 insertions(+), 4 deletions(-) diff --git a/supervisor/api/network.py b/supervisor/api/network.py index f9374923644..dc5cfb0a124 100644 --- a/supervisor/api/network.py +++ b/supervisor/api/network.py @@ -24,7 +24,9 @@ ATTR_INTERFACES, ATTR_IPV4, ATTR_IPV6, + ATTR_LLMNR, ATTR_MAC, + ATTR_MDNS, ATTR_METHOD, ATTR_MODE, ATTR_NAMESERVERS, @@ -49,6 +51,7 @@ InterfaceMethod, IpConfig, IpSetting, + MulticastDnsMode, VlanConfig, WifiConfig, ) @@ -90,6 +93,8 @@ vol.Optional(ATTR_IPV6): _SCHEMA_IPV6_CONFIG, vol.Optional(ATTR_WIFI): _SCHEMA_WIFI_CONFIG, vol.Optional(ATTR_ENABLED): vol.Boolean(), + vol.Optional(ATTR_MDNS): vol.Coerce(MulticastDnsMode), + vol.Optional(ATTR_LLMNR): vol.Coerce(MulticastDnsMode), } ) @@ -136,6 +141,8 @@ def interface_struct(interface: Interface) -> dict[str, Any]: ATTR_IPV6: ipconfig_struct(interface.ipv6, interface.ipv6setting), ATTR_WIFI: wifi_struct(interface.wifi) if interface.wifi else None, ATTR_VLAN: vlan_struct(interface.vlan) if interface.vlan else None, + ATTR_MDNS: interface.mdns, + ATTR_LLMNR: interface.lldmp, } @@ -230,6 +237,10 @@ async def interface_update(self, request: web.Request) -> None: ) elif key == ATTR_ENABLED: interface.enabled = config + elif key == ATTR_MDNS: + interface.mdns = config + elif key == ATTR_LLMNR: + interface.llmnr = config await asyncio.shield(self.sys_host.network.apply_changes(interface)) diff --git a/supervisor/const.py b/supervisor/const.py index 1f1fec244ce..6669905adc9 100644 --- a/supervisor/const.py +++ b/supervisor/const.py @@ -228,6 +228,7 @@ ATTR_LABELS = "labels" ATTR_LAST_BOOT = "last_boot" ATTR_LEGACY = "legacy" +ATTR_LLMNR = "llmnr" ATTR_LOCALS = "locals" ATTR_LOCATION = "location" ATTR_LOGGING = "logging" @@ -237,6 +238,7 @@ ATTR_MACHINE = "machine" ATTR_MAINTAINER = "maintainer" ATTR_MAP = "map" +ATTR_MDNS = "mdns" ATTR_MEMORY_LIMIT = "memory_limit" ATTR_MEMORY_PERCENT = "memory_percent" ATTR_MEMORY_USAGE = "memory_usage" diff --git a/supervisor/dbus/const.py b/supervisor/dbus/const.py index 82c75d8f3d3..e16f94a65c5 100644 --- a/supervisor/dbus/const.py +++ b/supervisor/dbus/const.py @@ -296,6 +296,14 @@ class MulticastProtocolEnabled(StrEnum): RESOLVE = "resolve" +class MulticastDnsValue(IntEnum): + """Connection MulticastDNS (mdns/lldmp) values.""" + + OFF = 0 + RESOLVE = 1 + ANNOUNCE = 2 + + class DNSOverTLSEnabled(StrEnum): """DNS over TLS enabled.""" diff --git a/supervisor/dbus/network/configuration.py b/supervisor/dbus/network/configuration.py index b78855f85b1..6b21811bb7f 100644 --- a/supervisor/dbus/network/configuration.py +++ b/supervisor/dbus/network/configuration.py @@ -23,6 +23,8 @@ class ConnectionProperties: uuid: str | None type: str | None interface_name: str | None + mdns: int | None + lldmp: int | None @dataclass(slots=True) diff --git a/supervisor/dbus/network/setting/__init__.py b/supervisor/dbus/network/setting/__init__.py index 5bf1dbad2e6..d002186ae84 100644 --- a/supervisor/dbus/network/setting/__init__.py +++ b/supervisor/dbus/network/setting/__init__.py @@ -219,6 +219,8 @@ async def reload(self): data[CONF_ATTR_CONNECTION].get(CONF_ATTR_CONNECTION_UUID), data[CONF_ATTR_CONNECTION].get(CONF_ATTR_CONNECTION_TYPE), data[CONF_ATTR_CONNECTION].get(CONF_ATTR_CONNECTION_INTERFACE_NAME), + data[CONF_ATTR_CONNECTION].get(CONF_ATTR_CONNECTION_MDNS), + data[CONF_ATTR_CONNECTION].get(CONF_ATTR_CONNECTION_LLMNR), ) if CONF_ATTR_802_ETHERNET in data: diff --git a/supervisor/dbus/network/setting/generate.py b/supervisor/dbus/network/setting/generate.py index 554c5b230e7..9cc6c59c77a 100644 --- a/supervisor/dbus/network/setting/generate.py +++ b/supervisor/dbus/network/setting/generate.py @@ -8,7 +8,8 @@ from dbus_fast import Variant -from ....host.const import InterfaceMethod, InterfaceType +from ....host.const import InterfaceMethod, InterfaceType, MulticastDnsMode +from ...const import MulticastDnsValue from .. import NetworkManager from . import ( CONF_ATTR_802_ETHERNET, @@ -133,6 +134,16 @@ def _get_ipv6_connection_settings(ipv6setting) -> dict: return ipv6 +def _map_mdns_setting(mode: MulticastDnsMode | None) -> int: + mapping = { + MulticastDnsMode.OFF: MulticastDnsValue.OFF, + MulticastDnsMode.RESOLVE: MulticastDnsValue.RESOLVE, + MulticastDnsMode.ANNOUNCE: MulticastDnsValue.ANNOUNCE, + } + + return int(mapping[mode] if mode else MulticastDnsValue.ANNOUNCE) + + def get_connection_from_interface( interface: Interface, network_manager: NetworkManager, @@ -158,13 +169,16 @@ def get_connection_from_interface( if not uuid: uuid = str(uuid4()) + llmnr = _map_mdns_setting(interface.llmnr) + mdns = _map_mdns_setting(interface.mdns) + conn: dict[str, dict[str, Variant]] = { CONF_ATTR_CONNECTION: { CONF_ATTR_CONNECTION_ID: Variant("s", name), CONF_ATTR_CONNECTION_UUID: Variant("s", uuid), CONF_ATTR_CONNECTION_TYPE: Variant("s", iftype), - CONF_ATTR_CONNECTION_LLMNR: Variant("i", 2), - CONF_ATTR_CONNECTION_MDNS: Variant("i", 2), + CONF_ATTR_CONNECTION_LLMNR: Variant("i", llmnr), + CONF_ATTR_CONNECTION_MDNS: Variant("i", mdns), CONF_ATTR_CONNECTION_AUTOCONNECT: Variant("b", True), }, } diff --git a/supervisor/host/configuration.py b/supervisor/host/configuration.py index cfce58831fe..a0f9deb9665 100644 --- a/supervisor/host/configuration.py +++ b/supervisor/host/configuration.py @@ -9,10 +9,17 @@ ConnectionStateType, DeviceType, InterfaceMethod as NMInterfaceMethod, + MulticastDnsValue, ) from ..dbus.network.connection import NetworkConnection from ..dbus.network.interface import NetworkInterface -from .const import AuthMethod, InterfaceMethod, InterfaceType, WifiMode +from .const import ( + AuthMethod, + InterfaceMethod, + InterfaceType, + MulticastDnsMode, + WifiMode, +) @dataclass(slots=True) @@ -82,6 +89,8 @@ class Interface: ipv6setting: IpSetting | None wifi: WifiConfig | None vlan: VlanConfig | None + mdns: MulticastDnsMode | None + llmnr: MulticastDnsMode | None def equals_dbus_interface(self, inet: NetworkInterface) -> bool: """Return true if this represents the dbus interface.""" @@ -145,6 +154,13 @@ def from_dbus_interface(inet: NetworkInterface) -> "Interface": and ConnectionStateFlags.IP6_READY in inet.connection.state_flags ) + if inet.settings and inet.settings.connection: + mdns = inet.settings.connection.mdns + lldmp = inet.settings.connection.lldmp + else: + mdns = None + lldmp = None + return Interface( inet.name, inet.hw_address, @@ -181,6 +197,8 @@ def from_dbus_interface(inet: NetworkInterface) -> "Interface": ipv6_setting, Interface._map_nm_wifi(inet), Interface._map_nm_vlan(inet), + Interface._map_nm_multicast_dns(mdns), + Interface._map_nm_multicast_dns(lldmp), ) @staticmethod @@ -258,3 +276,13 @@ def _map_nm_vlan(inet: NetworkInterface) -> WifiConfig | None: return None return VlanConfig(inet.settings.vlan.id, inet.settings.vlan.parent) + + @staticmethod + def _map_nm_multicast_dns(mode: int | None) -> MulticastDnsMode | None: + mapping = { + MulticastDnsValue.OFF: MulticastDnsMode.OFF, + MulticastDnsValue.RESOLVE: MulticastDnsMode.RESOLVE, + MulticastDnsValue.ANNOUNCE: MulticastDnsMode.ANNOUNCE, + } + + return mapping[mode] diff --git a/supervisor/host/const.py b/supervisor/host/const.py index 9c3a9dc4ada..ddd92072886 100644 --- a/supervisor/host/const.py +++ b/supervisor/host/const.py @@ -70,3 +70,11 @@ class LogFormatter(StrEnum): PLAIN = "plain" VERBOSE = "verbose" + + +class MulticastDnsMode(StrEnum): + """Multicast DNS (MDNS/LLMNR) mode.""" + + OFF = "off" + RESOLVE = "resolve" + ANNOUNCE = "announce" From 0b7f2b6a156e12ac56bea95c722512983134c753 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 14 Jan 2025 11:43:38 +0100 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- supervisor/api/network.py | 2 +- supervisor/dbus/network/configuration.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/supervisor/api/network.py b/supervisor/api/network.py index dc5cfb0a124..c72ba679ccf 100644 --- a/supervisor/api/network.py +++ b/supervisor/api/network.py @@ -142,7 +142,7 @@ def interface_struct(interface: Interface) -> dict[str, Any]: ATTR_WIFI: wifi_struct(interface.wifi) if interface.wifi else None, ATTR_VLAN: vlan_struct(interface.vlan) if interface.vlan else None, ATTR_MDNS: interface.mdns, - ATTR_LLMNR: interface.lldmp, + ATTR_LLMNR: interface.llmnr, } diff --git a/supervisor/dbus/network/configuration.py b/supervisor/dbus/network/configuration.py index 6b21811bb7f..83e610fb15a 100644 --- a/supervisor/dbus/network/configuration.py +++ b/supervisor/dbus/network/configuration.py @@ -24,7 +24,7 @@ class ConnectionProperties: type: str | None interface_name: str | None mdns: int | None - lldmp: int | None + llmnr: int | None @dataclass(slots=True)