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