Annotation of tme/host/bsd/bsd-bpf.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.