Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion homeassistant/components/axis/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Config flow to configure Axis devices."""

from ipaddress import ip_address

import voluptuous as vol

from homeassistant import config_entries
Expand All @@ -11,6 +13,7 @@
CONF_PORT,
CONF_USERNAME,
)
from homeassistant.util.network import is_link_local

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

if discovery_info[CONF_HOST].startswith("169.254"):
if is_link_local(ip_address(discovery_info[CONF_HOST])):
return self.async_abort(reason="link_local_address")

await self.async_set_unique_id(serial_number)
Expand Down
4 changes: 3 additions & 1 deletion homeassistant/components/doorbird/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Config flow for DoorBird integration."""
from ipaddress import ip_address
import logging
import urllib

Expand All @@ -8,6 +9,7 @@
from homeassistant import config_entries, core, exceptions
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import callback
from homeassistant.util.network import is_link_local

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

if macaddress[:6] != DOORBIRD_OUI:
return self.async_abort(reason="not_doorbird_device")
if discovery_info[CONF_HOST].startswith("169.254"):
if is_link_local(ip_address(discovery_info[CONF_HOST])):
return self.async_abort(reason="link_local_address")

await self.async_set_unique_id(macaddress)
Expand Down
37 changes: 30 additions & 7 deletions homeassistant/util/network.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,41 @@
"""Network utilities."""
from ipaddress import IPv4Address, IPv6Address, ip_address, ip_network
from ipaddress import IPv4Address, IPv6Address, ip_network
from typing import Union

# IP addresses of loopback interfaces
LOCAL_IPS = (ip_address("127.0.0.1"), ip_address("::1"))
# RFC6890 - IP addresses of loopback interfaces
LOOPBACK_NETWORKS = (
ip_network("127.0.0.0/8"),
ip_network("::1/128"),
ip_network("::ffff:127.0.0.0/104"),
)

# RFC1918 - Address allocation for Private Internets
LOCAL_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"),
)

# RFC6890 - Link local ranges
LINK_LOCAL_NETWORK = ip_network("169.254.0.0/16")


def is_loopback(address: Union[IPv4Address, IPv6Address]) -> bool:
"""Check if an address is a loopback address."""
return any(address in network for network in LOOPBACK_NETWORKS)


def is_private(address: Union[IPv4Address, IPv6Address]) -> bool:
"""Check if an address is a private address."""
return any(address in network for network in PRIVATE_NETWORKS)


def is_link_local(address: Union[IPv4Address, IPv6Address]) -> bool:
"""Check if an address is link local."""
return address in LINK_LOCAL_NETWORK


def is_local(address: Union[IPv4Address, IPv6Address]) -> bool:
"""Check if an address is local."""
return address in LOCAL_IPS or any(address in network for network in LOCAL_NETWORKS)
"""Check if an address is loopback or private."""
return is_loopback(address) or is_private(address)
40 changes: 40 additions & 0 deletions tests/util/test_network.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Test Home Assistant volume utility functions."""

from ipaddress import ip_address

import homeassistant.util.network as network_util


def test_is_loopback():
"""Test loopback addresses."""
assert network_util.is_loopback(ip_address("127.0.0.2"))
assert network_util.is_loopback(ip_address("127.0.0.1"))
assert network_util.is_loopback(ip_address("::1"))
assert network_util.is_loopback(ip_address("::ffff:127.0.0.0"))
assert network_util.is_loopback(ip_address("0:0:0:0:0:0:0:1"))
assert network_util.is_loopback(ip_address("0:0:0:0:0:ffff:7f00:1"))
assert not network_util.is_loopback(ip_address("104.26.5.238"))
assert not network_util.is_loopback(ip_address("2600:1404:400:1a4::356e"))


def test_is_private():
"""Test private addresses."""
assert network_util.is_private(ip_address("192.168.0.1"))
assert network_util.is_private(ip_address("172.16.12.0"))
assert network_util.is_private(ip_address("10.5.43.3"))
assert network_util.is_private(ip_address("fd12:3456:789a:1::1"))
assert not network_util.is_private(ip_address("127.0.0.1"))
assert not network_util.is_private(ip_address("::1"))


def test_is_link_local():
"""Test link local addresses."""
assert network_util.is_link_local(ip_address("169.254.12.3"))
assert not network_util.is_link_local(ip_address("127.0.0.1"))


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 not network_util.is_local(ip_address("208.5.4.2"))