Skip to content

pkg/lwip/netdev: fix return types in send()#21833

Merged
crasbe merged 2 commits intoRIOT-OS:masterfrom
mguetschow:lwip-netdev-type-fix
Oct 30, 2025
Merged

pkg/lwip/netdev: fix return types in send()#21833
crasbe merged 2 commits intoRIOT-OS:masterfrom
mguetschow:lwip-netdev-type-fix

Conversation

@mguetschow
Copy link
Copy Markdown
Contributor

Contribution description

The netdev API returns int, with negative meaning error conditions. err_t, which is currently used as the type for the return values, is unsigned. This would lead to spurious issues with error conditions.

Testing procedure

  1. sudo ip tuntap add tap0 mode tap user ${USER}; sudo ip link set tap0 up;
  2. LWIP_IPV6=1 make -C examples/networking/coap/gcoap_dtls flash term
  3. (in another terminal, with the correct IPv6 address) coap-client-gnutls "coaps://[fe80::xxxx:xxxx:xxxx:xxxx%tap0]/.well-known/core" -k "secretPSK" -u "Client_identity" (libcoap cli client)

On master this fails with ERR cannot send CoAP pdu and Wireshark shows a DTLS abort package sent by RIOT right after the successfully sent ServerHello. Enabling debug in sock_dtls.c, lwip_sock.c and lwip_netdev.c shows a problem.

With the changes from this PR, everything works as expected.

@mguetschow mguetschow requested a review from miri64 as a code owner October 30, 2025 13:33
@github-actions github-actions bot added Area: network Area: Networking Area: pkg Area: External package ports labels Oct 30, 2025
@mguetschow mguetschow requested review from crasbe and maribu October 30, 2025 13:34
@mguetschow mguetschow added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Oct 30, 2025
@crasbe crasbe added the Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors) label Oct 30, 2025
@crasbe
Copy link
Copy Markdown
Contributor

crasbe commented Oct 30, 2025

According to the documentation, err_t is the right type to use. Why is it defined as unsigned... and where?
https://rtl-lwip.sourceforge.net/sources/err_8h.html#a13

I added some error messages to build/pkg/lwip/src/include/lwip/err.h:

/** Define LWIP_ERR_T in cc.h if you want to use
 *  a different type for your platform (must be signed). */
#ifdef LWIP_ERR_T
typedef LWIP_ERR_T err_t;
+#error Custom Type
#else /* LWIP_ERR_T */
typedef s8_t err_t;
+#error Default s8_t
#endif /* LWIP_ERR_T*/

And this is the result:

buechse@skyleaf:~/RIOTstuff/riot-nrfvendor/RIOT$ LWIP_IPV6=1 make -C examples/networking/coap/gcoap_dtls
make: Entering directory '/home/buechse/RIOTstuff/riot-nrfvendor/RIOT/examples/networking/coap/gcoap_dtls'
using BOARD="native64" as "native" on a 64-bit system
Building application "gcoap_example" for "native64" with CPU "native".

"make" -C /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/pkg/lwip/
"make" -C /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/api -f /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/Makefile.base MODULE=lwip_api
In file included from /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/include/lwip/pbuf.h:42,
                 from /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/include/lwip/netbuf.h:46,
                 from /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/include/lwip/api.h:47,
                 from /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/api/api_lib.c:62:
/home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/include/lwip/err.h:98:2: error: #error Default s8_t
   98 | #error Default s8_t
      |  ^~~~~
make[2]: *** [/home/buechse/RIOTstuff/riot-nrfvendor/RIOT/Makefile.base:157: /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/examples/networking/coap/gcoap_dtls/bin/native64/lwip_api/api_lib.o] Error 1
make[1]: *** [Makefile:31: lwip_api] Error 2
make: *** [/home/buechse/RIOTstuff/riot-nrfvendor/RIOT/examples/networking/coap/gcoap_dtls/../../../../Makefile.include:807: pkg-build] Error 2
make: Leaving directory '/home/buechse/RIOTstuff/riot-nrfvendor/RIOT/examples/networking/coap/gcoap_dtls'

Therefore at some point, s8_t is defined as an unsigned?

@crasbe
Copy link
Copy Markdown
Contributor

crasbe commented Oct 30, 2025

This is very odd:

This is where s8_t is defined:

buechse@skyleaf:~/RIOTstuff/riot-nrfvendor/RIOT$ grep -Rnw . -e "typedef .* s8_t;"
./build/pkg/lwip/src/include/lwip/arch.h:128:typedef int8_t    s8_t;

And the header is actually used:

buechse@skyleaf:~/RIOTstuff/riot-nrfvendor/RIOT$ LWIP_IPV6=1 make -C examples/networking/coap/gcoap_dtls
make: Entering directory '/home/buechse/RIOTstuff/riot-nrfvendor/RIOT/examples/networking/coap/gcoap_dtls'
using BOARD="native64" as "native" on a 64-bit system
Building application "gcoap_example" for "native64" with CPU "native".

"make" -C /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/pkg/lwip/
"make" -C /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/api -f /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/Makefile.base MODULE=lwip_api
In file included from /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/include/lwip/debug.h:40,
                 from /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/include/lwip/opt.h:52,
                 from /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/api/api_lib.c:58:
/home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/include/lwip/arch.h:40:2: error: #error I am included somewhere!
   40 | #error I am included somewhere!
      |  ^~~~~
In file included from /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/include/lwip/pbuf.h:42,
                 from /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/include/lwip/netbuf.h:46,
                 from /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/include/lwip/api.h:47,
                 from /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/api/api_lib.c:62:
/home/buechse/RIOTstuff/riot-nrfvendor/RIOT/build/pkg/lwip/src/include/lwip/err.h:98:2: error: #error Default s8_t
   98 | #error Default s8_t
      |  ^~~~~
make[2]: *** [/home/buechse/RIOTstuff/riot-nrfvendor/RIOT/Makefile.base:157: /home/buechse/RIOTstuff/riot-nrfvendor/RIOT/examples/networking/coap/gcoap_dtls/bin/native64/lwip_api/api_lib.o] Error 1
make[1]: *** [Makefile:31: lwip_api] Error 2
make: *** [/home/buechse/RIOTstuff/riot-nrfvendor/RIOT/examples/networking/coap/gcoap_dtls/../../../../Makefile.include:807: pkg-build] Error 2
make: Leaving directory '/home/buechse/RIOTstuff/riot-nrfvendor/RIOT/examples/networking/coap/gcoap_dtls'

@mguetschow
Copy link
Copy Markdown
Contributor Author

According to the documentation, err_t is the right type to use. Why is it defined as unsigned... and where?
rtl-lwip.sourceforge.net/sources/err_8h.html#a13

Sure it's the right return type for the lwip callbacks, but not the right return type for the netdev callbacks.

@crasbe
Copy link
Copy Markdown
Contributor

crasbe commented Oct 30, 2025

According to the documentation, err_t is the right type to use. Why is it defined as unsigned... and where?
rtl-lwip.sourceforge.net/sources/err_8h.html#a13

Sure it's the right return type for the lwip callbacks, but not the right return type for the netdev callbacks.

Not sure I follow, the return type of the function is literally err_t. The proposed change shouldn't make any difference:

#if (IS_USED(MODULE_NETDEV_NEW_API))
static err_t _common_link_output(struct netif *netif, netdev_t *netdev, iolist_t *iolist)
{
lwip_netif_dev_acquire(netif);
err_t res;
if (is_netdev_legacy_api(netdev)) {
res = (netdev->driver->send(netdev, iolist) > 0) ? ERR_OK : ERR_BUF;
lwip_netif_dev_release(netif);
return res;
}

Same for the old API:

#else /* only old API */
static err_t _common_link_output(struct netif *netif, netdev_t *netdev, iolist_t *iolist)
{
lwip_netif_dev_acquire(netif);
err_t res = (netdev->driver->send(netdev, iolist) > 0) ? ERR_OK : ERR_BUF;
lwip_netif_dev_release(netif);
return res;
}

@crasbe
Copy link
Copy Markdown
Contributor

crasbe commented Oct 30, 2025

Oh... The only reason an issue might occur is if the return value of send is bigger than 127, leading to a wrap-around. Might that be the case?

@mguetschow
Copy link
Copy Markdown
Contributor Author

According to the documentation, err_t is the right type to use. Why is it defined as unsigned... and where?
rtl-lwip.sourceforge.net/sources/err_8h.html#a13

Sure it's the right return type for the lwip callbacks, but not the right return type for the netdev callbacks.

Not sure I follow, the return type of the function is literally err_t.

Yes, _common_link_output has err_t as return type, but netdev->driver->send has int as return type. When storing the return of the send function in res (which is currently err_t), it will loose negative values and wrap around for larger values.

@crasbe
Copy link
Copy Markdown
Contributor

crasbe commented Oct 30, 2025

According to the documentation, err_t is the right type to use. Why is it defined as unsigned... and where?
rtl-lwip.sourceforge.net/sources/err_8h.html#a13

Sure it's the right return type for the lwip callbacks, but not the right return type for the netdev callbacks.

Not sure I follow, the return type of the function is literally err_t.

Yes, _common_link_output has err_t as return type, but netdev->driver->send has int as return type. When storing the return of the send function in res (which is currently err_t), it will loose negative values and wrap around for larger values.

Yes, that's the same conclusion I had after writing the first reply:

Oh... The only reason an issue might occur is if the return value of send is bigger than 127, leading to a wrap-around. Might that be the case?


Nevertheless the err_t is not unsigned as you wrote initially 😅

It's still odd that the return value of the function is implicitly cast to int8_t before checking if it's bigger than 0, but it is what it is I guess.

The function for the old API has to be changed too.

@mguetschow
Copy link
Copy Markdown
Contributor Author

Nevertheless the err_t is not unsigned as you wrote initially 😅

I'll give you that :)

The function for the old API has to be changed too.

Good point, done now.

Copy link
Copy Markdown
Contributor

@crasbe crasbe left a comment

Choose a reason for hiding this comment

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

If @maribu is also happy, you can squash :)

@maribu
Copy link
Copy Markdown
Member

maribu commented Oct 30, 2025

Never wait for me with regard to squashing 😉

@mguetschow mguetschow force-pushed the lwip-netdev-type-fix branch from e53a6d6 to a045acb Compare October 30, 2025 14:38
@riot-ci
Copy link
Copy Markdown

riot-ci commented Oct 30, 2025

Murdock results

✔️ PASSED

a045acb pkg/lwip/netdev: fix return types in send()

Success Failures Total Runtime
10560 0 10560 11m:39s

Artifacts

@crasbe crasbe added this pull request to the merge queue Oct 30, 2025
Merged via the queue into RIOT-OS:master with commit 4b55480 Oct 30, 2025
25 checks passed
@mguetschow
Copy link
Copy Markdown
Contributor Author

Thanks everyone for your reviews! 🤗

@mguetschow mguetschow deleted the lwip-netdev-type-fix branch November 3, 2025 08:28
@benpicco benpicco added this to the Release 2025.10 milestone Dec 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: network Area: Networking Area: pkg Area: External package ports CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants