• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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