/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 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 /* * 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 int rarp_write(int, struct arphdr *, uchar_t *, size_t, size_t); static int rarp_open(char *, t_uscalar_t, size_t *, uchar_t **, uchar_t **); /* ARGSUSED */ int doifrevarp(char *ifname, struct sockaddr_in *laddr) { int if_fd; struct pollfd pfd; int s, flags, ret; char *ctlbuf, *databuf, *cause; struct strbuf ctl, data; struct arphdr *req, *ans; struct in_addr from; struct in_addr answer; union DL_primitives *dlp; struct lifreq lifr; struct timeval senttime; struct timeval currenttime; int waittime; int tries_left; size_t ifaddrlen, ifrarplen; uchar_t *my_macaddr = NULL, *my_broadcast = NULL; if (ifname[0] == '\0') { (void) fprintf(stderr, "ifconfig: doifrevarp: name not set\n"); exit(1); } if (debug) (void) printf("doifrevarp interface %s\n", ifname); if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { Perror0_exit("socket"); } (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFFLAGS, (char *)&lifr) < 0) 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)) return (0); /* open rarp interface */ if_fd = rarp_open(ifname, ETHERTYPE_REVARP, &ifaddrlen, &my_macaddr, &my_broadcast); if (if_fd < 0) return (0); /* * RARP looks at /etc/ethers and NIS, which only works * with 6 byte addresses currently. */ if (ifaddrlen != ETHERADDRL) { (void) close(if_fd); free(my_macaddr); free(my_broadcast); return (0); } ifrarplen = sizeof (struct arphdr) + (2 * IPADDRL) + (2 * ifaddrlen); /* 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 = (struct arphdr *)malloc(ifrarplen)) == NULL) || ((ans = (struct arphdr *)malloc(ifrarplen)) == NULL)) { (void) close(if_fd); free(req); free(my_macaddr); free(my_broadcast); 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 = ifaddrlen; req->ar_pln = IPADDRL; req->ar_op = htons(REVARP_REQUEST); (void) memcpy((uchar_t *)req + sizeof (struct arphdr), my_macaddr, ifaddrlen); (void) memcpy((uchar_t *)req + sizeof (struct arphdr) + IPADDRL + ifaddrlen, my_macaddr, ifaddrlen); tries_left = rarp_retries; rarp_retry: /* send the request */ if (rarp_write(if_fd, req, my_broadcast, ifaddrlen, ifrarplen) < 0) goto fail; gettimeofday(&senttime, NULL); if (debug) (void) printf("rarp sent\n"); /* read the answers */ if ((databuf = malloc(BUFSIZ)) == NULL) { (void) fprintf(stderr, "ifconfig: malloc() failed\n"); goto fail; } if ((ctlbuf = malloc(BUFSIZ)) == NULL) { (void) fprintf(stderr, "ifconfig: malloc() failed\n"); goto fail; } for (;;) { ctl.len = 0; ctl.maxlen = BUFSIZ; ctl.buf = ctlbuf; data.len = 0; data.maxlen = BUFSIZ; data.buf = databuf; flags = 0; /* * Check to see when the packet was last sent. * If we have not sent a packet in the last * RARP_TIMEOUT seconds, we should send one now. * Note that if some other host on the network is * sending a broadcast packet, poll will return and we * will find out that it does not match the reply we * are waiting for and then go back to poll. If the * frequency of such packets is > rarp_timeout, we don't * want to just go back to poll. We should send out one * more RARP request before blocking in poll. */ gettimeofday(¤ttime, NULL); waittime = rarp_timeout - (currenttime.tv_sec - senttime.tv_sec); if (waittime <= 0) { if (--tries_left > 0) { if (debug) (void) printf("rarp retry\n"); goto rarp_retry; } else { if (debug) (void) printf("rarp timeout\n"); goto fail; } } /* start RARP reply timeout */ pfd.fd = if_fd; pfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; if ((ret = poll(&pfd, 1, waittime * 1000)) == 0) { if (--tries_left > 0) { if (debug) (void) printf("rarp retry\n"); goto rarp_retry; } else { if (debug) (void) printf("rarp timeout\n"); goto fail; } } else if (ret == -1) { perror("ifconfig: RARP reply poll"); goto fail; } /* poll returned > 0 for this fd so getmsg should not block */ if ((ret = getmsg(if_fd, &ctl, &data, &flags)) < 0) { perror("ifconfig: RARP reply getmsg"); goto fail; } if (debug) { (void) printf("rarp: ret[%d] ctl.len[%d] data.len[%d] " "flags[%d]\n", ret, ctl.len, data.len, flags); } /* Validate DL_UNITDATA_IND. */ /* LINTED: malloc returns a pointer aligned for any use */ dlp = (union DL_primitives *)ctlbuf; if (debug) { (void) printf("rarp: dl_primitive[%lu]\n", dlp->dl_primitive); if (dlp->dl_primitive == DL_ERROR_ACK) { (void) printf( "rarp: err ak: dl_errno %lu errno %lu\n", dlp->error_ack.dl_errno, dlp->error_ack.dl_unix_errno); } if (dlp->dl_primitive == DL_UDERROR_IND) { (void) printf("rarp: ud err: err[%lu] len[%lu] " "off[%lu]\n", dlp->uderror_ind.dl_errno, dlp->uderror_ind.dl_dest_addr_length, dlp->uderror_ind.dl_dest_addr_offset); } } (void) memcpy(ans, databuf, ifrarplen); cause = NULL; if (ret & MORECTL) cause = "MORECTL flag"; else if (ret & MOREDATA) cause = "MOREDATA flag"; else if (ctl.len == 0) cause = "missing control part of message"; else if (ctl.len < 0) cause = "short control part of message"; else if (dlp->dl_primitive != DL_UNITDATA_IND) cause = "not unitdata_ind"; else if (ctl.len < DL_UNITDATA_IND_SIZE) cause = "short unitdata_ind"; else if (data.len < ifrarplen) cause = "short arp"; else if (ans->ar_hrd != htons(ARPHRD_ETHER)) cause = "hrd"; else if (ans->ar_pro != htons(ETHERTYPE_IP)) cause = "pro"; else if (ans->ar_hln != ifaddrlen) cause = "hln"; else if (ans->ar_pln != IPADDRL) cause = "pln"; if (cause) { (void) fprintf(stderr, "sanity check failed; cause: %s\n", cause); continue; } switch (ntohs(ans->ar_op)) { case ARPOP_REQUEST: if (debug) (void) printf("Got an arp request\n"); break; case ARPOP_REPLY: if (debug) (void) printf("Got an arp reply.\n"); break; case REVARP_REQUEST: if (debug) (void) printf("Got a rarp request.\n"); break; case REVARP_REPLY: (void) memcpy(&answer, (uchar_t *)ans + sizeof (struct arphdr) + (2 * ifaddrlen) + IPADDRL, sizeof (answer)); (void) memcpy(&from, (uchar_t *)ans + sizeof (struct arphdr) + ifaddrlen, sizeof (from)); if (debug) { (void) printf("answer: %s", inet_ntoa(answer)); (void) printf(" [from %s]\n", inet_ntoa(from)); } laddr->sin_addr = answer; (void) close(if_fd); free(req); free(ans); free(my_macaddr); free(my_broadcast); return (1); default: (void) fprintf(stderr, "ifconfig: unknown opcode 0x%xd\n", ans->ar_op); break; } } /* NOTREACHED */ fail: (void) close(if_fd); free(req); free(ans); free(my_macaddr); free(my_broadcast); return (0); } /* * Open the datalink provider device and bind to the REVARP type. * Return the resulting descriptor. */ static int rarp_open(char *ifname, t_uscalar_t type, size_t *alen, uchar_t **myaddr, uchar_t **mybaddr) { int fd, len; char *str; dl_info_ack_t dlinfo; dlpi_if_attr_t dia; int i; if (debug) (void) printf("rarp_open %s\n", ifname); fd = dlpi_if_open(ifname, &dia, _B_FALSE); if (fd < 0) { (void) fprintf(stderr, "ifconfig: could not open device for " "%s\n", ifname); return (-1); } if (dlpi_info(fd, -1, &dlinfo, NULL, NULL, NULL, NULL, NULL, NULL) < 0) { (void) fprintf(stderr, "ifconfig: info req failed\n"); goto failed; } if ((*mybaddr = malloc(dlinfo.dl_brdcst_addr_length)) == NULL) { (void) fprintf(stderr, "rarp_open: malloc() failed\n"); goto failed; } if (dlpi_info(fd, -1, &dlinfo, NULL, NULL, NULL, NULL, *mybaddr, NULL) < 0) { (void) fprintf(stderr, "ifconfig: info req failed\n"); goto failed; } if (debug) { (void) printf("broadcast addr: "); for (i = 0; i < dlinfo.dl_brdcst_addr_length; i++) (void) printf("%02x", (*mybaddr)[i]); (void) printf("\n"); } len = *alen = dlinfo.dl_addr_length - abs(dlinfo.dl_sap_length); if (debug) (void) printf("rarp_open: addr length = %d\n", len); if ((*myaddr = malloc(len)) == NULL) { (void) fprintf(stderr, "rarp_open: malloc() failed\n"); goto failed; } if (dlpi_bind(fd, -1, type, DL_CLDLS, _B_FALSE, NULL, NULL, *myaddr, NULL) < 0) { (void) fprintf(stderr, "rarp_open: dlpi_bind failed\n"); goto failed; } if (debug) { str = _link_ntoa(*myaddr, str, len, IFT_OTHER); if (str != NULL) { (void) printf("device %s mac address %s\n", ifname, str); free(str); } } return (fd); failed: (void) dlpi_close(fd); free(*mybaddr); free(*myaddr); return (-1); } static int rarp_write(int fd, struct arphdr *ahdr, uchar_t *dhost, size_t maclen, size_t rarplen) { struct strbuf ctl, data; union DL_primitives *dlp; char *ctlbuf; int ret; ushort_t etype = ETHERTYPE_REVARP; /* * Construct DL_UNITDATA_REQ. Allocate at least BUFSIZ bytes. */ ctl.len = DL_UNITDATA_REQ_SIZE + maclen + sizeof (ushort_t); if ((ctl.buf = ctlbuf = malloc(ctl.len)) == NULL) { (void) fprintf(stderr, "ifconfig: malloc() failed\n"); return (-1); } /* LINTED: malloc returns a pointer aligned for any use */ dlp = (union DL_primitives *)ctlbuf; dlp->unitdata_req.dl_primitive = DL_UNITDATA_REQ; dlp->unitdata_req.dl_dest_addr_length = maclen + sizeof (ushort_t); dlp->unitdata_req.dl_dest_addr_offset = DL_UNITDATA_REQ_SIZE; dlp->unitdata_req.dl_priority.dl_min = 0; dlp->unitdata_req.dl_priority.dl_max = 0; /* * XXX FIXME Assumes a specific DLPI address format. */ (void) memcpy(ctlbuf + DL_UNITDATA_REQ_SIZE, dhost, maclen); (void) memcpy(ctlbuf + DL_UNITDATA_REQ_SIZE + maclen, &etype, sizeof (etype)); /* Send DL_UNITDATA_REQ. */ data.len = rarplen; data.buf = (char *)ahdr; ret = putmsg(fd, &ctl, &data, 0); free(ctlbuf); return (ret); } int dlpi_set_address(char *ifname, uchar_t *ea, int length) { int fd; dlpi_if_attr_t dia; fd = dlpi_if_open(ifname, &dia, _B_FALSE); if (fd < 0) { (void) fprintf(stderr, "ifconfig: could not open device for " "%s\n", ifname); return (-1); } if (dlpi_set_phys_addr(fd, -1, ea, length) < 0) { (void) dlpi_close(fd); return (-1); } (void) dlpi_close(fd); return (0); } void dlpi_print_address(char *ifname) { int fd, len; uchar_t *laddr; dl_info_ack_t dl_info; char *str = NULL; dlpi_if_attr_t dia; fd = dlpi_if_open(ifname, &dia, _B_FALSE); if (fd < 0) { /* Do not report an error */ return; } if (dlpi_info(fd, -1, &dl_info, NULL, NULL, NULL, NULL, NULL, NULL) < 0) { (void) fprintf(stderr, "ifconfig: info req failed\n"); (void) dlpi_close(fd); return; } len = dl_info.dl_addr_length - abs(dl_info.dl_sap_length); if ((laddr = malloc(len)) == NULL) { goto failed; } if (dlpi_phys_addr(fd, -1, DL_CURR_PHYS_ADDR, laddr, NULL) < 0) { (void) fprintf(stderr, "ifconfig: phys_addr failed\n"); goto failed; } (void) dlpi_close(fd); str = _link_ntoa(laddr, str, len, IFT_OTHER); if (str != NULL) { switch (dl_info.dl_mac_type) { case DL_IB: (void) printf("\tipib %s \n", str); break; default: (void) printf("\tether %s \n", str); break; } free(str); } failed: free(laddr); (void) dlpi_close(fd); }