diff --git a/homeassistant/util/network.py b/homeassistant/util/network.py index 87077a0eb0a..7d0d6e99639 100644 --- a/homeassistant/util/network.py +++ b/homeassistant/util/network.py @@ -14,14 +14,21 @@ LOOPBACK_NETWORKS = ( # RFC6890 - Address allocation for Private Internets PRIVATE_NETWORKS = ( - ip_network("fd00::/8"), ip_network("10.0.0.0/8"), ip_network("172.16.0.0/12"), ip_network("192.168.0.0/16"), + ip_network("fd00::/8"), + ip_network("::ffff:10.0.0.0/104"), + ip_network("::ffff:172.16.0.0/108"), + ip_network("::ffff:192.168.0.0/112"), ) # RFC6890 - Link local ranges -LINK_LOCAL_NETWORK = ip_network("169.254.0.0/16") +LINK_LOCAL_NETWORKS = ( + ip_network("169.254.0.0/16"), + ip_network("fe80::/10"), + ip_network("::ffff:169.254.0.0/112"), +) def is_loopback(address: IPv4Address | IPv6Address) -> bool: @@ -30,18 +37,18 @@ def is_loopback(address: IPv4Address | IPv6Address) -> bool: def is_private(address: IPv4Address | IPv6Address) -> bool: - """Check if an address is a private address.""" + """Check if an address is a unique local non-loopback address.""" return any(address in network for network in PRIVATE_NETWORKS) def is_link_local(address: IPv4Address | IPv6Address) -> bool: - """Check if an address is link local.""" - return address in LINK_LOCAL_NETWORK + """Check if an address is link-local (local but not necessarily unique).""" + return any(address in network for network in LINK_LOCAL_NETWORKS) def is_local(address: IPv4Address | IPv6Address) -> bool: - """Check if an address is loopback or private.""" - return is_loopback(address) or is_private(address) + """Check if an address is on a local network.""" + return is_loopback(address) or is_private(address) or is_link_local(address) def is_invalid(address: IPv4Address | IPv6Address) -> bool: diff --git a/tests/util/test_network.py b/tests/util/test_network.py index 4f372e5e1a7..7339b6dc51d 100644 --- a/tests/util/test_network.py +++ b/tests/util/test_network.py @@ -30,7 +30,9 @@ def test_is_private(): def test_is_link_local(): """Test link local addresses.""" assert network_util.is_link_local(ip_address("169.254.12.3")) + assert network_util.is_link_local(ip_address("fe80::1234:5678:abcd")) assert not network_util.is_link_local(ip_address("127.0.0.1")) + assert not network_util.is_link_local(ip_address("::1")) def test_is_invalid(): @@ -43,7 +45,13 @@ def test_is_local(): """Test local addresses.""" assert network_util.is_local(ip_address("192.168.0.1")) assert network_util.is_local(ip_address("127.0.0.1")) + assert network_util.is_local(ip_address("fd12:3456:789a:1::1")) + assert network_util.is_local(ip_address("fe80::1234:5678:abcd")) + assert network_util.is_local(ip_address("::ffff:192.168.0.1")) assert not network_util.is_local(ip_address("208.5.4.2")) + assert not network_util.is_local(ip_address("198.51.100.1")) + assert not network_util.is_local(ip_address("2001:DB8:FA1::1")) + assert not network_util.is_local(ip_address("::ffff:208.5.4.2")) def test_is_ip_address():