You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
334 lines
8.5 KiB
334 lines
8.5 KiB
/* brctl.c - ethernet bridge control
|
|
*
|
|
* Copyright 2013 Ashwini Kumar <ak.ashwini1981@gmail.com>
|
|
* Copyright 2013 Kyungwan Han <asura321@gmail.com>
|
|
*
|
|
* No Standard
|
|
|
|
USE_BRCTL(NEWTOY(brctl, "<1", TOYFLAG_USR|TOYFLAG_SBIN))
|
|
|
|
config BRCTL
|
|
bool "brctl"
|
|
default n
|
|
help
|
|
usage: brctl COMMAND [BRIDGE [INTERFACE]]
|
|
|
|
Manage ethernet bridges
|
|
|
|
Commands:
|
|
show Show a list of bridges
|
|
addbr BRIDGE Create BRIDGE
|
|
delbr BRIDGE Delete BRIDGE
|
|
addif BRIDGE IFACE Add IFACE to BRIDGE
|
|
delif BRIDGE IFACE Delete IFACE from BRIDGE
|
|
setageing BRIDGE TIME Set ageing time
|
|
setfd BRIDGE TIME Set bridge forward delay
|
|
sethello BRIDGE TIME Set hello time
|
|
setmaxage BRIDGE TIME Set max message age
|
|
setpathcost BRIDGE PORT COST Set path cost
|
|
setportprio BRIDGE PORT PRIO Set port priority
|
|
setbridgeprio BRIDGE PRIO Set bridge priority
|
|
stp BRIDGE [1/yes/on|0/no/off] STP on/off
|
|
*/
|
|
|
|
#define FOR_brctl
|
|
#include "toys.h"
|
|
#include <linux/if_bridge.h>
|
|
|
|
GLOBALS(
|
|
int sockfd;
|
|
)
|
|
#define MAX_BRIDGES 1024 //same is no of ports supported
|
|
|
|
static void get_ports(char *bridge, int *indices)
|
|
{
|
|
struct ifreq ifr;
|
|
int ifindices[MAX_BRIDGES];
|
|
unsigned long args[4] = { BRCTL_GET_PORT_LIST,
|
|
(unsigned long) ifindices, MAX_BRIDGES, 0 };
|
|
|
|
memset(ifindices, 0, MAX_BRIDGES);
|
|
args[1] = (unsigned long)ifindices;
|
|
xstrncpy(ifr.ifr_name, bridge, IFNAMSIZ);
|
|
ifr.ifr_data = (char *)args;
|
|
xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
|
|
if (indices) memcpy(indices, ifindices, sizeof(ifindices));
|
|
}
|
|
|
|
void get_br_info(char *bridge, struct __bridge_info *info)
|
|
{
|
|
struct ifreq ifr;
|
|
unsigned long args[4] = { BRCTL_GET_BRIDGE_INFO,
|
|
(unsigned long) info, 0, 0 };
|
|
|
|
memset(info, 0, sizeof(*info));
|
|
xstrncpy(ifr.ifr_name, bridge, IFNAMSIZ);
|
|
ifr.ifr_data = (char *)args;
|
|
|
|
if (ioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr) < 0) {
|
|
perror_msg("%s: can't get info %s\n", bridge, strerror(errno));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void br_show(char **argv)
|
|
{
|
|
struct __bridge_info info;
|
|
int num, cnt, i, j, ifindices[MAX_BRIDGES], pindices[MAX_BRIDGES];
|
|
unsigned long args[4] = { BRCTL_GET_BRIDGES,
|
|
(unsigned long)ifindices, MAX_BRIDGES,0 };
|
|
char br[IF_NAMESIZE], ifn[IF_NAMESIZE];
|
|
|
|
num = ioctl(TT.sockfd, SIOCGIFBR, args); //ret is num of bridges found
|
|
if (num < 0) error_exit("get bridges fail");
|
|
printf("bridge name\tbridge id\t\tSTP enabled\tinterfaces\n");
|
|
|
|
for (i = 0; i < num; i++) {
|
|
unsigned char *id;
|
|
|
|
if (!if_indextoname(ifindices[i], br)) perror_exit("interface not found");
|
|
get_br_info(br, &info);
|
|
id = (unsigned char*)&(info.bridge_id);
|
|
printf("%s\t\t",br);
|
|
printf("%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x", id[0], id[1],
|
|
id[2], id[3], id[4], id[5], id[6], id[7]);
|
|
printf("\t%s\t\t",(info.stp_enabled)?"yes" : "no");
|
|
|
|
memset(pindices, 0, sizeof(pindices));
|
|
get_ports(br, pindices);
|
|
for (j = 0, cnt = 0; j < MAX_BRIDGES; j++) {
|
|
if (!pindices[j]) continue;
|
|
if (!if_indextoname(pindices[j], ifn)) {
|
|
error_msg("no name for index :%d", pindices[j]);
|
|
continue;
|
|
}
|
|
if (cnt) printf("\n\t\t\t\t\t\t\t");
|
|
printf("%s", ifn);
|
|
cnt++;
|
|
}
|
|
xputc('\n');
|
|
}
|
|
}
|
|
|
|
void br_addbr(char **argv)
|
|
{
|
|
char br[IFNAMSIZ];
|
|
unsigned long args[4] = {BRCTL_ADD_BRIDGE, (unsigned long) br, 0, 0};
|
|
|
|
#ifdef SIOCBRADDBR
|
|
xioctl(TT.sockfd, SIOCBRADDBR, argv[0]);
|
|
#else
|
|
xstrncpy(br, argv[0], IFNAMSIZ);
|
|
xioctl(TT.sockfd, SIOCSIFBR, args);
|
|
#endif
|
|
}
|
|
|
|
void br_delbr(char **argv)
|
|
{
|
|
char br[IFNAMSIZ];
|
|
unsigned long args[4] = {BRCTL_DEL_BRIDGE, (unsigned long) br, 0, 0};
|
|
|
|
#ifdef SIOCBRDELBR
|
|
xioctl(TT.sockfd, SIOCBRDELBR, argv[0]);
|
|
#else
|
|
xstrncpy(br, argv[0], IFNAMSIZ);
|
|
xioctl(TT.sockfd, SIOCSIFBR, args);
|
|
#endif
|
|
}
|
|
|
|
void br_addif(char **argv)
|
|
{
|
|
int index;
|
|
struct ifreq ifr;
|
|
unsigned long args[4] = {BRCTL_ADD_IF, 0, 0, 0};
|
|
|
|
if (!(index = if_nametoindex(argv[1]))) perror_exit("interface %s", argv[1]);
|
|
#ifdef SIOCBRADDIF
|
|
ifr.ifr_ifindex = index;
|
|
xioctl(TT.sockfd, SIOCBRADDIF, &ifr);
|
|
#else
|
|
args[1] = index;
|
|
xstrncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
|
|
ifr.ifr_data = (char *)args;
|
|
xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
|
|
#endif
|
|
}
|
|
|
|
void br_delif(char **argv)
|
|
{
|
|
int index;
|
|
struct ifreq ifr;
|
|
unsigned long args[4] = {BRCTL_DEL_IF, 0, 0, 0};
|
|
|
|
if (!(index = if_nametoindex(argv[1]))) perror_exit("interface %s",argv[1]);
|
|
#ifdef SIOCBRDELIF
|
|
ifr.ifr_ifindex = ifindex;
|
|
xioctl(TT.sockfd, SIOCBRDELIF, &ifr);
|
|
#else
|
|
args[1] = index;
|
|
xstrncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
|
|
ifr.ifr_data = (char *)args;
|
|
xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
|
|
#endif
|
|
}
|
|
|
|
static void strtotimeval(struct timeval *tv, char *time)
|
|
{
|
|
double secs;
|
|
|
|
if (sscanf(time, "%lf", &secs) != 1) error_exit("time format not proper");
|
|
tv->tv_sec = secs;
|
|
tv->tv_usec = 1000000 * (secs - tv->tv_sec);
|
|
}
|
|
|
|
static unsigned long tv_to_jify(struct timeval *tv)
|
|
{
|
|
unsigned long long jify;
|
|
|
|
jify = 1000000ULL * tv->tv_sec + tv->tv_usec;
|
|
return (jify/10000);
|
|
}
|
|
|
|
void set_time(char *br, unsigned long cmd, unsigned long val)
|
|
{
|
|
struct ifreq ifr;
|
|
unsigned long args[4] = {cmd, val, 0, 0};
|
|
|
|
xstrncpy(ifr.ifr_name, br, IFNAMSIZ);
|
|
ifr.ifr_data = (char *)args;
|
|
xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
|
|
}
|
|
|
|
void br_set_ageing_time(char **argv)
|
|
{
|
|
struct timeval tv;
|
|
|
|
strtotimeval(&tv, argv[1]);
|
|
set_time(argv[0], BRCTL_SET_AGEING_TIME, tv_to_jify(&tv));
|
|
}
|
|
|
|
void br_set_fwd_delay(char **argv)
|
|
{
|
|
struct timeval tv;
|
|
|
|
strtotimeval(&tv, argv[1]);
|
|
set_time(argv[0], BRCTL_SET_BRIDGE_FORWARD_DELAY, tv_to_jify(&tv));
|
|
}
|
|
|
|
void br_set_hello_time(char **argv)
|
|
{
|
|
struct timeval tv;
|
|
|
|
strtotimeval(&tv, argv[1]);
|
|
set_time(argv[0], BRCTL_SET_BRIDGE_HELLO_TIME, tv_to_jify(&tv));
|
|
}
|
|
|
|
void br_set_max_age(char **argv)
|
|
{
|
|
struct timeval tv;
|
|
|
|
strtotimeval(&tv, argv[1]);
|
|
set_time(argv[0], BRCTL_SET_BRIDGE_MAX_AGE, tv_to_jify(&tv));
|
|
}
|
|
|
|
void br_set_bridge_prio(char **argv)
|
|
{
|
|
int prio;
|
|
|
|
if (sscanf(argv[1], "%i", &prio) != 1) error_exit("prio not proper");
|
|
set_time(argv[0], BRCTL_SET_BRIDGE_PRIORITY, prio);
|
|
}
|
|
|
|
void br_set_stp(char **argv)
|
|
{
|
|
int i;
|
|
struct stp {
|
|
char *n;
|
|
int set;
|
|
} ss[] = {{"1", 1}, {"yes", 1},{"on", 1},
|
|
{"0", 0}, {"no", 0},{"off", 0}};
|
|
|
|
for (i = 0; i < ARRAY_LEN(ss); i++) {
|
|
if (!strcmp(ss[i].n, argv[1])) break;
|
|
}
|
|
if (i >= ARRAY_LEN(ss)) error_exit("invalid stp state");
|
|
set_time(argv[0], BRCTL_SET_BRIDGE_STP_STATE, ss[i].set);
|
|
}
|
|
|
|
void set_cost_prio(char *br, char *port, unsigned long cmd, unsigned long val)
|
|
{
|
|
struct ifreq ifr;
|
|
int i, index, pindices[MAX_BRIDGES];
|
|
unsigned long args[4] = {cmd, 0, val, 0};
|
|
|
|
if (!(index = if_nametoindex(port))) error_exit("invalid port");
|
|
|
|
memset(pindices, 0, sizeof(pindices));
|
|
get_ports(br, pindices);
|
|
for (i = 0; i < MAX_BRIDGES; i++) {
|
|
if (index == pindices[i]) break;
|
|
}
|
|
if (i >= MAX_BRIDGES) error_exit("%s not in bridge", port);
|
|
args[1] = i;
|
|
xstrncpy(ifr.ifr_name, br, IFNAMSIZ);
|
|
ifr.ifr_data = (char *)args;
|
|
xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
|
|
}
|
|
|
|
void br_set_path_cost(char **argv)
|
|
{
|
|
int cost;
|
|
|
|
cost = atolx_range(argv[2], 0, INT_MAX);
|
|
set_cost_prio(argv[0], argv[1], BRCTL_SET_PATH_COST, cost);
|
|
}
|
|
|
|
void br_set_port_prio(char **argv)
|
|
{
|
|
int prio;
|
|
|
|
prio = atolx_range(argv[2], 0, INT_MAX);
|
|
set_cost_prio(argv[0], argv[1], BRCTL_SET_PORT_PRIORITY, prio);
|
|
|
|
}
|
|
|
|
void brctl_main(void)
|
|
{
|
|
int i;
|
|
struct cmds {
|
|
char *cmd;
|
|
int nargs;
|
|
void (*f)(char **argv);
|
|
} cc[] = {{"show", 0, br_show},
|
|
{"addbr", 1, br_addbr}, {"delbr", 1, br_delbr},
|
|
{"addif", 2, br_addif}, {"delif", 2, br_delif},
|
|
{"setageing", 2, br_set_ageing_time},
|
|
{"setfd", 2, br_set_fwd_delay},
|
|
{"sethello", 2, br_set_hello_time},
|
|
{"setmaxage", 2, br_set_max_age},
|
|
{"setpathcost", 3, br_set_path_cost},
|
|
{"setportprio", 3, br_set_port_prio},
|
|
{"setbridgeprio", 2, br_set_bridge_prio},
|
|
{"stp", 2, br_set_stp},
|
|
};
|
|
|
|
TT.sockfd = xsocket(AF_INET, SOCK_STREAM, 0);
|
|
while (*toys.optargs) {
|
|
for (i = 0; i < ARRAY_LEN(cc); i++) {
|
|
struct cmds *t = cc + i;
|
|
|
|
if (strcmp(t->cmd, *toys.optargs)) continue;
|
|
|
|
toys.optargs++, toys.optc--;
|
|
if (toys.optc < t->nargs) help_exit("check args");
|
|
t->f(toys.optargs);
|
|
toys.optargs += t->nargs;
|
|
toys.optc -= t->nargs;
|
|
break;
|
|
}
|
|
|
|
if (i == ARRAY_LEN(cc)) help_exit("invalid option '%s'", *toys.optargs);
|
|
}
|
|
xclose(TT.sockfd);
|
|
}
|