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