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>
16 #include <json_writer.h>
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) \
25 ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg))))
26 #endif
27
28 static unsigned int filter_index, filter_vlan;
29 json_writer_t *jw_global;
30 static bool print_mdb_entries = true;
31 static bool print_mdb_router = true;
32
usage(void)33 static void usage(void)
34 {
35 fprintf(stderr, "Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [permanent | temp] [vid VID]\n");
36 fprintf(stderr, " bridge mdb {show} [ dev DEV ] [ vid VID ]\n");
37 exit(-1);
38 }
39
is_temp_mcast_rtr(__u8 type)40 static bool is_temp_mcast_rtr(__u8 type)
41 {
42 return type == MDB_RTR_TYPE_TEMP_QUERY || type == MDB_RTR_TYPE_TEMP;
43 }
44
__print_router_port_stats(FILE * f,struct rtattr * pattr)45 static 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]));
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 }
67 }
68 if (tb[MDBA_ROUTER_PATTR_TYPE]) {
69 type = rta_getattr_u8(tb[MDBA_ROUTER_PATTR_TYPE]);
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");
76 }
77 }
78
br_print_router_ports(FILE * f,struct rtattr * attr,__u32 brifidx)79 static void br_print_router_ports(FILE *f, struct rtattr *attr, __u32 brifidx)
80 {
81 uint32_t *port_ifindex;
82 struct rtattr *i;
83 int rem;
84
85 rem = RTA_PAYLOAD(attr);
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 }
116 }
117 if (!show_stats)
118 fprintf(f, "\n");
119 }
120 }
121
start_json_mdb_flags_array(bool * mdb_flags)122 static 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;
129 }
130
print_mdb_entry(FILE * f,int ifindex,struct br_mdb_entry * e,struct nlmsghdr * n,struct rtattr ** tb)131 static void print_mdb_entry(FILE *f, int ifindex, struct br_mdb_entry *e,
132 struct nlmsghdr *n, struct rtattr **tb)
133 {
134 SPRINT_BUF(abuf);
135 const void *src;
136 int af;
137 bool mdb_flags = false;
138
139 if (filter_vlan && e->vid != filter_vlan)
140 return;
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;
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 }
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]));
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 }
196 }
197 if (jw_global)
198 jsonw_end_object(jw_global);
199 else
200 fprintf(f, "\n");
201 }
202
br_print_mdb_entry(FILE * f,int ifindex,struct rtattr * attr,struct nlmsghdr * n)203 static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr,
204 struct nlmsghdr *n)
205 {
206 struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1];
207 struct br_mdb_entry *e;
208 struct rtattr *i;
209 int rem;
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);
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);
217 }
218 }
219
print_mdb(const struct sockaddr_nl * who,struct nlmsghdr * n,void * arg)220 int 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;
225 struct rtattr *tb[MDBA_MAX+1], *i;
226
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",
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
245 if (tb[MDBA_MDB] && print_mdb_entries) {
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))
249 br_print_mdb_entry(fp, r->ifindex, i, n);
250 }
251
252 if (tb[MDBA_ROUTER] && print_mdb_router) {
253 if (n->nlmsg_type == RTM_GETMDB) {
254 if (show_details)
255 br_print_router_ports(fp, tb[MDBA_ROUTER],
256 r->ifindex);
257 } else {
258 uint32_t *port_ifindex;
259
260 i = RTA_DATA(tb[MDBA_ROUTER]);
261 port_ifindex = RTA_DATA(i);
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 }
284 }
285 }
286
287 if (!jw_global)
288 fflush(fp);
289
290 return 0;
291 }
292
mdb_show(int argc,char ** argv)293 static 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;
303 } else if (strcmp(*argv, "vid") == 0) {
304 NEXT_ARG();
305 if (filter_vlan)
306 duparg("vid", *argv);
307 filter_vlan = atoi(*argv);
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
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 */
352 if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETMDB) < 0) {
353 perror("Cannot send dump request");
354 return -1;
355 }
356 jsonw_name(jw_global, "router");
357 jsonw_start_object(jw_global);
358
359 /* print router ports */
360 print_mdb_entries = false;
361 print_mdb_router = true;
362 if (rtnl_dump_filter(&rth, print_mdb, stdout) < 0) {
363 fprintf(stderr, "Dump terminated\n");
364 return -1;
365 }
366 jsonw_end_object(jw_global);
367 jsonw_end_object(jw_global);
368 jsonw_destroy(&jw_global);
369
370 return 0;
371 }
372
mdb_modify(int cmd,int flags,int argc,char ** argv)373 static int mdb_modify(int cmd, int flags, int argc, char **argv)
374 {
375 struct {
376 struct nlmsghdr n;
377 struct br_port_msg bpm;
378 char buf[1024];
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 = {};
386 char *d = NULL, *p = NULL, *grp = NULL;
387 short vid = 0;
388
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;
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 */
404 } else if (strcmp(*argv, "vid") == 0) {
405 NEXT_ARG();
406 vid = atoi(*argv);
407 } else {
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");
416 return -1;
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
440 entry.vid = vid;
441 addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
442
443 if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
444 return -1;
445
446 return 0;
447 }
448
do_mdb(int argc,char ** argv)449 int do_mdb(int argc, char **argv)
450 {
451 ll_init_map(&rth);
452
453 if (argc > 0) {
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
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 }
471