xref: /dragonfly/libexec/rbootd/bpf.c (revision e62ef63c)
1 /*
2  * Copyright (c) 1988, 1992 The University of Utah and the Center
3  *	for Software Science (CSS).
4  * Copyright (c) 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * the Center for Software Science of the University of Utah Computer
9  * Science Department.  CSS requests users of this software to return
10  * to css-dist@cs.utah.edu any improvements that they make and grant
11  * CSS redistribution rights.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	from: @(#)bpf.c	8.1 (Berkeley) 6/4/93
38  *
39  * From: Utah Hdr: bpf.c 3.1 92/07/06
40  * Author: Jeff Forys, University of Utah CSS
41  *
42  * @(#)bpf.c	8.1 (Berkeley) 6/4/93
43  * $FreeBSD: src/libexec/rbootd/bpf.c,v 1.10 1999/08/28 00:09:44 peter Exp $
44  */
45 
46 #include <sys/param.h>
47 #include <sys/ioctl.h>
48 #include <sys/socket.h>
49 #include <sys/time.h>
50 
51 #include <net/if.h>
52 #include <net/bpf.h>
53 
54 #include <ctype.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <syslog.h>
61 #include <unistd.h>
62 #include "defs.h"
63 #include "pathnames.h"
64 
65 static int BpfFd = -1;
66 static unsigned BpfLen = 0;
67 static u_int8_t *BpfPkt = NULL;
68 
69 /*
70 **  BpfOpen -- Open and initialize a BPF device.
71 **
72 **	Parameters:
73 **		None.
74 **
75 **	Returns:
76 **		File descriptor of opened BPF device (for select() etc).
77 **
78 **	Side Effects:
79 **		If an error is encountered, the program terminates here.
80 */
81 int
82 BpfOpen(void)
83 {
84 	struct ifreq ifr;
85 	char bpfdev[32];
86 	int n = 0;
87 
88 	/*
89 	 *  Open the first available BPF device.
90 	 */
91 	do {
92 		(void) sprintf(bpfdev, _PATH_BPF, n++);
93 		BpfFd = open(bpfdev, O_RDWR);
94 	} while (BpfFd < 0 && (errno == EBUSY || errno == EPERM));
95 
96 	if (BpfFd < 0) {
97 		syslog(LOG_ERR, "bpf: no available devices: %m");
98 		Exit(0);
99 	}
100 
101 	/*
102 	 *  Set interface name for bpf device, get data link layer
103 	 *  type and make sure it's type Ethernet.
104 	 */
105 	(void) strncpy(ifr.ifr_name, IntfName, sizeof(ifr.ifr_name));
106 	if (ioctl(BpfFd, BIOCSETIF, (caddr_t)&ifr) < 0) {
107 		syslog(LOG_ERR, "bpf: ioctl(BIOCSETIF,%s): %m", IntfName);
108 		Exit(0);
109 	}
110 
111 	/*
112 	 *  Make sure we are dealing with an Ethernet device.
113 	 */
114 	if (ioctl(BpfFd, BIOCGDLT, (caddr_t)&n) < 0) {
115 		syslog(LOG_ERR, "bpf: ioctl(BIOCGDLT): %m");
116 		Exit(0);
117 	}
118 	if (n != DLT_EN10MB) {
119 		syslog(LOG_ERR,"bpf: %s: data-link type %d unsupported",
120 		       IntfName, n);
121 		Exit(0);
122 	}
123 
124 	/*
125 	 *  On read(), return packets immediately (do not buffer them).
126 	 */
127 	n = 1;
128 	if (ioctl(BpfFd, BIOCIMMEDIATE, (caddr_t)&n) < 0) {
129 		syslog(LOG_ERR, "bpf: ioctl(BIOCIMMEDIATE): %m");
130 		Exit(0);
131 	}
132 
133 	/*
134 	 *  Try to enable the chip/driver's multicast address filter to
135 	 *  grab our RMP address.  If this fails, try promiscuous mode.
136 	 *  If this fails, there's no way we are going to get any RMP
137 	 *  packets so just exit here.
138 	 */
139 #ifdef MSG_EOR
140 	ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2;
141 #endif
142 	ifr.ifr_addr.sa_family = AF_UNSPEC;
143 	memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[0], RMP_ADDRLEN);
144 	if (ioctl(BpfFd, BIOCPROMISC, (caddr_t)0) < 0) {
145 		syslog(LOG_ERR, "bpf: can't set promiscuous mode: %m");
146 		Exit(0);
147 	}
148 
149 	/*
150 	 *  Ask BPF how much buffer space it requires and allocate one.
151 	 */
152 	if (ioctl(BpfFd, BIOCGBLEN, (caddr_t)&BpfLen) < 0) {
153 		syslog(LOG_ERR, "bpf: ioctl(BIOCGBLEN): %m");
154 		Exit(0);
155 	}
156 	if (BpfPkt == NULL)
157 		BpfPkt = (u_int8_t *)malloc(BpfLen);
158 
159 	if (BpfPkt == NULL) {
160 		syslog(LOG_ERR, "bpf: out of memory (%u bytes for bpfpkt)",
161 		       BpfLen);
162 		Exit(0);
163 	}
164 
165 	/*
166 	 *  Write a little program to snarf RMP Boot packets and stuff
167 	 *  it down BPF's throat (i.e. set up the packet filter).
168 	 */
169 	{
170 #define	RMP	((struct rmp_packet *)0)
171 		static struct bpf_insn bpf_insn[] = {
172 		    { BPF_LD|BPF_B|BPF_ABS,  0, 0, (long)&RMP->hp_llc.dsap },
173 		    { BPF_JMP|BPF_JEQ|BPF_K, 0, 5, IEEE_DSAP_HP },
174 		    { BPF_LD|BPF_H|BPF_ABS,  0, 0, (long)&RMP->hp_llc.cntrl },
175 		    { BPF_JMP|BPF_JEQ|BPF_K, 0, 3, IEEE_CNTL_HP },
176 		    { BPF_LD|BPF_H|BPF_ABS,  0, 0, (long)&RMP->hp_llc.dxsap },
177 		    { BPF_JMP|BPF_JEQ|BPF_K, 0, 1, HPEXT_DXSAP },
178 		    { BPF_RET|BPF_K,         0, 0, RMP_MAX_PACKET },
179 		    { BPF_RET|BPF_K,         0, 0, 0x0 }
180 		};
181 #undef	RMP
182 		static struct bpf_program bpf_pgm = {
183 		    NELEM(bpf_insn), bpf_insn
184 		};
185 
186 		if (ioctl(BpfFd, BIOCSETF, (caddr_t)&bpf_pgm) < 0) {
187 			syslog(LOG_ERR, "bpf: ioctl(BIOCSETF): %m");
188 			Exit(0);
189 		}
190 	}
191 
192 	return(BpfFd);
193 }
194 
195 /*
196 **  BPF GetIntfName -- Return the name of a network interface attached to
197 **		the system, or 0 if none can be found.  The interface
198 **		must be configured up; the lowest unit number is
199 **		preferred; loopback is ignored.
200 **
201 **	Parameters:
202 **		errmsg - if no network interface found, *errmsg explains why.
203 **
204 **	Returns:
205 **		A (static) pointer to interface name, or NULL on error.
206 **
207 **	Side Effects:
208 **		None.
209 */
210 char *
211 BpfGetIntfName(char **errmsg)
212 {
213 	struct ifreq ibuf[8], *ifrp, *ifend, *mp;
214 	struct ifconf ifc;
215 	int fd;
216 	int minunit, n;
217 	char *cp;
218 	static char device[sizeof(ifrp->ifr_name)];
219 	static char errbuf[128] = "No Error!";
220 
221 	if (errmsg != NULL)
222 		*errmsg = errbuf;
223 
224 	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
225 		(void) strcpy(errbuf, "bpf: socket: %m");
226 		return(NULL);
227 	}
228 	ifc.ifc_len = sizeof ibuf;
229 	ifc.ifc_buf = (caddr_t)ibuf;
230 
231 #ifdef OSIOCGIFCONF
232 	if (ioctl(fd, OSIOCGIFCONF, (char *)&ifc) < 0 ||
233 	    ifc.ifc_len < sizeof(struct ifreq)) {
234 		(void) strcpy(errbuf, "bpf: ioctl(OSIOCGIFCONF): %m");
235 		return(NULL);
236 	}
237 #else
238 	if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 ||
239 	    ifc.ifc_len < sizeof(struct ifreq)) {
240 		(void) strcpy(errbuf, "bpf: ioctl(SIOCGIFCONF): %m");
241 		return(NULL);
242 	}
243 #endif
244 	ifrp = ibuf;
245 	ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
246 
247 	mp = NULL;
248 	minunit = 666;
249 	for (; ifrp < ifend; ++ifrp) {
250 		if (ioctl(fd, SIOCGIFFLAGS, (char *)ifrp) < 0) {
251 			(void) strcpy(errbuf, "bpf: ioctl(SIOCGIFFLAGS): %m");
252 			return(NULL);
253 		}
254 
255 		/*
256 		 *  If interface is down or this is the loopback interface,
257 		 *  ignore it.
258 		 */
259 		if ((ifrp->ifr_flags & IFF_UP) == 0 ||
260 #ifdef IFF_LOOPBACK
261 		    (ifrp->ifr_flags & IFF_LOOPBACK))
262 #else
263 		    (strcmp(ifrp->ifr_name, "lo0") == 0))
264 #endif
265 			continue;
266 
267 		for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp)
268 			;
269 		n = atoi(cp);
270 		if (n < minunit) {
271 			minunit = n;
272 			mp = ifrp;
273 		}
274 	}
275 
276 	(void) close(fd);
277 	if (mp == NULL) {
278 		(void) strcpy(errbuf, "bpf: no interfaces found");
279 		return(NULL);
280 	}
281 
282 	(void) strcpy(device, mp->ifr_name);
283 	return(device);
284 }
285 
286 /*
287 **  BpfRead -- Read packets from a BPF device and fill in `rconn'.
288 **
289 **	Parameters:
290 **		rconn - filled in with next packet.
291 **		doread - is True if we can issue a read() syscall.
292 **
293 **	Returns:
294 **		True if `rconn' contains a new packet, False otherwise.
295 **
296 **	Side Effects:
297 **		None.
298 */
299 int
300 BpfRead(RMPCONN *rconn, int doread)
301 {
302 	int datlen, caplen, hdrlen;
303 	static u_int8_t *bp = NULL, *ep = NULL;
304 	int cc;
305 
306 	/*
307 	 *  The read() may block, or it may return one or more packets.
308 	 *  We let the caller decide whether or not we can issue a read().
309 	 */
310 	if (doread) {
311 		if ((cc = read(BpfFd, (char *)BpfPkt, (int)BpfLen)) < 0) {
312 			syslog(LOG_ERR, "bpf: read: %m");
313 			return(0);
314 		} else {
315 			bp = BpfPkt;
316 			ep = BpfPkt + cc;
317 		}
318 	}
319 
320 #define bhp ((struct bpf_hdr *)bp)
321 	/*
322 	 *  If there is a new packet in the buffer, stuff it into `rconn'
323 	 *  and return a success indication.
324 	 */
325 	if (bp < ep) {
326 		datlen = bhp->bh_datalen;
327 		caplen = bhp->bh_caplen;
328 		hdrlen = bhp->bh_hdrlen;
329 
330 		if (caplen != datlen)
331 			syslog(LOG_ERR,
332 			       "bpf: short packet dropped (%d of %d bytes)",
333 			       caplen, datlen);
334 		else if (caplen > sizeof(struct rmp_packet))
335 			syslog(LOG_ERR, "bpf: large packet dropped (%d bytes)",
336 			       caplen);
337 		else {
338 			rconn->rmplen = caplen;
339 			memmove((char *)&rconn->tstamp, (char *)&bhp->bh_tstamp,
340 			      sizeof(struct timeval));
341 			memmove((char *)&rconn->rmp, (char *)bp + hdrlen, caplen);
342 		}
343 		bp += BPF_WORDALIGN(caplen + hdrlen);
344 		return(1);
345 	}
346 #undef bhp
347 
348 	return(0);
349 }
350 
351 /*
352 **  BpfWrite -- Write packet to BPF device.
353 **
354 **	Parameters:
355 **		rconn - packet to send.
356 **
357 **	Returns:
358 **		True if write succeeded, False otherwise.
359 **
360 **	Side Effects:
361 **		None.
362 */
363 int
364 BpfWrite(RMPCONN *rconn)
365 {
366 	if (write(BpfFd, (char *)&rconn->rmp, rconn->rmplen) < 0) {
367 		syslog(LOG_ERR, "write: %s: %m", EnetStr(rconn));
368 		return(0);
369 	}
370 
371 	return(1);
372 }
373 
374 /*
375 **  BpfClose -- Close a BPF device.
376 **
377 **	Parameters:
378 **		None.
379 **
380 **	Returns:
381 **		Nothing.
382 **
383 **	Side Effects:
384 **		None.
385 */
386 void
387 BpfClose(void)
388 {
389 	struct ifreq ifr;
390 
391 	if (BpfPkt != NULL) {
392 		free((char *)BpfPkt);
393 		BpfPkt = NULL;
394 	}
395 
396 	if (BpfFd == -1)
397 		return;
398 
399 #ifdef MSG_EOR
400 	ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2;
401 #endif
402 	ifr.ifr_addr.sa_family = AF_UNSPEC;
403 	memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[0], RMP_ADDRLEN);
404 	if (ioctl(BpfFd, SIOCDELMULTI, (caddr_t)&ifr) < 0)
405 		(void) ioctl(BpfFd, BIOCPROMISC, (caddr_t)0);
406 
407 	(void) close(BpfFd);
408 	BpfFd = -1;
409 }
410