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
doifrevarp(const char * linkname,struct sockaddr_in * laddr)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
rarp_open(const char * linkname,size_t * alen,uchar_t * myaddr,uchar_t * mybaddr)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
rarp_recv(dlpi_handle_t dh,struct arphdr * ans,size_t msglen,size_t physaddrlen,int64_t waitms)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
dlpi_print_address(const char * linkname)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