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.12 (Berkeley) 06/25/91"; 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 doing_proxy = SIN_PROXY; 180 else if (strncmp(argv[0], "trail", 5) == 0) 181 flags = RTF_PROTO1; 182 argv++; 183 } 184 tryagain: 185 if (rtmsg(RTM_GET) < 0) { 186 perror(host); 187 return (1); 188 } 189 sin = (struct sockaddr_inarp *)(rtm + 1); 190 sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin); 191 if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { 192 if (sdl->sdl_family == AF_LINK && 193 (rtm->rtm_flags & RTF_LLINFO) && 194 !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) { 195 case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: 196 case IFT_ISO88024: case IFT_ISO88025: 197 goto overwrite; 198 } 199 if (doing_proxy == 0) { 200 printf("set: can only proxy for %s\n", host); 201 return (1); 202 } 203 if (sin_m.sin_other & SIN_PROXY) { 204 printf("set: proxy entry exists for non 802 device\n"); 205 return(1); 206 } 207 sin_m.sin_other = SIN_PROXY; 208 export_only = 1; 209 goto tryagain; 210 } 211 overwrite: 212 if (sdl->sdl_family != AF_LINK) { 213 printf("cannot intuit interface index and type for %s\n", host); 214 return (1); 215 } 216 sdl_m.sdl_type = sdl->sdl_type; 217 sdl_m.sdl_index = sdl->sdl_index; 218 return (rtmsg(RTM_ADD)); 219 } 220 221 /* 222 * Display an individual arp entry 223 */ 224 get(host) 225 char *host; 226 { 227 struct hostent *hp; 228 struct sockaddr_inarp *sin = &sin_m; 229 u_char *ea; 230 char *inet_ntoa(); 231 232 sin_m = blank_sin; 233 sin->sin_addr.s_addr = inet_addr(host); 234 if (sin->sin_addr.s_addr == -1) { 235 if (!(hp = gethostbyname(host))) { 236 fprintf(stderr, "arp: %s: ", host); 237 herror((char *)NULL); 238 exit(1); 239 } 240 bcopy((char *)hp->h_addr, (char *)&sin->sin_addr, 241 sizeof sin->sin_addr); 242 } 243 dump(sin->sin_addr.s_addr); 244 if (found_entry == 0) { 245 printf("%s (%s) -- no entry\n", 246 host, inet_ntoa(sin->sin_addr)); 247 exit(1); 248 } 249 } 250 251 /* 252 * Delete an arp entry 253 */ 254 delete(host, info) 255 char *host; 256 char *info; 257 { 258 struct hostent *hp; 259 register struct sockaddr_inarp *sin = &sin_m; 260 register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 261 struct sockaddr_dl *sdl; 262 u_char *ea; 263 char *eaddr; 264 265 if (info && strncmp(info, "pro", 3) ) 266 export_only = 1; 267 getsocket(); 268 sin_m = blank_sin; 269 sin->sin_addr.s_addr = inet_addr(host); 270 if (sin->sin_addr.s_addr == -1) { 271 if (!(hp = gethostbyname(host))) { 272 fprintf(stderr, "arp: %s: ", host); 273 herror((char *)NULL); 274 return (1); 275 } 276 bcopy((char *)hp->h_addr, (char *)&sin->sin_addr, 277 sizeof sin->sin_addr); 278 } 279 tryagain: 280 if (rtmsg(RTM_GET) < 0) { 281 perror(host); 282 return (1); 283 } 284 sin = (struct sockaddr_inarp *)(rtm + 1); 285 sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin); 286 if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { 287 if (sdl->sdl_family == AF_LINK && 288 (rtm->rtm_flags & RTF_LLINFO) && 289 !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) { 290 case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: 291 case IFT_ISO88024: case IFT_ISO88025: 292 goto delete; 293 } 294 } 295 if (sin_m.sin_other & SIN_PROXY) { 296 fprintf(stderr, "delete: can't locate %s\n",host); 297 return (1); 298 } else { 299 sin_m.sin_other = SIN_PROXY; 300 goto tryagain; 301 } 302 delete: 303 if (sdl->sdl_family != AF_LINK) { 304 printf("cannot locate %s\n", host); 305 return (1); 306 } 307 if (rtmsg(RTM_DELETE) == 0) 308 printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr)); 309 } 310 311 /* 312 * Dump the entire arp table 313 */ 314 dump(addr) 315 u_long addr; 316 { 317 int sz, needed, rlen; 318 long op = KINFO_RT_FLAGS | (((long)AF_INET) << 16); 319 char *host, *malloc(), *lim, *buf, *next; 320 struct rt_msghdr *rtm; 321 struct sockaddr_inarp *sin; 322 struct sockaddr_dl *sdl; 323 extern int h_errno; 324 struct hostent *hp; 325 326 if ((needed = getkerninfo(op, 0, 0, RTF_LLINFO)) < 0) 327 quit("route-getkerninfo-estimate"); 328 if ((buf = malloc(needed)) == NULL) 329 quit("malloc"); 330 if ((rlen = getkerninfo(op, buf, &needed, RTF_LLINFO)) < 0) 331 quit("actual retrieval of routing table"); 332 lim = buf + rlen; 333 for (next = buf; next < lim; next += rtm->rtm_msglen) { 334 rtm = (struct rt_msghdr *)next; 335 sin = (struct sockaddr_inarp *)(rtm + 1); 336 sdl = (struct sockaddr_dl *)(sin + 1); 337 if (addr) { 338 if (addr != sin->sin_addr.s_addr) 339 continue; 340 found_entry = 1; 341 } 342 if (nflag == 0) 343 hp = gethostbyaddr((caddr_t)&(sin->sin_addr), 344 sizeof sin->sin_addr, AF_INET); 345 else 346 hp = 0; 347 if (hp) 348 host = hp->h_name; 349 else { 350 host = "?"; 351 if (h_errno == TRY_AGAIN) 352 nflag = 1; 353 } 354 printf("%s (%s) at ", host, inet_ntoa(sin->sin_addr)); 355 if (sdl->sdl_alen) 356 ether_print(LLADDR(sdl)); 357 else 358 printf("(incomplete)"); 359 if (rtm->rtm_rmx.rmx_expire == 0) 360 printf(" permanent"); 361 if (sin->sin_other & SIN_PROXY) 362 printf(" published (proxy only)"); 363 if (rtm->rtm_flags & RTF_PROTO1) 364 printf(" trailers"); 365 if (rtm->rtm_addrs & RTA_NETMASK) { 366 sin = (struct sockaddr_inarp *) 367 (sdl->sdl_len + (char *)sdl); 368 if (sin->sin_addr.s_addr == 0xffffffff) 369 printf(" published"); 370 if (sin->sin_len != 8) 371 printf("(wierd)"); 372 } 373 printf("\n"); 374 } 375 } 376 377 ether_print(cp) 378 u_char *cp; 379 { 380 printf("%x:%x:%x:%x:%x:%x", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); 381 } 382 383 ether_aton(a, n) 384 char *a; 385 u_char *n; 386 { 387 int i, o[6]; 388 389 i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2], 390 &o[3], &o[4], &o[5]); 391 if (i != 6) { 392 fprintf(stderr, "arp: invalid Ethernet address '%s'\n", a); 393 return (1); 394 } 395 for (i=0; i<6; i++) 396 n[i] = o[i]; 397 return (0); 398 } 399 400 usage() 401 { 402 printf("usage: arp hostname\n"); 403 printf(" arp -a [kernel] [kernel_memory]\n"); 404 printf(" arp -d hostname\n"); 405 printf(" arp -s hostname ether_addr [temp] [pub] [trail]\n"); 406 printf(" arp -f filename\n"); 407 exit(1); 408 } 409 410 rtmsg(cmd) 411 { 412 static int seq; 413 int rlen; 414 register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; 415 register char *cp = m_rtmsg.m_space; 416 register int l; 417 418 errno = 0; 419 if (cmd == RTM_DELETE) 420 goto doit; 421 bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); 422 rtm->rtm_flags = flags; 423 rtm->rtm_version = RTM_VERSION; 424 425 switch (cmd) { 426 default: 427 fprintf(stderr, "arp: internal wrong cmd\n"); 428 exit(1); 429 case RTM_ADD: 430 rtm->rtm_addrs |= RTA_GATEWAY; 431 rtm->rtm_rmx.rmx_expire = expire_time; 432 rtm->rtm_inits = RTV_EXPIRE; 433 rtm->rtm_flags |= RTF_HOST; 434 sin_m.sin_other = 0; 435 if (doing_proxy) { 436 if (export_only) 437 sin_m.sin_other = SIN_PROXY; 438 else { 439 rtm->rtm_addrs |= RTA_NETMASK; 440 rtm->rtm_flags &= ~RTF_HOST; 441 } 442 } 443 case RTM_GET: 444 rtm->rtm_addrs |= RTA_DST; 445 } 446 #define NEXTADDR(w, s) \ 447 if (rtm->rtm_addrs & (w)) { \ 448 bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);} 449 450 NEXTADDR(RTA_DST, sin_m); 451 NEXTADDR(RTA_GATEWAY, sdl_m); 452 NEXTADDR(RTA_NETMASK, so_mask); 453 454 rtm->rtm_msglen = cp - (char *)&m_rtmsg; 455 doit: 456 l = rtm->rtm_msglen; 457 rtm->rtm_seq = ++seq; 458 rtm->rtm_type = cmd; 459 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { 460 if (errno != ESRCH || cmd != RTM_DELETE) { 461 perror("writing to routing socket"); 462 return (-1); 463 } 464 } 465 do { 466 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); 467 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); 468 if (l < 0) 469 (void) fprintf(stderr, "arp: read from routing socket: %s\n", 470 strerror(errno)); 471 return (0); 472 } 473 474 quit(msg) 475 char *msg; 476 { 477 fprintf(stderr, "%s\n", msg); 478 exit(1); 479 } 480