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