Home | History | Annotate | Download | only in ifconfig
      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
     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
    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
    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
    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