1 /*
2  * Copyright (c) 2021 BayLibre SAS
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdlib.h>
8 #include <zephyr/shell/shell.h>
9 #include <zephyr/net/net_if.h>
10 #include <zephyr/net/ethernet.h>
11 #include <zephyr/net/ethernet_bridge.h>
12 #include <zephyr/sys/slist.h>
13 
get_idx(const struct shell * sh,char * index_str)14 static int get_idx(const struct shell *sh, char *index_str)
15 {
16 	char *endptr;
17 	int idx;
18 
19 	idx = strtol(index_str, &endptr, 10);
20 	if (*endptr != '\0') {
21 		shell_warn(sh, "Invalid index %s\n", index_str);
22 		return -ENOENT;
23 	}
24 
25 	return idx;
26 }
27 
cmd_bridge_addif(const struct shell * sh,size_t argc,char * argv[])28 static int cmd_bridge_addif(const struct shell *sh, size_t argc, char *argv[])
29 {
30 	int ret = 0, br_idx, if_idx;
31 	struct net_if *iface;
32 	struct net_if *br;
33 
34 	br_idx = get_idx(sh, argv[1]);
35 	if (br_idx < 0) {
36 		return br_idx;
37 	}
38 
39 	br = eth_bridge_get_by_index(br_idx);
40 	if (br == NULL) {
41 		shell_warn(sh, "Bridge %d not found\n", br_idx);
42 		return -ENOENT;
43 	}
44 
45 	for (int i = 2; i < argc; i++) {
46 		if_idx = get_idx(sh, argv[i]);
47 		if (if_idx < 0) {
48 			continue;
49 		}
50 
51 		iface = net_if_get_by_index(if_idx);
52 		if (iface == NULL) {
53 			shell_warn(sh, "Interface %d not found\n", if_idx);
54 			continue;
55 		}
56 
57 		if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
58 			shell_warn(sh, "Interface %d is not Ethernet\n", if_idx);
59 			continue;
60 		}
61 
62 		if (!(net_eth_get_hw_capabilities(iface) & ETHERNET_PROMISC_MODE)) {
63 			shell_warn(sh, "Interface %d cannot do promiscuous mode\n", if_idx);
64 			continue;
65 		}
66 
67 		ret = eth_bridge_iface_add(br, iface);
68 		if (ret < 0) {
69 			shell_error(sh, "error: bridge iface add (%d)\n", ret);
70 		}
71 	}
72 
73 	return ret;
74 }
75 
cmd_bridge_delif(const struct shell * sh,size_t argc,char * argv[])76 static int cmd_bridge_delif(const struct shell *sh, size_t argc, char *argv[])
77 {
78 	int ret = 0, br_idx, if_idx;
79 	struct net_if *iface;
80 	struct net_if *br;
81 
82 	br_idx = get_idx(sh, argv[1]);
83 	if (br_idx < 0) {
84 		return br_idx;
85 	}
86 
87 	br = eth_bridge_get_by_index(br_idx);
88 	if (br == NULL) {
89 		shell_warn(sh, "Bridge %d not found\n", br_idx);
90 		return -ENOENT;
91 	}
92 
93 	for (int i = 2; i < argc; i++) {
94 		if_idx = get_idx(sh, argv[i]);
95 		if (if_idx < 0) {
96 			continue;
97 		}
98 
99 		iface = net_if_get_by_index(if_idx);
100 		if (iface == NULL) {
101 			shell_warn(sh, "Interface %d not found\n", if_idx);
102 			continue;
103 		}
104 
105 		ret = eth_bridge_iface_remove(br, iface);
106 		if (ret < 0) {
107 			shell_error(sh, "error: bridge iface remove (%d)\n", ret);
108 		}
109 	}
110 
111 	return ret;
112 }
113 
bridge_show(struct eth_bridge_iface_context * ctx,void * data)114 static void bridge_show(struct eth_bridge_iface_context *ctx, void *data)
115 {
116 	int br_idx = eth_bridge_get_index(ctx->iface);
117 	const struct shell *sh = data;
118 
119 	shell_fprintf(sh, SHELL_NORMAL, "%-7d", br_idx);
120 
121 	if (net_if_is_up(ctx->iface)) {
122 		shell_fprintf(sh, SHELL_NORMAL, "%-9s", "up");
123 	} else {
124 		shell_fprintf(sh, SHELL_NORMAL, "%-9s", "down");
125 	}
126 
127 	if (ctx->is_setup) {
128 		shell_fprintf(sh, SHELL_NORMAL, "%-9s", "ok");
129 	} else {
130 		shell_fprintf(sh, SHELL_NORMAL, "%-9s", "no");
131 	}
132 
133 	k_mutex_lock(&ctx->lock, K_FOREVER);
134 
135 	ARRAY_FOR_EACH(ctx->eth_iface, i) {
136 		int if_idx;
137 
138 		if (ctx->eth_iface[i] == NULL) {
139 			continue;
140 		}
141 
142 		if_idx = net_if_get_by_iface(ctx->eth_iface[i]);
143 
144 		shell_fprintf(sh, SHELL_NORMAL, "%-2d", if_idx);
145 	}
146 
147 	shell_fprintf(sh, SHELL_NORMAL, "\n");
148 
149 	k_mutex_unlock(&ctx->lock);
150 }
151 
cmd_bridge_show(const struct shell * sh,size_t argc,char * argv[])152 static int cmd_bridge_show(const struct shell *sh, size_t argc, char *argv[])
153 {
154 	struct net_if *br = NULL;
155 	int br_idx;
156 
157 	if (argc == 2) {
158 		br_idx = get_idx(sh, argv[1]);
159 		if (br_idx < 0) {
160 			return br_idx;
161 		}
162 
163 		br = eth_bridge_get_by_index(br_idx);
164 		if (br == NULL) {
165 			shell_warn(sh, "Bridge %d not found\n", br_idx);
166 			return -ENOENT;
167 		}
168 	}
169 
170 	shell_fprintf(sh, SHELL_NORMAL, "Bridge %-9s%-9sInterfaces\n",
171 		      "Status", "Config");
172 
173 	if (br != NULL) {
174 		bridge_show(net_if_get_device(br)->data, (void *)sh);
175 	} else {
176 		net_eth_bridge_foreach(bridge_show, (void *)sh);
177 	}
178 
179 	return 0;
180 }
181 
182 SHELL_STATIC_SUBCMD_SET_CREATE(bridge_commands,
183 	SHELL_CMD_ARG(addif, NULL,
184 		  "Add a network interface to a bridge.\n"
185 		  "'bridge addif <bridge_index> <one or more interface index>'",
186 		  cmd_bridge_addif, 3, 5),
187 	SHELL_CMD_ARG(delif, NULL,
188 		  "Delete a network interface from a bridge.\n"
189 		  "'bridge delif <bridge_index> <one or more interface index>'",
190 		  cmd_bridge_delif, 3, 5),
191 	SHELL_CMD_ARG(show, NULL,
192 		  "Show bridge information.\n"
193 		  "'bridge show [<bridge_index>]'",
194 		  cmd_bridge_show, 1, 1),
195 	SHELL_SUBCMD_SET_END
196 );
197 
198 SHELL_SUBCMD_ADD((net), bridge, &bridge_commands,
199 		 "Ethernet bridge commands.",
200 		 cmd_bridge_show, 1, 1);
201