Skip to content

examples/lwip_ipv4: add example for LWIP IPv4 client/server #21519

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ Here is a quick overview of the examples available in the RIOT:
| [sniffer](./networking/misc/sniffer/README.md) | This application is built to run together with the script `./tools/sniffer.py` as a sniffer for (wireless) data traffic. |
| [benckmark_udp](./networking/misc/benchmark_udp/README.md) | This example uses the `benchmark_udp` module to create a stress-test for the RIOT network stack. |
| [sock_tcp_echo](./networking/misc/sock_tcp_echo/README.md) | This is a simple TCP echo server / client that uses the SOCK API. |
| [lwip_ipv4](./networking/misc/lwip_ipv4/README.md) | This is a simple UDP client / server using LWIP for IPv4. |


## Advanced Examples

Expand Down
34 changes: 34 additions & 0 deletions examples/networking/misc/lwip_ipv4/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Set the name of your application:
APPLICATION = lwip_ipv4

# If no BOARD is found in the environment, use this default:
BOARD ?= native

# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../../../../

# Uncomment this to enable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP = 1

# Change this to 0 to show compiler invocation lines by default:
QUIET ?= 1

# Modules to include:
USEMODULE += shell

USEMODULE += netdev_default

#enables LWIP IPv4 stack
USEMODULE += ipv4_addr
USEMODULE += lwip_arp
USEMODULE += lwip_ipv4

#enables UDP LWIP sockets
USEMODULE += sock_udp

#nables ifconfig command, which allows IPv4 address configuration
USEMODULE += shell_cmds_default

include $(RIOTBASE)/Makefile.include
19 changes: 19 additions & 0 deletions examples/networking/misc/lwip_ipv4/Makefile.ci
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
BOARD_INSUFFICIENT_MEMORY := \
bluepill-stm32f030c8 \
i-nucleo-lrwan1 \
nucleo-c031c6 \
nucleo-f030r8 \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-l011k4 \
nucleo-l031k6 \
nucleo-l053r8 \
samd10-xmini \
slstk3400a \
stk3200 \
stm32f030f4-demo \
stm32f0discovery \
stm32g0316-disco \
stm32l0538-disco \
weact-g030f6 \
#
116 changes: 116 additions & 0 deletions examples/networking/misc/lwip_ipv4/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# LWIP IPv4 client and server example

This example shows how to send and receive IPv4 UDP datagrams using LWIP
network library.

# Shell commands

The following commands are available:
- `ifconfig`: allows manual IPv4 address configuration.
- `server`: run simple server which receives UDP datagrams.
- `client`: sends UDP datagram to given IPv4 address.

# Usage on BOARD=native

This instruction, using native board, utilize tap interface and allows
communication between RIOT system and host Linux.

In the following sections we describe how to:
- configure tap interface using RIOT `tapsetup` tool.
- send data from the Linux host to the RIOT.
- send data from the RIOT to the Linux host.

## Network configuration

Create `tap0` interface and `tapbr0` using RIOT `tapsetup` tool.
Commands should be executed on the Linux host:

```
$sudo ./dist/tools/tapsetup/tapsetup -c 1
$sudo ip addr add 192.168.100.100/24 dev tapbr0
```

Compile and manually configure IPv4 address on the RIOT.

```
$make all
$./bin/native64/lwip_ipv4.elf tap0
RIOT native interrupts/signals initialized.
TZ not set, setting UTC
RIOT native64 board initialized.
RIOT native hardware initialization complete.

main(): This is RIOT! (Version: 2024.04-devel-2705-g39b6f-examples-lwip_ipv4)

> ifconfig add ET0 192.168.100.11/24
ifconfig add ET0 192.168.100.11/24
> ifconfig
ifconfig
Iface ET0 HWaddr: 8e:9d:7d:a1:fd:cd Link: up State: up
Link type: wired
inet addr: 192.168.100.11 mask: 255.255.255.0 gw: 0.0.0.0
>
```

Check connectivity from the Linux host machine to the RIOT:

```
$ping 192.168.100.11
PING 192.168.100.11 (192.168.100.11) 56(84) bytes of data.
64 bytes from 192.168.100.11: icmp_seq=1 ttl=255 time=0.240 ms
64 bytes from 192.168.100.11: icmp_seq=2 ttl=255 time=0.210 ms
...
```

## RIOT LWIP IPv4 server

Run server on the RIOT using command:

```
> server 4444
server 4444
Server started.
```

Send UDP datagram from the Linux host using `nc` tool:

```
$ nc -u 192.168.100.11 4444
Msg from Linux
Msg from Linux
```

Observe results on the RIOT:

```
> server 4444
server 4444
Server started.
Received 15 bytes - Msg from Linux

>
```

## RIOT LWIP IPv4 client

Run server on the Linux host using `nc` tool:

```
$ nc -ul -p 4444
```

Send UDP datagram from the RIOT:

```
> client 192.168.100.100 4444 Msg_from_RIOT
client 192.168.100.100 4444 Msg_from_RIOT
>
```

Observe result on the Linux host:

```
$nc -ul -p 4444
Msg_from_RIOT

```
174 changes: 174 additions & 0 deletions examples/networking/misc/lwip_ipv4/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Copyright (C) 2025 Krzysztof Cabaj <[email protected]>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup examples
* @{
*
* @file
* @brief lwip_ipv4 - sample application for demonstrating basic LWIP
* IPv4 client/server functions.
*
* @author Krzysztof Cabaj <[email protected]>
*
* @}
*/
#include "stdio.h"
#include "stdlib.h"
#include "shell.h"
#include "net/sock/udp.h"
#include <arpa/inet.h>
#include "thread.h"
#include "mutex.h"

#define CLIENT_TIMEOUT 3 /* in seconds */

static char server_stack[THREAD_STACKSIZE_DEFAULT];
static mutex_t server_mutex = MUTEX_INIT;
static int server_running = 0;
static uint16_t server_port;

static int _client_cmd(int argc, char **argv)
{
uint32_t dest_ip;

if (argc < 4) {
printf("usage: %s <destination IP> <port> <text>\n", argv[0]);
return -1;
}

if (inet_pton(AF_INET, argv[1], &dest_ip) != 1) {
printf("\"%s\" - is not a valid IPv4 address!\n", argv[1]);
return -1;
}

uint16_t port = atoi(argv[2]);
size_t data_len = strlen(argv[3]);
sock_udp_t sock;
sock_udp_ep_t remote = { .addr = { .ipv4_u32 = dest_ip },
.family = AF_INET,
.port = port };
int result;
char buffer[64];

if ((result = sock_udp_create(&sock, NULL, &remote, SOCK_FLAGS_REUSE_EP)) != 0) {
printf("Error sock_udp_create - result %d!\n", result);
return -1;
}

if (data_len != (size_t)sock_udp_send(&sock, argv[3], data_len, NULL)) {
printf("Error sock_udp_send! Error code %d.\n", (int)data_len);
sock_udp_close(&sock);
return -1;
}

if ((result = sock_udp_recv(&sock, buffer, sizeof(buffer) - 1, CLIENT_TIMEOUT * US_PER_SEC, &remote)) < 0) {

Check warning on line 70 in examples/networking/misc/lwip_ipv4/main.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
printf("Sock_udp_recv error! Error code %d\n", result);
sock_udp_close(&sock);
return -1;
}

buffer[result] = 0;
printf("Received %d bytes - %s\n", result, buffer);

sock_udp_close(&sock);

return 0;
}

SHELL_COMMAND(client, "Send UDP datagram", _client_cmd);

void *server_thread(void *arg)
{
(void)arg;
sock_udp_t sock;
sock_udp_ep_t local = { .family = AF_INET,
.port = server_port };
sock_udp_ep_t remote;
int result;
int error;
char buffer[64];

if (sock_udp_create(&sock, &local, NULL, SOCK_FLAGS_REUSE_EP) < 0) {
printf("Sock_udp_create error!\n");
Copy link
Contributor

Choose a reason for hiding this comment

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

You want to print the error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I want to print error.
Your comment concerns wrong condition was ! = 0 is < 0?

printf("Server stopped.\n");

mutex_lock(&server_mutex);
server_running = 0;
mutex_unlock(&server_mutex);
Comment on lines +101 to +103
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you need that for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My idea is that if due to some errors this thread ends - we could start server once again.
I tested this situation with similar code after sock_udp_recv function (lines 98-105). When RIOT receives UDP datagram longer than 64 bytes (buffer size) it ends with error, but I could restart server and later everything works well.

Copy link
Contributor

Choose a reason for hiding this comment

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

But why the need for a mutex?


return NULL;
}

printf("Server started.\n");

while(1) {

Check warning on line 110 in examples/networking/misc/lwip_ipv4/main.c

View workflow job for this annotation

GitHub Actions / static-tests

keyword 'while' not followed by a single space
if ((result = sock_udp_recv(&sock, buffer, sizeof(buffer) - 1, SOCK_NO_TIMEOUT, &remote)) < 0) {

Check warning on line 111 in examples/networking/misc/lwip_ipv4/main.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
printf("Sock_udp_recv error! Error code %d.\n", result);
printf("Server stopped.\n");

sock_udp_close(&sock);

mutex_lock(&server_mutex);
server_running = 0;
mutex_unlock(&server_mutex);

return NULL;
}

buffer[result] = 0;
printf("Received %d bytes - %s\n", result, buffer);

if ( (error = sock_udp_send(&sock, buffer, result, &remote)) != result) {
printf("Sock_udp_send error! Error code %d.\n", error);
}
}

sock_udp_close(&sock);

return NULL;
}

static int _server_cmd(int argc, char **argv)
{
int is_running;
if (argc < 2) {
printf("usage: %s <port>\n", argv[0]);
return -1;
}

mutex_lock(&server_mutex);
is_running = server_running;
mutex_unlock(&server_mutex);

if(is_running == 1) {

Check warning on line 149 in examples/networking/misc/lwip_ipv4/main.c

View workflow job for this annotation

GitHub Actions / static-tests

keyword 'if' not followed by a single space
printf("Server already running!\n");
return -1;
}

server_port = atoi(argv[1]);

mutex_lock(&server_mutex);
server_running = 1;
mutex_unlock(&server_mutex);

thread_create(server_stack, sizeof(server_stack), THREAD_PRIORITY_MAIN - 1, 0, server_thread, NULL, "server");

Check warning on line 160 in examples/networking/misc/lwip_ipv4/main.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters

return -1;
}

SHELL_COMMAND(server, "Starts server which receives UDP datagrams", _server_cmd);
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be more convenient for testing if the server did auto-start.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would prefer do not start server automatically.
This is (1) example, (2) user can from CLI start it on various port.


int main(void)
{
char line_buf[SHELL_DEFAULT_BUFSIZE];

shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);

return 0;
}
Loading