Commit | Line | Data |
---|---|---|
984263bc MD |
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. | |
dc71b7ab | 21 | * 3. Neither the name of the University nor the names of its contributors |
984263bc MD |
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 | |
1de703da MD |
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 $ | |
984263bc MD |
44 | */ |
45 | ||
984263bc MD |
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 | |
89a89091 | 82 | BpfOpen(void) |
984263bc MD |
83 | { |
84 | struct ifreq ifr; | |
984263bc MD |
85 | int n = 0; |
86 | ||
87 | /* | |
f8da163f | 88 | * Open the BPF device. |
984263bc | 89 | */ |
f8da163f | 90 | BpfFd = open(_PATH_BPF, O_RDWR); |
984263bc MD |
91 | if (BpfFd < 0) { |
92 | syslog(LOG_ERR, "bpf: no available devices: %m"); | |
93 | Exit(0); | |
94 | } | |
95 | ||
96 | /* | |
97 | * Set interface name for bpf device, get data link layer | |
98 | * type and make sure it's type Ethernet. | |
99 | */ | |
100 | (void) strncpy(ifr.ifr_name, IntfName, sizeof(ifr.ifr_name)); | |
101 | if (ioctl(BpfFd, BIOCSETIF, (caddr_t)&ifr) < 0) { | |
102 | syslog(LOG_ERR, "bpf: ioctl(BIOCSETIF,%s): %m", IntfName); | |
103 | Exit(0); | |
104 | } | |
105 | ||
106 | /* | |
107 | * Make sure we are dealing with an Ethernet device. | |
108 | */ | |
109 | if (ioctl(BpfFd, BIOCGDLT, (caddr_t)&n) < 0) { | |
110 | syslog(LOG_ERR, "bpf: ioctl(BIOCGDLT): %m"); | |
111 | Exit(0); | |
112 | } | |
113 | if (n != DLT_EN10MB) { | |
114 | syslog(LOG_ERR,"bpf: %s: data-link type %d unsupported", | |
115 | IntfName, n); | |
116 | Exit(0); | |
117 | } | |
118 | ||
119 | /* | |
120 | * On read(), return packets immediately (do not buffer them). | |
121 | */ | |
122 | n = 1; | |
123 | if (ioctl(BpfFd, BIOCIMMEDIATE, (caddr_t)&n) < 0) { | |
124 | syslog(LOG_ERR, "bpf: ioctl(BIOCIMMEDIATE): %m"); | |
125 | Exit(0); | |
126 | } | |
127 | ||
128 | /* | |
129 | * Try to enable the chip/driver's multicast address filter to | |
130 | * grab our RMP address. If this fails, try promiscuous mode. | |
131 | * If this fails, there's no way we are going to get any RMP | |
132 | * packets so just exit here. | |
133 | */ | |
134 | #ifdef MSG_EOR | |
135 | ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2; | |
136 | #endif | |
137 | ifr.ifr_addr.sa_family = AF_UNSPEC; | |
138 | memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[0], RMP_ADDRLEN); | |
139 | if (ioctl(BpfFd, BIOCPROMISC, (caddr_t)0) < 0) { | |
140 | syslog(LOG_ERR, "bpf: can't set promiscuous mode: %m"); | |
141 | Exit(0); | |
142 | } | |
143 | ||
144 | /* | |
145 | * Ask BPF how much buffer space it requires and allocate one. | |
146 | */ | |
147 | if (ioctl(BpfFd, BIOCGBLEN, (caddr_t)&BpfLen) < 0) { | |
148 | syslog(LOG_ERR, "bpf: ioctl(BIOCGBLEN): %m"); | |
149 | Exit(0); | |
150 | } | |
151 | if (BpfPkt == NULL) | |
152 | BpfPkt = (u_int8_t *)malloc(BpfLen); | |
153 | ||
154 | if (BpfPkt == NULL) { | |
155 | syslog(LOG_ERR, "bpf: out of memory (%u bytes for bpfpkt)", | |
156 | BpfLen); | |
157 | Exit(0); | |
158 | } | |
159 | ||
160 | /* | |
161 | * Write a little program to snarf RMP Boot packets and stuff | |
162 | * it down BPF's throat (i.e. set up the packet filter). | |
163 | */ | |
164 | { | |
165 | #define RMP ((struct rmp_packet *)0) | |
166 | static struct bpf_insn bpf_insn[] = { | |
167 | { BPF_LD|BPF_B|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dsap }, | |
168 | { BPF_JMP|BPF_JEQ|BPF_K, 0, 5, IEEE_DSAP_HP }, | |
169 | { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.cntrl }, | |
170 | { BPF_JMP|BPF_JEQ|BPF_K, 0, 3, IEEE_CNTL_HP }, | |
171 | { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dxsap }, | |
172 | { BPF_JMP|BPF_JEQ|BPF_K, 0, 1, HPEXT_DXSAP }, | |
173 | { BPF_RET|BPF_K, 0, 0, RMP_MAX_PACKET }, | |
174 | { BPF_RET|BPF_K, 0, 0, 0x0 } | |
175 | }; | |
176 | #undef RMP | |
177 | static struct bpf_program bpf_pgm = { | |
e62ef63c | 178 | NELEM(bpf_insn), bpf_insn |
984263bc MD |
179 | }; |
180 | ||
181 | if (ioctl(BpfFd, BIOCSETF, (caddr_t)&bpf_pgm) < 0) { | |
182 | syslog(LOG_ERR, "bpf: ioctl(BIOCSETF): %m"); | |
183 | Exit(0); | |
184 | } | |
185 | } | |
186 | ||
187 | return(BpfFd); | |
188 | } | |
189 | ||
190 | /* | |
191 | ** BPF GetIntfName -- Return the name of a network interface attached to | |
192 | ** the system, or 0 if none can be found. The interface | |
193 | ** must be configured up; the lowest unit number is | |
194 | ** preferred; loopback is ignored. | |
195 | ** | |
196 | ** Parameters: | |
197 | ** errmsg - if no network interface found, *errmsg explains why. | |
198 | ** | |
199 | ** Returns: | |
200 | ** A (static) pointer to interface name, or NULL on error. | |
201 | ** | |
202 | ** Side Effects: | |
203 | ** None. | |
204 | */ | |
205 | char * | |
89a89091 | 206 | BpfGetIntfName(char **errmsg) |
984263bc MD |
207 | { |
208 | struct ifreq ibuf[8], *ifrp, *ifend, *mp; | |
209 | struct ifconf ifc; | |
210 | int fd; | |
211 | int minunit, n; | |
212 | char *cp; | |
213 | static char device[sizeof(ifrp->ifr_name)]; | |
214 | static char errbuf[128] = "No Error!"; | |
215 | ||
216 | if (errmsg != NULL) | |
217 | *errmsg = errbuf; | |
218 | ||
219 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { | |
220 | (void) strcpy(errbuf, "bpf: socket: %m"); | |
221 | return(NULL); | |
222 | } | |
223 | ifc.ifc_len = sizeof ibuf; | |
224 | ifc.ifc_buf = (caddr_t)ibuf; | |
225 | ||
984263bc MD |
226 | if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 || |
227 | ifc.ifc_len < sizeof(struct ifreq)) { | |
228 | (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFCONF): %m"); | |
229 | return(NULL); | |
230 | } | |
984263bc MD |
231 | ifrp = ibuf; |
232 | ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); | |
233 | ||
678e8cc6 | 234 | mp = NULL; |
984263bc MD |
235 | minunit = 666; |
236 | for (; ifrp < ifend; ++ifrp) { | |
237 | if (ioctl(fd, SIOCGIFFLAGS, (char *)ifrp) < 0) { | |
238 | (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFFLAGS): %m"); | |
239 | return(NULL); | |
240 | } | |
241 | ||
242 | /* | |
243 | * If interface is down or this is the loopback interface, | |
244 | * ignore it. | |
245 | */ | |
246 | if ((ifrp->ifr_flags & IFF_UP) == 0 || | |
247 | #ifdef IFF_LOOPBACK | |
248 | (ifrp->ifr_flags & IFF_LOOPBACK)) | |
249 | #else | |
250 | (strcmp(ifrp->ifr_name, "lo0") == 0)) | |
251 | #endif | |
252 | continue; | |
253 | ||
254 | for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp) | |
255 | ; | |
256 | n = atoi(cp); | |
257 | if (n < minunit) { | |
258 | minunit = n; | |
259 | mp = ifrp; | |
260 | } | |
261 | } | |
262 | ||
263 | (void) close(fd); | |
678e8cc6 | 264 | if (mp == NULL) { |
984263bc MD |
265 | (void) strcpy(errbuf, "bpf: no interfaces found"); |
266 | return(NULL); | |
267 | } | |
268 | ||
269 | (void) strcpy(device, mp->ifr_name); | |
270 | return(device); | |
271 | } | |
272 | ||
273 | /* | |
274 | ** BpfRead -- Read packets from a BPF device and fill in `rconn'. | |
275 | ** | |
276 | ** Parameters: | |
277 | ** rconn - filled in with next packet. | |
278 | ** doread - is True if we can issue a read() syscall. | |
279 | ** | |
280 | ** Returns: | |
281 | ** True if `rconn' contains a new packet, False otherwise. | |
282 | ** | |
283 | ** Side Effects: | |
284 | ** None. | |
285 | */ | |
286 | int | |
89a89091 | 287 | BpfRead(RMPCONN *rconn, int doread) |
984263bc MD |
288 | { |
289 | int datlen, caplen, hdrlen; | |
290 | static u_int8_t *bp = NULL, *ep = NULL; | |
291 | int cc; | |
292 | ||
293 | /* | |
294 | * The read() may block, or it may return one or more packets. | |
295 | * We let the caller decide whether or not we can issue a read(). | |
296 | */ | |
297 | if (doread) { | |
298 | if ((cc = read(BpfFd, (char *)BpfPkt, (int)BpfLen)) < 0) { | |
299 | syslog(LOG_ERR, "bpf: read: %m"); | |
300 | return(0); | |
301 | } else { | |
302 | bp = BpfPkt; | |
303 | ep = BpfPkt + cc; | |
304 | } | |
305 | } | |
306 | ||
307 | #define bhp ((struct bpf_hdr *)bp) | |
308 | /* | |
309 | * If there is a new packet in the buffer, stuff it into `rconn' | |
310 | * and return a success indication. | |
311 | */ | |
312 | if (bp < ep) { | |
313 | datlen = bhp->bh_datalen; | |
314 | caplen = bhp->bh_caplen; | |
315 | hdrlen = bhp->bh_hdrlen; | |
316 | ||
317 | if (caplen != datlen) | |
318 | syslog(LOG_ERR, | |
319 | "bpf: short packet dropped (%d of %d bytes)", | |
320 | caplen, datlen); | |
321 | else if (caplen > sizeof(struct rmp_packet)) | |
322 | syslog(LOG_ERR, "bpf: large packet dropped (%d bytes)", | |
323 | caplen); | |
324 | else { | |
325 | rconn->rmplen = caplen; | |
326 | memmove((char *)&rconn->tstamp, (char *)&bhp->bh_tstamp, | |
327 | sizeof(struct timeval)); | |
328 | memmove((char *)&rconn->rmp, (char *)bp + hdrlen, caplen); | |
329 | } | |
330 | bp += BPF_WORDALIGN(caplen + hdrlen); | |
331 | return(1); | |
332 | } | |
333 | #undef bhp | |
334 | ||
335 | return(0); | |
336 | } | |
337 | ||
338 | /* | |
339 | ** BpfWrite -- Write packet to BPF device. | |
340 | ** | |
341 | ** Parameters: | |
342 | ** rconn - packet to send. | |
343 | ** | |
344 | ** Returns: | |
345 | ** True if write succeeded, False otherwise. | |
346 | ** | |
347 | ** Side Effects: | |
348 | ** None. | |
349 | */ | |
350 | int | |
89a89091 | 351 | BpfWrite(RMPCONN *rconn) |
984263bc MD |
352 | { |
353 | if (write(BpfFd, (char *)&rconn->rmp, rconn->rmplen) < 0) { | |
354 | syslog(LOG_ERR, "write: %s: %m", EnetStr(rconn)); | |
355 | return(0); | |
356 | } | |
357 | ||
358 | return(1); | |
359 | } | |
360 | ||
361 | /* | |
362 | ** BpfClose -- Close a BPF device. | |
363 | ** | |
364 | ** Parameters: | |
365 | ** None. | |
366 | ** | |
367 | ** Returns: | |
368 | ** Nothing. | |
369 | ** | |
370 | ** Side Effects: | |
371 | ** None. | |
372 | */ | |
373 | void | |
89a89091 | 374 | BpfClose(void) |
984263bc MD |
375 | { |
376 | struct ifreq ifr; | |
377 | ||
378 | if (BpfPkt != NULL) { | |
379 | free((char *)BpfPkt); | |
380 | BpfPkt = NULL; | |
381 | } | |
382 | ||
383 | if (BpfFd == -1) | |
384 | return; | |
385 | ||
386 | #ifdef MSG_EOR | |
387 | ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2; | |
388 | #endif | |
389 | ifr.ifr_addr.sa_family = AF_UNSPEC; | |
390 | memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[0], RMP_ADDRLEN); | |
391 | if (ioctl(BpfFd, SIOCDELMULTI, (caddr_t)&ifr) < 0) | |
392 | (void) ioctl(BpfFd, BIOCPROMISC, (caddr_t)0); | |
393 | ||
394 | (void) close(BpfFd); | |
395 | BpfFd = -1; | |
396 | } |