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