| /* |
| * 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 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ |
| /* All Rights Reserved */ |
| |
| #include "defs.h" |
| #include "ifconfig.h" |
| #include <sys/types.h> |
| #include <libdlpi.h> |
| #include <sys/sysmacros.h> |
| #include <sys/time.h> |
| #include <deflt.h> |
| |
| #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_IPMP) || |
| (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); |
| } |
| |
| 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); |
| } |
| } |