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