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