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