Skip to content

Conversation

@francoismichel
Copy link

This PR allows running the whole otbr-agent executable and attach it to a simulated RCP so that it run in OTNS and be a border router for a real Thread mesh.

There are a few points that probably remain to be discussed in this PR, but at least the code is available and works on Linux (Tested on Ubuntu 25).

Here are the points that remain to be discussed:

  • otbr-agent needs to be pre-installed on the system, I felt lukewarm about adding ot-br as a dependency of OTNS, especially that it does not build entirely on all platforms.
  • otns needs to be run as root or we want it to run otbr-agent when clicking on the button
  • I needed to add OpenThread's uart.c file in ot-rfsim, so the code here is simply duplicated from OpenThread's uart.c. There may be better ways to reuse this code from OpenThread directly.
  • I add to re-add OTOutFilter.go since otbr-agent provides a real-time CLI through its stdin.
  • I made a few changes to avoid a busy loop when processing events.

You should be able to test all this on a fresh ubuntu by:

  1. installing otbr
  2. installing OTNS
  3. Creating a mesh with a few nodes and then add an OTBR node
  4. Note the ML-EID address of one of the nodes (not the OTBR node) in the mesh, we call it node-mleid
  5. Open a new terminal window. Run ip addr
  6. Note the ML-EID attach to the wpan0 interface, this is the ML-EID of the OTBR node, and therefore the ML-EID of your computer, we call it my-mleid
  7. Ping a node in the mesh from your computer by running ping -I <my-mleid> <node-mleid>. Don't forget the -I switch since it seems that Ubuntu can't pick the right IP address by itself.
  8. You should see your computer successfully pinging a simulated node! :-)

Let me know your comments and let's iterate on that PR.
Cheers !

@google-cla
Copy link

google-cla bot commented Oct 21, 2025

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist
Copy link

Summary of Changes

Hello @francoismichel, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the OpenThread Network Simulator (OTNS) by introducing the capability to simulate a real OpenThread Border Router (OTBR) agent. By attaching otbr-agent to a simulated Radio Co-processor (RCP), users can now integrate a full-fledged border router into their simulated Thread networks, enabling real-world devices to communicate with simulated Thread nodes. The changes involve adding a new OTBR node type, implementing real-time UART communication for the RCP, and refining event processing within the simulator to handle this new mode of operation efficiently.

Highlights

  • OTBR Integration: Enables running otbr-agent with a simulated RCP in OTNS, allowing a real Thread Border Router to interact with a simulated Thread mesh.
  • New OTBR Node Type: Introduces a dedicated "otbr" node type in the simulator's CLI and UI.
  • Real-time UART Communication: Implements real-time UART handling for the simulated RCP, diverging from the previous virtual-time approach for standard nodes.
  • Improved Event Processing: Refactors event handling in simulation/node.go to use a channel-based approach, eliminating busy-loop calls and improving responsiveness.
  • CLI Output Filtering: Re-introduces OTOutFilter.go to clean up and process CLI output from OpenThread nodes, removing internal log messages and command prompts.
  • Cross-Architecture Support: Updates dependency installation script to support arm64 architecture for grpcwebproxy.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for running an OT-BR node attached to a simulated RCP, which is a great feature for testing real Thread mesh scenarios. The changes are extensive, touching Go, C, and build scripts. The core logic for integrating otbr-agent and ot-ctl seems well-thought-out. I've identified a couple of issues: a likely copy-paste error in the command runner that affects how OTBR RCP paths are displayed, and a potential race condition when starting the otbr-agent process due to a fixed-time sleep. Addressing these will make the implementation more robust. The refactoring to eliminate busy-loops in event processing is a significant improvement in efficiency. Overall, this is a solid contribution.

Comment on lines +1265 to +1270
cc.outputf("ot-rcp : %s\n", ec.Br)
cc.outputf("Executables search path: %s\n", ec.SearchPathsString())
cc.outputf("Detected FTD path : %s\n", ec.FindExecutable(ec.Ftd))
cc.outputf("Detected MTD path : %s\n", ec.FindExecutable(ec.Mtd))
cc.outputf("Detected BR path : %s\n", ec.FindExecutable(ec.Br))
cc.outputf("Detected OTBR RCP path : %s\n", ec.FindExecutable(ec.Br))

Choose a reason for hiding this comment

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

high

It seems there's a copy-paste error here. The output for ot-rcp and Detected OTBR RCP path is using ec.Br instead of ec.Rcp. This will result in displaying the wrong executable path for the OTBR RCP.

Suggested change
cc.outputf("ot-rcp : %s\n", ec.Br)
cc.outputf("Executables search path: %s\n", ec.SearchPathsString())
cc.outputf("Detected FTD path : %s\n", ec.FindExecutable(ec.Ftd))
cc.outputf("Detected MTD path : %s\n", ec.FindExecutable(ec.Mtd))
cc.outputf("Detected BR path : %s\n", ec.FindExecutable(ec.Br))
cc.outputf("Detected OTBR RCP path : %s\n", ec.FindExecutable(ec.Br))
cc.outputf("ot-rcp : %s\n", ec.Rcp)
cc.outputf("Executables search path: %s\n", ec.SearchPathsString())
cc.outputf("Detected FTD path : %s\n", ec.FindExecutable(ec.Ftd))
cc.outputf("Detected MTD path : %s\n", ec.FindExecutable(ec.Mtd))
cc.outputf("Detected BR path : %s\n", ec.FindExecutable(ec.Br))
cc.outputf("Detected OTBR RCP path : %s\n", ec.FindExecutable(ec.Rcp))

logger.Errorf("could not start otbr-agent: %s", err)
return nil, err
}
time.Sleep(300 * time.Millisecond)

Choose a reason for hiding this comment

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

high

Using a fixed time.Sleep to wait for otbr-agent to start can be unreliable and may lead to race conditions, as the startup time can vary based on system load. A more robust approach would be to check if otbr-agent is ready before launching ot-ctl. For example, you could poll for the creation of the ot-ctl control socket or parse the otbr-agent logs for a specific message indicating it's ready.

It also requires a small modification to platform-rfsim.c since
it uses the otIp6AddressFromString() but the OpenThread NCP does
not link to the Ip6 library.
@EskoDijk
Copy link
Contributor

@francoismichel Thanks for submitting! I'd like to try it out soon. Some first thoughts:

  • Are multiple OTBRs supported, or would these get in eachother's way?
    • in the log files used (/tmp/otbr-agent-out etc) it looks like only a single one could run. This could be easily updated to use a node-specific log file name in the ./tmp/ folder together with other log files.
    • for the first use cases for this, it would also be ok if we limit to only 1 OTBR - where this OTBR equals the host machine where the simulation is running on.
    • this can e.g. be used to have a real OTBR in a cert test topology, using TREL, and being attached to a simulated network of a few nodes.
  • For the first phase it seems ok to have the OTBR build separate, to be done by the user.
  • If I understand correctly: due to the RCP architecture, and time now real-time rather than simulated-time, the serial UART data is not flowing via the OT-RFSIM event communication, therefore it requires the real-time UART support back in. The setup script is then also sent via this UART, as well as node CLI commands.
  • Which thing in otbr-agent requires running as root? Sending ICMPv6 messages comes to mind. I recall I had a similar problem, but when using Docker OTBR in the simulation one could get around that. Docker can be configured as being automatically in the sudo'ers group. Maybe there's a way to avoid running as root?

Finally a meta thought: do we really need OTBR-agent from ot-br-posix? Current "BR" node type is based on OpenThread (core) only and would have nearly everything needed for an OTBR included, if we configure it to use OT-core mDNS/AdvProxy/DiscProxy I think.

  • so the only thing that OTBR-agent offers more is ability to use an external mDNS library and the webGUI, I think?
  • Using OT would also avoid a separate RCP process.
  • It would also allow to run the whole OTBR in simulated (virtual) time.
  • do we see any obstacles to this approach, that the otbr-agent-with-RCP approach doesn't have?
  • one obstacle can be certain platform functions implementation. Certain APIs need a platform implementation taken from / copied from openthread/src/posix/platform such as otPlatInfraIfRecvIcmp6Nd and many related functions.
  • Maybe what I sketch above here is an alternative track to pursue for later.

@EskoDijk
Copy link
Contributor

@francoismichel Maybe if you have some time early next year (or this year), we could have a call to go over the questions I had. These questions are mostly on "could we do things differently" (in order to get certain benefits or simplify setup) or to understand the rationale for certain functions.

Despite these questions being still open, I think the answers would imply that the current PR's approach is still a useful thing to add. Given that, I'll do suggestions also for the current PR code - hopefully still in this year!

@EskoDijk
Copy link
Contributor

@francoismichel Based on the proposed changes here I'd like to propose that I create a separate PR first for adding generic RCP support, which re-introduces the "realtime" UART also. That would also allow running ordinary (e.g. FTD) ot-cli nodes that use the RCP design. This PR is almost done; hopefully in the next few days.

In this new PR/design it's done in such a way that the internal OTNS code is more readable (less code) and the internals of the Dispatcher are not exposed to other classes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants