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