13229Spst/* 23229Spst * getether.c : get the ethernet address of an interface 33229Spst * 43229Spst * All of this code is quite system-specific. As you may well 53229Spst * guess, it took a good bit of detective work to figure out! 63229Spst * 73229Spst * If you figure out how to do this on another system, 83229Spst * please let me know. <gwr@mc.com> 918471Swosch * 1050476Speter * $FreeBSD$ 113229Spst */ 123229Spst 133229Spst#include <sys/types.h> 143229Spst#include <sys/socket.h> 153229Spst 1613575Spst#ifndef NO_UNISTD 1713575Spst#include <unistd.h> 1813575Spst#endif 1913575Spst 203229Spst#include <ctype.h> 2169793Sobrien#include <paths.h> 2284125Siedowse#include <string.h> 233229Spst#include <syslog.h> 243229Spst 2513575Spst#include "getether.h" 263229Spst#include "report.h" 273229Spst#define EALEN 6 283229Spst 293229Spst#if defined(ultrix) || (defined(__osf__) && defined(__alpha)) 303229Spst/* 313229Spst * This is really easy on Ultrix! Thanks to 323229Spst * Harald Lundberg <hl@tekla.fi> for this code. 333229Spst * 343229Spst * The code here is not specific to the Alpha, but that was the 353229Spst * only symbol we could find to identify DEC's version of OSF. 363229Spst * (Perhaps we should just define DEC in the Makefile... -gwr) 373229Spst */ 383229Spst 393229Spst#include <sys/ioctl.h> 403229Spst#include <net/if.h> /* struct ifdevea */ 413229Spst 423229Spstgetether(ifname, eap) 433229Spst char *ifname, *eap; 443229Spst{ 453229Spst int rc = -1; 463229Spst int fd; 473229Spst struct ifdevea phys; 483229Spst bzero(&phys, sizeof(phys)); 493229Spst strcpy(phys.ifr_name, ifname); 503229Spst if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 513229Spst report(LOG_ERR, "getether: socket(INET,DGRAM) failed"); 523229Spst return -1; 533229Spst } 543229Spst if (ioctl(fd, SIOCRPHYSADDR, &phys) < 0) { 553229Spst report(LOG_ERR, "getether: ioctl SIOCRPHYSADDR failed"); 563229Spst } else { 573229Spst bcopy(&phys.current_pa[0], eap, EALEN); 583229Spst rc = 0; 593229Spst } 603229Spst close(fd); 613229Spst return rc; 623229Spst} 633229Spst 643229Spst#define GETETHER 653229Spst#endif /* ultrix|osf1 */ 663229Spst 673229Spst 683229Spst#ifdef SUNOS 693229Spst 703229Spst#include <sys/sockio.h> 713229Spst#include <sys/time.h> /* needed by net_if.h */ 723229Spst#include <net/nit_if.h> /* for NIOCBIND */ 733229Spst#include <net/if.h> /* for struct ifreq */ 743229Spst 753229Spstgetether(ifname, eap) 763229Spst char *ifname; /* interface name from ifconfig structure */ 773229Spst char *eap; /* Ether address (output) */ 783229Spst{ 793229Spst int rc = -1; 803229Spst 813229Spst struct ifreq ifrnit; 823229Spst int nit; 833229Spst 843229Spst bzero((char *) &ifrnit, sizeof(ifrnit)); 85105039Skris strlcpy(&ifrnit.ifr_name[0], ifname, IFNAMSIZ); 863229Spst 873229Spst nit = open("/dev/nit", 0); 883229Spst if (nit < 0) { 893229Spst report(LOG_ERR, "getether: open /dev/nit: %s", 903229Spst get_errmsg()); 913229Spst return rc; 923229Spst } 933229Spst do { 943229Spst if (ioctl(nit, NIOCBIND, &ifrnit) < 0) { 953229Spst report(LOG_ERR, "getether: NIOCBIND on nit"); 963229Spst break; 973229Spst } 983229Spst if (ioctl(nit, SIOCGIFADDR, &ifrnit) < 0) { 993229Spst report(LOG_ERR, "getether: SIOCGIFADDR on nit"); 1003229Spst break; 1013229Spst } 1023229Spst bcopy(&ifrnit.ifr_addr.sa_data[0], eap, EALEN); 1033229Spst rc = 0; 1043229Spst } while (0); 1053229Spst close(nit); 1063229Spst return rc; 1073229Spst} 1083229Spst 1093229Spst#define GETETHER 1103229Spst#endif /* SUNOS */ 1113229Spst 1123229Spst 1134131Sjkh#if defined(__FreeBSD__) || defined(__NetBSD__) 1143229Spst/* Thanks to John Brezak <brezak@ch.hp.com> for this code. */ 1153229Spst#include <sys/ioctl.h> 11620287Swollman#include <sys/time.h> 1173229Spst#include <net/if.h> 1183229Spst#include <net/if_dl.h> 1193229Spst#include <net/if_types.h> 1203229Spst 12184125Siedowseint 1223229Spstgetether(ifname, eap) 1233229Spst char *ifname; /* interface name from ifconfig structure */ 1243229Spst char *eap; /* Ether address (output) */ 1253229Spst{ 1263229Spst int fd, rc = -1; 1273229Spst register int n; 12884125Siedowse struct ifreq ibuf[16]; 1293229Spst struct ifconf ifc; 1303229Spst register struct ifreq *ifrp, *ifend; 1313229Spst 1323229Spst /* Fetch the interface configuration */ 1333229Spst fd = socket(AF_INET, SOCK_DGRAM, 0); 1343229Spst if (fd < 0) { 1353229Spst report(LOG_ERR, "getether: socket %s: %s", ifname, get_errmsg()); 1363229Spst return (fd); 1373229Spst } 1383229Spst ifc.ifc_len = sizeof(ibuf); 1393229Spst ifc.ifc_buf = (caddr_t) ibuf; 1403229Spst if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0 || 1413229Spst ifc.ifc_len < sizeof(struct ifreq)) { 14290159Skris report(LOG_ERR, "getether: SIOCGIFCONF: %s", get_errmsg()); 1433229Spst goto out; 1443229Spst } 1453229Spst /* Search interface configuration list for link layer address. */ 1463229Spst ifrp = ibuf; 1473229Spst ifend = (struct ifreq *) ((char *) ibuf + ifc.ifc_len); 1483229Spst while (ifrp < ifend) { 1493229Spst /* Look for interface */ 1503229Spst if (strcmp(ifname, ifrp->ifr_name) == 0 && 1513229Spst ifrp->ifr_addr.sa_family == AF_LINK && 1523229Spst ((struct sockaddr_dl *) &ifrp->ifr_addr)->sdl_type == IFT_ETHER) { 1533229Spst bcopy(LLADDR((struct sockaddr_dl *) &ifrp->ifr_addr), eap, EALEN); 1543229Spst rc = 0; 1553229Spst break; 1563229Spst } 1573229Spst /* Bump interface config pointer */ 1583229Spst n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); 1593229Spst if (n < sizeof(*ifrp)) 1603229Spst n = sizeof(*ifrp); 1613229Spst ifrp = (struct ifreq *) ((char *) ifrp + n); 1623229Spst } 1633229Spst 1643229Spst out: 1653229Spst close(fd); 1663229Spst return (rc); 1673229Spst} 1683229Spst 1693229Spst#define GETETHER 1703229Spst#endif /* __NetBSD__ */ 1713229Spst 1723229Spst 1733229Spst#ifdef SVR4 1743229Spst/* 1753229Spst * This is for "Streams TCP/IP" by Lachman Associates. 1763229Spst * They sure made this cumbersome! -gwr 1773229Spst */ 1783229Spst 1793229Spst#include <sys/sockio.h> 1803229Spst#include <sys/dlpi.h> 1813229Spst#include <stropts.h> 18213575Spst#include <string.h> 1833229Spst#ifndef NULL 1843229Spst#define NULL 0 1853229Spst#endif 1863229Spst 18713575Spstint 1883229Spstgetether(ifname, eap) 1893229Spst char *ifname; /* interface name from ifconfig structure */ 1903229Spst char *eap; /* Ether address (output) */ 1913229Spst{ 1923229Spst int rc = -1; 1933229Spst char devname[32]; 1943229Spst char tmpbuf[sizeof(union DL_primitives) + 16]; 1953229Spst struct strbuf cbuf; 1963229Spst int fd, flags; 1973229Spst union DL_primitives *dlp; 1983229Spst char *enaddr; 1993229Spst int unit = -1; /* which unit to attach */ 2003229Spst 20169793Sobrien snprintf(devname, sizeof(devname), "%s%s", _PATH_DEV, ifname); 2023229Spst fd = open(devname, 2); 2033229Spst if (fd < 0) { 2043229Spst /* Try without the trailing digit. */ 2053229Spst char *p = devname + 5; 2063229Spst while (isalpha(*p)) 2073229Spst p++; 2083229Spst if (isdigit(*p)) { 2093229Spst unit = *p - '0'; 2103229Spst *p = '\0'; 2113229Spst } 2123229Spst fd = open(devname, 2); 2133229Spst if (fd < 0) { 2143229Spst report(LOG_ERR, "getether: open %s: %s", 2153229Spst devname, get_errmsg()); 2163229Spst return rc; 2173229Spst } 2183229Spst } 2193229Spst#ifdef DL_ATTACH_REQ 2203229Spst /* 2213229Spst * If this is a "Style 2" DLPI, then we must "attach" first 2223229Spst * to tell the driver which unit (board, port) we want. 2233229Spst * For now, decide this based on the device name. 2243229Spst * (Should do "info_req" and check dl_provider_style ...) 2253229Spst */ 2263229Spst if (unit >= 0) { 2273229Spst memset(tmpbuf, 0, sizeof(tmpbuf)); 2283229Spst dlp = (union DL_primitives *) tmpbuf; 2293229Spst dlp->dl_primitive = DL_ATTACH_REQ; 2303229Spst dlp->attach_req.dl_ppa = unit; 2313229Spst cbuf.buf = tmpbuf; 2323229Spst cbuf.len = DL_ATTACH_REQ_SIZE; 2333229Spst if (putmsg(fd, &cbuf, NULL, 0) < 0) { 2343229Spst report(LOG_ERR, "getether: attach: putmsg: %s", get_errmsg()); 2353229Spst goto out; 2363229Spst } 2373229Spst /* Recv the ack. */ 2383229Spst cbuf.buf = tmpbuf; 2393229Spst cbuf.maxlen = sizeof(tmpbuf); 2403229Spst flags = 0; 2413229Spst if (getmsg(fd, &cbuf, NULL, &flags) < 0) { 2423229Spst report(LOG_ERR, "getether: attach: getmsg: %s", get_errmsg()); 2433229Spst goto out; 2443229Spst } 2453229Spst /* 2463229Spst * Check the type, etc. 2473229Spst */ 2483229Spst if (dlp->dl_primitive == DL_ERROR_ACK) { 2493229Spst report(LOG_ERR, "getether: attach: dlpi_errno=%d, unix_errno=%d", 2503229Spst dlp->error_ack.dl_errno, 2513229Spst dlp->error_ack.dl_unix_errno); 2523229Spst goto out; 2533229Spst } 2543229Spst if (dlp->dl_primitive != DL_OK_ACK) { 2553229Spst report(LOG_ERR, "getether: attach: not OK or ERROR"); 2563229Spst goto out; 2573229Spst } 2583229Spst } /* unit >= 0 */ 2593229Spst#endif /* DL_ATTACH_REQ */ 2603229Spst 2613229Spst /* 2623229Spst * Get the Ethernet address the same way the ARP module 2633229Spst * does when it is pushed onto a new stream (bind). 264108470Sschweikh * One should instead be able just do a dl_info_req 2653229Spst * but many drivers do not supply the hardware address 2663229Spst * in the response to dl_info_req (they MUST supply it 2673229Spst * for dl_bind_ack because the ARP module requires it). 2683229Spst */ 2693229Spst memset(tmpbuf, 0, sizeof(tmpbuf)); 2703229Spst dlp = (union DL_primitives *) tmpbuf; 2713229Spst dlp->dl_primitive = DL_BIND_REQ; 2723229Spst dlp->bind_req.dl_sap = 0x8FF; /* XXX - Unused SAP */ 2733229Spst cbuf.buf = tmpbuf; 2743229Spst cbuf.len = DL_BIND_REQ_SIZE; 2753229Spst if (putmsg(fd, &cbuf, NULL, 0) < 0) { 2763229Spst report(LOG_ERR, "getether: bind: putmsg: %s", get_errmsg()); 2773229Spst goto out; 2783229Spst } 2793229Spst /* Recv the ack. */ 2803229Spst cbuf.buf = tmpbuf; 2813229Spst cbuf.maxlen = sizeof(tmpbuf); 2823229Spst flags = 0; 2833229Spst if (getmsg(fd, &cbuf, NULL, &flags) < 0) { 2843229Spst report(LOG_ERR, "getether: bind: getmsg: %s", get_errmsg()); 2853229Spst goto out; 2863229Spst } 2873229Spst /* 2883229Spst * Check the type, etc. 2893229Spst */ 2903229Spst if (dlp->dl_primitive == DL_ERROR_ACK) { 2913229Spst report(LOG_ERR, "getether: bind: dlpi_errno=%d, unix_errno=%d", 2923229Spst dlp->error_ack.dl_errno, 2933229Spst dlp->error_ack.dl_unix_errno); 2943229Spst goto out; 2953229Spst } 2963229Spst if (dlp->dl_primitive != DL_BIND_ACK) { 2973229Spst report(LOG_ERR, "getether: bind: not OK or ERROR"); 2983229Spst goto out; 2993229Spst } 3003229Spst if (dlp->bind_ack.dl_addr_offset == 0) { 3013229Spst report(LOG_ERR, "getether: bind: ack has no address"); 3023229Spst goto out; 3033229Spst } 3043229Spst if (dlp->bind_ack.dl_addr_length < EALEN) { 3053229Spst report(LOG_ERR, "getether: bind: ack address truncated"); 3063229Spst goto out; 3073229Spst } 3083229Spst /* 3093229Spst * Copy the Ethernet address out of the message. 3103229Spst */ 3113229Spst enaddr = tmpbuf + dlp->bind_ack.dl_addr_offset; 3123229Spst memcpy(eap, enaddr, EALEN); 3133229Spst rc = 0; 3143229Spst 3153229Spst out: 3163229Spst close(fd); 3173229Spst return rc; 3183229Spst} 3193229Spst 3203229Spst#define GETETHER 3213229Spst#endif /* SVR4 */ 3223229Spst 3233229Spst 32413575Spst#ifdef __linux__ 3253229Spst/* 3263229Spst * This is really easy on Linux! This version (for linux) 32713575Spst * written by Nigel Metheringham <nigelm@ohm.york.ac.uk> and 32813575Spst * updated by Pauline Middelink <middelin@polyware.iaf.nl> 3293229Spst * 3303229Spst * The code is almost identical to the Ultrix code - however 3313229Spst * the names are different to confuse the innocent :-) 3323229Spst * Most of this code was stolen from the Ultrix bit above. 3333229Spst */ 3343229Spst 33513575Spst#include <memory.h> 3363229Spst#include <sys/ioctl.h> 3373229Spst#include <net/if.h> /* struct ifreq */ 33813575Spst#include <sys/socketio.h> /* Needed for IOCTL defs */ 3393229Spst 34013575Spstint 3413229Spstgetether(ifname, eap) 3423229Spst char *ifname, *eap; 3433229Spst{ 3443229Spst int rc = -1; 3453229Spst int fd; 3463229Spst struct ifreq phys; 34713575Spst 34813575Spst memset(&phys, 0, sizeof(phys)); 3493229Spst strcpy(phys.ifr_name, ifname); 3503229Spst if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 3513229Spst report(LOG_ERR, "getether: socket(INET,DGRAM) failed"); 3523229Spst return -1; 3533229Spst } 3543229Spst if (ioctl(fd, SIOCGIFHWADDR, &phys) < 0) { 3553229Spst report(LOG_ERR, "getether: ioctl SIOCGIFHWADDR failed"); 3563229Spst } else { 35713575Spst memcpy(eap, &phys.ifr_hwaddr.sa_data, EALEN); 3583229Spst rc = 0; 3593229Spst } 3603229Spst close(fd); 3613229Spst return rc; 3623229Spst} 3633229Spst 3643229Spst#define GETETHER 36513575Spst#endif /* __linux__ */ 3663229Spst 3673229Spst 3683229Spst/* If we don't know how on this system, just return an error. */ 3693229Spst#ifndef GETETHER 37013575Spstint 3713229Spstgetether(ifname, eap) 3723229Spst char *ifname, *eap; 3733229Spst{ 3743229Spst return -1; 3753229Spst} 3763229Spst 3773229Spst#endif /* !GETETHER */ 3783229Spst 3793229Spst/* 3803229Spst * Local Variables: 3813229Spst * tab-width: 4 3823229Spst * c-indent-level: 4 3833229Spst * c-argdecl-indent: 4 3843229Spst * c-continued-statement-offset: 4 3853229Spst * c-continued-brace-offset: -4 3863229Spst * c-label-offset: -4 3873229Spst * c-brace-offset: 0 3883229Spst * End: 3893229Spst */ 390