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