|
|
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.