Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ed7f173
added input variables: omr_prefix and leader_weight
tannerdsilva Aug 2, 2026
7826c37
delete nat64 features. just seems like a distraction right now
tannerdsilva Aug 2, 2026
60aa2c6
replaced iptables firewall with more robust chains on nftables
tannerdsilva Aug 2, 2026
ace4ab4
revised Thread configuration and attachment processes to optimize for…
tannerdsilva Aug 3, 2026
ae56cdd
installed upstream openthread patches for the non-beta build
tannerdsilva Aug 3, 2026
eaef03b
adding new configuration spec to header
tannerdsilva Aug 3, 2026
065502b
otbr: sync routing layer + fix Dockerfile COPY for 0003/0004 patches
tannerdsilva Aug 8, 2026
d89076c
otbr: sync nftables firewall with ts-otbr (scope-gated multicast forw…
tannerdsilva Aug 8, 2026
84ed9fb
otbr: sync s6 hardening services from ts-otbr (route-guard, ipset-syn…
tannerdsilva Aug 8, 2026
b821ff3
otbr: wire otbr-route-guard into user/contents.d (match ts-otbr)
tannerdsilva Aug 8, 2026
9870b31
this should be ready to submit
tannerdsilva Aug 22, 2026
ce727c4
otbr: drop min/max router threshold options from config UI
tannerdsilva Aug 25, 2026
b5d7711
otbr: drop min/max router threshold docs references
tannerdsilva Aug 25, 2026
93712e1
otbr: remove router up/downgrade threshold handling from agent config…
tannerdsilva Aug 26, 2026
8e4dc74
otbr: rebase onto ot-br-posix #3325 and trim patch set to one
tannerdsilva Aug 29, 2026
14d8fa1
otbr: enable in-process nftables firewall backend at build time
tannerdsilva Aug 29, 2026
ef7dc40
otbr: wire runtime for in-process nftables backend (ot-br-posix#3325)
tannerdsilva Aug 31, 2026
70457f8
otbr: derive deterministic OMR prefix from Thread network name
tannerdsilva Sep 1, 2026
805d6b4
otbr: scrub changelog for rebased patch set and refresh PR docs pack
tannerdsilva Sep 1, 2026
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
30 changes: 0 additions & 30 deletions openthread_border_router/0001-rest-SO_REUSEADDR.patch

This file was deleted.

167 changes: 0 additions & 167 deletions openthread_border_router/0002-nat64-handle-ipv4-options.patch

This file was deleted.

38 changes: 38 additions & 0 deletions openthread_border_router/0006-routing-manager-corrections.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp
index 6be8b9002..8dfeb1a25 100644
--- a/src/core/border_router/routing_manager.cpp
+++ b/src/core/border_router/routing_manager.cpp
@@ -1943,7 +1943,7 @@ exit:

const otIp6Prefix RoutingManager::RoutePublisher::kUlaPrefix = {
{{{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
- 7,
+ 64,
};

RoutingManager::RoutePublisher::RoutePublisher(Instance &aInstance)
@@ -1995,9 +1995,10 @@ void RoutingManager::RoutePublisher::DeterminePrefixFor(State aState, Ip6::Prefi
case kDoNotPublish:
case kPublishDefault:
// `Clear()` will set the prefix to `::/0`.
+ if (aState == kPublishDefault) aPrefix.Clear();
break;
case kPublishUla:
- aPrefix = GetUlaPrefix();
+ Get<RoutingManager>().GetOmrPrefix(aPrefix);
break;
}
}
@@ -2026,6 +2027,12 @@ void RoutingManager::RoutePublisher::UpdatePublishedRoute(State aNewState)
routeConfig.mAdvPio = mAdvPioFlag;
routeConfig.mStable = true;
DeterminePrefixFor(aNewState, routeConfig.GetPrefix());
+
+ if (routeConfig.GetPrefix().GetLength() == 0)
+ {
+ LogWarn("Blocked publishing default route (::/0) to Linux platform API");
+ return; // Exit early, never call PublishExternalRoute
+ }

// If we were not publishing a route prefix before, publish the new
// `routeConfig`. Otherwise, use `ReplacePublishedExternalRoute()` to
35 changes: 35 additions & 0 deletions openthread_border_router/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
# Changelog

## 4.0.0

### Major: fix multi-border-router routing loop (critical)

This version fixes a critical routing loop affecting any deployment with more than one
OpenThread Border Router on the same L2 segment. OTBR's RoutePublisher advertised the broad
`fc00::/7` catch-all prefix as an external route `/7` covers the entire ULA space, so ULA
traffic destined for other VLANs was misrouted into the Thread mesh, circulating between BRs
instead of egressing the physical uplink. Recovery from the resulting state is time-consuming
and operationally damaging across both large and small sites, which is why this is shipped as
a major version.

**The fix has two complementary layers:**
1. **Stable OMR prefix** (`custom_omr_prefix`, or a deterministic hash of the Thread network name) - the BR prefix is now
deterministic across restarts, eliminating the orphaned-partition trigger for the
catch-all route.
2. **Routing manager corrections** - `kUlaPrefix` tightened `/7` -> `/64`,
`NetworkDataContainsUlaRoute()` now requires a stable `/64`, and `kPublishUla` publishes
the real OMR prefix. This is the protocol-layer fix: without it, patched BRs still honor
`/7` advertised by unpatched peers.

### Also in 4.0.0

- NAT64: the add-on's nftables NAT44 masquerade (mark + postrouting + forward) and the in-process backend's NAT44 (via the `#3325` backend when enabled) replace the removed iptables rules. NAT64 now works without iptables in the runtime image.
- Build: the patch set is a single `0006` routing-manager-corrections patch applied to both build stages, rebased onto the ot-br-posix main tip:
- Patches 0001 (SO_REUSEADDR) and 0002 (NAT64 IPv4 options) proved to be already upstream on the rebased base and were dropped.
- The speculative ULA-prefix patches (0003/0004) and the broad-ULA-route patch (0005) were removed; external-route installation is instead disabled with `OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE=0` in the project config header (the upstream openthread#13562 mechanism).
- Firewall hardening (production readiness):
- **Rate limiting**: OMR accept rules in both forward directions now rate-limited (1000/sec backbone→Thread, 2000/sec Thread→backbone) to prevent DoS of the Thread mesh.
- **TCP MSS clamping**: Backbone→Thread TCP SYNs have MSS clamped to 1220 bytes to prevent fragmentation across the 1280-byte Thread MTU boundary.
- **TREL port race fix**: The TREL UDP port is now queried before firewall creation and pre-populated into the `trel_ports` nftables set, eliminating the 1-30 second window where TREL was silently dropped after restart.
- OMR prefix default: when `custom_omr_prefix` is empty, a deterministic ULA `/64` is derived by hashing the Thread network name, so every border router on a mesh converges on the same prefix automatically (Apple border routers behave the same way) without setting it per-BR.
- Rebase: build from the ot-br-posix main tip (includes #3325, the opt-in in-process nftables firewall backend) and trim the patch set to a single routing-manager patch (`0001`/`0002` are now upstream, the speculative ULA-prefix patches were removed, and external-route installation is disabled via the config header instead of a broad-ULA-route patch).
- Docs: document `backbone_interface`, `custom_omr_priority`, and `leader_weight` configuration options.

## 3.0.2

- Honor the configured `otbr_log_level` for the OTBR web interface (previously always logged at info level)
Expand Down
4 changes: 4 additions & 0 deletions openthread_border_router/DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ App configuration:
| nat64 | Enable NAT64 to allow Thread devices accessing IPv4 addresses |
| network_device | IP address and port to connect to a network-based RCP (see below) |
| beta | Enable beta mode to run a newer, experimental version of OpenThread Border Router |
| backbone_interface | Override the auto-detected primary network interface used for IPv6 routing |
| custom_omr_prefix | Force a specific Off-Mesh Routable (OMR) prefix (e.g. `fd42:0001::/64`), or leave empty to derive a deterministic `/64` from the Thread network name. With derivation, every border router on a mesh converges on the same prefix automatically (Apple border routers behave the same way). A stable OMR prefix across restarts prevents the multi-BR loop (`fc00::/7` catch-all). |
| custom_omr_priority | Set the Route Information Option (RIO) preference for the OMR prefix. One of `high`, `med` (default), or `low`. In multi-BR deployments, the BR with the highest-priority OMR prefix is preferred by LAN hosts. Set this lower on backup BRs so primary BR routes are preferred. |
| leader_weight | Thread Leader weight (0--255, default: 72). Influences which BR is elected as the Thread partition Leader. Higher values make this BR more likely to become Leader. In multi-BR deployments, set a higher weight on the primary BR and lower on backups to ensure stable Leader election. |

> [!WARNING]
> The OTBR expects the RCP connected radio to be on a reliable link such as
Expand Down
28 changes: 19 additions & 9 deletions openthread_border_router/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ ENV DOCKER=1


COPY openthread-core-ha-config-posix.h /usr/src/
COPY 0006-routing-manager-corrections.patch /usr/src/

WORKDIR /usr/src
RUN \
Expand All @@ -31,9 +32,11 @@ RUN \
build-essential \
ninja-build \
cmake \
iptables \
nftables \
libjsoncpp-dev \
libnetfilter-queue-dev \
libnftnl-dev \
libmnl-dev \
nodejs \
npm \
libprotobuf-dev \
Expand All @@ -43,7 +46,9 @@ RUN \
&& cd /usr/src/ot-br-posix \
&& git fetch origin ${OTBR_BETA_VERSION} \
&& git checkout ${OTBR_BETA_VERSION} \
&& git submodule update --init --recursive --depth 1
&& git submodule update --init --recursive --depth 1 \
&& echo "--- Applying patches to beta build ---" \
&& patch -p1 -d third_party/openthread/repo < /usr/src/0006-routing-manager-corrections.patch

WORKDIR /usr/src/ot-br-posix
RUN \
Expand Down Expand Up @@ -72,12 +77,14 @@ RUN \
-DOTBR_REST=ON \
-DOTBR_BACKBONE_ROUTER=ON \
-DOTBR_TREL=ON \
-DOTBR_NFTABLES=ON \
-DOTBR_NAT64=ON \
-DOT_POSIX_NAT64_CIDR="192.168.255.0/24" \
-DOTBR_DNS_UPSTREAM_QUERY=ON \
-DOT_CHANNEL_MONITOR=ON \
-DOT_COAP=OFF \
-DOT_COAPS=OFF \
-DOTBR_NO_AUTO_ATTACH=0 \
-DOT_THREAD_VERSION=1.4 \
-DOT_PROJECT_CONFIG="/usr/src/ot-br-posix/third_party/openthread/repo/openthread-core-ha-config-posix.h" \
-DOT_RCP_RESTORATION_MAX_COUNT=2 \
Expand All @@ -99,10 +106,8 @@ ENV WEB_GUI=1
ENV REST_API=1
ENV DOCKER=1


COPY openthread-core-ha-config-posix.h /usr/src/
COPY 0001-rest-SO_REUSEADDR.patch /usr/src/
COPY 0002-nat64-handle-ipv4-options.patch /usr/src/
COPY 0006-routing-manager-corrections.patch /usr/src/

WORKDIR /usr/src
RUN \
Expand All @@ -118,9 +123,11 @@ RUN \
build-essential \
ninja-build \
cmake \
iptables \
nftables \
libjsoncpp-dev \
libnetfilter-queue-dev \
libnftnl-dev \
libmnl-dev \
nodejs \
npm \
libprotobuf-dev \
Expand All @@ -130,8 +137,7 @@ RUN \
&& git fetch origin ${OTBR_VERSION} \
&& git checkout ${OTBR_VERSION} \
&& git submodule update --init --recursive --depth 1 \
&& patch -p1 < /usr/src/0001-rest-SO_REUSEADDR.patch \
&& patch -p1 -d third_party/openthread/repo < /usr/src/0002-nat64-handle-ipv4-options.patch
&& patch -p1 -d third_party/openthread/repo < /usr/src/0006-routing-manager-corrections.patch

WORKDIR /usr/src/ot-br-posix
RUN \
Expand Down Expand Up @@ -160,13 +166,17 @@ RUN \
-DOTBR_REST=ON \
-DOTBR_BACKBONE_ROUTER=ON \
-DOTBR_TREL=ON \
-DOTBR_NFTABLES=ON \
-DOTBR_NAT64=ON \
-DOT_POSIX_NAT64_CIDR="192.168.255.0/24" \
-DOTBR_DNS_UPSTREAM_QUERY=ON \
-DOT_CHANNEL_MONITOR=ON \
-DOT_COAP=OFF \
-DOT_COAPS=OFF \
-DOTBR_NO_AUTO_ATTACH=0 \
-DOT_THREAD_VERSION=1.4 \
-DOTBR_DHCP6_PD=ON \
-DOTBR_DHCP6_PD_CLIENT=openthread \
-DOT_PROJECT_CONFIG="/usr/src/ot-br-posix/third_party/openthread/repo/openthread-core-ha-config-posix.h" \
-DOT_RCP_RESTORATION_MAX_COUNT=2 \
&& ninja \
Expand All @@ -191,7 +201,7 @@ RUN \
iproute2 \
iputils-ping \
ipset \
iptables \
nftables \
libreadline8 \
libncurses6 \
libprotobuf-lite32 \
Expand Down
Loading