• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Get/set/delete fdb table with netlink
3  *
4  * TODO: merge/replace this with ip neighbour
5  *
6  * Authors:	Stephen Hemminger <shemminger@vyatta.com>
7  */
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <netdb.h>
13 #include <time.h>
14 #include <fcntl.h>
15 #include <sys/socket.h>
16 #include <sys/time.h>
17 #include <net/if.h>
18 #include <netinet/in.h>
19 #include <linux/if_bridge.h>
20 #include <linux/if_ether.h>
21 #include <linux/neighbour.h>
22 #include <string.h>
23 #include <limits.h>
24 #include <json_writer.h>
25 #include <stdbool.h>
26 
27 #include "libnetlink.h"
28 #include "br_common.h"
29 #include "rt_names.h"
30 #include "utils.h"
31 
32 static unsigned int filter_index, filter_vlan, filter_state;
33 
34 json_writer_t *jw_global;
35 
usage(void)36 static void usage(void)
37 {
38 	fprintf(stderr,
39 		"Usage: bridge fdb { add | append | del | replace } ADDR dev DEV\n"
40 		"              [ self ] [ master ] [ use ] [ router ]\n"
41 		"              [ local | static | dynamic ] [ dst IPADDR ] [ vlan VID ]\n"
42 		"              [ port PORT] [ vni VNI ] [ via DEV ]\n"
43 		"       bridge fdb [ show [ br BRDEV ] [ brport DEV ] [ vlan VID ] [ state STATE ] ]\n");
44 	exit(-1);
45 }
46 
state_n2a(unsigned int s)47 static const char *state_n2a(unsigned int s)
48 {
49 	static char buf[32];
50 
51 	if (s & NUD_PERMANENT)
52 		return "permanent";
53 
54 	if (s & NUD_NOARP)
55 		return "static";
56 
57 	if (s & NUD_STALE)
58 		return "stale";
59 
60 	if (s & NUD_REACHABLE)
61 		return "";
62 
63 	sprintf(buf, "state=%#x", s);
64 	return buf;
65 }
66 
state_a2n(unsigned int * s,const char * arg)67 static int state_a2n(unsigned int *s, const char *arg)
68 {
69 	if (matches(arg, "permanent") == 0)
70 		*s = NUD_PERMANENT;
71 	else if (matches(arg, "static") == 0 || matches(arg, "temp") == 0)
72 		*s = NUD_NOARP;
73 	else if (matches(arg, "stale") == 0)
74 		*s = NUD_STALE;
75 	else if (matches(arg, "reachable") == 0 || matches(arg, "dynamic") == 0)
76 		*s = NUD_REACHABLE;
77 	else if (strcmp(arg, "all") == 0)
78 		*s = ~0;
79 	else if (get_unsigned(s, arg, 0))
80 		return -1;
81 
82 	return 0;
83 }
84 
start_json_fdb_flags_array(bool * fdb_flags)85 static void start_json_fdb_flags_array(bool *fdb_flags)
86 {
87 	if (*fdb_flags)
88 		return;
89 	jsonw_name(jw_global, "flags");
90 	jsonw_start_array(jw_global);
91 	*fdb_flags = true;
92 }
93 
print_fdb(const struct sockaddr_nl * who,struct nlmsghdr * n,void * arg)94 int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
95 {
96 	FILE *fp = arg;
97 	struct ndmsg *r = NLMSG_DATA(n);
98 	int len = n->nlmsg_len;
99 	struct rtattr *tb[NDA_MAX+1];
100 	__u16 vid = 0;
101 	bool fdb_flags = false;
102 	const char *state_s;
103 
104 	if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) {
105 		fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n",
106 			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
107 		return 0;
108 	}
109 
110 	len -= NLMSG_LENGTH(sizeof(*r));
111 	if (len < 0) {
112 		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
113 		return -1;
114 	}
115 
116 	if (r->ndm_family != AF_BRIDGE)
117 		return 0;
118 
119 	if (filter_index && filter_index != r->ndm_ifindex)
120 		return 0;
121 
122 	if (filter_state && !(r->ndm_state & filter_state))
123 		return 0;
124 
125 	parse_rtattr(tb, NDA_MAX, NDA_RTA(r),
126 		     n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
127 
128 	if (tb[NDA_VLAN])
129 		vid = rta_getattr_u16(tb[NDA_VLAN]);
130 
131 	if (filter_vlan && filter_vlan != vid)
132 		return 0;
133 
134 	if (jw_global) {
135 		jsonw_pretty(jw_global, 1);
136 		jsonw_start_object(jw_global);
137 	}
138 
139 	if (n->nlmsg_type == RTM_DELNEIGH) {
140 		if (jw_global)
141 			jsonw_string_field(jw_global, "opCode", "deleted");
142 		else
143 			fprintf(fp, "Deleted ");
144 	}
145 
146 	if (tb[NDA_LLADDR]) {
147 		SPRINT_BUF(b1);
148 		ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
149 			    RTA_PAYLOAD(tb[NDA_LLADDR]),
150 			    ll_index_to_type(r->ndm_ifindex),
151 			    b1, sizeof(b1));
152 		if (jw_global)
153 			jsonw_string_field(jw_global, "mac", b1);
154 		else
155 			fprintf(fp, "%s ", b1);
156 	}
157 
158 	if (!filter_index && r->ndm_ifindex) {
159 		if (jw_global)
160 			jsonw_string_field(jw_global, "dev",
161 					   ll_index_to_name(r->ndm_ifindex));
162 		else
163 			fprintf(fp, "dev %s ",
164 				ll_index_to_name(r->ndm_ifindex));
165 	}
166 
167 	if (tb[NDA_DST]) {
168 		int family = AF_INET;
169 		const char *abuf_s;
170 
171 		if (RTA_PAYLOAD(tb[NDA_DST]) == sizeof(struct in6_addr))
172 			family = AF_INET6;
173 
174 		abuf_s = format_host(family,
175 				     RTA_PAYLOAD(tb[NDA_DST]),
176 				     RTA_DATA(tb[NDA_DST]));
177 		if (jw_global)
178 			jsonw_string_field(jw_global, "dst", abuf_s);
179 		else
180 			fprintf(fp, "dst %s ", abuf_s);
181 	}
182 
183 	if (vid) {
184 		if (jw_global)
185 			jsonw_uint_field(jw_global, "vlan", vid);
186 		else
187 			fprintf(fp, "vlan %hu ", vid);
188 	}
189 
190 	if (tb[NDA_PORT]) {
191 		if (jw_global)
192 			jsonw_uint_field(jw_global, "port",
193 					 rta_getattr_be16(tb[NDA_PORT]));
194 		else
195 			fprintf(fp, "port %d ",
196 				rta_getattr_be16(tb[NDA_PORT]));
197 	}
198 
199 	if (tb[NDA_VNI]) {
200 		if (jw_global)
201 			jsonw_uint_field(jw_global, "vni",
202 					 rta_getattr_u32(tb[NDA_VNI]));
203 		else
204 			fprintf(fp, "vni %d ",
205 				rta_getattr_u32(tb[NDA_VNI]));
206 	}
207 
208 	if (tb[NDA_IFINDEX]) {
209 		unsigned int ifindex = rta_getattr_u32(tb[NDA_IFINDEX]);
210 
211 		if (ifindex) {
212 			char ifname[IF_NAMESIZE];
213 
214 			if (!tb[NDA_LINK_NETNSID] &&
215 			    if_indextoname(ifindex, ifname)) {
216 				if (jw_global)
217 					jsonw_string_field(jw_global, "viaIf",
218 							   ifname);
219 				else
220 					fprintf(fp, "via %s ", ifname);
221 			} else {
222 				if (jw_global)
223 					jsonw_uint_field(jw_global, "viaIfIndex",
224 							 ifindex);
225 				else
226 					fprintf(fp, "via ifindex %u ", ifindex);
227 			}
228 		}
229 	}
230 
231 	if (tb[NDA_LINK_NETNSID]) {
232 		if (jw_global)
233 			jsonw_uint_field(jw_global, "linkNetNsId",
234 					 rta_getattr_u32(tb[NDA_LINK_NETNSID]));
235 		else
236 			fprintf(fp, "link-netnsid %d ",
237 				rta_getattr_u32(tb[NDA_LINK_NETNSID]));
238 	}
239 
240 	if (show_stats && tb[NDA_CACHEINFO]) {
241 		struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
242 		int hz = get_user_hz();
243 
244 		if (jw_global) {
245 			jsonw_uint_field(jw_global, "used",
246 				ci->ndm_used/hz);
247 			jsonw_uint_field(jw_global, "updated",
248 				ci->ndm_updated/hz);
249 		} else {
250 			fprintf(fp, "used %d/%d ", ci->ndm_used/hz,
251 					ci->ndm_updated/hz);
252 		}
253 	}
254 
255 	if (jw_global) {
256 		if (r->ndm_flags & NTF_SELF) {
257 			start_json_fdb_flags_array(&fdb_flags);
258 			jsonw_string(jw_global, "self");
259 		}
260 		if (r->ndm_flags & NTF_ROUTER) {
261 			start_json_fdb_flags_array(&fdb_flags);
262 			jsonw_string(jw_global, "router");
263 		}
264 		if (r->ndm_flags & NTF_EXT_LEARNED) {
265 			start_json_fdb_flags_array(&fdb_flags);
266 			jsonw_string(jw_global, "extern_learn");
267 		}
268 		if (r->ndm_flags & NTF_OFFLOADED) {
269 			start_json_fdb_flags_array(&fdb_flags);
270 			jsonw_string(jw_global, "offload");
271 		}
272 		if (r->ndm_flags & NTF_MASTER)
273 			jsonw_string(jw_global, "master");
274 		if (fdb_flags)
275 			jsonw_end_array(jw_global);
276 
277 		if (tb[NDA_MASTER])
278 			jsonw_string_field(jw_global,
279 					   "master",
280 					   ll_index_to_name(rta_getattr_u32(tb[NDA_MASTER])));
281 
282 	} else {
283 		if (r->ndm_flags & NTF_SELF)
284 			fprintf(fp, "self ");
285 		if (r->ndm_flags & NTF_ROUTER)
286 			fprintf(fp, "router ");
287 		if (r->ndm_flags & NTF_EXT_LEARNED)
288 			fprintf(fp, "extern_learn ");
289 		if (r->ndm_flags & NTF_OFFLOADED)
290 			fprintf(fp, "offload ");
291 		if (tb[NDA_MASTER]) {
292 			fprintf(fp, "master %s ",
293 				ll_index_to_name(rta_getattr_u32(tb[NDA_MASTER])));
294 		} else if (r->ndm_flags & NTF_MASTER) {
295 			fprintf(fp, "master ");
296 		}
297 	}
298 
299 	state_s = state_n2a(r->ndm_state);
300 	if (jw_global) {
301 		if (state_s[0])
302 			jsonw_string_field(jw_global, "state", state_s);
303 
304 		jsonw_end_object(jw_global);
305 	} else {
306 		fprintf(fp, "%s\n", state_s);
307 
308 		fflush(fp);
309 	}
310 
311 	return 0;
312 }
313 
fdb_show(int argc,char ** argv)314 static int fdb_show(int argc, char **argv)
315 {
316 	struct {
317 		struct nlmsghdr	n;
318 		struct ifinfomsg	ifm;
319 		char			buf[256];
320 	} req = {
321 		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
322 		.ifm.ifi_family = PF_BRIDGE,
323 	};
324 
325 	char *filter_dev = NULL;
326 	char *br = NULL;
327 	int msg_size = sizeof(struct ifinfomsg);
328 
329 	while (argc > 0) {
330 		if ((strcmp(*argv, "brport") == 0) || strcmp(*argv, "dev") == 0) {
331 			NEXT_ARG();
332 			filter_dev = *argv;
333 		} else if (strcmp(*argv, "br") == 0) {
334 			NEXT_ARG();
335 			br = *argv;
336 		} else if (strcmp(*argv, "vlan") == 0) {
337 			NEXT_ARG();
338 			if (filter_vlan)
339 				duparg("vlan", *argv);
340 			filter_vlan = atoi(*argv);
341 		} else if (strcmp(*argv, "state") == 0) {
342 			unsigned int state;
343 
344 			NEXT_ARG();
345 			if (state_a2n(&state, *argv))
346 				invarg("invalid state", *argv);
347 			filter_state |= state;
348 		} else {
349 			if (matches(*argv, "help") == 0)
350 				usage();
351 		}
352 		argc--; argv++;
353 	}
354 
355 	if (br) {
356 		int br_ifindex = ll_name_to_index(br);
357 
358 		if (br_ifindex == 0) {
359 			fprintf(stderr, "Cannot find bridge device \"%s\"\n", br);
360 			return -1;
361 		}
362 		addattr32(&req.n, sizeof(req), IFLA_MASTER, br_ifindex);
363 		msg_size += RTA_LENGTH(4);
364 	}
365 
366 	/*we'll keep around filter_dev for older kernels */
367 	if (filter_dev) {
368 		filter_index = if_nametoindex(filter_dev);
369 		if (filter_index == 0) {
370 			fprintf(stderr, "Cannot find device \"%s\"\n",
371 				filter_dev);
372 			return -1;
373 		}
374 		req.ifm.ifi_index = filter_index;
375 	}
376 
377 	if (rtnl_dump_request(&rth, RTM_GETNEIGH, &req.ifm, msg_size) < 0) {
378 		perror("Cannot send dump request");
379 		exit(1);
380 	}
381 
382 	if (json_output) {
383 		jw_global = jsonw_new(stdout);
384 		if (!jw_global) {
385 			fprintf(stderr, "Error allocation json object\n");
386 			exit(1);
387 		}
388 		jsonw_start_array(jw_global);
389 	}
390 	if (rtnl_dump_filter(&rth, print_fdb, stdout) < 0) {
391 		fprintf(stderr, "Dump terminated\n");
392 		exit(1);
393 	}
394 	if (jw_global) {
395 		jsonw_end_array(jw_global);
396 		jsonw_destroy(&jw_global);
397 	}
398 
399 	return 0;
400 }
401 
fdb_modify(int cmd,int flags,int argc,char ** argv)402 static int fdb_modify(int cmd, int flags, int argc, char **argv)
403 {
404 	struct {
405 		struct nlmsghdr	n;
406 		struct ndmsg		ndm;
407 		char			buf[256];
408 	} req = {
409 		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
410 		.n.nlmsg_flags = NLM_F_REQUEST | flags,
411 		.n.nlmsg_type = cmd,
412 		.ndm.ndm_family = PF_BRIDGE,
413 		.ndm.ndm_state = NUD_NOARP,
414 	};
415 	char *addr = NULL;
416 	char *d = NULL;
417 	char abuf[ETH_ALEN];
418 	int dst_ok = 0;
419 	inet_prefix dst;
420 	unsigned long port = 0;
421 	unsigned long vni = ~0;
422 	unsigned int via = 0;
423 	char *endptr;
424 	short vid = -1;
425 
426 	while (argc > 0) {
427 		if (strcmp(*argv, "dev") == 0) {
428 			NEXT_ARG();
429 			d = *argv;
430 		} else if (strcmp(*argv, "dst") == 0) {
431 			NEXT_ARG();
432 			if (dst_ok)
433 				duparg2("dst", *argv);
434 			get_addr(&dst, *argv, preferred_family);
435 			dst_ok = 1;
436 		} else if (strcmp(*argv, "port") == 0) {
437 
438 			NEXT_ARG();
439 			port = strtoul(*argv, &endptr, 0);
440 			if (endptr && *endptr) {
441 				struct servent *pse;
442 
443 				pse = getservbyname(*argv, "udp");
444 				if (!pse)
445 					invarg("invalid port\n", *argv);
446 				port = ntohs(pse->s_port);
447 			} else if (port > 0xffff)
448 				invarg("invalid port\n", *argv);
449 		} else if (strcmp(*argv, "vni") == 0) {
450 			NEXT_ARG();
451 			vni = strtoul(*argv, &endptr, 0);
452 			if ((endptr && *endptr) ||
453 			    (vni >> 24) || vni == ULONG_MAX)
454 				invarg("invalid VNI\n", *argv);
455 		} else if (strcmp(*argv, "via") == 0) {
456 			NEXT_ARG();
457 			via = if_nametoindex(*argv);
458 			if (via == 0)
459 				invarg("invalid device\n", *argv);
460 		} else if (strcmp(*argv, "self") == 0) {
461 			req.ndm.ndm_flags |= NTF_SELF;
462 		} else if (matches(*argv, "master") == 0) {
463 			req.ndm.ndm_flags |= NTF_MASTER;
464 		} else if (matches(*argv, "router") == 0) {
465 			req.ndm.ndm_flags |= NTF_ROUTER;
466 		} else if (matches(*argv, "local") == 0 ||
467 			   matches(*argv, "permanent") == 0) {
468 			req.ndm.ndm_state |= NUD_PERMANENT;
469 		} else if (matches(*argv, "temp") == 0 ||
470 			   matches(*argv, "static") == 0) {
471 			req.ndm.ndm_state |= NUD_REACHABLE;
472 		} else if (matches(*argv, "dynamic") == 0) {
473 			req.ndm.ndm_state |= NUD_REACHABLE;
474 			req.ndm.ndm_state &= ~NUD_NOARP;
475 		} else if (matches(*argv, "vlan") == 0) {
476 			if (vid >= 0)
477 				duparg2("vlan", *argv);
478 			NEXT_ARG();
479 			vid = atoi(*argv);
480 		} else if (matches(*argv, "use") == 0) {
481 			req.ndm.ndm_flags |= NTF_USE;
482 		} else {
483 			if (strcmp(*argv, "to") == 0)
484 				NEXT_ARG();
485 
486 			if (matches(*argv, "help") == 0)
487 				usage();
488 			if (addr)
489 				duparg2("to", *argv);
490 			addr = *argv;
491 		}
492 		argc--; argv++;
493 	}
494 
495 	if (d == NULL || addr == NULL) {
496 		fprintf(stderr, "Device and address are required arguments.\n");
497 		return -1;
498 	}
499 
500 	/* Assume self */
501 	if (!(req.ndm.ndm_flags&(NTF_SELF|NTF_MASTER)))
502 		req.ndm.ndm_flags |= NTF_SELF;
503 
504 	/* Assume permanent */
505 	if (!(req.ndm.ndm_state&(NUD_PERMANENT|NUD_REACHABLE)))
506 		req.ndm.ndm_state |= NUD_PERMANENT;
507 
508 	if (sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
509 		   abuf, abuf+1, abuf+2,
510 		   abuf+3, abuf+4, abuf+5) != 6) {
511 		fprintf(stderr, "Invalid mac address %s\n", addr);
512 		return -1;
513 	}
514 
515 	addattr_l(&req.n, sizeof(req), NDA_LLADDR, abuf, ETH_ALEN);
516 	if (dst_ok)
517 		addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen);
518 
519 	if (vid >= 0)
520 		addattr16(&req.n, sizeof(req), NDA_VLAN, vid);
521 
522 	if (port) {
523 		unsigned short dport;
524 
525 		dport = htons((unsigned short)port);
526 		addattr16(&req.n, sizeof(req), NDA_PORT, dport);
527 	}
528 	if (vni != ~0)
529 		addattr32(&req.n, sizeof(req), NDA_VNI, vni);
530 	if (via)
531 		addattr32(&req.n, sizeof(req), NDA_IFINDEX, via);
532 
533 	req.ndm.ndm_ifindex = ll_name_to_index(d);
534 	if (req.ndm.ndm_ifindex == 0) {
535 		fprintf(stderr, "Cannot find device \"%s\"\n", d);
536 		return -1;
537 	}
538 
539 	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
540 		return -1;
541 
542 	return 0;
543 }
544 
do_fdb(int argc,char ** argv)545 int do_fdb(int argc, char **argv)
546 {
547 	ll_init_map(&rth);
548 
549 	if (argc > 0) {
550 		if (matches(*argv, "add") == 0)
551 			return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
552 		if (matches(*argv, "append") == 0)
553 			return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_APPEND, argc-1, argv+1);
554 		if (matches(*argv, "replace") == 0)
555 			return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
556 		if (matches(*argv, "delete") == 0)
557 			return fdb_modify(RTM_DELNEIGH, 0, argc-1, argv+1);
558 		if (matches(*argv, "show") == 0 ||
559 		    matches(*argv, "lst") == 0 ||
560 		    matches(*argv, "list") == 0)
561 			return fdb_show(argc-1, argv+1);
562 		if (matches(*argv, "help") == 0)
563 			usage();
564 	} else
565 		return fdb_show(0, NULL);
566 
567 	fprintf(stderr, "Command \"%s\" is unknown, try \"bridge fdb help\".\n", *argv);
568 	exit(-1);
569 }
570