aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorshemminger <shemminger>2004-05-21 17:41:48 +0000
committershemminger <shemminger>2004-05-21 17:41:48 +0000
commit328f4711bbc369dcccf8f8cfba2adf5dd0f74479 (patch)
tree8dd07660534f32407d249d0259e2e8f3a62f2ae5
parent064717a4d7e47b47ba42e658abfd36acaa4a65d6 (diff)
downloadbridge-utils-328f4711bbc369dcccf8f8cfba2adf5dd0f74479.tar.gz
New version of command and library that use sysfs.
Update make system to build with or without sysfs.
-rw-r--r--Makefile.in4
-rw-r--r--brctl/Makefile.in2
-rw-r--r--brctl/brctl.c62
-rw-r--r--brctl/brctl.h19
-rw-r--r--brctl/brctl_cmd.c367
-rw-r--r--brctl/brctl_disp.c59
-rw-r--r--configure.in3
-rw-r--r--doc/Makefile2
-rw-r--r--libbridge/libbridge.h75
-rw-r--r--libbridge/libbridge_devif.c477
-rw-r--r--libbridge/libbridge_if.c66
-rw-r--r--libbridge/libbridge_init.c353
-rw-r--r--libbridge/libbridge_misc.c59
-rw-r--r--libbridge/libbridge_private.h52
14 files changed, 941 insertions, 659 deletions
diff --git a/Makefile.in b/Makefile.in
index be1d3ad..6028513 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -2,13 +2,14 @@
DESTDIR=
KERNEL_HEADERS=-I@KERNEL_HEADERS@
-INSTALL=install -s
+INSTALL=@INSTALL@
prefix=@prefix@
exec_prefix=@exec_prefix@
bindir=@bindir@
sbindir=@sbindir@
mandir=@mandir@
+distdir = $(PACKAGE)-$(VERSION)
SUBDIRS=libbridge brctl doc
@@ -31,3 +32,4 @@ maintainer-clean: distclean
install:
for x in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$x install; done
+
diff --git a/brctl/Makefile.in b/brctl/Makefile.in
index fd66905..f56bd9c 100644
--- a/brctl/Makefile.in
+++ b/brctl/Makefile.in
@@ -4,7 +4,7 @@ KERNEL_HEADERS=-I@KERNEL_HEADERS@
CC=@CC@
CFLAGS= -Wall -g @CFLAGS@
INCLUDE=-I../libbridge $(KERNEL_HEADERS)
-LIBS= -L ../libbridge -lbridge
+LIBS= -L ../libbridge -lbridge -lsysfs
prefix=@prefix@
exec_prefix=@exec_prefix@
diff --git a/brctl/brctl.c b/brctl/brctl.c
index 5ff9ab4..11dc1f3 100644
--- a/brctl/brctl.c
+++ b/brctl/brctl.c
@@ -21,45 +21,25 @@
#include <string.h>
#include <sys/errno.h>
#include "libbridge.h"
-#include "brctl.h"
-
-const char *version = "bridge-utils 1.0 (11-May-2004)";
+#include "config.h"
-const char *help_message =
-"commands:\n"
-"\taddbr\t\t<bridge>\t\tadd bridge\n"
-"\taddif\t\t<bridge> <device>\tadd interface to bridge\n"
-"\tdelbr\t\t<bridge>\t\tdelete bridge\n"
-"\tdelif\t\t<bridge> <device>\tdelete interface from bridge\n"
-"\tshow\t\t\t\t\tshow a list of bridges\n"
-"\tshowmacs\t<bridge>\t\tshow a list of mac addrs\n"
-"\tshowstp\t\t<bridge>\t\tshow bridge stp info\n"
-"\n"
-"\tsetageing\t<bridge> <time>\t\tset ageing time\n"
-"\tsetbridgeprio\t<bridge> <prio>\t\tset bridge priority\n"
-"\tsetfd\t\t<bridge> <time>\t\tset bridge forward delay\n"
-"\tsethello\t<bridge> <time>\t\tset hello time\n"
-"\tsetmaxage\t<bridge> <time>\t\tset max message age\n"
-"\tsetpathcost\t<bridge> <port> <cost>\tset path cost\n"
-"\tsetportprio\t<bridge> <port> <prio>\tset port priority\n"
-"\tstp\t\t<bridge> <state>\tturn stp on/off\n";
+#include "brctl.h"
-void help()
+static void help()
{
- fprintf(stderr, help_message);
+ printf("commands:\n");
+ command_helpall();
}
int main(int argc, char *argv[])
{
- int argindex;
- struct bridge *br;
- struct command *cmd;
+ const struct command *cmd;
if (argc < 2)
goto help;
if (strcmp(argv[1], "-V") == 0) {
- fprintf(stderr, "%s, %s\n", argv[0], version);
+ printf("%s, %s\n", PACKAGE, VERSION);
return 0;
}
@@ -69,37 +49,17 @@ int main(int argc, char *argv[])
return 1;
}
- if ((cmd = br_command_lookup(argv[1])) == NULL) {
+ if ((cmd = command_lookup(argv[1])) == NULL) {
fprintf(stderr, "never heard of command [%s]\n", argv[1]);
goto help;
}
-
- argindex = 2;
- br = NULL;
- if (cmd->needs_bridge_argument) {
- if (argindex >= argc) {
- fprintf(stderr, "this option requires a bridge name as argument\n");
- return 1;
- }
-
- br = br_find_bridge(argv[argindex]);
- if (br == NULL) {
- fprintf(stderr, "bridge %s doesn't exist?\n", argv[argindex]);
- return 1;
- }
-
- argindex++;
- }
-
- if (argc - argindex != cmd->num_string_arguments) {
+ if (argc < cmd->nargs + 2) {
fprintf(stderr, "incorrect number of arguments for command\n");
- return 1;
+ goto help;
}
- cmd->func(br, argv[argindex], argv[argindex+1]);
-
- return 0;
+ return cmd->func(++argv);
help:
help();
diff --git a/brctl/brctl.h b/brctl/brctl.h
index 901b6ce..8fdd9ef 100644
--- a/brctl/brctl.h
+++ b/brctl/brctl.h
@@ -21,18 +21,19 @@
struct command
{
- int needs_bridge_argument;
- int num_string_arguments;
- char *name;
- void (*func)(struct bridge *br, char *arg0, char *arg1);
+ int nargs;
+ const char *name;
+ int (*func)(char **argv);
+ const char *help;
};
-struct command *br_command_lookup(char *cmd);
+const struct command *command_lookup(const char *cmd);
+void command_help(const struct command *);
+void command_helpall(void);
+
void br_dump_bridge_id(const unsigned char *x);
void br_show_timer(const struct timeval *tv);
-void br_dump_interface_list(const struct bridge *br);
-void br_dump_port_info(const struct port *p);
-void br_dump_info(const struct bridge *br,
- const struct bridge_info *bri);
+void br_dump_interface_list(const char *br);
+void br_dump_info(const char *br, const struct bridge_info *bri);
#endif
diff --git a/brctl/brctl_cmd.c b/brctl/brctl_cmd.c
index 99d9725..f9da974 100644
--- a/brctl/brctl_cmd.c
+++ b/brctl/brctl_cmd.c
@@ -35,280 +35,291 @@ static int strtotimeval(struct timeval *tv, const char *time)
return 0;
}
-void br_cmd_addbr(struct bridge *br, char *brname, char *arg1)
+static int br_cmd_addbr(char** argv)
{
int err;
- if ((err = br_add_bridge(brname)) == 0)
- return;
+ switch (err = br_add_bridge(argv[1])) {
+ case 0:
+ return 0;
- switch (err) {
case EEXIST:
fprintf(stderr, "device %s already exists; can't create "
- "bridge with the same name\n", brname);
- break;
-
+ "bridge with the same name\n", argv[1]);
+ return 1;
default:
fprintf(stderr, "add bridge failed: %s\n",
strerror(err));
- break;
+ return 1;
}
}
-void br_cmd_delbr(struct bridge *br, char *brname, char *arg1)
+static int br_cmd_delbr(char** argv)
{
int err;
- if ((err = br_del_bridge(brname)) == 0)
- return;
+ switch (err = br_del_bridge(argv[1])){
+ case 0:
+ return 0;
- switch (err) {
case ENXIO:
fprintf(stderr, "bridge %s doesn't exist; can't delete it\n",
- brname);
- break;
+ argv[1]);
+ return 1;
case EBUSY:
fprintf(stderr, "bridge %s is still up; can't delete it\n",
- brname);
- break;
+ argv[1]);
+ return 1;
default:
fprintf(stderr, "can't delete bridge %s: %s\n",
- brname, strerror(err));
- break;
+ argv[1], strerror(err));
+ return 1;
}
}
-void br_cmd_addif(struct bridge *br, char *ifname, char *arg1)
+static int br_cmd_addif(char** argv)
{
int err;
- int ifindex;
+ int ifindex = if_nametoindex(argv[2]);
- ifindex = if_nametoindex(ifname);
if (!ifindex) {
- fprintf(stderr, "interface %s does not exist!\n", ifname);
- return;
+ fprintf(stderr, "interface %s does not exist!\n", argv[2]);
+ return 1;
}
- if ((err = br_add_interface(br, ifindex)) == 0)
- return;
+ switch (err = br_add_interface(argv[1], ifindex)) {
+ case 0:
+ return 0;
- switch (err) {
case EBUSY:
fprintf(stderr, "device %s is already a member of a bridge; "
- "can't enslave it to bridge %s.\n", ifname,
- br->ifname);
- break;
+ "can't enslave it to bridge %s.\n", argv[2],
+ argv[1]);
+ return 1;
case ELOOP:
fprintf(stderr, "device %s is a bridge device itself; "
"can't enslave a bridge device to a bridge device.\n",
- ifname);
- break;
+ argv[2]);
+ return 1;
default:
fprintf(stderr, "can't add %s to bridge %s: %s\n",
- ifname, br->ifname, strerror(err));
- break;
+ argv[2], argv[1], strerror(err));
+ return 1;
}
}
-void br_cmd_delif(struct bridge *br, char *ifname, char *arg1)
+static int br_cmd_delif(char** argv)
{
int err;
- int ifindex;
+ int ifindex = if_nametoindex(argv[2]);
- ifindex = if_nametoindex(ifname);
if (!ifindex) {
- fprintf(stderr, "interface %s does not exist!\n", ifname);
- return;
+ fprintf(stderr, "interface %s does not exist!\n", argv[2]);
+ return 1;
}
- if ((err = br_del_interface(br, ifindex)) == 0)
- return;
+ switch (err = br_del_interface(argv[1], ifindex)) {
+ case 0:
+ return 0;
- switch (err) {
case EINVAL:
fprintf(stderr, "device %s is not a slave of %s\n",
- ifname, br->ifname);
- break;
+ argv[2], argv[1]);
+ return 1;
default:
fprintf(stderr, "can't delete %s from %s: %s\n",
- ifname, br->ifname, strerror(err));
- break;
+ argv[2], argv[1], strerror(err));
+ return 1;
}
}
-void br_cmd_setageing(struct bridge *br, char *time, char *arg1)
+static int br_cmd_setageing(char** argv)
{
int err;
struct timeval tv;
-
- if (strtotimeval(&tv, time)) {
+
+ if (strtotimeval(&tv, argv[2])) {
fprintf(stderr, "bad ageing time value\n");
- return;
+ return 1;
}
- err = br_set_ageing_time(br, &tv);
+
+ err = br_set_ageing_time(argv[1], &tv);
if (err)
fprintf(stderr, "set ageing time failed: %s\n",
strerror(err));
+
+ return err != 0;
}
-void br_cmd_setbridgeprio(struct bridge *br, char *_prio, char *arg1)
+static int br_cmd_setbridgeprio(char** argv)
{
int prio;
int err;
- if (sscanf(_prio, "%i", &prio) != 1) {
+ if (sscanf(argv[2], "%i", &prio) != 1) {
fprintf(stderr,"bad priority\n");
- return;
+ return 1;
}
- err = br_set_bridge_priority(br, prio);
+
+ err = br_set_bridge_priority(argv[1], prio);
if (err)
fprintf(stderr, "set bridge priority failed: %s\n",
strerror(err));
+ return err != 0;
}
-void br_cmd_setfd(struct bridge *br, char *time, char *arg1)
+static int br_cmd_setfd(char** argv)
{
struct timeval tv;
int err;
- if (strtotimeval(&tv, time)) {
+ if (strtotimeval(&tv, argv[2])) {
fprintf(stderr, "bad forward delay value\n");
- return;
+ return 1;
}
- err = br_set_bridge_forward_delay(br, &tv);
+ err = br_set_bridge_forward_delay(argv[1], &tv);
if (err)
fprintf(stderr, "set forward delay failed: %s\n",
strerror(err));
+
+ return err != 0;
}
-void br_cmd_sethello(struct bridge *br, char *time, char *arg1)
+static int br_cmd_sethello(char** argv)
{
struct timeval tv;
int err;
- if (strtotimeval(&tv, time)) {
+ if (strtotimeval(&tv, argv[2])) {
fprintf(stderr, "bad hello timer value\n");
- return;
+ return 1;
}
- err = br_set_bridge_hello_time(br, &tv);
+ err = br_set_bridge_hello_time(argv[1], &tv);
if (err)
fprintf(stderr, "set hello timer failed: %s\n",
strerror(err));
+
+ return err != 0;
}
-void br_cmd_setmaxage(struct bridge *br, char *time, char *arg1)
+static int br_cmd_setmaxage(char** argv)
{
struct timeval tv;
int err;
- if (strtotimeval(&tv, time)) {
+ if (strtotimeval(&tv, argv[2])) {
fprintf(stderr, "bad max age value\n");
- return;
+ return 1;
}
- err = br_set_bridge_max_age(br, &tv);
+ err = br_set_bridge_max_age(argv[1], &tv);
if (err)
fprintf(stderr, "set max age failed: %s\n",
strerror(err));
+
+ return err != 0;
}
-void br_cmd_setpathcost(struct bridge *br, char *arg0, char *arg1)
+static int br_cmd_setpathcost(char** argv)
{
int cost, err;
- struct port *p;
- if (sscanf(arg1, "%i", &cost) != 1) {
+ if (sscanf(argv[3], "%i", &cost) != 1) {
fprintf(stderr, "bad path cost value\n");
- return;
- }
-
- if ((p = br_find_port(br, arg0)) == NULL) {
- fprintf(stderr, "can't find port %s in bridge %s\n", arg0, br->ifname);
- return;
+ return 1;
}
- err = br_set_path_cost(p, cost);
+ err = br_set_path_cost(argv[1], argv[2], cost);
if (err)
fprintf(stderr, "set path cost failed: %s\n",
strerror(err));
+ return err != 0;
}
-void br_cmd_setportprio(struct bridge *br, char *arg0, char *arg1)
+static int br_cmd_setportprio(char** argv)
{
int cost, err;
- struct port *p;
- if (sscanf(arg1, "%i", &cost) != 1) {
+ if (sscanf(argv[3], "%i", &cost) != 1) {
fprintf(stderr, "bad path priority value\n");
- return;
+ return 1;
}
- if ((p = br_find_port(br, arg0)) == NULL) {
- fprintf(stderr, "can't find port %s in bridge %s\n", arg0, br->ifname);
- return;
- }
- err = br_set_port_priority(p, cost);
+ err = br_set_path_cost(argv[1], argv[2], cost);
if (err)
fprintf(stderr, "set port priority failed: %s\n",
- strerror(err));
+ strerror(errno));
+
+ return err != 0;
}
-void br_cmd_stp(struct bridge *br, char *arg0, char *arg1)
+static int br_cmd_stp(char** argv)
{
int stp, err;
- if (!strcmp(arg0, "on") || !strcmp(arg0, "yes")
- || !strcmp(arg0, "1"))
+ if (!strcmp(argv[2], "on") || !strcmp(argv[2], "yes")
+ || !strcmp(argv[2], "1"))
stp = 1;
- else if (!strcmp(arg0, "off") || !strcmp(arg0, "no")
- || !strcmp(arg0, "0"))
+ else if (!strcmp(argv[2], "off") || !strcmp(argv[2], "no")
+ || !strcmp(argv[2], "0"))
stp = 0;
else {
fprintf(stderr, "expect on/off for argument\n");
- return;
+ return 1;
}
- err = br_set_stp_state(br, stp);
+ err = br_set_stp_state(argv[1], stp);
if (err)
- fprintf(stderr, "set stp status failed: %d\n", err);
+ fprintf(stderr, "set stp status failed: %s\n",
+ strerror(errno));
+ return err != 0;
}
-void br_cmd_showstp(struct bridge *br, char *arg0, char *arg1)
+static int br_cmd_showstp(char** argv)
{
struct bridge_info info;
- if (br_get_bridge_info(br, &info)) {
- fprintf(stderr, "%s: can't get info %s\n", br->ifname,
+ if (br_get_bridge_info(argv[1], &info)) {
+ fprintf(stderr, "%s: can't get info %s\n", argv[1],
strerror(errno));
- return;
+ return 1;
}
- br_dump_info(br, &info);
+
+ br_dump_info(argv[1], &info);
+ return 0;
}
-void br_cmd_show(struct bridge *br, char *arg0, char *arg1)
+static int show_bridge(const char *name, void *arg)
{
struct bridge_info info;
- printf("bridge name\tbridge id\t\tSTP enabled\tinterfaces\n");
- for (br = bridge_list; br; br = br->next) {
- printf("%s\t\t", br->ifname);
- if (br_get_bridge_info(br, &info)) {
- fprintf(stderr, "can't get info %s\n",
+ printf("%s\t\t", name);
+ fflush(stdout);
+
+ if (br_get_bridge_info(name, &info)) {
+ fprintf(stderr, "can't get info %s\n",
strerror(errno));
- continue;
- }
+ return 1;
+ }
- br_dump_bridge_id((unsigned char *)&info.bridge_id);
- printf("\t%s\t\t", info.stp_enabled?"yes":"no");
- br_dump_interface_list(br);
+ br_dump_bridge_id((unsigned char *)&info.bridge_id);
+ printf("\t%s\t\t", info.stp_enabled?"yes":"no");
- }
+ br_dump_interface_list(name);
+ return 0;
+}
+
+static int br_cmd_show(char** argv)
+{
+ printf("bridge name\tbridge id\t\tSTP enabled\tinterfaces\n");
+ br_foreach_bridge(show_bridge, NULL);
+ return 0;
}
static int compare_fdbs(const void *_f0, const void *_f1)
@@ -319,75 +330,101 @@ static int compare_fdbs(const void *_f0, const void *_f1)
return memcmp(f0->mac_addr, f1->mac_addr, 6);
}
-static void __dump_fdb_entry(const struct fdb_entry *f)
-{
- printf("%3i\t", f->port_no);
- printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t",
- f->mac_addr[0], f->mac_addr[1], f->mac_addr[2],
- f->mac_addr[3], f->mac_addr[4], f->mac_addr[5]);
- printf("%s\t\t", f->is_local?"yes":"no");
- br_show_timer(&f->ageing_timer_value);
- printf("\n");
-}
-
-void br_cmd_showmacs(struct bridge *br, char *arg0, char *arg1)
+static int br_cmd_showmacs(char** argv)
{
- struct fdb_entry fdb[1024];
- int offset;
-
- printf("port no\tmac addr\t\tis local?\tageing timer\n");
-
- offset = 0;
- while (1) {
- int i;
- int num;
-
- num = br_read_fdb(br, fdb, offset, 1024);
- if (num < 0) {
- fprintf(stderr, "read of forward table failed\n");
- break;
+ const char *brname = argv[1];
+#define CHUNK 128
+ int i, n;
+ struct fdb_entry *fdb = NULL;
+ int offset = 0;
+
+ for(;;) {
+ fdb = realloc(fdb, (offset + CHUNK) * sizeof(struct fdb_entry));
+ if (!fdb) {
+ fprintf(stderr, "Out of memory\n");
+ return 1;
}
-
- if (!num)
+
+ n = br_read_fdb(brname, fdb+offset, offset, CHUNK);
+ if (n == 0)
break;
-
- qsort(fdb, num, sizeof(struct fdb_entry), compare_fdbs);
- for (i=0;i<num;i++)
- __dump_fdb_entry(fdb+i);
+ if (n < 0) {
+ fprintf(stderr, "read of forward table failed: %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+ offset += n;
+ }
+
+ qsort(fdb, offset, sizeof(struct fdb_entry), compare_fdbs);
- offset += num;
+ printf("port no\tmac addr\t\tis local?\tageing timer\n");
+ for (i = 0; i < offset; i++) {
+ const struct fdb_entry *f = fdb + i;
+ printf("%3i\t", f->port_no);
+ printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t",
+ f->mac_addr[0], f->mac_addr[1], f->mac_addr[2],
+ f->mac_addr[3], f->mac_addr[4], f->mac_addr[5]);
+ printf("%s\t\t", f->is_local?"yes":"no");
+ br_show_timer(&f->ageing_timer_value);
+ printf("\n");
}
+ return 0;
}
-static struct command commands[] = {
- {0, 1, "addbr", br_cmd_addbr},
- {1, 1, "addif", br_cmd_addif},
- {0, 1, "delbr", br_cmd_delbr},
- {1, 1, "delif", br_cmd_delif},
- {1, 1, "setageing", br_cmd_setageing},
- {1, 1, "setbridgeprio", br_cmd_setbridgeprio},
- {1, 1, "setfd", br_cmd_setfd},
- {1, 1, "sethello", br_cmd_sethello},
- {1, 1, "setmaxage", br_cmd_setmaxage},
- {1, 2, "setpathcost", br_cmd_setpathcost},
- {1, 2, "setportprio", br_cmd_setportprio},
- {0, 0, "show", br_cmd_show},
- {1, 0, "showmacs", br_cmd_showmacs},
- {1, 0, "showstp", br_cmd_showstp},
- {1, 1, "stp", br_cmd_stp},
+static const struct command commands[] = {
+ { 1, "addbr", br_cmd_addbr, "<bridge>\t\tadd bridge" },
+ { 1, "delbr", br_cmd_delbr, "<bridge>\t\tdelete bridge" },
+ { 2, "addif", br_cmd_addif,
+ "<bridge> <device>\tadd interface to bridge" },
+ { 2, "delif", br_cmd_delif,
+ "<bridge> <device>\tdelete interface from bridge" },
+ { 2, "setageing", br_cmd_setageing,
+ "<bridge> <time>\t\tset ageing time" },
+ { 2, "setbridgeprio", br_cmd_setbridgeprio,
+ "<bridge> <prio>\t\tset bridge priority" },
+ { 2, "setfd", br_cmd_setfd,
+ "<bridge> <time>\t\tset bridge forward delay" },
+ { 2, "sethello", br_cmd_sethello,
+ "<bridge> <time>\t\tset hello time" },
+ { 2, "setmaxage", br_cmd_setmaxage,
+ "<bridge> <time>\t\tset max message age" },
+ { 3, "setpathcost", br_cmd_setpathcost,
+ "<bridge> <port> <cost>\tset path cost" },
+ { 3, "setportprio", br_cmd_setportprio,
+ "<bridge> <port> <prio>\tset port priority" },
+ { 0, "show", br_cmd_show, "\t\t\tshow a list of bridges" },
+ { 1, "showmacs", br_cmd_showmacs,
+ "<bridge>\t\tshow a list of mac addrs"},
+ { 1, "showstp", br_cmd_showstp,
+ "<bridge>\t\tshow bridge stp info"},
+ { 1, "stp", br_cmd_stp,
+ "<bridge> <state>\tturn stp on/off" },
};
-struct command *br_command_lookup(char *cmd)
+const struct command *command_lookup(const char *cmd)
{
int i;
- int numcommands;
-
- numcommands = sizeof(commands)/sizeof(commands[0]);
- for (i=0;i<numcommands;i++)
+ for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) {
if (!strcmp(cmd, commands[i].name))
return &commands[i];
+ }
return NULL;
}
+
+void command_help(const struct command *cmd)
+{
+ printf("\t%-10s\t%s\n", cmd->name, cmd->help);
+}
+
+void command_helpall(void)
+{
+ int i;
+
+ for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++)
+ command_help(commands+i);
+}
diff --git a/brctl/brctl_disp.c b/brctl/brctl_disp.c
index 9849f18..6eb72ca 100644
--- a/brctl/brctl_disp.c
+++ b/brctl/brctl_disp.c
@@ -33,37 +33,41 @@ void br_show_timer(const struct timeval *tv)
printf("%4i.%.2i", (int)tv->tv_sec, (int)tv->tv_usec/10000);
}
-void br_dump_interface_list(const struct bridge *br)
+static int first;
+
+static int dump_interface(const char *b, const char *p, int ind, void *arg)
{
- char ifname[IFNAMSIZ];
- struct port *p;
- p = br->firstport;
- if (p != NULL) {
- printf("%s", if_indextoname(p->ifindex, ifname));
- p = p->next;
- }
- printf("\n");
+ if (first)
+ first = 0;
+ else
+ printf("\n\t\t\t\t\t\t\t");
- while (p != NULL) {
- printf("\t\t\t\t\t\t\t%s\n", if_indextoname(p->ifindex, ifname));
- p = p->next;
- }
+ printf("%s", p);
+
+ return 0;
+}
+
+void br_dump_interface_list(const char *br)
+{
+ first = 1;
+ br_foreach_port(br, dump_interface, NULL);
+ printf("\n");
}
-void br_dump_port_info(const struct port *p)
+static int dump_port_info(const char *br, const char *p, int ifindex,
+ void *arg)
{
- char ifname[IFNAMSIZ];
struct port_info pinfo;
- printf("%s (%i)\n", if_indextoname(p->ifindex, ifname), p->index);
- if (br_get_port_info(p, &pinfo)) {
+ printf("%s (%i)\n", p, if_nametoindex(p));
+ if (br_get_port_info(p, ifindex, &pinfo)) {
printf(" can't get port info\n");
- return;
+ return 1;
}
printf(" port id\t\t%.4x\t\t\t", pinfo.port_id);
- printf("state\t\t\t%s\n", br_get_state_name(pinfo.state));
+ printf("state\t\t%15s\n", br_get_state_name(pinfo.state));
printf(" designated root\t");
br_dump_bridge_id((unsigned char *)&pinfo.designated_root);
printf("\tpath cost\t\t%4i\n", pinfo.path_cost);
@@ -85,18 +89,13 @@ void br_dump_port_info(const struct port *p)
printf("TOPOLOGY_CHANGE_ACK ");
printf("\n");
printf("\n");
+ return 0;
}
-void br_dump_info(const struct bridge *br, const struct bridge_info *bri)
+void br_dump_info(const char *br, const struct bridge_info *bri)
{
- const struct port *p;
-
- printf("%s\n", br->ifname);
- if (!bri->stp_enabled) {
- printf(" STP is disabled for this interface\n");
- return;
- }
+ printf("%s\n", br);
printf(" bridge id\t\t");
br_dump_bridge_id((unsigned char *)&bri->bridge_id);
printf("\n designated root\t");
@@ -134,9 +133,5 @@ void br_dump_info(const struct bridge *br, const struct bridge_info *bri)
printf("\n");
printf("\n");
- p = br->firstport;
- while (p != NULL) {
- br_dump_port_info(p);
- p = p->next;
- }
+ br_foreach_port(br, dump_port_info, NULL);
}
diff --git a/configure.in b/configure.in
index 0ed11cc..53c0eac 100644
--- a/configure.in
+++ b/configure.in
@@ -1,5 +1,7 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT(brctl/brctl.c)
+AC_CONFIG_HEADER(libbridge/config.h)
+AM_INIT_AUTOMAKE(bridge-utils,1.0)
AC_ARG_WITH( linux, [ --with-linux-headers Location of the linux headers to use],
KERNEL_HEADERS=$withval, KERNEL_HEADERS="/usr/src/linux/include")
@@ -22,6 +24,7 @@ AC_PROG_GCC_TRADITIONAL
AC_FUNC_MEMCMP
AC_CHECK_FUNCS(gethostname socket strdup uname)
AC_CHECK_FUNCS(if_nametoindex if_indextoname)
+AC_CHECK_LIB(sysfs, sysfs_open_directory)
AC_SUBST(KERNEL_HEADERS)
diff --git a/doc/Makefile b/doc/Makefile
index 011b86e..8c57ff7 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -1,6 +1,6 @@
DESTDIR=
-KERNEL_HEADERS=-I/usr/src/linux/include
+KERNEL_HEADERS=-I/home/shemminger/bridge-2.6/include
INSTALL=install -s
diff --git a/libbridge/libbridge.h b/libbridge/libbridge.h
index 503effc..b1e1fb5 100644
--- a/libbridge/libbridge.h
+++ b/libbridge/libbridge.h
@@ -22,12 +22,6 @@
#include <net/if.h>
#include <linux/if_bridge.h>
-struct bridge;
-struct bridge_info;
-struct fdb_entry;
-struct port;
-struct port_info;
-
struct bridge_id
{
unsigned char prio[2];
@@ -56,15 +50,6 @@ struct bridge_info
struct timeval gc_timer_value;
};
-struct bridge
-{
- struct bridge *next;
-
- int ifindex;
- char ifname[IFNAMSIZ];
- struct port *firstport;
-};
-
struct fdb_entry
{
u_int8_t mac_addr[6];
@@ -90,37 +75,35 @@ struct port_info
struct timeval hold_timer_value;
};
-struct port
-{
- struct port *next;
- int index;
- int ifindex;
- struct bridge *parent;
-};
-
-extern struct bridge *bridge_list;
+extern int br_init(void);
+extern int br_refresh(void);
+extern void br_shutdown(void);
-int br_init(void);
-int br_refresh(void);
-struct bridge *br_find_bridge(const char *brname);
-struct port *br_find_port(struct bridge *br, const char *portname);
-const char *br_get_state_name(int state);
+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, int ifindex,
+ void *),
+ void *arg);
+extern const char *br_get_state_name(int state);
-int br_get_bridge_info(const struct bridge *br, struct bridge_info *);
-int br_get_port_info(const struct port *port, struct port_info *);
-int br_get_version(void);
-int br_add_bridge(const char *brname);
-int br_del_bridge(const char *brname);
-int br_add_interface(struct bridge *br, int ifindex);
-int br_del_interface(struct bridge *br, int ifindex);
-int br_set_bridge_forward_delay(struct bridge *br, struct timeval *tv);
-int br_set_bridge_hello_time(struct bridge *br, struct timeval *tv);
-int br_set_bridge_max_age(struct bridge *br, struct timeval *tv);
-int br_set_ageing_time(struct bridge *br, struct timeval *tv);
-int br_set_gc_interval(struct bridge *br, struct timeval *tv);
-int br_set_stp_state(struct bridge *br, int stp_state);
-int br_set_bridge_priority(struct bridge *br, int bridge_priority);
-int br_set_port_priority(struct port *p, int port_priority);
-int br_set_path_cost(struct port *p, int path_cost);
-int br_read_fdb(struct bridge *br, struct fdb_entry *fdbs, int offset, int num);
+extern int br_get_bridge_info(const char *br, struct bridge_info *);
+extern int br_get_port_info(const char *port, int ifindex, struct port_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, int ifindex);
+extern int br_del_interface(const char *br, int ifindex);
+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);
#endif
diff --git a/libbridge/libbridge_devif.c b/libbridge/libbridge_devif.c
index ef947b6..baf450b 100644
--- a/libbridge/libbridge_devif.c
+++ b/libbridge/libbridge_devif.c
@@ -16,142 +16,419 @@
* 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 <sys/fcntl.h>
-#include <linux/sockios.h>
-#include <sys/ioctl.h>
-#include <sys/time.h>
#include "libbridge.h"
#include "libbridge_private.h"
-int br_device_ioctl(const struct bridge *br, unsigned long arg0,
- unsigned long arg1, unsigned long arg2, unsigned long arg3)
+#define dprintf(fmt,arg...)
+
+#ifdef HAVE_LIBSYSFS
+/* Given two two character "0a" convert it to a byte */
+static unsigned char getoctet(const char *cp)
{
- unsigned long args[4];
- struct ifreq ifr;
+ char t[3] = { cp[0], cp[1], 0 };
+ return strtoul(t, NULL, 16);
+}
- args[0] = arg0;
- args[1] = arg1;
- args[2] = arg2;
- args[3] = arg3;
+static struct sysfs_directory *bridge_sysfs_directory(const char *devname,
+ const char *subname)
+{
+ struct sysfs_directory *sdir;
+ struct sysfs_class_device *dev;
+ char path[SYSFS_PATH_MAX];
- strncpy(ifr.ifr_name, br->ifname, IFNAMSIZ);
- ((unsigned long *)(&ifr.ifr_data))[0] = (unsigned long)args;
+ if (!br_class_net) {
+ dprintf("can't find class_net\n");
+ return NULL;
+ }
-#ifdef SIOCBRDEV
- /* New interface which allows 32bit/64 bit compatiability to work. */
- { int err = ioctl(br_socket_fd, SIOCBRDEV, &ifr);
- if (err >= 0)
- return err;
+ dev = sysfs_get_class_device(br_class_net, (char *) devname);
+ if (!dev) {
+ dprintf("can't find device %s in %s\n", devname, br_class_net->path);
+ return NULL;
}
-#endif
- /* Old fall back */
- return ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr);
+
+ snprintf(path, SYSFS_PATH_MAX, "%s/%s", dev->path, subname);
+ sdir = sysfs_open_directory(path);
+ if (!sdir)
+ fprintf(stderr, "Can't open directory: %s\n", path);
+ return sdir;
}
-int br_add_interface(struct bridge *br, int ifindex)
+static void fetch_id(struct sysfs_directory *sdir, char *name,
+ struct bridge_id *id)
{
- if (br_device_ioctl(br, BRCTL_ADD_IF, ifindex, 0, 0) < 0)
- return errno;
+ struct sysfs_attribute *attr;
- return 0;
+ memset(id, 0, sizeof(id));
+ attr = sysfs_get_directory_attribute(sdir, name);
+ dprintf("fetch_id %s/%s = %s\n", sdir->path, name,
+ attr ? attr->value : "<null>\n");
+
+ if (!attr) {
+ fprintf(stderr, "Can't find attribute %s/%s\n", sdir->path, name);
+ return;
+ }
+
+ if (strlen(attr->value) < 17)
+ fprintf(stderr, "Bad format for %s: '%s'\n", name, attr->value);
+ else {
+ const char *cp = attr->value;
+ id->prio[0] = getoctet(cp); cp += 2;
+ id->prio[1] = getoctet(cp); cp += 3;
+ id->addr[0] = getoctet(cp); cp += 2;
+ id->addr[1] = getoctet(cp); cp += 2;
+ id->addr[2] = getoctet(cp); cp += 2;
+ id->addr[3] = getoctet(cp); cp += 2;
+ id->addr[4] = getoctet(cp); cp += 2;
+ id->addr[5] = getoctet(cp);
+ }
}
-int br_del_interface(struct bridge *br, int ifindex)
+static void fetch_tv(struct sysfs_directory *sdir, char *name,
+ struct timeval *tv)
{
- if (br_device_ioctl(br, BRCTL_DEL_IF, ifindex, 0, 0) < 0)
- return errno;
+ struct sysfs_attribute *attr
+ = sysfs_get_directory_attribute(sdir, name);
- return 0;
+ if (!attr) {
+ fprintf(stderr, "Can't find attribute %s/%s\n", sdir->path, name);
+ memset(tv, 0, sizeof(tv));
+ return;
+ }
+
+ __jiffies_to_tv(tv, strtoul(attr->value, NULL, 0));
+}
+
+static int fetch_int(struct sysfs_directory *sdir, char *name)
+{
+ struct sysfs_attribute *attr
+ = sysfs_get_directory_attribute(sdir, name);
+ int val = 0;
+
+ if (!attr)
+ fprintf(stderr, "Can't find attribute %s/%s\n", sdir->path, name);
+ else
+ val = strtol(attr->value, NULL, 0);
+ return val;
}
+#endif
-int br_set_bridge_forward_delay(struct bridge *br, struct timeval *tv)
+/* get information via ioctl */
+static int old_get_bridge_info(const char *bridge, struct bridge_info *info)
{
- unsigned long jif = __tv_to_jiffies(tv);
+ 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 (br_device_ioctl(br, BRCTL_SET_BRIDGE_FORWARD_DELAY,
- jif, 0, 0) < 0)
+ if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) {
+ fprintf(stderr, "%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;
}
-int br_set_bridge_hello_time(struct bridge *br, struct timeval *tv)
+int br_get_bridge_info(const char *bridge, struct bridge_info *info)
{
- unsigned long jif = __tv_to_jiffies(tv);
-
- if (br_device_ioctl(br, BRCTL_SET_BRIDGE_HELLO_TIME, jif, 0, 0) < 0)
- return errno;
+#ifndef HAVE_LIBSYSFS
+ return old_get_bridge_info(bridge, info);
+#else
+ struct sysfs_directory *sdir;
+
+ sdir = bridge_sysfs_directory(bridge, SYSFS_BRIDGE_ATTR);
+ if (!sdir)
+ return old_get_bridge_info(bridge,info);
+
+ memset(info, 0, sizeof(*info));
+ fetch_id(sdir, "root_id", &info->designated_root);
+ fetch_id(sdir, "bridge_id", &info->bridge_id);
+ info->root_path_cost = fetch_int(sdir, "root_path_cost");
+ fetch_tv(sdir, "max_age", &info->max_age);
+ fetch_tv(sdir, "hello_time", &info->hello_time);
+ fetch_tv(sdir, "forward_delay", &info->forward_delay);
+ fetch_tv(sdir, "max_age", &info->bridge_max_age);
+ fetch_tv(sdir, "hello_time", &info->bridge_hello_time);
+ fetch_tv(sdir, "forward_delay", &info->bridge_forward_delay);
+ fetch_tv(sdir, "ageing_time", &info->ageing_time);
+ fetch_tv(sdir, "hello_timer", &info->hello_timer_value);
+ fetch_tv(sdir, "tcn_timer", &info->tcn_timer_value);
+ fetch_tv(sdir, "topology_change_timer",
+ &info->topology_change_timer_value);;
+ fetch_tv(sdir, "gc_timer", &info->gc_timer_value);
+
+ info->root_port = fetch_int(sdir, "root_port");
+ info->stp_enabled = fetch_int(sdir, "stp_state");
+ info->topology_change = fetch_int(sdir, "topology_change");
+ info->topology_change_detected = fetch_int(sdir, "topology_change_detected");
+ sysfs_close_directory(sdir);
return 0;
+#endif
}
-int br_set_bridge_max_age(struct bridge *br, struct timeval *tv)
+static int old_get_port_info(const char *port, int ifindex, struct port_info *info)
{
- unsigned long jif = __tv_to_jiffies(tv);
+ struct __port_info i;
+ struct ifreq ifr;
+ unsigned long args[4] = { BRCTL_GET_PORT_INFO,
+ (unsigned long) &i, ifindex, 0 };
+
+ memset(info, 0, sizeof(*info));
+ strncpy(ifr.ifr_name, port, IFNAMSIZ);
+ ifr.ifr_data = (char *) &args;
- if (br_device_ioctl(br, BRCTL_SET_BRIDGE_MAX_AGE, jif, 0, 0) < 0)
+ if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) {
+ fprintf(stderr, "can't get port %s(%d) info %s\n",
+ port, ifindex, strerror(errno));
return errno;
+ }
+
+
+ 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);
+ return 0;
+}
+
+int br_get_port_info(const char *port, int ifindex, struct port_info *info)
+{
+#ifndef HAVE_LIBSYSFS
+ return old_get_port_info(port, ifindex, info);
+#else
+ struct sysfs_directory *sdir;
+
+ sdir = bridge_sysfs_directory(port, SYSFS_BRIDGE_PORT_ATTR);
+ if (!sdir)
+ return old_get_port_info(port, ifindex, info);
+
+ memset(info, 0, sizeof(*info));
+ fetch_id(sdir, "designated_root", &info->designated_root);
+ fetch_id(sdir, "designated_bridge", &info->designated_bridge);
+ info->port_id = fetch_int(sdir, "port_id");
+ info->designated_port = fetch_int(sdir, "designated_port");
+ info->path_cost = fetch_int(sdir, "path_cost");
+ info->designated_cost = fetch_int(sdir, "designated_cost");
+ info->state = fetch_int(sdir, "state");
+ info->top_change_ack = fetch_int(sdir, "change_ack");
+ info->config_pending = fetch_int(sdir, "config_pending");
+ fetch_tv(sdir, "message_age_timer",
+ &info->message_age_timer_value);
+ fetch_tv(sdir, "forward_delay_timer",
+ &info->forward_delay_timer_value);
+ fetch_tv(sdir, "hold_timer",
+ &info->hold_timer_value);
+ sysfs_close_directory(sdir);
return 0;
+#endif
+}
+
+int br_add_interface(const char *bridge, int ifindex)
+{
+ struct ifreq ifr;
+ int err;
+
+ strncpy(ifr.ifr_name, bridge, IFNAMSIZ);
+#ifdef SIOCBRADDIF
+ ifr.ifr_ifindex = ifindex;
+ err = ioctl(br_socket_fd, SIOCBRADDIF, &ifr);
+ if (err < 0 && errno == EOPNOTSUPP)
+#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_set_ageing_time(struct bridge *br, struct timeval *tv)
+int br_del_interface(const char *bridge, int ifindex)
{
- unsigned long jif = __tv_to_jiffies(tv);
+ struct ifreq ifr;
+ int err;
+
+ strncpy(ifr.ifr_name, bridge, IFNAMSIZ);
+#ifdef SIOCBRDELIF
+ ifr.ifr_ifindex = ifindex;
+ err = ioctl(br_socket_fd, SIOCBRDELIF, &ifr);
+ if (err < 0 && errno == EOPNOTSUPP)
+#endif
+ {
+ unsigned long args[4] = { BRCTL_DEL_IF, ifindex, 0, 0 };
+
+ ifr.ifr_data = (char *) args;
+ err = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr);
+ }
- if (br_device_ioctl(br, BRCTL_SET_AGEING_TIME, jif, 0, 0) < 0)
- return errno;
+ return err < 0 ? errno : 0;
+}
- return 0;
+static int br_set(const char *bridge, const char *name,
+ unsigned long value, unsigned long oldcode)
+{
+ int ret;
+#ifdef HAVE_LIBSYSFS
+ struct sysfs_directory *sdir;
+
+ sdir = bridge_sysfs_directory(bridge, SYSFS_BRIDGE_ATTR);
+ if (sdir) {
+ struct sysfs_attribute *attr;
+ char buf[32];
+ sprintf(buf, "%ld", value);
+
+ attr = sysfs_get_directory_attribute(sdir, (char *) name);
+ if (attr)
+ ret = sysfs_write_attribute(attr, buf, strlen(buf));
+ else {
+ ret = -1;
+ errno = EINVAL;
+ }
+ sysfs_close_directory(sdir);
+ } else
+#endif
+ {
+ 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_gc_interval(struct bridge *br, struct timeval *tv)
+int br_set_bridge_forward_delay(const char *br, struct timeval *tv)
{
- return 0;
+ return br_set(br, "forward_delay", __tv_to_jiffies(tv),
+ BRCTL_SET_BRIDGE_FORWARD_DELAY);
}
-int br_set_stp_state(struct bridge *br, int stp_state)
+int br_set_bridge_hello_time(const char *br, struct timeval *tv)
{
- if (br_device_ioctl(br, BRCTL_SET_BRIDGE_STP_STATE, stp_state,
- 0, 0) < 0)
- return errno;
+ return br_set(br, "hello_time", __tv_to_jiffies(tv),
+ BRCTL_SET_BRIDGE_HELLO_TIME);
+}
- return 0;
+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_bridge_priority(struct bridge *br, int bridge_priority)
+int br_set_ageing_time(const char *br, struct timeval *tv)
{
- if (br_device_ioctl(br, BRCTL_SET_BRIDGE_PRIORITY, bridge_priority,
- 0, 0) < 0)
- return errno;
+ return br_set(br, "ageing_time", __tv_to_jiffies(tv),
+ BRCTL_SET_AGEING_TIME);
+}
- return 0;
+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_port_priority(struct port *p, int port_priority)
+int br_set_bridge_priority(const char *br, int bridge_priority)
{
- if (br_device_ioctl(p->parent, BRCTL_SET_PORT_PRIORITY, p->index,
- port_priority, 0) < 0)
- return errno;
+ return br_set(br, "priority", bridge_priority,
+ BRCTL_SET_BRIDGE_PRIORITY);
+}
- return 0;
+static int port_set(const char *bridge, const char *ifname,
+ const char *name, unsigned long value,
+ unsigned long oldcode)
+{
+ int ret;
+#ifdef HAVE_LIBSYSFS
+ struct sysfs_directory *sdir;
+
+ sdir = bridge_sysfs_directory(ifname, SYSFS_BRIDGE_PORT_ATTR);
+ if (sdir) {
+ struct sysfs_attribute *attr;
+ char buf[32];
+
+ sprintf(buf, "%ld", value);
+
+ attr = sysfs_get_directory_attribute(sdir, (char *) name);
+ if (attr)
+ ret = sysfs_write_attribute(attr, buf, strlen(buf));
+ else {
+ ret = -1;
+ errno = EINVAL;
+ }
+ sysfs_close_directory(sdir);
+ } else
+#endif
+ {
+ struct ifreq ifr;
+ int ifindex = if_nametoindex(ifname);
+ unsigned long args[4] = { oldcode, ifindex, 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_path_cost(struct port *p, int path_cost)
+int br_set_port_priority(const char *bridge, const char *port, int priority)
{
- if (br_device_ioctl(p->parent, BRCTL_SET_PATH_COST, p->index,
- path_cost, 0) < 0)
- return errno;
+ return port_set(bridge, port, "priority", priority, BRCTL_SET_PORT_PRIORITY);
+}
- return 0;
+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);
}
-static void __copy_fdb(struct fdb_entry *ent, const struct __fdb_entry *f)
+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;
@@ -159,25 +436,55 @@ static void __copy_fdb(struct fdb_entry *ent, const struct __fdb_entry *f)
__jiffies_to_tv(&ent->ageing_timer_value, f->ageing_timer_value);
}
-int br_read_fdb(struct bridge *br, struct fdb_entry *fdbs, int offset, int num)
+int br_read_fdb(const char *bridge, struct fdb_entry *fdbs,
+ unsigned long offset, int num)
{
- struct __fdb_entry f[num];
- int i;
- int numread;
-
- again:
- numread = br_device_ioctl(br, BRCTL_GET_FDB_ENTRIES,
- (unsigned long)f, num, offset);
- if (numread < 0) {
- if (errno == EAGAIN)
- goto again;
+ int i, fd = -1, n;
+ struct __fdb_entry fe[num];
+#ifdef HAVE_LIBSYSFS
+ struct sysfs_class_device *dev;
+
+ /* open /sys/class/net/brXXX/brforward */
+ if (br_class_net &&
+ (dev = sysfs_get_class_device(br_class_net, (char *) bridge))) {
+ char path[SYSFS_PATH_MAX];
+
+ snprintf(path, SYSFS_PATH_MAX, "%s/%s", dev->path,
+ SYSFS_BRIDGE_FDB);
+ fd = open(path, O_RDONLY, 0);
+ }
- return -errno;
+ if (fd != -1) {
+ /* read records from file */
+ lseek(fd, offset*sizeof(struct __fdb_entry), SEEK_SET);
+ n = read(fd, fe, num*sizeof(struct __fdb_entry));
+ if (n > 0)
+ n /= sizeof(struct __fdb_entry);
+ } else
+#endif
+ {
+ /* 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);
+ sleep(0);
+ if (n < 0 && errno == EAGAIN && ++retries < 10)
+ goto retry;
}
- for (i=0;i<numread;i++)
- __copy_fdb(fdbs+i, f+i);
+ for (i = 0; i < n; i++)
+ __copy_fdb(fdbs+i, fe+i);
- return numread;
+ if (fd > 0)
+ close(fd);
+
+ return n;
}
-
diff --git a/libbridge/libbridge_if.c b/libbridge/libbridge_if.c
index 518d2b4..9eff89b 100644
--- a/libbridge/libbridge_if.c
+++ b/libbridge/libbridge_if.c
@@ -22,56 +22,46 @@
#include <string.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
-#include <sys/time.h>
-#include <sys/utsname.h>
+
#include "libbridge.h"
#include "libbridge_private.h"
-int br_get_br(unsigned long arg0, unsigned long arg1, unsigned long arg2)
-{
- unsigned long arg[3];
-
- arg[0] = arg0;
- arg[1] = arg1;
- arg[2] = arg2;
-
- return ioctl(br_socket_fd, SIOCGIFBR, arg);
-}
-
-int br_set_br(unsigned long arg0, unsigned long arg1, unsigned long arg2)
-{
- unsigned long arg[3];
-
- arg[0] = arg0;
- arg[1] = arg1;
- arg[2] = arg2;
-
- return ioctl(br_socket_fd, SIOCSIFBR, arg);
-}
-
-int br_get_version(void)
-{
- return br_get_br(BRCTL_GET_VERSION, 0, 0);
-}
int br_add_bridge(const char *brname)
{
- char _br[IFNAMSIZ];
+ int ret;
+
+#ifdef SIOCBRADDBR
+ ret = ioctl(br_socket_fd, SIOCBRADDBR, brname);
+ if (ret < 0 && errno == -EOPNOTSUPP)
+#endif
+ {
+ char _br[IFNAMSIZ];
+ unsigned long arg[3]
+ = { BRCTL_ADD_BRIDGE, (unsigned long) _br };
- strncpy(_br, brname, IFNAMSIZ);
- if (br_set_br(BRCTL_ADD_BRIDGE, (unsigned long)_br, 0) < 0)
- return errno;
+ strncpy(_br, brname, IFNAMSIZ);
+ ret = ioctl(br_socket_fd, SIOCSIFBR, arg);
+ }
- return 0;
+ return ret < 0 ? errno : 0;
}
int br_del_bridge(const char *brname)
{
- char _br[IFNAMSIZ];
+ int ret;
- strncpy(_br, brname, IFNAMSIZ);
- if (br_set_br(BRCTL_DEL_BRIDGE, (unsigned long)_br, 0) < 0)
- return errno;
+#ifdef SIOCBRDELBR
+ ret = ioctl(br_socket_fd, SIOCBRDELBR, brname);
+ if (ret < 0 && errno == -EOPNOTSUPP)
+#endif
+ {
+ char _br[IFNAMSIZ];
+ unsigned long arg[3]
+ = { BRCTL_DEL_BRIDGE, (unsigned long) _br };
- return 0;
+ strncpy(_br, brname, IFNAMSIZ);
+ ret = ioctl(br_socket_fd, SIOCSIFBR, arg);
+ }
+ return ret < 0 ? errno : 0;
}
diff --git a/libbridge/libbridge_init.c b/libbridge/libbridge_init.c
index d401250..2704f32 100644
--- a/libbridge/libbridge_init.c
+++ b/libbridge/libbridge_init.c
@@ -18,236 +18,245 @@
#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
#include <errno.h>
#include <string.h>
-#include <sys/fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/time.h>
+
#include "libbridge.h"
#include "libbridge_private.h"
-#define MAX_BRIDGES 1024 /* arbitrary */
-#define MAX_PORTS 4096
+#define MAX_BRIDGES 1024
+#define MAX_PORTS 1024
-int br_socket_fd;
-struct bridge *bridge_list;
+#define dprintf(fmt,arg...)
-static void __bridge_info_copy(struct bridge_info *info,
- const struct __bridge_info *i)
-{
- 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->topology_change = i->topology_change;
- info->topology_change_detected = i->topology_change_detected;
- info->root_port = i->root_port;
- 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);
-}
+int br_socket_fd = -1;
+struct sysfs_class *br_class_net;
-
-int br_get_bridge_info(const struct bridge *br, struct bridge_info *info)
+int br_init(void)
{
- struct __bridge_info i;
-
- if (br_device_ioctl(br, BRCTL_GET_BRIDGE_INFO,
- (unsigned long )&i, 0, 0) < 0) {
+ if ((br_socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return errno;
- }
- __bridge_info_copy(info, &i);
+ br_class_net = sysfs_open_class("net");
return 0;
}
-static void __port_info_copy(struct port_info *info,
- const struct __port_info *i)
+int br_refresh(void)
{
- 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);
+ if (br_class_net) {
+ sysfs_close_class(br_class_net);
+ br_class_net = sysfs_open_class("net");
+ }
+
+ return 0;
}
-int br_get_port_info(const struct port *p, struct port_info *info)
+void br_shutdown(void)
{
- struct __port_info i;
+ sysfs_close_class(br_class_net);
+ br_class_net = NULL;
+ close(br_socket_fd);
+ br_socket_fd = -1;
+}
- if (br_device_ioctl(p->parent, BRCTL_GET_PORT_INFO,
- (unsigned long)&i, p->index, 0) < 0) {
- fprintf(stderr, "%s: can't get port %d info %s\n",
- p->parent->ifname, p->index, strerror(errno));
- return errno;
- }
- __port_info_copy(info, &i);
+#ifdef HAVE_LIBSYSFS
+/* If /sys/class/net/XXX/bridge exists then it must be a bridge */
+static int isbridge(const struct sysfs_class_device *dev)
+{
+ char path[SYSFS_PATH_MAX];
- return 0;
+ snprintf(path, sizeof(path), "%s/bridge", dev->path);
+ return !sysfs_path_is_dir(path);
}
-static void br_nuke_bridge(struct bridge *b)
+/*
+ * New interface uses sysfs to find bridges
+ */
+static int new_foreach_bridge(int (*iterator)(const char *name, void *),
+ void *arg)
{
- struct port *p, *n;
+ struct sysfs_class_device *dev;
+ struct dlist *devlist;
+ int count = 0;
- for (p = b->firstport; p; p = n) {
- n = p->next;
- free(p);
+ if (!br_class_net) {
+ dprintf("no class /sys/class/net\n");
+ return -EOPNOTSUPP;
}
- free(b);
-}
-
-static int br_make_port_list(struct bridge *br)
-{
- int i, cnt;
- struct port *p, **top;
- int *ifindices;
-
- ifindices = calloc(MAX_PORTS, sizeof(int));
- if (!ifindices)
- return -ENOMEM;
-
- cnt = br_device_ioctl(br, BRCTL_GET_PORT_LIST,
- (unsigned long)ifindices,
- MAX_PORTS, 0);
- if (cnt < 0)
- return errno;
+ devlist = sysfs_get_class_devices(br_class_net);
+ if (!devlist) {
+ fprintf(stderr, "Can't read devices from sysfs\n");
+ return -errno;
+ }
- if (cnt == 0)
- cnt = 256; /* old 2.4 compatiablity */
+ dlist_for_each_data(devlist, dev, struct sysfs_class_device) {
+ if (isbridge(dev)) {
+ ++count;
+ if (iterator(dev->name, arg))
+ break;
+ }
+ }
- top = &br->firstport;
- for (i = 0; i < cnt; i++) {
- if (!ifindices[i])
- continue;
+ return count;
+}
+#endif
- p = malloc(sizeof(struct port));
- if (!p)
- goto nomem;
+/*
+ * 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 };
- p->next = NULL;
- p->ifindex = ifindices[i];
- p->parent = br;
- p->index = i;
- *top = p;
- top = &p->next;
+ num = ioctl(br_socket_fd, SIOCGIFBR, args);
+ dprintf("old_foreach_bridge num=%d\n", num);
+ if (num < 0) {
+ fprintf(stderr, "Get bridge indices failed: %s\n",
+ strerror(errno));
+ return -errno;
}
- free(ifindices);
- return 0;
+ for (i = 0; i < num; i++) {
+ if (!if_indextoname(ifindices[i], ifname)) {
+ fprintf(stderr, "get find name for ifindex %d\n",
+ ifindices[i]);
+ return -errno;
+ }
- nomem:
- p = br->firstport;
- while (p) {
- struct port *n = p->next;
- free(p);
- p = n;
+ ++ret;
+ if(iterator(ifname, iarg))
+ break;
+
}
- br->firstport = NULL;
- free(ifindices);
- return -ENOMEM;
+ return ret;
+
}
-static struct bridge *new_bridge(int ifindex, const char *name)
+/*
+ * 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)
{
- struct bridge *br;
-
- br = malloc(sizeof(struct bridge));
- if (br) {
- memset(br, 0, sizeof(struct bridge));
- br->ifindex = ifindex;
- strncpy(br->ifname, name, IFNAMSIZ);
- br->firstport = NULL;
- }
- return br;
+ int ret;
+#ifdef HAVE_LIBSYSFS
+
+ ret = new_foreach_bridge(iterator, arg);
+ if (ret <= 0)
+#endif
+ ret = old_foreach_bridge(iterator, arg);
+
+ return ret;
}
-static int br_make_bridge_list(void)
+/*
+ * Only used if sysfs is not available.
+ */
+static int old_foreach_port(const char *brname,
+ int (*iterator)(const char *br,
+ const char *port, int ind,
+ void *arg),
+ void *arg)
{
- struct bridge *br;
- int i, num;
- int ifindices[MAX_BRIDGES];
+ 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 };
- num = br_get_br(BRCTL_GET_BRIDGES, (unsigned long)ifindices,
- MAX_BRIDGES);
- if (num < 0) {
- fprintf(stderr, "Get bridge indices failed: %s\n",
- strerror(errno));
- return errno;
- }
+ memset(ifindices, 0, sizeof(ifindices));
+ strncpy(ifr.ifr_name, brname, IFNAMSIZ);
+ ifr.ifr_data = (char *) &args;
- bridge_list = NULL;
- for (i = 0; i < num; i++) {
- if (!if_indextoname(ifindices[i], ifname))
- continue;
- br = new_bridge(ifindices[i], ifname);
- if (!br) /* ignore the problem could just be a race! */
+ err = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr);
+ if (err < 0)
+ return -errno;
+
+ count = 0;
+ for (i = 0; i < MAX_PORTS; i++) {
+ if (!ifindices[i])
continue;
- if ( br_make_port_list(br)) {
- free(br);
+ if (!if_indextoname(ifindices[i], ifname))
continue;
- }
- br->next = bridge_list;
- bridge_list = br;
+ ++count;
+ if (iterator(brname, ifname, ifindices[i], arg))
+ break;
}
- return 0;
+ return count;
}
+
-int br_init()
-{
- int err;
- if ((br_socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
- return errno;
-
- if (br_get_version() != BRCTL_VERSION) {
- fprintf(stderr, "bridge utilities not compatiable with kernel version\n");
- exit(1);
+/*
+ * Iterate over all ports in bridge (using sysfs).
+ */
+int br_foreach_port(const char *brname,
+ int (*iterator)(const char *br, const char *port,
+ int ind, void *arg),
+ void *arg)
+{
+#ifndef HAVE_LIBSYSFS
+ return old_foreach_port(brname, iterator, arg);
+#else
+ struct sysfs_class_device *dev;
+ struct sysfs_directory *dir;
+ struct sysfs_link *plink;
+ struct dlist *links;
+ int err = 0;
+ char path[SYSFS_PATH_MAX];
+
+ if (!br_class_net ||
+ !(dev = sysfs_get_class_device(br_class_net, (char *) brname)))
+ return -ENODEV;
+
+ snprintf(path, sizeof(path), "%s/%s",
+ dev->path, SYSFS_BRIDGE_PORT_SUBDIR);
+
+ dir = sysfs_open_directory(path);
+ if (!dir) {
+ /* no /sys/class/net/ethX/brif subdirectory
+ * either: old kernel, or not really a bridge
+ */
+ err = old_foreach_port(brname, iterator, arg);
+ goto out1;
}
- if ((err = br_make_bridge_list()) != 0)
- return err;
+ links = sysfs_get_dir_links(dir);
+ if (!links) {
+ err = -ENOSYS;
+ goto out2;
+ }
- return 0;
-}
+ err = 0;
+ dlist_for_each_data(links, plink, struct sysfs_link) {
+ int ifindex = if_nametoindex(plink->name);
-int br_refresh()
-{
- struct bridge *b;
+ if (!ifindex) {
+ dprintf("can't find ifindex for %s\n", plink->name);
+ continue;
+ }
- b = bridge_list;
- while (b != NULL) {
- struct bridge *bnext;
+ ++err;
+ if (iterator(brname, plink->name, ifindex, arg))
+ break;
- bnext = b->next;
- br_nuke_bridge(b);
- b = bnext;
}
-
- return br_make_bridge_list();
+ out2:
+ sysfs_close_directory(dir);
+ out1:
+ return err;
+#endif
}
diff --git a/libbridge/libbridge_misc.c b/libbridge/libbridge_misc.c
index c19d2fc..b227185 100644
--- a/libbridge/libbridge_misc.c
+++ b/libbridge/libbridge_misc.c
@@ -24,26 +24,14 @@
#include "libbridge.h"
#include "libbridge_private.h"
-unsigned long __tv_to_jiffies(struct timeval *tv)
-{
- unsigned long long jif;
-
- jif = 1000000ULL * tv->tv_sec + tv->tv_usec;
-
- return (HZ*jif)/1000000;
-}
-void __jiffies_to_tv(struct timeval *tv, unsigned long jiffies)
-{
- unsigned long long tvusec;
-
- tvusec = (1000000ULL*jiffies)/HZ;
- tv->tv_sec = tvusec/1000000;
- tv->tv_usec = tvusec - 1000000 * tv->tv_sec;
-}
-
-static const char *state_names[5]
-= {"disabled", "listening", "learning", "forwarding", "blocking"};
+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)
{
@@ -52,36 +40,3 @@ const char *br_get_state_name(int state)
return "<INVALID STATE>";
}
-
-struct bridge *br_find_bridge(const char *brname)
-{
- struct bridge *b;
-
- b = bridge_list;
- while (b != NULL) {
- if (!strcmp(b->ifname, brname))
- return b;
-
- b = b->next;
- }
-
- return NULL;
-}
-
-
-struct port *br_find_port(struct bridge *br, const char *portname)
-{
- int index;
- struct port *p;
-
- index = if_nametoindex(portname);
- if (index <= 0)
- return NULL;
-
- for (p = br->firstport; p; p = p->next) {
- if (p->ifindex == index)
- return p;
- }
-
- return NULL;
-}
diff --git a/libbridge/libbridge_private.h b/libbridge/libbridge_private.h
index 98ded65..57d0233 100644
--- a/libbridge/libbridge_private.h
+++ b/libbridge/libbridge_private.h
@@ -19,16 +19,56 @@
#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>
#include <asm/param.h>
+#ifdef HAVE_LIBSYSFS
+#include <sysfs/libsysfs.h>
+
+#ifndef SYSFS_BRIDGE_PORT_ATTR
+#error Using wrong kernel headers if_bridge.h is out of date.
+#endif
+
+#ifndef SIOCBRADDBR
+#error Using wrong kernel headers sockios.h is out of date.
+#endif
+
+#else
+struct sysfs_class { const char *name; };
+
+static inline struct sysfs_class *sysfs_open_class(const char *name)
+{
+ return NULL;
+}
+
+static inline void sysfs_close_class(struct sysfs_class *class)
+{
+}
+#endif
+
extern int br_socket_fd;
+extern struct sysfs_class *br_class_net;
+
+static inline unsigned long __tv_to_jiffies(const struct timeval *tv)
+{
+ unsigned long long jif;
+
+ jif = 1000000ULL * tv->tv_sec + tv->tv_usec;
-void __jiffies_to_tv(struct timeval *tv, unsigned long jiffies);
-unsigned long __tv_to_jiffies(struct timeval *tv);
+ return (HZ*jif)/1000000;
+}
-int br_get_br(unsigned long arg0, unsigned long arg1, unsigned long arg2);
-int br_set_br(unsigned long arg0, unsigned long arg1, unsigned long arg2);
-int br_device_ioctl(const struct bridge *br, unsigned long arg0,
- unsigned long arg1, unsigned long arg2, unsigned long arg3);
+static inline void __jiffies_to_tv(struct timeval *tv, unsigned long jiffies)
+{
+ unsigned long long tvusec;
+ tvusec = (1000000ULL*jiffies)/HZ;
+ tv->tv_sec = tvusec/1000000;
+ tv->tv_usec = tvusec - 1000000 * tv->tv_sec;
+}
#endif