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, NULL, 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_state || !(filter.state&0x100)) && 213 (r->ndm_family != AF_DECnet)) 214 return 0; 215 216 parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); 217 218 if (tb[NDA_DST]) { 219 if (filter.pfx.family) { 220 inet_prefix dst; 221 memset(&dst, 0, sizeof(dst)); 222 dst.family = r->ndm_family; 223 memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST])); 224 if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) 225 return 0; 226 } 227 } 228 if (filter.unused_only && tb[NDA_CACHEINFO]) { 229 struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); 230 if (ci->ndm_refcnt) 231 return 0; 232 } 233 234 if (filter.flushb) { 235 struct nlmsghdr *fn; 236 if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { 237 if (flush_update()) 238 return -1; 239 } 240 fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); 241 memcpy(fn, n, n->nlmsg_len); 242 fn->nlmsg_type = RTM_DELNEIGH; 243 fn->nlmsg_flags = NLM_F_REQUEST; 244 fn->nlmsg_seq = ++rth.seq; 245 filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; 246 filter.flushed++; 247 if (show_stats < 2) 248 return 0; 249 } 250 251 if (tb[NDA_DST]) { 252 fprintf(fp, "%s ", 253 format_host(r->ndm_family, 254 RTA_PAYLOAD(tb[NDA_DST]), 255 RTA_DATA(tb[NDA_DST]), 256 abuf, sizeof(abuf))); 257 } 258 if (!filter.index && r->ndm_ifindex) 259 fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex)); 260 if (tb[NDA_LLADDR]) { 261 SPRINT_BUF(b1); 262 fprintf(fp, "lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), 263 RTA_PAYLOAD(tb[NDA_LLADDR]), 264 ll_index_to_type(r->ndm_ifindex), 265 b1, sizeof(b1))); 266 } 267 if (r->ndm_flags & NTF_ROUTER) { 268 fprintf(fp, " router"); 269 } 270 if (tb[NDA_CACHEINFO] && show_stats) { 271 struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); 272 int hz = get_user_hz(); 273 274 if (ci->ndm_refcnt) 275 printf(" ref %d", ci->ndm_refcnt); 276 fprintf(fp, " used %d/%d/%d", ci->ndm_used/hz, 277 ci->ndm_confirmed/hz, ci->ndm_updated/hz); 278 } 279 280 #ifdef NDA_PROBES 281 if (tb[NDA_PROBES] && show_stats) { 282 __u32 p = *(__u32 *) RTA_DATA(tb[NDA_PROBES]); 283 fprintf(fp, " probes %u", p); 284 } 285 #endif 286 287 if (r->ndm_state) { 288 int nud = r->ndm_state; 289 fprintf(fp, " "); 290 291 #define PRINT_FLAG(f) if (nud & NUD_##f) { \ 292 nud &= ~NUD_##f; fprintf(fp, #f "%s", nud ? "," : ""); } 293 PRINT_FLAG(INCOMPLETE); 294 PRINT_FLAG(REACHABLE); 295 PRINT_FLAG(STALE); 296 PRINT_FLAG(DELAY); 297 PRINT_FLAG(PROBE); 298 PRINT_FLAG(FAILED); 299 PRINT_FLAG(NOARP); 300 PRINT_FLAG(PERMANENT); 301 #undef PRINT_FLAG 302 } 303 fprintf(fp, "\n"); 304 305 fflush(fp); 306 return 0; 307 } 308 309 void ipneigh_reset_filter() 310 { 311 memset(&filter, 0, sizeof(filter)); 312 filter.state = ~0; 313 } 314 315 int do_show_or_flush(int argc, char **argv, int flush) 316 { 317 char *filter_dev = NULL; 318 int state_given = 0; 319 320 ipneigh_reset_filter(); 321 322 if (!filter.family) 323 filter.family = preferred_family; 324 325 if (flush) { 326 if (argc <= 0) { 327 fprintf(stderr, "Flush requires arguments.\n"); 328 return -1; 329 } 330 filter.state = ~(NUD_PERMANENT|NUD_NOARP); 331 } else 332 filter.state = 0xFF & ~NUD_NOARP; 333 334 while (argc > 0) { 335 if (strcmp(*argv, "dev") == 0) { 336 NEXT_ARG(); 337 if (filter_dev) 338 duparg("dev", *argv); 339 filter_dev = *argv; 340 } else if (strcmp(*argv, "unused") == 0) { 341 filter.unused_only = 1; 342 } else if (strcmp(*argv, "nud") == 0) { 343 unsigned state; 344 NEXT_ARG(); 345 if (!state_given) { 346 state_given = 1; 347 filter.state = 0; 348 } 349 if (nud_state_a2n(&state, *argv)) { 350 if (strcmp(*argv, "all") != 0) 351 invarg("nud state is bad", *argv); 352 state = ~0; 353 if (flush) 354 state &= ~NUD_NOARP; 355 } 356 if (state == 0) 357 state = 0x100; 358 filter.state |= state; 359 } else { 360 if (strcmp(*argv, "to") == 0) { 361 NEXT_ARG(); 362 } 363 if (matches(*argv, "help") == 0) 364 usage(); 365 get_prefix(&filter.pfx, *argv, filter.family); 366 if (filter.family == AF_UNSPEC) 367 filter.family = filter.pfx.family; 368 } 369 argc--; argv++; 370 } 371 372 ll_init_map(&rth); 373 374 if (filter_dev) { 375 if ((filter.index = ll_name_to_index(filter_dev)) == 0) { 376 fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev); 377 return -1; 378 } 379 } 380 381 if (flush) { 382 int round = 0; 383 char flushb[4096-512]; 384 385 filter.flushb = flushb; 386 filter.flushp = 0; 387 filter.flushe = sizeof(flushb); 388 filter.state &= ~NUD_FAILED; 389 390 while (round < MAX_ROUNDS) { 391 if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) { 392 perror("Cannot send dump request"); 393 exit(1); 394 } 395 filter.flushed = 0; 396 if (rtnl_dump_filter(&rth, print_neigh, stdout, NULL, NULL) < 0) { 397 fprintf(stderr, "Flush terminated\n"); 398 exit(1); 399 } 400 if (filter.flushed == 0) { 401 if (show_stats) { 402 if (round == 0) 403 printf("Nothing to flush.\n"); 404 else 405 printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":""); 406 } 407 fflush(stdout); 408 return 0; 409 } 410 round++; 411 if (flush_update() < 0) 412 exit(1); 413 if (show_stats) { 414 printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed); 415 fflush(stdout); 416 } 417 } 418 printf("*** Flush not complete bailing out after %d rounds\n", 419 MAX_ROUNDS); 420 return 1; 421 } 422 423 if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) { 424 perror("Cannot send dump request"); 425 exit(1); 426 } 427 428 if (rtnl_dump_filter(&rth, print_neigh, stdout, NULL, NULL) < 0) { 429 fprintf(stderr, "Dump terminated\n"); 430 exit(1); 431 } 432 433 return 0; 434 } 435 436 int do_ipneigh(int argc, char **argv) 437 { 438 if (argc > 0) { 439 if (matches(*argv, "add") == 0) 440 return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1); 441 if (matches(*argv, "change") == 0 || 442 strcmp(*argv, "chg") == 0) 443 return ipneigh_modify(RTM_NEWNEIGH, NLM_F_REPLACE, argc-1, argv+1); 444 if (matches(*argv, "replace") == 0) 445 return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1); 446 if (matches(*argv, "delete") == 0) 447 return ipneigh_modify(RTM_DELNEIGH, 0, argc-1, argv+1); 448 if (matches(*argv, "get") == 0) { 449 fprintf(stderr, "Sorry, \"neigh get\" is not implemented :-(\n"); 450 return -1; 451 } 452 if (matches(*argv, "show") == 0 || 453 matches(*argv, "lst") == 0 || 454 matches(*argv, "list") == 0) 455 return do_show_or_flush(argc-1, argv+1, 0); 456 if (matches(*argv, "flush") == 0) 457 return do_show_or_flush(argc-1, argv+1, 1); 458 if (matches(*argv, "help") == 0) 459 usage(); 460 } else 461 return do_show_or_flush(0, NULL, 0); 462 463 fprintf(stderr, "Command \"%s\" is unknown, try \"ip neigh help\".\n", *argv); 464 exit(-1); 465 } 466