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