]>
git.proxmox.com Git - mirror_iproute2.git/blob - bridge/mdb.c
1 /* SPDX-License-Identifier: GPL-2.0 */
3 * Get mdb table with netlink
10 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <linux/if_bridge.h>
14 #include <linux/if_ether.h>
16 #include <arpa/inet.h>
18 #include "libnetlink.h"
19 #include "br_common.h"
22 #include "json_print.h"
26 ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg))))
29 static unsigned int filter_index
, filter_vlan
;
31 static void usage(void)
34 "Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [permanent | temp] [vid VID]\n"
35 " bridge mdb {show} [ dev DEV ] [ vid VID ]\n");
39 static bool is_temp_mcast_rtr(__u8 type
)
41 return type
== MDB_RTR_TYPE_TEMP_QUERY
|| type
== MDB_RTR_TYPE_TEMP
;
44 static const char *format_timer(__u32 ticks
)
49 __jiffies_to_tv(&tv
, ticks
);
50 snprintf(tbuf
, sizeof(tbuf
), "%4lu.%.2lu",
51 (unsigned long)tv
.tv_sec
,
52 (unsigned long)tv
.tv_usec
/ 10000);
57 static void __print_router_port_stats(FILE *f
, struct rtattr
*pattr
)
59 struct rtattr
*tb
[MDBA_ROUTER_PATTR_MAX
+ 1];
61 parse_rtattr(tb
, MDBA_ROUTER_PATTR_MAX
, MDB_RTR_RTA(RTA_DATA(pattr
)),
62 RTA_PAYLOAD(pattr
) - RTA_ALIGN(sizeof(uint32_t)));
64 if (tb
[MDBA_ROUTER_PATTR_TIMER
]) {
65 __u32 timer
= rta_getattr_u32(tb
[MDBA_ROUTER_PATTR_TIMER
]);
67 print_string(PRINT_ANY
, "timer", " %s",
71 if (tb
[MDBA_ROUTER_PATTR_TYPE
]) {
72 __u8 type
= rta_getattr_u8(tb
[MDBA_ROUTER_PATTR_TYPE
]);
74 print_string(PRINT_ANY
, "type", " %s",
75 is_temp_mcast_rtr(type
) ? "temp" : "permanent");
79 static void br_print_router_ports(FILE *f
, struct rtattr
*attr
,
82 int rem
= RTA_PAYLOAD(attr
);
85 if (is_json_context())
86 open_json_array(PRINT_JSON
, brifname
);
88 fprintf(f
, "router ports on %s: ", brifname
);
90 for (i
= RTA_DATA(attr
); RTA_OK(i
, rem
); i
= RTA_NEXT(i
, rem
)) {
91 uint32_t *port_ifindex
= RTA_DATA(i
);
92 const char *port_ifname
= ll_index_to_name(*port_ifindex
);
94 if (is_json_context()) {
95 open_json_object(NULL
);
96 print_string(PRINT_JSON
, "port", NULL
, port_ifname
);
99 __print_router_port_stats(f
, i
);
101 } else if (show_stats
) {
102 fprintf(f
, "router ports on %s: %s",
103 brifname
, port_ifname
);
105 __print_router_port_stats(f
, i
);
108 fprintf(f
, "%s ", port_ifname
);
115 close_json_array(PRINT_JSON
, NULL
);
118 static void print_mdb_entry(FILE *f
, int ifindex
, const struct br_mdb_entry
*e
,
119 struct nlmsghdr
*n
, struct rtattr
**tb
)
126 if (filter_vlan
&& e
->vid
!= filter_vlan
)
129 af
= e
->addr
.proto
== htons(ETH_P_IP
) ? AF_INET
: AF_INET6
;
130 src
= af
== AF_INET
? (const void *)&e
->addr
.u
.ip4
:
131 (const void *)&e
->addr
.u
.ip6
;
132 dev
= ll_index_to_name(ifindex
);
134 open_json_object(NULL
);
136 print_int(PRINT_JSON
, "index", NULL
, ifindex
);
137 print_color_string(PRINT_ANY
, COLOR_IFNAME
, "dev", "dev %s", dev
);
138 print_string(PRINT_ANY
, "port", " port %s",
139 ll_index_to_name(e
->ifindex
));
141 print_color_string(PRINT_ANY
, ifa_family_color(af
),
143 inet_ntop(af
, src
, abuf
, sizeof(abuf
)));
145 print_string(PRINT_ANY
, "state", " %s",
146 (e
->state
& MDB_PERMANENT
) ? "permanent" : "temp");
148 open_json_array(PRINT_JSON
, "flags");
149 if (e
->flags
& MDB_FLAGS_OFFLOAD
)
150 print_string(PRINT_ANY
, NULL
, " %s", "offload");
151 close_json_array(PRINT_JSON
, NULL
);
154 print_uint(PRINT_ANY
, "vid", " vid %u", e
->vid
);
156 if (show_stats
&& tb
&& tb
[MDBA_MDB_EATTR_TIMER
]) {
157 __u32 timer
= rta_getattr_u32(tb
[MDBA_MDB_EATTR_TIMER
]);
159 print_string(PRINT_ANY
, "timer", " %s",
160 format_timer(timer
));
167 static void br_print_mdb_entry(FILE *f
, int ifindex
, struct rtattr
*attr
,
170 struct rtattr
*etb
[MDBA_MDB_EATTR_MAX
+ 1];
171 struct br_mdb_entry
*e
;
175 rem
= RTA_PAYLOAD(attr
);
176 for (i
= RTA_DATA(attr
); RTA_OK(i
, rem
); i
= RTA_NEXT(i
, rem
)) {
178 parse_rtattr(etb
, MDBA_MDB_EATTR_MAX
, MDB_RTA(RTA_DATA(i
)),
179 RTA_PAYLOAD(i
) - RTA_ALIGN(sizeof(*e
)));
180 print_mdb_entry(f
, ifindex
, e
, n
, etb
);
184 static void print_mdb_entries(FILE *fp
, struct nlmsghdr
*n
,
185 int ifindex
, struct rtattr
*mdb
)
187 int rem
= RTA_PAYLOAD(mdb
);
190 for (i
= RTA_DATA(mdb
); RTA_OK(i
, rem
); i
= RTA_NEXT(i
, rem
))
191 br_print_mdb_entry(fp
, ifindex
, i
, n
);
194 static void print_router_entries(FILE *fp
, struct nlmsghdr
*n
,
195 int ifindex
, struct rtattr
*router
)
197 const char *brifname
= ll_index_to_name(ifindex
);
199 if (n
->nlmsg_type
== RTM_GETMDB
) {
201 br_print_router_ports(fp
, router
, brifname
);
203 struct rtattr
*i
= RTA_DATA(router
);
204 uint32_t *port_ifindex
= RTA_DATA(i
);
205 const char *port_name
= ll_index_to_name(*port_ifindex
);
207 if (is_json_context()) {
208 open_json_array(PRINT_JSON
, brifname
);
209 open_json_object(NULL
);
211 print_string(PRINT_JSON
, "port", NULL
,
214 close_json_array(PRINT_JSON
, NULL
);
216 fprintf(fp
, "router port dev %s master %s\n",
217 port_name
, brifname
);
222 static int __parse_mdb_nlmsg(struct nlmsghdr
*n
, struct rtattr
**tb
)
224 struct br_port_msg
*r
= NLMSG_DATA(n
);
225 int len
= n
->nlmsg_len
;
227 if (n
->nlmsg_type
!= RTM_GETMDB
&&
228 n
->nlmsg_type
!= RTM_NEWMDB
&&
229 n
->nlmsg_type
!= RTM_DELMDB
) {
231 "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n",
232 n
->nlmsg_len
, n
->nlmsg_type
, n
->nlmsg_flags
);
237 len
-= NLMSG_LENGTH(sizeof(*r
));
239 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
243 if (filter_index
&& filter_index
!= r
->ifindex
)
246 parse_rtattr(tb
, MDBA_MAX
, MDBA_RTA(r
), n
->nlmsg_len
- NLMSG_LENGTH(sizeof(*r
)));
251 static int print_mdbs(struct nlmsghdr
*n
, void *arg
)
253 struct br_port_msg
*r
= NLMSG_DATA(n
);
254 struct rtattr
*tb
[MDBA_MAX
+1];
258 ret
= __parse_mdb_nlmsg(n
, tb
);
263 print_mdb_entries(fp
, n
, r
->ifindex
, tb
[MDBA_MDB
]);
268 static int print_rtrs(struct nlmsghdr
*n
, void *arg
)
270 struct br_port_msg
*r
= NLMSG_DATA(n
);
271 struct rtattr
*tb
[MDBA_MAX
+1];
275 ret
= __parse_mdb_nlmsg(n
, tb
);
280 print_router_entries(fp
, n
, r
->ifindex
, tb
[MDBA_ROUTER
]);
285 int print_mdb_mon(struct nlmsghdr
*n
, void *arg
)
287 struct br_port_msg
*r
= NLMSG_DATA(n
);
288 struct rtattr
*tb
[MDBA_MAX
+1];
292 ret
= __parse_mdb_nlmsg(n
, tb
);
296 if (n
->nlmsg_type
== RTM_DELMDB
)
297 print_bool(PRINT_ANY
, "deleted", "Deleted ", true);
300 print_mdb_entries(fp
, n
, r
->ifindex
, tb
[MDBA_MDB
]);
303 print_router_entries(fp
, n
, r
->ifindex
, tb
[MDBA_ROUTER
]);
308 static int mdb_show(int argc
, char **argv
)
310 char *filter_dev
= NULL
;
313 if (strcmp(*argv
, "dev") == 0) {
316 duparg("dev", *argv
);
318 } else if (strcmp(*argv
, "vid") == 0) {
321 duparg("vid", *argv
);
322 filter_vlan
= atoi(*argv
);
328 filter_index
= ll_name_to_index(filter_dev
);
330 return nodev(filter_dev
);
334 open_json_object(NULL
);
336 /* get mdb entries */
337 if (rtnl_mdbdump_req(&rth
, PF_BRIDGE
) < 0) {
338 perror("Cannot send dump request");
342 open_json_array(PRINT_JSON
, "mdb");
343 if (rtnl_dump_filter(&rth
, print_mdbs
, stdout
) < 0) {
344 fprintf(stderr
, "Dump terminated\n");
347 close_json_array(PRINT_JSON
, NULL
);
349 /* get router ports */
350 if (rtnl_mdbdump_req(&rth
, PF_BRIDGE
) < 0) {
351 perror("Cannot send dump request");
355 open_json_object("router");
356 if (rtnl_dump_filter(&rth
, print_rtrs
, stdout
) < 0) {
357 fprintf(stderr
, "Dump terminated\n");
369 static int mdb_modify(int cmd
, int flags
, int argc
, char **argv
)
373 struct br_port_msg bpm
;
376 .n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct br_port_msg
)),
377 .n
.nlmsg_flags
= NLM_F_REQUEST
| flags
,
379 .bpm
.family
= PF_BRIDGE
,
381 struct br_mdb_entry entry
= {};
382 char *d
= NULL
, *p
= NULL
, *grp
= NULL
;
386 if (strcmp(*argv
, "dev") == 0) {
389 } else if (strcmp(*argv
, "grp") == 0) {
392 } else if (strcmp(*argv
, "port") == 0) {
395 } else if (strcmp(*argv
, "permanent") == 0) {
396 if (cmd
== RTM_NEWMDB
)
397 entry
.state
|= MDB_PERMANENT
;
398 } else if (strcmp(*argv
, "temp") == 0) {
400 } else if (strcmp(*argv
, "vid") == 0) {
404 if (matches(*argv
, "help") == 0)
410 if (d
== NULL
|| grp
== NULL
|| p
== NULL
) {
411 fprintf(stderr
, "Device, group address and port name are required arguments.\n");
415 req
.bpm
.ifindex
= ll_name_to_index(d
);
416 if (!req
.bpm
.ifindex
)
419 entry
.ifindex
= ll_name_to_index(p
);
423 if (!inet_pton(AF_INET
, grp
, &entry
.addr
.u
.ip4
)) {
424 if (!inet_pton(AF_INET6
, grp
, &entry
.addr
.u
.ip6
)) {
425 fprintf(stderr
, "Invalid address \"%s\"\n", grp
);
428 entry
.addr
.proto
= htons(ETH_P_IPV6
);
430 entry
.addr
.proto
= htons(ETH_P_IP
);
433 addattr_l(&req
.n
, sizeof(req
), MDBA_SET_ENTRY
, &entry
, sizeof(entry
));
435 if (rtnl_talk(&rth
, &req
.n
, NULL
) < 0)
441 int do_mdb(int argc
, char **argv
)
446 if (matches(*argv
, "add") == 0)
447 return mdb_modify(RTM_NEWMDB
, NLM_F_CREATE
|NLM_F_EXCL
, argc
-1, argv
+1);
448 if (matches(*argv
, "delete") == 0)
449 return mdb_modify(RTM_DELMDB
, 0, argc
-1, argv
+1);
451 if (matches(*argv
, "show") == 0 ||
452 matches(*argv
, "lst") == 0 ||
453 matches(*argv
, "list") == 0)
454 return mdb_show(argc
-1, argv
+1);
455 if (matches(*argv
, "help") == 0)
458 return mdb_show(0, NULL
);
460 fprintf(stderr
, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv
);