Skip to content

Commit 10670b7

Browse files
fengming-yehenrikbrixandersen
authored andcommitted
net: zperf: multicast support on multi interfaces
Zperf upload multicast always use default interface. Zperf download multicast cannot receive packets from other than 224.0.0.1 which is default multicast group. Add zperf upload/download option -I <interface name> for multicast. So that user can select interface for multicast. Add join multicast group for zperf download. Use the "device list" command to get the interface name as follows: "- ua (READY)" #uAP interface name "- ml (READY)" #STA interface name Multicast traffic commands: zperf udp upload -a -I ua 224.0.0.2 5001 10 1470 1M zperf udp download -I ua 5001 224.0.0.3 Signed-off-by: Fengming Ye <[email protected]>
1 parent 8c19142 commit 10670b7

File tree

4 files changed

+196
-2
lines changed

4 files changed

+196
-2
lines changed

include/zephyr/net/zperf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define ZEPHYR_INCLUDE_NET_ZPERF_H_
1919

2020
#include <zephyr/net/net_ip.h>
21+
#include <zephyr/net/socket.h>
2122

2223
#ifdef __cplusplus
2324
extern "C" {
@@ -34,6 +35,7 @@ struct zperf_upload_params {
3435
uint32_t duration_ms;
3536
uint32_t rate_kbps;
3637
uint16_t packet_size;
38+
char if_name[IFNAMSIZ];
3739
struct {
3840
uint8_t tos;
3941
int tcp_nodelay;
@@ -44,6 +46,7 @@ struct zperf_upload_params {
4446
struct zperf_download_params {
4547
uint16_t port;
4648
struct sockaddr addr;
49+
char if_name[IFNAMSIZ];
4750
};
4851

4952
struct zperf_results {

subsys/net/lib/zperf/zperf_shell.c

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,53 @@ static void udp_session_cb(enum zperf_status status,
368368
}
369369
}
370370

371+
/*
372+
* parse download options with '-'
373+
* return < 0 if parse error
374+
* return 0 if no '-' options
375+
* return > 0 num of argc we parsed
376+
* and following parse starts from this num
377+
*/
378+
static int shell_cmd_download(const struct shell *sh, size_t argc,
379+
char *argv[],
380+
struct zperf_download_params *param)
381+
{
382+
int opt_cnt = 0;
383+
size_t i;
384+
385+
for (i = 1; i < argc; ++i) {
386+
if (*argv[i] != '-') {
387+
break;
388+
}
389+
390+
switch (argv[i][1]) {
391+
case 'I':
392+
/*
393+
* IFNAMSIZ by default CONFIG_NET_INTERFACE_NAME_LEN
394+
* is at least 1 so no overflow risk here
395+
*/
396+
i++;
397+
if (i >= argc) {
398+
shell_fprintf(sh, SHELL_WARNING,
399+
"-I <interface name>\n");
400+
return -ENOEXEC;
401+
}
402+
(void)memset(param->if_name, 0x0, IFNAMSIZ);
403+
strncpy(param->if_name, argv[i], IFNAMSIZ - 1);
404+
405+
opt_cnt += 2;
406+
break;
407+
408+
default:
409+
shell_fprintf(sh, SHELL_WARNING,
410+
"Unrecognized argument: %s\n", argv[i]);
411+
return -ENOEXEC;
412+
}
413+
}
414+
415+
return opt_cnt;
416+
}
417+
371418
static int cmd_udp_download_stop(const struct shell *sh, size_t argc,
372419
char *argv[])
373420
{
@@ -390,8 +437,16 @@ static int cmd_udp_download(const struct shell *sh, size_t argc,
390437
if (IS_ENABLED(CONFIG_NET_UDP)) {
391438
struct zperf_download_params param = { 0 };
392439
int ret;
440+
int start;
441+
442+
start = shell_cmd_download(sh, argc, argv, &param);
443+
if (start < 0) {
444+
shell_fprintf(sh, SHELL_WARNING,
445+
"Unable to parse option.\n");
446+
return -ENOEXEC;
447+
}
393448

394-
ret = zperf_bind_host(sh, argc, argv, &param);
449+
ret = zperf_bind_host(sh, argc - start, &argv[start], &param);
395450
if (ret < 0) {
396451
shell_fprintf(sh, SHELL_WARNING,
397452
"Unable to bind host.\n");
@@ -801,6 +856,19 @@ static int shell_cmd_upload(const struct shell *sh, size_t argc,
801856
break;
802857
#endif /* CONFIG_NET_CONTEXT_PRIORITY */
803858

859+
case 'I':
860+
i++;
861+
if (i >= argc) {
862+
shell_fprintf(sh, SHELL_WARNING,
863+
"-I <interface name>\n");
864+
return -ENOEXEC;
865+
}
866+
(void)memset(param.if_name, 0x0, IFNAMSIZ);
867+
strncpy(param.if_name, argv[i], IFNAMSIZ - 1);
868+
869+
opt_cnt += 2;
870+
break;
871+
804872
default:
805873
shell_fprintf(sh, SHELL_WARNING,
806874
"Unrecognized argument: %s\n", argv[i]);
@@ -987,6 +1055,19 @@ static int shell_cmd_upload2(const struct shell *sh, size_t argc,
9871055
break;
9881056
#endif /* CONFIG_NET_CONTEXT_PRIORITY */
9891057

1058+
case 'I':
1059+
i++;
1060+
if (i >= argc) {
1061+
shell_fprintf(sh, SHELL_WARNING,
1062+
"-I <interface name>\n");
1063+
return -ENOEXEC;
1064+
}
1065+
(void)memset(param.if_name, 0x0, IFNAMSIZ);
1066+
strncpy(param.if_name, argv[i], IFNAMSIZ - 1);
1067+
1068+
opt_cnt += 2;
1069+
break;
1070+
9901071
default:
9911072
shell_fprintf(sh, SHELL_WARNING,
9921073
"Unrecognized argument: %s\n", argv[i]);
@@ -1177,8 +1258,16 @@ static int cmd_tcp_download(const struct shell *sh, size_t argc,
11771258
if (IS_ENABLED(CONFIG_NET_TCP)) {
11781259
struct zperf_download_params param = { 0 };
11791260
int ret;
1261+
int start;
1262+
1263+
start = shell_cmd_download(sh, argc, argv, &param);
1264+
if (start < 0) {
1265+
shell_fprintf(sh, SHELL_WARNING,
1266+
"Unable to parse option.\n");
1267+
return -ENOEXEC;
1268+
}
11801269

1181-
ret = zperf_bind_host(sh, argc, argv, &param);
1270+
ret = zperf_bind_host(sh, argc - start, &argv[start], &param);
11821271
if (ret < 0) {
11831272
shell_fprintf(sh, SHELL_WARNING,
11841273
"Unable to bind host.\n");
@@ -1345,6 +1434,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_udp,
13451434
#ifdef CONFIG_NET_CONTEXT_PRIORITY
13461435
"-p: Specify custom packet priority\n"
13471436
#endif /* CONFIG_NET_CONTEXT_PRIORITY */
1437+
"-I: Specify host interface name\n"
13481438
"Example: udp upload 192.0.2.2 1111 1 1K 1M\n"
13491439
"Example: udp upload 2001:db8::2\n",
13501440
cmd_udp_upload),
@@ -1362,6 +1452,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_udp,
13621452
#ifdef CONFIG_NET_CONTEXT_PRIORITY
13631453
"-p: Specify custom packet priority\n"
13641454
#endif /* CONFIG_NET_CONTEXT_PRIORITY */
1455+
"-I: Specify host interface name\n"
13651456
"Example: udp upload2 v4 1 1K 1M\n"
13661457
"Example: udp upload2 v6\n"
13671458
#if defined(CONFIG_NET_IPV6) && defined(MY_IP6ADDR_SET)
@@ -1375,8 +1466,11 @@ SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_udp,
13751466
,
13761467
cmd_udp_upload2),
13771468
SHELL_CMD(download, &zperf_cmd_udp_download,
1469+
"[<options>] command options (optional): [-I eth0]\n"
13781470
"[<port>]: Server port to listen on/connect to\n"
13791471
"[<host>]: Bind to <host>, an interface address\n"
1472+
"Available options:\n"
1473+
"-I <interface name>: Specify host interface name\n"
13801474
"Example: udp download 5001 192.168.0.1\n",
13811475
cmd_udp_download),
13821476
SHELL_SUBCMD_SET_END

subsys/net/lib/zperf/zperf_udp_receiver.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ LOG_MODULE_DECLARE(net_zperf, CONFIG_NET_ZPERF_LOG_LEVEL);
2323
#define NET_LOG_ENABLED 1
2424
#include "net_private.h"
2525

26+
/* To support multicast */
27+
#include "ipv6.h"
28+
#include "zephyr/net/igmp.h"
29+
2630
static struct sockaddr_in6 *in6_addr_my;
2731
static struct sockaddr_in *in4_addr_my;
2832

@@ -45,6 +49,7 @@ static void udp_svc_handler(struct k_work *work);
4549

4650
NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(svc_udp, NULL, udp_svc_handler,
4751
SOCK_ID_MAX);
52+
static char udp_server_iface_name[IFNAMSIZ];
4853

4954
static inline void build_reply(struct zperf_udp_datagram *hdr,
5055
struct zperf_server_hdr *stat,
@@ -225,6 +230,63 @@ static void udp_received(int sock, const struct sockaddr *addr, uint8_t *data,
225230
}
226231
}
227232

233+
static void zperf_udp_join_mcast_ipv4(char *if_name, struct in_addr *addr)
234+
{
235+
struct net_if *iface = NULL;
236+
237+
if (if_name[0]) {
238+
iface = net_if_get_by_index(net_if_get_by_name(if_name));
239+
if (iface == NULL)
240+
iface = net_if_get_default();
241+
} else {
242+
iface = net_if_get_default();
243+
}
244+
245+
if (iface != NULL) {
246+
net_ipv4_igmp_join(iface, addr, NULL);
247+
}
248+
}
249+
250+
static void zperf_udp_join_mcast_ipv6(char *if_name, struct in6_addr *addr)
251+
{
252+
struct net_if *iface = NULL;
253+
254+
if (if_name[0]) {
255+
iface = net_if_get_by_index(net_if_get_by_name(if_name));
256+
if (iface == NULL)
257+
iface = net_if_get_default();
258+
} else {
259+
iface = net_if_get_default();
260+
}
261+
262+
if (iface != NULL) {
263+
net_ipv6_mld_join(iface, addr);
264+
}
265+
}
266+
267+
static void zperf_udp_leave_mcast(int sock)
268+
{
269+
struct net_if *iface = NULL;
270+
struct sockaddr addr = {0};
271+
socklen_t addr_len = NET_IPV6_ADDR_SIZE;
272+
273+
zsock_getsockname(sock, &addr, &addr_len);
274+
275+
if (IS_ENABLED(CONFIG_NET_IPV4) && addr.sa_family == AF_INET) {
276+
struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr;
277+
278+
if (net_ipv4_is_addr_mcast(&addr4->sin_addr))
279+
net_ipv4_igmp_leave(iface, &addr4->sin_addr);
280+
}
281+
282+
if (IS_ENABLED(CONFIG_NET_IPV6) && addr.sa_family == AF_INET6) {
283+
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr;
284+
285+
if (net_ipv6_is_addr_mcast(&addr6->sin6_addr))
286+
net_ipv6_mld_leave(iface, &addr6->sin6_addr);
287+
}
288+
}
289+
228290
static void udp_receiver_cleanup(void)
229291
{
230292
int i;
@@ -233,6 +295,7 @@ static void udp_receiver_cleanup(void)
233295

234296
for (i = 0; i < ARRAY_SIZE(fds); i++) {
235297
if (fds[i].fd >= 0) {
298+
zperf_udp_leave_mcast(fds[i].fd);
236299
zsock_close(fds[i].fd);
237300
fds[i].fd = -1;
238301
}
@@ -350,6 +413,11 @@ static int zperf_udp_receiver_init(void)
350413
in4_addr_my->sin_addr.s_addr = INADDR_ANY;
351414
}
352415

416+
if (net_ipv4_is_addr_mcast(&in4_addr_my->sin_addr)) {
417+
zperf_udp_join_mcast_ipv4(udp_server_iface_name,
418+
&in4_addr_my->sin_addr);
419+
}
420+
353421
NET_INFO("Binding to %s",
354422
net_sprint_ipv4_addr(&in4_addr_my->sin_addr));
355423

@@ -402,6 +470,11 @@ static int zperf_udp_receiver_init(void)
402470
sizeof(struct in6_addr));
403471
}
404472

473+
if (net_ipv6_is_addr_mcast(&in6_addr_my->sin6_addr)) {
474+
zperf_udp_join_mcast_ipv6(udp_server_iface_name,
475+
&in6_addr_my->sin6_addr);
476+
}
477+
405478
NET_INFO("Binding to %s",
406479
net_sprint_ipv6_addr(&in6_addr_my->sin6_addr));
407480

@@ -451,6 +524,18 @@ int zperf_udp_download(const struct zperf_download_params *param,
451524
udp_server_port = param->port;
452525
memcpy(&udp_server_addr, &param->addr, sizeof(struct sockaddr));
453526

527+
if (param->if_name[0]) {
528+
/*
529+
* IFNAMSIZ by default CONFIG_NET_INTERFACE_NAME_LEN
530+
* is at least 1 so no overflow risk here
531+
*/
532+
(void)memset(udp_server_iface_name, 0, IFNAMSIZ);
533+
strncpy(udp_server_iface_name, param->if_name, IFNAMSIZ);
534+
udp_server_iface_name[IFNAMSIZ - 1] = 0;
535+
} else {
536+
udp_server_iface_name[0] = 0;
537+
}
538+
454539
ret = zperf_udp_receiver_init();
455540
if (ret < 0) {
456541
udp_receiver_cleanup();

subsys/net/lib/zperf/zperf_udp_uploader.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ int zperf_udp_upload(const struct zperf_upload_params *param,
288288
int port = 0;
289289
int sock;
290290
int ret;
291+
struct ifreq req;
291292

292293
if (param == NULL || result == NULL) {
293294
return -EINVAL;
@@ -309,6 +310,17 @@ int zperf_udp_upload(const struct zperf_upload_params *param,
309310
return sock;
310311
}
311312

313+
if (param->if_name[0]) {
314+
(void)memset(req.ifr_name, 0, sizeof(req.ifr_name));
315+
strncpy(req.ifr_name, param->if_name, IFNAMSIZ);
316+
req.ifr_name[IFNAMSIZ - 1] = 0;
317+
318+
if (zsock_setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &req,
319+
sizeof(struct ifreq)) != 0) {
320+
NET_WARN("setsockopt SO_BINDTODEVICE error (%d)", -errno);
321+
}
322+
}
323+
312324
ret = udp_upload(sock, port, param, result);
313325

314326
zsock_close(sock);

0 commit comments

Comments
 (0)