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