Avoid enabling ipv6 dual stack for zeroconf on unsupported platforms (#56584)
This commit is contained in:
parent
f74291ccb6
commit
26f73779cc
2 changed files with 96 additions and 18 deletions
|
@ -5,9 +5,10 @@ import asyncio
|
|||
from collections.abc import Coroutine
|
||||
from contextlib import suppress
|
||||
import fnmatch
|
||||
from ipaddress import IPv6Address, ip_address
|
||||
from ipaddress import IPv4Address, IPv6Address, ip_address
|
||||
import logging
|
||||
import socket
|
||||
import sys
|
||||
from typing import Any, TypedDict, cast
|
||||
|
||||
import voluptuous as vol
|
||||
|
@ -131,18 +132,31 @@ async def _async_get_instance(hass: HomeAssistant, **zcargs: Any) -> HaAsyncZero
|
|||
return aio_zc
|
||||
|
||||
|
||||
@callback
|
||||
def _async_zc_has_functional_dual_stack() -> bool:
|
||||
"""Return true for platforms that not support IP_ADD_MEMBERSHIP on an AF_INET6 socket.
|
||||
|
||||
Zeroconf only supports a single listen socket at this time.
|
||||
"""
|
||||
return not sys.platform.startswith("freebsd") and not sys.platform.startswith(
|
||||
"darwin"
|
||||
)
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up Zeroconf and make Home Assistant discoverable."""
|
||||
zc_args: dict = {}
|
||||
zc_args: dict = {"ip_version": IPVersion.V4Only}
|
||||
|
||||
adapters = await network.async_get_adapters(hass)
|
||||
|
||||
ipv6 = True
|
||||
if not any(adapter["enabled"] and adapter["ipv6"] for adapter in adapters):
|
||||
ipv6 = False
|
||||
zc_args["ip_version"] = IPVersion.V4Only
|
||||
else:
|
||||
zc_args["ip_version"] = IPVersion.All
|
||||
ipv6 = False
|
||||
if _async_zc_has_functional_dual_stack():
|
||||
if any(adapter["enabled"] and adapter["ipv6"] for adapter in adapters):
|
||||
ipv6 = True
|
||||
zc_args["ip_version"] = IPVersion.All
|
||||
elif not any(adapter["enabled"] and adapter["ipv4"] for adapter in adapters):
|
||||
zc_args["ip_version"] = IPVersion.V6Only
|
||||
ipv6 = True
|
||||
|
||||
if not ipv6 and network.async_only_default_interface_enabled(adapters):
|
||||
zc_args["interfaces"] = InterfaceChoice.Default
|
||||
|
@ -152,6 +166,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
for source_ip in await network.async_get_enabled_source_ips(hass)
|
||||
if not source_ip.is_loopback
|
||||
and not (isinstance(source_ip, IPv6Address) and source_ip.is_global)
|
||||
and not (
|
||||
isinstance(source_ip, IPv6Address)
|
||||
and zc_args["ip_version"] == IPVersion.V4Only
|
||||
)
|
||||
and not (
|
||||
isinstance(source_ip, IPv4Address)
|
||||
and zc_args["ip_version"] == IPVersion.V6Only
|
||||
)
|
||||
]
|
||||
|
||||
aio_zc = await _async_get_instance(hass, **zc_args)
|
||||
|
|
|
@ -779,11 +779,13 @@ _ADAPTERS_WITH_MANUAL_CONFIG = [
|
|||
]
|
||||
|
||||
|
||||
async def test_async_detect_interfaces_setting_empty_route(hass, mock_async_zeroconf):
|
||||
"""Test without default interface config and the route returns nothing."""
|
||||
with patch("homeassistant.components.zeroconf.HaZeroconf") as mock_zc, patch.object(
|
||||
hass.config_entries.flow, "async_init"
|
||||
), patch.object(
|
||||
async def test_async_detect_interfaces_setting_empty_route_linux(
|
||||
hass, mock_async_zeroconf
|
||||
):
|
||||
"""Test without default interface config and the route returns nothing on linux."""
|
||||
with patch("homeassistant.components.zeroconf.sys.platform", "linux"), patch(
|
||||
"homeassistant.components.zeroconf.HaZeroconf"
|
||||
) as mock_zc, patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||
zeroconf, "HaAsyncServiceBrowser", side_effect=service_update_mock
|
||||
), patch(
|
||||
"homeassistant.components.zeroconf.network.async_get_adapters",
|
||||
|
@ -807,6 +809,33 @@ async def test_async_detect_interfaces_setting_empty_route(hass, mock_async_zero
|
|||
)
|
||||
|
||||
|
||||
async def test_async_detect_interfaces_setting_empty_route_freebsd(
|
||||
hass, mock_async_zeroconf
|
||||
):
|
||||
"""Test without default interface config and the route returns nothing on freebsd."""
|
||||
with patch("homeassistant.components.zeroconf.sys.platform", "freebsd"), patch(
|
||||
"homeassistant.components.zeroconf.HaZeroconf"
|
||||
) as mock_zc, patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||
zeroconf, "HaAsyncServiceBrowser", side_effect=service_update_mock
|
||||
), patch(
|
||||
"homeassistant.components.zeroconf.network.async_get_adapters",
|
||||
return_value=_ADAPTERS_WITH_MANUAL_CONFIG,
|
||||
), patch(
|
||||
"homeassistant.components.zeroconf.AsyncServiceInfo",
|
||||
side_effect=get_service_info_mock,
|
||||
):
|
||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_zc.mock_calls[0] == call(
|
||||
interfaces=[
|
||||
"192.168.1.5",
|
||||
"172.16.1.5",
|
||||
],
|
||||
ip_version=IPVersion.V4Only,
|
||||
)
|
||||
|
||||
|
||||
async def test_get_announced_addresses(hass, mock_async_zeroconf):
|
||||
"""Test addresses for mDNS announcement."""
|
||||
expected = {
|
||||
|
@ -848,11 +877,13 @@ _ADAPTER_WITH_DEFAULT_ENABLED_AND_IPV6 = [
|
|||
]
|
||||
|
||||
|
||||
async def test_async_detect_interfaces_explicitly_set_ipv6(hass, mock_async_zeroconf):
|
||||
"""Test interfaces are explicitly set when IPv6 is present."""
|
||||
with patch("homeassistant.components.zeroconf.HaZeroconf") as mock_zc, patch.object(
|
||||
hass.config_entries.flow, "async_init"
|
||||
), patch.object(
|
||||
async def test_async_detect_interfaces_explicitly_set_ipv6_linux(
|
||||
hass, mock_async_zeroconf
|
||||
):
|
||||
"""Test interfaces are explicitly set when IPv6 is present on linux."""
|
||||
with patch("homeassistant.components.zeroconf.sys.platform", "linux"), patch(
|
||||
"homeassistant.components.zeroconf.HaZeroconf"
|
||||
) as mock_zc, patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||
zeroconf, "HaAsyncServiceBrowser", side_effect=service_update_mock
|
||||
), patch(
|
||||
"homeassistant.components.zeroconf.network.async_get_adapters",
|
||||
|
@ -871,6 +902,31 @@ async def test_async_detect_interfaces_explicitly_set_ipv6(hass, mock_async_zero
|
|||
)
|
||||
|
||||
|
||||
async def test_async_detect_interfaces_explicitly_set_ipv6_freebsd(
|
||||
hass, mock_async_zeroconf
|
||||
):
|
||||
"""Test interfaces are explicitly set when IPv6 is present on freebsd."""
|
||||
with patch("homeassistant.components.zeroconf.sys.platform", "freebsd"), patch(
|
||||
"homeassistant.components.zeroconf.HaZeroconf"
|
||||
) as mock_zc, patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||
zeroconf, "HaAsyncServiceBrowser", side_effect=service_update_mock
|
||||
), patch(
|
||||
"homeassistant.components.zeroconf.network.async_get_adapters",
|
||||
return_value=_ADAPTER_WITH_DEFAULT_ENABLED_AND_IPV6,
|
||||
), patch(
|
||||
"homeassistant.components.zeroconf.AsyncServiceInfo",
|
||||
side_effect=get_service_info_mock,
|
||||
):
|
||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_zc.mock_calls[0] == call(
|
||||
interfaces=InterfaceChoice.Default,
|
||||
ip_version=IPVersion.V4Only,
|
||||
)
|
||||
|
||||
|
||||
async def test_no_name(hass, mock_async_zeroconf):
|
||||
"""Test fallback to Home for mDNS announcement if the name is missing."""
|
||||
hass.config.location_name = ""
|
||||
|
|
Loading…
Add table
Reference in a new issue