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