NCS v3.3.0: OPENTHREAD_TREL=n build broken when OPENTHREAD_ZEPHYR_BORDER_ROUTER=y

Summary

With CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER=y and CONFIG_OPENTHREAD_TREL=n, the Zephyr/NCS build fails to link:

ld.bfd: ... openthread_platform.a(trel.c.obj):
    in function `socket_received_cb':
    trel.c:(.text.socket_received_cb+0x40):
    undefined reference to `otPlatTrelHandleReceived'

The reason is that zephyr/modules/openthread/platform/trel.c is gated on CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER, not on CONFIG_OPENTHREAD_TREL. So the TREL platform glue compiles even when TREL itself is disabled, and references otPlatTrelHandleReceived which is only emitted by the OT core when OPENTHREAD_TREL=y.

This blocks any attempt to use the BR feature group without TREL, which is exactly the configuration we need (see companion DevZone post devzone-trel-mle-wedge.md — TREL=y deadlocks the OT thread on NCS v3.3.0 + OT pin a03011cf7).

Environment

  • nRF Connect SDK: v3.3.0
  • Zephyr: bundled v4.3.99 from the NCS workspace
  • OpenThread: OPENTHREAD_SOURCES=y, upstream pin a03011cf7 (DevZone #127950 prevents using the prebuilt Nordic library here)
  • Board: nrf54lm20dk/nrf54lm20a/cpuapp (also reproduces on nrf5340dk/nrf5340/cpuapp)

Failing Kconfig (minimal):

CONFIG_OPENTHREAD=y
CONFIG_OPENTHREAD_SOURCES=y
CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER=y
CONFIG_OPENTHREAD_TREL=n


Root cause

In zephyr/modules/openthread/CMakeLists.txt (or the equivalent NCS overlay) the openthread_platform library unconditionally adds platform/trel.c whenever OPENTHREAD_ZEPHYR_BORDER_ROUTER is set:

zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER
  ...
  platform/trel.c
  ...
)

platform/trel.c calls otPlatTrelHandleReceived() from its receive callback, but that symbol is only emitted by OT core when OPENTHREAD_TREL=y.

Suggested fix

Tighten the gate:

-zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER
-  platform/trel.c
-)
+zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_TREL
+  platform/trel.c
+)

trel.c belongs to the TREL platform implementation; gating it on OPENTHREAD_TREL is semantically correct and resolves the link error.

A second small change is helpful but optional: in zephyr/subsys/net/l2/openthread/openthread_border_router.c, openthread_start_border_router_services() calls trel_plat_init() unconditionally. With the CMake fix above, trel_plat_init is no longer compiled, so the wrapper now also fails to link. Either:

  • gate the call on IS_ENABLED(CONFIG_OPENTHREAD_TREL), or
  • provide a weak no-op trel_plat_init returning OT_ERROR_NONE in the platform glue when TREL is disabled.

Workaround we are running

In the application's CMakeLists.txt:

if(CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER)
  target_sources(openthread_platform PRIVATE
    ${ZEPHYR_BASE}/modules/openthread/platform/infra_if.c
    ${ZEPHYR_BASE}/modules/openthread/platform/udp.c
    ${ZEPHYR_BASE}/modules/openthread/platform/mdns_socket.c
    ${ZEPHYR_BASE}/modules/openthread/platform/border_agent.c
    ${ZEPHYR_BASE}/modules/openthread/platform/dhcp6_pd.c
  )
  if(CONFIG_OPENTHREAD_TREL)
    target_sources(openthread_platform PRIVATE
      ${ZEPHYR_BASE}/modules/openthread/platform/trel.c
    )
  endif()
endif()

Plus an app-side stub for the missing-when-TREL=n symbols:

#if !defined(CONFIG_OPENTHREAD_TREL)
void otPlatTrelHandleReceived(otInstance *aInstance, uint8_t *aBuffer,
                              uint16_t aLength,
                              const otSockAddr *aSenderAddr) { ... }

otError trel_plat_init(otInstance *instance, struct net_if *ail_iface) {
    return OT_ERROR_NONE;
}
#endif

This is a cosmetic, app-local workaround for what should be a one-line CMake fix upstream.

Reproducer

A minimal app with the Kconfig snippet above and any OPENTHREAD_ZEPHYR_BORDER_ROUTER=y configuration will fail to link in stock NCS v3.3.0.

Parents Reply Children
Related