From f07516c3b0c33cee34e2a93dad1046f7c2c04313 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 18 Apr 2021 15:01:32 +0300 Subject: [PATCH 1/6] bridge: rename and export print_portstate Rename print_portstate to print_stp_state in preparation for use by vlan code as well (per-vlan state), and export it. To be in line with the new naming rename also port_states to stp_states as they'll be used for vlans, too. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David Ahern --- bridge/br_common.h | 1 + bridge/link.c | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/bridge/br_common.h b/bridge/br_common.h index b5798da3..e3f46765 100644 --- a/bridge/br_common.h +++ b/bridge/br_common.h @@ -10,6 +10,7 @@ void print_vlan_info(struct rtattr *tb, int ifindex); int print_linkinfo(struct nlmsghdr *n, void *arg); int print_mdb_mon(struct nlmsghdr *n, void *arg); int print_fdb(struct nlmsghdr *n, void *arg); +void print_stp_state(__u8 state); int do_fdb(int argc, char **argv); int do_mdb(int argc, char **argv); diff --git a/bridge/link.c b/bridge/link.c index d88c469d..a8cfa181 100644 --- a/bridge/link.c +++ b/bridge/link.c @@ -19,7 +19,7 @@ static unsigned int filter_index; -static const char *port_states[] = { +static const char *stp_states[] = { [BR_STATE_DISABLED] = "disabled", [BR_STATE_LISTENING] = "listening", [BR_STATE_LEARNING] = "learning", @@ -68,11 +68,11 @@ static void print_link_flags(FILE *fp, unsigned int flags, unsigned int mdown) close_json_array(PRINT_ANY, "> "); } -static void print_portstate(__u8 state) +void print_stp_state(__u8 state) { if (state <= BR_STATE_BLOCKING) print_string(PRINT_ANY, "state", - "state %s ", port_states[state]); + "state %s ", stp_states[state]); else print_uint(PRINT_ANY, "state", "state (%d) ", state); @@ -96,7 +96,7 @@ static void print_protinfo(FILE *fp, struct rtattr *attr) parse_rtattr_nested(prtb, IFLA_BRPORT_MAX, attr); if (prtb[IFLA_BRPORT_STATE]) - print_portstate(rta_getattr_u8(prtb[IFLA_BRPORT_STATE])); + print_stp_state(rta_getattr_u8(prtb[IFLA_BRPORT_STATE])); if (prtb[IFLA_BRPORT_PRIORITY]) print_uint(PRINT_ANY, "priority", @@ -161,7 +161,7 @@ static void print_protinfo(FILE *fp, struct rtattr *attr) print_on_off(PRINT_ANY, "isolated", "isolated %s ", rta_getattr_u8(prtb[IFLA_BRPORT_ISOLATED])); } else - print_portstate(rta_getattr_u8(attr)); + print_stp_state(rta_getattr_u8(attr)); } @@ -359,12 +359,12 @@ static int brlink_modify(int argc, char **argv) } else if (strcmp(*argv, "state") == 0) { NEXT_ARG(); char *endptr; - size_t nstates = ARRAY_SIZE(port_states); + size_t nstates = ARRAY_SIZE(stp_states); state = strtol(*argv, &endptr, 10); if (!(**argv != '\0' && *endptr == '\0')) { for (state = 0; state < nstates; state++) - if (strcasecmp(port_states[state], *argv) == 0) + if (strcasecmp(stp_states[state], *argv) == 0) break; if (state == nstates) { fprintf(stderr, From f2f52fcabea957e9e5c3339a18f00c88f9e91b19 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 18 Apr 2021 15:01:33 +0300 Subject: [PATCH 2/6] bridge: add parse_stp_state helper Add a helper which parses an STP state string to its numeric value. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David Ahern --- bridge/br_common.h | 1 + bridge/link.c | 22 +++++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/bridge/br_common.h b/bridge/br_common.h index e3f46765..33e56452 100644 --- a/bridge/br_common.h +++ b/bridge/br_common.h @@ -11,6 +11,7 @@ int print_linkinfo(struct nlmsghdr *n, void *arg); int print_mdb_mon(struct nlmsghdr *n, void *arg); int print_fdb(struct nlmsghdr *n, void *arg); void print_stp_state(__u8 state); +int parse_stp_state(const char *arg); int do_fdb(int argc, char **argv); int do_mdb(int argc, char **argv); diff --git a/bridge/link.c b/bridge/link.c index a8cfa181..205a2fe7 100644 --- a/bridge/link.c +++ b/bridge/link.c @@ -78,6 +78,21 @@ void print_stp_state(__u8 state) "state (%d) ", state); } +int parse_stp_state(const char *arg) +{ + size_t nstates = ARRAY_SIZE(stp_states); + int state; + + for (state = 0; state < nstates; state++) + if (strcmp(stp_states[state], arg) == 0) + break; + + if (state == nstates) + state = -1; + + return state; +} + static void print_hwmode(__u16 mode) { if (mode >= ARRAY_SIZE(hw_mode)) @@ -359,14 +374,11 @@ static int brlink_modify(int argc, char **argv) } else if (strcmp(*argv, "state") == 0) { NEXT_ARG(); char *endptr; - size_t nstates = ARRAY_SIZE(stp_states); state = strtol(*argv, &endptr, 10); if (!(**argv != '\0' && *endptr == '\0')) { - for (state = 0; state < nstates; state++) - if (strcasecmp(stp_states[state], *argv) == 0) - break; - if (state == nstates) { + state = parse_stp_state(*argv); + if (state == -1) { fprintf(stderr, "Error: invalid STP port state\n"); return -1; From 04e2783d5e1080f5709a679055f7e361793335bf Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 18 Apr 2021 15:01:34 +0300 Subject: [PATCH 3/6] bridge: vlan: add option set command and state option Add a new per-vlan option set command. It allows to manipulate vlan options, those can be bridge-wide or per-port depending on what device is specified. The first option that can be set is the vlan STP state, it is identical to the bridge port STP state. The man page is also updated accordingly. Example: $ bridge vlan set vid 10 dev br0 state learning or a range: $ bridge vlan set vid 10-20 dev swp1 state blocking Signed-off-by: Nikolay Aleksandrov Signed-off-by: David Ahern --- bridge/vlan.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++ man/man8/bridge.8 | 64 +++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) diff --git a/bridge/vlan.c b/bridge/vlan.c index 0d142bc9..09884870 100644 --- a/bridge/vlan.c +++ b/bridge/vlan.c @@ -33,6 +33,7 @@ static void usage(void) "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ tunnel_info id TUNNEL_ID ]\n" " [ pvid ] [ untagged ]\n" " [ self ] [ master ]\n" + " bridge vlan { set } vid VLAN_ID dev DEV [ state STP_STATE ]\n" " bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n" " bridge vlan { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n"); exit(-1); @@ -241,6 +242,100 @@ static int vlan_modify(int cmd, int argc, char **argv) return 0; } +static int vlan_option_set(int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct br_vlan_msg bvm; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_vlan_msg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_NEWVLAN, + .bvm.family = PF_BRIDGE, + }; + struct bridge_vlan_info vinfo = {}; + struct rtattr *afspec; + short vid_end = -1; + char *d = NULL; + short vid = -1; + int state = -1; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + } else if (strcmp(*argv, "vid") == 0) { + char *p; + + NEXT_ARG(); + p = strchr(*argv, '-'); + if (p) { + *p = '\0'; + p++; + vid = atoi(*argv); + vid_end = atoi(p); + if (vid >= vid_end || vid_end >= 4096) { + fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n", + vid, vid_end); + return -1; + } + } else { + vid = atoi(*argv); + } + } else if (strcmp(*argv, "state") == 0) { + char *endptr; + + NEXT_ARG(); + state = strtol(*argv, &endptr, 10); + if (!(**argv != '\0' && *endptr == '\0')) + state = parse_stp_state(*argv); + if (state == -1) { + fprintf(stderr, "Error: invalid STP state\n"); + return -1; + } + } else { + if (matches(*argv, "help") == 0) + NEXT_ARG(); + } + argc--; argv++; + } + + if (d == NULL || vid == -1) { + fprintf(stderr, "Device and VLAN ID are required arguments.\n"); + return -1; + } + + req.bvm.ifindex = ll_name_to_index(d); + if (req.bvm.ifindex == 0) { + fprintf(stderr, "Cannot find network device \"%s\"\n", d); + return -1; + } + + if (vid >= 4096) { + fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid); + return -1; + } + afspec = addattr_nest(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY); + afspec->rta_type |= NLA_F_NESTED; + + vinfo.flags = BRIDGE_VLAN_INFO_ONLY_OPTS; + vinfo.vid = vid; + addattr_l(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_INFO, &vinfo, + sizeof(vinfo)); + if (vid_end != -1) + addattr16(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_RANGE, + vid_end); + if (state >= 0) + addattr8(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_STATE, state); + addattr_nest_end(&req.n, afspec); + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + return -1; + + return 0; +} + /* In order to use this function for both filtering and non-filtering cases * we need to make it a tristate: * return -1 - if filtering we've gone over so don't continue @@ -667,6 +762,8 @@ int do_vlan(int argc, char **argv) if (matches(*argv, "tunnelshow") == 0) { return vlan_show(argc-1, argv+1, VLAN_SHOW_TUNNELINFO); } + if (matches(*argv, "set") == 0) + return vlan_option_set(argc-1, argv+1); if (matches(*argv, "help") == 0) usage(); } else { diff --git a/man/man8/bridge.8 b/man/man8/bridge.8 index 9d8663bd..90dcae73 100644 --- a/man/man8/bridge.8 +++ b/man/man8/bridge.8 @@ -138,6 +138,15 @@ bridge \- show / manipulate bridge addresses and devices .BR pvid " ] [ " untagged " ] [ " .BR self " ] [ " master " ] " +.ti -8 +.BR "bridge vlan set" +.B dev +.I DEV +.B vid +.IR VID " [ " +.B state +.IR STP_STATE " ] " + .ti -8 .BR "bridge vlan" " [ " show " | " tunnelshow " ] [ " .B dev @@ -813,6 +822,61 @@ The .BR "pvid " and " untagged" flags are ignored. +.SS bridge vlan set - change vlan filter entry's options + +This command changes vlan filter entry's options. + +.TP +.BI dev " NAME" +the interface with which this vlan is associated. + +.TP +.BI vid " VID" +the VLAN ID that identifies the vlan. + +.TP +.BI state " STP_STATE " +the operation state of the vlan. One may enter STP state name (case insensitive), or one of the +numbers below. Negative inputs are ignored, and unrecognized names return an +error. Note that the state is set only for the vlan of the specified device, e.g. if it is +a bridge port then the state will be set only for the vlan of the port. + +.B 0 +- vlan is in STP +.B DISABLED +state. Make this vlan completely inactive for STP. This is also called +BPDU filter and could be used to disable STP on an untrusted vlan. +.sp + +.B 1 +- vlan is in STP +.B LISTENING +state. Only valid if STP is enabled on the bridge. In this +state the vlan listens for STP BPDUs and drops all other traffic frames. +.sp + +.B 2 +- vlan is in STP +.B LEARNING +state. Only valid if STP is enabled on the bridge. In this +state the vlan will accept traffic only for the purpose of updating MAC +address tables. +.sp + +.B 3 +- vlan is in STP +.B FORWARDING +state. This is the default vlan state. +.sp + +.B 4 +- vlan is in STP +.B BLOCKING +state. Only valid if STP is enabled on the bridge. This state +is used during the STP election process. In this state, the vlan will only process +STP BPDUs. +.sp + .SS bridge vlan show - list vlan configuration. This command displays the current VLAN filter table. From 34c14bea227e6e52766359f90f37856881e6cfa7 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 18 Apr 2021 15:01:35 +0300 Subject: [PATCH 4/6] libnetlink: add bridge vlan dump request helper Add rtnl bridge vlan dump request helper which will be used to retrieve bridge vlan information and options. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David Ahern --- include/libnetlink.h | 2 ++ lib/libnetlink.c | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/include/libnetlink.h b/include/libnetlink.h index e8ed5d7f..da96c69b 100644 --- a/include/libnetlink.h +++ b/include/libnetlink.h @@ -69,6 +69,8 @@ int rtnl_neightbldump_req(struct rtnl_handle *rth, int family) __attribute__((warn_unused_result)); int rtnl_mdbdump_req(struct rtnl_handle *rth, int family) __attribute__((warn_unused_result)); +int rtnl_brvlandump_req(struct rtnl_handle *rth, int family, __u32 dump_flags) + __attribute__((warn_unused_result)); int rtnl_netconfdump_req(struct rtnl_handle *rth, int family) __attribute__((warn_unused_result)); diff --git a/lib/libnetlink.c b/lib/libnetlink.c index 6885087b..2f2cc1fe 100644 --- a/lib/libnetlink.c +++ b/lib/libnetlink.c @@ -450,6 +450,25 @@ int rtnl_mdbdump_req(struct rtnl_handle *rth, int family) return send(rth->fd, &req, sizeof(req), 0); } +int rtnl_brvlandump_req(struct rtnl_handle *rth, int family, __u32 dump_flags) +{ + struct { + struct nlmsghdr nlh; + struct br_vlan_msg bvm; + char buf[256]; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_vlan_msg)), + .nlh.nlmsg_type = RTM_GETVLAN, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .bvm.family = family, + }; + + addattr32(&req.nlh, sizeof(req), BRIDGE_VLANDB_DUMP_FLAGS, dump_flags); + + return send(rth->fd, &req, sizeof(req), 0); +} + int rtnl_netconfdump_req(struct rtnl_handle *rth, int family) { struct { From e5f87c83419363a0480880294d3b6ff1f38393bd Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 18 Apr 2021 15:01:36 +0300 Subject: [PATCH 5/6] bridge: vlan: add support for the new rtm dump call Use the new bridge vlan rtm dump helper to dump all of the available vlan information when -details (-d) is used with vlan show. It is also capable of dumping vlan stats if -statistics (-s) is added. Currently this is the only interface capable of dumping per-vlan options. The vlan dump format is compatible with current vlan show, it uses the same helpers to dump vlan information. The new addition is one line which will contain the per-vlan options (similar to ip -d link show for ports). Currently only the vlan STP state is printed. The call uses compressed vlan format by default. Example: $ bridge -s -d vlan show port vlan-id virbr1 1 PVID Egress Untagged state forwarding Signed-off-by: Nikolay Aleksandrov Signed-off-by: David Ahern --- bridge/br_common.h | 1 + bridge/vlan.c | 147 ++++++++++++++++++++++++++++++++++++++++--- include/libnetlink.h | 5 ++ man/man8/bridge.8 | 7 ++- 4 files changed, 152 insertions(+), 8 deletions(-) diff --git a/bridge/br_common.h b/bridge/br_common.h index 33e56452..43870546 100644 --- a/bridge/br_common.h +++ b/bridge/br_common.h @@ -12,6 +12,7 @@ int print_mdb_mon(struct nlmsghdr *n, void *arg); int print_fdb(struct nlmsghdr *n, void *arg); void print_stp_state(__u8 state); int parse_stp_state(const char *arg); +int print_vlan_rtm(struct nlmsghdr *n, void *arg); int do_fdb(int argc, char **argv); int do_mdb(int argc, char **argv); diff --git a/bridge/vlan.c b/bridge/vlan.c index 09884870..c681e141 100644 --- a/bridge/vlan.c +++ b/bridge/vlan.c @@ -16,6 +16,7 @@ #include "utils.h" static unsigned int filter_index, filter_vlan; +static int vlan_rtm_cur_ifidx = -1; enum vlan_show_subject { VLAN_SHOW_VLAN, @@ -517,14 +518,8 @@ static void print_vlan_flags(__u16 flags) close_json_array(PRINT_JSON, NULL); } -static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats) +static void __print_one_vlan_stats(const struct bridge_vlan_xstats *vstats) { - open_json_object(NULL); - - print_hu(PRINT_ANY, "vid", "%hu", vstats->vid); - print_vlan_flags(vstats->flags); - print_nl(); - print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", ""); print_lluint(PRINT_ANY, "rx_bytes", "RX: %llu bytes", vstats->rx_bytes); @@ -536,6 +531,16 @@ static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats) vstats->tx_bytes); print_lluint(PRINT_ANY, "tx_packets", " %llu packets\n", vstats->tx_packets); +} + +static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats) +{ + open_json_object(NULL); + + print_hu(PRINT_ANY, "vid", "%hu", vstats->vid); + print_vlan_flags(vstats->flags); + print_nl(); + __print_one_vlan_stats(vstats); close_json_object(); } @@ -616,6 +621,105 @@ static int print_vlan_stats(struct nlmsghdr *n, void *arg) return 0; } +int print_vlan_rtm(struct nlmsghdr *n, void *arg) +{ + struct rtattr *vtb[BRIDGE_VLANDB_ENTRY_MAX + 1], *a; + struct br_vlan_msg *bvm = NLMSG_DATA(n); + int len = n->nlmsg_len; + bool newport = false; + int rem; + + if (n->nlmsg_type != RTM_NEWVLAN && n->nlmsg_type != RTM_DELVLAN && + n->nlmsg_type != RTM_GETVLAN) { + fprintf(stderr, "Unknown vlan rtm message: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*bvm)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (bvm->family != AF_BRIDGE) + return 0; + + if (filter_index && filter_index != bvm->ifindex) + return 0; + + if (vlan_rtm_cur_ifidx == -1 || vlan_rtm_cur_ifidx != bvm->ifindex) { + if (vlan_rtm_cur_ifidx != -1) + close_vlan_port(); + open_vlan_port(bvm->ifindex, VLAN_SHOW_VLAN); + vlan_rtm_cur_ifidx = bvm->ifindex; + newport = true; + } + + rem = len; + for (a = BRVLAN_RTA(bvm); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) { + struct bridge_vlan_xstats vstats; + struct bridge_vlan_info *vinfo; + __u32 vrange = 0; + __u8 state = 0; + + parse_rtattr_flags(vtb, BRIDGE_VLANDB_ENTRY_MAX, RTA_DATA(a), + RTA_PAYLOAD(a), NLA_F_NESTED); + vinfo = RTA_DATA(vtb[BRIDGE_VLANDB_ENTRY_INFO]); + + memset(&vstats, 0, sizeof(vstats)); + if (vtb[BRIDGE_VLANDB_ENTRY_RANGE]) + vrange = rta_getattr_u16(vtb[BRIDGE_VLANDB_ENTRY_RANGE]); + else + vrange = vinfo->vid; + + if (vtb[BRIDGE_VLANDB_ENTRY_STATE]) + state = rta_getattr_u8(vtb[BRIDGE_VLANDB_ENTRY_STATE]); + + if (vtb[BRIDGE_VLANDB_ENTRY_STATS]) { + struct rtattr *stb[BRIDGE_VLANDB_STATS_MAX+1]; + struct rtattr *attr; + + attr = vtb[BRIDGE_VLANDB_ENTRY_STATS]; + parse_rtattr(stb, BRIDGE_VLANDB_STATS_MAX, RTA_DATA(attr), + RTA_PAYLOAD(attr)); + + if (stb[BRIDGE_VLANDB_STATS_RX_BYTES]) { + attr = stb[BRIDGE_VLANDB_STATS_RX_BYTES]; + vstats.rx_bytes = rta_getattr_u64(attr); + } + if (stb[BRIDGE_VLANDB_STATS_RX_PACKETS]) { + attr = stb[BRIDGE_VLANDB_STATS_RX_PACKETS]; + vstats.rx_packets = rta_getattr_u64(attr); + } + if (stb[BRIDGE_VLANDB_STATS_TX_PACKETS]) { + attr = stb[BRIDGE_VLANDB_STATS_TX_PACKETS]; + vstats.tx_packets = rta_getattr_u64(attr); + } + if (stb[BRIDGE_VLANDB_STATS_TX_BYTES]) { + attr = stb[BRIDGE_VLANDB_STATS_TX_BYTES]; + vstats.tx_bytes = rta_getattr_u64(attr); + } + } + open_json_object(NULL); + if (!newport) + print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", ""); + else + newport = false; + print_range("vlan", vinfo->vid, vrange); + print_vlan_flags(vinfo->flags); + print_nl(); + print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", ""); + print_stp_state(state); + print_nl(); + if (show_stats) + __print_one_vlan_stats(&vstats); + close_json_object(); + } + + return 0; +} + static int vlan_show(int argc, char **argv, int subject) { char *filter_dev = NULL; @@ -644,6 +748,34 @@ static int vlan_show(int argc, char **argv, int subject) new_json_obj(json); + /* if show_details is true then use the new bridge vlan dump format */ + if (show_details && subject == VLAN_SHOW_VLAN) { + __u32 dump_flags = show_stats ? BRIDGE_VLANDB_DUMPF_STATS : 0; + + if (rtnl_brvlandump_req(&rth, PF_BRIDGE, dump_flags) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (!is_json_context()) { + printf("%-" __stringify(IFNAMSIZ) "s %-" + __stringify(VLAN_ID_LEN) "s", "port", + "vlan-id"); + printf("\n"); + } + + ret = rtnl_dump_filter(&rth, print_vlan_rtm, &subject); + if (ret < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + if (vlan_rtm_cur_ifidx != -1) + close_vlan_port(); + + goto out; + } + if (!show_stats) { if (rtnl_linkdump_req_filter(&rth, PF_BRIDGE, (compress_vlans ? @@ -697,6 +829,7 @@ static int vlan_show(int argc, char **argv, int subject) } } +out: delete_json_obj(); fflush(stdout); return 0; diff --git a/include/libnetlink.h b/include/libnetlink.h index da96c69b..6bff6bae 100644 --- a/include/libnetlink.h +++ b/include/libnetlink.h @@ -285,6 +285,11 @@ int rtnl_from_file(FILE *, rtnl_listen_filter_t handler, ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct if_stats_msg)))) #endif +#ifndef BRVLAN_RTA +#define BRVLAN_RTA(r) \ + ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_vlan_msg)))) +#endif + /* User defined nlmsg_type which is used mostly for logging netlink * messages from dump file */ #define NLMSG_TSTAMP 15 diff --git a/man/man8/bridge.8 b/man/man8/bridge.8 index 90dcae73..9c8ebac3 100644 --- a/man/man8/bridge.8 +++ b/man/man8/bridge.8 @@ -171,7 +171,7 @@ As a rule, the information is statistics or some time values. .TP .BR "\-d" , " \-details" -print detailed information about MDB router ports. +print detailed information about bridge vlan filter entries or MDB router ports. .TP .BR "\-n" , " \-net" , " \-netns " @@ -881,6 +881,11 @@ STP BPDUs. This command displays the current VLAN filter table. +.PP +With the +.B -details +option, the command becomes verbose. It displays the per-vlan options. + .PP With the .B -statistics From c31140478016611a952e2555e00c425fac7853da Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sun, 18 Apr 2021 15:01:37 +0300 Subject: [PATCH 6/6] bridge: monitor: add support for vlan monitoring Add support for vlan activity monitoring, we display vlan notifications on vlan add/del/options change. The man page and help are also updated accordingly. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David Ahern --- bridge/br_common.h | 2 +- bridge/mdb.c | 2 +- bridge/monitor.c | 19 ++++++++++++++++++- bridge/vlan.c | 15 +++++++++++++-- man/man8/bridge.8 | 4 ++-- 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/bridge/br_common.h b/bridge/br_common.h index 43870546..b9adafd9 100644 --- a/bridge/br_common.h +++ b/bridge/br_common.h @@ -12,7 +12,7 @@ int print_mdb_mon(struct nlmsghdr *n, void *arg); int print_fdb(struct nlmsghdr *n, void *arg); void print_stp_state(__u8 state); int parse_stp_state(const char *arg); -int print_vlan_rtm(struct nlmsghdr *n, void *arg); +int print_vlan_rtm(struct nlmsghdr *n, void *arg, bool monitor); int do_fdb(int argc, char **argv); int do_mdb(int argc, char **argv); diff --git a/bridge/mdb.c b/bridge/mdb.c index ef89258b..b427d878 100644 --- a/bridge/mdb.c +++ b/bridge/mdb.c @@ -16,9 +16,9 @@ #include #include "libnetlink.h" +#include "utils.h" #include "br_common.h" #include "rt_names.h" -#include "utils.h" #include "json_print.h" #ifndef MDBA_RTA diff --git a/bridge/monitor.c b/bridge/monitor.c index 08439a60..88f52f52 100644 --- a/bridge/monitor.c +++ b/bridge/monitor.c @@ -31,7 +31,7 @@ static int prefix_banner; static void usage(void) { - fprintf(stderr, "Usage: bridge monitor [file | link | fdb | mdb | all]\n"); + fprintf(stderr, "Usage: bridge monitor [file | link | fdb | mdb | vlan | all]\n"); exit(-1); } @@ -67,6 +67,12 @@ static int accept_msg(struct rtnl_ctrl_data *ctrl, print_nlmsg_timestamp(fp, n); return 0; + case RTM_NEWVLAN: + case RTM_DELVLAN: + if (prefix_banner) + fprintf(fp, "[VLAN]"); + return print_vlan_rtm(n, arg, true); + default: return 0; } @@ -79,6 +85,7 @@ int do_monitor(int argc, char **argv) int llink = 0; int lneigh = 0; int lmdb = 0; + int lvlan = 0; rtnl_close(&rth); @@ -95,8 +102,12 @@ int do_monitor(int argc, char **argv) } else if (matches(*argv, "mdb") == 0) { lmdb = 1; groups = 0; + } else if (matches(*argv, "vlan") == 0) { + lvlan = 1; + groups = 0; } else if (strcmp(*argv, "all") == 0) { groups = ~RTMGRP_TC; + lvlan = 1; prefix_banner = 1; } else if (matches(*argv, "help") == 0) { usage(); @@ -134,6 +145,12 @@ int do_monitor(int argc, char **argv) if (rtnl_open(&rth, groups) < 0) exit(1); + + if (lvlan && rtnl_add_nl_group(&rth, RTNLGRP_BRVLAN) < 0) { + fprintf(stderr, "Failed to add bridge vlan group to list\n"); + exit(1); + } + ll_init_map(&rth); if (rtnl_listen(&rth, accept_msg, stdout) < 0) diff --git a/bridge/vlan.c b/bridge/vlan.c index c681e141..9bb9e28d 100644 --- a/bridge/vlan.c +++ b/bridge/vlan.c @@ -621,7 +621,7 @@ static int print_vlan_stats(struct nlmsghdr *n, void *arg) return 0; } -int print_vlan_rtm(struct nlmsghdr *n, void *arg) +int print_vlan_rtm(struct nlmsghdr *n, void *arg, bool monitor) { struct rtattr *vtb[BRIDGE_VLANDB_ENTRY_MAX + 1], *a; struct br_vlan_msg *bvm = NLMSG_DATA(n); @@ -648,6 +648,12 @@ int print_vlan_rtm(struct nlmsghdr *n, void *arg) if (filter_index && filter_index != bvm->ifindex) return 0; + if (n->nlmsg_type == RTM_DELVLAN) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + if (monitor) + vlan_rtm_cur_ifidx = -1; + if (vlan_rtm_cur_ifidx == -1 || vlan_rtm_cur_ifidx != bvm->ifindex) { if (vlan_rtm_cur_ifidx != -1) close_vlan_port(); @@ -720,6 +726,11 @@ int print_vlan_rtm(struct nlmsghdr *n, void *arg) return 0; } +static int print_vlan_rtm_filter(struct nlmsghdr *n, void *arg) +{ + return print_vlan_rtm(n, arg, false); +} + static int vlan_show(int argc, char **argv, int subject) { char *filter_dev = NULL; @@ -764,7 +775,7 @@ static int vlan_show(int argc, char **argv, int subject) printf("\n"); } - ret = rtnl_dump_filter(&rth, print_vlan_rtm, &subject); + ret = rtnl_dump_filter(&rth, print_vlan_rtm_filter, &subject); if (ret < 0) { fprintf(stderr, "Dump terminated\n"); exit(1); diff --git a/man/man8/bridge.8 b/man/man8/bridge.8 index 9c8ebac3..eec7df43 100644 --- a/man/man8/bridge.8 +++ b/man/man8/bridge.8 @@ -153,7 +153,7 @@ bridge \- show / manipulate bridge addresses and devices .IR DEV " ]" .ti -8 -.BR "bridge monitor" " [ " all " | " neigh " | " link " | " mdb " ]" +.BR "bridge monitor" " [ " all " | " neigh " | " link " | " mdb " | " vlan " ]" .SH OPTIONS @@ -911,7 +911,7 @@ command is the first in the command line and then the object list follows: .I OBJECT-LIST is the list of object types that we want to monitor. It may contain -.BR link ", " fdb ", and " mdb "." +.BR link ", " fdb ", " vlan " and " mdb "." If no .B file argument is given,