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

openthread-border-router: init at unstable-2024-10-18 #332296

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions nixos/doc/manual/release-notes/rl-2505.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@

- [MaryTTS](https://github.com/marytts/marytts), an open-source, multilingual text-to-speech synthesis system written in pure Java. Available as [services.marytts](options.html#opt-services.marytts).

- [OpenThread Border Router](https://github.com/openthread/ot-br-posix), a border router bridging Thread mesh networks with IPv6 networks. Available as [services.openthread-border-router](#opt-services.openthread-border-router.enable).

- [networking.modemmanager](options.html#opt-networking.modemmanager) has been split out of [networking.networkmanager](options.html#opt-networking.networkmanager). NetworkManager still enables ModemManager by default, but options exist now to run NetworkManager without ModemManager.

- [Conduwuit](https://conduwuit.puppyirl.gay/), a federated chat server implementing the Matrix protocol, forked from Conduit. Available as [services.conduwuit](#opt-services.conduwuit.enable).
Expand Down
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@
./services/home-automation/govee2mqtt.nix
./services/home-automation/home-assistant.nix
./services/home-automation/matter-server.nix
./services/home-automation/openthread-border-router.nix
./services/home-automation/wyoming/faster-whisper.nix
./services/home-automation/wyoming/openwakeword.nix
./services/home-automation/wyoming/piper.nix
Expand Down
234 changes: 234 additions & 0 deletions nixos/modules/services/home-automation/openthread-border-router.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
{
lib,
config,
pkgs,
...
}:

let
cfg = config.services.openthread-border-router;
logLevelMappings = {
"emerg" = 0;
"alert" = 1;
"crit" = 2;
"err" = 3;
"warning" = 4;
"notice" = 5;
"info" = 6;
"debug" = 7;
};
logLevel = lib.getAttr cfg.logLevel logLevelMappings;
in
{
meta.maintainers = with lib.maintainers; [ mrene ];
mrene marked this conversation as resolved.
Show resolved Hide resolved

options.services.openthread-border-router = {
Copy link
Contributor

@milas milas Dec 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EDIT: Hmm...it looks like the port number for the listening service is dynamic. I was confused and thought 49154 was hardcoded somewhere (note: there ARE other hardcoded ports in OpenThread code 🙈) but I think now it's simply from the "Dynamic and/or Private Ports (49152-65535)" range and shows up a lot in the OpenThread docs by coincidence.

Frustratingly, from what I can tell, it's not clear if this is customizable after compile-time, which makes this tricky.


Original comment:

I think we also need an openFirewall option:

networking.firewall.allowedUDPPorts = [
    49154
];

I don't think it makes sense to have firewall options for REST/web.

In a VM/lab environment, the admin should add allowedTCPPorts for web.listenPort / rest.listenPort themselves.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened a discussion about listener ports upstream: https://github.com/orgs/openthread/discussions/11093

enable = lib.mkEnableOption "the OpenThread Border Router";

package = lib.mkPackageOption pkgs "openthread-border-router" { };

radioDevice = lib.mkOption {
type = lib.types.str;
default = "/dev/ttyUSB0";
description = "The device name of the serial port of the radio device";
};

backboneInterface = lib.mkOption {
type = lib.types.str;
default = "eth0";
description = "The network interface on which to advertise the thread ipv6 mesh prefix";
};

interfaceName = lib.mkOption {
type = lib.types.str;
default = "wpan0";
description = "The network interface to create for thread packets";
};

logLevel = lib.mkOption {
type = lib.types.enum [
"emerg"
"alert"
"crit"
"err"
"warning"
"notice"
"info"
"debug"
];
default = "err";
description = "The level to use when logging messages";
};

rest = {
listenAddress = lib.mkOption {
type = lib.types.str;
default = "::";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this should default to 127.0.0.1. (Specifically, a localhost IP, and it appears that this is the convention throughout NixOS/nixpgs.)

The REST API works fine on IPv4 even though Matter comms themselves use IPv6.

(Users can still pass ::1 if they want to listen on IPv6 localhost instead or :: as currently to listen on all.)

description = "The address on which to listen for REST API requests";
example = "::";
};

listenPort = lib.mkOption {
type = lib.types.port;
default = 8081;
description = "The port on which to listen for REST API requests";
};
};

web = {
enable = lib.mkEnableOption "the web interface";
listenAddress = lib.mkOption {
type = lib.types.str;
default = "::";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, I think the web interface should default to a local IP, i.e. 127.0.0.1.

description = "The address on which the web interface should listen";
example = "::";
};

listenPort = lib.mkOption {
type = lib.types.port;
default = 8082;
description = "The port on which the web interface should listen";
};
};

radio = {
device = lib.mkOption {
type = lib.types.path;
default = "/dev/ttyUSB0";
description = "The device name of the serial port of the radio device. Ignored if services.openthread-border-router.radio.url is set.";
};

baudRate = lib.mkOption {
type = lib.types.int;
default = 115200;
description = "The baud rate of the radio device. Ignored if services.openthread-border-router.radio.url is set.";
};

flowControl = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable hardware flow control. Ignored if services.openthread-border-router.radio.url is set.";
};

urlQueryString = lib.mkOption {
type = lib.types.str;
default = "";
description = "Extra URL query string parameters. Ignored if services.openthread-border-router.radio.url is set.";
example = "bus-latency=100&region=ca";
};

url = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "The URL of the radio device to use";
example = "spinel+hdlc+uart:///dev/ttyUSB0?uart-baudrate=460800&uart-flow-control";
};

extraDevices = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra devices to add to the radio device";
example = "[ \"trel://eth0\" ]";
};
};
};

config = lib.mkIf cfg.enable {
services.openthread-border-router.radio.url = lib.mkDefault (
"spinel+hdlc+uart://${cfg.radio.device}?"
+ lib.concatStringsSep "&" (
[ "uart-baudrate=${toString cfg.radio.baudRate}" ]
++ lib.optional cfg.radio.flowControl "uart-flow-control"
++ lib.optional (cfg.radio.urlQueryString != "") cfg.radio.urlQueryString
)
);

# ot-ctl can be used to query the router instance
environment.systemPackages = [ cfg.package ];

# Make sure we have ipv6 support, and that forwarding is enabled
networking.enableIPv6 = true;
boot.kernel.sysctl = {
"net.ipv4.conf.all.forwarding" = 1;
"net.ipv6.conf.all.forwarding" = 1;
"net.ipv6.ip_forward" = 1;

# Make sure we accept IPv6 router advertisements from the local network interface
"net.ipv6.conf.${cfg.backboneInterface}.accept_ra" = 2;
"net.ipv6.conf.${cfg.backboneInterface}.accept_ra_rt_info_max_plen" = 64;
};

# OTBR needs to publish its addresses via avahi
services.avahi = {
enable = true;
publish = {
enable = true;
userServices = true;
};
};

# Synchronize the services with the unit files defined in the source pacakge
systemd.services = {
# Sync with: src/agent/otbr-agent.service.in
# Manually added otbr-firewall calls because they are handled inside platform-specific scripts
# The agent keeps its local state in /var/lib/thread
otbr-agent = {
description = "OpenThread Border Router Agent";
wantedBy = [ "multi-user.target" ];
requires = [ "dbus.socket" ];
after = [ "dbus.socket" ];
environment = {
THREAD_IF = cfg.interfaceName;
};
serviceConfig = {
ExecStartPre = "${lib.getExe' cfg.package "otbr-firewall"} start";
ExecStart = (
lib.concatStringsSep " " (
[ (lib.getExe' cfg.package "otbr-agent") ]
++ [
"--verbose"
"--backbone-ifname ${cfg.backboneInterface}"
"--thread-ifname ${cfg.interfaceName}"
"--debug-level ${toString logLevel}"
]
++ lib.optional (cfg.rest.listenPort != 0) "--rest-listen-port ${toString cfg.rest.listenPort}"
++ lib.optional (cfg.rest.listenAddress != "") "--rest-listen-address ${cfg.rest.listenAddress}"
++ [ cfg.radio.url ]
++ cfg.radio.extraDevices
)
);
ExecStopPost = "${lib.getExe' cfg.package "otbr-firewall"} stop";
KillMode = "mixed";
Restart = "on-failure";
RestartSec = 5;
RestartPreventExitStatus = "SIGKILL";
};
path = [
pkgs.ipset
pkgs.iptables
];
};

# Sync with: src/web/otbr-web.service.in
otbr-web = {
description = "OpenThread Border Router Web";
after = [ "otbr-agent.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = (
lib.concatStringsSep " " (
[
(lib.getExe' cfg.package "otbr-web")
"-I"
"${cfg.interfaceName}"
"-d"
"${toString logLevel}"
]
++ lib.optional (cfg.web.listenAddress != "") "-a ${cfg.web.listenAddress}"
++ lib.optional (cfg.web.listenPort != 0) "-p ${toString cfg.web.listenPort}"
)
);
};
};
};
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d31b2096b1..5d2dec8049 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -104,9 +104,6 @@ include(GNUInstallDirs)

pkg_check_modules(SYSTEMD systemd)

-if(SYSTEMD_FOUND)
- pkg_get_variable(OTBR_SYSTEMD_UNIT_DIR systemd systemdsystemunitdir)
-endif()


add_subdirectory(third_party EXCLUDE_FROM_ALL)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diff --git a/src/web/CMakeLists.txt b/src/web/CMakeLists.txt
index 37ba68ffcf..3706061de5 100644
--- a/src/web/CMakeLists.txt
+++ b/src/web/CMakeLists.txt
@@ -27,7 +27,6 @@
#

pkg_check_modules(JSONCPP jsoncpp REQUIRED)
-set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
find_package(Boost REQUIRED COMPONENTS filesystem system)
17 changes: 17 additions & 0 deletions pkgs/by-name/op/openthread-border-router/firewall-script.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
diff --git a/script/otbr-firewall b/script/otbr-firewall
index 52cef42dd0b..75840f8ae12 100644
--- a/script/otbr-firewall
+++ b/script/otbr-firewall
@@ -38,12 +38,8 @@
# Description: This service sets up firewall for OTBR.
### END INIT INFO

-THREAD_IF="wpan0"
OTBR_FORWARD_INGRESS_CHAIN="OTBR_FORWARD_INGRESS"

-. /lib/lsb/init-functions
-. /lib/init/vars.sh
-
set -euxo pipefail

ipset_destroy_if_exist()
Loading
Loading