/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #pragma ident "%Z%%M% %I% %E% SMI" #include "defs.h" #include "ifconfig.h" #include #include #include #include #include #define IPADDRL sizeof (struct in_addr) #define RARPRETRIES 5 #define MSEC2NSEC(msec) ((msec) * 1000000) #define NSEC2MSEC(nsec) ((nsec) / 1000000) /* * The following value (8) is determined to work reliably in switched 10/100MB * ethernet environments. Use caution if you plan on decreasing it. */ #define RARPTIMEOUT 8 static char defaultfile[] = "/etc/inet/rarp"; static char retries_var[] = "RARP_RETRIES="; static int rarp_timeout = RARPTIMEOUT; static int rarp_retries = RARPRETRIES; static dlpi_handle_t rarp_open(const char *, size_t *, uchar_t *, uchar_t *); static int rarp_recv(dlpi_handle_t, struct arphdr *, size_t, size_t, int64_t); int doifrevarp(const char *linkname, struct sockaddr_in *laddr) { int s, retval; struct arphdr *req, *ans; struct in_addr from; struct in_addr answer; struct lifreq lifr; int tries_left; size_t physaddrlen, ifrarplen; uchar_t my_macaddr[DLPI_PHYSADDR_MAX]; uchar_t my_broadcast[DLPI_PHYSADDR_MAX]; dlpi_handle_t dh; if (linkname[0] == '\0') { (void) fprintf(stderr, "ifconfig: doifrevarp: name not set\n"); exit(1); } if (debug) (void) printf("doifrevarp interface %s\n", linkname); if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) Perror0_exit("socket"); (void) strlcpy(lifr.lifr_name, linkname, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFFLAGS, (char *)&lifr) < 0) { (void) close(s); Perror0_exit("SIOCGLIFFLAGS"); } /* don't try to revarp if we know it won't work */ if ((lifr.lifr_flags & IFF_LOOPBACK) || (lifr.lifr_flags & IFF_NOARP) || (lifr.lifr_flags & IFF_POINTOPOINT)) { (void) close(s); return (0); } /* open rarp interface */ dh = rarp_open(linkname, &physaddrlen, my_macaddr, my_broadcast); if (dh == NULL) { (void) close(s); return (0); } /* * RARP looks at /etc/ethers and NIS, which only works * with 6 byte addresses currently. */ if (physaddrlen != ETHERADDRL) { dlpi_close(dh); (void) close(s); return (0); } ifrarplen = sizeof (struct arphdr) + (2 * IPADDRL) + (2 * physaddrlen); /* look for adjustments to rarp_retries in the RARP defaults file */ if (defopen(defaultfile) == 0) { char *cp; if (cp = defread(retries_var)) { int ntries; ntries = atoi(cp); if (ntries > 0) rarp_retries = ntries; } (void) defopen(NULL); /* close default file */ } /* allocate request and response buffers */ if (((req = malloc(ifrarplen)) == NULL) || ((ans = malloc(ifrarplen)) == NULL)) { dlpi_close(dh); (void) close(s); free(req); return (0); } /* create rarp request */ (void) memset(req, 0, ifrarplen); req->ar_hrd = htons(ARPHRD_ETHER); req->ar_pro = htons(ETHERTYPE_IP); req->ar_hln = physaddrlen; req->ar_pln = IPADDRL; req->ar_op = htons(REVARP_REQUEST); (void) memcpy(&req[1], my_macaddr, physaddrlen); (void) memcpy((uchar_t *)req + sizeof (struct arphdr) + IPADDRL + physaddrlen, my_macaddr, physaddrlen); for (tries_left = rarp_retries; tries_left > 0; --tries_left) { /* send the request */ retval = dlpi_send(dh, my_broadcast, physaddrlen, req, ifrarplen, NULL); if (retval != DLPI_SUCCESS) { Perrdlpi("doifrevarp: cannot send rarp request", linkname, retval); break; } if (debug) (void) printf("rarp sent\n"); retval = rarp_recv(dh, ans, ifrarplen, physaddrlen, rarp_timeout * MILLISEC); if (retval != DLPI_ETIMEDOUT) break; if (debug) (void) printf("rarp retry\n"); } if (retval == DLPI_SUCCESS) { (void) memcpy(&answer, (uchar_t *)ans + sizeof (struct arphdr) + (2 * physaddrlen) + IPADDRL, sizeof (answer)); (void) memcpy(&from, (uchar_t *)ans + physaddrlen + sizeof (struct arphdr), sizeof (from)); if (debug) { (void) printf("answer: %s", inet_ntoa(answer)); (void) printf(" [from %s]\n", inet_ntoa(from)); } laddr->sin_addr = answer; } else if (debug) { Perrdlpi("doifrevarp: could not receive rarp reply", linkname, retval); } dlpi_close(dh); (void) close(s); free(req); free(ans); return (retval == DLPI_SUCCESS); } /* * Open the datalink provider device and bind to the REVARP type. * Return the resulting DLPI handle. */ static dlpi_handle_t rarp_open(const char *linkname, size_t *alen, uchar_t *myaddr, uchar_t *mybaddr) { int retval; char *physaddr, *bcastaddr; dlpi_info_t dlinfo; dlpi_handle_t dh; if (debug) (void) printf("rarp_open %s\n", linkname); if ((retval = dlpi_open(linkname, &dh, 0)) != DLPI_SUCCESS) { Perrdlpi("rarp_open: dlpi_open failed", linkname, retval); return (NULL); } if ((retval = dlpi_bind(dh, ETHERTYPE_REVARP, NULL)) != DLPI_SUCCESS) { Perrdlpi("rarp_open: dlpi_bind failed", linkname, retval); goto failed; } if ((retval = dlpi_info(dh, &dlinfo, 0)) != DLPI_SUCCESS) { Perrdlpi("rarp_open: dlpi_info failed", linkname, retval); goto failed; } if (dlinfo.di_bcastaddrlen == 0) { (void) fprintf(stderr, "ifconfig: rarp_open: %s broadcast " "not supported\n", linkname); goto failed; } /* we assume the following are equal and fill in 'alen' */ assert(dlinfo.di_bcastaddrlen == dlinfo.di_physaddrlen); (void) memcpy(mybaddr, dlinfo.di_bcastaddr, dlinfo.di_bcastaddrlen); *alen = dlinfo.di_physaddrlen; (void) memcpy(myaddr, dlinfo.di_physaddr, dlinfo.di_physaddrlen); if (debug) { bcastaddr = _link_ntoa(mybaddr, NULL, dlinfo.di_bcastaddrlen, IFT_OTHER); physaddr = _link_ntoa(myaddr, NULL, dlinfo.di_physaddrlen, IFT_OTHER); if (physaddr != NULL && bcastaddr != NULL) { (void) printf("device %s: broadcast address %s, mac " "address %s\n", linkname, bcastaddr, physaddr); } free(physaddr); free(bcastaddr); (void) printf("rarp_open: addr length = %d\n", dlinfo.di_physaddrlen); } return (dh); failed: dlpi_close(dh); return (NULL); } /* * Read reply for RARP request. If a reply is received within waitms, * validate the reply. If it is a correct RARP reply return DLPI_SUCCESS, * otherwise return DLPI_ETIMEDOUT. If there is an error while reading retrun * the error code. */ static int rarp_recv(dlpi_handle_t dh, struct arphdr *ans, size_t msglen, size_t physaddrlen, int64_t waitms) { int retval; char *cause; size_t anslen = msglen; hrtime_t endtime = gethrtime() + MSEC2NSEC(waitms); hrtime_t currtime; while ((currtime = gethrtime()) < endtime) { waitms = NSEC2MSEC(endtime - currtime); retval = dlpi_recv(dh, NULL, NULL, ans, &anslen, waitms, NULL); if (retval == DLPI_SUCCESS) { cause = NULL; if (anslen < msglen) cause = "short packet"; else if (ans->ar_hrd != htons(ARPHRD_ETHER)) cause = "hardware type not Ethernet"; else if (ans->ar_pro != htons(ETHERTYPE_IP)) cause = "protocol type not IP"; else if (ans->ar_hln != physaddrlen) cause = "unexpected hardware address length"; else if (ans->ar_pln != IPADDRL) cause = "unexpected protocol address length"; if (cause != NULL) { (void) fprintf(stderr, "RARP packet received " "but discarded (%s)\n", cause); continue; } switch (ntohs(ans->ar_op)) { case REVARP_REQUEST: if (debug) (void) printf("Got a rarp request.\n"); break; case REVARP_REPLY: return (DLPI_SUCCESS); default: (void) fprintf(stderr, "ifconfig: unknown " "RARP opcode 0x%x\n", ans->ar_op); break; } } else if (retval != DLPI_ETIMEDOUT) { Perrdlpi("doifrevarp: dlpi_recv failed", dlpi_linkname(dh), retval); return (retval); } } return (DLPI_ETIMEDOUT); } int dlpi_set_address(const char *linkname, uchar_t *physaddr, uint_t physaddrlen) { int retval; dlpi_handle_t dh; if ((retval = dlpi_open(linkname, &dh, 0)) != DLPI_SUCCESS) { Perrdlpi("dlpi_open failed", linkname, retval); return (-1); } if ((retval = dlpi_set_physaddr(dh, DL_CURR_PHYS_ADDR, physaddr, physaddrlen)) != DLPI_SUCCESS) { Perrdlpi("dlpi_set_physaddr failed", linkname, retval); dlpi_close(dh); return (-1); } dlpi_close(dh); return (0); } void dlpi_print_address(const char *linkname) { uint_t physaddrlen = DLPI_PHYSADDR_MAX; uchar_t physaddr[DLPI_PHYSADDR_MAX]; char *str; int retv; dlpi_handle_t dh; dlpi_info_t dlinfo; if (dlpi_open(linkname, &dh, 0) != DLPI_SUCCESS) { /* Do not report an error */ return; } retv = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, physaddr, &physaddrlen); if (retv != DLPI_SUCCESS) { Perrdlpi("dlpi_get_physaddr failed", linkname, retv); dlpi_close(dh); return; } retv = dlpi_info(dh, &dlinfo, 0); if (retv != DLPI_SUCCESS) { Perrdlpi("dlpi_info failed", linkname, retv); dlpi_close(dh); return; } dlpi_close(dh); str = _link_ntoa(physaddr, NULL, physaddrlen, IFT_OTHER); if (str != NULL && physaddrlen != 0) { switch (dlinfo.di_mactype) { case DL_IB: (void) printf("\tipib %s \n", str); break; default: (void) printf("\tether %s \n", str); break; } free(str); } }