Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scope link-local IPv6 addresses received from WebSocket #501

Merged
Show file tree
Hide file tree
Changes from 5 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
12 changes: 11 additions & 1 deletion matter_server/server/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@
default=None,
help="Log file to write to (optional).",
)
parser.add_argument(
"--primary-interface",
type=str,
default=None,
help="Primary network interface for link-local addresses (optional).",
)

args = parser.parse_args()

Expand Down Expand Up @@ -85,7 +91,11 @@ def main() -> None:

# Init server
server = MatterServer(
args.storage_path, int(args.vendorid), int(args.fabricid), int(args.port)
args.storage_path,
int(args.vendorid),
int(args.fabricid),
int(args.port),
args.primary_interface,
)

async def handle_stop(loop: asyncio.AbstractEventLoop) -> None:
Expand Down
6 changes: 4 additions & 2 deletions matter_server/server/device_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ async def commission_on_network(
raise RuntimeError("Device Controller not initialized.")

node_id = self._get_next_node_id()
if ip_addr is not None:
ip_addr = self.server.scope_ipv6_lla(ip_addr)

attempts = 0
# we retry commissioning a few times as we've seen devices in the wild
Expand All @@ -281,10 +283,10 @@ async def commission_on_network(
filter=filter,
)
else:
# Ip-adress provided, use CommissionIP method
LOGGER.info(
"Starting Matter commissioning with IP using Node ID %s (attempt %s/%s).",
"Starting Matter commissioning using Node ID %s and IP %s (attempt %s/%s).",
node_id,
ip_addr,
attempts,
MAX_COMMISSION_RETRIES,
)
Expand Down
29 changes: 29 additions & 0 deletions matter_server/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from __future__ import annotations

import asyncio
import ipaddress
import logging
from typing import Any, Callable, Set
import weakref
Expand Down Expand Up @@ -61,12 +62,14 @@ def __init__(
vendor_id: int,
fabric_id: int,
port: int,
primary_interface: str | None,
) -> None:
"""Initialize the Matter Server."""
self.storage_path = storage_path
self.vendor_id = vendor_id
self.fabric_id = fabric_id
self.port = port
self.primary_interface = primary_interface
self.logger = logging.getLogger(__name__)
self.app = web.Application()
self.loop: asyncio.AbstractEventLoop | None = None
Expand Down Expand Up @@ -166,6 +169,32 @@ def signal_event(self, evt: EventType, data: Any = None) -> None:
else:
callback(evt, data)

def scope_ipv6_lla(self, ip_addr: str) -> str:
"""Scope IPv6 link-local addresses to primary interface.

IPv6 link-local addresses received through the websocket might have no
scope_id or a scope_id which isn't valid on this device. Just assume the
device is connected on the primary interface.
"""
ip_addr_parsed = ipaddress.ip_address(ip_addr)
if not ip_addr_parsed.is_link_local:
return ip_addr

if ip_addr_parsed.scope_id is not None:
# This type of IPv6 manipulation is not supported by the ipaddress lib
ip_addr = ip_addr.split("%")[0]

# Rely on host OS routing table
if self.primary_interface is None:
return ip_addr

self.logger.debug(
"Setting scope of link-local IP address %s to %s",
ip_addr,
self.primary_interface,
)
return ip_addr + "%" + self.primary_interface
agners marked this conversation as resolved.
Show resolved Hide resolved

def register_api_command(
self,
command: str,
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ dependencies = [
"coloredlogs",
"dacite",
"orjson",
"home-assistant-chip-clusters==2023.12.0"
"home-assistant-chip-clusters==2024.1.0"
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
]

[project.optional-dependencies]
server = [
"home-assistant-chip-core==2023.12.0",
"home-assistant-chip-core==2024.1.0",
"cryptography==41.0.7"
]
test = [
Expand Down
2 changes: 1 addition & 1 deletion tests/server/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def fetch_certificates_fixture() -> Generator[MagicMock, None, None]:
@pytest.fixture(name="server")
async def server_fixture() -> AsyncGenerator[MatterServer, None]:
"""Yield a server."""
server = MatterServer("test_storage_path", 1234, 5678, 5580)
server = MatterServer("test_storage_path", 1234, 5678, 5580, None)
await server.start()
yield server
await server.stop()
Expand Down
Loading