]>
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>
17 #include <json_writer.h>
19 #include "libnetlink.h"
20 #include "br_common.h"
26 ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg))))
29 static unsigned int filter_index
, filter_vlan
;
30 json_writer_t
*jw_global
;
31 static bool print_mdb_entries
= true;
32 static bool print_mdb_router
= true;
34 static void usage(void)
36 fprintf(stderr
, "Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [permanent | temp] [vid VID]\n");
37 fprintf(stderr
, " bridge mdb {show} [ dev DEV ] [ vid VID ]\n");
41 static bool is_temp_mcast_rtr(__u8 type
)
43 return type
== MDB_RTR_TYPE_TEMP_QUERY
|| type
== MDB_RTR_TYPE_TEMP
;
46 static void __print_router_port_stats(FILE *f
, struct rtattr
*pattr
)
48 struct rtattr
*tb
[MDBA_ROUTER_PATTR_MAX
+ 1];
52 parse_rtattr(tb
, MDBA_ROUTER_PATTR_MAX
, MDB_RTR_RTA(RTA_DATA(pattr
)),
53 RTA_PAYLOAD(pattr
) - RTA_ALIGN(sizeof(uint32_t)));
54 if (tb
[MDBA_ROUTER_PATTR_TIMER
]) {
56 rta_getattr_u32(tb
[MDBA_ROUTER_PATTR_TIMER
]));
58 char formatted_time
[32];
60 snprintf(formatted_time
, sizeof(formatted_time
),
61 "%4i.%.2i", (int)tv
.tv_sec
,
62 (int)tv
.tv_usec
/10000);
63 jsonw_string_field(jw_global
, "timer", formatted_time
);
65 fprintf(f
, " %4i.%.2i",
66 (int)tv
.tv_sec
, (int)tv
.tv_usec
/10000);
69 if (tb
[MDBA_ROUTER_PATTR_TYPE
]) {
70 type
= rta_getattr_u8(tb
[MDBA_ROUTER_PATTR_TYPE
]);
72 jsonw_string_field(jw_global
, "type",
73 is_temp_mcast_rtr(type
) ? "temp" : "permanent");
76 is_temp_mcast_rtr(type
) ? "temp" : "permanent");
80 static void br_print_router_ports(FILE *f
, struct rtattr
*attr
, __u32 brifidx
)
82 uint32_t *port_ifindex
;
86 rem
= RTA_PAYLOAD(attr
);
88 jsonw_name(jw_global
, ll_index_to_name(brifidx
));
89 jsonw_start_array(jw_global
);
90 for (i
= RTA_DATA(attr
); RTA_OK(i
, rem
); i
= RTA_NEXT(i
, rem
)) {
91 port_ifindex
= RTA_DATA(i
);
92 jsonw_start_object(jw_global
);
93 jsonw_string_field(jw_global
,
95 ll_index_to_name(*port_ifindex
));
97 __print_router_port_stats(f
, i
);
98 jsonw_end_object(jw_global
);
100 jsonw_end_array(jw_global
);
103 fprintf(f
, "router ports on %s: ",
104 ll_index_to_name(brifidx
));
105 for (i
= RTA_DATA(attr
); RTA_OK(i
, rem
); i
= RTA_NEXT(i
, rem
)) {
106 port_ifindex
= RTA_DATA(i
);
108 fprintf(f
, "router ports on %s: %s",
109 ll_index_to_name(brifidx
),
110 ll_index_to_name(*port_ifindex
));
111 __print_router_port_stats(f
, i
);
115 ll_index_to_name(*port_ifindex
));
123 static void start_json_mdb_flags_array(bool *mdb_flags
)
127 jsonw_name(jw_global
, "flags");
128 jsonw_start_array(jw_global
);
132 static void print_mdb_entry(FILE *f
, int ifindex
, struct br_mdb_entry
*e
,
133 struct nlmsghdr
*n
, struct rtattr
**tb
)
138 bool mdb_flags
= false;
140 if (filter_vlan
&& e
->vid
!= filter_vlan
)
142 af
= e
->addr
.proto
== htons(ETH_P_IP
) ? AF_INET
: AF_INET6
;
143 src
= af
== AF_INET
? (const void *)&e
->addr
.u
.ip4
:
144 (const void *)&e
->addr
.u
.ip6
;
146 jsonw_start_object(jw_global
);
147 if (n
->nlmsg_type
== RTM_DELMDB
) {
149 jsonw_string_field(jw_global
, "opCode", "deleted");
151 fprintf(f
, "Deleted ");
154 jsonw_string_field(jw_global
, "dev", ll_index_to_name(ifindex
));
155 jsonw_string_field(jw_global
,
157 ll_index_to_name(e
->ifindex
));
158 jsonw_string_field(jw_global
, "grp", inet_ntop(af
, src
,
159 abuf
, sizeof(abuf
)));
160 jsonw_string_field(jw_global
, "state",
161 (e
->state
& MDB_PERMANENT
) ? "permanent" : "temp");
162 if (e
->flags
& MDB_FLAGS_OFFLOAD
) {
163 start_json_mdb_flags_array(&mdb_flags
);
164 jsonw_string(jw_global
, "offload");
167 jsonw_end_array(jw_global
);
169 fprintf(f
, "dev %s port %s grp %s %s%s",
170 ll_index_to_name(ifindex
),
171 ll_index_to_name(e
->ifindex
),
172 inet_ntop(af
, src
, abuf
, sizeof(abuf
)),
173 (e
->state
& MDB_PERMANENT
) ? "permanent" : "temp",
174 (e
->flags
& MDB_FLAGS_OFFLOAD
) ? " offload" : "");
178 jsonw_uint_field(jw_global
, "vid", e
->vid
);
180 fprintf(f
, " vid %hu", e
->vid
);
182 if (show_stats
&& tb
&& tb
[MDBA_MDB_EATTR_TIMER
]) {
185 __jiffies_to_tv(&tv
, rta_getattr_u32(tb
[MDBA_MDB_EATTR_TIMER
]));
187 char formatted_time
[32];
189 snprintf(formatted_time
, sizeof(formatted_time
),
190 "%4i.%.2i", (int)tv
.tv_sec
,
191 (int)tv
.tv_usec
/10000);
192 jsonw_string_field(jw_global
, "timer", formatted_time
);
194 fprintf(f
, "%4i.%.2i", (int)tv
.tv_sec
,
195 (int)tv
.tv_usec
/10000);
199 jsonw_end_object(jw_global
);
204 static void br_print_mdb_entry(FILE *f
, int ifindex
, struct rtattr
*attr
,
207 struct rtattr
*etb
[MDBA_MDB_EATTR_MAX
+ 1];
208 struct br_mdb_entry
*e
;
212 rem
= RTA_PAYLOAD(attr
);
213 for (i
= RTA_DATA(attr
); RTA_OK(i
, rem
); i
= RTA_NEXT(i
, rem
)) {
215 parse_rtattr(etb
, MDBA_MDB_EATTR_MAX
, MDB_RTA(RTA_DATA(i
)),
216 RTA_PAYLOAD(i
) - RTA_ALIGN(sizeof(*e
)));
217 print_mdb_entry(f
, ifindex
, e
, n
, etb
);
221 int print_mdb(const struct sockaddr_nl
*who
, struct nlmsghdr
*n
, void *arg
)
224 struct br_port_msg
*r
= NLMSG_DATA(n
);
225 int len
= n
->nlmsg_len
;
226 struct rtattr
*tb
[MDBA_MAX
+1], *i
;
228 if (n
->nlmsg_type
!= RTM_GETMDB
&& n
->nlmsg_type
!= RTM_NEWMDB
&& n
->nlmsg_type
!= RTM_DELMDB
) {
229 fprintf(stderr
, "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n",
230 n
->nlmsg_len
, n
->nlmsg_type
, n
->nlmsg_flags
);
235 len
-= NLMSG_LENGTH(sizeof(*r
));
237 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
241 if (filter_index
&& filter_index
!= r
->ifindex
)
244 parse_rtattr(tb
, MDBA_MAX
, MDBA_RTA(r
), n
->nlmsg_len
- NLMSG_LENGTH(sizeof(*r
)));
246 if (tb
[MDBA_MDB
] && print_mdb_entries
) {
247 int rem
= RTA_PAYLOAD(tb
[MDBA_MDB
]);
249 for (i
= RTA_DATA(tb
[MDBA_MDB
]); RTA_OK(i
, rem
); i
= RTA_NEXT(i
, rem
))
250 br_print_mdb_entry(fp
, r
->ifindex
, i
, n
);
253 if (tb
[MDBA_ROUTER
] && print_mdb_router
) {
254 if (n
->nlmsg_type
== RTM_GETMDB
) {
256 br_print_router_ports(fp
, tb
[MDBA_ROUTER
],
259 uint32_t *port_ifindex
;
261 i
= RTA_DATA(tb
[MDBA_ROUTER
]);
262 port_ifindex
= RTA_DATA(i
);
263 if (n
->nlmsg_type
== RTM_DELMDB
) {
265 jsonw_string_field(jw_global
,
269 fprintf(fp
, "Deleted ");
272 jsonw_name(jw_global
,
273 ll_index_to_name(r
->ifindex
));
274 jsonw_start_array(jw_global
);
275 jsonw_start_object(jw_global
);
276 jsonw_string_field(jw_global
, "port",
277 ll_index_to_name(*port_ifindex
));
278 jsonw_end_object(jw_global
);
279 jsonw_end_array(jw_global
);
281 fprintf(fp
, "router port dev %s master %s\n",
282 ll_index_to_name(*port_ifindex
),
283 ll_index_to_name(r
->ifindex
));
294 static int mdb_show(int argc
, char **argv
)
296 char *filter_dev
= NULL
;
299 if (strcmp(*argv
, "dev") == 0) {
302 duparg("dev", *argv
);
304 } else if (strcmp(*argv
, "vid") == 0) {
307 duparg("vid", *argv
);
308 filter_vlan
= atoi(*argv
);
314 filter_index
= ll_name_to_index(filter_dev
);
315 if (filter_index
== 0) {
316 fprintf(stderr
, "Cannot find device \"%s\"\n",
323 if (rtnl_wilddump_request(&rth
, PF_BRIDGE
, RTM_GETMDB
) < 0) {
324 perror("Cannot send dump request");
330 if (rtnl_dump_filter(&rth
, print_mdb
, stdout
) < 0) {
331 fprintf(stderr
, "Dump terminated\n");
337 jw_global
= jsonw_new(stdout
);
338 jsonw_pretty(jw_global
, 1);
339 jsonw_start_object(jw_global
);
340 jsonw_name(jw_global
, "mdb");
341 jsonw_start_array(jw_global
);
343 /* print mdb entries */
344 print_mdb_entries
= true;
345 print_mdb_router
= false;
346 if (rtnl_dump_filter(&rth
, print_mdb
, stdout
) < 0) {
347 fprintf(stderr
, "Dump terminated\n");
350 jsonw_end_array(jw_global
);
352 /* get router ports */
353 if (rtnl_wilddump_request(&rth
, PF_BRIDGE
, RTM_GETMDB
) < 0) {
354 perror("Cannot send dump request");
357 jsonw_name(jw_global
, "router");
358 jsonw_start_object(jw_global
);
360 /* print router ports */
361 print_mdb_entries
= false;
362 print_mdb_router
= true;
363 if (rtnl_dump_filter(&rth
, print_mdb
, stdout
) < 0) {
364 fprintf(stderr
, "Dump terminated\n");
367 jsonw_end_object(jw_global
);
368 jsonw_end_object(jw_global
);
369 jsonw_destroy(&jw_global
);
374 static int mdb_modify(int cmd
, int flags
, int argc
, char **argv
)
378 struct br_port_msg bpm
;
381 .n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct br_port_msg
)),
382 .n
.nlmsg_flags
= NLM_F_REQUEST
| flags
,
384 .bpm
.family
= PF_BRIDGE
,
386 struct br_mdb_entry entry
= {};
387 char *d
= NULL
, *p
= NULL
, *grp
= NULL
;
391 if (strcmp(*argv
, "dev") == 0) {
394 } else if (strcmp(*argv
, "grp") == 0) {
397 } else if (strcmp(*argv
, "port") == 0) {
400 } else if (strcmp(*argv
, "permanent") == 0) {
401 if (cmd
== RTM_NEWMDB
)
402 entry
.state
|= MDB_PERMANENT
;
403 } else if (strcmp(*argv
, "temp") == 0) {
405 } else if (strcmp(*argv
, "vid") == 0) {
409 if (matches(*argv
, "help") == 0)
415 if (d
== NULL
|| grp
== NULL
|| p
== NULL
) {
416 fprintf(stderr
, "Device, group address and port name are required arguments.\n");
420 req
.bpm
.ifindex
= ll_name_to_index(d
);
421 if (req
.bpm
.ifindex
== 0) {
422 fprintf(stderr
, "Cannot find device \"%s\"\n", d
);
426 entry
.ifindex
= ll_name_to_index(p
);
427 if (entry
.ifindex
== 0) {
428 fprintf(stderr
, "Cannot find device \"%s\"\n", p
);
432 if (!inet_pton(AF_INET
, grp
, &entry
.addr
.u
.ip4
)) {
433 if (!inet_pton(AF_INET6
, grp
, &entry
.addr
.u
.ip6
)) {
434 fprintf(stderr
, "Invalid address \"%s\"\n", grp
);
437 entry
.addr
.proto
= htons(ETH_P_IPV6
);
439 entry
.addr
.proto
= htons(ETH_P_IP
);
442 addattr_l(&req
.n
, sizeof(req
), MDBA_SET_ENTRY
, &entry
, sizeof(entry
));
444 if (rtnl_talk(&rth
, &req
.n
, NULL
) < 0)
450 int do_mdb(int argc
, char **argv
)
455 if (matches(*argv
, "add") == 0)
456 return mdb_modify(RTM_NEWMDB
, NLM_F_CREATE
|NLM_F_EXCL
, argc
-1, argv
+1);
457 if (matches(*argv
, "delete") == 0)
458 return mdb_modify(RTM_DELMDB
, 0, argc
-1, argv
+1);
460 if (matches(*argv
, "show") == 0 ||
461 matches(*argv
, "lst") == 0 ||
462 matches(*argv
, "list") == 0)
463 return mdb_show(argc
-1, argv
+1);
464 if (matches(*argv
, "help") == 0)
467 return mdb_show(0, NULL
);
469 fprintf(stderr
, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv
);