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