1/* $NetBSD: getether.c,v 1.8 2007/05/27 16:31:42 tls Exp $ */ 2 3#include <sys/cdefs.h> 4#ifndef lint 5__RCSID("$NetBSD: getether.c,v 1.8 2007/05/27 16:31:42 tls Exp $"); 6#endif 7 8/* 9 * getether.c : get the ethernet address of an interface 10 * 11 * All of this code is quite system-specific. As you may well 12 * guess, it took a good bit of detective work to figure out! 13 * 14 * If you figure out how to do this on another system, 15 * please let me know. <gwr@mc.com> 16 */ 17 18#include <sys/types.h> 19#include <sys/socket.h> 20 21#include <ctype.h> 22#include <string.h> 23#include <strings.h> 24#include <syslog.h> 25#include <unistd.h> 26 27#include "report.h" 28#define EALEN 6 29 30extern int getether(char *, char *); 31 32#if defined(ultrix) || (defined(__osf__) && defined(__alpha)) 33/* 34 * This is really easy on Ultrix! Thanks to 35 * Harald Lundberg <hl@tekla.fi> for this code. 36 * 37 * The code here is not specific to the Alpha, but that was the 38 * only symbol we could find to identify DEC's version of OSF. 39 * (Perhaps we should just define DEC in the Makefile... -gwr) 40 */ 41 42#include <sys/ioctl.h> 43#include <net/if.h> /* struct ifdevea */ 44 45int 46getether(char *ifname, char *eap) 47{ 48 int rc = -1; 49 int fd; 50 struct ifdevea phys; 51 52 bzero(&phys, sizeof(phys)); 53 strncpy(phys.ifr_name, ifname, sizeof(phys.ifr_name)); 54 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 55 report(LOG_ERR, "getether: socket(INET,DGRAM) failed"); 56 return -1; 57 } 58 if (ioctl(fd, SIOCRPHYSADDR, &phys) < 0) { 59 report(LOG_ERR, "getether: ioctl SIOCRPHYSADDR failed"); 60 } else { 61 bcopy(&phys.current_pa[0], eap, EALEN); 62 rc = 0; 63 } 64 close(fd); 65 return rc; 66} 67 68#define GETETHER 69#endif /* ultrix|osf1 */ 70 71 72#ifdef SUNOS 73 74#include <sys/sockio.h> 75#include <sys/time.h> /* needed by net_if.h */ 76#include <net/nit_if.h> /* for NIOCBIND */ 77#include <net/if.h> /* for struct ifreq */ 78 79/* ifname: interface name from ifconfig structure */ 80/* eap: Ether address (output) */ 81getether(char *ifname, char *eap) 82{ 83 int rc = -1; 84 85 struct ifreq ifrnit; 86 int nit; 87 88 bzero((char *) &ifrnit, sizeof(ifrnit)); 89 strncpy(&ifrnit.ifr_name[0], ifname, IFNAMSIZ); 90 91 nit = open("/dev/nit", 0); 92 if (nit < 0) { 93 report(LOG_ERR, "getether: open /dev/nit: %s", 94 get_errmsg()); 95 return rc; 96 } 97 do { 98 if (ioctl(nit, NIOCBIND, &ifrnit) < 0) { 99 report(LOG_ERR, "getether: NIOCBIND on nit"); 100 break; 101 } 102 if (ioctl(nit, SIOCGIFADDR, &ifrnit) < 0) { 103 report(LOG_ERR, "getether: SIOCGIFADDR on nit"); 104 break; 105 } 106 bcopy(&ifrnit.ifr_addr.sa_data[0], eap, EALEN); 107 rc = 0; 108 } while (0); 109 close(nit); 110 return rc; 111} 112 113#define GETETHER 114#endif /* SUNOS */ 115 116 117#if defined(__386BSD__) || defined(__NetBSD__) 118/* Thanks to John Brezak <brezak@ch.hp.com> for this code. */ 119#include <sys/ioctl.h> 120#include <net/if.h> 121#include <net/if_dl.h> 122#include <net/if_types.h> 123 124/* ifname: interface name from ifconfig structure */ 125/* eap: Ether address (output) */ 126int 127getether(char *ifname, char *eap) 128{ 129 int fd, rc = -1; 130 int n; 131 struct ifreq ibuf[16]; 132 struct ifconf ifc; 133 struct ifreq *ifrp, *ifend; 134 135 /* Fetch the interface configuration */ 136 fd = socket(AF_INET, SOCK_DGRAM, 0); 137 if (fd < 0) { 138 report(LOG_ERR, "getether: socket %s: %s", ifname, get_errmsg()); 139 return (fd); 140 } 141 ifc.ifc_len = sizeof(ibuf); 142 ifc.ifc_buf = (caddr_t) ibuf; 143 if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0 || 144 ifc.ifc_len < (int)sizeof(struct ifreq)) { 145 report(LOG_ERR, "getether: SIOCGIFCONF: %s", get_errmsg()); 146 goto out; 147 } 148 /* Search interface configuration list for link layer address. */ 149 ifrp = ibuf; 150 ifend = (struct ifreq *) ((char *) ibuf + ifc.ifc_len); 151 while (ifrp < ifend) { 152 /* Look for interface */ 153 if (strcmp(ifname, ifrp->ifr_name) == 0 && 154 ifrp->ifr_addr.sa_family == AF_LINK && 155 ((struct sockaddr_dl *) &ifrp->ifr_addr)->sdl_type == IFT_ETHER) { 156 bcopy(LLADDR((struct sockaddr_dl *) &ifrp->ifr_addr), eap, EALEN); 157 rc = 0; 158 break; 159 } 160 /* Bump interface config pointer */ 161 n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); 162 if (n < (int)sizeof(*ifrp)) 163 n = sizeof(*ifrp); 164 ifrp = (struct ifreq *) ((char *) ifrp + n); 165 } 166 167 out: 168 close(fd); 169 return (rc); 170} 171 172#define GETETHER 173#endif /* __NetBSD__ */ 174 175 176#ifdef SVR4 177/* 178 * This is for "Streams TCP/IP" by Lachman Associates. 179 * They sure made this cumbersome! -gwr 180 */ 181 182#include <sys/sockio.h> 183#include <sys/dlpi.h> 184#include <stropts.h> 185#ifndef NULL 186#define NULL 0 187#endif 188 189/* ifname: interface name from ifconfig structure */ 190/* eap: Ether address (output) */ 191getether(char *ifname, char *eap) 192{ 193 int rc = -1; 194 char devname[32]; 195 char tmpbuf[sizeof(union DL_primitives) + 16]; 196 struct strbuf cbuf; 197 int fd, flags; 198 union DL_primitives *dlp; 199 char *enaddr; 200 int unit = -1; /* which unit to attach */ 201 202 snprintf(devname, sizeof(devname), "/dev/%s", ifname); 203 fd = open(devname, 2); 204 if (fd < 0) { 205 /* Try without the trailing digit. */ 206 char *p = devname + 5; 207 while (isalpha(*p)) 208 p++; 209 if (isdigit(*p)) { 210 unit = *p - '0'; 211 *p = '\0'; 212 } 213 fd = open(devname, 2); 214 if (fd < 0) { 215 report(LOG_ERR, "getether: open %s: %s", 216 devname, get_errmsg()); 217 return rc; 218 } 219 } 220#ifdef DL_ATTACH_REQ 221 /* 222 * If this is a "Style 2" DLPI, then we must "attach" first 223 * to tell the driver which unit (board, port) we want. 224 * For now, decide this based on the device name. 225 * (Should do "info_req" and check dl_provider_style ...) 226 */ 227 if (unit >= 0) { 228 memset(tmpbuf, 0, sizeof(tmpbuf)); 229 dlp = (union DL_primitives *) tmpbuf; 230 dlp->dl_primitive = DL_ATTACH_REQ; 231 dlp->attach_req.dl_ppa = unit; 232 cbuf.buf = tmpbuf; 233 cbuf.len = DL_ATTACH_REQ_SIZE; 234 if (putmsg(fd, &cbuf, NULL, 0) < 0) { 235 report(LOG_ERR, "getether: attach: putmsg: %s", get_errmsg()); 236 goto out; 237 } 238 /* Recv the ack. */ 239 cbuf.buf = tmpbuf; 240 cbuf.maxlen = sizeof(tmpbuf); 241 flags = 0; 242 if (getmsg(fd, &cbuf, NULL, &flags) < 0) { 243 report(LOG_ERR, "getether: attach: getmsg: %s", get_errmsg()); 244 goto out; 245 } 246 /* 247 * Check the type, etc. 248 */ 249 if (dlp->dl_primitive == DL_ERROR_ACK) { 250 report(LOG_ERR, "getether: attach: dlpi_errno=%d, unix_errno=%d", 251 dlp->error_ack.dl_errno, 252 dlp->error_ack.dl_unix_errno); 253 goto out; 254 } 255 if (dlp->dl_primitive != DL_OK_ACK) { 256 report(LOG_ERR, "getether: attach: not OK or ERROR"); 257 goto out; 258 } 259 } /* unit >= 0 */ 260#endif /* DL_ATTACH_REQ */ 261 262 /* 263 * Get the Ethernet address the same way the ARP module 264 * does when it is pushed onto a new stream (bind). 265 * One should instead be able just do an dl_info_req 266 * but many drivers do not supply the hardware address 267 * in the response to dl_info_req (they MUST supply it 268 * for dl_bind_ack because the ARP module requires it). 269 */ 270 memset(tmpbuf, 0, sizeof(tmpbuf)); 271 dlp = (union DL_primitives *) tmpbuf; 272 dlp->dl_primitive = DL_BIND_REQ; 273 dlp->bind_req.dl_sap = 0x8FF; /* XXX - Unused SAP */ 274 cbuf.buf = tmpbuf; 275 cbuf.len = DL_BIND_REQ_SIZE; 276 if (putmsg(fd, &cbuf, NULL, 0) < 0) { 277 report(LOG_ERR, "getether: bind: putmsg: %s", get_errmsg()); 278 goto out; 279 } 280 /* Recv the ack. */ 281 cbuf.buf = tmpbuf; 282 cbuf.maxlen = sizeof(tmpbuf); 283 flags = 0; 284 if (getmsg(fd, &cbuf, NULL, &flags) < 0) { 285 report(LOG_ERR, "getether: bind: getmsg: %s", get_errmsg()); 286 goto out; 287 } 288 /* 289 * Check the type, etc. 290 */ 291 if (dlp->dl_primitive == DL_ERROR_ACK) { 292 report(LOG_ERR, "getether: bind: dlpi_errno=%d, unix_errno=%d", 293 dlp->error_ack.dl_errno, 294 dlp->error_ack.dl_unix_errno); 295 goto out; 296 } 297 if (dlp->dl_primitive != DL_BIND_ACK) { 298 report(LOG_ERR, "getether: bind: not OK or ERROR"); 299 goto out; 300 } 301 if (dlp->bind_ack.dl_addr_offset == 0) { 302 report(LOG_ERR, "getether: bind: ack has no address"); 303 goto out; 304 } 305 if (dlp->bind_ack.dl_addr_length < EALEN) { 306 report(LOG_ERR, "getether: bind: ack address truncated"); 307 goto out; 308 } 309 /* 310 * Copy the Ethernet address out of the message. 311 */ 312 enaddr = tmpbuf + dlp->bind_ack.dl_addr_offset; 313 memcpy(eap, enaddr, EALEN); 314 rc = 0; 315 316 out: 317 close(fd); 318 return rc; 319} 320 321#define GETETHER 322#endif /* SVR4 */ 323 324 325#ifdef linux 326/* 327 * This is really easy on Linux! This version (for linux) 328 * written by Nigel Metheringham <nigelm@ohm.york.ac.uk> 329 * 330 * The code is almost identical to the Ultrix code - however 331 * the names are different to confuse the innocent :-) 332 * Most of this code was stolen from the Ultrix bit above. 333 */ 334 335#include <sys/ioctl.h> 336#include <net/if.h> /* struct ifreq */ 337 338/* In a properly configured system this should be either sys/socketio.h 339 or sys/sockios.h, but on my distribution these don't line up correctly */ 340#include <linux/sockios.h> /* Needed for IOCTL defs */ 341 342getether(char *ifname, char *eap) 343{ 344 int rc = -1; 345 int fd; 346 struct ifreq phys; 347 348 bzero(&phys, sizeof(phys)); 349 strncpy(phys.ifr_name, ifname, sizeof(phys.ifr_name)); 350 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 351 report(LOG_ERR, "getether: socket(INET,DGRAM) failed"); 352 return -1; 353 } 354 if (ioctl(fd, SIOCGIFHWADDR, &phys) < 0) { 355 report(LOG_ERR, "getether: ioctl SIOCGIFHWADDR failed"); 356 } else { 357 bcopy(phys.ifr_hwaddr, eap, EALEN); 358 rc = 0; 359 } 360 close(fd); 361 return rc; 362} 363 364#define GETETHER 365#endif /* linux */ 366 367 368/* If we don't know how on this system, just return an error. */ 369#ifndef GETETHER 370getether(char *ifname, char *eap) 371{ 372 return -1; 373} 374 375#endif /* !GETETHER */ 376 377/* 378 * Local Variables: 379 * tab-width: 4 380 * c-indent-level: 4 381 * c-argdecl-indent: 4 382 * c-continued-statement-offset: 4 383 * c-continued-brace-offset: -4 384 * c-label-offset: -4 385 * c-brace-offset: 0 386 * End: 387 */ 388