1/*
2 * Copyright (C) 2000 Lennert Buytenhek
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sys/time.h>
23#include <sys/select.h>
24#include <unistd.h>
25#include <errno.h>
26#include <asm/param.h>
27#include "libbridge.h"
28#include "brctl.h"
29
30static int strtotimeval(struct timeval *tv, const char *time)
31{
32	double secs;
33	if (sscanf(time, "%lf", &secs) != 1)
34		return -1;
35	tv->tv_sec = secs;
36	tv->tv_usec = 1000000 * (secs - tv->tv_sec);
37	return 0;
38}
39
40static int br_cmd_addbr(int argc, char*const* argv)
41{
42	int err;
43
44	switch (err = br_add_bridge(argv[1])) {
45	case 0:
46		return 0;
47
48	case EEXIST:
49		fprintf(stderr,	"device %s already exists; can't create "
50			"bridge with the same name\n", argv[1]);
51		return 1;
52	default:
53		fprintf(stderr, "add bridge failed: %s\n",
54			strerror(err));
55		return 1;
56	}
57}
58
59static int br_cmd_delbr(int argc, char*const* argv)
60{
61	int err;
62
63	switch (err = br_del_bridge(argv[1])){
64	case 0:
65		return 0;
66
67	case ENXIO:
68		fprintf(stderr, "bridge %s doesn't exist; can't delete it\n",
69			argv[1]);
70		return 1;
71
72	case EBUSY:
73		fprintf(stderr, "bridge %s is still up; can't delete it\n",
74			argv[1]);
75		return 1;
76
77	default:
78		fprintf(stderr, "can't delete bridge %s: %s\n",
79			argv[1], strerror(err));
80		return 1;
81	}
82}
83
84static int wait_to_forward_state(const char *br_ifname, const char *ifname)
85{
86	int err;
87	struct port_info info;
88	struct timeval now, later, tout;
89
90	if ((err = br_refresh())) {
91		fprintf(stderr, "Unable to refresh bridge info:%d\n", err);
92		return 1;
93	}
94
95	if ((err = br_get_port_info(br_ifname, ifname, &info))) {
96		fprintf(stderr, "Unable to get port info:%d\n", err);
97		return 1;
98	}
99
100	/* Wait for this port transition to FORWARDING state. */
101	gettimeofday(&now, NULL);
102	later = now;
103	while (info.state != BR_STATE_FORWARDING) {
104		if ((later.tv_sec - now.tv_sec) > 5) {
105			printf("device %s timedout changing to"
106				"BR_STATE_FORWARDING\n", ifname);
107			return 1;
108		}
109		tout.tv_sec = 0;
110		tout.tv_usec = 200000; /* 200 milli secs */
111		select(0, NULL, NULL, NULL, &tout);
112		if ((err = br_get_port_info(br_ifname, ifname, &info))) {
113			fprintf(stderr, "Unable to get port info:%d\n", err);
114			return 1;
115		}
116		gettimeofday(&later, NULL);
117	}
118
119	return 0;
120}
121
122static int br_cmd_addif(int argc, char *const* argv)
123{
124	const char *brname;
125	int err;
126	int waitarg = 0;
127
128	argc -= 2;
129	brname = *++argv;
130
131	/* is last argv "wait" */
132	if (!strcmp(*(argv+argc), "wait")) {
133		waitarg = 1;
134		argc -= 1; /* skip last wait argv */
135	}
136
137	while (argc-- > 0) {
138		const char *ifname = *++argv;
139		err = br_add_interface(brname, ifname);
140
141		switch(err) {
142		case 0:
143			if (waitarg && wait_to_forward_state(brname, ifname))
144				break;
145			continue;
146
147		case ENODEV:
148			fprintf(stderr, "interface %s does not exist!\n", ifname);
149			break;
150
151		case EBUSY:
152			fprintf(stderr,	"device %s is already a member of a bridge; "
153				"can't enslave it to bridge %s.\n", ifname,
154				brname);
155			break;
156
157		case ELOOP:
158			fprintf(stderr, "device %s is a bridge device itself; "
159				"can't enslave a bridge device to a bridge device.\n",
160				ifname);
161			break;
162
163		default:
164			fprintf(stderr, "can't add %s to bridge %s: %s\n",
165				ifname, brname, strerror(err));
166		}
167		return 1;
168	}
169	return 0;
170}
171
172static int br_cmd_delif(int argc, char *const* argv)
173{
174	const char *brname;
175	int err;
176
177	argc -= 2;
178	brname = *++argv;
179
180	while (argc-- > 0) {
181		const char *ifname = *++argv;
182		err = br_del_interface(brname, ifname);
183		switch (err) {
184		case 0:
185			continue;
186
187		case ENODEV:
188			fprintf(stderr, "interface %s does not exist!\n",
189				ifname);
190			break;
191
192		case EINVAL:
193			fprintf(stderr, "device %s is not a slave of %s\n",
194				ifname, brname);
195			break;
196
197		default:
198			fprintf(stderr, "can't delete %s from %s: %s\n",
199				ifname, brname, strerror(err));
200		}
201		return 1;
202	}
203	return 0;
204}
205
206static int br_cmd_setageing(int argc, char *const* argv)
207{
208	int err;
209	struct timeval tv;
210
211	if (strtotimeval(&tv, argv[2])) {
212		fprintf(stderr, "bad ageing time value\n");
213		return 1;
214	}
215
216	err = br_set_ageing_time(argv[1], &tv);
217	if (err)
218		fprintf(stderr, "set ageing time failed: %s\n",
219			strerror(err));
220
221	return err != 0;
222}
223
224static int br_cmd_setbridgeprio(int argc, char *const* argv)
225{
226	int prio;
227	int err;
228
229	if (sscanf(argv[2], "%i", &prio) != 1) {
230		fprintf(stderr,"bad priority\n");
231		return 1;
232	}
233
234	err = br_set_bridge_priority(argv[1], prio);
235	if (err)
236		fprintf(stderr, "set bridge priority failed: %s\n",
237			strerror(err));
238	return err != 0;
239}
240
241static int br_cmd_setfd(int argc, char *const* argv)
242{
243	struct timeval tv;
244	int err;
245
246	if (strtotimeval(&tv, argv[2])) {
247		fprintf(stderr, "bad forward delay value\n");
248		return 1;
249	}
250
251	err = br_set_bridge_forward_delay(argv[1], &tv);
252	if (err)
253		fprintf(stderr, "set forward delay failed: %s\n",
254			strerror(err));
255
256	return err != 0;
257}
258
259static int br_cmd_sethello(int argc, char *const* argv)
260{
261	struct timeval tv;
262	int err;
263
264	if (strtotimeval(&tv, argv[2])) {
265		fprintf(stderr, "bad hello timer value\n");
266		return 1;
267	}
268
269	err = br_set_bridge_hello_time(argv[1], &tv);
270	if (err)
271		fprintf(stderr, "set hello timer failed: %s\n",
272			strerror(err));
273
274	return err != 0;
275}
276
277static int br_cmd_setmaxage(int argc, char *const* argv)
278{
279	struct timeval tv;
280	int err;
281
282	if (strtotimeval(&tv, argv[2])) {
283		fprintf(stderr, "bad max age value\n");
284		return 1;
285	}
286	err = br_set_bridge_max_age(argv[1], &tv);
287	if (err)
288		fprintf(stderr, "set max age failed: %s\n",
289			strerror(err));
290
291	return err != 0;
292}
293
294static int br_cmd_setpathcost(int argc, char *const* argv)
295{
296	int cost, err;
297
298	if (sscanf(argv[3], "%i", &cost) != 1) {
299		fprintf(stderr, "bad path cost value\n");
300		return 1;
301	}
302
303	err = br_set_path_cost(argv[1], argv[2], cost);
304	if (err)
305		fprintf(stderr, "set path cost failed: %s\n",
306			strerror(err));
307	return err != 0;
308}
309
310static int br_cmd_setportprio(int argc, char *const* argv)
311{
312	int cost, err;
313
314	if (sscanf(argv[3], "%i", &cost) != 1) {
315		fprintf(stderr, "bad path priority value\n");
316		return 1;
317	}
318
319	err = br_set_path_cost(argv[1], argv[2], cost);
320	if (err)
321		fprintf(stderr, "set port priority failed: %s\n",
322			strerror(errno));
323
324	return err != 0;
325}
326
327static int br_cmd_stp(int argc, char *const* argv)
328{
329	int stp, err;
330
331	if (!strcmp(argv[2], "on") || !strcmp(argv[2], "yes")
332	    || !strcmp(argv[2], "1"))
333		stp = 1;
334	else if (!strcmp(argv[2], "off") || !strcmp(argv[2], "no")
335		 || !strcmp(argv[2], "0"))
336		stp = 0;
337	else {
338		fprintf(stderr, "expect on/off for argument\n");
339		return 1;
340	}
341
342	err = br_set_stp_state(argv[1], stp);
343	if (err)
344		fprintf(stderr, "set stp status failed: %s\n",
345			strerror(errno));
346	return err != 0;
347}
348
349static int br_cmd_showstp(int argc, char *const* argv)
350{
351	struct bridge_info info;
352
353	if (br_get_bridge_info(argv[1], &info)) {
354		fprintf(stderr, "%s: can't get info %s\n", argv[1],
355			strerror(errno));
356		return 1;
357	}
358
359	br_dump_info(argv[1], &info);
360	return 0;
361}
362
363static int show_bridge(const char *name, void *arg)
364{
365	struct bridge_info info;
366
367	printf("%s\t\t", name);
368	fflush(stdout);
369
370	if (br_get_bridge_info(name, &info)) {
371		fprintf(stderr, "can't get info %s\n",
372				strerror(errno));
373		return 1;
374	}
375
376	br_dump_bridge_id((unsigned char *)&info.bridge_id);
377	printf("\t%s\t\t", info.stp_enabled?"yes":"no");
378
379	br_dump_interface_list(name);
380	return 0;
381}
382
383static int br_cmd_show(int argc, char *const* argv)
384{
385	printf("bridge name\tbridge id\t\tSTP enabled\tinterfaces\n");
386	br_foreach_bridge(show_bridge, NULL);
387	return 0;
388}
389
390static int compare_fdbs(const void *_f0, const void *_f1)
391{
392	const struct fdb_entry *f0 = _f0;
393	const struct fdb_entry *f1 = _f1;
394
395	return memcmp(f0->mac_addr, f1->mac_addr, 6);
396}
397
398static int br_cmd_showmacs(int argc, char *const* argv)
399{
400	const char *brname = argv[1];
401#define CHUNK 128
402	int i, n;
403	struct fdb_entry *fdb = NULL;
404	int offset = 0;
405
406	for(;;) {
407		fdb = realloc(fdb, (offset + CHUNK) * sizeof(struct fdb_entry));
408		if (!fdb) {
409			fprintf(stderr, "Out of memory\n");
410			return 1;
411		}
412
413		n = br_read_fdb(brname, fdb+offset, offset, CHUNK);
414		if (n == 0)
415			break;
416
417		if (n < 0) {
418			fprintf(stderr, "read of forward table failed: %s\n",
419				strerror(errno));
420			return 1;
421		}
422
423		offset += n;
424	}
425
426	qsort(fdb, offset, sizeof(struct fdb_entry), compare_fdbs);
427
428	printf("port no\tmac addr\t\tis local?\tageing timer\n");
429	for (i = 0; i < offset; i++) {
430		const struct fdb_entry *f = fdb + i;
431		printf("%3i\t", f->port_no);
432		printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t",
433		       f->mac_addr[0], f->mac_addr[1], f->mac_addr[2],
434		       f->mac_addr[3], f->mac_addr[4], f->mac_addr[5]);
435		printf("%s\t\t", f->is_local?"yes":"no");
436		br_show_timer(&f->ageing_timer_value);
437		printf("\n");
438	}
439	return 0;
440}
441
442static const struct command commands[] = {
443	{ 1, "addbr", br_cmd_addbr, "<bridge>\t\tadd bridge" },
444	{ 1, "delbr", br_cmd_delbr, "<bridge>\t\tdelete bridge" },
445	{ 2, "addif", br_cmd_addif,
446	  "<bridge> <device>\tadd interface to bridge" },
447	{ 2, "delif", br_cmd_delif,
448	  "<bridge> <device>\tdelete interface from bridge" },
449	{ 2, "setageing", br_cmd_setageing,
450	  "<bridge> <time>\t\tset ageing time" },
451	{ 2, "setbridgeprio", br_cmd_setbridgeprio,
452	  "<bridge> <prio>\t\tset bridge priority" },
453	{ 2, "setfd", br_cmd_setfd,
454	  "<bridge> <time>\t\tset bridge forward delay" },
455	{ 2, "sethello", br_cmd_sethello,
456	  "<bridge> <time>\t\tset hello time" },
457	{ 2, "setmaxage", br_cmd_setmaxage,
458	  "<bridge> <time>\t\tset max message age" },
459	{ 3, "setpathcost", br_cmd_setpathcost,
460	  "<bridge> <port> <cost>\tset path cost" },
461	{ 3, "setportprio", br_cmd_setportprio,
462	  "<bridge> <port> <prio>\tset port priority" },
463	{ 0, "show", br_cmd_show, "\t\t\tshow a list of bridges" },
464	{ 1, "showmacs", br_cmd_showmacs,
465	  "<bridge>\t\tshow a list of mac addrs"},
466	{ 1, "showstp", br_cmd_showstp,
467	  "<bridge>\t\tshow bridge stp info"},
468	{ 2, "stp", br_cmd_stp,
469	  "<bridge> {on|off}\tturn stp on/off" },
470};
471
472const struct command *command_lookup(const char *cmd)
473{
474	int i;
475
476	for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) {
477		if (!strcmp(cmd, commands[i].name))
478			return &commands[i];
479	}
480
481	return NULL;
482}
483
484void command_helpall(void)
485{
486	int i;
487
488	for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) {
489		printf("\t%-10s\t%s\n", commands[i].name, commands[i].help);
490	}
491}
492