]> git.proxmox.com Git - mirror_iproute2.git/blame - bridge/mdb.c
iplink: Use ll_index_to_name() instead of if_indextoname()
[mirror_iproute2.git] / bridge / mdb.c
CommitLineData
6054c1eb 1/* SPDX-License-Identifier: GPL-2.0 */
e06c7f7e
CW
2/*
3 * Get mdb table with netlink
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <unistd.h>
9#include <fcntl.h>
10#include <sys/socket.h>
11#include <net/if.h>
12#include <netinet/in.h>
13#include <linux/if_bridge.h>
14#include <linux/if_ether.h>
15#include <string.h>
16#include <arpa/inet.h>
44e0f6f3 17#include <json_writer.h>
e06c7f7e
CW
18
19#include "libnetlink.h"
20#include "br_common.h"
21#include "rt_names.h"
22#include "utils.h"
23
24#ifndef MDBA_RTA
25#define MDBA_RTA(r) \
df4b043f 26 ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg))))
e06c7f7e
CW
27#endif
28
24687d67 29static unsigned int filter_index, filter_vlan;
44e0f6f3
NG
30json_writer_t *jw_global;
31static bool print_mdb_entries = true;
32static bool print_mdb_router = true;
e06c7f7e
CW
33
34static void usage(void)
35{
6aac8617 36 fprintf(stderr, "Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [permanent | temp] [vid VID]\n");
24687d67 37 fprintf(stderr, " bridge mdb {show} [ dev DEV ] [ vid VID ]\n");
e06c7f7e
CW
38 exit(-1);
39}
40
ba037267
NA
41static bool is_temp_mcast_rtr(__u8 type)
42{
43 return type == MDB_RTR_TYPE_TEMP_QUERY || type == MDB_RTR_TYPE_TEMP;
44}
45
46static void __print_router_port_stats(FILE *f, struct rtattr *pattr)
47{
48 struct rtattr *tb[MDBA_ROUTER_PATTR_MAX + 1];
49 struct timeval tv;
50 __u8 type;
51
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]) {
55 __jiffies_to_tv(&tv,
56 rta_getattr_u32(tb[MDBA_ROUTER_PATTR_TIMER]));
44e0f6f3
NG
57 if (jw_global) {
58 char formatted_time[9];
59
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);
64 } else {
65 fprintf(f, " %4i.%.2i",
66 (int)tv.tv_sec, (int)tv.tv_usec/10000);
67 }
ba037267
NA
68 }
69 if (tb[MDBA_ROUTER_PATTR_TYPE]) {
70 type = rta_getattr_u8(tb[MDBA_ROUTER_PATTR_TYPE]);
44e0f6f3
NG
71 if (jw_global)
72 jsonw_string_field(jw_global, "type",
73 is_temp_mcast_rtr(type) ? "temp" : "permanent");
74 else
75 fprintf(f, " %s",
76 is_temp_mcast_rtr(type) ? "temp" : "permanent");
ba037267
NA
77 }
78}
79
80static void br_print_router_ports(FILE *f, struct rtattr *attr, __u32 brifidx)
e06c7f7e
CW
81{
82 uint32_t *port_ifindex;
83 struct rtattr *i;
84 int rem;
85
86 rem = RTA_PAYLOAD(attr);
44e0f6f3
NG
87 if (jw_global) {
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,
94 "port",
95 ll_index_to_name(*port_ifindex));
96 if (show_stats)
97 __print_router_port_stats(f, i);
98 jsonw_end_object(jw_global);
99 }
100 jsonw_end_array(jw_global);
101 } else {
102 if (!show_stats)
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);
107 if (show_stats) {
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);
112 fprintf(f, "\n");
113 } else{
114 fprintf(f, "%s ",
115 ll_index_to_name(*port_ifindex));
116 }
ba037267 117 }
44e0f6f3
NG
118 if (!show_stats)
119 fprintf(f, "\n");
e06c7f7e 120 }
44e0f6f3
NG
121}
122
123static void start_json_mdb_flags_array(bool *mdb_flags)
124{
125 if (*mdb_flags)
126 return;
127 jsonw_name(jw_global, "flags");
128 jsonw_start_array(jw_global);
129 *mdb_flags = true;
e06c7f7e
CW
130}
131
90d73159 132static void print_mdb_entry(FILE *f, int ifindex, struct br_mdb_entry *e,
05d4f64d 133 struct nlmsghdr *n, struct rtattr **tb)
e06c7f7e
CW
134{
135 SPRINT_BUF(abuf);
6aac8617
NA
136 const void *src;
137 int af;
44e0f6f3 138 bool mdb_flags = false;
6aac8617 139
24687d67
NA
140 if (filter_vlan && e->vid != filter_vlan)
141 return;
6aac8617
NA
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;
44e0f6f3
NG
145 if (jw_global)
146 jsonw_start_object(jw_global);
147 if (n->nlmsg_type == RTM_DELMDB) {
148 if (jw_global)
149 jsonw_string_field(jw_global, "opCode", "deleted");
150 else
151 fprintf(f, "Deleted ");
152 }
153 if (jw_global) {
154 jsonw_string_field(jw_global, "dev", ll_index_to_name(ifindex));
155 jsonw_string_field(jw_global,
156 "port",
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");
165 }
166 if (mdb_flags)
167 jsonw_end_array(jw_global);
168 } else{
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" : "");
175 }
176 if (e->vid) {
177 if (jw_global)
178 jsonw_uint_field(jw_global, "vid", e->vid);
179 else
180 fprintf(f, " vid %hu", e->vid);
181 }
05d4f64d
NA
182 if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) {
183 struct timeval tv;
184
185 __jiffies_to_tv(&tv, rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER]));
44e0f6f3
NG
186 if (jw_global) {
187 char formatted_time[9];
188
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);
193 } else {
194 fprintf(f, "%4i.%.2i", (int)tv.tv_sec,
195 (int)tv.tv_usec/10000);
196 }
05d4f64d 197 }
44e0f6f3
NG
198 if (jw_global)
199 jsonw_end_object(jw_global);
200 else
201 fprintf(f, "\n");
e06c7f7e
CW
202}
203
90d73159
NA
204static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr,
205 struct nlmsghdr *n)
e06c7f7e 206{
05d4f64d
NA
207 struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1];
208 struct br_mdb_entry *e;
e06c7f7e
CW
209 struct rtattr *i;
210 int rem;
e06c7f7e
CW
211
212 rem = RTA_PAYLOAD(attr);
213 for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
214 e = RTA_DATA(i);
05d4f64d
NA
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);
e06c7f7e
CW
218 }
219}
220
221int print_mdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
222{
223 FILE *fp = arg;
224 struct br_port_msg *r = NLMSG_DATA(n);
225 int len = n->nlmsg_len;
6b4867e6 226 struct rtattr *tb[MDBA_MAX+1], *i;
e06c7f7e 227
4a4ee616
CW
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",
e06c7f7e
CW
230 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
231
232 return 0;
233 }
234
235 len -= NLMSG_LENGTH(sizeof(*r));
236 if (len < 0) {
237 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
238 return -1;
239 }
240
241 if (filter_index && filter_index != r->ifindex)
242 return 0;
243
244 parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
245
44e0f6f3 246 if (tb[MDBA_MDB] && print_mdb_entries) {
e06c7f7e
CW
247 int rem = RTA_PAYLOAD(tb[MDBA_MDB]);
248
249 for (i = RTA_DATA(tb[MDBA_MDB]); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
90d73159 250 br_print_mdb_entry(fp, r->ifindex, i, n);
e06c7f7e
CW
251 }
252
44e0f6f3 253 if (tb[MDBA_ROUTER] && print_mdb_router) {
6b4867e6 254 if (n->nlmsg_type == RTM_GETMDB) {
ba037267
NA
255 if (show_details)
256 br_print_router_ports(fp, tb[MDBA_ROUTER],
257 r->ifindex);
6b4867e6
NA
258 } else {
259 uint32_t *port_ifindex;
260
261 i = RTA_DATA(tb[MDBA_ROUTER]);
262 port_ifindex = RTA_DATA(i);
44e0f6f3
NG
263 if (n->nlmsg_type == RTM_DELMDB) {
264 if (jw_global)
265 jsonw_string_field(jw_global,
266 "opCode",
267 "deleted");
268 else
269 fprintf(fp, "Deleted ");
270 }
271 if (jw_global) {
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);
280 } else {
281 fprintf(fp, "router port dev %s master %s\n",
282 ll_index_to_name(*port_ifindex),
283 ll_index_to_name(r->ifindex));
284 }
e06c7f7e
CW
285 }
286 }
287
44e0f6f3
NG
288 if (!jw_global)
289 fflush(fp);
4d45bf3b 290
e06c7f7e
CW
291 return 0;
292}
293
294static int mdb_show(int argc, char **argv)
295{
296 char *filter_dev = NULL;
297
298 while (argc > 0) {
299 if (strcmp(*argv, "dev") == 0) {
300 NEXT_ARG();
301 if (filter_dev)
302 duparg("dev", *argv);
303 filter_dev = *argv;
24687d67
NA
304 } else if (strcmp(*argv, "vid") == 0) {
305 NEXT_ARG();
306 if (filter_vlan)
307 duparg("vid", *argv);
308 filter_vlan = atoi(*argv);
e06c7f7e
CW
309 }
310 argc--; argv++;
311 }
312
313 if (filter_dev) {
314 filter_index = if_nametoindex(filter_dev);
315 if (filter_index == 0) {
316 fprintf(stderr, "Cannot find device \"%s\"\n",
317 filter_dev);
318 return -1;
319 }
320 }
321
44e0f6f3
NG
322 /* get mdb entries*/
323 if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETMDB) < 0) {
324 perror("Cannot send dump request");
325 return -1;
326 }
327
328 if (!json_output) {
329 /* Normal output */
330 if (rtnl_dump_filter(&rth, print_mdb, stdout) < 0) {
331 fprintf(stderr, "Dump terminated\n");
332 return -1;
333 }
334 return 0;
335 }
336 /* Json output */
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);
342
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");
348 return -1;
349 }
350 jsonw_end_array(jw_global);
351
352 /* get router ports */
e06c7f7e
CW
353 if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETMDB) < 0) {
354 perror("Cannot send dump request");
42ecedd4 355 return -1;
e06c7f7e 356 }
44e0f6f3
NG
357 jsonw_name(jw_global, "router");
358 jsonw_start_object(jw_global);
e06c7f7e 359
44e0f6f3
NG
360 /* print router ports */
361 print_mdb_entries = false;
362 print_mdb_router = true;
e06c7f7e
CW
363 if (rtnl_dump_filter(&rth, print_mdb, stdout) < 0) {
364 fprintf(stderr, "Dump terminated\n");
42ecedd4 365 return -1;
e06c7f7e 366 }
44e0f6f3
NG
367 jsonw_end_object(jw_global);
368 jsonw_end_object(jw_global);
369 jsonw_destroy(&jw_global);
e06c7f7e
CW
370
371 return 0;
372}
373
9dca6767
CW
374static int mdb_modify(int cmd, int flags, int argc, char **argv)
375{
376 struct {
df4b043f 377 struct nlmsghdr n;
9dca6767 378 struct br_port_msg bpm;
df4b043f 379 char buf[1024];
d17b136f
PS
380 } req = {
381 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
382 .n.nlmsg_flags = NLM_F_REQUEST | flags,
383 .n.nlmsg_type = cmd,
384 .bpm.family = PF_BRIDGE,
385 };
386 struct br_mdb_entry entry = {};
9dca6767 387 char *d = NULL, *p = NULL, *grp = NULL;
6aac8617 388 short vid = 0;
9dca6767 389
9dca6767
CW
390 while (argc > 0) {
391 if (strcmp(*argv, "dev") == 0) {
392 NEXT_ARG();
393 d = *argv;
394 } else if (strcmp(*argv, "grp") == 0) {
395 NEXT_ARG();
396 grp = *argv;
d8b75d1a
CW
397 } else if (strcmp(*argv, "port") == 0) {
398 NEXT_ARG();
399 p = *argv;
400 } else if (strcmp(*argv, "permanent") == 0) {
401 if (cmd == RTM_NEWMDB)
402 entry.state |= MDB_PERMANENT;
403 } else if (strcmp(*argv, "temp") == 0) {
404 ;/* nothing */
6aac8617
NA
405 } else if (strcmp(*argv, "vid") == 0) {
406 NEXT_ARG();
407 vid = atoi(*argv);
9dca6767 408 } else {
9dca6767
CW
409 if (matches(*argv, "help") == 0)
410 usage();
411 }
412 argc--; argv++;
413 }
414
415 if (d == NULL || grp == NULL || p == NULL) {
416 fprintf(stderr, "Device, group address and port name are required arguments.\n");
42ecedd4 417 return -1;
9dca6767
CW
418 }
419
420 req.bpm.ifindex = ll_name_to_index(d);
421 if (req.bpm.ifindex == 0) {
422 fprintf(stderr, "Cannot find device \"%s\"\n", d);
423 return -1;
424 }
425
426 entry.ifindex = ll_name_to_index(p);
427 if (entry.ifindex == 0) {
428 fprintf(stderr, "Cannot find device \"%s\"\n", p);
429 return -1;
430 }
431
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);
435 return -1;
436 } else
437 entry.addr.proto = htons(ETH_P_IPV6);
438 } else
439 entry.addr.proto = htons(ETH_P_IP);
440
6aac8617 441 entry.vid = vid;
9dca6767
CW
442 addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
443
86bf43c7 444 if (rtnl_talk(&rth, &req.n, NULL) < 0)
42ecedd4 445 return -1;
9dca6767
CW
446
447 return 0;
448}
449
e06c7f7e
CW
450int do_mdb(int argc, char **argv)
451{
452 ll_init_map(&rth);
453
454 if (argc > 0) {
9dca6767
CW
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);
459
e06c7f7e
CW
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)
465 usage();
466 } else
467 return mdb_show(0, NULL);
468
469 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv);
470 exit(-1);
471}