1 /*        $NetBSD: getether.c,v 1.10 2024/09/08 09:36:53 rillig Exp $ */
2 
3 #include <sys/cdefs.h>
4 #ifndef lint
5 __RCSID("$NetBSD: getether.c,v 1.10 2024/09/08 09:36:53 rillig 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 
30 extern 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 
45 int
getether(char * ifname,char * eap)46 getether(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) */
getether(char * ifname,char * eap)81 getether(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) */
126 int
getether(char * ifname,char * eap)127 getether(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) */
getether(char * ifname,char * eap)191 getether(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 a 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 
getether(char * ifname,char * eap)342 getether(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
getether(char * ifname,char * eap)370 getether(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