xref: /original-bsd/usr.sbin/arp/arp.c (revision 4a884f8b)
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.16 (Berkeley) 03/08/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/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 <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 sz, needed, rlen;
322 	long op = KINFO_RT_FLAGS | (((long)AF_INET) << 16);
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 	if ((needed = getkerninfo(op, 0, 0, RTF_LLINFO)) < 0)
331 		quit("route-getkerninfo-estimate");
332 	if ((buf = malloc(needed)) == NULL)
333 		quit("malloc");
334 	if ((rlen = getkerninfo(op, buf, &needed, RTF_LLINFO)) < 0)
335 		quit("actual retrieval of routing table");
336 	lim = buf + rlen;
337 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
338 		rtm = (struct rt_msghdr *)next;
339 		sin = (struct sockaddr_inarp *)(rtm + 1);
340 		sdl = (struct sockaddr_dl *)(sin + 1);
341 		if (addr) {
342 			if (addr != sin->sin_addr.s_addr)
343 				continue;
344 			found_entry = 1;
345 		}
346 		if (nflag == 0)
347 			hp = gethostbyaddr((caddr_t)&(sin->sin_addr),
348 			    sizeof sin->sin_addr, AF_INET);
349 		else
350 			hp = 0;
351 		if (hp)
352 			host = hp->h_name;
353 		else {
354 			host = "?";
355 			if (h_errno == TRY_AGAIN)
356 				nflag = 1;
357 		}
358 		printf("%s (%s) at ", host, inet_ntoa(sin->sin_addr));
359 		if (sdl->sdl_alen)
360 			ether_print(LLADDR(sdl));
361 		else
362 			printf("(incomplete)");
363 		if (rtm->rtm_rmx.rmx_expire == 0)
364 			printf(" permanent");
365 		if (sin->sin_other & SIN_PROXY)
366 			printf(" published (proxy only)");
367 		if (rtm->rtm_addrs & RTA_NETMASK) {
368 			sin = (struct sockaddr_inarp *)
369 				(sdl->sdl_len + (char *)sdl);
370 			if (sin->sin_addr.s_addr == 0xffffffff)
371 				printf(" published");
372 			if (sin->sin_len != 8)
373 				printf("(wierd)");
374 		}
375 		printf("\n");
376 	}
377 }
378 
379 ether_print(cp)
380 	u_char *cp;
381 {
382 	printf("%x:%x:%x:%x:%x:%x", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
383 }
384 
385 ether_aton(a, n)
386 	char *a;
387 	u_char *n;
388 {
389 	int i, o[6];
390 
391 	i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
392 					   &o[3], &o[4], &o[5]);
393 	if (i != 6) {
394 		fprintf(stderr, "arp: invalid Ethernet address '%s'\n", a);
395 		return (1);
396 	}
397 	for (i=0; i<6; i++)
398 		n[i] = o[i];
399 	return (0);
400 }
401 
402 usage()
403 {
404 	printf("usage: arp hostname\n");
405 	printf("       arp -a [kernel] [kernel_memory]\n");
406 	printf("       arp -d hostname\n");
407 	printf("       arp -s hostname ether_addr [temp] [pub]\n");
408 	printf("       arp -f filename\n");
409 	exit(1);
410 }
411 
412 rtmsg(cmd)
413 {
414 	static int seq;
415 	int rlen;
416 	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
417 	register char *cp = m_rtmsg.m_space;
418 	register int l;
419 
420 	errno = 0;
421 	if (cmd == RTM_DELETE)
422 		goto doit;
423 	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
424 	rtm->rtm_flags = flags;
425 	rtm->rtm_version = RTM_VERSION;
426 
427 	switch (cmd) {
428 	default:
429 		fprintf(stderr, "arp: internal wrong cmd\n");
430 		exit(1);
431 	case RTM_ADD:
432 		rtm->rtm_addrs |= RTA_GATEWAY;
433 		rtm->rtm_rmx.rmx_expire = expire_time;
434 		rtm->rtm_inits = RTV_EXPIRE;
435 		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
436 		sin_m.sin_other = 0;
437 		if (doing_proxy) {
438 			if (export_only)
439 				sin_m.sin_other = SIN_PROXY;
440 			else {
441 				rtm->rtm_addrs |= RTA_NETMASK;
442 				rtm->rtm_flags &= ~RTF_HOST;
443 			}
444 		}
445 	case RTM_GET:
446 		rtm->rtm_addrs |= RTA_DST;
447 	}
448 #define NEXTADDR(w, s) \
449 	if (rtm->rtm_addrs & (w)) { \
450 		bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);}
451 
452 	NEXTADDR(RTA_DST, sin_m);
453 	NEXTADDR(RTA_GATEWAY, sdl_m);
454 	NEXTADDR(RTA_NETMASK, so_mask);
455 
456 	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
457 doit:
458 	l = rtm->rtm_msglen;
459 	rtm->rtm_seq = ++seq;
460 	rtm->rtm_type = cmd;
461 	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
462 		if (errno != ESRCH || cmd != RTM_DELETE) {
463 			perror("writing to routing socket");
464 			return (-1);
465 		}
466 	}
467 	do {
468 		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
469 	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
470 	if (l < 0)
471 		(void) fprintf(stderr, "arp: read from routing socket: %s\n",
472 		    strerror(errno));
473 	return (0);
474 }
475 
476 quit(msg)
477 char *msg;
478 {
479 	fprintf(stderr, "%s\n", msg);
480 	exit(1);
481 }
482