xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/revarp.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
26 /*	  All Rights Reserved	*/
27 
28 #include "defs.h"
29 #include "ifconfig.h"
30 #include <sys/types.h>
31 #include <libdlpi.h>
32 #include <sys/sysmacros.h>
33 #include <sys/time.h>
34 #include <deflt.h>
35 
36 #define	IPADDRL		sizeof (struct in_addr)
37 #define	RARPRETRIES	5
38 #define	MSEC2NSEC(msec)	((msec) * 1000000)
39 #define	NSEC2MSEC(nsec)	((nsec) / 1000000)
40 
41 /*
42  * The following value (8) is determined to work reliably in switched 10/100MB
43  * ethernet environments. Use caution if you plan on decreasing it.
44  */
45 #define	RARPTIMEOUT	8
46 
47 static char	defaultfile[] = "/etc/inet/rarp";
48 static char	retries_var[] = "RARP_RETRIES=";
49 static int rarp_timeout = RARPTIMEOUT;
50 static int rarp_retries = RARPRETRIES;
51 
52 static dlpi_handle_t rarp_open(const char *, size_t *, uchar_t *, uchar_t *);
53 static int rarp_recv(dlpi_handle_t, struct arphdr *, size_t, size_t, int64_t);
54 
55 int
56 doifrevarp(const char *linkname, struct sockaddr_in *laddr)
57 {
58 	int			s, retval;
59 	struct arphdr		*req, *ans;
60 	struct in_addr		from;
61 	struct in_addr		answer;
62 	struct lifreq		lifr;
63 	int			tries_left;
64 	size_t			physaddrlen, ifrarplen;
65 	uchar_t			my_macaddr[DLPI_PHYSADDR_MAX];
66 	uchar_t 		my_broadcast[DLPI_PHYSADDR_MAX];
67 	dlpi_handle_t		dh;
68 
69 	if (linkname[0] == '\0') {
70 		(void) fprintf(stderr, "ifconfig: doifrevarp: name not set\n");
71 		exit(1);
72 	}
73 
74 	if (debug)
75 		(void) printf("doifrevarp interface %s\n", linkname);
76 
77 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
78 		Perror0_exit("socket");
79 
80 	(void) strlcpy(lifr.lifr_name, linkname, sizeof (lifr.lifr_name));
81 	if (ioctl(s, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
82 		(void) close(s);
83 		Perror0_exit("SIOCGLIFFLAGS");
84 	}
85 
86 	/* don't try to revarp if we know it won't work */
87 	if ((lifr.lifr_flags & IFF_LOOPBACK) ||
88 	    (lifr.lifr_flags & IFF_NOARP) ||
89 	    (lifr.lifr_flags & IFF_IPMP) ||
90 	    (lifr.lifr_flags & IFF_POINTOPOINT)) {
91 		(void) close(s);
92 		return (0);
93 	}
94 
95 	/* open rarp interface */
96 	dh = rarp_open(linkname, &physaddrlen, my_macaddr, my_broadcast);
97 	if (dh == NULL) {
98 		(void) close(s);
99 		return (0);
100 	}
101 
102 	/*
103 	 * RARP looks at /etc/ethers and NIS, which only works
104 	 * with 6 byte addresses currently.
105 	 */
106 	if (physaddrlen != ETHERADDRL) {
107 		dlpi_close(dh);
108 		(void) close(s);
109 		return (0);
110 	}
111 
112 	ifrarplen = sizeof (struct arphdr) + (2 * IPADDRL) + (2 * physaddrlen);
113 
114 	/* look for adjustments to rarp_retries in the RARP defaults file */
115 	if (defopen(defaultfile) == 0) {
116 		char	*cp;
117 
118 		if (cp = defread(retries_var)) {
119 			int	ntries;
120 
121 			ntries = atoi(cp);
122 			if (ntries > 0)
123 				rarp_retries = ntries;
124 		}
125 		(void) defopen(NULL);	/* close default file */
126 	}
127 
128 	/* allocate request and response buffers */
129 	if (((req = malloc(ifrarplen)) == NULL) ||
130 	    ((ans = malloc(ifrarplen)) == NULL)) {
131 		dlpi_close(dh);
132 		(void) close(s);
133 		free(req);
134 		return (0);
135 	}
136 
137 	/* create rarp request */
138 	(void) memset(req, 0, ifrarplen);
139 	req->ar_hrd = htons(ARPHRD_ETHER);
140 	req->ar_pro = htons(ETHERTYPE_IP);
141 	req->ar_hln = physaddrlen;
142 	req->ar_pln = IPADDRL;
143 	req->ar_op = htons(REVARP_REQUEST);
144 
145 	(void) memcpy(&req[1], my_macaddr, physaddrlen);
146 	(void) memcpy((uchar_t *)req + sizeof (struct arphdr) + IPADDRL +
147 	    physaddrlen, my_macaddr, physaddrlen);
148 
149 	for (tries_left = rarp_retries; tries_left > 0; --tries_left) {
150 		/* send the request */
151 		retval = dlpi_send(dh, my_broadcast, physaddrlen, req,
152 		    ifrarplen, NULL);
153 		if (retval != DLPI_SUCCESS) {
154 			Perrdlpi("doifrevarp: cannot send rarp request",
155 			    linkname, retval);
156 			break;
157 		}
158 
159 		if (debug)
160 			(void) printf("rarp sent\n");
161 
162 		retval = rarp_recv(dh, ans, ifrarplen, physaddrlen,
163 		    rarp_timeout * MILLISEC);
164 
165 		if (retval != DLPI_ETIMEDOUT)
166 			break;
167 
168 		if (debug)
169 			(void) printf("rarp retry\n");
170 	}
171 
172 	if (retval == DLPI_SUCCESS) {
173 		(void) memcpy(&answer, (uchar_t *)ans +
174 		    sizeof (struct arphdr) + (2 * physaddrlen) + IPADDRL,
175 		    sizeof (answer));
176 		(void) memcpy(&from, (uchar_t *)ans + physaddrlen +
177 		    sizeof (struct arphdr), sizeof (from));
178 
179 		if (debug) {
180 			(void) printf("answer: %s", inet_ntoa(answer));
181 			(void) printf(" [from %s]\n", inet_ntoa(from));
182 		}
183 		laddr->sin_addr = answer;
184 	} else if (debug) {
185 		Perrdlpi("doifrevarp: could not receive rarp reply",
186 		    linkname, retval);
187 	}
188 
189 	dlpi_close(dh);
190 	(void) close(s);
191 	free(req);
192 	free(ans);
193 	return (retval == DLPI_SUCCESS);
194 }
195 
196 /*
197  * Open the datalink provider device and bind to the REVARP type.
198  * Return the resulting DLPI handle.
199  */
200 static	dlpi_handle_t
201 rarp_open(const char *linkname, size_t *alen, uchar_t *myaddr, uchar_t *mybaddr)
202 {
203 	int		retval;
204 	char		*physaddr, *bcastaddr;
205 	dlpi_info_t	dlinfo;
206 	dlpi_handle_t	dh;
207 
208 	if (debug)
209 		(void) printf("rarp_open %s\n", linkname);
210 
211 	if ((retval = dlpi_open(linkname, &dh, 0)) != DLPI_SUCCESS) {
212 		Perrdlpi("rarp_open: dlpi_open failed", linkname, retval);
213 		return (NULL);
214 	}
215 
216 	if ((retval = dlpi_bind(dh, ETHERTYPE_REVARP, NULL)) != DLPI_SUCCESS) {
217 		Perrdlpi("rarp_open: dlpi_bind failed", linkname, retval);
218 		goto failed;
219 	}
220 
221 	if ((retval = dlpi_info(dh, &dlinfo, 0)) != DLPI_SUCCESS) {
222 		Perrdlpi("rarp_open: dlpi_info failed", linkname, retval);
223 		goto failed;
224 	}
225 
226 	if (dlinfo.di_bcastaddrlen == 0) {
227 		(void) fprintf(stderr, "ifconfig: rarp_open: %s broadcast "
228 		    "not supported\n", linkname);
229 		goto failed;
230 	}
231 
232 	/* we assume the following are equal and fill in 'alen' */
233 	assert(dlinfo.di_bcastaddrlen == dlinfo.di_physaddrlen);
234 
235 	(void) memcpy(mybaddr, dlinfo.di_bcastaddr, dlinfo.di_bcastaddrlen);
236 
237 	*alen = dlinfo.di_physaddrlen;
238 
239 	(void) memcpy(myaddr, dlinfo.di_physaddr, dlinfo.di_physaddrlen);
240 
241 	if (debug) {
242 		bcastaddr = _link_ntoa(mybaddr, NULL, dlinfo.di_bcastaddrlen,
243 		    IFT_OTHER);
244 
245 		physaddr = _link_ntoa(myaddr, NULL, dlinfo.di_physaddrlen,
246 		    IFT_OTHER);
247 
248 		if (physaddr != NULL && bcastaddr != NULL) {
249 			(void) printf("device %s: broadcast address %s, mac "
250 			    "address %s\n", linkname, bcastaddr, physaddr);
251 		}
252 
253 		free(physaddr);
254 		free(bcastaddr);
255 
256 		(void) printf("rarp_open: addr length = %d\n",
257 		    dlinfo.di_physaddrlen);
258 	}
259 
260 	return (dh);
261 
262 failed:
263 	dlpi_close(dh);
264 	return (NULL);
265 }
266 
267 /*
268  * Read reply for RARP request. If a reply is received within waitms,
269  * validate the reply. If it is a correct RARP reply return DLPI_SUCCESS,
270  * otherwise return DLPI_ETIMEDOUT. If there is an error while reading retrun
271  * the error code.
272  */
273 static int
274 rarp_recv(dlpi_handle_t dh, struct arphdr *ans, size_t msglen,
275     size_t physaddrlen, int64_t waitms)
276 {
277 	int		retval;
278 	char		*cause;
279 	size_t		anslen = msglen;
280 	hrtime_t	endtime = gethrtime() + MSEC2NSEC(waitms);
281 	hrtime_t	currtime;
282 
283 	while ((currtime = gethrtime()) < endtime) {
284 		waitms = NSEC2MSEC(endtime - currtime);
285 		retval = dlpi_recv(dh, NULL, NULL, ans, &anslen, waitms, NULL);
286 		if (retval == DLPI_SUCCESS) {
287 			cause = NULL;
288 
289 			if (anslen < msglen)
290 				cause = "short packet";
291 			else if (ans->ar_hrd != htons(ARPHRD_ETHER))
292 				cause = "hardware type not Ethernet";
293 			else if (ans->ar_pro != htons(ETHERTYPE_IP))
294 				cause = "protocol type not IP";
295 			else if (ans->ar_hln != physaddrlen)
296 				cause = "unexpected hardware address length";
297 			else if (ans->ar_pln != IPADDRL)
298 				cause = "unexpected protocol address length";
299 			if (cause != NULL) {
300 				(void) fprintf(stderr, "RARP packet received "
301 				    "but discarded (%s)\n", cause);
302 				continue;
303 			}
304 			switch (ntohs(ans->ar_op)) {
305 			case REVARP_REQUEST:
306 				if (debug)
307 					(void) printf("Got a rarp request.\n");
308 				break;
309 
310 			case REVARP_REPLY:
311 				return (DLPI_SUCCESS);
312 
313 			default:
314 				(void) fprintf(stderr, "ifconfig: unknown "
315 				    "RARP opcode 0x%x\n", ans->ar_op);
316 				break;
317 			}
318 		} else if (retval != DLPI_ETIMEDOUT) {
319 			Perrdlpi("doifrevarp: dlpi_recv failed",
320 			    dlpi_linkname(dh), retval);
321 			return (retval);
322 		}
323 	}
324 
325 	return (DLPI_ETIMEDOUT);
326 }
327 
328 void
329 dlpi_print_address(const char *linkname)
330 {
331 	uint_t	physaddrlen = DLPI_PHYSADDR_MAX;
332 	uchar_t	physaddr[DLPI_PHYSADDR_MAX];
333 	char	*str;
334 	int	retv;
335 	dlpi_handle_t	dh;
336 	dlpi_info_t	dlinfo;
337 
338 	if (dlpi_open(linkname, &dh, 0) != DLPI_SUCCESS) {
339 		/* Do not report an error */
340 		return;
341 	}
342 
343 	retv = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, physaddr, &physaddrlen);
344 	if (retv != DLPI_SUCCESS) {
345 		Perrdlpi("dlpi_get_physaddr failed", linkname, retv);
346 		dlpi_close(dh);
347 		return;
348 	}
349 
350 	retv = dlpi_info(dh, &dlinfo, 0);
351 	if (retv != DLPI_SUCCESS) {
352 		Perrdlpi("dlpi_info failed", linkname, retv);
353 		dlpi_close(dh);
354 		return;
355 	}
356 	dlpi_close(dh);
357 
358 	str = _link_ntoa(physaddr, NULL, physaddrlen, IFT_OTHER);
359 
360 	if (str != NULL && physaddrlen != 0) {
361 		switch (dlinfo.di_mactype) {
362 			case DL_IB:
363 				(void) printf("\tipib %s \n", str);
364 				break;
365 			default:
366 				(void) printf("\tether %s \n", str);
367 				break;
368 		}
369 		free(str);
370 	}
371 }
372