Home | History | Annotate | Download | only in ip
      1 /*
      2  * ipneigh.c		"ip neigh".
      3  *
      4  *		This program is free software; you can redistribute it and/or
      5  *		modify it under the terms of the GNU General Public License
      6  *		as published by the Free Software Foundation; either version
      7  *		2 of the License, or (at your option) any later version.
      8  *
      9  * Authors:	Alexey Kuznetsov, <kuznet (at) ms2.inr.ac.ru>
     10  *
     11  */
     12 
     13 #include <stdio.h>
     14 #include <stdlib.h>
     15 #include <unistd.h>
     16 #include <syslog.h>
     17 #include <fcntl.h>
     18 #include <string.h>
     19 #include <sys/time.h>
     20 #include <sys/socket.h>
     21 #include <netinet/in.h>
     22 #include <netinet/ip.h>
     23 
     24 #include "rt_names.h"
     25 #include "utils.h"
     26 #include "ip_common.h"
     27 
     28 #define NUD_VALID	(NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
     29 #define MAX_ROUNDS	10
     30 
     31 static struct
     32 {
     33 	int family;
     34         int index;
     35 	int state;
     36 	int unused_only;
     37 	inet_prefix pfx;
     38 	int flushed;
     39 	char *flushb;
     40 	int flushp;
     41 	int flushe;
     42 } filter;
     43 
     44 static void usage(void) __attribute__((noreturn));
     45 
     46 static void usage(void)
     47 {
     48 	fprintf(stderr, "Usage: ip neigh { add | del | change | replace } { ADDR [ lladdr LLADDR ]\n"
     49 		        "          [ nud { permanent | noarp | stale | reachable } ]\n"
     50 		        "          | proxy ADDR } [ dev DEV ]\n");
     51 	fprintf(stderr, "       ip neigh {show|flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n");
     52 	exit(-1);
     53 }
     54 
     55 int nud_state_a2n(unsigned *state, char *arg)
     56 {
     57 	if (matches(arg, "permanent") == 0)
     58 		*state = NUD_PERMANENT;
     59 	else if (matches(arg, "reachable") == 0)
     60 		*state = NUD_REACHABLE;
     61 	else if (strcmp(arg, "noarp") == 0)
     62 		*state = NUD_NOARP;
     63 	else if (strcmp(arg, "none") == 0)
     64 		*state = NUD_NONE;
     65 	else if (strcmp(arg, "stale") == 0)
     66 		*state = NUD_STALE;
     67 	else if (strcmp(arg, "incomplete") == 0)
     68 		*state = NUD_INCOMPLETE;
     69 	else if (strcmp(arg, "delay") == 0)
     70 		*state = NUD_DELAY;
     71 	else if (strcmp(arg, "probe") == 0)
     72 		*state = NUD_PROBE;
     73 	else if (matches(arg, "failed") == 0)
     74 		*state = NUD_FAILED;
     75 	else {
     76 		if (get_unsigned(state, arg, 0))
     77 			return -1;
     78 		if (*state>=0x100 || (*state&((*state)-1)))
     79 			return -1;
     80 	}
     81 	return 0;
     82 }
     83 
     84 static int flush_update(void)
     85 {
     86 	if (rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) {
     87 		perror("Failed to send flush request");
     88 		return -1;
     89 	}
     90 	filter.flushp = 0;
     91 	return 0;
     92 }
     93 
     94 
     95 static int ipneigh_modify(int cmd, int flags, int argc, char **argv)
     96 {
     97 	struct {
     98 		struct nlmsghdr 	n;
     99 		struct ndmsg 		ndm;
    100 		char   			buf[256];
    101 	} req;
    102 	char  *d = NULL;
    103 	int dst_ok = 0;
    104 	int lladdr_ok = 0;
    105 	char * lla = NULL;
    106 	inet_prefix dst;
    107 
    108 	memset(&req, 0, sizeof(req));
    109 
    110 	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
    111 	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
    112 	req.n.nlmsg_type = cmd;
    113 	req.ndm.ndm_family = preferred_family;
    114 	req.ndm.ndm_state = NUD_PERMANENT;
    115 
    116 	while (argc > 0) {
    117 		if (matches(*argv, "lladdr") == 0) {
    118 			NEXT_ARG();
    119 			if (lladdr_ok)
    120 				duparg("lladdr", *argv);
    121 			lla = *argv;
    122 			lladdr_ok = 1;
    123 		} else if (strcmp(*argv, "nud") == 0) {
    124 			unsigned state;
    125 			NEXT_ARG();
    126 			if (nud_state_a2n(&state, *argv))
    127 				invarg("nud state is bad", *argv);
    128 			req.ndm.ndm_state = state;
    129 		} else if (matches(*argv, "proxy") == 0) {
    130 			NEXT_ARG();
    131 			if (matches(*argv, "help") == 0)
    132 				usage();
    133 			if (dst_ok)
    134 				duparg("address", *argv);
    135 			get_addr(&dst, *argv, preferred_family);
    136 			dst_ok = 1;
    137 			req.ndm.ndm_flags |= NTF_PROXY;
    138 		} else if (strcmp(*argv, "dev") == 0) {
    139 			NEXT_ARG();
    140 			d = *argv;
    141 		} else {
    142 			if (strcmp(*argv, "to") == 0) {
    143 				NEXT_ARG();
    144 			}
    145 			if (matches(*argv, "help") == 0) {
    146 				NEXT_ARG();
    147 			}
    148 			if (dst_ok)
    149 				duparg2("to", *argv);
    150 			get_addr(&dst, *argv, preferred_family);
    151 			dst_ok = 1;
    152 		}
    153 		argc--; argv++;
    154 	}
    155 	if (d == NULL || !dst_ok || dst.family == AF_UNSPEC) {
    156 		fprintf(stderr, "Device and destination are required arguments.\n");
    157 		exit(-1);
    158 	}
    159 	req.ndm.ndm_family = dst.family;
    160 	addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen);
    161 
    162 	if (lla && strcmp(lla, "null")) {
    163 		char llabuf[20];
    164 		int l;
    165 
    166 		l = ll_addr_a2n(llabuf, sizeof(llabuf), lla);
    167 		addattr_l(&req.n, sizeof(req), NDA_LLADDR, llabuf, l);
    168 	}
    169 
    170 	ll_init_map(&rth);
    171 
    172 	if ((req.ndm.ndm_ifindex = ll_name_to_index(d)) == 0) {
    173 		fprintf(stderr, "Cannot find device \"%s\"\n", d);
    174 		return -1;
    175 	}
    176 
    177 	if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
    178 		exit(2);
    179 
    180 	return 0;
    181 }
    182 
    183 
    184 int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
    185 {
    186 	FILE *fp = (FILE*)arg;
    187 	struct ndmsg *r = NLMSG_DATA(n);
    188 	int len = n->nlmsg_len;
    189 	struct rtattr * tb[NDA_MAX+1];
    190 	char abuf[256];
    191 
    192 	if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) {
    193 		fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n",
    194 			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
    195 
    196 		return 0;
    197 	}
    198 	len -= NLMSG_LENGTH(sizeof(*r));
    199 	if (len < 0) {
    200 		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
    201 		return -1;
    202 	}
    203 
    204 	if (filter.flushb && n->nlmsg_type != RTM_NEWNEIGH)
    205 		return 0;
    206 
    207 	if (filter.family && filter.family != r->ndm_family)
    208 		return 0;
    209 	if (filter.index && filter.index != r->ndm_ifindex)
    210 		return 0;
    211 	if (!(filter.state&r->ndm_state) &&
    212 	    !(r->ndm_flags & NTF_PROXY) &&
    213 	    (r->ndm_state || !(filter.state&0x100)) &&
    214              (r->ndm_family != AF_DECnet))
    215 		return 0;
    216 
    217 	parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
    218 
    219 	if (tb[NDA_DST]) {
    220 		if (filter.pfx.family) {
    221 			inet_prefix dst;
    222 			memset(&dst, 0, sizeof(dst));
    223 			dst.family = r->ndm_family;
    224 			memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
    225 			if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
    226 				return 0;
    227 		}
    228 	}
    229 	if (filter.unused_only && tb[NDA_CACHEINFO]) {
    230 		struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
    231 		if (ci->ndm_refcnt)
    232 			return 0;
    233 	}
    234 
    235 	if (filter.flushb) {
    236 		struct nlmsghdr *fn;
    237 		if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
    238 			if (flush_update())
    239 				return -1;
    240 		}
    241 		fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
    242 		memcpy(fn, n, n->nlmsg_len);
    243 		fn->nlmsg_type = RTM_DELNEIGH;
    244 		fn->nlmsg_flags = NLM_F_REQUEST;
    245 		fn->nlmsg_seq = ++rth.seq;
    246 		filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
    247 		filter.flushed++;
    248 		if (show_stats < 2)
    249 			return 0;
    250 	}
    251 
    252 	if (tb[NDA_DST]) {
    253 		fprintf(fp, "%s ",
    254 			format_host(r->ndm_family,
    255 				    RTA_PAYLOAD(tb[NDA_DST]),
    256 				    RTA_DATA(tb[NDA_DST]),
    257 				    abuf, sizeof(abuf)));
    258 	}
    259 	if (!filter.index && r->ndm_ifindex)
    260 		fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex));
    261 	if (tb[NDA_LLADDR]) {
    262 		SPRINT_BUF(b1);
    263 		fprintf(fp, "lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
    264 					      RTA_PAYLOAD(tb[NDA_LLADDR]),
    265 					      ll_index_to_type(r->ndm_ifindex),
    266 					      b1, sizeof(b1)));
    267 	}
    268 	if (r->ndm_flags & NTF_ROUTER) {
    269 		fprintf(fp, " router");
    270 	}
    271 	if (r->ndm_flags & NTF_PROXY) {
    272 		fprintf(fp, " proxy");
    273 	}
    274 	if (tb[NDA_CACHEINFO] && show_stats) {
    275 		struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
    276 		int hz = get_user_hz();
    277 
    278 		if (ci->ndm_refcnt)
    279 			printf(" ref %d", ci->ndm_refcnt);
    280 		fprintf(fp, " used %d/%d/%d", ci->ndm_used/hz,
    281 		       ci->ndm_confirmed/hz, ci->ndm_updated/hz);
    282 	}
    283 
    284 	if (tb[NDA_PROBES] && show_stats) {
    285 		__u32 p = rta_getattr_u32(tb[NDA_PROBES]);
    286 		fprintf(fp, " probes %u", p);
    287 	}
    288 
    289 	if (r->ndm_state) {
    290 		int nud = r->ndm_state;
    291 		fprintf(fp, " ");
    292 
    293 #define PRINT_FLAG(f) if (nud & NUD_##f) { \
    294 	nud &= ~NUD_##f; fprintf(fp, #f "%s", nud ? "," : ""); }
    295 		PRINT_FLAG(INCOMPLETE);
    296 		PRINT_FLAG(REACHABLE);
    297 		PRINT_FLAG(STALE);
    298 		PRINT_FLAG(DELAY);
    299 		PRINT_FLAG(PROBE);
    300 		PRINT_FLAG(FAILED);
    301 		PRINT_FLAG(NOARP);
    302 		PRINT_FLAG(PERMANENT);
    303 #undef PRINT_FLAG
    304 	}
    305 	fprintf(fp, "\n");
    306 
    307 	fflush(fp);
    308 	return 0;
    309 }
    310 
    311 void ipneigh_reset_filter()
    312 {
    313 	memset(&filter, 0, sizeof(filter));
    314 	filter.state = ~0;
    315 }
    316 
    317 int do_show_or_flush(int argc, char **argv, int flush)
    318 {
    319 	char *filter_dev = NULL;
    320 	int state_given = 0;
    321 	struct ndmsg ndm = { 0 };
    322 
    323 	ipneigh_reset_filter();
    324 
    325 	if (!filter.family)
    326 		filter.family = preferred_family;
    327 
    328 	if (flush) {
    329 		if (argc <= 0) {
    330 			fprintf(stderr, "Flush requires arguments.\n");
    331 			return -1;
    332 		}
    333 		filter.state = ~(NUD_PERMANENT|NUD_NOARP);
    334 	} else
    335 		filter.state = 0xFF & ~NUD_NOARP;
    336 
    337 	while (argc > 0) {
    338 		if (strcmp(*argv, "dev") == 0) {
    339 			NEXT_ARG();
    340 			if (filter_dev)
    341 				duparg("dev", *argv);
    342 			filter_dev = *argv;
    343 		} else if (strcmp(*argv, "unused") == 0) {
    344 			filter.unused_only = 1;
    345 		} else if (strcmp(*argv, "nud") == 0) {
    346 			unsigned state;
    347 			NEXT_ARG();
    348 			if (!state_given) {
    349 				state_given = 1;
    350 				filter.state = 0;
    351 			}
    352 			if (nud_state_a2n(&state, *argv)) {
    353 				if (strcmp(*argv, "all") != 0)
    354 					invarg("nud state is bad", *argv);
    355 				state = ~0;
    356 				if (flush)
    357 					state &= ~NUD_NOARP;
    358 			}
    359 			if (state == 0)
    360 				state = 0x100;
    361 			filter.state |= state;
    362 		} else if (strcmp(*argv, "proxy") == 0)
    363 			ndm.ndm_flags = NTF_PROXY;
    364 		else {
    365 			if (strcmp(*argv, "to") == 0) {
    366 				NEXT_ARG();
    367 			}
    368 			if (matches(*argv, "help") == 0)
    369 				usage();
    370 			get_prefix(&filter.pfx, *argv, filter.family);
    371 			if (filter.family == AF_UNSPEC)
    372 				filter.family = filter.pfx.family;
    373 		}
    374 		argc--; argv++;
    375 	}
    376 
    377 	ll_init_map(&rth);
    378 
    379 	if (filter_dev) {
    380 		if ((filter.index = ll_name_to_index(filter_dev)) == 0) {
    381 			fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev);
    382 			return -1;
    383 		}
    384 	}
    385 
    386 	if (flush) {
    387 		int round = 0;
    388 		char flushb[4096-512];
    389 
    390 		filter.flushb = flushb;
    391 		filter.flushp = 0;
    392 		filter.flushe = sizeof(flushb);
    393 		filter.state &= ~NUD_FAILED;
    394 
    395 		while (round < MAX_ROUNDS) {
    396 			if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) {
    397 				perror("Cannot send dump request");
    398 				exit(1);
    399 			}
    400 			filter.flushed = 0;
    401 			if (rtnl_dump_filter(&rth, print_neigh, stdout) < 0) {
    402 				fprintf(stderr, "Flush terminated\n");
    403 				exit(1);
    404 			}
    405 			if (filter.flushed == 0) {
    406 				if (show_stats) {
    407 					if (round == 0)
    408 						printf("Nothing to flush.\n");
    409 					else
    410 						printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
    411 				}
    412 				fflush(stdout);
    413 				return 0;
    414 			}
    415 			round++;
    416 			if (flush_update() < 0)
    417 				exit(1);
    418 			if (show_stats) {
    419 				printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed);
    420 				fflush(stdout);
    421 			}
    422 		}
    423 		printf("*** Flush not complete bailing out after %d rounds\n",
    424 			MAX_ROUNDS);
    425 		return 1;
    426 	}
    427 
    428 	ndm.ndm_family = filter.family;
    429 
    430 	if (rtnl_dump_request(&rth, RTM_GETNEIGH, &ndm, sizeof(struct ndmsg)) < 0) {
    431 		perror("Cannot send dump request");
    432 		exit(1);
    433 	}
    434 
    435 	if (rtnl_dump_filter(&rth, print_neigh, stdout) < 0) {
    436 		fprintf(stderr, "Dump terminated\n");
    437 		exit(1);
    438 	}
    439 
    440 	return 0;
    441 }
    442 
    443 int do_ipneigh(int argc, char **argv)
    444 {
    445 	if (argc > 0) {
    446 		if (matches(*argv, "add") == 0)
    447 			return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
    448 		if (matches(*argv, "change") == 0 ||
    449 		    strcmp(*argv, "chg") == 0)
    450 			return ipneigh_modify(RTM_NEWNEIGH, NLM_F_REPLACE, argc-1, argv+1);
    451 		if (matches(*argv, "replace") == 0)
    452 			return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
    453 		if (matches(*argv, "delete") == 0)
    454 			return ipneigh_modify(RTM_DELNEIGH, 0, argc-1, argv+1);
    455 		if (matches(*argv, "get") == 0) {
    456 			fprintf(stderr, "Sorry, \"neigh get\" is not implemented :-(\n");
    457 			return -1;
    458 		}
    459 		if (matches(*argv, "show") == 0 ||
    460 		    matches(*argv, "lst") == 0 ||
    461 		    matches(*argv, "list") == 0)
    462 			return do_show_or_flush(argc-1, argv+1, 0);
    463 		if (matches(*argv, "flush") == 0)
    464 			return do_show_or_flush(argc-1, argv+1, 1);
    465 		if (matches(*argv, "help") == 0)
    466 			usage();
    467 	} else
    468 		return do_show_or_flush(0, NULL, 0);
    469 
    470 	fprintf(stderr, "Command \"%s\" is unknown, try \"ip neigh help\".\n", *argv);
    471 	exit(-1);
    472 }
    473