mirror of
https://git.proxmox.com/git/mirror_iproute2
synced 2025-10-04 11:24:58 +00:00
Add bridge command
New tool to allow manipulating forwarding entries and monitoring bridge events.
This commit is contained in:
parent
bc84585e47
commit
d04bc300c3
2
Makefile
2
Makefile
@ -38,7 +38,7 @@ WFLAGS = -Wall -Wstrict-prototypes
|
|||||||
CFLAGS = $(WFLAGS) $(CCOPTS) -I../include $(DEFINES)
|
CFLAGS = $(WFLAGS) $(CCOPTS) -I../include $(DEFINES)
|
||||||
YACCFLAGS = -d -t -v
|
YACCFLAGS = -d -t -v
|
||||||
|
|
||||||
SUBDIRS=lib ip tc misc netem genl man
|
SUBDIRS=lib ip tc bridge misc netem genl man
|
||||||
|
|
||||||
LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a
|
LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a
|
||||||
LDLIBS += $(LIBNETLINK)
|
LDLIBS += $(LIBNETLINK)
|
||||||
|
1
bridge/.gitignore
vendored
Normal file
1
bridge/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
bridge
|
14
bridge/Makefile
Normal file
14
bridge/Makefile
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
BROBJ = bridge.o fdb.o monitor.o link.o
|
||||||
|
|
||||||
|
include ../Config
|
||||||
|
|
||||||
|
all: bridge
|
||||||
|
|
||||||
|
bridge: $(BROBJ) $(LIBNETLINK)
|
||||||
|
|
||||||
|
install: all
|
||||||
|
install -m 0755 br $(DESTDIR)$(SBINDIR)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(BROBJ) br
|
||||||
|
|
13
bridge/br_common.h
Normal file
13
bridge/br_common.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
extern int print_linkinfo(const struct sockaddr_nl *who,
|
||||||
|
struct nlmsghdr *n,
|
||||||
|
void *arg);
|
||||||
|
extern int print_fdb(const struct sockaddr_nl *who,
|
||||||
|
struct nlmsghdr *n, void *arg);
|
||||||
|
|
||||||
|
extern int do_fdb(int argc, char **argv);
|
||||||
|
extern int do_monitor(int argc, char **argv);
|
||||||
|
|
||||||
|
extern int show_stats;
|
||||||
|
extern int show_detail;
|
||||||
|
extern int timestamp;
|
||||||
|
extern struct rtnl_handle rth;
|
104
bridge/bridge.c
Normal file
104
bridge/bridge.c
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Get/set/delete bridge with netlink
|
||||||
|
*
|
||||||
|
* Authors: Stephen Hemminger <shemminger@vyatta.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "SNAPSHOT.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "br_common.h"
|
||||||
|
|
||||||
|
struct rtnl_handle rth = { .fd = -1 };
|
||||||
|
int resolve_hosts;
|
||||||
|
int show_stats;
|
||||||
|
int show_details;
|
||||||
|
int timestamp;
|
||||||
|
|
||||||
|
static void usage(void) __attribute__((noreturn));
|
||||||
|
|
||||||
|
static void usage(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: br [ OPTIONS ] OBJECT { COMMAND | help }\n"
|
||||||
|
"where OBJECT := { fdb | monitor }\n"
|
||||||
|
" OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails]\n" );
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_help(int argc, char **argv)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const struct cmd {
|
||||||
|
const char *cmd;
|
||||||
|
int (*func)(int argc, char **argv);
|
||||||
|
} cmds[] = {
|
||||||
|
{ "fdb", do_fdb },
|
||||||
|
{ "monitor", do_monitor },
|
||||||
|
{ "help", do_help },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static int do_cmd(const char *argv0, int argc, char **argv)
|
||||||
|
{
|
||||||
|
const struct cmd *c;
|
||||||
|
|
||||||
|
for (c = cmds; c->cmd; ++c) {
|
||||||
|
if (matches(argv0, c->cmd) == 0)
|
||||||
|
return c->func(argc-1, argv+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Object \"%s\" is unknown, try \"br help\".\n", argv0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
while (argc > 1) {
|
||||||
|
char *opt = argv[1];
|
||||||
|
if (strcmp(opt,"--") == 0) {
|
||||||
|
argc--; argv++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (opt[0] != '-')
|
||||||
|
break;
|
||||||
|
if (opt[1] == '-')
|
||||||
|
opt++;
|
||||||
|
|
||||||
|
if (matches(opt, "-help") == 0) {
|
||||||
|
usage();
|
||||||
|
} else if (matches(opt, "-Version") == 0) {
|
||||||
|
printf("br utility, 0.0\n");
|
||||||
|
exit(0);
|
||||||
|
} else if (matches(opt, "-stats") == 0 ||
|
||||||
|
matches(opt, "-statistics") == 0) {
|
||||||
|
++show_stats;
|
||||||
|
} else if (matches(opt, "-details") == 0) {
|
||||||
|
++show_details;
|
||||||
|
} else if (matches(opt, "-timestamp") == 0) {
|
||||||
|
++timestamp;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Option \"%s\" is unknown, try \"br -help\".\n", opt);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
argc--; argv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rtnl_open(&rth, 0) < 0)
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
return do_cmd(argv[1], argc-1, argv+1);
|
||||||
|
|
||||||
|
rtnl_close(&rth);
|
||||||
|
usage();
|
||||||
|
}
|
244
bridge/fdb.c
Normal file
244
bridge/fdb.c
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
/*
|
||||||
|
* Get/set/delete fdb table with netlink
|
||||||
|
*
|
||||||
|
* Authors: Stephen Hemminger <shemminger@vyatta.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <linux/if_bridge.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/neighbour.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "libnetlink.h"
|
||||||
|
#include "br_common.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
int filter_index;
|
||||||
|
|
||||||
|
static void usage(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: br fdb { add | del | replace } ADDR dev DEV\n");
|
||||||
|
fprintf(stderr, " br fdb {show} [ dev DEV ]\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *state_n2a(unsigned s)
|
||||||
|
{
|
||||||
|
static char buf[32];
|
||||||
|
|
||||||
|
if (s & NUD_PERMANENT)
|
||||||
|
return "local";
|
||||||
|
|
||||||
|
if (s & NUD_NOARP)
|
||||||
|
return "static";
|
||||||
|
|
||||||
|
if (s & NUD_STALE)
|
||||||
|
return "stale";
|
||||||
|
|
||||||
|
if (s & NUD_REACHABLE)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
sprintf(buf, "state=%#x", s);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *fmt_time(char *b, size_t l, unsigned long tick)
|
||||||
|
{
|
||||||
|
static int hz;
|
||||||
|
|
||||||
|
if (hz == 0)
|
||||||
|
hz = __get_user_hz();
|
||||||
|
|
||||||
|
snprintf(b, l, "%lu.%02lu", tick / hz, ((tick % hz) * hz) / 100);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
|
||||||
|
{
|
||||||
|
struct ndmsg *r = NLMSG_DATA(n);
|
||||||
|
int len = n->nlmsg_len;
|
||||||
|
struct rtattr * tb[NDA_MAX+1];
|
||||||
|
const __u8 *addr = NULL;
|
||||||
|
char b1[32];
|
||||||
|
|
||||||
|
len -= NLMSG_LENGTH(sizeof(*r));
|
||||||
|
if (len < 0) {
|
||||||
|
fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r->ndm_family != AF_BRIDGE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (filter_index && filter_index != r->ndm_ifindex)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
parse_rtattr(tb, NDA_MAX, NDA_RTA(r),
|
||||||
|
n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
|
||||||
|
|
||||||
|
if (n->nlmsg_type == RTM_DELNEIGH)
|
||||||
|
printf("Deleted ");
|
||||||
|
|
||||||
|
if (tb[NDA_LLADDR])
|
||||||
|
addr = RTA_DATA(tb[NDA_LLADDR]);
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "missing lladdr\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s\t%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t%s",
|
||||||
|
ll_index_to_name(r->ndm_ifindex),
|
||||||
|
addr[0], addr[1], addr[2],
|
||||||
|
addr[3], addr[4], addr[5],
|
||||||
|
state_n2a(r->ndm_state));
|
||||||
|
|
||||||
|
if (show_stats && tb[NDA_CACHEINFO]) {
|
||||||
|
struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
|
||||||
|
|
||||||
|
printf("\t%8s", fmt_time(b1, sizeof(b1), ci->ndm_updated));
|
||||||
|
printf(" %8s", fmt_time(b1, sizeof(b1), ci->ndm_used));
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fdb_show(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *filter_dev = NULL;
|
||||||
|
|
||||||
|
while (argc > 0) {
|
||||||
|
if (strcmp(*argv, "dev") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (filter_dev)
|
||||||
|
duparg("dev", *argv);
|
||||||
|
filter_dev = *argv;
|
||||||
|
}
|
||||||
|
argc--; argv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter_dev) {
|
||||||
|
if ((filter_index = if_nametoindex(filter_dev)) == 0) {
|
||||||
|
fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETNEIGH) < 0) {
|
||||||
|
perror("Cannot send dump request");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("port\tmac addr\t\tflags%s\n",
|
||||||
|
show_stats ? "\t updated used" : "");
|
||||||
|
|
||||||
|
if (rtnl_dump_filter(&rth, print_fdb, NULL) < 0) {
|
||||||
|
fprintf(stderr, "Dump terminated\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fdb_modify(int cmd, int flags, int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
struct nlmsghdr n;
|
||||||
|
struct ndmsg ndm;
|
||||||
|
char buf[256];
|
||||||
|
} req;
|
||||||
|
char *addr = NULL;
|
||||||
|
char *d = NULL;
|
||||||
|
char abuf[ETH_ALEN];
|
||||||
|
|
||||||
|
memset(&req, 0, sizeof(req));
|
||||||
|
|
||||||
|
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
|
||||||
|
req.n.nlmsg_flags = NLM_F_REQUEST|flags;
|
||||||
|
req.n.nlmsg_type = cmd;
|
||||||
|
req.ndm.ndm_family = PF_BRIDGE;
|
||||||
|
req.ndm.ndm_state = NUD_NOARP;
|
||||||
|
|
||||||
|
while (argc > 0) {
|
||||||
|
if (strcmp(*argv, "dev") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
d = *argv;
|
||||||
|
} else if (strcmp(*argv, "local") == 0) {
|
||||||
|
req.ndm.ndm_state = NUD_PERMANENT;
|
||||||
|
} else if (strcmp(*argv, "temp") == 0) {
|
||||||
|
req.ndm.ndm_state = NUD_REACHABLE;
|
||||||
|
} else {
|
||||||
|
if (strcmp(*argv, "to") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
}
|
||||||
|
if (matches(*argv, "help") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
}
|
||||||
|
if (addr)
|
||||||
|
duparg2("to", *argv);
|
||||||
|
addr = *argv;
|
||||||
|
}
|
||||||
|
argc--; argv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d == NULL || addr == NULL) {
|
||||||
|
fprintf(stderr, "Device and address are required arguments.\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
||||||
|
abuf, abuf+1, abuf+2,
|
||||||
|
abuf+3, abuf+4, abuf+5) != 6) {
|
||||||
|
fprintf(stderr, "Invalid mac address %s\n", addr);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
addattr_l(&req.n, sizeof(req), NDA_LLADDR, abuf, ETH_ALEN);
|
||||||
|
|
||||||
|
req.ndm.ndm_ifindex = ll_name_to_index(d);
|
||||||
|
if (req.ndm.ndm_ifindex == 0) {
|
||||||
|
fprintf(stderr, "Cannot find device \"%s\"\n", d);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
|
||||||
|
exit(2);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_fdb(int argc, char **argv)
|
||||||
|
{
|
||||||
|
ll_init_map(&rth);
|
||||||
|
|
||||||
|
if (argc > 0) {
|
||||||
|
if (matches(*argv, "add") == 0)
|
||||||
|
return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
|
||||||
|
if (matches(*argv, "change") == 0)
|
||||||
|
return fdb_modify(RTM_NEWNEIGH, NLM_F_REPLACE, argc-1, argv+1);
|
||||||
|
|
||||||
|
if (matches(*argv, "replace") == 0)
|
||||||
|
return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
|
||||||
|
if (matches(*argv, "delete") == 0)
|
||||||
|
return fdb_modify(RTM_DELNEIGH, 0, argc-1, argv+1);
|
||||||
|
if (matches(*argv, "show") == 0 ||
|
||||||
|
matches(*argv, "lst") == 0 ||
|
||||||
|
matches(*argv, "list") == 0)
|
||||||
|
return fdb_show(argc-1, argv+1);
|
||||||
|
if (matches(*argv, "help") == 0)
|
||||||
|
usage();
|
||||||
|
} else
|
||||||
|
return fdb_show(0, NULL);
|
||||||
|
|
||||||
|
fprintf(stderr, "Command \"%s\" is unknown, try \"ip neigh help\".\n", *argv);
|
||||||
|
exit(-1);
|
||||||
|
}
|
142
bridge/link.c
Normal file
142
bridge/link.c
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <linux/if.h>
|
||||||
|
#include <linux/if_bridge.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "br_common.h"
|
||||||
|
|
||||||
|
static const char *port_states[] = {
|
||||||
|
[BR_STATE_DISABLED] = "disabled",
|
||||||
|
[BR_STATE_LISTENING] = "listening",
|
||||||
|
[BR_STATE_LEARNING] = "learning",
|
||||||
|
[BR_STATE_FORWARDING] = "forwarding",
|
||||||
|
[BR_STATE_BLOCKING] = "blocking",
|
||||||
|
};
|
||||||
|
|
||||||
|
extern char *if_indextoname (unsigned int __ifindex, char *__ifname);
|
||||||
|
|
||||||
|
static void print_link_flags(FILE *fp, unsigned flags)
|
||||||
|
{
|
||||||
|
fprintf(fp, "<");
|
||||||
|
if (flags & IFF_UP && !(flags & IFF_RUNNING))
|
||||||
|
fprintf(fp, "NO-CARRIER%s", flags ? "," : "");
|
||||||
|
flags &= ~IFF_RUNNING;
|
||||||
|
#define _PF(f) if (flags&IFF_##f) { \
|
||||||
|
flags &= ~IFF_##f ; \
|
||||||
|
fprintf(fp, #f "%s", flags ? "," : ""); }
|
||||||
|
_PF(LOOPBACK);
|
||||||
|
_PF(BROADCAST);
|
||||||
|
_PF(POINTOPOINT);
|
||||||
|
_PF(MULTICAST);
|
||||||
|
_PF(NOARP);
|
||||||
|
_PF(ALLMULTI);
|
||||||
|
_PF(PROMISC);
|
||||||
|
_PF(MASTER);
|
||||||
|
_PF(SLAVE);
|
||||||
|
_PF(DEBUG);
|
||||||
|
_PF(DYNAMIC);
|
||||||
|
_PF(AUTOMEDIA);
|
||||||
|
_PF(PORTSEL);
|
||||||
|
_PF(NOTRAILERS);
|
||||||
|
_PF(UP);
|
||||||
|
_PF(LOWER_UP);
|
||||||
|
_PF(DORMANT);
|
||||||
|
_PF(ECHO);
|
||||||
|
#undef _PF
|
||||||
|
if (flags)
|
||||||
|
fprintf(fp, "%x", flags);
|
||||||
|
fprintf(fp, "> ");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *oper_states[] = {
|
||||||
|
"UNKNOWN", "NOTPRESENT", "DOWN", "LOWERLAYERDOWN",
|
||||||
|
"TESTING", "DORMANT", "UP"
|
||||||
|
};
|
||||||
|
|
||||||
|
static void print_operstate(FILE *f, __u8 state)
|
||||||
|
{
|
||||||
|
if (state >= sizeof(oper_states)/sizeof(oper_states[0]))
|
||||||
|
fprintf(f, "state %#x ", state);
|
||||||
|
else
|
||||||
|
fprintf(f, "state %s ", oper_states[state]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int print_linkinfo(const struct sockaddr_nl *who,
|
||||||
|
struct nlmsghdr *n, void *arg)
|
||||||
|
{
|
||||||
|
FILE *fp = arg;
|
||||||
|
int len = n->nlmsg_len;
|
||||||
|
struct ifinfomsg *ifi = NLMSG_DATA(n);
|
||||||
|
struct rtattr * tb[IFLA_MAX+1];
|
||||||
|
char b1[IFNAMSIZ];
|
||||||
|
|
||||||
|
len -= NLMSG_LENGTH(sizeof(*ifi));
|
||||||
|
if (len < 0) {
|
||||||
|
fprintf(stderr, "Message too short!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(ifi->ifi_family == AF_BRIDGE || ifi->ifi_family == AF_UNSPEC))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
|
||||||
|
|
||||||
|
if (tb[IFLA_IFNAME] == NULL) {
|
||||||
|
fprintf(stderr, "BUG: nil ifname\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->nlmsg_type == RTM_DELLINK)
|
||||||
|
fprintf(fp, "Deleted ");
|
||||||
|
|
||||||
|
fprintf(fp, "%d: %s ", ifi->ifi_index,
|
||||||
|
tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>");
|
||||||
|
|
||||||
|
if (tb[IFLA_OPERSTATE])
|
||||||
|
print_operstate(fp, *(__u8 *)RTA_DATA(tb[IFLA_OPERSTATE]));
|
||||||
|
|
||||||
|
if (tb[IFLA_LINK]) {
|
||||||
|
SPRINT_BUF(b1);
|
||||||
|
int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
|
||||||
|
|
||||||
|
if (iflink == 0)
|
||||||
|
fprintf(fp, "@NONE: ");
|
||||||
|
else {
|
||||||
|
fprintf(fp, "@%s: ",
|
||||||
|
if_indextoname(iflink, b1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(fp, ": ");
|
||||||
|
}
|
||||||
|
|
||||||
|
print_link_flags(fp, ifi->ifi_flags);
|
||||||
|
|
||||||
|
if (tb[IFLA_MTU])
|
||||||
|
fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
|
||||||
|
|
||||||
|
if (tb[IFLA_MASTER]) {
|
||||||
|
fprintf(fp, "master %s ",
|
||||||
|
if_indextoname(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tb[IFLA_PROTINFO]) {
|
||||||
|
uint8_t state = *(uint8_t *)RTA_DATA(tb[IFLA_PROTINFO]);
|
||||||
|
if (state <= BR_STATE_BLOCKING)
|
||||||
|
fprintf(fp, "state %s", port_states[state]);
|
||||||
|
else
|
||||||
|
fprintf(fp, "state (%d)", state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
fflush(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
138
bridge/monitor.c
Normal file
138
bridge/monitor.c
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* brmonitor.c "br monitor"
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Authors: Stephen Hemminger <shemminger@vyatta.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <linux/if_bridge.h>
|
||||||
|
#include <linux/neighbour.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "br_common.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void usage(void) __attribute__((noreturn));
|
||||||
|
int prefix_banner;
|
||||||
|
|
||||||
|
static void usage(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: br monitor\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int show_mark(FILE *fp, const struct nlmsghdr *n)
|
||||||
|
{
|
||||||
|
char *tstr;
|
||||||
|
time_t secs = ((__u32*)NLMSG_DATA(n))[0];
|
||||||
|
long usecs = ((__u32*)NLMSG_DATA(n))[1];
|
||||||
|
tstr = asctime(localtime(&secs));
|
||||||
|
tstr[strlen(tstr)-1] = 0;
|
||||||
|
fprintf(fp, "Timestamp: %s %lu us\n", tstr, usecs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int accept_msg(const struct sockaddr_nl *who,
|
||||||
|
struct nlmsghdr *n, void *arg)
|
||||||
|
{
|
||||||
|
FILE *fp = arg;
|
||||||
|
|
||||||
|
if (timestamp)
|
||||||
|
print_timestamp(fp);
|
||||||
|
|
||||||
|
switch (n->nlmsg_type) {
|
||||||
|
case RTM_NEWLINK:
|
||||||
|
case RTM_DELLINK:
|
||||||
|
if (prefix_banner)
|
||||||
|
fprintf(fp, "[LINK]");
|
||||||
|
|
||||||
|
return print_linkinfo(who, n, arg);
|
||||||
|
|
||||||
|
case RTM_NEWNEIGH:
|
||||||
|
case RTM_DELNEIGH:
|
||||||
|
if (prefix_banner)
|
||||||
|
fprintf(fp, "[NEIGH]");
|
||||||
|
return print_fdb(who, n, arg);
|
||||||
|
|
||||||
|
case 15:
|
||||||
|
return show_mark(fp, n);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_monitor(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *file = NULL;
|
||||||
|
unsigned groups = ~RTMGRP_TC;
|
||||||
|
int llink=0;
|
||||||
|
int lneigh=0;
|
||||||
|
|
||||||
|
rtnl_close(&rth);
|
||||||
|
|
||||||
|
while (argc > 0) {
|
||||||
|
if (matches(*argv, "file") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
file = *argv;
|
||||||
|
} else if (matches(*argv, "link") == 0) {
|
||||||
|
llink=1;
|
||||||
|
groups = 0;
|
||||||
|
} else if (matches(*argv, "fdb") == 0) {
|
||||||
|
lneigh = 1;
|
||||||
|
groups = 0;
|
||||||
|
} else if (strcmp(*argv, "all") == 0) {
|
||||||
|
groups = ~RTMGRP_TC;
|
||||||
|
prefix_banner=1;
|
||||||
|
} else if (matches(*argv, "help") == 0) {
|
||||||
|
usage();
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Argument \"%s\" is unknown, try \"br monitor help\".\n", *argv);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
argc--; argv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llink)
|
||||||
|
groups |= nl_mgrp(RTNLGRP_LINK);
|
||||||
|
|
||||||
|
if (lneigh) {
|
||||||
|
groups |= nl_mgrp(RTNLGRP_NEIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
FILE *fp;
|
||||||
|
fp = fopen(file, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
perror("Cannot fopen");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
return rtnl_from_file(fp, accept_msg, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rtnl_open(&rth, groups) < 0)
|
||||||
|
exit(1);
|
||||||
|
ll_init_map(&rth);
|
||||||
|
|
||||||
|
if (rtnl_listen(&rth, accept_msg, stdout) < 0)
|
||||||
|
exit(2);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ MAN8PAGES = $(TARGETS) ip.8 arpd.8 lnstat.8 routel.8 rtacct.8 rtmon.8 ss.8 \
|
|||||||
tc-bfifo.8 tc-cbq-details.8 tc-cbq.8 tc-drr.8 tc-htb.8 \
|
tc-bfifo.8 tc-cbq-details.8 tc-cbq.8 tc-drr.8 tc-htb.8 \
|
||||||
tc-pfifo.8 tc-pfifo_fast.8 tc-prio.8 tc-red.8 tc-sfq.8 \
|
tc-pfifo.8 tc-pfifo_fast.8 tc-prio.8 tc-red.8 tc-sfq.8 \
|
||||||
tc-tbf.8 tc.8tc-codel.8 tc-fq_codel.8 tc-sfb.8 tc-netem.8 tc-choke.8 \
|
tc-tbf.8 tc.8tc-codel.8 tc-fq_codel.8 tc-sfb.8 tc-netem.8 tc-choke.8 \
|
||||||
rtstat.8 ctstat.8 nstat.8 routef.8 \
|
bridge.8 rtstat.8 ctstat.8 nstat.8 routef.8 \
|
||||||
ip-tunnel.8 ip-rule.8 ip-ntable.8 \
|
ip-tunnel.8 ip-rule.8 ip-ntable.8 \
|
||||||
ip-monitor.8 tc-stab.8 tc-hfsc.8 ip-xfrm.8 ip-netns.8 \
|
ip-monitor.8 tc-stab.8 tc-hfsc.8 ip-xfrm.8 ip-netns.8 \
|
||||||
ip-neighbour.8 ip-mroute.8 ip-maddress.8 ip-addrlabel.8
|
ip-neighbour.8 ip-mroute.8 ip-maddress.8 ip-addrlabel.8
|
||||||
|
186
man/man8/bridge.8
Normal file
186
man/man8/bridge.8
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
.TH BRIDGE 8 "1 August 2012" "iproute2" "Linux"
|
||||||
|
.SH NAME
|
||||||
|
bridge \- show / manipulate bridge addresses and devices
|
||||||
|
.SH SYNOPSIS
|
||||||
|
|
||||||
|
.ad l
|
||||||
|
.in +8
|
||||||
|
.ti -8
|
||||||
|
.B bridge
|
||||||
|
.RI "[ " OPTIONS " ] " OBJECT " { " COMMAND " | "
|
||||||
|
.BR help " }"
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR OBJECT " := { "
|
||||||
|
.BR fdb " | " monitor " }"
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR OPTIONS " := { "
|
||||||
|
\fB\-V\fR[\fIersion\fR] |
|
||||||
|
\fB\-s\fR[\fItatistics\fR]
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.BR "bridge fdb" " { " add " | " del " | " change " | " replace " } "
|
||||||
|
.I LLADDR
|
||||||
|
.B dev
|
||||||
|
.IR DEV " { "
|
||||||
|
.BR local " | " temp " }"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.BR "bridge fdb" " [ " show " ] [ "
|
||||||
|
.B dev
|
||||||
|
.IR DEV " ]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.BR "bridge monitor" " [ " all " | " neigh " | " link " ]"
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-V" , " -Version"
|
||||||
|
print the version of the
|
||||||
|
.B bridge
|
||||||
|
utility and exit.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-s" , " \-stats", " \-statistics"
|
||||||
|
output more information. If the option
|
||||||
|
appears twice or more, the amount of information increases.
|
||||||
|
As a rule, the information is statistics or some time values.
|
||||||
|
|
||||||
|
|
||||||
|
.SH BRIDGE - COMMAND SYNTAX
|
||||||
|
|
||||||
|
.SS
|
||||||
|
.I OBJECT
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B fdb
|
||||||
|
- Forwarding Database entry.
|
||||||
|
|
||||||
|
.SS
|
||||||
|
.I COMMAND
|
||||||
|
|
||||||
|
Specifies the action to perform on the object.
|
||||||
|
The set of possible actions depends on the object type.
|
||||||
|
As a rule, it is possible to
|
||||||
|
.BR "add" , " delete"
|
||||||
|
and
|
||||||
|
.B show
|
||||||
|
(or
|
||||||
|
.B list
|
||||||
|
) objects, but some objects do not allow all of these operations
|
||||||
|
or have some additional commands. The
|
||||||
|
.B help
|
||||||
|
command is available for all objects. It prints
|
||||||
|
out a list of available commands and argument syntax conventions.
|
||||||
|
.sp
|
||||||
|
If no command is given, some default command is assumed.
|
||||||
|
Usually it is
|
||||||
|
.B list
|
||||||
|
or, if the objects of this class cannot be listed,
|
||||||
|
.BR "help" .
|
||||||
|
|
||||||
|
.SH bridge fdb - forwarding database management
|
||||||
|
|
||||||
|
.B fdb
|
||||||
|
objects contain known Ethernet addresses on a link.
|
||||||
|
|
||||||
|
.P
|
||||||
|
The corresponding commands display fdb entries, add new entries,
|
||||||
|
and delete old ones.
|
||||||
|
|
||||||
|
.SS bridge fdb add - add a new neighbor entry
|
||||||
|
.SS bridge fdb change - change an existing entry
|
||||||
|
.SS bridge fdb replace - add a new entry or change an existing one
|
||||||
|
|
||||||
|
These commands create new neighbor records or update existing ones.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "ADDRESS"
|
||||||
|
the Ethernet MAC address.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI dev " NAME"
|
||||||
|
the interface to which this address is associated.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.in +8
|
||||||
|
.B local
|
||||||
|
- the address is associated with a local interface on the system
|
||||||
|
and is never forwarded.
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.B temp
|
||||||
|
- the address is a dynamic entry, and will be removed if not used.
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.in -8
|
||||||
|
|
||||||
|
.SS bridge fdb delete - delete a forwarding database entry
|
||||||
|
This command removes an existing fdb entry.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
The arguments are the same as with
|
||||||
|
.BR "bridge fdb add" ,
|
||||||
|
|
||||||
|
.SS bridge fdb show - list forwarding entries.
|
||||||
|
|
||||||
|
This commands displays current forwarding table.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
With the
|
||||||
|
.B -statistics
|
||||||
|
option, the command becomes verbose. It prints out the last updated
|
||||||
|
and last used time for each entry.
|
||||||
|
|
||||||
|
.SH bridge monitor - state monitoring
|
||||||
|
|
||||||
|
The
|
||||||
|
.B bridge
|
||||||
|
utility can monitor the state of devices and addresses
|
||||||
|
continuously. This option has a slightly different format.
|
||||||
|
Namely, the
|
||||||
|
.B monitor
|
||||||
|
command is the first in the command line and then the object list follows:
|
||||||
|
|
||||||
|
.BR "bridge monitor" " [ " all " |"
|
||||||
|
.IR LISTofOBJECTS " ]"
|
||||||
|
|
||||||
|
.I OBJECT-LIST
|
||||||
|
is the list of object types that we want to monitor.
|
||||||
|
It may contain
|
||||||
|
.BR link ", and " fdb "."
|
||||||
|
If no
|
||||||
|
.B file
|
||||||
|
argument is given,
|
||||||
|
.B bridge
|
||||||
|
opens RTNETLINK, listens on it and dumps state changes in the format
|
||||||
|
described in previous sections.
|
||||||
|
|
||||||
|
.P
|
||||||
|
If a file name is given, it does not listen on RTNETLINK,
|
||||||
|
but opens the file containing RTNETLINK messages saved in binary format
|
||||||
|
and dumps them. Such a history file can be generated with the
|
||||||
|
|
||||||
|
|
||||||
|
.SH NOTES
|
||||||
|
This command uses facilities added in Linux 3.0.
|
||||||
|
|
||||||
|
Although the forwarding table is maintained on a per-bridge device basis
|
||||||
|
the bridge device is not part of the syntax. This is a limitation of the
|
||||||
|
underlying netlink neighbour message protocol. When displaying the
|
||||||
|
forwarding table, entries for all bridges are displayed.
|
||||||
|
Add/delete/modify commands determine the underlying bridge device
|
||||||
|
based on the bridge to which the coresponding ethernet device is attached.
|
||||||
|
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR ip (8)
|
||||||
|
.br
|
||||||
|
.RB "Please direct bugreports and patches to: " <netdev@vger.kernel.org>
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Original Manpage by Stephen Hemminger
|
Loading…
Reference in New Issue
Block a user