|
|
1.1 root 1: /* $Id: bsd-bpf.c,v 1.3 2003/05/18 00:06:06 fredette Exp $ */
2:
3: /* host/bsd/bsd-bpf.c - BSD Berkeley Packet Filter Ethernet support: */
4:
5: /*
6: * Copyright (c) 2001, 2003 Matt Fredette
7: * All rights reserved.
8: *
9: * Redistribution and use in source and binary forms, with or without
10: * modification, are permitted provided that the following conditions
11: * are met:
12: * 1. Redistributions of source code must retain the above copyright
13: * notice, this list of conditions and the following disclaimer.
14: * 2. Redistributions in binary form must reproduce the above copyright
15: * notice, this list of conditions and the following disclaimer in the
16: * documentation and/or other materials provided with the distribution.
17: * 3. All advertising materials mentioning features or use of this software
18: * must display the following acknowledgement:
19: * This product includes software developed by Matt Fredette.
20: * 4. The name of the author may not be used to endorse or promote products
21: * derived from this software without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33: * POSSIBILITY OF SUCH DAMAGE.
34: */
35:
36: #include <tme/common.h>
37: _TME_RCSID("$Id: bsd-bpf.c,v 1.3 2003/05/18 00:06:06 fredette Exp $");
38:
39: /* includes: */
40: #include "bsd-impl.h"
41: #include <tme/generic/ethernet.h>
42: #include <stdio.h>
43: #include <string.h>
44: #include <errno.h>
45: #include <fcntl.h>
46: #include <netdb.h>
47: #include <sys/param.h>
48: #include <sys/socket.h>
49: #include <sys/stat.h>
50: #include <net/if.h>
51: #include <netinet/in_systm.h>
52: #include <netinet/in.h>
53: #if defined(HAVE_SYS_SOCKIO_H)
54: #include <sys/sockio.h>
55: #elif defined(HAVE_SYS_SOCKETIO_H)
56: #include <sys/socketio.h>
57: #endif /* HAVE_SYS_SOCKETIO_H */
58: #include <sys/ioctl.h>
59: #ifdef HAVE_IOCTLS_H
60: #include <ioctls.h>
61: #endif /* HAVE_IOCTLS_H */
62: #ifdef HAVE_NET_IF_ETHER_H
63: #include <net/if_ether.h>
64: #endif /* HAVE_NET_IF_ETHER_H */
65: #ifdef HAVE_NET_ETHERNET_H
66: #include <net/ethernet.h>
67: #endif /* HAVE_NET_ETHERNET_H */
68: #include <netinet/ip.h>
69: #ifdef HAVE_NET_IF_DL_H
70: #include <net/if_dl.h>
71: #endif /* HAVE_NET_IF_DL_H */
72: #include <arpa/inet.h>
73: #include <net/bpf.h>
74:
75: /* macros: */
76:
77: /* the callout flags: */
78: #define TME_BSD_BPF_CALLOUT_CHECK (0)
79: #define TME_BSD_BPF_CALLOUT_RUNNING TME_BIT(0)
80: #define TME_BSD_BPF_CALLOUTS_MASK (-2)
81: #define TME_BSD_BPF_CALLOUT_CTRL TME_BIT(1)
82: #define TME_BSD_BPF_CALLOUT_READ TME_BIT(2)
83:
84: /* structures: */
85:
86: /* our internal data structure: */
87: struct tme_bsd_bpf {
88:
89: /* backpointer to our element: */
90: struct tme_element *tme_bsd_bpf_element;
91:
92: /* our mutex: */
93: tme_mutex_t tme_bsd_bpf_mutex;
94:
95: /* our reader and writer conditions: */
96: tme_cond_t tme_bsd_bpf_cond_reader;
97: tme_cond_t tme_bsd_bpf_cond_writer;
98:
99: /* the callout flags: */
100: unsigned int tme_bsd_bpf_callout_flags;
101:
102: /* our Ethernet connection: */
103: struct tme_ethernet_connection *tme_bsd_bpf_eth_connection;
104:
105: /* the BPF file descriptor: */
106: int tme_bsd_bpf_fd;
107:
108: /* the size of the packet buffer for the interface: */
109: size_t tme_bsd_bpf_buffer_size;
110:
111: /* the packet buffer for the interface: */
112: tme_uint8_t *tme_bsd_bpf_buffer;
113:
114: /* the next offset within the packet buffer, and the end of the data
115: in the packet buffer: */
116: size_t tme_bsd_bpf_buffer_offset;
117: size_t tme_bsd_bpf_buffer_end;
118: };
119:
120: /* the accept and reject packet insns: */
121: static const struct bpf_insn _tme_bsd_bpf_insn_accept = BPF_STMT(BPF_RET + BPF_K, (u_int) -1);
122: static const struct bpf_insn _tme_bsd_bpf_insn_reject = BPF_STMT(BPF_RET + BPF_K, 0);
123:
124: /* this creates a BPF filter that accepts Ethernet packets with
125: destination addresses in the configured set. the broadcast address
126: must be in this set, it isn't accepted automatically: */
127: static int
128: _tme_bsd_bpf_filter(struct tme_ethernet_config *config,
129: const tme_uint8_t *prefix,
130: unsigned int prefix_len,
131: struct bpf_insn *bpf_filter,
132: int bpf_filter_size,
133: int *_first_pc)
134: {
135: unsigned int addr_i;
136: tme_uint8_t byte;
137: tme_uint8_t byte_bitmap[(1 << (8 * sizeof(byte))) >> 3];
138: int match_pc, miss_pc, this_pc;
139:
140: /* clear the byte bitmap: */
141: memset(byte_bitmap, 0, sizeof(byte_bitmap));
142:
143: /* the last instruction jumps to the reject insn when it fails: */
144: miss_pc = bpf_filter_size - 1;
145:
146: /* loop over all of the addresses: */
147: for (addr_i = 0;
148: addr_i < config->tme_ethernet_config_addr_count;
149: addr_i++) {
150:
151: /* skip this address if it doesn't match the prefix: */
152: if (prefix_len > 0
153: && memcmp(config->tme_ethernet_config_addrs[addr_i],
154: prefix,
155: prefix_len)) {
156: continue;
157: }
158:
159: /* get the next byte, and skip this address if this byte has
160: already been done: */
161: byte = config->tme_ethernet_config_addrs[addr_i][prefix_len];
162: if (byte_bitmap[byte >> 3] & TME_BIT(byte & 7)) {
163: continue;
164: }
165: byte_bitmap[byte >> 3] |= TME_BIT(byte & 7);
166:
167: /* get the PC of the instruction to branch to if this byte
168: matches. if this is the last byte of the address, the branch
169: target is the accept insn, otherwise recurse and get the first
170: insn of the rest of the matcher: */
171: match_pc = ((prefix_len == (TME_ETHERNET_ADDR_SIZE - 1))
172: ? bpf_filter_size - 2
173: : _tme_bsd_bpf_filter(config,
174: config->tme_ethernet_config_addrs[addr_i],
175: prefix_len + 1,
176: bpf_filter,
177: bpf_filter_size,
178: _first_pc));
179:
180: /* add this testing instruction: */
181: this_pc = --(*_first_pc);
182: assert(this_pc >= 0);
183: bpf_filter[this_pc].code = BPF_JMP + BPF_JEQ + BPF_K;
184: bpf_filter[this_pc].jt = match_pc - (this_pc + 1);
185: bpf_filter[this_pc].jf = miss_pc - (this_pc + 1);
186: bpf_filter[this_pc].k = byte;
187:
188: /* update the miss pc: */
189: miss_pc = this_pc;
190: }
191:
192: /* add this load instruction: */
193: this_pc = --(*_first_pc);
194: assert(this_pc >= 0);
195: bpf_filter[this_pc].code = BPF_LD + BPF_B + BPF_ABS;
196: bpf_filter[this_pc].k = prefix_len;
197:
198: /* return our pc: */
199: return (this_pc);
200: }
201:
202: /* this dumps a BPF filter. not all insns are supported, just
203: those used by our address matching filters: */
204: void
205: _tme_bsd_bpf_dump_filter(const struct bpf_program *program)
206: {
207: int pc;
208: FILE *fp;
209: const struct bpf_insn *insn;
210: char ldsize;
211: const char *opc;
212:
213: fp = stderr;
214: for (pc = 0, insn = program->bf_insns;
215: pc < program->bf_len;
216: pc++, insn++) {
217:
218: /* the PC: */
219: fprintf(fp, "%d:\t", pc);
220:
221: /* dispatch on the instruction class: */
222: switch (BPF_CLASS(insn->code)) {
223:
224: case BPF_LD:
225:
226: switch (BPF_SIZE(insn->code)) {
227: case BPF_B: ldsize = 'b'; break;
228: case BPF_H: ldsize = 'w'; break;
229: case BPF_W: ldsize = 'l'; break;
230: default: ldsize = '?'; break;
231: }
232: fprintf(fp, "ld.%c ", ldsize);
233:
234: switch (BPF_MODE(insn->code)) {
235: case BPF_ABS: fprintf(fp, "0x%x", insn->k); break;
236: default: fprintf(fp, "??");
237: }
238:
239: break;
240:
241: case BPF_JMP:
242:
243: switch (BPF_OP(insn->code)) {
244: case BPF_JEQ: opc = "jeq"; break;
245: default: opc = "??"; break;
246: }
247: fprintf(fp, "%s ", opc);
248:
249: switch (BPF_SRC(insn->code)) {
250: case BPF_K: fprintf(fp, "#0x%x", insn->k); break;
251: case BPF_X: fprintf(fp, "x"); break;
252: default: fprintf(fp, "??"); break;
253: }
254:
255: fprintf(fp, ", %d, %d", pc + 1 + insn->jt, pc + 1 + insn->jf);
256: break;
257:
258: case BPF_RET:
259: switch (BPF_RVAL(insn->code)) {
260: case BPF_A: fprintf(fp, "ret a"); break;
261: case BPF_X: fprintf(fp, "ret x"); break;
262: case BPF_K: fprintf(fp, "ret #0x%x", insn->k); break;
263: default: fprintf(fp, "ret ??"); break;
264: }
265: break;
266:
267: default:
268: fprintf(fp, "??");
269: break;
270: }
271:
272: putc('\n', fp);
273: }
274: }
275:
276: /* the bpf callout function. it must be called with the mutex locked: */
277: static void
278: _tme_bsd_bpf_callout(struct tme_bsd_bpf *bpf, int new_callouts)
279: {
280: struct tme_ethernet_connection *conn_eth;
281: int callouts, later_callouts;
282: unsigned int ctrl;
283: int rc;
284: tme_ethernet_fid_t frame_id;
285: struct tme_ethernet_frame_chunk frame_chunk_buffer;
286: tme_uint8_t frame[TME_ETHERNET_FRAME_MAX];
287:
288: /* add in any new callouts: */
289: bpf->tme_bsd_bpf_callout_flags |= new_callouts;
290:
291: /* if this function is already running in another thread, simply
292: return now. the other thread will do our work: */
293: if (bpf->tme_bsd_bpf_callout_flags & TME_BSD_BPF_CALLOUT_RUNNING) {
294: return;
295: }
296:
297: /* callouts are now running: */
298: bpf->tme_bsd_bpf_callout_flags |= TME_BSD_BPF_CALLOUT_RUNNING;
299:
300: /* assume that we won't need any later callouts: */
301: later_callouts = 0;
302:
303: /* loop while callouts are needed: */
304: for (; (callouts = bpf->tme_bsd_bpf_callout_flags) & TME_BSD_BPF_CALLOUTS_MASK; ) {
305:
306: /* clear the needed callouts: */
307: bpf->tme_bsd_bpf_callout_flags = callouts & ~TME_BSD_BPF_CALLOUTS_MASK;
308: callouts &= TME_BSD_BPF_CALLOUTS_MASK;
309:
310: /* get our Ethernet connection: */
311: conn_eth = bpf->tme_bsd_bpf_eth_connection;
312:
313: /* if we need to call out new control information: */
314: if (callouts & TME_BSD_BPF_CALLOUT_CTRL) {
315:
316: /* form the new ctrl: */
317: ctrl = 0;
318: if (bpf->tme_bsd_bpf_buffer_offset
319: < bpf->tme_bsd_bpf_buffer_end) {
320: ctrl |= TME_ETHERNET_CTRL_OK_READ;
321: }
322:
323: /* unlock the mutex: */
324: tme_mutex_unlock(&bpf->tme_bsd_bpf_mutex);
325:
326: /* do the callout: */
327: rc = (conn_eth != NULL
328: ? ((*conn_eth->tme_ethernet_connection_ctrl)
329: (conn_eth,
330: ctrl))
331: : TME_OK);
332:
333: /* lock the mutex: */
334: tme_mutex_lock(&bpf->tme_bsd_bpf_mutex);
335:
336: /* if the callout was unsuccessful, remember that at some later
337: time this callout should be attempted again: */
338: if (rc != TME_OK) {
339: later_callouts |= TME_BSD_BPF_CALLOUT_CTRL;
340: }
341: }
342:
343: /* if the Ethernet is readable: */
344: if (callouts & TME_BSD_BPF_CALLOUT_READ) {
345:
346: /* unlock the mutex: */
347: tme_mutex_unlock(&bpf->tme_bsd_bpf_mutex);
348:
349: /* make a frame chunk to receive this frame: */
350: frame_chunk_buffer.tme_ethernet_frame_chunk_next = NULL;
351: frame_chunk_buffer.tme_ethernet_frame_chunk_bytes = frame;
352: frame_chunk_buffer.tme_ethernet_frame_chunk_bytes_count
353: = sizeof(frame);
354:
355: /* do the callout: */
356: rc = (conn_eth == NULL
357: ? TME_OK
358: : ((*conn_eth->tme_ethernet_connection_read)
359: (conn_eth,
360: &frame_id,
361: &frame_chunk_buffer,
362: TME_ETHERNET_READ_NEXT)));
363:
364: /* lock the mutex: */
365: tme_mutex_lock(&bpf->tme_bsd_bpf_mutex);
366:
367: /* if the read was successful: */
368: if (rc > 0) {
369:
370: /* do the write: */
371: tme_thread_write(bpf->tme_bsd_bpf_fd, frame, rc);
372:
373: /* mark that we need to loop to callout to read more frames: */
374: bpf->tme_bsd_bpf_callout_flags |= TME_BSD_BPF_CALLOUT_READ;
375: }
376:
377: /* otherwise, the read failed. convention dictates that we
378: forget that the connection was readable, which we already
379: have done by clearing the CALLOUT_READ flag: */
380: }
381:
382: }
383:
384: /* put in any later callouts, and clear that callouts are running: */
385: bpf->tme_bsd_bpf_callout_flags = later_callouts;
386: }
387:
388: /* the BPF reader thread: */
389: static void
390: _tme_bsd_bpf_th_reader(struct tme_bsd_bpf *bpf)
391: {
392: ssize_t buffer_end;
393:
394: /* lock the mutex: */
395: tme_mutex_lock(&bpf->tme_bsd_bpf_mutex);
396:
397: /* loop forever: */
398: for (;;) {
399:
400: /* if the buffer is not empty, wait until it is: */
401: if (bpf->tme_bsd_bpf_buffer_offset
402: < bpf->tme_bsd_bpf_buffer_end) {
403: tme_cond_wait_yield(&bpf->tme_bsd_bpf_cond_reader,
404: &bpf->tme_bsd_bpf_mutex);
405: }
406:
407: /* unlock the mutex: */
408: tme_mutex_unlock(&bpf->tme_bsd_bpf_mutex);
409:
410: /* read the BPF socket: */
411: tme_log(&bpf->tme_bsd_bpf_element->tme_element_log_handle, 1, TME_OK,
412: (&bpf->tme_bsd_bpf_element->tme_element_log_handle,
413: _("calling read")));
414: buffer_end =
415: tme_thread_read_yield(bpf->tme_bsd_bpf_fd,
416: bpf->tme_bsd_bpf_buffer,
417: bpf->tme_bsd_bpf_buffer_size);
418:
419: /* lock the mutex: */
420: tme_mutex_lock(&bpf->tme_bsd_bpf_mutex);
421:
422: /* if the read failed: */
423: if (buffer_end <= 0) {
424: tme_log(&bpf->tme_bsd_bpf_element->tme_element_log_handle, 1, errno,
425: (&bpf->tme_bsd_bpf_element->tme_element_log_handle,
426: _("failed to read packets")));
427: continue;
428: }
429:
430: /* the read succeeded: */
431: tme_log(&bpf->tme_bsd_bpf_element->tme_element_log_handle, 1, TME_OK,
432: (&bpf->tme_bsd_bpf_element->tme_element_log_handle,
433: _("read %ld bytes of packets"), (long) buffer_end));
434: bpf->tme_bsd_bpf_buffer_offset = 0;
435: bpf->tme_bsd_bpf_buffer_end = buffer_end;
436:
437: /* call out that we can be read again: */
438: _tme_bsd_bpf_callout(bpf, TME_BSD_BPF_CALLOUT_CTRL);
439: }
440: /* NOTREACHED */
441: }
442:
443: /* this is called when the ethernet configuration changes: */
444: static int
445: _tme_bsd_bpf_config(struct tme_ethernet_connection *conn_eth,
446: struct tme_ethernet_config *config)
447: {
448: struct tme_bsd_bpf *bpf;
449: struct bpf_insn *bpf_filter;
450: struct bpf_program program;
451: int bpf_filter_size, first_pc;
452: int rc;
453:
454: /* recover our data structures: */
455: bpf = conn_eth->tme_ethernet_connection.tme_connection_element->tme_element_private;
456:
457: /* assume we will succeed: */
458: rc = TME_OK;
459:
460: /* lock the mutex: */
461: tme_mutex_lock(&bpf->tme_bsd_bpf_mutex);
462:
463: /* allocate space for the worst-case filter: one insn for the packet
464: accept, one insn for the packet reject, and TME_ETHERNET_ADDR_SIZE
465: * 2 insns for each address - one insn to load an address byte and
466: one insn to test it and branch: */
467: bpf_filter_size = (1
468: + 1
469: + ((1 + 1)
470: * TME_ETHERNET_ADDR_SIZE
471: * config->tme_ethernet_config_addr_count));
472: bpf_filter = tme_new(struct bpf_insn, bpf_filter_size);
473: first_pc = bpf_filter_size;
474:
475: /* if this Ethernet is promiscuous, we will accept all packets: */
476: if (config->tme_ethernet_config_flags & TME_ETHERNET_CONFIG_PROMISC) {
477: bpf_filter[--first_pc] = _tme_bsd_bpf_insn_accept;
478: }
479:
480: /* if this Ethernet does have a set of addresses, we will accept all
481: packets for one of those addresses: */
482: else if (config->tme_ethernet_config_addr_count > 0) {
483:
484: /* the last insn in the filter is always the packet reject,
485: and the next-to-last insn in the filter is always the
486: packet accept. _tme_bsd_bpf_filter depends on this: */
487: bpf_filter[--first_pc] = _tme_bsd_bpf_insn_reject;
488: bpf_filter[--first_pc] = _tme_bsd_bpf_insn_accept;
489:
490: /* make the address filter: */
491: _tme_bsd_bpf_filter(config,
492: NULL,
493: 0,
494: bpf_filter,
495: bpf_filter_size,
496: &first_pc);
497: }
498:
499: /* otherwise this filter doesn't need to accept any packets: */
500: else {
501: bpf_filter[--first_pc] = _tme_bsd_bpf_insn_reject;
502: }
503:
504: /* set the filter on the BPF device: */
505: program.bf_len = bpf_filter_size - first_pc;
506: program.bf_insns = bpf_filter + first_pc;
507: if (ioctl(bpf->tme_bsd_bpf_fd, BIOCSETF, &program) < 0) {
508: tme_log(&bpf->tme_bsd_bpf_element->tme_element_log_handle, 1, errno,
509: (&bpf->tme_bsd_bpf_element->tme_element_log_handle,
510: _("failed to set the filter")));
511: rc = errno;
512: }
513:
514: /* free the filter: */
515: tme_free(bpf_filter);
516:
517: /* unlock the mutex: */
518: tme_mutex_unlock(&bpf->tme_bsd_bpf_mutex);
519:
520: /* done: */
521: return (rc);
522: }
523:
524: /* this is called when control lines change: */
525: static int
526: _tme_bsd_bpf_ctrl(struct tme_ethernet_connection *conn_eth,
527: unsigned int ctrl)
528: {
529: struct tme_bsd_bpf *bpf;
530: int new_callouts;
531:
532: /* recover our data structures: */
533: bpf = conn_eth->tme_ethernet_connection.tme_connection_element->tme_element_private;
534:
535: /* assume that we won't need any new callouts: */
536: new_callouts = 0;
537:
538: /* lock the mutex: */
539: tme_mutex_lock(&bpf->tme_bsd_bpf_mutex);
540:
541: /* if this connection is readable, call out a read: */
542: if (ctrl & TME_ETHERNET_CTRL_OK_READ) {
543: new_callouts |= TME_BSD_BPF_CALLOUT_READ;
544: }
545:
546: /* make any new callouts: */
547: _tme_bsd_bpf_callout(bpf, new_callouts);
548:
549: /* unlock the mutex: */
550: tme_mutex_unlock(&bpf->tme_bsd_bpf_mutex);
551:
552: return (TME_OK);
553: }
554:
555: /* this is called to read a frame: */
556: static int
557: _tme_bsd_bpf_read(struct tme_ethernet_connection *conn_eth,
558: tme_ethernet_fid_t *_frame_id,
559: struct tme_ethernet_frame_chunk *frame_chunks,
560: unsigned int flags)
561: {
562: struct tme_bsd_bpf *bpf;
563: struct bpf_hdr the_bpf_header;
564: struct tme_ethernet_frame_chunk frame_chunk_buffer;
565: unsigned int count;
566: int rc;
567:
568: /* recover our data structure: */
569: bpf = conn_eth->tme_ethernet_connection.tme_connection_element->tme_element_private;
570:
571: /* lock our mutex: */
572: tme_mutex_lock(&bpf->tme_bsd_bpf_mutex);
573:
574: /* assume that we won't be able to return a packet: */
575: rc = -ENOENT;
576:
577: /* loop until we have a good captured packet or until we
578: exhaust the buffer: */
579: for (;;) {
580:
581: /* if there's not enough for a BPF header, flush the buffer: */
582: if ((bpf->tme_bsd_bpf_buffer_offset
583: + sizeof(the_bpf_header))
584: > bpf->tme_bsd_bpf_buffer_end) {
585: tme_log(&bpf->tme_bsd_bpf_element->tme_element_log_handle, 1, TME_OK,
586: (&bpf->tme_bsd_bpf_element->tme_element_log_handle,
587: _("flushed garbage BPF header bytes")));
588: bpf->tme_bsd_bpf_buffer_end = 0;
589: break;
590: }
591:
592: /* get the BPF header and check it: */
593: memcpy(&the_bpf_header,
594: bpf->tme_bsd_bpf_buffer
595: + bpf->tme_bsd_bpf_buffer_offset,
596: sizeof(the_bpf_header));
597: bpf->tme_bsd_bpf_buffer_offset += the_bpf_header.bh_hdrlen;
598:
599: /* if we're missing some part of the packet: */
600: if (the_bpf_header.bh_caplen != the_bpf_header.bh_datalen
601: || ((bpf->tme_bsd_bpf_buffer_offset + the_bpf_header.bh_datalen)
602: > bpf->tme_bsd_bpf_buffer_end)) {
603: tme_log(&bpf->tme_bsd_bpf_element->tme_element_log_handle, 1, TME_OK,
604: (&bpf->tme_bsd_bpf_element->tme_element_log_handle,
605: _("flushed truncated BPF packet")));
606: bpf->tme_bsd_bpf_buffer_offset += the_bpf_header.bh_datalen;
607: continue;
608: }
609:
610: /* if this packet isn't big enough to even have an Ethernet header: */
611: if (the_bpf_header.bh_datalen < sizeof(struct tme_ethernet_header)) {
612: tme_log(&bpf->tme_bsd_bpf_element->tme_element_log_handle, 1, TME_OK,
613: (&bpf->tme_bsd_bpf_element->tme_element_log_handle,
614: _("flushed short BPF packet")));
615: bpf->tme_bsd_bpf_buffer_offset += the_bpf_header.bh_datalen;
616: continue;
617: }
618:
619: /* form the single frame chunk: */
620: frame_chunk_buffer.tme_ethernet_frame_chunk_next = NULL;
621: frame_chunk_buffer.tme_ethernet_frame_chunk_bytes
622: = bpf->tme_bsd_bpf_buffer + bpf->tme_bsd_bpf_buffer_offset;
623: frame_chunk_buffer.tme_ethernet_frame_chunk_bytes_count
624: = the_bpf_header.bh_datalen;
625:
626: /* copy out the frame: */
627: count = tme_ethernet_chunks_copy(frame_chunks, &frame_chunk_buffer);
628:
629: /* if this isn't a peek: */
630: if (!(flags & TME_ETHERNET_READ_PEEK)) {
631:
632: /* update the buffer pointer: */
633: bpf->tme_bsd_bpf_buffer_offset += the_bpf_header.bh_datalen;
634: }
635:
636: /* success: */
637: rc = count;
638: break;
639: }
640:
641: /* if the buffer is empty, notify the reader that we need more data: */
642: if (bpf->tme_bsd_bpf_buffer_offset
643: >= bpf->tme_bsd_bpf_buffer_end) {
644: tme_cond_notify(&bpf->tme_bsd_bpf_cond_reader, TRUE);
645: }
646:
647: /* unlock our mutex: */
648: tme_mutex_unlock(&bpf->tme_bsd_bpf_mutex);
649:
650: /* done: */
651: return (rc);
652: }
653:
654: /* this makes a new Ethernet connection: */
655: static int
656: _tme_bsd_bpf_connection_make(struct tme_connection *conn, unsigned int state)
657: {
658: struct tme_bsd_bpf *bpf;
659: struct tme_ethernet_connection *conn_eth;
660: struct tme_ethernet_connection *conn_eth_other;
661:
662: /* recover our data structures: */
663: bpf = conn->tme_connection_element->tme_element_private;
664: conn_eth = (struct tme_ethernet_connection *) conn;
665: conn_eth_other = (struct tme_ethernet_connection *) conn->tme_connection_other;
666:
667: /* both sides must be Ethernet connections: */
668: assert(conn->tme_connection_type == TME_CONNECTION_ETHERNET);
669: assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_ETHERNET);
670:
671: /* we're always set up to answer calls across the connection, so we
672: only have to do work when the connection has gone full, namely
673: taking the other side of the connection: */
674: if (state == TME_CONNECTION_FULL) {
675:
676: /* lock our mutex: */
677: tme_mutex_lock(&bpf->tme_bsd_bpf_mutex);
678:
679: /* save our connection: */
680: bpf->tme_bsd_bpf_eth_connection = conn_eth_other;
681:
682: /* unlock our mutex: */
683: tme_mutex_unlock(&bpf->tme_bsd_bpf_mutex);
684: }
685:
686: return (TME_OK);
687: }
688:
689: /* this breaks a connection: */
690: static int
691: _tme_bsd_bpf_connection_break(struct tme_connection *conn, unsigned int state)
692: {
693: abort();
694: }
695:
696: /* this makes a new connection side for a BPF: */
697: static int
698: _tme_bsd_bpf_connections_new(struct tme_element *element,
699: const char * const *args,
700: struct tme_connection **_conns,
701: char **_output)
702: {
703: struct tme_bsd_bpf *bpf;
704: struct tme_ethernet_connection *conn_eth;
705: struct tme_connection *conn;
706:
707: /* recover our data structure: */
708: bpf = (struct tme_bsd_bpf *) element->tme_element_private;
709:
710: /* if we already have an Ethernet connection, do nothing: */
711: if (bpf->tme_bsd_bpf_eth_connection != NULL) {
712: return (TME_OK);
713: }
714:
715: /* allocate the new Ethernet connection: */
716: conn_eth = tme_new0(struct tme_ethernet_connection, 1);
717: conn = &conn_eth->tme_ethernet_connection;
718:
719: /* fill in the generic connection: */
720: conn->tme_connection_next = *_conns;
721: conn->tme_connection_type = TME_CONNECTION_ETHERNET;
722: conn->tme_connection_score = tme_ethernet_connection_score;
723: conn->tme_connection_make = _tme_bsd_bpf_connection_make;
724: conn->tme_connection_break = _tme_bsd_bpf_connection_break;
725:
726: /* fill in the Ethernet connection: */
727: conn_eth->tme_ethernet_connection_config = _tme_bsd_bpf_config;
728: conn_eth->tme_ethernet_connection_ctrl = _tme_bsd_bpf_ctrl;
729: conn_eth->tme_ethernet_connection_read = _tme_bsd_bpf_read;
730:
731: /* return the connection side possibility: */
732: *_conns = conn;
733:
734: /* done: */
735: return (TME_OK);
736: }
737:
738: /* the new BPF function: */
739: TME_ELEMENT_SUB_NEW_DECL(tme_host_bsd,bpf) {
740: struct tme_bsd_bpf *bpf;
741: int bpf_fd;
742: #define DEV_BPF_FORMAT "/dev/bpf%d"
743: char dev_bpf_filename[sizeof(DEV_BPF_FORMAT) + (sizeof(int) * 3) + 1];
744: int minor;
745: int saved_errno;
746: u_int bpf_opt;
747: struct bpf_version version;
748: u_int packet_buffer_size;
749: const char *ifr_name_user;
750: struct ifreq *ifr;
751: int arg_i;
752: int usage;
753: int rc;
754:
755: /* check our arguments: */
756: usage = 0;
757: ifr_name_user = NULL;
758: arg_i = 1;
759: for (;;) {
760:
761: /* the interface we're supposed to use: */
762: if (TME_ARG_IS(args[arg_i + 0], "interface")
763: && args[arg_i + 1] != NULL) {
764: ifr_name_user = args[arg_i + 1];
765: arg_i += 2;
766: }
767:
768: /* if we ran out of arguments: */
769: else if (args[arg_i + 0] == NULL) {
770: break;
771: }
772:
773: /* otherwise this is a bad argument: */
774: else {
775: tme_output_append_error(_output,
776: "%s %s",
777: args[arg_i],
778: _("unexpected"));
779: usage = TRUE;
780: break;
781: }
782: }
783:
784: if (usage) {
785: tme_output_append_error(_output,
786: "%s %s [ interface %s ]",
787: _("usage:"),
788: args[0],
789: _("INTERFACE"));
790: return (EINVAL);
791: }
792:
793: /* find the interface we will use: */
794: rc = tme_bsd_if_find(ifr_name_user, &ifr, NULL, NULL);
795: if (rc != TME_OK) {
796: tme_output_append_error(_output, _("couldn't find an interface"));
797: return (ENOENT);
798: }
799: tme_log(&element->tme_element_log_handle, 1, TME_OK,
800: (&element->tme_element_log_handle,
801: "using interface %s",
802: ifr->ifr_name));
803:
804: /* loop trying to open a /dev/bpf device: */
805: for (minor = 0;; minor++) {
806:
807: /* form the name of the next device to try, then try opening
808: it. if we succeed, we're done: */
809: sprintf(dev_bpf_filename, DEV_BPF_FORMAT, minor);
810: tme_log(&element->tme_element_log_handle, 1, TME_OK,
811: (&element->tme_element_log_handle,
812: "trying %s",
813: dev_bpf_filename));
814: if ((bpf_fd = open(dev_bpf_filename, O_RDWR)) >= 0) {
815: tme_log(&element->tme_element_log_handle, 1, TME_OK,
816: (&element->tme_element_log_handle,
817: "opened %s",
818: dev_bpf_filename));
819: break;
820: }
821:
822: /* we failed to open this device. if this device was simply
823: busy, loop: */
824: saved_errno = errno;
825: tme_log(&element->tme_element_log_handle, 1, saved_errno,
826: (&element->tme_element_log_handle,
827: "%s", dev_bpf_filename));
828: if (saved_errno == EBUSY
829: || saved_errno == EACCES) {
830: continue;
831: }
832:
833: /* otherwise, we have failed: */
834: return (saved_errno);
835: }
836:
837: /* this macro helps in closing the BPF socket on error: */
838: #define _TME_BPF_RAW_OPEN_ERROR(x) saved_errno = errno; x; errno = saved_errno
839:
840: /* check the BPF version: */
841: if (ioctl(bpf_fd, BIOCVERSION, &version) < 0) {
842: tme_log(&element->tme_element_log_handle, 1, errno,
843: (&element->tme_element_log_handle,
844: _("failed to get the BPF version on %s"),
845: dev_bpf_filename));
846: _TME_BPF_RAW_OPEN_ERROR(close(bpf_fd));
847: return (errno);
848: }
849: if (version.bv_major != BPF_MAJOR_VERSION
850: || version.bv_minor < BPF_MINOR_VERSION) {
851: tme_log(&element->tme_element_log_handle, 1, errno,
852: (&element->tme_element_log_handle,
853: _("kernel BPF version is %d.%d, my BPF version is %d.%d"),
854: version.bv_major, version.bv_minor,
855: BPF_MAJOR_VERSION, BPF_MINOR_VERSION));
856: close(bpf_fd);
857: return (ENXIO);
858: }
859:
860: /* put the BPF device into immediate mode: */
861: bpf_opt = TRUE;
862: if (ioctl(bpf_fd, BIOCIMMEDIATE, &bpf_opt) < 0) {
863: tme_log(&element->tme_element_log_handle, 1, errno,
864: (&element->tme_element_log_handle,
865: _("failed to put %s into immediate mode"),
866: dev_bpf_filename));
867: _TME_BPF_RAW_OPEN_ERROR(close(bpf_fd));
868: return (errno);
869: }
870:
871: /* tell the BPF device we're providing complete Ethernet headers: */
872: bpf_opt = TRUE;
873: if (ioctl(bpf_fd, BIOCSHDRCMPLT, &bpf_opt) < 0) {
874: tme_log(&element->tme_element_log_handle, 1, errno,
875: (&element->tme_element_log_handle,
876: _("failed to put %s into complete-headers mode"),
877: dev_bpf_filename));
878: _TME_BPF_RAW_OPEN_ERROR(close(bpf_fd));
879: return (errno);
880: }
881:
882: /* point the BPF device at the interface we're using: */
883: if (ioctl(bpf_fd, BIOCSETIF, ifr) < 0) {
884: tme_log(&element->tme_element_log_handle, 1, errno,
885: (&element->tme_element_log_handle,
886: _("failed to point BPF socket at %s"),
887: ifr->ifr_name));
888: saved_errno = errno;
889: close(bpf_fd);
890: errno = saved_errno;
891: return (errno);
892: }
893:
894: /* get the BPF read buffer size: */
895: if (ioctl(bpf_fd, BIOCGBLEN, &packet_buffer_size) < 0) {
896: tme_log(&element->tme_element_log_handle, 1, errno,
897: (&element->tme_element_log_handle,
898: _("failed to read the buffer size for %s"),
899: dev_bpf_filename));
900: _TME_BPF_RAW_OPEN_ERROR(close(bpf_fd));
901: return (errno);
902: }
903: tme_log(&element->tme_element_log_handle, 1, errno,
904: (&element->tme_element_log_handle,
905: _("buffer size for %s is %u"),
906: dev_bpf_filename, packet_buffer_size));
907:
908: /* set the interface into promiscuous mode: */
909: if (ioctl(bpf_fd, BIOCPROMISC) < 0) {
910: tme_log(&element->tme_element_log_handle, 1, errno,
911: (&element->tme_element_log_handle,
912: _("failed to set promiscuous mode on %s"),
913: dev_bpf_filename));
914: _TME_BPF_RAW_OPEN_ERROR(close(bpf_fd));
915: return (errno);
916: }
917:
918: /* start our data structure: */
919: bpf = tme_new0(struct tme_bsd_bpf, 1);
920: bpf->tme_bsd_bpf_element = element;
921: bpf->tme_bsd_bpf_fd = bpf_fd;
922: bpf->tme_bsd_bpf_buffer_size = packet_buffer_size;
923: bpf->tme_bsd_bpf_buffer = tme_new(tme_uint8_t, packet_buffer_size);
924:
925: /* start the threads: */
926: tme_mutex_init(&bpf->tme_bsd_bpf_mutex);
927: tme_cond_init(&bpf->tme_bsd_bpf_cond_writer);
928: tme_thread_create((tme_thread_t) _tme_bsd_bpf_th_reader, bpf);
929:
930: /* fill the element: */
931: element->tme_element_private = bpf;
932: element->tme_element_connections_new = _tme_bsd_bpf_connections_new;
933:
934: return (TME_OK);
935: #undef _TME_BPF_RAW_OPEN_ERROR
936: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.