80f93635b5623128e76fad22d47b3d467c148529
[bcm963xx.git] / userapps / opensource / bridge-utils / libbridge / libbridge_devif.c
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
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <sys/fcntl.h>
26
27 #include "libbridge.h"
28 #include "libbridge_private.h"
29
30 #ifdef HAVE_LIBSYSFS
31 /* Given two two character "0a" convert it to a byte */
32 static unsigned char getoctet(const char *cp) 
33 {
34         char t[3] = { cp[0], cp[1], 0 };
35         return strtoul(t, NULL, 16);
36 }
37
38 static struct sysfs_directory *bridge_sysfs_directory(const char *devname,
39                                                       const char *subname)
40 {
41         struct sysfs_directory *sdir;
42         struct sysfs_class_device *dev;
43         char path[SYSFS_PATH_MAX];
44
45         if (!devname)
46                 return NULL;
47
48         if (!br_class_net) {
49                 dprintf("can't find class_net\n");
50                 return NULL;
51         }
52
53         dev = sysfs_get_class_device(br_class_net, (char *) devname);
54         if (!dev) {
55                 dprintf("can't find device %s in %s\n", devname, br_class_net->path);
56                 return NULL;
57         }
58
59         snprintf(path, SYSFS_PATH_MAX, "%s/%s", dev->path, subname);
60         sdir = sysfs_open_directory(path);
61         if (!sdir)
62                 dprintf("can't open directory: %s\n", path);
63         return sdir;
64 }
65
66 static void fetch_id(struct sysfs_directory *sdir, const char *name, 
67                      struct bridge_id *id)
68 {
69         struct sysfs_attribute *attr;
70
71         memset(id, 0, sizeof(id));
72         attr = sysfs_get_directory_attribute(sdir, (char *) name);
73         if (!attr) {
74                 dprintf("Can't find attribute %s/%s\n", sdir->path, name);
75                 return;
76         }
77
78         if (strlen(attr->value) < 17) 
79                 dprintf("Bad format for %s: '%s'\n", name, attr->value);
80         else {
81                 const char *cp = attr->value;
82                 id->prio[0] = getoctet(cp); cp += 2;
83                 id->prio[1] = getoctet(cp); cp += 3;
84                 id->addr[0] = getoctet(cp); cp += 2;
85                 id->addr[1] = getoctet(cp); cp += 2;
86                 id->addr[2] = getoctet(cp); cp += 2;
87                 id->addr[3] = getoctet(cp); cp += 2;
88                 id->addr[4] = getoctet(cp); cp += 2;
89                 id->addr[5] = getoctet(cp);
90         }
91 }
92
93 /* Get a time value out of sysfs */
94 static void fetch_tv(struct sysfs_directory *sdir, const char *name,
95                      struct timeval *tv)
96 {
97         struct sysfs_attribute *attr
98                 = sysfs_get_directory_attribute(sdir, (char *) name);
99
100         if (!attr) {
101                 dprintf("Can't find attribute %s/%s\n", sdir->path, name);
102                 memset(tv, 0, sizeof(tv));
103                 return;
104         }
105
106         __jiffies_to_tv(tv, strtoul(attr->value, NULL, 0));
107 }
108
109 /* Fetch an integer attribute out of sysfs. */
110 static int fetch_int(struct sysfs_directory *sdir, const char *name)
111 {
112         struct sysfs_attribute *attr
113                 = sysfs_get_directory_attribute(sdir, (char *) name);
114         int val = 0;
115
116         if (!attr) 
117                 dprintf("Can't find attribute %s/%s\n", sdir->path, name);
118         else 
119                 val = strtol(attr->value, NULL, 0);
120         return val;
121 }
122 #endif
123
124 /*
125  * Convert device name to an index in the list of ports in bridge.
126  *
127  * Old API does bridge operations as if ports were an array
128  * inside bridge structure.
129  */
130 static int get_portno(const char *brname, const char *ifname)
131 {
132         int i;
133         int ifindex = if_nametoindex(ifname);
134         int ifindices[MAX_PORTS];
135         unsigned long args[4] = { BRCTL_GET_PORT_LIST,
136                                   (unsigned long)ifindices, MAX_PORTS, 0 };
137         struct ifreq ifr;
138
139         if (ifindex <= 0)
140                 goto error;
141
142         memset(ifindices, 0, sizeof(ifindices));
143         strncpy(ifr.ifr_name, brname, IFNAMSIZ);
144         ifr.ifr_data = (char *) &args;
145
146         if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) {
147                 dprintf("get_portno: get ports of %s failed: %s\n", 
148                         brname, strerror(errno));
149                 goto error;
150         }
151
152         for (i = 0; i < MAX_PORTS; i++) {
153                 if (ifindices[i] == ifindex)
154                         return i;
155         }
156
157         dprintf("%s is not a in bridge %s\n", ifname, brname);
158  error:
159         return -1;
160 }
161
162 /* get information via ioctl */
163 static int old_get_bridge_info(const char *bridge, struct bridge_info *info)
164 {
165         struct ifreq ifr;
166         struct __bridge_info i;
167         unsigned long args[4] = { BRCTL_GET_BRIDGE_INFO,
168                                   (unsigned long) &i, 0, 0 };
169
170         memset(info, 0, sizeof(*info));
171         strncpy(ifr.ifr_name, bridge, IFNAMSIZ);
172         ifr.ifr_data = (char *) &args;
173
174         if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) {
175                 dprintf("%s: can't get info %s\n",
176                         bridge, strerror(errno));
177                 return errno;
178         }
179
180         memcpy(&info->designated_root, &i.designated_root, 8);
181         memcpy(&info->bridge_id, &i.bridge_id, 8);
182         info->root_path_cost = i.root_path_cost;
183         info->root_port = i.root_port;
184         info->topology_change = i.topology_change;
185         info->topology_change_detected = i.topology_change_detected;
186         info->stp_enabled = i.stp_enabled;
187         __jiffies_to_tv(&info->max_age, i.max_age);
188         __jiffies_to_tv(&info->hello_time, i.hello_time);
189         __jiffies_to_tv(&info->forward_delay, i.forward_delay);
190         __jiffies_to_tv(&info->bridge_max_age, i.bridge_max_age);
191         __jiffies_to_tv(&info->bridge_hello_time, i.bridge_hello_time);
192         __jiffies_to_tv(&info->bridge_forward_delay, i.bridge_forward_delay);
193         __jiffies_to_tv(&info->ageing_time, i.ageing_time);
194         __jiffies_to_tv(&info->hello_timer_value, i.hello_timer_value);
195         __jiffies_to_tv(&info->tcn_timer_value, i.tcn_timer_value);
196         __jiffies_to_tv(&info->topology_change_timer_value, 
197                         i.topology_change_timer_value);
198         __jiffies_to_tv(&info->gc_timer_value, i.gc_timer_value);
199
200         return 0;
201 }
202
203 /*
204  * Get bridge parameters using either sysfs or old
205  * ioctl.
206  */
207 int br_get_bridge_info(const char *bridge, struct bridge_info *info)
208 {
209 #ifndef HAVE_LIBSYSFS
210         return old_get_bridge_info(bridge, info);
211 #else
212         struct sysfs_directory *sdir;
213
214         sdir = bridge_sysfs_directory(bridge, SYSFS_BRIDGE_ATTR);
215         if (!sdir) 
216                 return old_get_bridge_info(bridge,info);
217
218         memset(info, 0, sizeof(*info));
219         fetch_id(sdir, "root_id", &info->designated_root);
220         fetch_id(sdir, "bridge_id", &info->bridge_id);
221         info->root_path_cost = fetch_int(sdir, "root_path_cost");
222         fetch_tv(sdir, "max_age", &info->max_age);
223         fetch_tv(sdir, "hello_time", &info->hello_time);
224         fetch_tv(sdir, "forward_delay", &info->forward_delay);
225         fetch_tv(sdir, "max_age", &info->bridge_max_age);
226         fetch_tv(sdir, "hello_time", &info->bridge_hello_time);
227         fetch_tv(sdir, "forward_delay", &info->bridge_forward_delay);
228         fetch_tv(sdir, "ageing_time", &info->ageing_time);
229         fetch_tv(sdir, "hello_timer", &info->hello_timer_value);
230         fetch_tv(sdir, "tcn_timer", &info->tcn_timer_value);
231         fetch_tv(sdir, "topology_change_timer", 
232                  &info->topology_change_timer_value);;
233         fetch_tv(sdir, "gc_timer", &info->gc_timer_value);
234
235         info->root_port = fetch_int(sdir, "root_port");
236         info->stp_enabled = fetch_int(sdir, "stp_state");
237         info->topology_change = fetch_int(sdir, "topology_change");
238         info->topology_change_detected = fetch_int(sdir, "topology_change_detected");
239         sysfs_close_directory(sdir);
240
241         return 0;
242 #endif
243 }
244
245 static int old_get_port_info(const char *brname, const char *port,
246                              struct port_info *info)
247 {
248         struct __port_info i;
249         int index;
250
251         memset(info, 0, sizeof(*info));
252
253         index = get_portno(brname, port);
254         if (index < 0)
255                 return errno;
256         
257         else {
258                 struct ifreq ifr;
259                 unsigned long args[4] = { BRCTL_GET_PORT_INFO,
260                                            (unsigned long) &i, index, 0 };
261         
262                 strncpy(ifr.ifr_name, brname, IFNAMSIZ);
263                 ifr.ifr_data = (char *) &args;
264                 
265                 if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) {
266                         dprintf("old can't get port %s(%d) info %s\n",
267                                 brname, index, strerror(errno));
268                         return errno;
269                 }
270         }
271
272         info->port_no = index;
273         memcpy(&info->designated_root, &i.designated_root, 8);
274         memcpy(&info->designated_bridge, &i.designated_bridge, 8);
275         info->port_id = i.port_id;
276         info->designated_port = i.designated_port;
277         info->path_cost = i.path_cost;
278         info->designated_cost = i.designated_cost;
279         info->state = i.state;
280         info->top_change_ack = i.top_change_ack;
281         info->config_pending = i.config_pending;
282         __jiffies_to_tv(&info->message_age_timer_value, 
283                         i.message_age_timer_value);
284         __jiffies_to_tv(&info->forward_delay_timer_value, 
285                         i.forward_delay_timer_value);
286         __jiffies_to_tv(&info->hold_timer_value, i.hold_timer_value);
287         return 0;
288 }
289
290 /*
291  * Get information about port on bridge.
292  */
293 int br_get_port_info(const char *brname, const char *port, 
294                      struct port_info *info)
295 {
296 #ifndef HAVE_LIBSYSFS
297         return old_get_port_info(brname, port, info);
298 #else
299         struct sysfs_directory *sdir
300                 = bridge_sysfs_directory(port, SYSFS_BRIDGE_PORT_ATTR);
301
302         if (!sdir) 
303                 return old_get_port_info(brname, port, info);
304
305         memset(info, 0, sizeof(*info));
306         fetch_id(sdir, "designated_root", &info->designated_root);
307         fetch_id(sdir, "designated_bridge", &info->designated_bridge);
308         info->port_no = fetch_int(sdir, "port_no");
309         info->port_id = fetch_int(sdir, "port_id");
310         info->designated_port = fetch_int(sdir, "designated_port");
311         info->path_cost = fetch_int(sdir, "path_cost");
312         info->designated_cost = fetch_int(sdir, "designated_cost");
313         info->state = fetch_int(sdir, "state");
314         info->top_change_ack = fetch_int(sdir, "change_ack");
315         info->config_pending = fetch_int(sdir, "config_pending");
316         fetch_tv(sdir, "message_age_timer", 
317                  &info->message_age_timer_value);
318         fetch_tv(sdir, "forward_delay_timer",
319                  &info->forward_delay_timer_value);
320         fetch_tv(sdir, "hold_timer",
321                  &info->hold_timer_value);
322         sysfs_close_directory(sdir);
323
324         return 0;
325 #endif
326 }
327
328
329 static int br_set(const char *bridge, const char *name,
330                   unsigned long value, unsigned long oldcode)
331 {
332         int ret;
333 #ifdef HAVE_LIBSYSFS
334         struct sysfs_directory *sdir;
335         
336         sdir = bridge_sysfs_directory(bridge, SYSFS_BRIDGE_ATTR);
337         if (sdir) {
338                 struct sysfs_attribute *attr;
339                 char buf[32];
340                 sprintf(buf, "%ld", value);
341                 
342                 attr = sysfs_get_directory_attribute(sdir, (char *) name);
343                 if (attr) 
344                         ret = sysfs_write_attribute(attr, buf, strlen(buf));
345                 else {
346                         ret = -1;
347                         errno = EINVAL;
348                 }
349                 sysfs_close_directory(sdir);
350         } else 
351 #endif
352         {
353                 struct ifreq ifr;
354                 unsigned long args[4] = { oldcode, value, 0, 0 };
355                 
356                 strncpy(ifr.ifr_name, bridge, IFNAMSIZ);
357                 ifr.ifr_data = (char *) &args;
358                 ret = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr);
359         }
360
361         return ret < 0 ? errno : 0;
362 }
363
364 int br_set_bridge_forward_delay(const char *br, struct timeval *tv)
365 {
366         return br_set(br, "forward_delay", __tv_to_jiffies(tv),
367                       BRCTL_SET_BRIDGE_FORWARD_DELAY);
368 }
369
370 int br_set_bridge_hello_time(const char *br, struct timeval *tv)
371 {
372         return br_set(br, "hello_time", __tv_to_jiffies(tv),
373                       BRCTL_SET_BRIDGE_HELLO_TIME);
374 }
375
376 int br_set_bridge_max_age(const char *br, struct timeval *tv)
377 {
378         return br_set(br, "max_age", __tv_to_jiffies(tv),
379                       BRCTL_SET_BRIDGE_MAX_AGE);
380 }
381
382 int br_set_ageing_time(const char *br, struct timeval *tv)
383 {
384         return br_set(br, "ageing_time", __tv_to_jiffies(tv),
385                       BRCTL_SET_AGEING_TIME);
386 }
387
388 int br_set_stp_state(const char *br, int stp_state)
389 {
390         return br_set(br, "stp_state", stp_state, BRCTL_SET_BRIDGE_STP_STATE);
391 }
392
393 int br_set_bridge_priority(const char *br, int bridge_priority)
394 {
395         return br_set(br, "priority", bridge_priority, 
396                       BRCTL_SET_BRIDGE_PRIORITY);
397 }
398
399 static int port_set(const char *bridge, const char *ifname, 
400                     const char *name, unsigned long value, 
401                     unsigned long oldcode)
402 {
403         int ret, index;
404 #ifdef HAVE_LIBSYSFS
405         struct sysfs_directory *sdir;
406
407         sdir = bridge_sysfs_directory(ifname, SYSFS_BRIDGE_PORT_ATTR);
408         if (sdir) {
409                 struct sysfs_attribute *attr;
410                 char buf[32];
411
412                 sprintf(buf, "%ld", value);
413
414                 attr = sysfs_get_directory_attribute(sdir, (char *) name);
415                 if (attr) 
416                         ret = sysfs_write_attribute(attr, buf, strlen(buf));
417                 else {
418                         ret = -1; 
419                         errno = EINVAL;
420                 }
421                 sysfs_close_directory(sdir);
422         } else
423 #endif
424         if ( (index = get_portno(bridge, ifname)) < 0)
425                 ret = index;
426
427         else {
428                 struct ifreq ifr;
429                 unsigned long args[4] = { oldcode, index, value, 0 };
430
431                 strncpy(ifr.ifr_name, bridge, IFNAMSIZ);
432                 ifr.ifr_data = (char *) &args;
433                 ret = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr);
434         }
435
436         return ret < 0 ? errno : 0;
437 }
438
439 int br_set_port_priority(const char *bridge, const char *port, int priority)
440 {
441         return port_set(bridge, port, "priority", priority, BRCTL_SET_PORT_PRIORITY);
442 }
443
444 // brcm begin
445 #define BRCTL_SET_PORT_SNOOPING 21
446 #define BRCTL_CLEAR_PORT_SNOOPING 22
447 #define BRCTL_ENABLE_SNOOPING 23
448 #define BRCTL_SHOW_SNOOPING 24
449
450 int br_set_port_snooping(const char *br, const char *port, const char *addr)
451 {
452         unsigned int iaddr[6];
453         unsigned char dest[6];
454         int i = 0;
455         
456         sscanf(addr, "%02x%02x%02x%02x%02x%02x", iaddr, iaddr+1, iaddr+2, iaddr+3, iaddr+4, iaddr+5);
457         for (i=0; i < 6; i++)
458             dest[i] = iaddr[i];
459         sscanf(addr+13, "%02x%02x%02x%02x%02x%02x", iaddr, iaddr+1, iaddr+2, iaddr+3, iaddr+4, iaddr+5);
460         for (i=0; i < 6; i++)
461             dest[i+6] = iaddr[i];
462         return port_set(br, port, "port_snooping", dest, BRCTL_SET_PORT_SNOOPING);
463 }
464
465 int br_clear_port_snooping(const char *br, const char *port, const char *addr)
466 {
467         unsigned int iaddr[6];
468         unsigned char dest[6];
469         int i = 0;
470         
471         sscanf(addr, "%02x%02x%02x%02x%02x%02x", iaddr, iaddr+1, iaddr+2, iaddr+3, iaddr+4, iaddr+5);
472         for (i=0; i < 6; i++)
473             dest[i] = iaddr[i];
474         sscanf(addr+13, "%02x%02x%02x%02x%02x%02x", iaddr, iaddr+1, iaddr+2, iaddr+3, iaddr+4, iaddr+5);
475         for (i=0; i < 6; i++)
476             dest[i+6] = iaddr[i];
477         return port_set(br, port, "port_snooping", dest, BRCTL_CLEAR_PORT_SNOOPING);
478 }
479
480 int br_show_port_snooping(const char *brname)
481 {
482         int ret;
483
484         {
485                 char _br[IFNAMSIZ];
486                 unsigned long arg[3] 
487                         = { BRCTL_SHOW_SNOOPING, (unsigned long) _br };
488
489                 strncpy(_br, brname, IFNAMSIZ);
490                 ret = ioctl(br_socket_fd, SIOCSIFBR, arg);
491         } 
492
493         return ret < 0 ? errno : 0;
494 }
495
496 int br_enable_port_snooping(int enable)
497 {
498         int ret;
499
500         {
501                 unsigned long arg[3] 
502                         = { BRCTL_ENABLE_SNOOPING, 0, 0};
503
504                 arg[1] = enable;
505                 ret = ioctl(br_socket_fd, SIOCSIFBR, arg);
506         } 
507
508         return ret < 0 ? errno : 0;
509 }
510 // brcm end
511
512 int br_set_path_cost(const char *bridge, const char *port, int cost)
513 {
514         return port_set(bridge, port, "path_cost", cost, BRCTL_SET_PATH_COST);
515 }
516
517 static inline void __copy_fdb(struct fdb_entry *ent, 
518                               const struct __fdb_entry *f)
519 {
520         memcpy(ent->mac_addr, f->mac_addr, 6);
521         ent->port_no = f->port_no;
522         ent->is_local = f->is_local;
523         __jiffies_to_tv(&ent->ageing_timer_value, f->ageing_timer_value);
524 }
525
526 int br_read_fdb(const char *bridge, struct fdb_entry *fdbs, 
527                 unsigned long offset, int num)
528 {
529         int i, fd = -1, n;
530         struct __fdb_entry fe[num];
531 #ifdef HAVE_LIBSYSFS
532         struct sysfs_class_device *dev;
533         
534         /* open /sys/class/net/brXXX/brforward */
535         if (br_class_net &&
536             (dev = sysfs_get_class_device(br_class_net, (char *) bridge))) {
537                 char path[SYSFS_PATH_MAX];
538
539                 snprintf(path, SYSFS_PATH_MAX, "%s/%s", dev->path, 
540                          SYSFS_BRIDGE_FDB);
541                 fd = open(path, O_RDONLY, 0);
542         }
543
544         if (fd != -1) {
545                 /* read records from file */
546                 lseek(fd, offset*sizeof(struct __fdb_entry), SEEK_SET);
547                 n = read(fd, fe, num*sizeof(struct __fdb_entry));
548                 if (n > 0)
549                         n /= sizeof(struct __fdb_entry);
550         } else
551 #endif
552         {
553                 /* old kernel, use ioctl */
554                 unsigned long args[4] = { BRCTL_GET_FDB_ENTRIES,
555                                           (unsigned long) fe,
556                                           num, offset };
557                 struct ifreq ifr;
558                 int retries = 0;
559
560                 strncpy(ifr.ifr_name, bridge, IFNAMSIZ);
561                 ifr.ifr_data = (char *) args;
562
563         retry:
564                 n = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr);
565
566                 /* table can change during ioctl processing */
567                 if (n < 0 && errno == EAGAIN && ++retries < 10) {
568                         sleep(0);
569                         goto retry;
570                 }
571         }
572
573         for (i = 0; i < n; i++) 
574                 __copy_fdb(fdbs+i, fe+i);
575
576         if (fd > 0)
577                 close(fd);
578         
579         return n;
580 }
581
582 //michaelc
583 int br_addstaticmac_fdb(char *brname, char *ifname, char *macp)
584 {
585 #define BRCTL_ADD_STATIC_MAC 19
586         char *mac[6];
587         int index, ret, addResult;
588         memcpy(mac,macp,6);
589
590         if ( (index = get_portno(brname, ifname)) < 0)
591                 ret = index;
592         else {
593                 struct ifreq ifr;
594                 unsigned long args[4] = { BRCTL_ADD_STATIC_MAC, index, mac, 0 };
595                 strncpy(ifr.ifr_name, brname, IFNAMSIZ);
596                 ifr.ifr_data = (char *) &args;
597                 ret = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr);
598         }
599
600         return ret < 0 ? errno : 0;
601 }
602
603