The legacy bridging code prevented normal IP traffic to the
bridged Ethernet interfaces. This is not intuitive and differs
how bridging setup works in Linux. This commit changes that and
creates a separate virtual interface that is doing the actual
bridging. This enables the bridged Ethernet interfaces to work
normally and provide IP connectivity.
How this works in practice:
* User needs to enable CONFIG_NET_ETHERNET_BRIDGE
* User needs to have a device with more than one Ethernet
interface
* After booting, the net-shell or program API can be used
to add interfaces to the bridge like this.
net bridge addif 1 3 2
where the 1 is the bridge interface index and
2 and 3 are the Ethernet interface indices.
* The bridging is then finally enabled / started when the
bridge interface 1 is taken up
net iface up 1
* If bridged interfaces are removed from the bridge (minimum
of two interfaces are needed there), then the bridging is
disabled automatically. The bridge interface stays up in
this case and can be taken down manually.
Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
201 lines
4.3 KiB
C
201 lines
4.3 KiB
C
/*
|
|
* Copyright (c) 2021 BayLibre SAS
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <zephyr/shell/shell.h>
|
|
#include <zephyr/net/net_if.h>
|
|
#include <zephyr/net/ethernet.h>
|
|
#include <zephyr/net/ethernet_bridge.h>
|
|
#include <zephyr/sys/slist.h>
|
|
|
|
static int get_idx(const struct shell *sh, char *index_str)
|
|
{
|
|
char *endptr;
|
|
int idx;
|
|
|
|
idx = strtol(index_str, &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
shell_warn(sh, "Invalid index %s\n", index_str);
|
|
return -ENOENT;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
static int cmd_bridge_addif(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
int ret = 0, br_idx, if_idx;
|
|
struct net_if *iface;
|
|
struct net_if *br;
|
|
|
|
br_idx = get_idx(sh, argv[1]);
|
|
if (br_idx < 0) {
|
|
return br_idx;
|
|
}
|
|
|
|
br = eth_bridge_get_by_index(br_idx);
|
|
if (br == NULL) {
|
|
shell_warn(sh, "Bridge %d not found\n", br_idx);
|
|
return -ENOENT;
|
|
}
|
|
|
|
for (int i = 2; i < argc; i++) {
|
|
if_idx = get_idx(sh, argv[i]);
|
|
if (if_idx < 0) {
|
|
continue;
|
|
}
|
|
|
|
iface = net_if_get_by_index(if_idx);
|
|
if (iface == NULL) {
|
|
shell_warn(sh, "Interface %d not found\n", if_idx);
|
|
continue;
|
|
}
|
|
|
|
if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
|
|
shell_warn(sh, "Interface %d is not Ethernet\n", if_idx);
|
|
continue;
|
|
}
|
|
|
|
if (!(net_eth_get_hw_capabilities(iface) & ETHERNET_PROMISC_MODE)) {
|
|
shell_warn(sh, "Interface %d cannot do promiscuous mode\n", if_idx);
|
|
continue;
|
|
}
|
|
|
|
ret = eth_bridge_iface_add(br, iface);
|
|
if (ret < 0) {
|
|
shell_error(sh, "error: bridge iface add (%d)\n", ret);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int cmd_bridge_delif(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
int ret = 0, br_idx, if_idx;
|
|
struct net_if *iface;
|
|
struct net_if *br;
|
|
|
|
br_idx = get_idx(sh, argv[1]);
|
|
if (br_idx < 0) {
|
|
return br_idx;
|
|
}
|
|
|
|
br = eth_bridge_get_by_index(br_idx);
|
|
if (br == NULL) {
|
|
shell_warn(sh, "Bridge %d not found\n", br_idx);
|
|
return -ENOENT;
|
|
}
|
|
|
|
for (int i = 2; i < argc; i++) {
|
|
if_idx = get_idx(sh, argv[i]);
|
|
if (if_idx < 0) {
|
|
continue;
|
|
}
|
|
|
|
iface = net_if_get_by_index(if_idx);
|
|
if (iface == NULL) {
|
|
shell_warn(sh, "Interface %d not found\n", if_idx);
|
|
continue;
|
|
}
|
|
|
|
ret = eth_bridge_iface_remove(br, iface);
|
|
if (ret < 0) {
|
|
shell_error(sh, "error: bridge iface remove (%d)\n", ret);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void bridge_show(struct eth_bridge_iface_context *ctx, void *data)
|
|
{
|
|
int br_idx = eth_bridge_get_index(ctx->iface);
|
|
const struct shell *sh = data;
|
|
|
|
shell_fprintf(sh, SHELL_NORMAL, "%-7d", br_idx);
|
|
|
|
if (net_if_is_up(ctx->iface)) {
|
|
shell_fprintf(sh, SHELL_NORMAL, "%-9s", "up");
|
|
} else {
|
|
shell_fprintf(sh, SHELL_NORMAL, "%-9s", "down");
|
|
}
|
|
|
|
if (ctx->is_setup) {
|
|
shell_fprintf(sh, SHELL_NORMAL, "%-9s", "ok");
|
|
} else {
|
|
shell_fprintf(sh, SHELL_NORMAL, "%-9s", "no");
|
|
}
|
|
|
|
k_mutex_lock(&ctx->lock, K_FOREVER);
|
|
|
|
ARRAY_FOR_EACH(ctx->eth_iface, i) {
|
|
int if_idx;
|
|
|
|
if (ctx->eth_iface[i] == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if_idx = net_if_get_by_iface(ctx->eth_iface[i]);
|
|
|
|
shell_fprintf(sh, SHELL_NORMAL, "%-2d", if_idx);
|
|
}
|
|
|
|
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
|
|
|
k_mutex_unlock(&ctx->lock);
|
|
}
|
|
|
|
static int cmd_bridge_show(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
struct net_if *br = NULL;
|
|
int br_idx;
|
|
|
|
if (argc == 2) {
|
|
br_idx = get_idx(sh, argv[1]);
|
|
if (br_idx < 0) {
|
|
return br_idx;
|
|
}
|
|
|
|
br = eth_bridge_get_by_index(br_idx);
|
|
if (br == NULL) {
|
|
shell_warn(sh, "Bridge %d not found\n", br_idx);
|
|
return -ENOENT;
|
|
}
|
|
}
|
|
|
|
shell_fprintf(sh, SHELL_NORMAL, "Bridge %-9s%-9sInterfaces\n",
|
|
"Status", "Config");
|
|
|
|
if (br != NULL) {
|
|
bridge_show(net_if_get_device(br)->data, (void *)sh);
|
|
} else {
|
|
net_eth_bridge_foreach(bridge_show, (void *)sh);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(bridge_commands,
|
|
SHELL_CMD_ARG(addif, NULL,
|
|
"Add a network interface to a bridge.\n"
|
|
"'bridge addif <bridge_index> <one or more interface index>'",
|
|
cmd_bridge_addif, 3, 5),
|
|
SHELL_CMD_ARG(delif, NULL,
|
|
"Delete a network interface from a bridge.\n"
|
|
"'bridge delif <bridge_index> <one or more interface index>'",
|
|
cmd_bridge_delif, 3, 5),
|
|
SHELL_CMD_ARG(show, NULL,
|
|
"Show bridge information.\n"
|
|
"'bridge show [<bridge_index>]'",
|
|
cmd_bridge_show, 1, 1),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_SUBCMD_ADD((net), bridge, &bridge_commands,
|
|
"Ethernet bridge commands.",
|
|
cmd_bridge_show, 1, 1);
|