1 /* 2 * Copyright (c) 1984 Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Sun Microsystems, Inc. 7 * 8 * %sccs.include.redist.c% 9 */ 10 11 #ifndef lint 12 char copyright[] = 13 "@(#) Copyright (c) 1984 Regents of the University of California.\n\ 14 All rights reserved.\n"; 15 #endif /* not lint */ 16 17 #ifndef lint 18 static char sccsid[] = "@(#)arp.c 5.15 (Berkeley) 07/30/92"; 19 #endif /* not lint */ 20 21 /* 22 * arp - display, set, and delete arp table entries 23 */ 24 25 26 #include <sys/param.h> 27 #include <sys/file.h> 28 #include <sys/socket.h> 29 #include <sys/kinfo.h> 30 31 #include <net/if.h> 32 #include <net/if_dl.h> 33 #include <net/if_types.h> 34 #include <net/route.h> 35 36 #include <netinet/in.h> 37 #include <netinet/if_ether.h> 38 39 #include <netdb.h> 40 #include <errno.h> 41 #include <nlist.h> 42 #include <stdio.h> 43 #include <paths.h> 44 45 extern int errno; 46 static int pid; 47 static int kflag; 48 static int nflag; 49 static int s = -1; 50 51 main(argc, argv) 52 int argc; 53 char **argv; 54 { 55 int ch; 56 57 pid = getpid(); 58 while ((ch = getopt(argc, argv, "ands")) != EOF) 59 switch((char)ch) { 60 case 'a': 61 dump(0); 62 exit(0); 63 case 'd': 64 if (argc < 3 || argc > 4) 65 usage(); 66 delete(argv[2], argv[3]); 67 exit(0); 68 case 'n': 69 nflag = 1; 70 continue; 71 case 's': 72 if (argc < 4 || argc > 7) 73 usage(); 74 exit(set(argc-2, &argv[2]) ? 1 : 0); 75 case '?': 76 default: 77 usage(); 78 } 79 if (argc != 2) 80 usage(); 81 get(argv[1]); 82 exit(0); 83 } 84 85 /* 86 * Process a file to set standard arp entries 87 */ 88 file(name) 89 char *name; 90 { 91 FILE *fp; 92 int i, retval; 93 char line[100], arg[5][50], *args[5]; 94 95 if ((fp = fopen(name, "r")) == NULL) { 96 fprintf(stderr, "arp: cannot open %s\n", name); 97 exit(1); 98 } 99 args[0] = &arg[0][0]; 100 args[1] = &arg[1][0]; 101 args[2] = &arg[2][0]; 102 args[3] = &arg[3][0]; 103 args[4] = &arg[4][0]; 104 retval = 0; 105 while(fgets(line, 100, fp) != NULL) { 106 i = sscanf(line, "%s %s %s %s %s", arg[0], arg[1], arg[2], 107 arg[3], arg[4]); 108 if (i < 2) { 109 fprintf(stderr, "arp: bad line: %s\n", line); 110 retval = 1; 111 continue; 112 } 113 if (set(i, args)) 114 retval = 1; 115 } 116 fclose(fp); 117 return (retval); 118 } 119 120 getsocket() { 121 if (s < 0) { 122 s = socket(PF_ROUTE, SOCK_RAW, 0); 123 if (s < 0) { 124 perror("arp: socket"); 125 exit(1); 126 } 127 } 128 } 129 130 struct sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}}; 131 struct sockaddr_inarp blank_sin = {sizeof(blank_sin), AF_INET }, sin_m; 132 struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m; 133 int expire_time, flags, export_only, doing_proxy, found_entry; 134 struct { 135 struct rt_msghdr m_rtm; 136 char m_space[512]; 137 } m_rtmsg; 138 139 /* 140 * Set an individual arp entry 141 */ 142 set(argc, argv) 143 int argc; 144 char **argv; 145 { 146 struct hostent *hp; 147 register struct sockaddr_inarp *sin = &sin_m; 148 register struct sockaddr_dl *sdl; 149 register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); 150 u_char *ea; 151 char *host = argv[0], *eaddr = argv[1]; 152 153 getsocket(); 154 argc -= 2; 155 argv += 2; 156 sdl_m = blank_sdl; 157 sin_m = blank_sin; 158 sin->sin_addr.s_addr = inet_addr(host); 159 if (sin->sin_addr.s_addr == -1) { 160 if (!(hp = gethostbyname(host))) { 161 fprintf(stderr, "arp: %s: ", host); 162 herror((char *)NULL); 163 return (1); 164 } 165 bcopy((char *)hp->h_addr, (char *)&sin->sin_addr, 166 sizeof sin->sin_addr); 167 } 168 ea = (u_char *)LLADDR(&sdl_m); 169 if (ether_aton(eaddr, ea) == 0) 170 sdl_m.sdl_alen = 6; 171 doing_proxy = flags = export_only = expire_time = 0; 172 while (argc-- > 0) { 173 if (strncmp(argv[0], "temp", 4) == 0) { 174 struct timeval time; 175 gettimeofday(&time, 0); 176 expire_time = time.tv_sec + 20 * 60; 177 } 178 else if (strncmp(argv[0], "pub", 3) == 0) { 179 flags |= RTF_ANNOUNCE; 180 doing_proxy = SIN_PROXY; 181 } else if (strncmp(argv[0], "trail", 5) == 0) { 182 printf("%s: Sending trailers is no longer supported\n", 183 host); 184 } 185 argv++; 186 } 187 tryagain: 188 if (rtmsg(RTM_GET) < 0) { 189 perror(host); 190 return (1); 191 } 192 sin = (struct sockaddr_inarp *)(rtm + 1); 193 sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin); 194 if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { 195 if (sdl->sdl_family == AF_LINK && 196 (rtm->rtm_flags & RTF_LLINFO) && 197 !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) { 198 case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: 199 case IFT_ISO88024: case IFT_ISO88025: 200 goto overwrite; 201 } 202 if (doing_proxy == 0) { 203 printf("set: can only proxy for %s\n", host); 204 return (1); 205 } 206 if (sin_m.sin_other & SIN_PROXY) { 207 printf("set: proxy entry exists for non 802 device\n"); 208 return(1); 209 } 210 sin_m.sin_other = SIN_PROXY; 211 export_only = 1; 212 goto tryagain; 213 } 214 overwrite: 215 if (sdl->sdl_family != AF_LINK) { 216 printf("cannot intuit interface index and type for %s\n", host); 217 return (1); 218 } 219 sdl_m.sdl_type = sdl->sdl_type; 220 sdl_m.sdl_index = sdl->sdl_index; 221 return (rtmsg(RTM_ADD)); 222 } 223 224 /* 225 * Display an individual arp entry 226 */ 227 get(host) 228 char *host; 229 { 230 struct hostent *hp; 231 struct sockaddr_inarp *sin = &sin_m; 232 u_char *ea; 233 char *inet_ntoa(); 234 235 sin_m = blank_sin; 236 sin->sin_addr.s_addr = inet_addr(host); 237 if (sin->sin_addr.s_addr == -1) { 238 if (!(hp = gethostbyname(host))) { 239 fprintf(stderr, "arp: %s: ", host); 240 herror((char *)NULL); 241 exit(1); 242 } 243 bcopy((char *)hp->h_addr, (char *)&sin->sin_addr, 244 sizeof sin->sin_addr); 245 } 246 dump(sin->sin_addr.s_addr); 247 if (found_entry == 0) { 248 printf("%s (%s) -- no entry\n", 249 host, inet_ntoa(sin->sin_addr)); 250 exit(1); 251 } 252 } 253 254 /* 255 * Delete an arp entry 256 */ 257 delete(host, info) 258 char *host; 259 char *info; 260 { 261 struct hostent *hp; 262 register struct sockaddr_inarp *sin = &sin_m; 263 register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 264 struct sockaddr_dl *sdl; 265 u_char *ea; 266 char *eaddr; 267 268 if (info && strncmp(info, "pro", 3) ) 269 export_only = 1; 270 getsocket(); 271 sin_m = blank_sin; 272 sin->sin_addr.s_addr = inet_addr(host); 273 if (sin->sin_addr.s_addr == -1) { 274 if (!(hp = gethostbyname(host))) { 275 fprintf(stderr, "arp: %s: ", host); 276 herror((char *)NULL); 277 return (1); 278 } 279 bcopy((char *)hp->h_addr, (char *)&sin->sin_addr, 280 sizeof sin->sin_addr); 281 } 282 tryagain: 283 if (rtmsg(RTM_GET) < 0) { 284 perror(host); 285 return (1); 286 } 287 sin = (struct sockaddr_inarp *)(rtm + 1); 288 sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin); 289 if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { 290 if (sdl->sdl_family == AF_LINK && 291 (rtm->rtm_flags & RTF_LLINFO) && 292 !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) { 293 case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: 294 case IFT_ISO88024: case IFT_ISO88025: 295 goto delete; 296 } 297 } 298 if (sin_m.sin_other & SIN_PROXY) { 299 fprintf(stderr, "delete: can't locate %s\n",host); 300 return (1); 301 } else { 302 sin_m.sin_other = SIN_PROXY; 303 goto tryagain; 304 } 305 delete: 306 if (sdl->sdl_family != AF_LINK) { 307 printf("cannot locate %s\n", host); 308 return (1); 309 } 310 if (rtmsg(RTM_DELETE) == 0) 311 printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr)); 312 } 313 314 /* 315 * Dump the entire arp table 316 */ 317 dump(addr) 318 u_long addr; 319 { 320 int sz, needed, rlen; 321 long op = KINFO_RT_FLAGS | (((long)AF_INET) << 16); 322 char *host, *malloc(), *lim, *buf, *next; 323 struct rt_msghdr *rtm; 324 struct sockaddr_inarp *sin; 325 struct sockaddr_dl *sdl; 326 extern int h_errno; 327 struct hostent *hp; 328 329 if ((needed = getkerninfo(op, 0, 0, RTF_LLINFO)) < 0) 330 quit("route-getkerninfo-estimate"); 331 if ((buf = malloc(needed)) == NULL) 332 quit("malloc"); 333 if ((rlen = getkerninfo(op, buf, &needed, RTF_LLINFO)) < 0) 334 quit("actual retrieval of routing table"); 335 lim = buf + rlen; 336 for (next = buf; next < lim; next += rtm->rtm_msglen) { 337 rtm = (struct rt_msghdr *)next; 338 sin = (struct sockaddr_inarp *)(rtm + 1); 339 sdl = (struct sockaddr_dl *)(sin + 1); 340 if (addr) { 341 if (addr != sin->sin_addr.s_addr) 342 continue; 343 found_entry = 1; 344 } 345 if (nflag == 0) 346 hp = gethostbyaddr((caddr_t)&(sin->sin_addr), 347 sizeof sin->sin_addr, AF_INET); 348 else 349 hp = 0; 350 if (hp) 351 host = hp->h_name; 352 else { 353 host = "?"; 354 if (h_errno == TRY_AGAIN) 355 nflag = 1; 356 } 357 printf("%s (%s) at ", host, inet_ntoa(sin->sin_addr)); 358 if (sdl->sdl_alen) 359 ether_print(LLADDR(sdl)); 360 else 361 printf("(incomplete)"); 362 if (rtm->rtm_rmx.rmx_expire == 0) 363 printf(" permanent"); 364 if (sin->sin_other & SIN_PROXY) 365 printf(" published (proxy only)"); 366 if (rtm->rtm_addrs & RTA_NETMASK) { 367 sin = (struct sockaddr_inarp *) 368 (sdl->sdl_len + (char *)sdl); 369 if (sin->sin_addr.s_addr == 0xffffffff) 370 printf(" published"); 371 if (sin->sin_len != 8) 372 printf("(wierd)"); 373 } 374 printf("\n"); 375 } 376 } 377 378 ether_print(cp) 379 u_char *cp; 380 { 381 printf("%x:%x:%x:%x:%x:%x", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); 382 } 383 384 ether_aton(a, n) 385 char *a; 386 u_char *n; 387 { 388 int i, o[6]; 389 390 i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2], 391 &o[3], &o[4], &o[5]); 392 if (i != 6) { 393 fprintf(stderr, "arp: invalid Ethernet address '%s'\n", a); 394 return (1); 395 } 396 for (i=0; i<6; i++) 397 n[i] = o[i]; 398 return (0); 399 } 400 401 usage() 402 { 403 printf("usage: arp hostname\n"); 404 printf(" arp -a [kernel] [kernel_memory]\n"); 405 printf(" arp -d hostname\n"); 406 printf(" arp -s hostname ether_addr [temp] [pub]\n"); 407 printf(" arp -f filename\n"); 408 exit(1); 409 } 410 411 rtmsg(cmd) 412 { 413 static int seq; 414 int rlen; 415 register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 416 register char *cp = m_rtmsg.m_space; 417 register int l; 418 419 errno = 0; 420 if (cmd == RTM_DELETE) 421 goto doit; 422 bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 423 rtm->rtm_flags = flags; 424 rtm->rtm_version = RTM_VERSION; 425 426 switch (cmd) { 427 default: 428 fprintf(stderr, "arp: internal wrong cmd\n"); 429 exit(1); 430 case RTM_ADD: 431 rtm->rtm_addrs |= RTA_GATEWAY; 432 rtm->rtm_rmx.rmx_expire = expire_time; 433 rtm->rtm_inits = RTV_EXPIRE; 434 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); 435 sin_m.sin_other = 0; 436 if (doing_proxy) { 437 if (export_only) 438 sin_m.sin_other = SIN_PROXY; 439 else { 440 rtm->rtm_addrs |= RTA_NETMASK; 441 rtm->rtm_flags &= ~RTF_HOST; 442 } 443 } 444 case RTM_GET: 445 rtm->rtm_addrs |= RTA_DST; 446 } 447 #define NEXTADDR(w, s) \ 448 if (rtm->rtm_addrs & (w)) { \ 449 bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);} 450 451 NEXTADDR(RTA_DST, sin_m); 452 NEXTADDR(RTA_GATEWAY, sdl_m); 453 NEXTADDR(RTA_NETMASK, so_mask); 454 455 rtm->rtm_msglen = cp - (char *)&m_rtmsg; 456 doit: 457 l = rtm->rtm_msglen; 458 rtm->rtm_seq = ++seq; 459 rtm->rtm_type = cmd; 460 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 461 if (errno != ESRCH || cmd != RTM_DELETE) { 462 perror("writing to routing socket"); 463 return (-1); 464 } 465 } 466 do { 467 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 468 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 469 if (l < 0) 470 (void) fprintf(stderr, "arp: read from routing socket: %s\n", 471 strerror(errno)); 472 return (0); 473 } 474 475 quit(msg) 476 char *msg; 477 { 478 fprintf(stderr, "%s\n", msg); 479 exit(1); 480 } 481