mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-25 18:16:32 +00:00
Support custom DNS configuration on auto
So far, when the network setting is set to auto, we don't allow a custom DNS configuration. However, it is a perfectly valid use case to provide custom DNS configuration while using automatic IP configuration (e.g. DHCP). Especially when the DHCP server doesn't provide a DNS server. In this case, systemd-resolved (at least on Home Assistant OS) falls back to Cloudflare. This might not what the user wants. Customze the DNS setting allows to set a custom fallback DNS server or override a potential non-working DHCP provided one.
This commit is contained in:
parent
8ab396d77c
commit
1e9a3d7d0f
@ -29,6 +29,7 @@ from ..const import (
|
|||||||
ATTR_METHOD,
|
ATTR_METHOD,
|
||||||
ATTR_MODE,
|
ATTR_MODE,
|
||||||
ATTR_NAMESERVERS,
|
ATTR_NAMESERVERS,
|
||||||
|
ATTR_NAMESERVERS_AUTO,
|
||||||
ATTR_PARENT,
|
ATTR_PARENT,
|
||||||
ATTR_PRIMARY,
|
ATTR_PRIMARY,
|
||||||
ATTR_PSK,
|
ATTR_PSK,
|
||||||
@ -90,6 +91,7 @@ def ipconfig_struct(config: IpConfig) -> dict[str, Any]:
|
|||||||
return {
|
return {
|
||||||
ATTR_METHOD: config.method,
|
ATTR_METHOD: config.method,
|
||||||
ATTR_ADDRESS: [address.with_prefixlen for address in config.address],
|
ATTR_ADDRESS: [address.with_prefixlen for address in config.address],
|
||||||
|
ATTR_NAMESERVERS_AUTO: config.nameservers_auto,
|
||||||
ATTR_NAMESERVERS: [str(address) for address in config.nameservers],
|
ATTR_NAMESERVERS: [str(address) for address in config.nameservers],
|
||||||
ATTR_GATEWAY: str(config.gateway) if config.gateway else None,
|
ATTR_GATEWAY: str(config.gateway) if config.gateway else None,
|
||||||
ATTR_READY: config.ready,
|
ATTR_READY: config.ready,
|
||||||
@ -200,15 +202,19 @@ class APINetwork(CoreSysAttributes):
|
|||||||
if key == ATTR_IPV4:
|
if key == ATTR_IPV4:
|
||||||
interface.ipv4 = replace(
|
interface.ipv4 = replace(
|
||||||
interface.ipv4
|
interface.ipv4
|
||||||
or IpConfig(InterfaceMethod.STATIC, [], None, [], None),
|
or IpConfig(InterfaceMethod.STATIC, [], None, False, [], None),
|
||||||
**config,
|
**config,
|
||||||
)
|
)
|
||||||
|
if interface.ipv4.method == InterfaceMethod.AUTO:
|
||||||
|
interface.ipv4.nameservers_auto = ATTR_NAMESERVERS not in config
|
||||||
elif key == ATTR_IPV6:
|
elif key == ATTR_IPV6:
|
||||||
interface.ipv6 = replace(
|
interface.ipv6 = replace(
|
||||||
interface.ipv6
|
interface.ipv6
|
||||||
or IpConfig(InterfaceMethod.STATIC, [], None, [], None),
|
or IpConfig(InterfaceMethod.STATIC, [], None, False, [], None),
|
||||||
**config,
|
**config,
|
||||||
)
|
)
|
||||||
|
if interface.ipv6.method == InterfaceMethod.AUTO:
|
||||||
|
interface.ipv6.nameservers_auto = ATTR_NAMESERVERS not in config
|
||||||
elif key == ATTR_WIFI:
|
elif key == ATTR_WIFI:
|
||||||
interface.wifi = replace(
|
interface.wifi = replace(
|
||||||
interface.wifi
|
interface.wifi
|
||||||
|
@ -244,6 +244,7 @@ ATTR_MODE = "mode"
|
|||||||
ATTR_MULTICAST = "multicast"
|
ATTR_MULTICAST = "multicast"
|
||||||
ATTR_NAME = "name"
|
ATTR_NAME = "name"
|
||||||
ATTR_NAMESERVERS = "nameservers"
|
ATTR_NAMESERVERS = "nameservers"
|
||||||
|
ATTR_NAMESERVERS_AUTO = "nameservers_auto"
|
||||||
ATTR_NETWORK = "network"
|
ATTR_NETWORK = "network"
|
||||||
ATTR_NETWORK_DESCRIPTION = "network_description"
|
ATTR_NETWORK_DESCRIPTION = "network_description"
|
||||||
ATTR_NETWORK_RX = "network_rx"
|
ATTR_NETWORK_RX = "network_rx"
|
||||||
|
@ -64,6 +64,7 @@ class IpProperties:
|
|||||||
"""IP properties object for Network Manager."""
|
"""IP properties object for Network Manager."""
|
||||||
|
|
||||||
method: str | None
|
method: str | None
|
||||||
|
dns: list[str] | None
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
|
@ -6,7 +6,7 @@ from typing import Any
|
|||||||
from dbus_fast import Variant
|
from dbus_fast import Variant
|
||||||
from dbus_fast.aio.message_bus import MessageBus
|
from dbus_fast.aio.message_bus import MessageBus
|
||||||
|
|
||||||
from ....const import ATTR_METHOD, ATTR_MODE, ATTR_PSK, ATTR_SSID
|
from ....const import ATTR_DNS, ATTR_METHOD, ATTR_MODE, ATTR_PSK, ATTR_SSID
|
||||||
from ...const import DBUS_NAME_NM
|
from ...const import DBUS_NAME_NM
|
||||||
from ...interface import DBusInterface
|
from ...interface import DBusInterface
|
||||||
from ...utils import dbus_connected
|
from ...utils import dbus_connected
|
||||||
@ -75,7 +75,7 @@ def _merge_settings_attribute(
|
|||||||
class NetworkSetting(DBusInterface):
|
class NetworkSetting(DBusInterface):
|
||||||
"""Network connection setting object for Network Manager.
|
"""Network connection setting object for Network Manager.
|
||||||
|
|
||||||
https://developer.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.Settings.Connection.html
|
https://networkmanager.dev/docs/api/1.48.0/gdbus-org.freedesktop.NetworkManager.Settings.Connection.html
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bus_name: str = DBUS_NAME_NM
|
bus_name: str = DBUS_NAME_NM
|
||||||
@ -229,11 +229,13 @@ class NetworkSetting(DBusInterface):
|
|||||||
if CONF_ATTR_IPV4 in data:
|
if CONF_ATTR_IPV4 in data:
|
||||||
self._ipv4 = IpProperties(
|
self._ipv4 = IpProperties(
|
||||||
data[CONF_ATTR_IPV4].get(ATTR_METHOD),
|
data[CONF_ATTR_IPV4].get(ATTR_METHOD),
|
||||||
|
data[CONF_ATTR_IPV4].get(ATTR_DNS),
|
||||||
)
|
)
|
||||||
|
|
||||||
if CONF_ATTR_IPV6 in data:
|
if CONF_ATTR_IPV6 in data:
|
||||||
self._ipv6 = IpProperties(
|
self._ipv6 = IpProperties(
|
||||||
data[CONF_ATTR_IPV6].get(ATTR_METHOD),
|
data[CONF_ATTR_IPV6].get(ATTR_METHOD),
|
||||||
|
data[CONF_ATTR_IPV6].get(ATTR_DNS),
|
||||||
)
|
)
|
||||||
|
|
||||||
if CONF_ATTR_MATCH in data:
|
if CONF_ATTR_MATCH in data:
|
||||||
|
@ -76,13 +76,6 @@ def get_connection_from_interface(
|
|||||||
ipv4["method"] = Variant("s", "disabled")
|
ipv4["method"] = Variant("s", "disabled")
|
||||||
else:
|
else:
|
||||||
ipv4["method"] = Variant("s", "manual")
|
ipv4["method"] = Variant("s", "manual")
|
||||||
ipv4["dns"] = Variant(
|
|
||||||
"au",
|
|
||||||
[
|
|
||||||
socket.htonl(int(ip_address))
|
|
||||||
for ip_address in interface.ipv4.nameservers
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
adressdata = []
|
adressdata = []
|
||||||
for address in interface.ipv4.address:
|
for address in interface.ipv4.address:
|
||||||
@ -96,6 +89,18 @@ def get_connection_from_interface(
|
|||||||
ipv4["address-data"] = Variant("aa{sv}", adressdata)
|
ipv4["address-data"] = Variant("aa{sv}", adressdata)
|
||||||
ipv4["gateway"] = Variant("s", str(interface.ipv4.gateway))
|
ipv4["gateway"] = Variant("s", str(interface.ipv4.gateway))
|
||||||
|
|
||||||
|
if (
|
||||||
|
interface.ipv4.method == InterfaceMethod.AUTO
|
||||||
|
and not interface.ipv4.nameservers_auto
|
||||||
|
) or interface.ipv4.method == InterfaceMethod.STATIC:
|
||||||
|
ipv4["dns"] = Variant(
|
||||||
|
"au",
|
||||||
|
[
|
||||||
|
socket.htonl(int(ip_address))
|
||||||
|
for ip_address in interface.ipv4.nameservers
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
conn[CONF_ATTR_IPV4] = ipv4
|
conn[CONF_ATTR_IPV4] = ipv4
|
||||||
|
|
||||||
ipv6 = {}
|
ipv6 = {}
|
||||||
@ -105,9 +110,6 @@ def get_connection_from_interface(
|
|||||||
ipv6["method"] = Variant("s", "link-local")
|
ipv6["method"] = Variant("s", "link-local")
|
||||||
else:
|
else:
|
||||||
ipv6["method"] = Variant("s", "manual")
|
ipv6["method"] = Variant("s", "manual")
|
||||||
ipv6["dns"] = Variant(
|
|
||||||
"aay", [ip_address.packed for ip_address in interface.ipv6.nameservers]
|
|
||||||
)
|
|
||||||
|
|
||||||
adressdata = []
|
adressdata = []
|
||||||
for address in interface.ipv6.address:
|
for address in interface.ipv6.address:
|
||||||
@ -121,6 +123,14 @@ def get_connection_from_interface(
|
|||||||
ipv6["address-data"] = Variant("aa{sv}", adressdata)
|
ipv6["address-data"] = Variant("aa{sv}", adressdata)
|
||||||
ipv6["gateway"] = Variant("s", str(interface.ipv6.gateway))
|
ipv6["gateway"] = Variant("s", str(interface.ipv6.gateway))
|
||||||
|
|
||||||
|
if (
|
||||||
|
interface.ipv6.method == InterfaceMethod.AUTO
|
||||||
|
and not interface.ipv6.nameservers_auto
|
||||||
|
) or interface.ipv6.method == InterfaceMethod.STATIC:
|
||||||
|
ipv6["dns"] = Variant(
|
||||||
|
"aay", [ip_address.packed for ip_address in interface.ipv6.nameservers]
|
||||||
|
)
|
||||||
|
|
||||||
conn[CONF_ATTR_IPV6] = ipv6
|
conn[CONF_ATTR_IPV6] = ipv6
|
||||||
|
|
||||||
if interface.type == InterfaceType.ETHERNET:
|
if interface.type == InterfaceType.ETHERNET:
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from ipaddress import IPv4Address, IPv4Interface, IPv6Address, IPv6Interface
|
from ipaddress import IPv4Address, IPv4Interface, IPv6Address, IPv6Interface
|
||||||
|
import socket
|
||||||
|
|
||||||
from ..dbus.const import (
|
from ..dbus.const import (
|
||||||
ConnectionStateFlags,
|
ConnectionStateFlags,
|
||||||
@ -32,6 +33,7 @@ class IpConfig:
|
|||||||
method: InterfaceMethod
|
method: InterfaceMethod
|
||||||
address: list[IPv4Interface | IPv6Interface]
|
address: list[IPv4Interface | IPv6Interface]
|
||||||
gateway: IPv4Address | IPv6Address | None
|
gateway: IPv4Address | IPv6Address | None
|
||||||
|
nameservers_auto: bool
|
||||||
nameservers: list[IPv4Address | IPv6Address]
|
nameservers: list[IPv4Address | IPv6Address]
|
||||||
ready: bool | None
|
ready: bool | None
|
||||||
|
|
||||||
@ -102,6 +104,30 @@ class Interface:
|
|||||||
bool(inet.connection)
|
bool(inet.connection)
|
||||||
and ConnectionStateFlags.IP6_READY in inet.connection.state_flags
|
and ConnectionStateFlags.IP6_READY in inet.connection.state_flags
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Use Nameserver from configuration if present (means manual DNS override)
|
||||||
|
if inet.settings.ipv4.dns:
|
||||||
|
ipv4_nameservers_auto = False
|
||||||
|
ipv4_nameservers = [
|
||||||
|
IPv4Address(socket.ntohl(ip)) for ip in inet.settings.ipv4.dns
|
||||||
|
]
|
||||||
|
elif inet.connection.ipv4.nameservers:
|
||||||
|
ipv4_nameservers_auto = True
|
||||||
|
ipv4_nameservers = inet.connection.ipv4.nameservers
|
||||||
|
else:
|
||||||
|
ipv4_nameservers_auto = True
|
||||||
|
ipv4_nameservers = []
|
||||||
|
|
||||||
|
if inet.settings.ipv6.dns:
|
||||||
|
ipv6_nameservers_auto = False
|
||||||
|
ipv6_nameservers = [IPv6Address(bytes(ip)) for ip in inet.settings.ipv6.dns]
|
||||||
|
elif inet.connection.ipv6.nameservers:
|
||||||
|
ipv6_nameservers_auto = True
|
||||||
|
ipv6_nameservers = inet.connection.ipv6.nameservers
|
||||||
|
else:
|
||||||
|
ipv6_nameservers_auto = True
|
||||||
|
ipv6_nameservers = []
|
||||||
|
|
||||||
return Interface(
|
return Interface(
|
||||||
inet.name,
|
inet.name,
|
||||||
inet.hw_address,
|
inet.hw_address,
|
||||||
@ -111,27 +137,29 @@ class Interface:
|
|||||||
inet.primary,
|
inet.primary,
|
||||||
Interface._map_nm_type(inet.type),
|
Interface._map_nm_type(inet.type),
|
||||||
IpConfig(
|
IpConfig(
|
||||||
ipv4_method,
|
method=ipv4_method,
|
||||||
inet.connection.ipv4.address if inet.connection.ipv4.address else [],
|
address=inet.connection.ipv4.address
|
||||||
inet.connection.ipv4.gateway,
|
if inet.connection.ipv4.address
|
||||||
inet.connection.ipv4.nameservers
|
|
||||||
if inet.connection.ipv4.nameservers
|
|
||||||
else [],
|
else [],
|
||||||
ipv4_ready,
|
gateway=inet.connection.ipv4.gateway,
|
||||||
|
nameservers_auto=ipv4_nameservers_auto,
|
||||||
|
nameservers=ipv4_nameservers,
|
||||||
|
ready=ipv4_ready,
|
||||||
)
|
)
|
||||||
if inet.connection and inet.connection.ipv4
|
if inet.connection and inet.connection.ipv4
|
||||||
else IpConfig(ipv4_method, [], None, [], ipv4_ready),
|
else IpConfig(ipv4_method, [], None, True, [], ipv4_ready),
|
||||||
IpConfig(
|
IpConfig(
|
||||||
ipv6_method,
|
method=ipv6_method,
|
||||||
inet.connection.ipv6.address if inet.connection.ipv6.address else [],
|
address=inet.connection.ipv6.address
|
||||||
inet.connection.ipv6.gateway,
|
if inet.connection.ipv6.address
|
||||||
inet.connection.ipv6.nameservers
|
|
||||||
if inet.connection.ipv6.nameservers
|
|
||||||
else [],
|
else [],
|
||||||
ipv6_ready,
|
gateway=inet.connection.ipv6.gateway,
|
||||||
|
nameservers_auto=ipv6_nameservers_auto,
|
||||||
|
nameservers=ipv6_nameservers,
|
||||||
|
ready=ipv6_ready,
|
||||||
)
|
)
|
||||||
if inet.connection and inet.connection.ipv6
|
if inet.connection and inet.connection.ipv6
|
||||||
else IpConfig(ipv6_method, [], None, [], ipv6_ready),
|
else IpConfig(ipv6_method, [], None, True, [], ipv6_ready),
|
||||||
Interface._map_nm_wifi(inet),
|
Interface._map_nm_wifi(inet),
|
||||||
Interface._map_nm_vlan(inet),
|
Interface._map_nm_vlan(inet),
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user