Home | History | Annotate | Download | only in bridge
      1 /*
      2  * Get/set/delete fdb table with netlink
      3  *
      4  * TODO: merge/replace this with ip neighbour
      5  *
      6  * Authors:	Stephen Hemminger <shemminger (at) 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 
     25 #include "libnetlink.h"
     26 #include "br_common.h"
     27 #include "rt_names.h"
     28 #include "utils.h"
     29 
     30 static unsigned int filter_index;
     31 
     32 static void usage(void)
     33 {
     34 	fprintf(stderr, "Usage: bridge fdb { add | append | del | replace } ADDR dev DEV\n"
     35 			"              [ self ] [ master ] [ use ] [ router ]\n"
     36 			"              [ local | temp ] [ dst IPADDR ] [ vlan VID ]\n"
     37 		        "              [ port PORT] [ vni VNI ] [ via DEV ]\n");
     38 	fprintf(stderr, "       bridge fdb [ show [ br BRDEV ] [ brport DEV ] ]\n");
     39 	exit(-1);
     40 }
     41 
     42 static const char *state_n2a(unsigned s)
     43 {
     44 	static char buf[32];
     45 
     46 	if (s & NUD_PERMANENT)
     47 		return "permanent";
     48 
     49 	if (s & NUD_NOARP)
     50 		return "static";
     51 
     52 	if (s & NUD_STALE)
     53 		return "stale";
     54 
     55 	if (s & NUD_REACHABLE)
     56 		return "";
     57 
     58 	sprintf(buf, "state=%#x", s);
     59 	return buf;
     60 }
     61 
     62 int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
     63 {
     64 	FILE *fp = arg;
     65 	struct ndmsg *r = NLMSG_DATA(n);
     66 	int len = n->nlmsg_len;
     67 	struct rtattr * tb[NDA_MAX+1];
     68 
     69 	if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) {
     70 		fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n",
     71 			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
     72 
     73 		return 0;
     74 	}
     75 
     76 	len -= NLMSG_LENGTH(sizeof(*r));
     77 	if (len < 0) {
     78 		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
     79 		return -1;
     80 	}
     81 
     82 	if (r->ndm_family != AF_BRIDGE)
     83 		return 0;
     84 
     85 	if (filter_index && filter_index != r->ndm_ifindex)
     86 		return 0;
     87 
     88 	parse_rtattr(tb, NDA_MAX, NDA_RTA(r),
     89 		     n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
     90 
     91 	if (n->nlmsg_type == RTM_DELNEIGH)
     92 		fprintf(fp, "Deleted ");
     93 
     94 	if (tb[NDA_LLADDR]) {
     95 		SPRINT_BUF(b1);
     96 		fprintf(fp, "%s ",
     97 			ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
     98 				    RTA_PAYLOAD(tb[NDA_LLADDR]),
     99 				    ll_index_to_type(r->ndm_ifindex),
    100 				    b1, sizeof(b1)));
    101 	}
    102 
    103 	if (!filter_index && r->ndm_ifindex)
    104 		fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex));
    105 
    106 	if (tb[NDA_DST]) {
    107 		SPRINT_BUF(abuf);
    108 		int family = AF_INET;
    109 
    110 		if (RTA_PAYLOAD(tb[NDA_DST]) == sizeof(struct in6_addr))
    111 			family = AF_INET6;
    112 
    113 		fprintf(fp, "dst %s ",
    114 			format_host(family,
    115 				    RTA_PAYLOAD(tb[NDA_DST]),
    116 				    RTA_DATA(tb[NDA_DST]),
    117 				    abuf, sizeof(abuf)));
    118 	}
    119 
    120 	if (tb[NDA_VLAN]) {
    121 		__u16 vid = rta_getattr_u16(tb[NDA_VLAN]);
    122 		fprintf(fp, "vlan %hu ", vid);
    123 	}
    124 
    125 	if (tb[NDA_PORT])
    126 		fprintf(fp, "port %d ", ntohs(rta_getattr_u16(tb[NDA_PORT])));
    127 	if (tb[NDA_VNI])
    128 		fprintf(fp, "vni %d ", rta_getattr_u32(tb[NDA_VNI]));
    129 	if (tb[NDA_IFINDEX]) {
    130 		unsigned int ifindex = rta_getattr_u32(tb[NDA_IFINDEX]);
    131 
    132 		if (ifindex) {
    133 			char ifname[IF_NAMESIZE];
    134 
    135 			if (!tb[NDA_LINK_NETNSID] &&
    136 			    if_indextoname(ifindex, ifname))
    137 				fprintf(fp, "via %s ", ifname);
    138 			else
    139 				fprintf(fp, "via ifindex %u ", ifindex);
    140 		}
    141 	}
    142 	if (tb[NDA_LINK_NETNSID])
    143 		fprintf(fp, "link-netnsid %d ",
    144 			rta_getattr_u32(tb[NDA_LINK_NETNSID]));
    145 
    146 	if (show_stats && tb[NDA_CACHEINFO]) {
    147 		struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
    148 		int hz = get_user_hz();
    149 
    150 		fprintf(fp, "used %d/%d ", ci->ndm_used/hz,
    151 		       ci->ndm_updated/hz);
    152 	}
    153 	if (r->ndm_flags & NTF_SELF)
    154 		fprintf(fp, "self ");
    155 	if (tb[NDA_MASTER])
    156 		fprintf(fp, "master %s ",
    157 			ll_index_to_name(rta_getattr_u32(tb[NDA_MASTER])));
    158 	else if (r->ndm_flags & NTF_MASTER)
    159 		fprintf(fp, "master ");
    160 	if (r->ndm_flags & NTF_ROUTER)
    161 		fprintf(fp, "router ");
    162 	if (r->ndm_flags & NTF_EXT_LEARNED)
    163 		fprintf(fp, "offload ");
    164 
    165 	fprintf(fp, "%s\n", state_n2a(r->ndm_state));
    166 	fflush(fp);
    167 
    168 	return 0;
    169 }
    170 
    171 static int fdb_show(int argc, char **argv)
    172 {
    173 	struct {
    174 		struct nlmsghdr 	n;
    175 		struct ifinfomsg	ifm;
    176 		char   			buf[256];
    177 	} req;
    178 
    179 	char *filter_dev = NULL;
    180 	char *br = NULL;
    181 	int msg_size = sizeof(struct ifinfomsg);
    182 
    183 	memset(&req, 0, sizeof(req));
    184 	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
    185 	req.ifm.ifi_family = PF_BRIDGE;
    186 
    187 	while (argc > 0) {
    188 		if ((strcmp(*argv, "brport") == 0) || strcmp(*argv, "dev") == 0) {
    189 			NEXT_ARG();
    190 			filter_dev = *argv;
    191 		} else if (strcmp(*argv, "br") == 0) {
    192 			NEXT_ARG();
    193 			br = *argv;
    194 		} else {
    195 			if (matches(*argv, "help") == 0)
    196 				usage();
    197 		}
    198 		argc--; argv++;
    199 	}
    200 
    201 	if (br) {
    202 		int br_ifindex = ll_name_to_index(br);
    203 		if (br_ifindex == 0) {
    204 			fprintf(stderr, "Cannot find bridge device \"%s\"\n", br);
    205 			return -1;
    206 		}
    207 		addattr32(&req.n, sizeof(req), IFLA_MASTER, br_ifindex);
    208 		msg_size += RTA_LENGTH(4);
    209 	}
    210 
    211 	/*we'll keep around filter_dev for older kernels */
    212 	if (filter_dev) {
    213 		filter_index = if_nametoindex(filter_dev);
    214 		if (filter_index == 0) {
    215 			fprintf(stderr, "Cannot find device \"%s\"\n",
    216 				filter_dev);
    217 			return -1;
    218 		}
    219 		req.ifm.ifi_index = filter_index;
    220 	}
    221 
    222 	if (rtnl_dump_request(&rth, RTM_GETNEIGH, &req.ifm, msg_size) < 0) {
    223 		perror("Cannot send dump request");
    224 		exit(1);
    225 	}
    226 
    227 	if (rtnl_dump_filter(&rth, print_fdb, stdout) < 0) {
    228 		fprintf(stderr, "Dump terminated\n");
    229 		exit(1);
    230 	}
    231 
    232 	return 0;
    233 }
    234 
    235 static int fdb_modify(int cmd, int flags, int argc, char **argv)
    236 {
    237 	struct {
    238 		struct nlmsghdr 	n;
    239 		struct ndmsg 		ndm;
    240 		char   			buf[256];
    241 	} req;
    242 	char *addr = NULL;
    243 	char *d = NULL;
    244 	char abuf[ETH_ALEN];
    245 	int dst_ok = 0;
    246 	inet_prefix dst;
    247 	unsigned long port = 0;
    248 	unsigned long vni = ~0;
    249 	unsigned int via = 0;
    250 	char *endptr;
    251 	short vid = -1;
    252 
    253 	memset(&req, 0, sizeof(req));
    254 
    255 	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
    256 	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
    257 	req.n.nlmsg_type = cmd;
    258 	req.ndm.ndm_family = PF_BRIDGE;
    259 	req.ndm.ndm_state = NUD_NOARP;
    260 
    261 	while (argc > 0) {
    262 		if (strcmp(*argv, "dev") == 0) {
    263 			NEXT_ARG();
    264 			d = *argv;
    265 		} else if (strcmp(*argv, "dst") == 0) {
    266 			NEXT_ARG();
    267 			if (dst_ok)
    268 				duparg2("dst", *argv);
    269 			get_addr(&dst, *argv, preferred_family);
    270 			dst_ok = 1;
    271 		} else if (strcmp(*argv, "port") == 0) {
    272 
    273 			NEXT_ARG();
    274 			port = strtoul(*argv, &endptr, 0);
    275 			if (endptr && *endptr) {
    276 				struct servent *pse;
    277 
    278 				pse = getservbyname(*argv, "udp");
    279 				if (!pse)
    280 					invarg("invalid port\n", *argv);
    281 				port = ntohs(pse->s_port);
    282 			} else if (port > 0xffff)
    283 				invarg("invalid port\n", *argv);
    284 		} else if (strcmp(*argv, "vni") == 0) {
    285 			NEXT_ARG();
    286 			vni = strtoul(*argv, &endptr, 0);
    287 			if ((endptr && *endptr) ||
    288 			    (vni >> 24) || vni == ULONG_MAX)
    289 				invarg("invalid VNI\n", *argv);
    290 		} else if (strcmp(*argv, "via") == 0) {
    291 			NEXT_ARG();
    292 			via = if_nametoindex(*argv);
    293 			if (via == 0)
    294 				invarg("invalid device\n", *argv);
    295 		} else if (strcmp(*argv, "self") == 0) {
    296 			req.ndm.ndm_flags |= NTF_SELF;
    297 		} else if (matches(*argv, "master") == 0) {
    298 			req.ndm.ndm_flags |= NTF_MASTER;
    299 		} else if (matches(*argv, "router") == 0) {
    300 			req.ndm.ndm_flags |= NTF_ROUTER;
    301 		} else if (matches(*argv, "local") == 0||
    302 			   matches(*argv, "permanent") == 0) {
    303 			req.ndm.ndm_state |= NUD_PERMANENT;
    304 		} else if (matches(*argv, "temp") == 0) {
    305 			req.ndm.ndm_state |= NUD_REACHABLE;
    306 		} else if (matches(*argv, "vlan") == 0) {
    307 			if (vid >= 0)
    308 				duparg2("vlan", *argv);
    309 			NEXT_ARG();
    310 			vid = atoi(*argv);
    311 		} else if (matches(*argv, "use") == 0) {
    312 			req.ndm.ndm_flags |= NTF_USE;
    313 		} else {
    314 			if (strcmp(*argv, "to") == 0) {
    315 				NEXT_ARG();
    316 			}
    317 			if (matches(*argv, "help") == 0)
    318 				usage();
    319 			if (addr)
    320 				duparg2("to", *argv);
    321 			addr = *argv;
    322 		}
    323 		argc--; argv++;
    324 	}
    325 
    326 	if (d == NULL || addr == NULL) {
    327 		fprintf(stderr, "Device and address are required arguments.\n");
    328 		return -1;
    329 	}
    330 
    331 	/* Assume self */
    332 	if (!(req.ndm.ndm_flags&(NTF_SELF|NTF_MASTER)))
    333 		req.ndm.ndm_flags |= NTF_SELF;
    334 
    335 	/* Assume permanent */
    336 	if (!(req.ndm.ndm_state&(NUD_PERMANENT|NUD_REACHABLE)))
    337 		req.ndm.ndm_state |= NUD_PERMANENT;
    338 
    339 	if (sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
    340 		   abuf, abuf+1, abuf+2,
    341 		   abuf+3, abuf+4, abuf+5) != 6) {
    342 		fprintf(stderr, "Invalid mac address %s\n", addr);
    343 		return -1;
    344 	}
    345 
    346 	addattr_l(&req.n, sizeof(req), NDA_LLADDR, abuf, ETH_ALEN);
    347 	if (dst_ok)
    348 		addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen);
    349 
    350 	if (vid >= 0)
    351 		addattr16(&req.n, sizeof(req), NDA_VLAN, vid);
    352 
    353 	if (port) {
    354 		unsigned short dport;
    355 
    356 		dport = htons((unsigned short)port);
    357 		addattr16(&req.n, sizeof(req), NDA_PORT, dport);
    358 	}
    359 	if (vni != ~0)
    360 		addattr32(&req.n, sizeof(req), NDA_VNI, vni);
    361 	if (via)
    362 		addattr32(&req.n, sizeof(req), NDA_IFINDEX, via);
    363 
    364 	req.ndm.ndm_ifindex = ll_name_to_index(d);
    365 	if (req.ndm.ndm_ifindex == 0) {
    366 		fprintf(stderr, "Cannot find device \"%s\"\n", d);
    367 		return -1;
    368 	}
    369 
    370 	if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
    371 		return -1;
    372 
    373 	return 0;
    374 }
    375 
    376 int do_fdb(int argc, char **argv)
    377 {
    378 	ll_init_map(&rth);
    379 
    380 	if (argc > 0) {
    381 		if (matches(*argv, "add") == 0)
    382 			return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
    383 		if (matches(*argv, "append") == 0)
    384 			return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_APPEND, argc-1, argv+1);
    385 		if (matches(*argv, "replace") == 0)
    386 			return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
    387 		if (matches(*argv, "delete") == 0)
    388 			return fdb_modify(RTM_DELNEIGH, 0, argc-1, argv+1);
    389 		if (matches(*argv, "show") == 0 ||
    390 		    matches(*argv, "lst") == 0 ||
    391 		    matches(*argv, "list") == 0)
    392 			return fdb_show(argc-1, argv+1);
    393 		if (matches(*argv, "help") == 0)
    394 			usage();
    395 	} else
    396 		return fdb_show(0, NULL);
    397 
    398 	fprintf(stderr, "Command \"%s\" is unknown, try \"bridge fdb help\".\n", *argv);
    399 	exit(-1);
    400 }
    401