arp.c revision 31145
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 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38static char const copyright[] =
39"@(#) Copyright (c) 1984, 1993\n\
40	The Regents of the University of California.  All rights reserved.\n";
41#endif /* not lint */
42
43#ifndef lint
44#if 0
45static char const sccsid[] = "@(#)from: arp.c	8.2 (Berkeley) 1/2/94";
46#endif
47static const char rcsid[] =
48	"$Id: arp.c,v 1.12 1997/09/03 06:32:31 charnier Exp $";
49#endif /* not lint */
50
51/*
52 * arp - display, set, and delete arp table entries
53 */
54
55
56#include <sys/param.h>
57#include <sys/file.h>
58#include <sys/socket.h>
59#include <sys/sockio.h>
60#include <sys/sysctl.h>
61#include <sys/ioctl.h>
62#include <sys/time.h>
63
64#include <net/if.h>
65#include <net/if_var.h>
66#include <net/if_dl.h>
67#include <net/if_types.h>
68#include <net/route.h>
69
70#include <netinet/in.h>
71#include <netinet/if_ether.h>
72
73#include <arpa/inet.h>
74
75#include <err.h>
76#include <errno.h>
77#include <netdb.h>
78#include <nlist.h>
79#include <paths.h>
80#include <stdio.h>
81#include <stdlib.h>
82#include <strings.h>
83#include <unistd.h>
84
85void search(u_long addr, void (*action)(struct sockaddr_dl *sdl,
86	struct sockaddr_inarp *sin, struct rt_msghdr *rtm));
87void print_entry(struct sockaddr_dl *sdl,
88	struct sockaddr_inarp *sin, struct rt_msghdr *rtm);
89void nuke_entry(struct sockaddr_dl *sdl,
90	struct sockaddr_inarp *sin, struct rt_msghdr *rtm);
91int delete(char *host, char *info);
92void ether_print(u_char *cp);
93void usage(void);
94int set(int argc, char **argv);
95int get(char *host);
96int file(char *name);
97void getsocket(void);
98int my_ether_aton(char *a, u_char *n);
99int rtmsg(int cmd);
100int get_ether_addr(u_long ipaddr, u_char *hwaddr);
101
102static int pid;
103static int nflag;	/* no reverse dns lookups */
104static int aflag;	/* do it for all entries */
105static int s = -1;
106
107/* which function we're supposed to do */
108#define F_GET		1
109#define F_SET		2
110#define F_FILESET	3
111#define F_REPLACE	4
112#define F_DELETE	5
113
114#define SETFUNC(f)	{ if (func) usage(); func = (f); }
115
116int
117main(argc, argv)
118	int argc;
119	char **argv;
120{
121	int ch, func = 0;
122	int rtn = 0;
123
124	pid = getpid();
125	while ((ch = getopt(argc, argv, "andfsS")) != -1)
126		switch((char)ch) {
127		case 'a':
128			aflag = 1;
129			break;
130		case 'd':
131			SETFUNC(F_DELETE);
132			break;
133		case 'n':
134			nflag = 1;
135			break;
136		case 'S':
137			SETFUNC(F_REPLACE);
138			break;
139		case 's':
140			SETFUNC(F_SET);
141			break;
142		case 'f' :
143			SETFUNC(F_FILESET);
144			break;
145		case '?':
146		default:
147			usage();
148		}
149	argc -= optind;
150	argv += optind;
151
152	if (!func)
153		func = F_GET;
154	switch (func) {
155	case F_GET:
156		if (aflag) {
157			if (argc != 0)
158				usage();
159			search(0, print_entry);
160		} else {
161			if (argc != 1)
162				usage();
163			get(argv[0]);
164		}
165		break;
166	case F_SET:
167	case F_REPLACE:
168		if (argc < 2 || argc > 5)
169			usage();
170		if (func == F_REPLACE)
171			(void) delete(argv[0], NULL);
172		rtn = set(argc, argv) ? 1 : 0;
173		break;
174	case F_DELETE:
175		if (aflag) {
176			if (argc != 0)
177				usage();
178			search(0, nuke_entry);
179		} else {
180			if (argc < 1 || argc > 2)
181				usage();
182			rtn = delete(argv[0], argv[1]);
183		}
184		break;
185	case F_FILESET:
186		if (argc != 1)
187			usage();
188		rtn = file(argv[0]);
189		break;
190	}
191
192	return(rtn);
193}
194
195/*
196 * Process a file to set standard arp entries
197 */
198int
199file(char *name)
200{
201	FILE *fp;
202	int i, retval;
203	char line[100], arg[5][50], *args[5];
204
205	if ((fp = fopen(name, "r")) == NULL)
206		errx(1, "cannot open %s", name);
207	args[0] = &arg[0][0];
208	args[1] = &arg[1][0];
209	args[2] = &arg[2][0];
210	args[3] = &arg[3][0];
211	args[4] = &arg[4][0];
212	retval = 0;
213	while(fgets(line, 100, fp) != NULL) {
214		i = sscanf(line, "%s %s %s %s %s", arg[0], arg[1], arg[2],
215		    arg[3], arg[4]);
216		if (i < 2) {
217			warnx("bad line: %s", line);
218			retval = 1;
219			continue;
220		}
221		if (set(i, args))
222			retval = 1;
223	}
224	fclose(fp);
225	return (retval);
226}
227
228void
229getsocket(void)
230{
231	if (s < 0) {
232		s = socket(PF_ROUTE, SOCK_RAW, 0);
233		if (s < 0)
234			err(1, "socket");
235	}
236}
237
238struct	sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}};
239struct	sockaddr_inarp blank_sin = {sizeof(blank_sin), AF_INET }, sin_m;
240struct	sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
241int	expire_time, flags, export_only, doing_proxy, found_entry;
242struct	{
243	struct	rt_msghdr m_rtm;
244	char	m_space[512];
245}	m_rtmsg;
246
247/*
248 * Set an individual arp entry
249 */
250int
251set(int argc, char **argv)
252{
253	struct hostent *hp;
254	register struct sockaddr_inarp *sin = &sin_m;
255	register struct sockaddr_dl *sdl;
256	register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
257	u_char *ea;
258	char *host = argv[0], *eaddr = argv[1];
259
260	getsocket();
261	argc -= 2;
262	argv += 2;
263	sdl_m = blank_sdl;
264	sin_m = blank_sin;
265	sin->sin_addr.s_addr = inet_addr(host);
266	if (sin->sin_addr.s_addr == -1) {
267		if (!(hp = gethostbyname(host))) {
268			warnx("%s: %s", host, hstrerror(h_errno));
269			return (1);
270		}
271		bcopy((char *)hp->h_addr, (char *)&sin->sin_addr,
272		    sizeof sin->sin_addr);
273	}
274	doing_proxy = flags = export_only = expire_time = 0;
275	while (argc-- > 0) {
276		if (strncmp(argv[0], "temp", 4) == 0) {
277			struct timeval time;
278			gettimeofday(&time, 0);
279			expire_time = time.tv_sec + 20 * 60;
280		}
281		else if (strncmp(argv[0], "pub", 3) == 0) {
282			flags |= RTF_ANNOUNCE;
283			doing_proxy = SIN_PROXY;
284		} else if (strncmp(argv[0], "trail", 5) == 0) {
285			printf("%s: Sending trailers is no longer supported\n",
286				host);
287		}
288		argv++;
289	}
290	ea = (u_char *)LLADDR(&sdl_m);
291	if (doing_proxy && !strcmp(eaddr, "auto")) {
292		if (!get_ether_addr(sin->sin_addr.s_addr, ea)) {
293			return (1);
294		}
295		sdl_m.sdl_alen = 6;
296	} else {
297		if (my_ether_aton(eaddr, ea) == 0)
298			sdl_m.sdl_alen = 6;
299	}
300tryagain:
301	if (rtmsg(RTM_GET) < 0) {
302		warn("%s", host);
303		return (1);
304	}
305	sin = (struct sockaddr_inarp *)(rtm + 1);
306	sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin);
307	if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
308		if (sdl->sdl_family == AF_LINK &&
309		    (rtm->rtm_flags & RTF_LLINFO) &&
310		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
311		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
312		case IFT_ISO88024: case IFT_ISO88025:
313			goto overwrite;
314		}
315		if (doing_proxy == 0) {
316			printf("set: can only proxy for %s\n", host);
317			return (1);
318		}
319		if (sin_m.sin_other & SIN_PROXY) {
320			printf("set: proxy entry exists for non 802 device\n");
321			return(1);
322		}
323		sin_m.sin_other = SIN_PROXY;
324		export_only = 1;
325		goto tryagain;
326	}
327overwrite:
328	if (sdl->sdl_family != AF_LINK) {
329		printf("cannot intuit interface index and type for %s\n", host);
330		return (1);
331	}
332	sdl_m.sdl_type = sdl->sdl_type;
333	sdl_m.sdl_index = sdl->sdl_index;
334	return (rtmsg(RTM_ADD));
335}
336
337/*
338 * Display an individual arp entry
339 */
340int
341get(char *host)
342{
343	struct hostent *hp;
344	struct sockaddr_inarp *sin = &sin_m;
345
346	sin_m = blank_sin;
347	sin->sin_addr.s_addr = inet_addr(host);
348	if (sin->sin_addr.s_addr == -1) {
349		if (!(hp = gethostbyname(host)))
350			errx(1, "%s: %s", host, hstrerror(h_errno));
351		bcopy((char *)hp->h_addr, (char *)&sin->sin_addr,
352		    sizeof sin->sin_addr);
353	}
354	search(sin->sin_addr.s_addr, print_entry);
355	if (found_entry == 0) {
356		printf("%s (%s) -- no entry\n",
357		    host, inet_ntoa(sin->sin_addr));
358		return(1);
359	}
360	return(0);
361}
362
363/*
364 * Delete an arp entry
365 */
366int
367delete(char *host, char *info)
368{
369	struct hostent *hp;
370	register struct sockaddr_inarp *sin = &sin_m;
371	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
372	struct sockaddr_dl *sdl;
373
374	if (info && strncmp(info, "pro", 3) )
375		export_only = 1;
376	getsocket();
377	sin_m = blank_sin;
378	sin->sin_addr.s_addr = inet_addr(host);
379	if (sin->sin_addr.s_addr == -1) {
380		if (!(hp = gethostbyname(host))) {
381			warnx("%s: %s", host, hstrerror(h_errno));
382			return (1);
383		}
384		bcopy((char *)hp->h_addr, (char *)&sin->sin_addr,
385		    sizeof sin->sin_addr);
386	}
387tryagain:
388	if (rtmsg(RTM_GET) < 0) {
389		warn("%s", host);
390		return (1);
391	}
392	sin = (struct sockaddr_inarp *)(rtm + 1);
393	sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin);
394	if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
395		if (sdl->sdl_family == AF_LINK &&
396		    (rtm->rtm_flags & RTF_LLINFO) &&
397		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
398		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
399		case IFT_ISO88024: case IFT_ISO88025:
400			goto delete;
401		}
402	}
403	if (sin_m.sin_other & SIN_PROXY) {
404		fprintf(stderr, "delete: can't locate %s\n",host);
405		return (1);
406	} else {
407		sin_m.sin_other = SIN_PROXY;
408		goto tryagain;
409	}
410delete:
411	if (sdl->sdl_family != AF_LINK) {
412		printf("cannot locate %s\n", host);
413		return (1);
414	}
415	if (rtmsg(RTM_DELETE) == 0) {
416		printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr));
417		return (0);
418	}
419	return (1);
420}
421
422/*
423 * Search the arp table and do some action on matching entries
424 */
425void
426search(u_long addr, void (*action)(struct sockaddr_dl *sdl,
427	struct sockaddr_inarp *sin, struct rt_msghdr *rtm))
428{
429	int mib[6];
430	size_t needed;
431	char *lim, *buf, *next;
432	struct rt_msghdr *rtm;
433	struct sockaddr_inarp *sin;
434	struct sockaddr_dl *sdl;
435	extern int h_errno;
436
437	mib[0] = CTL_NET;
438	mib[1] = PF_ROUTE;
439	mib[2] = 0;
440	mib[3] = AF_INET;
441	mib[4] = NET_RT_FLAGS;
442	mib[5] = RTF_LLINFO;
443	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
444		errx(1, "route-sysctl-estimate");
445	if ((buf = malloc(needed)) == NULL)
446		errx(1, "malloc");
447	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
448		errx(1, "actual retrieval of routing table");
449	lim = buf + needed;
450	for (next = buf; next < lim; next += rtm->rtm_msglen) {
451		rtm = (struct rt_msghdr *)next;
452		sin = (struct sockaddr_inarp *)(rtm + 1);
453		sdl = (struct sockaddr_dl *)(sin + 1);
454		if (addr) {
455			if (addr != sin->sin_addr.s_addr)
456				continue;
457			found_entry = 1;
458		}
459		(*action)(sdl, sin, rtm);
460	}
461}
462
463/*
464 * Display an arp entry
465 */
466void
467print_entry(struct sockaddr_dl *sdl,
468	struct sockaddr_inarp *sin, struct rt_msghdr *rtm)
469{
470	char *host;
471	extern int h_errno;
472	struct hostent *hp;
473
474	if (nflag == 0)
475		hp = gethostbyaddr((caddr_t)&(sin->sin_addr),
476		    sizeof sin->sin_addr, AF_INET);
477	else
478		hp = 0;
479	if (hp)
480		host = hp->h_name;
481	else {
482		host = "?";
483		if (h_errno == TRY_AGAIN)
484			nflag = 1;
485	}
486	printf("%s (%s) at ", host, inet_ntoa(sin->sin_addr));
487	if (sdl->sdl_alen)
488		ether_print(LLADDR(sdl));
489	else
490		printf("(incomplete)");
491	if (rtm->rtm_rmx.rmx_expire == 0)
492		printf(" permanent");
493	if (sin->sin_other & SIN_PROXY)
494		printf(" published (proxy only)");
495	if (rtm->rtm_addrs & RTA_NETMASK) {
496		sin = (struct sockaddr_inarp *)
497			(sdl->sdl_len + (char *)sdl);
498		if (sin->sin_addr.s_addr == 0xffffffff)
499			printf(" published");
500		if (sin->sin_len != 8)
501			printf("(wierd)");
502	}
503	printf("\n");
504}
505
506/*
507 * Nuke an arp entry
508 */
509void
510nuke_entry(struct sockaddr_dl *sdl,
511	struct sockaddr_inarp *sin, struct rt_msghdr *rtm)
512{
513	char ip[20];
514
515	snprintf(ip, sizeof(ip), "%s", inet_ntoa(sin->sin_addr));
516	delete(ip, NULL);
517}
518
519void
520ether_print(u_char *cp)
521{
522	printf("%x:%x:%x:%x:%x:%x", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
523}
524
525int
526my_ether_aton(char *a, u_char *n)
527{
528	int i, o[6];
529
530	i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
531					   &o[3], &o[4], &o[5]);
532	if (i != 6) {
533		warnx("invalid Ethernet address '%s'", a);
534		return (1);
535	}
536	for (i=0; i<6; i++)
537		n[i] = o[i];
538	return (0);
539}
540
541void
542usage(void)
543{
544	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
545		"usage: arp [-n] hostname",
546		"       arp [-n] -a",
547		"       arp -d hostname [proxy]",
548		"       arp -d -a",
549		"       arp -s hostname ether_addr [temp] [pub]",
550		"       arp -S hostname ether_addr [temp] [pub]",
551		"       arp -f filename");
552	exit(1);
553}
554
555int
556rtmsg(int cmd)
557{
558	static int seq;
559	int rlen;
560	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
561	register char *cp = m_rtmsg.m_space;
562	register int l;
563
564	errno = 0;
565	if (cmd == RTM_DELETE)
566		goto doit;
567	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
568	rtm->rtm_flags = flags;
569	rtm->rtm_version = RTM_VERSION;
570
571	switch (cmd) {
572	default:
573		errx(1, "internal wrong cmd");
574	case RTM_ADD:
575		rtm->rtm_addrs |= RTA_GATEWAY;
576		rtm->rtm_rmx.rmx_expire = expire_time;
577		rtm->rtm_inits = RTV_EXPIRE;
578		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
579		sin_m.sin_other = 0;
580		if (doing_proxy) {
581			if (export_only)
582				sin_m.sin_other = SIN_PROXY;
583			else {
584				rtm->rtm_addrs |= RTA_NETMASK;
585				rtm->rtm_flags &= ~RTF_HOST;
586			}
587		}
588		/* FALLTHROUGH */
589	case RTM_GET:
590		rtm->rtm_addrs |= RTA_DST;
591	}
592#define NEXTADDR(w, s) \
593	if (rtm->rtm_addrs & (w)) { \
594		bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);}
595
596	NEXTADDR(RTA_DST, sin_m);
597	NEXTADDR(RTA_GATEWAY, sdl_m);
598	NEXTADDR(RTA_NETMASK, so_mask);
599
600	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
601doit:
602	l = rtm->rtm_msglen;
603	rtm->rtm_seq = ++seq;
604	rtm->rtm_type = cmd;
605	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
606		if (errno != ESRCH || cmd != RTM_DELETE) {
607			warn("writing to routing socket");
608			return (-1);
609		}
610	}
611	do {
612		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
613	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
614	if (l < 0)
615		warn("read from routing socket");
616	return (0);
617}
618
619/*
620 * get_ether_addr - get the hardware address of an interface on the
621 * the same subnet as ipaddr.
622 */
623#define MAX_IFS		32
624
625int
626get_ether_addr(u_long ipaddr, u_char *hwaddr)
627{
628	struct ifreq *ifr, *ifend, *ifp;
629	u_long ina, mask;
630	struct sockaddr_dl *dla;
631	struct ifreq ifreq;
632	struct ifconf ifc;
633	struct ifreq ifs[MAX_IFS];
634	int s;
635
636	s = socket(AF_INET, SOCK_DGRAM, 0);
637	if (s < 0)
638		err(1, "socket");
639
640	ifc.ifc_len = sizeof(ifs);
641	ifc.ifc_req = ifs;
642	if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
643		warnx("ioctl(SIOCGIFCONF)");
644		close(s);
645		return 0;
646	}
647
648	/*
649	* Scan through looking for an interface with an Internet
650	* address on the same subnet as `ipaddr'.
651	*/
652	ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
653	for (ifr = ifc.ifc_req; ifr < ifend; ) {
654		if (ifr->ifr_addr.sa_family == AF_INET) {
655			ina = ((struct sockaddr_in *)
656				&ifr->ifr_addr)->sin_addr.s_addr;
657			strncpy(ifreq.ifr_name, ifr->ifr_name,
658				sizeof(ifreq.ifr_name));
659			/*
660			 * Check that the interface is up,
661			 * and not point-to-point or loopback.
662			 */
663			if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0)
664				continue;
665			if ((ifreq.ifr_flags &
666			     (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
667					IFF_LOOPBACK|IFF_NOARP))
668			     != (IFF_UP|IFF_BROADCAST))
669				goto nextif;
670			/*
671			 * Get its netmask and check that it's on
672			 * the right subnet.
673			 */
674			if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0)
675				continue;
676			mask = ((struct sockaddr_in *)
677				&ifreq.ifr_addr)->sin_addr.s_addr;
678			if ((ipaddr & mask) != (ina & mask))
679				goto nextif;
680			break;
681		}
682nextif:
683		ifr = (struct ifreq *)
684		    ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
685	}
686
687	if (ifr >= ifend) {
688		close(s);
689		return 0;
690	}
691
692	/*
693	* Now scan through again looking for a link-level address
694	* for this interface.
695	*/
696	ifp = ifr;
697	for (ifr = ifc.ifc_req; ifr < ifend; ) {
698		if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
699		    && ifr->ifr_addr.sa_family == AF_LINK) {
700			/*
701			 * Found the link-level address - copy it out
702			 */
703		 	dla = (struct sockaddr_dl *) &ifr->ifr_addr;
704			memcpy(hwaddr,  LLADDR(dla), dla->sdl_alen);
705			close (s);
706			printf("using interface %s for proxy with address ",
707				ifp->ifr_name);
708			ether_print(hwaddr);
709			printf("\n");
710			return dla->sdl_alen;
711		}
712		ifr = (struct ifreq *)
713			((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
714	}
715	return 0;
716}
717