1/* brctl.c - ethernet bridge control
2 *
3 * Copyright 2013 Ashwini Kumar <ak.ashwini1981@gmail.com>
4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
5 *
6 * No Standard
7
8USE_BRCTL(NEWTOY(brctl, "<1", TOYFLAG_USR|TOYFLAG_SBIN))
9
10config BRCTL
11  bool "brctl"
12  default n
13  help
14    usage: brctl COMMAND [BRIDGE [INTERFACE]]
15
16    Manage ethernet bridges
17
18    Commands:
19    show                  Show a list of bridges
20    addbr BRIDGE          Create BRIDGE
21    delbr BRIDGE          Delete BRIDGE
22    addif BRIDGE IFACE    Add IFACE to BRIDGE
23    delif BRIDGE IFACE    Delete IFACE from BRIDGE
24    setageing BRIDGE TIME Set ageing time
25    setfd BRIDGE TIME     Set bridge forward delay
26    sethello BRIDGE TIME  Set hello time
27    setmaxage BRIDGE TIME Set max message age
28    setpathcost BRIDGE PORT COST   Set path cost
29    setportprio BRIDGE PORT PRIO   Set port priority
30    setbridgeprio BRIDGE PRIO      Set bridge priority
31    stp BRIDGE [1/yes/on|0/no/off] STP on/off
32*/
33
34#define FOR_brctl
35#include "toys.h"
36#include <linux/if_bridge.h>
37
38GLOBALS(
39    int sockfd;
40)
41#define MAX_BRIDGES 1024 //same is no of ports supported
42
43static void get_ports(char *bridge, int *indices)
44{
45  struct ifreq ifr;
46  int ifindices[MAX_BRIDGES];
47  unsigned long args[4] = { BRCTL_GET_PORT_LIST,
48    (unsigned long) ifindices, MAX_BRIDGES, 0 };
49
50  memset(ifindices, 0, MAX_BRIDGES);
51  args[1] = (unsigned long)ifindices;
52  xstrncpy(ifr.ifr_name, bridge, IFNAMSIZ);
53  ifr.ifr_data = (char *)args;
54  xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
55  if (indices) memcpy(indices, ifindices, sizeof(ifindices));
56}
57
58void get_br_info(char *bridge, struct __bridge_info *info)
59{
60  struct ifreq ifr;
61  unsigned long args[4] = { BRCTL_GET_BRIDGE_INFO,
62    (unsigned long) info, 0, 0 };
63
64  memset(info, 0, sizeof(*info));
65  xstrncpy(ifr.ifr_name, bridge, IFNAMSIZ);
66  ifr.ifr_data = (char *)args;
67
68  if (ioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr) < 0) {
69    perror_msg("%s: can't get info %s\n", bridge, strerror(errno));
70    return;
71  }
72}
73
74void br_show(char **argv)
75{
76  struct __bridge_info info;
77  int num, cnt, i, j, ifindices[MAX_BRIDGES], pindices[MAX_BRIDGES];
78  unsigned long args[4] = { BRCTL_GET_BRIDGES,
79    (unsigned long)ifindices, MAX_BRIDGES,0 };
80  char br[IF_NAMESIZE], ifn[IF_NAMESIZE];
81
82  num = ioctl(TT.sockfd, SIOCGIFBR, args); //ret is num of bridges found
83  if (num < 0) error_exit("get bridges fail");
84  printf("bridge name\tbridge id\t\tSTP enabled\tinterfaces\n");
85
86  for (i = 0; i < num; i++) {
87    unsigned char *id;
88
89    if (!if_indextoname(ifindices[i], br)) perror_exit("interface not found");
90    get_br_info(br, &info);
91    id = (unsigned char*)&(info.bridge_id);
92    printf("%s\t\t",br);
93    printf("%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x", id[0], id[1],
94        id[2], id[3], id[4], id[5], id[6], id[7]);
95    printf("\t%s\t\t",(info.stp_enabled)?"yes" : "no");
96
97    memset(pindices, 0, sizeof(pindices));
98    get_ports(br, pindices);
99    for (j = 0, cnt = 0; j < MAX_BRIDGES; j++) {
100      if (!pindices[j]) continue;
101      if (!if_indextoname(pindices[j], ifn)) {
102        error_msg("no name for index :%d", pindices[j]);
103        continue;
104      }
105      if (cnt) printf("\n\t\t\t\t\t\t\t");
106      printf("%s", ifn);
107      cnt++;
108    }
109    xputc('\n');
110  }
111}
112
113void br_addbr(char **argv)
114{
115  char br[IFNAMSIZ];
116  unsigned long args[4] = {BRCTL_ADD_BRIDGE, (unsigned long) br, 0, 0};
117
118#ifdef SIOCBRADDBR
119  xioctl(TT.sockfd, SIOCBRADDBR, argv[0]);
120#else
121  xstrncpy(br, argv[0], IFNAMSIZ);
122  xioctl(TT.sockfd, SIOCSIFBR, args);
123#endif
124}
125
126void br_delbr(char **argv)
127{
128  char br[IFNAMSIZ];
129  unsigned long args[4] = {BRCTL_DEL_BRIDGE, (unsigned long) br, 0, 0};
130
131#ifdef SIOCBRDELBR
132  xioctl(TT.sockfd, SIOCBRDELBR, argv[0]);
133#else
134  xstrncpy(br, argv[0], IFNAMSIZ);
135  xioctl(TT.sockfd, SIOCSIFBR, args);
136#endif
137}
138
139void br_addif(char **argv)
140{
141  int index;
142  struct ifreq ifr;
143  unsigned long args[4] = {BRCTL_ADD_IF, 0, 0, 0};
144
145  if (!(index = if_nametoindex(argv[1]))) perror_exit("interface %s", argv[1]);
146#ifdef SIOCBRADDIF
147  ifr.ifr_ifindex = index;
148  xioctl(TT.sockfd, SIOCBRADDIF, &ifr);
149#else
150  args[1] = index;
151  xstrncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
152  ifr.ifr_data = (char *)args;
153  xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
154#endif
155}
156
157void br_delif(char **argv)
158{
159  int index;
160  struct ifreq ifr;
161  unsigned long args[4] = {BRCTL_DEL_IF, 0, 0, 0};
162
163  if (!(index = if_nametoindex(argv[1]))) perror_exit("interface %s",argv[1]);
164#ifdef SIOCBRDELIF
165  ifr.ifr_ifindex = ifindex;
166  xioctl(TT.sockfd, SIOCBRDELIF, &ifr);
167#else
168  args[1] = index;
169  xstrncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
170  ifr.ifr_data = (char *)args;
171  xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
172#endif
173}
174
175static void strtotimeval(struct timeval *tv, char *time)
176{
177  double secs;
178
179  if (sscanf(time, "%lf", &secs) != 1) error_exit("time format not proper");
180  tv->tv_sec = secs;
181  tv->tv_usec = 1000000 * (secs - tv->tv_sec);
182}
183
184static unsigned long tv_to_jify(struct timeval *tv)
185{
186  unsigned long long jify;
187
188  jify = 1000000ULL * tv->tv_sec + tv->tv_usec;
189  return (jify/10000);
190}
191
192void set_time(char *br, unsigned long cmd, unsigned long val)
193{
194  struct ifreq ifr;
195  unsigned long args[4] = {cmd, val, 0, 0};
196
197  xstrncpy(ifr.ifr_name, br, IFNAMSIZ);
198  ifr.ifr_data = (char *)args;
199  xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
200}
201
202void br_set_ageing_time(char **argv)
203{
204  struct timeval tv;
205
206  strtotimeval(&tv, argv[1]);
207  set_time(argv[0], BRCTL_SET_AGEING_TIME, tv_to_jify(&tv));
208}
209
210void br_set_fwd_delay(char **argv)
211{
212  struct timeval tv;
213
214  strtotimeval(&tv, argv[1]);
215  set_time(argv[0], BRCTL_SET_BRIDGE_FORWARD_DELAY, tv_to_jify(&tv));
216}
217
218void br_set_hello_time(char **argv)
219{
220  struct timeval tv;
221
222  strtotimeval(&tv, argv[1]);
223  set_time(argv[0], BRCTL_SET_BRIDGE_HELLO_TIME, tv_to_jify(&tv));
224}
225
226void br_set_max_age(char **argv)
227{
228  struct timeval tv;
229
230  strtotimeval(&tv, argv[1]);
231  set_time(argv[0], BRCTL_SET_BRIDGE_MAX_AGE, tv_to_jify(&tv));
232}
233
234void br_set_bridge_prio(char **argv)
235{
236  int prio;
237
238  if (sscanf(argv[1], "%i", &prio) != 1) error_exit("prio not proper");
239  set_time(argv[0], BRCTL_SET_BRIDGE_PRIORITY, prio);
240}
241
242void br_set_stp(char **argv)
243{
244  int i;
245  struct stp {
246    char *n;
247    int set;
248  } ss[] = {{"1", 1}, {"yes", 1},{"on", 1},
249    {"0", 0}, {"no", 0},{"off", 0}};
250
251  for (i = 0; i < ARRAY_LEN(ss); i++) {
252    if (!strcmp(ss[i].n, argv[1])) break;
253  }
254  if (i >= ARRAY_LEN(ss)) error_exit("invalid stp state");
255  set_time(argv[0], BRCTL_SET_BRIDGE_STP_STATE, ss[i].set);
256}
257
258void set_cost_prio(char *br, char *port, unsigned long cmd, unsigned long val)
259{
260  struct ifreq ifr;
261  int i, index, pindices[MAX_BRIDGES];
262  unsigned long args[4] = {cmd, 0, val, 0};
263
264  if (!(index = if_nametoindex(port))) error_exit("invalid port");
265
266  memset(pindices, 0, sizeof(pindices));
267  get_ports(br, pindices);
268  for (i = 0; i < MAX_BRIDGES; i++) {
269    if (index == pindices[i]) break;
270  }
271  if (i >= MAX_BRIDGES) error_exit("%s not in bridge", port);
272  args[1] = i;
273  xstrncpy(ifr.ifr_name, br, IFNAMSIZ);
274  ifr.ifr_data = (char *)args;
275  xioctl(TT.sockfd, SIOCDEVPRIVATE, &ifr);
276}
277
278void br_set_path_cost(char **argv)
279{
280  int cost;
281
282  cost = atolx_range(argv[2], 0, INT_MAX);
283  set_cost_prio(argv[0], argv[1], BRCTL_SET_PATH_COST, cost);
284}
285
286void br_set_port_prio(char **argv)
287{
288  int prio;
289
290  prio = atolx_range(argv[2], 0, INT_MAX);
291  set_cost_prio(argv[0], argv[1], BRCTL_SET_PORT_PRIORITY, prio);
292
293}
294
295void brctl_main(void)
296{
297  int i;
298  struct cmds {
299    char *cmd;
300    int nargs;
301    void (*f)(char **argv);
302  } cc[] = {{"show", 0, br_show},
303    {"addbr", 1, br_addbr}, {"delbr", 1, br_delbr},
304    {"addif", 2, br_addif}, {"delif", 2, br_delif},
305    {"setageing", 2, br_set_ageing_time},
306    {"setfd", 2, br_set_fwd_delay},
307    {"sethello", 2, br_set_hello_time},
308    {"setmaxage", 2, br_set_max_age},
309    {"setpathcost", 3, br_set_path_cost},
310    {"setportprio", 3, br_set_port_prio},
311    {"setbridgeprio", 2, br_set_bridge_prio},
312    {"stp", 2, br_set_stp},
313  };
314
315  TT.sockfd = xsocket(AF_INET, SOCK_STREAM, 0);
316  while (*toys.optargs) {
317    for (i = 0; i < ARRAY_LEN(cc); i++) {
318      struct cmds *t = cc + i;
319
320      if (strcmp(t->cmd, *toys.optargs)) continue;
321
322      toys.optargs++, toys.optc--;
323      if (toys.optc < t->nargs) help_exit("check args");
324      t->f(toys.optargs);
325      toys.optargs += t->nargs;
326      toys.optc -= t->nargs;
327      break;
328    }
329
330    if (i == ARRAY_LEN(cc)) help_exit("invalid option '%s'", *toys.optargs);
331  }
332  xclose(TT.sockfd);
333}
334