229 lines
5.3 KiB
C
229 lines
5.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 br_idx, if_idx;
|
|
struct eth_bridge *br;
|
|
struct net_if *iface;
|
|
|
|
br_idx = get_idx(sh, argv[1]);
|
|
if (br_idx < 0) {
|
|
return br_idx;
|
|
}
|
|
if_idx = get_idx(sh, argv[2]);
|
|
if (if_idx < 0) {
|
|
return if_idx;
|
|
}
|
|
br = eth_bridge_get_by_index(br_idx);
|
|
if (br == NULL) {
|
|
shell_warn(sh, "Bridge %d not found\n", br_idx);
|
|
return -ENOENT;
|
|
}
|
|
iface = net_if_get_by_index(if_idx);
|
|
if (iface == NULL) {
|
|
shell_warn(sh, "Interface %d not found\n", if_idx);
|
|
return -ENOENT;
|
|
}
|
|
if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
|
|
shell_warn(sh, "Interface %d is not Ethernet\n", if_idx);
|
|
return -EINVAL;
|
|
}
|
|
if (!(net_eth_get_hw_capabilities(iface) & ETHERNET_PROMISC_MODE)) {
|
|
shell_warn(sh, "Interface %d cannot do promiscuous mode\n", if_idx);
|
|
return -EINVAL;
|
|
}
|
|
|
|
int ret = eth_bridge_iface_add(br, iface);
|
|
|
|
if (ret < 0) {
|
|
shell_error(sh, "error: eth_bridge_iface_add() returned %d\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int cmd_bridge_delif(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
int br_idx, if_idx;
|
|
struct eth_bridge *br;
|
|
struct net_if *iface;
|
|
|
|
br_idx = get_idx(sh, argv[1]);
|
|
if (br_idx < 0) {
|
|
return br_idx;
|
|
}
|
|
if_idx = get_idx(sh, argv[2]);
|
|
if (if_idx < 0) {
|
|
return if_idx;
|
|
}
|
|
br = eth_bridge_get_by_index(br_idx);
|
|
if (br == NULL) {
|
|
shell_warn(sh, "Bridge %d not found\n", br_idx);
|
|
return -ENOENT;
|
|
}
|
|
iface = net_if_get_by_index(if_idx);
|
|
if (iface == NULL) {
|
|
shell_warn(sh, "Interface %d not found\n", if_idx);
|
|
return -ENOENT;
|
|
}
|
|
|
|
int ret = eth_bridge_iface_remove(br, iface);
|
|
|
|
if (ret < 0) {
|
|
shell_error(sh, "error: eth_bridge_iface_remove() returned %d\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int cmd_bridge_allow_tx(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
int br_idx, if_idx;
|
|
struct eth_bridge *br;
|
|
struct net_if *iface;
|
|
struct ethernet_context *ctx;
|
|
|
|
br_idx = get_idx(sh, argv[1]);
|
|
if (br_idx < 0) {
|
|
return br_idx;
|
|
}
|
|
if_idx = get_idx(sh, argv[2]);
|
|
if (if_idx < 0) {
|
|
return if_idx;
|
|
}
|
|
br = eth_bridge_get_by_index(br_idx);
|
|
if (br == NULL) {
|
|
shell_error(sh, "Bridge %d not found\n", br_idx);
|
|
return -ENOENT;
|
|
}
|
|
iface = net_if_get_by_index(if_idx);
|
|
if (iface == NULL) {
|
|
shell_error(sh, "Interface %d not found", if_idx);
|
|
return -ENOENT;
|
|
}
|
|
ctx = net_if_l2_data(iface);
|
|
if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET) ||
|
|
ctx->bridge.instance != br) {
|
|
shell_error(sh, "Interface %d is not tied to bridge %d",
|
|
if_idx, br_idx);
|
|
return -ENOENT;
|
|
}
|
|
|
|
if (!strcmp(argv[2], "1") ||
|
|
!strcmp(argv[2], "yes") ||
|
|
!strcmp(argv[2], "on") ||
|
|
!strcmp(argv[2], "true")) {
|
|
eth_bridge_iface_allow_tx(iface, true);
|
|
} else {
|
|
eth_bridge_iface_allow_tx(iface, false);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void bridge_show(struct eth_bridge *br, void *data)
|
|
{
|
|
const struct shell *sh = data;
|
|
int br_idx = eth_bridge_get_index(br);
|
|
sys_snode_t *node;
|
|
bool pad;
|
|
|
|
shell_fprintf(sh, SHELL_NORMAL, "%-10d", br_idx);
|
|
pad = false;
|
|
|
|
k_mutex_lock(&br->lock, K_FOREVER);
|
|
|
|
SYS_SLIST_FOR_EACH_NODE(&br->interfaces, node) {
|
|
struct ethernet_context *ctx;
|
|
int if_idx;
|
|
|
|
ctx = CONTAINER_OF(node, struct ethernet_context, bridge.node);
|
|
if_idx = net_if_get_by_iface(ctx->iface);
|
|
|
|
if (pad) {
|
|
shell_fprintf(sh, SHELL_NORMAL, "%-10s", "");
|
|
}
|
|
shell_fprintf(sh, SHELL_NORMAL, "%-10d%s", if_idx,
|
|
ctx->bridge.allow_tx ? "*" : "");
|
|
pad = true;
|
|
}
|
|
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
|
|
|
k_mutex_unlock(&br->lock);
|
|
}
|
|
|
|
static int cmd_bridge_show(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
int br_idx;
|
|
struct eth_bridge *br = NULL;
|
|
|
|
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 iface tx_enabled\n");
|
|
|
|
if (br != NULL) {
|
|
bridge_show(br, (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> <interface_index>'",
|
|
cmd_bridge_addif, 3, 0),
|
|
SHELL_CMD_ARG(delif, NULL,
|
|
"Delete a network interface from a bridge.\n"
|
|
"'bridge delif <bridge_index> <interface_index>'",
|
|
cmd_bridge_delif, 3, 0),
|
|
SHELL_CMD_ARG(tx, NULL,
|
|
"Enable/disable tx from given bridged interface.\n"
|
|
"'bridge tx <bridge_index> <interface_index> {on|off}'",
|
|
cmd_bridge_allow_tx, 4, 0),
|
|
SHELL_CMD_ARG(show, NULL,
|
|
"Show bridge information.\n"
|
|
"'bridge show [<bridge_index>]'",
|
|
cmd_bridge_show, 1, 1),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_CMD_REGISTER(bridge, &bridge_commands, "Ethernet Bridge commands", NULL);
|
|
|
|
#if defined(CONFIG_NET_ETHERNET_BRIDGE_DEFAULT)
|
|
static ETH_BRIDGE_INIT(shell_default_bridge);
|
|
#endif
|