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