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