Skip to content

drivers/slipmux: Add to RIOT #21418

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

Merged
merged 1 commit into from
Jul 7, 2025
Merged

Conversation

Teufelchen1
Copy link
Contributor

@Teufelchen1 Teufelchen1 commented Apr 17, 2025

Contribution description

Hey 🪼,

this adds an (incomplete) implementation of Slipmux to RIOT. It extends the current slip driver and is backwards compatible.

The second commit adds an usage example, where the shell commands are exposed to coap and through it to slipmux. That commit will be dropped prio merging.

It is incomplete as frame abort is missing.

Testing procedure

Grab your favorite slipmux tool and connect RIOTs stdio to it. Make sure to build RIOT with USEMODULE += slipdev_stdio and optionally set CFLAGS += "-DCONFIG_NANOCOAP_BLOCK_SIZE_EXP_MAX=10" to be able to handle coap packets that are longer than 64 bytes. See the changes I made to the default example.

Feedback wanted: coap server

My current approach spawns a new thread with a nanocoap server to handle the incoming and outgoing configuration frames. This might cause issues when another coap server is in use (one that is connected to the network instead of just the uart). For example, the NANOCOAP_RESOURCE(..)s will be shared between them.

I also experimented with sending the coap messages with a fake ip6+udp header into gnrc. One can then only run one single coap server (and it is no longer bound to nanocoap) who listens for such packets. The outgoing way is a bit more annoying, here a new dummy interface must be created, to which gnrc can direct those fake ip6+udp headers. Very similar to how it is already done with the current slip implementation.

What alternative approaches do you see? Any idea is appreciated.

@Teufelchen1 Teufelchen1 requested a review from miri64 as a code owner April 17, 2025 09:45
@github-actions github-actions bot added Area: drivers Area: Device drivers Area: sys Area: System Area: examples Area: Example Applications labels Apr 17, 2025
@miri64 miri64 requested a review from benpicco April 17, 2025 10:23
@benpicco
Copy link
Contributor

I don't quite understand the purpose if this.
When you already have a SLIP connection, you might as well do CoAP over UDP.

Sure, with this approach you can omit the IP and UDP header, but is that really worth the hassle?

@Teufelchen1
Copy link
Contributor Author

From the byte-wise overhead, it is not that much of a difference yes. I don't think it is that much of a hassle tho. Beside, as soon as you use IP+UDP you have endpoints in a network and those need to be configured (I need to know the IP I want to talk to). Doing slipmux configuration frames you have a guaranteed point to point channel with no configuration nor hassle ;)

@chrysn
Copy link
Member

chrysn commented Apr 17, 2025

I can't comment on the code right now, but maybe for the open aspects:

  • "Why?" Teufelchen already mentioned the configuration aspect; without precise knowledge of the concrete application (and a point of using SLIP is that this knowledge is not required) the host may not even know that it reaches the board this way. In particular,

    • this connection may be more trusted than a network connection: While I generally advocate no-TOFU / all-links-are-untrusted / cryptographically-secure-or-bust policy, TOFU policies do have their places, and TOFU over a link that is known not to be networked is preferable over a link where one would need to apply heuristics to know whether a peer is local. (Like, sure, might do link-local-only, but maybe there's NDP-proxying involved?).
    • This might be a good fit also for devices that don't do network at all, or not on that interface. On those, actual SLIP packets would be blackhole'd, but CoAP provides an interface that is easier to automate than shell interactions, and moreover easily ported to devices where no serial line but network is available.
    • Where we do have Ethernet-over-USB, I think that a dedicated CoAP-over-USB protocol would be preferred. However, I'm not aware of any such attempt, and even then, there are relatively new devices around still that still run through the moral equivalent of an FTDI chip.
  • "Which CoAP server to run?" I think we should run the same single CoAP server on all transports as a matter of simplicity; where a UART connection is more trusted than a network peer, which transport it is should be discoverable by inspecting the socket-ish thing it comes in over. I don't have an opinion yet on which implementation strategy I'd advocate for.

@Teufelchen1 Teufelchen1 changed the title DRAFT: Add slipmux to RIOT Add slipmux to RIOT May 16, 2025
@crasbe crasbe added Type: new feature The issue requests / The PR implemements a new feature for RIOT Discussion: RFC The issue/PR is used as a discussion starting point about the item of the issue/PR Community: help wanted The contributors require help from other members of the community labels May 16, 2025
@miri64 miri64 changed the title Add slipmux to RIOT slipmux: Add to RIOT May 16, 2025
miri64
miri64 previously requested changes May 16, 2025
Copy link
Member

@miri64 miri64 left a comment

Choose a reason for hiding this comment

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

First round of review.

#if IS_USED(MODULE_SLIPDEV_STDIO)
chunk_ringbuf_t rb_config; /**< Ringbuffer to store received CONFIG frames.*/
uint8_t rxmem_config[CONFIG_SLIPDEV_BUFSIZE]; /**< memory used by RX buffer */
kernel_pid_t coap_server_pid;
Copy link
Member

@miri64 miri64 May 16, 2025

Choose a reason for hiding this comment

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

Suggested change
kernel_pid_t coap_server_pid;
kernel_pid_t coap_server_pid; /**< The PID of the CoAP server */

Wrt to future extensions, maybe that can be more generalized so that, e.g., also a gcoap or unicoap queue could be put here, if they exist to not have to provide an extra server then.

Again, mainly a comment for future improvements, this does not have to be addressed in this PR.

(edit: but would also address @chrysn's comment)

@crasbe crasbe added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label May 16, 2025
@riot-ci
Copy link

riot-ci commented May 16, 2025

Murdock results

✔️ PASSED

166ffcb driver/slip: Add slipmux frames

Success Failures Total Runtime
10504 0 10504 20m:50s

Artifacts

@crasbe crasbe changed the title slipmux: Add to RIOT drivers/slipmux: Add to RIOT May 16, 2025
void slipdev_setup(slipdev_t *dev, const slipdev_params_t *params, uint8_t index)
{
#if IS_USED(MODULE_SLIPDEV_STDIO)
Copy link
Contributor

Choose a reason for hiding this comment

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

Please use a separate pseudo-module for this, normal slipdev stdio multiplex should not require a CoAP server.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are repeating yourself ;)

I am not sure yet if "just a separate pseudo-module" is the correct call. What do you think about making the driver "slipmux" with no functionality (not even slip). One can then add the pseudomodules slipmux_ip, slipmux_configuration and slipmux_stdio which add their respective functions. This way, one could use configuration frames (-> coap) without needing IP/gnrc as a dependency.

Copy link
Contributor

Choose a reason for hiding this comment

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

If you desire to do so go ahead.
I just want to avoid bloating the slipdev driver for simple device to device links that might have a multiplexed debug interface for convenience.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That will be much more work than I thought. I'll move that task to a followup PR. For this one, I adopted your wish and guarded it with its own pseudomodule.
In addition, I changed the way the parsing is guarded with the #if IS_USED(..). Before: The whole case of stdio was removed if the module was not selected. Now: Only the data processing is removed, the cases for parsing remain. (Same for configuration frames). Reason is simple: If you remove the parsing + states, any byte received will be treated as a IP packet and then dropped by gnrc as "undefined packet type". This no-longer the case, instead, receiving a diagnostic/configuration frame without their pseudomodules enabled, they will be silently ignored.

@github-actions github-actions bot added the Area: build system Area: Build system label Jun 11, 2025
@github-actions github-actions bot added the Area: tests Area: tests and testing framework label Jun 17, 2025
@Teufelchen1
Copy link
Contributor Author

@benpicco I think this is ready for another review. If that is positive, I will drop the shell/example related commit and squash the others.

@Teufelchen1
Copy link
Contributor Author

@benpicco ping :)

@mguetschow
Copy link
Contributor

Some high-level comments from my side:

  • incomplete implementation

this adds an (incomplete) implementation of Slipmux to RIOT. It extends the current slip driver and is backwards compatible. (...) It is incomplete as frame abort is missing.

Isn't it also missing diagnostic transfer? Just had a quick glance at the code, but could only find references to configuration frames.

  • documentation: before merging this should definitely get documentation, probably on the defined pseudomodule slipdev_config. Not sure if this may even warrant its own doc.md somewhere? It should definitely mention slipmux and clearly list what is currently supported. Also some notes on the implementation (currently spawning an additional thread, sharing NANOCOAP_RESOURCE with any other nanocoap server) should be in the documentation.

  • coap server with different backends: I would hope unicoap could help with that. I'm pretty sure it supports defining resources only for a subset of driver backends (e.g. just slipmux and not udp), which means that a single unicoap server could be used for several backends. Maybe @carl-tud has some more comments on this?

  • nanocoap/nanocoap_sock: could you maybe explain in a bit more detail how this actually works right now? I see the "server thread" just receives chunks from the ringbuffer, tries to parse and answer them (using nanocoap). Every chunk is supposed to be a CoAP packet as received within slipdev, right? Where do the NANOCOAP_RESOURCEs (which are part of nanocoap_sock afaict) actually come into play?

@Teufelchen1
Copy link
Contributor Author

Teufelchen1 commented Jun 30, 2025

Isn't it also missing diagnostic transfer? Just had a quick glance at the code, but could only find references to configuration frames.

That's because those were already implemented in RIOT and are just called stdio via slip. Blame @miri64 for that :p If you enable this, every printf() will be send slipmux encoded.

[..needs doc...] Not sure if this may even warrant its own doc.md somewhere?

Eventually, yes. There is still work to be done on the coap-handling/server side. If I can get away with minimal documentation this time, I would appreciate. I fear it will be duplicated work if I re-implement it later (for example with unicoap). Plus, as mentioned in a comment with benpicco, the handling of the pseudomodule is not ideal in the moment. For example, it is not possible to enable configuration transfer without packet transfer. And even if you could, nanocoap depends on udp, which means you would still pull in a lot of wasted code & bytes. Both issues are scheduled for follow-up work.

I see the "server thread" just receives chunks from the ringbuffer, tries to parse and answer them (using nanocoap). Every chunk is supposed to be a CoAP packet as received within slipdev, right?

That is correct.

Where do the NANOCOAP_RESOURCEs (which are part of nanocoap_sock afaict) actually come into play?

That's nanocoap specific, I do not interact with them myself (beside the shell stuff but thats just an example of what one could do and will be dropped before merging). Afaik it is just a XFA defining names and callbacks. The nanocoap server just magically knows that XFA and looks up the endpoints on the fly.

@mguetschow
Copy link
Contributor

If I can get away with minimal documentation this time, I would appreciate. (...) Both issues are scheduled for follow-up work.

Okay, I get your point and also don't want you to spend time inefficiently. However, I would appreciate some very short documentation for now (probably in the pseudomodule file then) just mentioning:

  • this is slipmux configuration with a link to the draft
  • @warning stating this is WIP / experimental work
  • briefly list the current shortcomings you mention above (or, alternatively link to this PR)

Just trying to safeguard against follow-up work being stalled at some point, where we would end up with yet another feature not documented at all.

The nanocoap server just magically knows that XFA and looks up the endpoints on the fly.

Ah, so coap_handle_req (https://github.com/RIOT-OS/RIOT/pull/21418/files#diff-b7bda8876512e2948e36a07d7926bd6d15c4b7bb1f043e832daaae113aadbb32R462) is actually a nanocoap_sock function making use of the XFA? I somehow assumed coap_*-prefixed functions where only part of the nanocoap parser. What a mess :'D

@Teufelchen1
Copy link
Contributor Author

Just trying to safeguard against follow-up work being stalled at some point, where we would end up with yet another feature not documented at all.

Yes, that's your job :p Speaking of non documentation, I am either overlooking something or slip has already been poorly documented :/ Either way, I roughly agree with you but there are remarks.

[Add] very short documentation for now (probably in the pseudomodule file then) just mentioning:

I opted against that and followed the example of the already existing documentation for slipdev_stdio.

this is slipmux configuration with a link to the draft

Yes, I added the links. Fun fact, I also fixed the broken link of SLIP dev itself :D

@warning stating this is WIP / experimental work

Not sure tbh. So far, it not claimed anywhere this is "rfc conform", as such, it can not be incomplete. It might be considered WIP? But what would that even mean for a pseudomodule that already works exactly like one would imagine? (still, you could persuade me to add that)

briefly list the current shortcomings you mention above (or, alternatively link to this PR)

There are no shortcomings for the user as "slipmux conform" is not claimed. Things would look different if one would enable a slipmux module but you don't. e.g. If one just uses slip (without stdio/configuration), the code is conform to SLIP rfc but not to Slipmux draft. This mess is part of the idea I outlined a bit before:

In future work, one will have to flip it on it's head. There will be one module called 'slipmux' that will enable all three frame types and be slipmux-draft-conform. There will be three sub-modules each guarding one of the three frame types, enabling you to only have partial slipmux. There will be a module called slip that is a alias for the submodule that enables the packet transfer and that will be slip-rfc conform.
(Just how I imagine it, nothing concrete yet)

@mguetschow
Copy link
Contributor

Thanks for adding some documentation!

There are no shortcomings for the user as "slipmux conform" is not claimed.

Maybe not shortcomings, but some things users should be aware of when using your module: there is an extra thread spawned, incoming coap requests over the wire are handled according to any NANOCOAP_RESOURCEs present in the binary. Basically a very short "how to use"/"how is this useful".

In future work, one will have to flip it on it's head. There will be one module called 'slipmux' that will enable all three frame types and be slipmux-draft-conform.

Sounds reasonable to me. Then why not briefly mention this in the documentation as well, so that users are aware this is probably only a temporary API (as in "module names" etc.)

Copy link
Member

@miri64 miri64 left a comment

Choose a reason for hiding this comment

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

Did not test this and did not look into the whole state machine, but I trust @benpicco's review on that (though please reply if @Teufelchen1 answers were to your liking). So from my side, we can go, if @mguetschow does not find anything else :-)

@mguetschow
Copy link
Contributor

Looks good from my side now, haven't tested it though. Do you have suggestions for slipmux implementations to test this with?

@Teufelchen1
Copy link
Contributor Author

I have been testing against this one: https://crates.io/crates/slipmux (disclaimer, I am the author).
There really aren't other viable implementations out there. There are debris by lobaro but I didn't bothered understanding that mess code base (I don't think it is intended as a library but just their own utility for their purpose which is fine). I also asked on the core-wg mailing list but no one knows about working implementations.

tl;dr: I've been actively using this for weeks now in my SeCrEt PrOjEcT. Works well for config+diagnostic. Never used networking but I didn't touch that code meaningful so I am confident that didn't degrade.

@github-actions github-actions bot removed Area: sys Area: System Area: examples Area: Example Applications labels Jul 2, 2025
@carl-tud
Copy link
Contributor

carl-tud commented Jul 2, 2025

@mguetschow

  • coap server with different backends: I would hope unicoap could help with that. I'm pretty sure it supports defining resources only for a subset of driver backends (e.g. just slipmux and not udp), which means that a single unicoap server could be used for several backends. Maybe @carl-tud has some more comments on this?

a, The notion of a CoAP server is loosely defined. A driver can open any number of sockets or for that matter 'concepts' similar to sockets. Each driver then forwards all packets to 'the unicoap server' after having removed the outer frame (UDP headers, etc). Information on what transport has been used is retained and optionally used to restrict resources to certain transports. I you really want to, you can handle this manually. There is an API for that. Right, now you can only have one 'unicoap server`.

b, Is this also what Lasse wants?: multiple ports and thus multiple sockets? I yes, I talked to Lasse off channel and we agreed on solution I have yet to implement. But, adding this to unicoap is almost trivial.

@Teufelchen1
Copy link
Contributor Author

Awesome, unicoap will fit perfectly into this.

Copy link
Contributor

@mguetschow mguetschow left a comment

Choose a reason for hiding this comment

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

Alright, if there's no other tools available, interoperability tests are somehow mood. Trusting your testing therefore.

@Teufelchen1
Copy link
Contributor Author

@miri64 you are still requesting change

@miri64 miri64 dismissed their stale review July 4, 2025 15:25

Nope, sorry. See #21418 (review)

@Teufelchen1 Teufelchen1 added this pull request to the merge queue Jul 7, 2025
Merged via the queue into RIOT-OS:master with commit b2406da Jul 7, 2025
27 checks passed
@Teufelchen1 Teufelchen1 added this to the Release 2025.07 milestone Jul 14, 2025
Comment on lines +444 to +445
while (crb_get_chunk_size(&dev->rb_config, &len)) {
crb_consume_chunk(&dev->rb_config, buf, len);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mguetschow @benpicco I think we had brain damage when we reviewed & merged this. It's an obvious buffer overflow waiting to happen.

if (len > sizeof(buf)) { goto Fail }

Copy link
Contributor

Choose a reason for hiding this comment

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

fun 👀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: build system Area: Build system Area: drivers Area: Device drivers Area: tests Area: tests and testing framework CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Community: help wanted The contributors require help from other members of the community Discussion: RFC The issue/PR is used as a discussion starting point about the item of the issue/PR Type: new feature The issue requests / The PR implemements a new feature for RIOT
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants