1*5b28f239Srillig /* $NetBSD: getether.c,v 1.10 2024/09/08 09:36:53 rillig Exp $ */ 29493bb79Slukem 39493bb79Slukem #include <sys/cdefs.h> 49493bb79Slukem #ifndef lint 5*5b28f239Srillig __RCSID("$NetBSD: getether.c,v 1.10 2024/09/08 09:36:53 rillig Exp $"); 69493bb79Slukem #endif 73fe138c1Sperry 85e9d01f2Sgwr /* 95e9d01f2Sgwr * getether.c : get the ethernet address of an interface 105e9d01f2Sgwr * 115e9d01f2Sgwr * All of this code is quite system-specific. As you may well 125e9d01f2Sgwr * guess, it took a good bit of detective work to figure out! 135e9d01f2Sgwr * 145e9d01f2Sgwr * If you figure out how to do this on another system, 155e9d01f2Sgwr * please let me know. <gwr@mc.com> 165e9d01f2Sgwr */ 175e9d01f2Sgwr 185e9d01f2Sgwr #include <sys/types.h> 195e9d01f2Sgwr #include <sys/socket.h> 205e9d01f2Sgwr 215e9d01f2Sgwr #include <ctype.h> 229493bb79Slukem #include <string.h> 23be45f4d0Stls #include <strings.h> 245e9d01f2Sgwr #include <syslog.h> 259493bb79Slukem #include <unistd.h> 265e9d01f2Sgwr 275e9d01f2Sgwr #include "report.h" 285e9d01f2Sgwr #define EALEN 6 295e9d01f2Sgwr 30131109e4Swiz extern int getether(char *, char *); 319493bb79Slukem 325e9d01f2Sgwr #if defined(ultrix) || (defined(__osf__) && defined(__alpha)) 335e9d01f2Sgwr /* 345e9d01f2Sgwr * This is really easy on Ultrix! Thanks to 355e9d01f2Sgwr * Harald Lundberg <hl@tekla.fi> for this code. 365e9d01f2Sgwr * 375e9d01f2Sgwr * The code here is not specific to the Alpha, but that was the 385e9d01f2Sgwr * only symbol we could find to identify DEC's version of OSF. 395e9d01f2Sgwr * (Perhaps we should just define DEC in the Makefile... -gwr) 405e9d01f2Sgwr */ 415e9d01f2Sgwr 425e9d01f2Sgwr #include <sys/ioctl.h> 435e9d01f2Sgwr #include <net/if.h> /* struct ifdevea */ 445e9d01f2Sgwr 459493bb79Slukem int 46131109e4Swiz getether(char *ifname, char *eap) 475e9d01f2Sgwr { 485e9d01f2Sgwr int rc = -1; 495e9d01f2Sgwr int fd; 505e9d01f2Sgwr struct ifdevea phys; 5147a6a58fSitojun 525e9d01f2Sgwr bzero(&phys, sizeof(phys)); 5347a6a58fSitojun strncpy(phys.ifr_name, ifname, sizeof(phys.ifr_name)); 545e9d01f2Sgwr if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 555e9d01f2Sgwr report(LOG_ERR, "getether: socket(INET,DGRAM) failed"); 565e9d01f2Sgwr return -1; 575e9d01f2Sgwr } 585e9d01f2Sgwr if (ioctl(fd, SIOCRPHYSADDR, &phys) < 0) { 595e9d01f2Sgwr report(LOG_ERR, "getether: ioctl SIOCRPHYSADDR failed"); 605e9d01f2Sgwr } else { 615e9d01f2Sgwr bcopy(&phys.current_pa[0], eap, EALEN); 625e9d01f2Sgwr rc = 0; 635e9d01f2Sgwr } 645e9d01f2Sgwr close(fd); 655e9d01f2Sgwr return rc; 665e9d01f2Sgwr } 675e9d01f2Sgwr 685e9d01f2Sgwr #define GETETHER 695e9d01f2Sgwr #endif /* ultrix|osf1 */ 705e9d01f2Sgwr 715e9d01f2Sgwr 725e9d01f2Sgwr #ifdef SUNOS 735e9d01f2Sgwr 745e9d01f2Sgwr #include <sys/sockio.h> 755e9d01f2Sgwr #include <sys/time.h> /* needed by net_if.h */ 765e9d01f2Sgwr #include <net/nit_if.h> /* for NIOCBIND */ 775e9d01f2Sgwr #include <net/if.h> /* for struct ifreq */ 785e9d01f2Sgwr 79131109e4Swiz /* ifname: interface name from ifconfig structure */ 80131109e4Swiz /* eap: Ether address (output) */ 81131109e4Swiz getether(char *ifname, char *eap) 825e9d01f2Sgwr { 835e9d01f2Sgwr int rc = -1; 845e9d01f2Sgwr 855e9d01f2Sgwr struct ifreq ifrnit; 865e9d01f2Sgwr int nit; 875e9d01f2Sgwr 885e9d01f2Sgwr bzero((char *) &ifrnit, sizeof(ifrnit)); 895e9d01f2Sgwr strncpy(&ifrnit.ifr_name[0], ifname, IFNAMSIZ); 905e9d01f2Sgwr 915e9d01f2Sgwr nit = open("/dev/nit", 0); 925e9d01f2Sgwr if (nit < 0) { 935e9d01f2Sgwr report(LOG_ERR, "getether: open /dev/nit: %s", 945e9d01f2Sgwr get_errmsg()); 955e9d01f2Sgwr return rc; 965e9d01f2Sgwr } 975e9d01f2Sgwr do { 985e9d01f2Sgwr if (ioctl(nit, NIOCBIND, &ifrnit) < 0) { 995e9d01f2Sgwr report(LOG_ERR, "getether: NIOCBIND on nit"); 1005e9d01f2Sgwr break; 1015e9d01f2Sgwr } 1025e9d01f2Sgwr if (ioctl(nit, SIOCGIFADDR, &ifrnit) < 0) { 1035e9d01f2Sgwr report(LOG_ERR, "getether: SIOCGIFADDR on nit"); 1045e9d01f2Sgwr break; 1055e9d01f2Sgwr } 1065e9d01f2Sgwr bcopy(&ifrnit.ifr_addr.sa_data[0], eap, EALEN); 1075e9d01f2Sgwr rc = 0; 1085e9d01f2Sgwr } while (0); 1095e9d01f2Sgwr close(nit); 1105e9d01f2Sgwr return rc; 1115e9d01f2Sgwr } 1125e9d01f2Sgwr 1135e9d01f2Sgwr #define GETETHER 1145e9d01f2Sgwr #endif /* SUNOS */ 1155e9d01f2Sgwr 1165e9d01f2Sgwr 1175e9d01f2Sgwr #if defined(__386BSD__) || defined(__NetBSD__) 1185e9d01f2Sgwr /* Thanks to John Brezak <brezak@ch.hp.com> for this code. */ 1195e9d01f2Sgwr #include <sys/ioctl.h> 1205e9d01f2Sgwr #include <net/if.h> 1215e9d01f2Sgwr #include <net/if_dl.h> 1225e9d01f2Sgwr #include <net/if_types.h> 1235e9d01f2Sgwr 124131109e4Swiz /* ifname: interface name from ifconfig structure */ 125131109e4Swiz /* eap: Ether address (output) */ 1269493bb79Slukem int 127131109e4Swiz getether(char *ifname, char *eap) 1285e9d01f2Sgwr { 1295e9d01f2Sgwr int fd, rc = -1; 130aae9c2a0Swiz int n; 1319493bb79Slukem struct ifreq ibuf[16]; 1325e9d01f2Sgwr struct ifconf ifc; 133aae9c2a0Swiz struct ifreq *ifrp, *ifend; 1345e9d01f2Sgwr 1355e9d01f2Sgwr /* Fetch the interface configuration */ 1365e9d01f2Sgwr fd = socket(AF_INET, SOCK_DGRAM, 0); 1375e9d01f2Sgwr if (fd < 0) { 1385e9d01f2Sgwr report(LOG_ERR, "getether: socket %s: %s", ifname, get_errmsg()); 1395e9d01f2Sgwr return (fd); 1405e9d01f2Sgwr } 1415e9d01f2Sgwr ifc.ifc_len = sizeof(ibuf); 1425e9d01f2Sgwr ifc.ifc_buf = (caddr_t) ibuf; 1435e9d01f2Sgwr if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0 || 1447affd655Slukem ifc.ifc_len < (int)sizeof(struct ifreq)) { 145d8302e2dSis report(LOG_ERR, "getether: SIOCGIFCONF: %s", get_errmsg()); 1465e9d01f2Sgwr goto out; 1475e9d01f2Sgwr } 1485e9d01f2Sgwr /* Search interface configuration list for link layer address. */ 1495e9d01f2Sgwr ifrp = ibuf; 1505e9d01f2Sgwr ifend = (struct ifreq *) ((char *) ibuf + ifc.ifc_len); 1515e9d01f2Sgwr while (ifrp < ifend) { 1525e9d01f2Sgwr /* Look for interface */ 1535e9d01f2Sgwr if (strcmp(ifname, ifrp->ifr_name) == 0 && 1545e9d01f2Sgwr ifrp->ifr_addr.sa_family == AF_LINK && 1555e9d01f2Sgwr ((struct sockaddr_dl *) &ifrp->ifr_addr)->sdl_type == IFT_ETHER) { 1565e9d01f2Sgwr bcopy(LLADDR((struct sockaddr_dl *) &ifrp->ifr_addr), eap, EALEN); 1575e9d01f2Sgwr rc = 0; 1585e9d01f2Sgwr break; 1595e9d01f2Sgwr } 1605e9d01f2Sgwr /* Bump interface config pointer */ 1615e9d01f2Sgwr n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); 1627affd655Slukem if (n < (int)sizeof(*ifrp)) 1635e9d01f2Sgwr n = sizeof(*ifrp); 1645e9d01f2Sgwr ifrp = (struct ifreq *) ((char *) ifrp + n); 1655e9d01f2Sgwr } 1665e9d01f2Sgwr 1675e9d01f2Sgwr out: 1685e9d01f2Sgwr close(fd); 1695e9d01f2Sgwr return (rc); 1705e9d01f2Sgwr } 1715e9d01f2Sgwr 1725e9d01f2Sgwr #define GETETHER 1735e9d01f2Sgwr #endif /* __NetBSD__ */ 1745e9d01f2Sgwr 1755e9d01f2Sgwr 1765e9d01f2Sgwr #ifdef SVR4 1775e9d01f2Sgwr /* 1785e9d01f2Sgwr * This is for "Streams TCP/IP" by Lachman Associates. 1795e9d01f2Sgwr * They sure made this cumbersome! -gwr 1805e9d01f2Sgwr */ 1815e9d01f2Sgwr 1825e9d01f2Sgwr #include <sys/sockio.h> 1835e9d01f2Sgwr #include <sys/dlpi.h> 1845e9d01f2Sgwr #include <stropts.h> 1855e9d01f2Sgwr #ifndef NULL 1865e9d01f2Sgwr #define NULL 0 1875e9d01f2Sgwr #endif 1885e9d01f2Sgwr 189131109e4Swiz /* ifname: interface name from ifconfig structure */ 190131109e4Swiz /* eap: Ether address (output) */ 191131109e4Swiz getether(char *ifname, char *eap) 1925e9d01f2Sgwr { 1935e9d01f2Sgwr int rc = -1; 1945e9d01f2Sgwr char devname[32]; 1955e9d01f2Sgwr char tmpbuf[sizeof(union DL_primitives) + 16]; 1965e9d01f2Sgwr struct strbuf cbuf; 1975e9d01f2Sgwr int fd, flags; 1985e9d01f2Sgwr union DL_primitives *dlp; 1995e9d01f2Sgwr char *enaddr; 2005e9d01f2Sgwr int unit = -1; /* which unit to attach */ 2015e9d01f2Sgwr 20247a6a58fSitojun snprintf(devname, sizeof(devname), "/dev/%s", ifname); 2035e9d01f2Sgwr fd = open(devname, 2); 2045e9d01f2Sgwr if (fd < 0) { 2055e9d01f2Sgwr /* Try without the trailing digit. */ 2065e9d01f2Sgwr char *p = devname + 5; 2075e9d01f2Sgwr while (isalpha(*p)) 2085e9d01f2Sgwr p++; 2095e9d01f2Sgwr if (isdigit(*p)) { 2105e9d01f2Sgwr unit = *p - '0'; 2115e9d01f2Sgwr *p = '\0'; 2125e9d01f2Sgwr } 2135e9d01f2Sgwr fd = open(devname, 2); 2145e9d01f2Sgwr if (fd < 0) { 2155e9d01f2Sgwr report(LOG_ERR, "getether: open %s: %s", 2165e9d01f2Sgwr devname, get_errmsg()); 2175e9d01f2Sgwr return rc; 2185e9d01f2Sgwr } 2195e9d01f2Sgwr } 2205e9d01f2Sgwr #ifdef DL_ATTACH_REQ 2215e9d01f2Sgwr /* 2225e9d01f2Sgwr * If this is a "Style 2" DLPI, then we must "attach" first 2235e9d01f2Sgwr * to tell the driver which unit (board, port) we want. 2245e9d01f2Sgwr * For now, decide this based on the device name. 2255e9d01f2Sgwr * (Should do "info_req" and check dl_provider_style ...) 2265e9d01f2Sgwr */ 2275e9d01f2Sgwr if (unit >= 0) { 2285e9d01f2Sgwr memset(tmpbuf, 0, sizeof(tmpbuf)); 2295e9d01f2Sgwr dlp = (union DL_primitives *) tmpbuf; 2305e9d01f2Sgwr dlp->dl_primitive = DL_ATTACH_REQ; 2315e9d01f2Sgwr dlp->attach_req.dl_ppa = unit; 2325e9d01f2Sgwr cbuf.buf = tmpbuf; 2335e9d01f2Sgwr cbuf.len = DL_ATTACH_REQ_SIZE; 2345e9d01f2Sgwr if (putmsg(fd, &cbuf, NULL, 0) < 0) { 2355e9d01f2Sgwr report(LOG_ERR, "getether: attach: putmsg: %s", get_errmsg()); 2365e9d01f2Sgwr goto out; 2375e9d01f2Sgwr } 2385e9d01f2Sgwr /* Recv the ack. */ 2395e9d01f2Sgwr cbuf.buf = tmpbuf; 2405e9d01f2Sgwr cbuf.maxlen = sizeof(tmpbuf); 2415e9d01f2Sgwr flags = 0; 2425e9d01f2Sgwr if (getmsg(fd, &cbuf, NULL, &flags) < 0) { 2435e9d01f2Sgwr report(LOG_ERR, "getether: attach: getmsg: %s", get_errmsg()); 2445e9d01f2Sgwr goto out; 2455e9d01f2Sgwr } 2465e9d01f2Sgwr /* 2475e9d01f2Sgwr * Check the type, etc. 2485e9d01f2Sgwr */ 2495e9d01f2Sgwr if (dlp->dl_primitive == DL_ERROR_ACK) { 2505e9d01f2Sgwr report(LOG_ERR, "getether: attach: dlpi_errno=%d, unix_errno=%d", 2515e9d01f2Sgwr dlp->error_ack.dl_errno, 2525e9d01f2Sgwr dlp->error_ack.dl_unix_errno); 2535e9d01f2Sgwr goto out; 2545e9d01f2Sgwr } 2555e9d01f2Sgwr if (dlp->dl_primitive != DL_OK_ACK) { 2565e9d01f2Sgwr report(LOG_ERR, "getether: attach: not OK or ERROR"); 2575e9d01f2Sgwr goto out; 2585e9d01f2Sgwr } 2595e9d01f2Sgwr } /* unit >= 0 */ 2605e9d01f2Sgwr #endif /* DL_ATTACH_REQ */ 2615e9d01f2Sgwr 2625e9d01f2Sgwr /* 2635e9d01f2Sgwr * Get the Ethernet address the same way the ARP module 2645e9d01f2Sgwr * does when it is pushed onto a new stream (bind). 265*5b28f239Srillig * One should instead be able just do a dl_info_req 2665e9d01f2Sgwr * but many drivers do not supply the hardware address 2675e9d01f2Sgwr * in the response to dl_info_req (they MUST supply it 2685e9d01f2Sgwr * for dl_bind_ack because the ARP module requires it). 2695e9d01f2Sgwr */ 2705e9d01f2Sgwr memset(tmpbuf, 0, sizeof(tmpbuf)); 2715e9d01f2Sgwr dlp = (union DL_primitives *) tmpbuf; 2725e9d01f2Sgwr dlp->dl_primitive = DL_BIND_REQ; 2735e9d01f2Sgwr dlp->bind_req.dl_sap = 0x8FF; /* XXX - Unused SAP */ 2745e9d01f2Sgwr cbuf.buf = tmpbuf; 2755e9d01f2Sgwr cbuf.len = DL_BIND_REQ_SIZE; 2765e9d01f2Sgwr if (putmsg(fd, &cbuf, NULL, 0) < 0) { 2775e9d01f2Sgwr report(LOG_ERR, "getether: bind: putmsg: %s", get_errmsg()); 2785e9d01f2Sgwr goto out; 2795e9d01f2Sgwr } 2805e9d01f2Sgwr /* Recv the ack. */ 2815e9d01f2Sgwr cbuf.buf = tmpbuf; 2825e9d01f2Sgwr cbuf.maxlen = sizeof(tmpbuf); 2835e9d01f2Sgwr flags = 0; 2845e9d01f2Sgwr if (getmsg(fd, &cbuf, NULL, &flags) < 0) { 2855e9d01f2Sgwr report(LOG_ERR, "getether: bind: getmsg: %s", get_errmsg()); 2865e9d01f2Sgwr goto out; 2875e9d01f2Sgwr } 2885e9d01f2Sgwr /* 2895e9d01f2Sgwr * Check the type, etc. 2905e9d01f2Sgwr */ 2915e9d01f2Sgwr if (dlp->dl_primitive == DL_ERROR_ACK) { 2925e9d01f2Sgwr report(LOG_ERR, "getether: bind: dlpi_errno=%d, unix_errno=%d", 2935e9d01f2Sgwr dlp->error_ack.dl_errno, 2945e9d01f2Sgwr dlp->error_ack.dl_unix_errno); 2955e9d01f2Sgwr goto out; 2965e9d01f2Sgwr } 2975e9d01f2Sgwr if (dlp->dl_primitive != DL_BIND_ACK) { 2985e9d01f2Sgwr report(LOG_ERR, "getether: bind: not OK or ERROR"); 2995e9d01f2Sgwr goto out; 3005e9d01f2Sgwr } 3015e9d01f2Sgwr if (dlp->bind_ack.dl_addr_offset == 0) { 3025e9d01f2Sgwr report(LOG_ERR, "getether: bind: ack has no address"); 3035e9d01f2Sgwr goto out; 3045e9d01f2Sgwr } 3055e9d01f2Sgwr if (dlp->bind_ack.dl_addr_length < EALEN) { 3065e9d01f2Sgwr report(LOG_ERR, "getether: bind: ack address truncated"); 3075e9d01f2Sgwr goto out; 3085e9d01f2Sgwr } 3095e9d01f2Sgwr /* 3105e9d01f2Sgwr * Copy the Ethernet address out of the message. 3115e9d01f2Sgwr */ 3125e9d01f2Sgwr enaddr = tmpbuf + dlp->bind_ack.dl_addr_offset; 3135e9d01f2Sgwr memcpy(eap, enaddr, EALEN); 3145e9d01f2Sgwr rc = 0; 3155e9d01f2Sgwr 3165e9d01f2Sgwr out: 3175e9d01f2Sgwr close(fd); 3185e9d01f2Sgwr return rc; 3195e9d01f2Sgwr } 3205e9d01f2Sgwr 3215e9d01f2Sgwr #define GETETHER 3225e9d01f2Sgwr #endif /* SVR4 */ 3235e9d01f2Sgwr 3245e9d01f2Sgwr 3255e9d01f2Sgwr #ifdef linux 3265e9d01f2Sgwr /* 3275e9d01f2Sgwr * This is really easy on Linux! This version (for linux) 3285e9d01f2Sgwr * written by Nigel Metheringham <nigelm@ohm.york.ac.uk> 3295e9d01f2Sgwr * 3305e9d01f2Sgwr * The code is almost identical to the Ultrix code - however 3315e9d01f2Sgwr * the names are different to confuse the innocent :-) 3325e9d01f2Sgwr * Most of this code was stolen from the Ultrix bit above. 3335e9d01f2Sgwr */ 3345e9d01f2Sgwr 3355e9d01f2Sgwr #include <sys/ioctl.h> 3365e9d01f2Sgwr #include <net/if.h> /* struct ifreq */ 3375e9d01f2Sgwr 3385e9d01f2Sgwr /* In a properly configured system this should be either sys/socketio.h 3395e9d01f2Sgwr or sys/sockios.h, but on my distribution these don't line up correctly */ 3405e9d01f2Sgwr #include <linux/sockios.h> /* Needed for IOCTL defs */ 3415e9d01f2Sgwr 342131109e4Swiz getether(char *ifname, char *eap) 3435e9d01f2Sgwr { 3445e9d01f2Sgwr int rc = -1; 3455e9d01f2Sgwr int fd; 3465e9d01f2Sgwr struct ifreq phys; 34747a6a58fSitojun 3485e9d01f2Sgwr bzero(&phys, sizeof(phys)); 34947a6a58fSitojun strncpy(phys.ifr_name, ifname, sizeof(phys.ifr_name)); 3505e9d01f2Sgwr if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 3515e9d01f2Sgwr report(LOG_ERR, "getether: socket(INET,DGRAM) failed"); 3525e9d01f2Sgwr return -1; 3535e9d01f2Sgwr } 3545e9d01f2Sgwr if (ioctl(fd, SIOCGIFHWADDR, &phys) < 0) { 3555e9d01f2Sgwr report(LOG_ERR, "getether: ioctl SIOCGIFHWADDR failed"); 3565e9d01f2Sgwr } else { 3575e9d01f2Sgwr bcopy(phys.ifr_hwaddr, eap, EALEN); 3585e9d01f2Sgwr rc = 0; 3595e9d01f2Sgwr } 3605e9d01f2Sgwr close(fd); 3615e9d01f2Sgwr return rc; 3625e9d01f2Sgwr } 3635e9d01f2Sgwr 3645e9d01f2Sgwr #define GETETHER 3655e9d01f2Sgwr #endif /* linux */ 3665e9d01f2Sgwr 3675e9d01f2Sgwr 3685e9d01f2Sgwr /* If we don't know how on this system, just return an error. */ 3695e9d01f2Sgwr #ifndef GETETHER 370131109e4Swiz getether(char *ifname, char *eap) 3715e9d01f2Sgwr { 3725e9d01f2Sgwr return -1; 3735e9d01f2Sgwr } 3745e9d01f2Sgwr 3755e9d01f2Sgwr #endif /* !GETETHER */ 3765e9d01f2Sgwr 3775e9d01f2Sgwr /* 3785e9d01f2Sgwr * Local Variables: 3795e9d01f2Sgwr * tab-width: 4 3805e9d01f2Sgwr * c-indent-level: 4 3815e9d01f2Sgwr * c-argdecl-indent: 4 3825e9d01f2Sgwr * c-continued-statement-offset: 4 3835e9d01f2Sgwr * c-continued-brace-offset: -4 3845e9d01f2Sgwr * c-label-offset: -4 3855e9d01f2Sgwr * c-brace-offset: 0 3865e9d01f2Sgwr * End: 3875e9d01f2Sgwr */ 388