1/* 2 * Copyright (c) 2000-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * Copyright (c) 1984, 1993 25 * The Regents of the University of California. All rights reserved. 26 * 27 * This code is derived from software contributed to Berkeley by 28 * Sun Microsystems, Inc. 29 * 30 * Redistribution and use in source and binary forms, with or without 31 * modification, are permitted provided that the following conditions 32 * are met: 33 * 1. Redistributions of source code must retain the above copyright 34 * notice, this list of conditions and the following disclaimer. 35 * 2. Redistributions in binary form must reproduce the above copyright 36 * notice, this list of conditions and the following disclaimer in the 37 * documentation and/or other materials provided with the distribution. 38 * 3. All advertising materials mentioning features or use of this software 39 * must display the following acknowledgement: 40 * This product includes software developed by the University of 41 * California, Berkeley and its contributors. 42 * 4. Neither the name of the University nor the names of its contributors 43 * may be used to endorse or promote products derived from this software 44 * without specific prior written permission. 45 * 46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 56 * SUCH DAMAGE. 57 */ 58 59/* 60 * Modification History: 61 * 62 * 25 Feb 1998 Dieter Siegmund (dieter@apple.com) 63 * - significantly restructured to make it useful for inclusion 64 * in a library 65 * - define MAIN to generate the arp command 66 * - removed use of most global variables, abstracted 67 * out the ability to call arp_set() as a library 68 * routine, moved error messages to error codes 69 * 70 * 22 Sep 2000 Dieter Siegmund (dieter@apple.com) 71 * - added arp_flush() 72 * 73 * 30 Apr 2010 Dieter Siegmund (dieter@apple.com) 74 * - eliminated unnecessary functions/API 75 * - making remaining functions less susceptible to failures at the routing 76 * socket layer 77 */ 78#include <sys/param.h> 79#include <sys/file.h> 80#include <sys/socket.h> 81#include <sys/sysctl.h> 82#include <sys/ioctl.h> 83 84#include <net/if.h> 85#include <net/if_dl.h> 86#include <net/if_types.h> 87 88#include <netinet/in.h> 89#include <netinet/if_ether.h> 90 91#include <arpa/inet.h> 92 93#include <errno.h> 94#include <netdb.h> 95#include <nlist.h> 96#include <paths.h> 97#include <stdio.h> 98#include <stdlib.h> 99#include <string.h> 100#include <unistd.h> 101 102#include "util.h" 103#include "arp.h" 104 105#ifdef MAIN 106#include <err.h> 107static int delete __P((int, int, char * *)); 108static int dump __P((u_long)); 109static void ether_print __P((u_char *)); 110static int get __P((char *)); 111static void usage __P((void)); 112static void dump_entry(struct rt_msghdr * rtm); 113 114static int nflag; 115#else /* MAIN */ 116#define err(a, b) return (ARP_RETURN_FAILURE) 117#endif /* MAIN */ 118 119static const struct sockaddr_inarp blank_sin = {sizeof(blank_sin), AF_INET }; 120 121#ifdef MAIN 122 123static const char * arperrors[] = { 124 "success", 125 "failure", 126 "internal error", 127 "write to routing socket failed", 128 "read to routing socket failed", 129 "can't locate", 130 0 131}; 132 133const char * 134arp_strerror(int err) 135{ 136 if (err < ARP_RETURN_LAST && err >= ARP_RETURN_SUCCESS) 137 return (arperrors[err]); 138 return ("unknown error"); 139} 140 141typedef enum { 142 command_none_e = 0, 143 command_dump_e, 144 command_delete_e, 145 command_flush_e, 146} command_t; 147 148int 149main(argc, argv) 150 int argc; 151 char **argv; 152{ 153 int ch; 154 int s; 155 int aflag = 0; 156 command_t cmd = command_none_e; 157 int ret = 0; 158 159 while ((ch = getopt(argc, argv, "andF")) != EOF) { 160 switch((char)ch) { 161 case 'a': 162 aflag = 1; 163 break; 164 case 'd': 165 if (nflag || aflag 166 || cmd != command_none_e) { 167 usage(); 168 } 169 cmd = command_delete_e; 170 break; 171 case 'n': 172 nflag = 1; 173 break; 174 case 'F': 175 if (cmd != command_none_e) 176 usage(); 177 cmd = command_flush_e; 178 break; 179 case '?': 180 default: 181 usage(); 182 } 183 } 184 if (cmd == command_none_e) { 185 cmd = command_dump_e; 186 } 187 switch (cmd) { 188 case command_dump_e: 189 if (aflag) 190 dump(0); 191 else { 192 if ((argc - optind) != 1) 193 usage(); 194 ret = get(argv[optind]) ? 1 : 0; 195 } 196 break; 197 case command_delete_e: 198 if ((argc - optind) < 1 || (argc - optind) > 2) 199 usage(); 200 s = arp_open_routing_socket(); 201 ret = delete(s, argc - optind, &argv[optind]) ? 1 : 0; 202 break; 203 case command_flush_e: 204 s = arp_open_routing_socket(); 205 arp_flush(s, aflag, 0); 206 break; 207 default: 208 break; 209 } 210 exit(ret); 211 return (0); 212} 213 214/* 215 * Display an individual arp entry 216 */ 217int 218get(host) 219 char *host; 220{ 221 struct hostent *hp; 222 struct sockaddr_inarp sin_m; 223 struct sockaddr_inarp *sin = &sin_m; 224 route_msg msg; 225 226 sin_m = blank_sin; 227 sin->sin_addr.s_addr = inet_addr(host); 228 if (sin->sin_addr.s_addr == -1) { 229 if (!(hp = gethostbyname(host))) { 230 fprintf(stderr, "arp: %s: ", host); 231 herror((char *)NULL); 232 return (1); 233 } 234 bcopy((char *)hp->h_addr, (char *)&sin->sin_addr, 235 sizeof sin->sin_addr); 236 } 237 if (arp_get(arp_open_routing_socket(), &msg, sin->sin_addr, 0) 238 != ARP_RETURN_SUCCESS) { 239 printf("%s (%s) -- no entry\n", 240 host, inet_ntoa(sin->sin_addr)); 241 return (1); 242 } 243 else { 244 dump_entry(&msg.m_rtm); 245 } 246 return (0); 247} 248 249/* 250 * Delete an arp entry 251 */ 252int 253delete(int s, int argc, char * * argv) 254{ 255 char * host; 256 struct hostent *hp; 257 struct in_addr iaddr; 258 259 host = argv[0]; 260 iaddr.s_addr = inet_addr(host); 261 if (iaddr.s_addr == -1) { 262 if (!(hp = gethostbyname(host))) { 263 fprintf(stderr, "arp: %s: ", host); 264 herror((char *)NULL); 265 return (1); 266 } 267 bcopy((char *)hp->h_addr, (char *)&iaddr, 268 sizeof (iaddr)); 269 } 270 { 271 int ret; 272 273 errno = 0; 274 ret = arp_delete(s, iaddr, 0); 275 if (ret == ARP_RETURN_SUCCESS) { 276 printf("%s (%s) deleted\n", host, inet_ntoa(iaddr)); 277 return (0); 278 } 279 printf("delete: %s %s", arp_strerror(ret), host); 280 if (errno) 281 printf(": %s\n", strerror(errno)); 282 else 283 printf("\n"); 284 } 285 return (1); 286} 287 288/* 289 * Dump the entire arp table 290 */ 291 292static void 293dump_entry(struct rt_msghdr * rtm) 294{ 295 char * host; 296 struct hostent * hp; 297 struct sockaddr_inarp * sin; 298 struct sockaddr_dl * sdl; 299 300 sin = (struct sockaddr_inarp *)(rtm + 1); 301 sdl = (struct sockaddr_dl *)(sin + 1); 302 if (nflag == 0) { 303 hp = gethostbyaddr((caddr_t)&(sin->sin_addr), 304 sizeof(sin->sin_addr), AF_INET); 305 } 306 else { 307 hp = NULL; 308 } 309 310 if (hp != NULL) { 311 host = hp->h_name; 312 } 313 else { 314 host = "?"; 315 } 316 if (h_errno == TRY_AGAIN) { 317 nflag = 1; 318 } 319 printf("%s (%s) at ", host, inet_ntoa(sin->sin_addr)); 320 if (sdl->sdl_alen != 0) { 321 ether_print((u_char *)LLADDR(sdl)); 322 } 323 else { 324 printf("(incomplete)"); 325 } 326 if (rtm->rtm_rmx.rmx_expire == 0) { 327 printf(" permanent"); 328 } 329 if (sin->sin_other & SIN_PROXY) { 330 printf(" published (proxy only)"); 331 } 332 if (rtm->rtm_addrs & RTA_NETMASK) { 333 sin = (struct sockaddr_inarp *) 334 (sdl->sdl_len + (char *)sdl); 335 if (sin->sin_addr.s_addr == 0xffffffff) 336 printf(" published"); 337 if (sin->sin_len != 8) 338 printf("(weird)"); 339 } 340 printf("\n"); 341 return; 342} 343 344int 345dump(addr) 346 u_long addr; 347{ 348 int mib[6]; 349 size_t needed; 350 char *lim, *buf, *next; 351 struct rt_msghdr *rtm; 352 struct sockaddr_inarp *sin; 353 struct sockaddr_dl *sdl; 354 extern int h_errno; 355 int found_entry = 0; 356 357 mib[0] = CTL_NET; 358 mib[1] = PF_ROUTE; 359 mib[2] = 0; 360 mib[3] = AF_INET; 361 mib[4] = NET_RT_FLAGS; 362 mib[5] = RTF_LLINFO; 363 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 364 err(1, "route-sysctl-estimate"); 365 if ((buf = malloc(needed)) == NULL) 366 err(1, "malloc"); 367 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { 368 free(buf); 369 err(1, "actual retrieval of routing table"); 370 } 371 lim = buf + needed; 372 for (next = buf; next < lim; next += rtm->rtm_msglen) { 373 /* ALIGN: trust that the kernel has taken care of alignment */ 374 rtm = (struct rt_msghdr *)(void *)next; 375 sin = (struct sockaddr_inarp *)(void *)(rtm + 1); 376 sdl = (struct sockaddr_dl *)(void *)(sin + 1); 377 378 if (addr) { 379 if (addr != sin->sin_addr.s_addr) 380 continue; 381 found_entry = 1; 382 } 383 dump_entry(rtm); 384 } 385 return (found_entry); 386} 387 388void 389ether_print(cp) 390 u_char *cp; 391{ 392 printf("%02x:%02x:%02x:%02x:%02x:%02x", 393 cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); 394} 395 396void 397usage() 398{ 399 printf("usage: arp hostname\n"); 400 printf(" arp -a [-n]\n"); 401 printf(" arp -d hostname\n"); 402 printf(" arp -f filename\n"); 403 printf(" arp -F [-a]\n"); 404 exit(1); 405} 406 407#endif /* MAIN */ 408 409int 410arp_open_routing_socket(void) 411{ 412 int opt; 413 int s; 414 415 s = socket(AF_ROUTE, SOCK_RAW, AF_ROUTE); 416 if (s < 0) { 417#ifdef MAIN 418 perror("arp: socket"); 419 exit(1); 420#else /* MAIN */ 421 return (-1); 422#endif /* MAIN */ 423 } 424 opt = 1; 425 if (ioctl(s, FIONBIO, &opt) < 0) { 426#ifdef MAIN 427 perror("arp: FIONBIO"); 428 exit(1); 429#else /* MAIN */ 430 close(s); 431 return (-1); 432#endif /* MAIN */ 433 } 434 return (s); 435} 436 437int 438arp_get_next_seq(void) 439{ 440 static int rtm_seq; 441 442 return (++rtm_seq); 443} 444 445static int 446route_get(int s, route_msg * msg_p, struct in_addr iaddr, int if_index) 447{ 448 ssize_t n; 449 int pid = getpid(); 450 struct rt_msghdr * rtm = &(msg_p->m_rtm); 451 struct sockaddr_inarp * sin; 452 int rtm_seq; 453 454 bzero((char *)rtm, sizeof(*rtm)); 455 rtm->rtm_flags = RTF_LLINFO; 456 if (if_index != 0) { 457 rtm->rtm_index = if_index; 458 rtm->rtm_flags |= RTF_IFSCOPE; 459 } 460 rtm->rtm_version = RTM_VERSION; 461 rtm->rtm_addrs = RTA_DST; 462 sin = (struct sockaddr_inarp *)(rtm + 1); 463 *sin = blank_sin; 464 sin->sin_addr = iaddr; 465 n = rtm->rtm_msglen = sizeof(*rtm) + sizeof(*sin); 466 rtm->rtm_seq = rtm_seq = arp_get_next_seq(); 467 rtm->rtm_type = RTM_GET_SILENT; 468 if (write(s, (char *)msg_p, n) != n) { 469 return (ARP_RETURN_WRITE_FAILED); 470 } 471 errno = 0; 472 while (1) { 473 n = read(s, (char *)msg_p, sizeof(*msg_p)); 474 if (n <= 0) { 475 break; 476 } 477 if (rtm->rtm_type == RTM_GET 478 && rtm->rtm_seq == rtm_seq && rtm->rtm_pid == pid) { 479 return (ARP_RETURN_SUCCESS); 480 } 481 } 482 return (ARP_RETURN_READ_FAILED); 483} 484 485static __inline__ int 486is_arp_sdl_type(int sdl_type) 487{ 488 switch (sdl_type) { 489 case IFT_ETHER: 490 case IFT_FDDI: 491 case IFT_ISO88023: 492 case IFT_ISO88024: 493 case IFT_ISO88025: 494 case IFT_IEEE1394: 495 return (1); 496 } 497 return (0); 498} 499 500int 501arp_get(int s, route_msg * msg_p, struct in_addr iaddr, int if_index) 502{ 503 int ret; 504 struct rt_msghdr * rtm = &(msg_p->m_rtm); 505 struct sockaddr_inarp * sin; 506 struct sockaddr_dl * sdl; 507 508 ret = route_get(s, msg_p, iaddr, if_index); 509 if (ret) { 510 goto done; 511 } 512#define WHICH_RTA (RTA_DST | RTA_GATEWAY) 513 ret = ARP_RETURN_HOST_NOT_FOUND; 514 if ((rtm->rtm_addrs & (WHICH_RTA)) != WHICH_RTA 515 || (rtm->rtm_flags & RTF_LLINFO) == 0 516 || (rtm->rtm_flags & RTF_GATEWAY) != 0) { 517 goto done; 518 } 519 sin = (struct sockaddr_inarp *)msg_p->m_space; 520 if (sin->sin_addr.s_addr != iaddr.s_addr) { 521 goto done; 522 } 523 524 /* ALIGN: msg_p->m_space is aligned sufficiently to dereference 525 * sdl safely */ 526 sdl = (struct sockaddr_dl *)(void *)(sin->sin_len + (char *)sin); 527 if (sdl->sdl_family == AF_LINK && is_arp_sdl_type(sdl->sdl_type)) { 528 ret = ARP_RETURN_SUCCESS; 529 } 530 done: 531 return (ret); 532} 533 534/* 535 * Function: arp_delete 536 * 537 * Purpose: 538 * Delete the arp entry for the given host. 539 * Assumes: 540 * s is an open routing socket 541 */ 542int 543arp_delete(int s, struct in_addr iaddr, int if_index) 544{ 545 route_msg msg; 546 int ret; 547 struct rt_msghdr * rtm = &msg.m_rtm; 548 549 ret = arp_get(s, &msg, iaddr, if_index); 550 if (ret) { 551 goto done; 552 } 553 /* turn the RTM_GET into an RTM_DELETE */ 554 rtm->rtm_seq = arp_get_next_seq(); 555 rtm->rtm_type = RTM_DELETE; 556 if (write(s, (char *)rtm, rtm->rtm_msglen) < 0) { 557 ret = ARP_RETURN_FAILURE; 558 } 559 else { 560 ret = ARP_RETURN_SUCCESS; 561 } 562 done: 563 return (ret); 564} 565 566int 567arp_flush(int s, int all, int if_index) 568{ 569 char * buf; 570 char * lim; 571 int mib[6]; 572 size_t needed; 573 char * next; 574 struct rt_msghdr * rtm; 575 struct sockaddr_dl * sdl; 576 struct sockaddr_inarp * sin; 577 578 mib[0] = CTL_NET; 579 mib[1] = PF_ROUTE; 580 mib[2] = 0; 581 mib[3] = AF_INET; 582 mib[4] = NET_RT_FLAGS; 583 mib[5] = RTF_LLINFO; 584 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { 585 err(1, "route-sysctl-estimate"); 586 } 587 if ((buf = malloc(needed)) == NULL) { 588 err(1, "malloc"); 589 } 590 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { 591 free(buf); 592 err(1, "actual retrieval of routing table"); 593 } 594 lim = buf + needed; 595 for (next = buf; next < lim; next += rtm->rtm_msglen) { 596 /* ALIGN: trust that the kernel has taken care of alignment */ 597 rtm = (struct rt_msghdr *)(void *)next; 598 sin = (struct sockaddr_inarp *)(void *)(rtm + 1); 599 600 if (all == 0 && rtm->rtm_rmx.rmx_expire == 0) { 601 /* skip permanent entries */ 602 continue; 603 } 604 if (ip_is_linklocal(sin->sin_addr) 605 && (if_index != 0 && if_index != rtm->rtm_index)) { 606 /* IPv4 LL ARP entry doesn't match specified interface */ 607 continue; 608 } 609 610 /* ALIGN: sin aligned sufficiently to dereference sdl safely */ 611 sdl = (struct sockaddr_dl *)(void *)(sin->sin_len + (char *)sin); 612 if (sdl->sdl_family != AF_LINK) { 613 continue; 614 } 615 /* turn the RTM_GET into an RTM_DELETE */ 616 rtm->rtm_seq = arp_get_next_seq(); 617 rtm->rtm_type = RTM_DELETE; 618 if (write(s, (char *)rtm, rtm->rtm_msglen) < 0) { 619#ifdef MAIN 620 fprintf(stderr, "flush: delete %s failed, %s\n", 621 inet_ntoa(sin->sin_addr), strerror(errno)); 622#endif /* MAIN */ 623 } 624 } 625 free(buf); 626 return (0); 627} 628