Skip to content

Commit 4a32a0f

Browse files
authored
Expand network util to check for link local addresses (#33499)
1 parent 0cf9268 commit 4a32a0f

File tree

4 files changed

+77
-9
lines changed

4 files changed

+77
-9
lines changed

homeassistant/components/axis/config_flow.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Config flow to configure Axis devices."""
22

3+
from ipaddress import ip_address
4+
35
import voluptuous as vol
46

57
from homeassistant import config_entries
@@ -11,6 +13,7 @@
1113
CONF_PORT,
1214
CONF_USERNAME,
1315
)
16+
from homeassistant.util.network import is_link_local
1417

1518
from .const import CONF_MODEL, DOMAIN
1619
from .device import get_device
@@ -129,7 +132,7 @@ async def async_step_zeroconf(self, discovery_info):
129132
if serial_number[:6] not in AXIS_OUI:
130133
return self.async_abort(reason="not_axis_device")
131134

132-
if discovery_info[CONF_HOST].startswith("169.254"):
135+
if is_link_local(ip_address(discovery_info[CONF_HOST])):
133136
return self.async_abort(reason="link_local_address")
134137

135138
await self.async_set_unique_id(serial_number)

homeassistant/components/doorbird/config_flow.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Config flow for DoorBird integration."""
2+
from ipaddress import ip_address
23
import logging
34
import urllib
45

@@ -8,6 +9,7 @@
89
from homeassistant import config_entries, core, exceptions
910
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME
1011
from homeassistant.core import callback
12+
from homeassistant.util.network import is_link_local
1113

1214
from .const import CONF_EVENTS, DOORBIRD_OUI
1315
from .const import DOMAIN # pylint:disable=unused-import
@@ -90,7 +92,7 @@ async def async_step_zeroconf(self, discovery_info):
9092

9193
if macaddress[:6] != DOORBIRD_OUI:
9294
return self.async_abort(reason="not_doorbird_device")
93-
if discovery_info[CONF_HOST].startswith("169.254"):
95+
if is_link_local(ip_address(discovery_info[CONF_HOST])):
9496
return self.async_abort(reason="link_local_address")
9597

9698
await self.async_set_unique_id(macaddress)

homeassistant/util/network.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,41 @@
11
"""Network utilities."""
2-
from ipaddress import IPv4Address, IPv6Address, ip_address, ip_network
2+
from ipaddress import IPv4Address, IPv6Address, ip_network
33
from typing import Union
44

5-
# IP addresses of loopback interfaces
6-
LOCAL_IPS = (ip_address("127.0.0.1"), ip_address("::1"))
5+
# RFC6890 - IP addresses of loopback interfaces
6+
LOOPBACK_NETWORKS = (
7+
ip_network("127.0.0.0/8"),
8+
ip_network("::1/128"),
9+
ip_network("::ffff:127.0.0.0/104"),
10+
)
711

8-
# RFC1918 - Address allocation for Private Internets
9-
LOCAL_NETWORKS = (
12+
# RFC6890 - Address allocation for Private Internets
13+
PRIVATE_NETWORKS = (
14+
ip_network("fd00::/8"),
1015
ip_network("10.0.0.0/8"),
1116
ip_network("172.16.0.0/12"),
1217
ip_network("192.168.0.0/16"),
1318
)
1419

20+
# RFC6890 - Link local ranges
21+
LINK_LOCAL_NETWORK = ip_network("169.254.0.0/16")
22+
23+
24+
def is_loopback(address: Union[IPv4Address, IPv6Address]) -> bool:
25+
"""Check if an address is a loopback address."""
26+
return any(address in network for network in LOOPBACK_NETWORKS)
27+
28+
29+
def is_private(address: Union[IPv4Address, IPv6Address]) -> bool:
30+
"""Check if an address is a private address."""
31+
return any(address in network for network in PRIVATE_NETWORKS)
32+
33+
34+
def is_link_local(address: Union[IPv4Address, IPv6Address]) -> bool:
35+
"""Check if an address is link local."""
36+
return address in LINK_LOCAL_NETWORK
37+
1538

1639
def is_local(address: Union[IPv4Address, IPv6Address]) -> bool:
17-
"""Check if an address is local."""
18-
return address in LOCAL_IPS or any(address in network for network in LOCAL_NETWORKS)
40+
"""Check if an address is loopback or private."""
41+
return is_loopback(address) or is_private(address)

tests/util/test_network.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Test Home Assistant volume utility functions."""
2+
3+
from ipaddress import ip_address
4+
5+
import homeassistant.util.network as network_util
6+
7+
8+
def test_is_loopback():
9+
"""Test loopback addresses."""
10+
assert network_util.is_loopback(ip_address("127.0.0.2"))
11+
assert network_util.is_loopback(ip_address("127.0.0.1"))
12+
assert network_util.is_loopback(ip_address("::1"))
13+
assert network_util.is_loopback(ip_address("::ffff:127.0.0.0"))
14+
assert network_util.is_loopback(ip_address("0:0:0:0:0:0:0:1"))
15+
assert network_util.is_loopback(ip_address("0:0:0:0:0:ffff:7f00:1"))
16+
assert not network_util.is_loopback(ip_address("104.26.5.238"))
17+
assert not network_util.is_loopback(ip_address("2600:1404:400:1a4::356e"))
18+
19+
20+
def test_is_private():
21+
"""Test private addresses."""
22+
assert network_util.is_private(ip_address("192.168.0.1"))
23+
assert network_util.is_private(ip_address("172.16.12.0"))
24+
assert network_util.is_private(ip_address("10.5.43.3"))
25+
assert network_util.is_private(ip_address("fd12:3456:789a:1::1"))
26+
assert not network_util.is_private(ip_address("127.0.0.1"))
27+
assert not network_util.is_private(ip_address("::1"))
28+
29+
30+
def test_is_link_local():
31+
"""Test link local addresses."""
32+
assert network_util.is_link_local(ip_address("169.254.12.3"))
33+
assert not network_util.is_link_local(ip_address("127.0.0.1"))
34+
35+
36+
def test_is_local():
37+
"""Test local addresses."""
38+
assert network_util.is_local(ip_address("192.168.0.1"))
39+
assert network_util.is_local(ip_address("127.0.0.1"))
40+
assert not network_util.is_local(ip_address("208.5.4.2"))

0 commit comments

Comments
 (0)