Commit | Line | Data |
---|---|---|
a87aa7a8 | 1 | /* |
a87aa7a8 KB |
2 | * Copyright (c) 1988, 1992 The University of Utah and the Center |
3 | * for Software Science (CSS). | |
08b68ced KB |
4 | * Copyright (c) 1992, 1993 |
5 | * The Regents of the University of California. All rights reserved. | |
a87aa7a8 KB |
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 | * %sccs.include.redist.c% | |
14 | * | |
08b68ced | 15 | * @(#)bpf.c 8.1 (Berkeley) %G% |
a87aa7a8 KB |
16 | * |
17 | * Utah $Hdr: bpf.c 3.1 92/07/06$ | |
18 | * Author: Jeff Forys, University of Utah CSS | |
19 | */ | |
20 | ||
21 | #ifndef lint | |
08b68ced | 22 | static char sccsid[] = "@(#)bpf.c 8.1 (Berkeley) %G%"; |
a87aa7a8 KB |
23 | #endif /* not lint */ |
24 | ||
fd0a34e2 | 25 | #include <sys/param.h> |
a87aa7a8 | 26 | #include <sys/ioctl.h> |
fd0a34e2 | 27 | #include <sys/socket.h> |
a87aa7a8 KB |
28 | |
29 | #include <net/if.h> | |
30 | #include <net/bpf.h> | |
31 | ||
fd0a34e2 KB |
32 | #include <ctype.h> |
33 | #include <errno.h> | |
34 | #include <fcntl.h> | |
35 | #include <stdio.h> | |
36 | #include <stdlib.h> | |
37 | #include <string.h> | |
38 | #include <syslog.h> | |
39 | #include <unistd.h> | |
40 | #include "defs.h" | |
a87aa7a8 KB |
41 | #include "pathnames.h" |
42 | ||
a87aa7a8 KB |
43 | static int BpfFd = -1; |
44 | static unsigned BpfLen = 0; | |
45 | static u_char *BpfPkt = NULL; | |
46 | ||
47 | /* | |
48 | ** BpfOpen -- Open and initialize a BPF device. | |
49 | ** | |
50 | ** Parameters: | |
51 | ** None. | |
52 | ** | |
53 | ** Returns: | |
54 | ** File descriptor of opened BPF device (for select() etc). | |
55 | ** | |
56 | ** Side Effects: | |
57 | ** If an error is encountered, the program terminates here. | |
58 | */ | |
a87aa7a8 KB |
59 | int |
60 | BpfOpen() | |
61 | { | |
62 | struct ifreq ifr; | |
63 | char bpfdev[32]; | |
64 | int n = 0; | |
65 | ||
66 | /* | |
67 | * Open the first available BPF device. | |
68 | */ | |
69 | do { | |
70 | (void) sprintf(bpfdev, _PATH_BPF, n++); | |
71 | BpfFd = open(bpfdev, O_RDWR); | |
72 | } while (BpfFd < 0 && (errno == EBUSY || errno == EPERM)); | |
73 | ||
74 | if (BpfFd < 0) { | |
75 | syslog(LOG_ERR, "bpf: no available devices: %m"); | |
76 | Exit(0); | |
77 | } | |
78 | ||
79 | /* | |
80 | * Set interface name for bpf device, get data link layer | |
81 | * type and make sure it's type Ethernet. | |
82 | */ | |
83 | (void) strncpy(ifr.ifr_name, IntfName, sizeof(ifr.ifr_name)); | |
84 | if (ioctl(BpfFd, BIOCSETIF, (caddr_t)&ifr) < 0) { | |
85 | syslog(LOG_ERR, "bpf: ioctl(BIOCSETIF,%s): %m", IntfName); | |
86 | Exit(0); | |
87 | } | |
88 | ||
89 | /* | |
90 | * Make sure we are dealing with an Ethernet device. | |
91 | */ | |
92 | if (ioctl(BpfFd, BIOCGDLT, (caddr_t)&n) < 0) { | |
93 | syslog(LOG_ERR, "bpf: ioctl(BIOCGDLT): %m"); | |
94 | Exit(0); | |
95 | } | |
96 | if (n != DLT_EN10MB) { | |
97 | syslog(LOG_ERR,"bpf: %s: data-link type %d unsupported", | |
98 | IntfName, n); | |
99 | Exit(0); | |
100 | } | |
101 | ||
102 | /* | |
103 | * On read(), return packets immediately (do not buffer them). | |
104 | */ | |
105 | n = 1; | |
106 | if (ioctl(BpfFd, BIOCIMMEDIATE, (caddr_t)&n) < 0) { | |
107 | syslog(LOG_ERR, "bpf: ioctl(BIOCIMMEDIATE): %m"); | |
108 | Exit(0); | |
109 | } | |
110 | ||
111 | /* | |
112 | * Try to enable the chip/driver's multicast address filter to | |
113 | * grab our RMP address. If this fails, try promiscuous mode. | |
114 | * If this fails, there's no way we are going to get any RMP | |
115 | * packets so just exit here. | |
116 | */ | |
117 | #ifdef MSG_EOR | |
118 | ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2; | |
119 | #endif | |
120 | ifr.ifr_addr.sa_family = AF_UNSPEC; | |
121 | bcopy(&RmpMcastAddr[0], (char *)&ifr.ifr_addr.sa_data[0], RMP_ADDRLEN); | |
122 | if (ioctl(BpfFd, SIOCADDMULTI, (caddr_t)&ifr) < 0) { | |
123 | syslog(LOG_WARNING, | |
124 | "bpf: can't add mcast addr (%m), setting promiscuous mode"); | |
125 | ||
126 | if (ioctl(BpfFd, BIOCPROMISC, (caddr_t)0) < 0) { | |
127 | syslog(LOG_ERR, "bpf: can't set promiscuous mode: %m"); | |
128 | Exit(0); | |
129 | } | |
130 | } | |
131 | ||
132 | /* | |
133 | * Ask BPF how much buffer space it requires and allocate one. | |
134 | */ | |
135 | if (ioctl(BpfFd, BIOCGBLEN, (caddr_t)&BpfLen) < 0) { | |
136 | syslog(LOG_ERR, "bpf: ioctl(BIOCGBLEN): %m"); | |
137 | Exit(0); | |
138 | } | |
139 | if (BpfPkt == NULL) | |
140 | BpfPkt = (u_char *)malloc(BpfLen); | |
141 | ||
142 | if (BpfPkt == NULL) { | |
143 | syslog(LOG_ERR, "bpf: out of memory (%u bytes for bpfpkt)", | |
144 | BpfLen); | |
145 | Exit(0); | |
146 | } | |
147 | ||
148 | /* | |
149 | * Write a little program to snarf RMP Boot packets and stuff | |
150 | * it down BPF's throat (i.e. set up the packet filter). | |
151 | */ | |
152 | { | |
153 | #define RMP ((struct rmp_packet *)0) | |
154 | static struct bpf_insn bpf_insn[] = { | |
155 | { BPF_LD|BPF_B|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dsap }, | |
156 | { BPF_JMP|BPF_JEQ|BPF_K, 0, 5, IEEE_DSAP_HP }, | |
157 | { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.cntrl }, | |
158 | { BPF_JMP|BPF_JEQ|BPF_K, 0, 3, IEEE_CNTL_HP }, | |
159 | { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dxsap }, | |
160 | { BPF_JMP|BPF_JEQ|BPF_K, 0, 1, HPEXT_DXSAP }, | |
161 | { BPF_RET|BPF_K, 0, 0, RMP_MAX_PACKET }, | |
162 | { BPF_RET|BPF_K, 0, 0, 0x0 } | |
163 | }; | |
164 | #undef RMP | |
165 | static struct bpf_program bpf_pgm = { | |
166 | sizeof(bpf_insn)/sizeof(bpf_insn[0]), bpf_insn | |
167 | }; | |
168 | ||
169 | if (ioctl(BpfFd, BIOCSETF, (caddr_t)&bpf_pgm) < 0) { | |
170 | syslog(LOG_ERR, "bpf: ioctl(BIOCSETF): %m"); | |
171 | Exit(0); | |
172 | } | |
173 | } | |
174 | ||
175 | return(BpfFd); | |
176 | } | |
177 | ||
178 | /* | |
179 | ** BPF GetIntfName -- Return the name of a network interface attached to | |
180 | ** the system, or 0 if none can be found. The interface | |
181 | ** must be configured up; the lowest unit number is | |
182 | ** preferred; loopback is ignored. | |
183 | ** | |
184 | ** Parameters: | |
185 | ** errmsg - if no network interface found, *errmsg explains why. | |
186 | ** | |
187 | ** Returns: | |
188 | ** A (static) pointer to interface name, or NULL on error. | |
189 | ** | |
190 | ** Side Effects: | |
191 | ** None. | |
192 | */ | |
a87aa7a8 KB |
193 | char * |
194 | BpfGetIntfName(errmsg) | |
195 | char **errmsg; | |
196 | { | |
197 | struct ifreq ibuf[8], *ifrp, *ifend, *mp; | |
198 | struct ifconf ifc; | |
199 | int fd; | |
200 | int minunit, n; | |
201 | char *cp; | |
202 | static char device[sizeof(ifrp->ifr_name)]; | |
203 | static char errbuf[128] = "No Error!"; | |
204 | ||
205 | if (errmsg != NULL) | |
206 | *errmsg = errbuf; | |
207 | ||
208 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { | |
209 | (void) strcpy(errbuf, "bpf: socket: %m"); | |
210 | return(NULL); | |
211 | } | |
212 | ifc.ifc_len = sizeof ibuf; | |
213 | ifc.ifc_buf = (caddr_t)ibuf; | |
214 | ||
215 | #ifdef OSIOCGIFCONF | |
216 | if (ioctl(fd, OSIOCGIFCONF, (char *)&ifc) < 0 || | |
217 | ifc.ifc_len < sizeof(struct ifreq)) { | |
218 | (void) strcpy(errbuf, "bpf: ioctl(OSIOCGIFCONF): %m"); | |
219 | return(NULL); | |
220 | } | |
221 | #else | |
222 | if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 || | |
223 | ifc.ifc_len < sizeof(struct ifreq)) { | |
224 | (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFCONF): %m"); | |
225 | return(NULL); | |
226 | } | |
227 | #endif | |
228 | ifrp = ibuf; | |
229 | ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); | |
230 | ||
231 | mp = 0; | |
232 | minunit = 666; | |
233 | for (; ifrp < ifend; ++ifrp) { | |
234 | if (ioctl(fd, SIOCGIFFLAGS, (char *)ifrp) < 0) { | |
235 | (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFFLAGS): %m"); | |
236 | return(NULL); | |
237 | } | |
238 | ||
239 | /* | |
240 | * If interface is down or this is the loopback interface, | |
241 | * ignore it. | |
242 | */ | |
243 | if ((ifrp->ifr_flags & IFF_UP) == 0 || | |
244 | #ifdef IFF_LOOPBACK | |
245 | (ifrp->ifr_flags & IFF_LOOPBACK)) | |
246 | #else | |
247 | (strcmp(ifrp->ifr_name, "lo0") == 0)) | |
248 | #endif | |
249 | continue; | |
250 | ||
251 | for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp) | |
252 | ; | |
253 | n = atoi(cp); | |
254 | if (n < minunit) { | |
255 | minunit = n; | |
256 | mp = ifrp; | |
257 | } | |
258 | } | |
259 | ||
260 | (void) close(fd); | |
261 | if (mp == 0) { | |
262 | (void) strcpy(errbuf, "bpf: no interfaces found"); | |
263 | return(NULL); | |
264 | } | |
265 | ||
266 | (void) strcpy(device, mp->ifr_name); | |
267 | return(device); | |
268 | } | |
269 | ||
270 | /* | |
271 | ** BpfRead -- Read packets from a BPF device and fill in `rconn'. | |
272 | ** | |
273 | ** Parameters: | |
274 | ** rconn - filled in with next packet. | |
275 | ** doread - is True if we can issue a read() syscall. | |
276 | ** | |
277 | ** Returns: | |
278 | ** True if `rconn' contains a new packet, False otherwise. | |
279 | ** | |
280 | ** Side Effects: | |
281 | ** None. | |
282 | */ | |
a87aa7a8 KB |
283 | int |
284 | BpfRead(rconn, doread) | |
285 | RMPCONN *rconn; | |
286 | int doread; | |
287 | { | |
288 | register int datlen, caplen, hdrlen; | |
289 | static u_char *bp = NULL, *ep = NULL; | |
290 | int cc; | |
291 | ||
292 | /* | |
293 | * The read() may block, or it may return one or more packets. | |
294 | * We let the caller decide whether or not we can issue a read(). | |
295 | */ | |
296 | if (doread) { | |
297 | if ((cc = read(BpfFd, (char *)BpfPkt, (int)BpfLen)) < 0) { | |
298 | syslog(LOG_ERR, "bpf: read: %m"); | |
299 | return(0); | |
300 | } else { | |
301 | bp = BpfPkt; | |
302 | ep = BpfPkt + cc; | |
303 | } | |
304 | } | |
305 | ||
306 | #define bhp ((struct bpf_hdr *)bp) | |
307 | /* | |
308 | * If there is a new packet in the buffer, stuff it into `rconn' | |
309 | * and return a success indication. | |
310 | */ | |
311 | if (bp < ep) { | |
312 | datlen = bhp->bh_datalen; | |
313 | caplen = bhp->bh_caplen; | |
314 | hdrlen = bhp->bh_hdrlen; | |
315 | ||
316 | if (caplen != datlen) | |
317 | syslog(LOG_ERR, | |
318 | "bpf: short packet dropped (%d of %d bytes)", | |
319 | caplen, datlen); | |
320 | else if (caplen > sizeof(struct rmp_packet)) | |
321 | syslog(LOG_ERR, "bpf: large packet dropped (%d bytes)", | |
322 | caplen); | |
323 | else { | |
324 | rconn->rmplen = caplen; | |
325 | bcopy((char *)&bhp->bh_tstamp, (char *)&rconn->tstamp, | |
326 | sizeof(struct timeval)); | |
327 | bcopy((char *)bp + hdrlen, (char *)&rconn->rmp, caplen); | |
328 | } | |
329 | bp += BPF_WORDALIGN(caplen + hdrlen); | |
330 | return(1); | |
331 | } | |
332 | #undef bhp | |
333 | ||
334 | return(0); | |
335 | } | |
336 | ||
337 | /* | |
338 | ** BpfWrite -- Write packet to BPF device. | |
339 | ** | |
340 | ** Parameters: | |
341 | ** rconn - packet to send. | |
342 | ** | |
343 | ** Returns: | |
344 | ** True if write succeeded, False otherwise. | |
345 | ** | |
346 | ** Side Effects: | |
347 | ** None. | |
348 | */ | |
fd0a34e2 | 349 | int |
a87aa7a8 KB |
350 | BpfWrite(rconn) |
351 | RMPCONN *rconn; | |
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 | /* | |
fd0a34e2 | 362 | ** BpfClose -- Close a BPF device. |
a87aa7a8 KB |
363 | ** |
364 | ** Parameters: | |
365 | ** None. | |
366 | ** | |
367 | ** Returns: | |
368 | ** Nothing. | |
369 | ** | |
370 | ** Side Effects: | |
371 | ** None. | |
372 | */ | |
fd0a34e2 | 373 | void |
a87aa7a8 KB |
374 | BpfClose() |
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 | bcopy(&RmpMcastAddr[0], (char *)&ifr.ifr_addr.sa_data[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 | } |