diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2013-01-04 21:55:57 -0800 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2013-01-04 21:55:57 -0800 |
commit | 1736fa987eb6b66b6eaedbc1cd3df4f48b873721 (patch) | |
tree | fab9a0cf0d8a8bfbabf131ae0e3209fed30f98b0 /libbridge | |
parent | 96abbc571f9645e14ba9d9faf549ebcb4df40036 (diff) | |
parent | 0315bd568b78bcae16e368cfce9311891b05e686 (diff) | |
download | android_external_brctl-1736fa987eb6b66b6eaedbc1cd3df4f48b873721.tar.gz android_external_brctl-1736fa987eb6b66b6eaedbc1cd3df4f48b873721.tar.bz2 android_external_brctl-1736fa987eb6b66b6eaedbc1cd3df4f48b873721.zip |
Merge "Merge remote-tracking branch 'origin/caf/kernel-bridge-utils/master'"
Diffstat (limited to 'libbridge')
-rw-r--r-- | libbridge/.gitignore | 2 | ||||
-rw-r--r-- | libbridge/Makefile.in | 41 | ||||
-rw-r--r-- | libbridge/config.h.in | 79 | ||||
-rw-r--r-- | libbridge/libbridge.h | 119 | ||||
-rw-r--r-- | libbridge/libbridge_devif.c | 449 | ||||
-rw-r--r-- | libbridge/libbridge_if.c | 117 | ||||
-rw-r--r-- | libbridge/libbridge_init.c | 224 | ||||
-rw-r--r-- | libbridge/libbridge_misc.c | 50 | ||||
-rw-r--r-- | libbridge/libbridge_private.h | 56 |
9 files changed, 1137 insertions, 0 deletions
diff --git a/libbridge/.gitignore b/libbridge/.gitignore new file mode 100644 index 0000000..f611548 --- /dev/null +++ b/libbridge/.gitignore @@ -0,0 +1,2 @@ +config.h +stamp-h1 diff --git a/libbridge/Makefile.in b/libbridge/Makefile.in new file mode 100644 index 0000000..20512c4 --- /dev/null +++ b/libbridge/Makefile.in @@ -0,0 +1,41 @@ + +KERNEL_HEADERS=-I@KERNEL_HEADERS@ + +AR=ar +RANLIB=@RANLIB@ + +CC=@CC@ +CFLAGS = -Wall -g $(KERNEL_HEADERS) + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +includedir=@includedir@ +libdir=@libdir@ + +libbridge_SOURCES= \ + libbridge_devif.c \ + libbridge_if.c \ + libbridge_init.c \ + libbridge_misc.c + +libbridge_OBJECTS=$(libbridge_SOURCES:.c=.o) + +all: libbridge.a + +# At present there is no need for a bridge-utils-devel package +install: + + +clean: + rm -f *.o libbridge.a + +libbridge.a: $(libbridge_OBJECTS) + $(AR) rcs $@ $(libbridge_OBJECTS) + $(RANLIB) $@ + +%.o: %.c libbridge.h libbridge_private.h + $(CC) $(CFLAGS) $(INCLUDE) -c $< + +libbridge_compat.o: libbridge_compat.c if_index.c + $(CC) $(CFLAGS) -c libbridge_compat.c + diff --git a/libbridge/config.h.in b/libbridge/config.h.in new file mode 100644 index 0000000..22d1d7a --- /dev/null +++ b/libbridge/config.h.in @@ -0,0 +1,79 @@ +/* libbridge/config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the `gethostname' function. */ +#undef HAVE_GETHOSTNAME + +/* Define to 1 if you have the `if_indextoname' function. */ +#undef HAVE_IF_INDEXTONAME + +/* Define to 1 if you have the `if_nametoindex' function. */ +#undef HAVE_IF_NAMETOINDEX + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `socket' function. */ +#undef HAVE_SOCKET + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/time.h> header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the `uname' function. */ +#undef HAVE_UNAME + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ +#undef TIME_WITH_SYS_TIME + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const diff --git a/libbridge/libbridge.h b/libbridge/libbridge.h new file mode 100644 index 0000000..39964f2 --- /dev/null +++ b/libbridge/libbridge.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2000 Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _LIBBRIDGE_H +#define _LIBBRIDGE_H + +#include <sys/socket.h> +#include <linux/if.h> +#include <linux/if_bridge.h> + +/* defined in net/if.h but that conflicts with linux/if.h... */ +extern unsigned int if_nametoindex (const char *__ifname); +extern char *if_indextoname (unsigned int __ifindex, char *__ifname); + + +struct bridge_id +{ + unsigned char prio[2]; + unsigned char addr[6]; +}; + +struct bridge_info +{ + struct bridge_id designated_root; + struct bridge_id bridge_id; + unsigned root_path_cost; + struct timeval max_age; + struct timeval hello_time; + struct timeval forward_delay; + struct timeval bridge_max_age; + struct timeval bridge_hello_time; + struct timeval bridge_forward_delay; + u_int16_t root_port; + unsigned char stp_enabled; + unsigned char topology_change; + unsigned char topology_change_detected; + struct timeval ageing_time; + struct timeval hello_timer_value; + struct timeval tcn_timer_value; + struct timeval topology_change_timer_value; + struct timeval gc_timer_value; +}; + +struct fdb_entry +{ + u_int8_t mac_addr[6]; + u_int16_t port_no; + unsigned char is_local; + struct timeval ageing_timer_value; +}; + +struct port_info +{ + unsigned port_no; + struct bridge_id designated_root; + struct bridge_id designated_bridge; + u_int16_t port_id; + u_int16_t designated_port; + u_int8_t priority; + unsigned char top_change_ack; + unsigned char config_pending; + unsigned char state; + unsigned path_cost; + unsigned designated_cost; + struct timeval message_age_timer_value; + struct timeval forward_delay_timer_value; + struct timeval hold_timer_value; + unsigned char hairpin_mode; +}; + +extern int br_init(void); +extern int br_refresh(void); +extern void br_shutdown(void); + +extern int br_foreach_bridge(int (*iterator)(const char *brname, void *), + void *arg); +extern int br_foreach_port(const char *brname, + int (*iterator)(const char *brname, const char *port, + void *arg ), + void *arg); +extern const char *br_get_state_name(int state); + +extern int br_get_bridge_info(const char *br, struct bridge_info *info); +extern int br_get_port_info(const char *brname, const char *port, + struct port_info *info); +extern int br_add_bridge(const char *brname); +extern int br_del_bridge(const char *brname); +extern int br_add_interface(const char *br, const char *dev); +extern int br_del_interface(const char *br, const char *dev); +extern int br_set_bridge_forward_delay(const char *br, struct timeval *tv); +extern int br_set_bridge_hello_time(const char *br, struct timeval *tv); +extern int br_set_bridge_max_age(const char *br, struct timeval *tv); +extern int br_set_ageing_time(const char *br, struct timeval *tv); +extern int br_set_stp_state(const char *br, int stp_state); +extern int br_set_bridge_priority(const char *br, int bridge_priority); +extern int br_set_port_priority(const char *br, const char *p, + int port_priority); +extern int br_set_path_cost(const char *br, const char *p, + int path_cost); +extern int br_read_fdb(const char *br, struct fdb_entry *fdbs, + unsigned long skip, int num); +extern int br_set_hairpin_mode(const char *bridge, const char *dev, + int hairpin_mode); +#endif diff --git a/libbridge/libbridge_devif.c b/libbridge/libbridge_devif.c new file mode 100644 index 0000000..1e83925 --- /dev/null +++ b/libbridge/libbridge_devif.c @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2000 Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <dirent.h> +#include <sys/fcntl.h> + +#include "libbridge.h" +#include "libbridge_private.h" + +static FILE *fpopen(const char *dir, const char *name) +{ + char path[SYSFS_PATH_MAX]; + + snprintf(path, SYSFS_PATH_MAX, "%s/%s", dir, name); + return fopen(path, "r"); +} + +static void fetch_id(const char *dev, const char *name, struct bridge_id *id) +{ + FILE *f = fpopen(dev, name); + + if (!f) + fprintf(stderr, "%s: %s\n", dev, strerror(errno)); + else { + fscanf(f, "%2hhx%2hhx.%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx", + &id->prio[0], &id->prio[1], + &id->addr[0], &id->addr[1], &id->addr[2], + &id->addr[3], &id->addr[4], &id->addr[5]); + fclose(f); + } +} + +/* Fetch an integer attribute out of sysfs. */ +static int fetch_int(const char *dev, const char *name) +{ + FILE *f = fpopen(dev, name); + int value = -1; + + if (!f) + return 0; + + fscanf(f, "%i", &value); + fclose(f); + return value; +} + +/* Get a time value out of sysfs */ +static void fetch_tv(const char *dev, const char *name, + struct timeval *tv) +{ + __jiffies_to_tv(tv, fetch_int(dev, name)); +} + +/* + * Convert device name to an index in the list of ports in bridge. + * + * Old API does bridge operations as if ports were an array + * inside bridge structure. + */ +static int get_portno(const char *brname, const char *ifname) +{ + int i; + int ifindex = if_nametoindex(ifname); + int ifindices[MAX_PORTS]; + unsigned long args[4] = { BRCTL_GET_PORT_LIST, + (unsigned long)ifindices, MAX_PORTS, 0 }; + struct ifreq ifr; + + if (ifindex <= 0) + goto error; + + memset(ifindices, 0, sizeof(ifindices)); + strncpy(ifr.ifr_name, brname, IFNAMSIZ); + ifr.ifr_data = (char *) &args; + + if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) { + dprintf("get_portno: get ports of %s failed: %s\n", + brname, strerror(errno)); + goto error; + } + + for (i = 0; i < MAX_PORTS; i++) { + if (ifindices[i] == ifindex) + return i; + } + + dprintf("%s is not a in bridge %s\n", ifname, brname); + error: + return -1; +} + +/* get information via ioctl */ +static int old_get_bridge_info(const char *bridge, struct bridge_info *info) +{ + struct ifreq ifr; + struct __bridge_info i; + unsigned long args[4] = { BRCTL_GET_BRIDGE_INFO, + (unsigned long) &i, 0, 0 }; + + memset(info, 0, sizeof(*info)); + strncpy(ifr.ifr_name, bridge, IFNAMSIZ); + ifr.ifr_data = (char *) &args; + + if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) { + dprintf("%s: can't get info %s\n", + bridge, strerror(errno)); + return errno; + } + + memcpy(&info->designated_root, &i.designated_root, 8); + memcpy(&info->bridge_id, &i.bridge_id, 8); + info->root_path_cost = i.root_path_cost; + info->root_port = i.root_port; + info->topology_change = i.topology_change; + info->topology_change_detected = i.topology_change_detected; + info->stp_enabled = i.stp_enabled; + __jiffies_to_tv(&info->max_age, i.max_age); + __jiffies_to_tv(&info->hello_time, i.hello_time); + __jiffies_to_tv(&info->forward_delay, i.forward_delay); + __jiffies_to_tv(&info->bridge_max_age, i.bridge_max_age); + __jiffies_to_tv(&info->bridge_hello_time, i.bridge_hello_time); + __jiffies_to_tv(&info->bridge_forward_delay, i.bridge_forward_delay); + __jiffies_to_tv(&info->ageing_time, i.ageing_time); + __jiffies_to_tv(&info->hello_timer_value, i.hello_timer_value); + __jiffies_to_tv(&info->tcn_timer_value, i.tcn_timer_value); + __jiffies_to_tv(&info->topology_change_timer_value, + i.topology_change_timer_value); + __jiffies_to_tv(&info->gc_timer_value, i.gc_timer_value); + + return 0; +} + +/* + * Get bridge parameters using either sysfs or old + * ioctl. + */ +int br_get_bridge_info(const char *bridge, struct bridge_info *info) +{ + DIR *dir; + char path[SYSFS_PATH_MAX]; + + snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "%s/bridge", bridge); + dir = opendir(path); + if (dir == NULL) { + dprintf("path '%s' is not a directory\n", path); + goto fallback; + } + + memset(info, 0, sizeof(*info)); + fetch_id(path, "root_id", &info->designated_root); + fetch_id(path, "bridge_id", &info->bridge_id); + info->root_path_cost = fetch_int(path, "root_path_cost"); + fetch_tv(path, "max_age", &info->max_age); + fetch_tv(path, "hello_time", &info->hello_time); + fetch_tv(path, "forward_delay", &info->forward_delay); + fetch_tv(path, "max_age", &info->bridge_max_age); + fetch_tv(path, "hello_time", &info->bridge_hello_time); + fetch_tv(path, "forward_delay", &info->bridge_forward_delay); + fetch_tv(path, "ageing_time", &info->ageing_time); + fetch_tv(path, "hello_timer", &info->hello_timer_value); + fetch_tv(path, "tcn_timer", &info->tcn_timer_value); + fetch_tv(path, "topology_change_timer", + &info->topology_change_timer_value);; + fetch_tv(path, "gc_timer", &info->gc_timer_value); + + info->root_port = fetch_int(path, "root_port"); + info->stp_enabled = fetch_int(path, "stp_state"); + info->topology_change = fetch_int(path, "topology_change"); + info->topology_change_detected = fetch_int(path, "topology_change_detected"); + + closedir(dir); + return 0; + +fallback: + return old_get_bridge_info(bridge, info); +} + +static int old_get_port_info(const char *brname, const char *port, + struct port_info *info) +{ + struct __port_info i; + int index; + + memset(info, 0, sizeof(*info)); + + index = get_portno(brname, port); + if (index < 0) + return errno; + + else { + struct ifreq ifr; + unsigned long args[4] = { BRCTL_GET_PORT_INFO, + (unsigned long) &i, index, 0 }; + + strncpy(ifr.ifr_name, brname, IFNAMSIZ); + ifr.ifr_data = (char *) &args; + + if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) { + dprintf("old can't get port %s(%d) info %s\n", + brname, index, strerror(errno)); + return errno; + } + } + + info->port_no = index; + memcpy(&info->designated_root, &i.designated_root, 8); + memcpy(&info->designated_bridge, &i.designated_bridge, 8); + info->port_id = i.port_id; + info->designated_port = i.designated_port; + info->path_cost = i.path_cost; + info->designated_cost = i.designated_cost; + info->state = i.state; + info->top_change_ack = i.top_change_ack; + info->config_pending = i.config_pending; + __jiffies_to_tv(&info->message_age_timer_value, + i.message_age_timer_value); + __jiffies_to_tv(&info->forward_delay_timer_value, + i.forward_delay_timer_value); + __jiffies_to_tv(&info->hold_timer_value, i.hold_timer_value); + info->hairpin_mode = 0; + return 0; +} + +/* + * Get information about port on bridge. + */ +int br_get_port_info(const char *brname, const char *port, + struct port_info *info) +{ + DIR *d; + char path[SYSFS_PATH_MAX]; + + snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "%s/brport", port); + d = opendir(path); + if (!d) + goto fallback; + + memset(info, 0, sizeof(*info)); + + fetch_id(path, "designated_root", &info->designated_root); + fetch_id(path, "designated_bridge", &info->designated_bridge); + info->port_no = fetch_int(path, "port_no"); + info->port_id = fetch_int(path, "port_id"); + info->designated_port = fetch_int(path, "designated_port"); + info->path_cost = fetch_int(path, "path_cost"); + info->designated_cost = fetch_int(path, "designated_cost"); + info->state = fetch_int(path, "state"); + info->top_change_ack = fetch_int(path, "change_ack"); + info->config_pending = fetch_int(path, "config_pending"); + fetch_tv(path, "message_age_timer", &info->message_age_timer_value); + fetch_tv(path, "forward_delay_timer", &info->forward_delay_timer_value); + fetch_tv(path, "hold_timer", &info->hold_timer_value); + info->hairpin_mode = fetch_int(path, "hairpin_mode"); + + closedir(d); + + return 0; +fallback: + return old_get_port_info(brname, port, info); +} + +static int set_sysfs(const char *path, unsigned long value) +{ + int fd, ret = 0, cc; + char buf[32]; + + fd = open(path, O_WRONLY); + if (fd < 0) + return -1; + + cc = snprintf(buf, sizeof(buf), "%lu\n", value); + if (write(fd, buf, cc) < 0) + ret = -1; + close(fd); + + return ret; +} + + +static int br_set(const char *bridge, const char *name, + unsigned long value, unsigned long oldcode) +{ + int ret; + char path[SYSFS_PATH_MAX]; + + snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "%s/bridge/%s", + bridge, name); + + if ((ret = set_sysfs(path, value)) < 0) { + /* fallback to old ioctl */ + struct ifreq ifr; + unsigned long args[4] = { oldcode, value, 0, 0 }; + + strncpy(ifr.ifr_name, bridge, IFNAMSIZ); + ifr.ifr_data = (char *) &args; + ret = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr); + } + + return ret < 0 ? errno : 0; +} + +int br_set_bridge_forward_delay(const char *br, struct timeval *tv) +{ + return br_set(br, "forward_delay", __tv_to_jiffies(tv), + BRCTL_SET_BRIDGE_FORWARD_DELAY); +} + +int br_set_bridge_hello_time(const char *br, struct timeval *tv) +{ + return br_set(br, "hello_time", __tv_to_jiffies(tv), + BRCTL_SET_BRIDGE_HELLO_TIME); +} + +int br_set_bridge_max_age(const char *br, struct timeval *tv) +{ + return br_set(br, "max_age", __tv_to_jiffies(tv), + BRCTL_SET_BRIDGE_MAX_AGE); +} + +int br_set_ageing_time(const char *br, struct timeval *tv) +{ + return br_set(br, "ageing_time", __tv_to_jiffies(tv), + BRCTL_SET_AGEING_TIME); +} + +int br_set_stp_state(const char *br, int stp_state) +{ + return br_set(br, "stp_state", stp_state, BRCTL_SET_BRIDGE_STP_STATE); +} + +int br_set_bridge_priority(const char *br, int bridge_priority) +{ + return br_set(br, "priority", bridge_priority, + BRCTL_SET_BRIDGE_PRIORITY); +} + +static int port_set(const char *bridge, const char *ifname, + const char *name, unsigned long value, + unsigned long oldcode) +{ + int ret; + char path[SYSFS_PATH_MAX]; + + snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "%s/brport/%s", ifname, name); + + if ((ret = set_sysfs(path, value)) < 0) { + int index = get_portno(bridge, ifname); + + if (index < 0) + ret = index; + else { + struct ifreq ifr; + unsigned long args[4] = { oldcode, index, value, 0 }; + + strncpy(ifr.ifr_name, bridge, IFNAMSIZ); + ifr.ifr_data = (char *) &args; + ret = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr); + } + } + + return ret < 0 ? errno : 0; +} + +int br_set_port_priority(const char *bridge, const char *port, int priority) +{ + return port_set(bridge, port, "priority", priority, BRCTL_SET_PORT_PRIORITY); +} + +int br_set_path_cost(const char *bridge, const char *port, int cost) +{ + return port_set(bridge, port, "path_cost", cost, BRCTL_SET_PATH_COST); +} + +int br_set_hairpin_mode(const char *bridge, const char *port, int hairpin_mode) +{ + return port_set(bridge, port, "hairpin_mode", hairpin_mode, 0); +} + +static inline void __copy_fdb(struct fdb_entry *ent, + const struct __fdb_entry *f) +{ + memcpy(ent->mac_addr, f->mac_addr, 6); + ent->port_no = f->port_no; + ent->is_local = f->is_local; + __jiffies_to_tv(&ent->ageing_timer_value, f->ageing_timer_value); +} + +int br_read_fdb(const char *bridge, struct fdb_entry *fdbs, + unsigned long offset, int num) +{ + FILE *f; + int i, n; + struct __fdb_entry fe[num]; + char path[SYSFS_PATH_MAX]; + + /* open /sys/class/net/brXXX/brforward */ + snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "%s/brforward", bridge); + f = fopen(path, "r"); + if (f) { + fseek(f, offset*sizeof(struct __fdb_entry), SEEK_SET); + n = fread(fe, sizeof(struct __fdb_entry), num, f); + fclose(f); + } else { + /* old kernel, use ioctl */ + unsigned long args[4] = { BRCTL_GET_FDB_ENTRIES, + (unsigned long) fe, + num, offset }; + struct ifreq ifr; + int retries = 0; + + strncpy(ifr.ifr_name, bridge, IFNAMSIZ); + ifr.ifr_data = (char *) args; + + retry: + n = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr); + + /* table can change during ioctl processing */ + if (n < 0 && errno == EAGAIN && ++retries < 10) { + sleep(0); + goto retry; + } + } + + for (i = 0; i < n; i++) + __copy_fdb(fdbs+i, fe+i); + + return n; +} diff --git a/libbridge/libbridge_if.c b/libbridge/libbridge_if.c new file mode 100644 index 0000000..77d3f8a --- /dev/null +++ b/libbridge/libbridge_if.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2000 Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/fcntl.h> +#include <sys/ioctl.h> + +#include "libbridge.h" +#include "libbridge_private.h" + + +int br_add_bridge(const char *brname) +{ + int ret; + +#ifdef SIOCBRADDBR + ret = ioctl(br_socket_fd, SIOCBRADDBR, brname); + if (ret < 0) +#endif + { + char _br[IFNAMSIZ]; + unsigned long arg[3] + = { BRCTL_ADD_BRIDGE, (unsigned long) _br }; + + strncpy(_br, brname, IFNAMSIZ); + ret = ioctl(br_socket_fd, SIOCSIFBR, arg); + } + + return ret < 0 ? errno : 0; +} + +int br_del_bridge(const char *brname) +{ + int ret; + +#ifdef SIOCBRDELBR + ret = ioctl(br_socket_fd, SIOCBRDELBR, brname); + if (ret < 0) +#endif + { + char _br[IFNAMSIZ]; + unsigned long arg[3] + = { BRCTL_DEL_BRIDGE, (unsigned long) _br }; + + strncpy(_br, brname, IFNAMSIZ); + ret = ioctl(br_socket_fd, SIOCSIFBR, arg); + } + return ret < 0 ? errno : 0; +} + +int br_add_interface(const char *bridge, const char *dev) +{ + struct ifreq ifr; + int err; + int ifindex = if_nametoindex(dev); + + if (ifindex == 0) + return ENODEV; + + strncpy(ifr.ifr_name, bridge, IFNAMSIZ); +#ifdef SIOCBRADDIF + ifr.ifr_ifindex = ifindex; + err = ioctl(br_socket_fd, SIOCBRADDIF, &ifr); + if (err < 0) +#endif + { + unsigned long args[4] = { BRCTL_ADD_IF, ifindex, 0, 0 }; + + ifr.ifr_data = (char *) args; + err = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr); + } + + return err < 0 ? errno : 0; +} + +int br_del_interface(const char *bridge, const char *dev) +{ + struct ifreq ifr; + int err; + int ifindex = if_nametoindex(dev); + + if (ifindex == 0) + return ENODEV; + + strncpy(ifr.ifr_name, bridge, IFNAMSIZ); +#ifdef SIOCBRDELIF + ifr.ifr_ifindex = ifindex; + err = ioctl(br_socket_fd, SIOCBRDELIF, &ifr); + if (err < 0) +#endif + { + unsigned long args[4] = { BRCTL_DEL_IF, ifindex, 0, 0 }; + + ifr.ifr_data = (char *) args; + err = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr); + } + + return err < 0 ? errno : 0; +} diff --git a/libbridge/libbridge_init.c b/libbridge/libbridge_init.c new file mode 100644 index 0000000..f3a551e --- /dev/null +++ b/libbridge/libbridge_init.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2000 Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "libbridge.h" +#include "libbridge_private.h" + +int br_socket_fd = -1; + +int br_init(void) +{ + if ((br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) + return errno; + return 0; +} + +void br_shutdown(void) +{ + close(br_socket_fd); + br_socket_fd = -1; +} + +/* If /sys/class/net/XXX/bridge exists then it must be a bridge */ +static int isbridge(const struct dirent *entry) +{ + char path[SYSFS_PATH_MAX]; + struct stat st; + int ret, saved_errno; + + if (entry->d_name[0] == '.' + && (entry->d_name[1] == '\0' + || (entry->d_name[1] == '.' + && entry->d_name[2] == '\0'))) + return 0; + + snprintf(path, SYSFS_PATH_MAX, + SYSFS_CLASS_NET "%s/bridge", entry->d_name); + + /* Workaround old glibc breakage. + If errno is set, then it fails scandir! */ + saved_errno = errno; + ret = (stat(path, &st) == 0 && S_ISDIR(st.st_mode)); + errno = saved_errno; + + return ret; +} + +/* + * New interface uses sysfs to find bridges + */ +static int new_foreach_bridge(int (*iterator)(const char *name, void *), + void *arg) +{ + struct dirent **namelist; + int i, count = 0; + + count = scandir(SYSFS_CLASS_NET, &namelist, isbridge, alphasort); + if (count < 0) + return -1; + + for (i = 0; i < count; i++) { + if (iterator(namelist[i]->d_name, arg)) + break; + } + + for (i = 0; i < count; i++) + free(namelist[i]); + free(namelist); + + return count; +} + +/* + * Old interface uses ioctl + */ +static int old_foreach_bridge(int (*iterator)(const char *, void *), + void *iarg) +{ + int i, ret=0, num; + char ifname[IFNAMSIZ]; + int ifindices[MAX_BRIDGES]; + unsigned long args[3] = { BRCTL_GET_BRIDGES, + (unsigned long)ifindices, MAX_BRIDGES }; + + num = ioctl(br_socket_fd, SIOCGIFBR, args); + if (num < 0) { + dprintf("Get bridge indices failed: %s\n", + strerror(errno)); + return -errno; + } + + for (i = 0; i < num; i++) { + if (!if_indextoname(ifindices[i], ifname)) { + dprintf("get find name for ifindex %d\n", + ifindices[i]); + return -errno; + } + + ++ret; + if(iterator(ifname, iarg)) + break; + + } + + return ret; + +} + +/* + * Go over all bridges and call iterator function. + * if iterator returns non-zero then stop. + */ +int br_foreach_bridge(int (*iterator)(const char *, void *), + void *arg) +{ + int ret; + + ret = new_foreach_bridge(iterator, arg); + if (ret <= 0) + ret = old_foreach_bridge(iterator, arg); + + return ret; +} + +/* + * Only used if sysfs is not available. + */ +static int old_foreach_port(const char *brname, + int (*iterator)(const char *br, const char *port, + void *arg), + void *arg) +{ + int i, err, count; + struct ifreq ifr; + char ifname[IFNAMSIZ]; + int ifindices[MAX_PORTS]; + unsigned long args[4] = { BRCTL_GET_PORT_LIST, + (unsigned long)ifindices, MAX_PORTS, 0 }; + + memset(ifindices, 0, sizeof(ifindices)); + strncpy(ifr.ifr_name, brname, IFNAMSIZ); + ifr.ifr_data = (char *) &args; + + err = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr); + if (err < 0) { + dprintf("list ports for bridge:'%s' failed: %s\n", + brname, strerror(errno)); + return -errno; + } + + count = 0; + for (i = 0; i < MAX_PORTS; i++) { + if (!ifindices[i]) + continue; + + if (!if_indextoname(ifindices[i], ifname)) { + dprintf("can't find name for ifindex:%d\n", + ifindices[i]); + continue; + } + + ++count; + if (iterator(brname, ifname, arg)) + break; + } + + return count; +} + +/* + * Iterate over all ports in bridge (using sysfs). + */ +int br_foreach_port(const char *brname, + int (*iterator)(const char *br, const char *port, void *arg), + void *arg) +{ + int i, count; + struct dirent **namelist; + char path[SYSFS_PATH_MAX]; + + snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "%s/brif", brname); + count = scandir(path, &namelist, 0, alphasort); + if (count < 0) + return old_foreach_port(brname, iterator, arg); + + for (i = 0; i < count; i++) { + if (namelist[i]->d_name[0] == '.' + && (namelist[i]->d_name[1] == '\0' + || (namelist[i]->d_name[1] == '.' + && namelist[i]->d_name[2] == '\0'))) + continue; + + if (iterator(brname, namelist[i]->d_name, arg)) + break; + } + for (i = 0; i < count; i++) + free(namelist[i]); + free(namelist); + + return count; +} diff --git a/libbridge/libbridge_misc.c b/libbridge/libbridge_misc.c new file mode 100644 index 0000000..5791638 --- /dev/null +++ b/libbridge/libbridge_misc.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2000 Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <asm/param.h> +#include "libbridge.h" +#include "libbridge_private.h" + + +static const char *state_names[5] = { + [BR_STATE_DISABLED] = "disabled", + [BR_STATE_LISTENING] = "listening", + [BR_STATE_LEARNING] = "learning", + [BR_STATE_FORWARDING] = "forwarding", + [BR_STATE_BLOCKING] = "blocking", +}; + +const char *br_get_state_name(int state) +{ + if (state >= 0 && state <= 4) + return state_names[state]; + + return "<INVALID STATE>"; +} + +int __br_hz_internal; + +int __get_hz(void) +{ + const char * s = getenv("HZ"); + return s ? atoi(s) : HZ; +} diff --git a/libbridge/libbridge_private.h b/libbridge/libbridge_private.h new file mode 100644 index 0000000..99a511d --- /dev/null +++ b/libbridge/libbridge_private.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2000 Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _LIBBRIDGE_PRIVATE_H +#define _LIBBRIDGE_PRIVATE_H + +#include "config.h" + +#include <linux/sockios.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <linux/if_bridge.h> + +#define MAX_BRIDGES 1024 +#define MAX_PORTS 1024 + +#define SYSFS_CLASS_NET "/sys/class/net/" +#define SYSFS_PATH_MAX 256 + +#define dprintf(fmt,arg...) + +extern int br_socket_fd; + +static inline unsigned long __tv_to_jiffies(const struct timeval *tv) +{ + unsigned long long jif; + + jif = 1000000ULL * tv->tv_sec + tv->tv_usec; + + return jif/10000; +} + +static inline void __jiffies_to_tv(struct timeval *tv, unsigned long jiffies) +{ + unsigned long long tvusec; + + tvusec = 10000ULL*jiffies; + tv->tv_sec = tvusec/1000000; + tv->tv_usec = tvusec - 1000000 * tv->tv_sec; +} +#endif |