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