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