net-pcap.cc

Go to the documentation of this file.
00001 /*-
00002  * Copyright (c) 1998 The Regents of the University of California.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. All advertising materials mentioning features or use of this software
00014  *    must display the following acknowledgement:
00015  *      This product includes software developed by the University of
00016  *      California, Berkeley and the Network Research Group at
00017  *      Lawrence Berkeley Laboratory.
00018  * 4. Neither the name of the University nor of the Laboratory may be used
00019  *    to endorse or promote products derived from this software without
00020  *    specific prior written permission.
00021  *
00022  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00023  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00024  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00025  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00026  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00027  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00028  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00029  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00031  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00032  * SUCH DAMAGE.
00033  */
00034 #ifndef lint
00035 static const char rcsid[] =
00036     "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/emulate/net-pcap.cc,v 1.23 2005/09/07 06:35:45 tomh Exp $ (LBL)";
00037 #endif
00038 
00039 #include <stdio.h>
00040 #ifndef WIN32
00041 #include <unistd.h>
00042 #endif
00043 #include <time.h>
00044 #include <errno.h>
00045 #include <string.h>
00046 #ifdef WIN32
00047 #include <io.h>
00048 #define close closesocket
00049 #else
00050 #include <sys/param.h>
00051 #include <sys/socket.h>
00052 #include <sys/ioctl.h>
00053 #endif
00054 #if defined(sun) && defined(__svr4__)
00055 #include <sys/systeminfo.h>
00056 #endif
00057 
00058 #ifdef __cplusplus
00059 extern "C" {
00060 #include <pcap.h>
00061 }
00062 #else
00063 #include <pcap.h>
00064 #endif
00065 
00066 #include "config.h"
00067 #include "scheduler.h"
00068 #include "net.h"
00069 #include "tclcl.h"
00070 #include "packet.h"
00071 
00072 /*
00073  * observations about pcap library
00074  *  device name is in the ifreq struct sense, should be doc'd
00075  *  pcap_lookupdev returns a ptr to static data
00076  *  q: does lookupdev only return devs in the AF_INET addr family?
00077  *  why does pcap_compile require a netmask? seems odd
00078  *  would like some way to tell it what buffer to use
00079  *  arriving packets have the link layer hdr at the beginning, doc
00080  *  not convenient/possible to open bpf read/write
00081  *  no real way to know what file (/dev/bpf?) it is using
00082  *      would be nice if pcap_lookdev helped out more by
00083  *      returning ifnet or ifreq or whatever structure
00084  *  pcap_lookupnet makes calls to get our addr, but
00085  *      then tosses it anyhow, should get us addr and netmask
00086  *  interface type codes could be via rfc1573
00087  *      see freebsd net/if_types.h
00088  *  want a way to set immed mode
00089  *  pcap_next masks errors by returning 0 if pcap_dispatch fails
00090  *  a pcap_t carries it's own internal buffer, and
00091  *      _dispatch gives pointers into it when invoked [eek]
00092  *  when you open pcap using a file, pcap_fileno always
00093  *      returns -1; not so convenient
00094  *  
00095  */
00096 
00097 #define PNET_PSTATE_INACTIVE    0
00098 #define PNET_PSTATE_ACTIVE  1
00099 
00100 //
00101 // PcapNetwork: a "network" (source or possibly sink of packets)
00102 //  this is a base class only-- the derived classes are:
00103 //  PcapLiveNetwork [a live net; currently bpf + ethernet]
00104 //  PcapFileNetwork [packets from a tcpdump-style trace file]
00105 //
00106 
00107 class PcapNetwork : public Network {
00108 
00109 public:
00110     PcapNetwork() : t_firstpkt_(0.0),
00111         pfd_(-1), pcnt_(0), local_netmask_(0) { }
00112     int rchannel() { return(pfd_); }
00113     int schannel() { return(pfd_); }
00114     virtual int command(int argc, const char*const* argv);
00115 
00116     virtual int open(int mode, const char *) = 0;
00117     virtual int skiphdr() = 0;
00118     virtual double gents(pcap_pkthdr*) = 0;     // generate timestamp
00119     int recv(u_char *buf, int len, sockaddr&, double&); // get from net
00120     int send(u_char *buf, int len);         // write to net
00121     int recv(netpkt_handler callback, void *clientdata); // get from net
00122     void close();
00123     void reset();
00124 
00125     int filter(const char*);    // compile + install a filter
00126     int stat_pkts();
00127     int stat_pdrops();
00128 
00129     double offset_;         // time offset to 1st pkt in a trace
00130     double t_firstpkt_;     // ts of 1st pkt recvd
00131 
00132 protected:
00133     static void phandler(u_char* u, const pcap_pkthdr* h, const u_char* p);
00134     static void phandler_callback(u_char* u, const pcap_pkthdr* h, const u_char* p);
00135     virtual void bindvars() = 0;
00136 
00137     char errbuf_[PCAP_ERRBUF_SIZE];     // place to put err msgs
00138     char srcname_[PATH_MAX];        // device or file name
00139     int pfd_;               // pcap fd
00140     int pcnt_;              // # pkts counted
00141     int state_;             // PNET_PSTATE_xxx (above)
00142     int optimize_;              // bpf optimizer enable
00143     pcap_t* pcap_;              // reference to pcap state
00144     struct bpf_program bpfpgm_;     // generated program
00145     struct pcap_stat pcs_;          // status
00146 
00147     unsigned int local_netmask_;    // seems shouldn't be necessary :(
00148 };
00149 
00150 //
00151 // PcapLiveNetwork: a live network tap
00152 //
00153 
00154 struct NetworkAddress {
00155         u_int   len_;
00156         u_char  addr_[16];  // enough for IPv6 ip addr
00157 };
00158 
00159 class PcapLiveNetwork : public PcapNetwork {
00160 public:
00161     PcapLiveNetwork() : local_net_(0), dlink_type_(-1) {
00162         linkaddr_.len_ = 0;
00163         netaddr_.len_ = 0;
00164         bindvars(); reset();
00165     }
00166     NetworkAddress& laddr() { return (linkaddr_); }
00167     NetworkAddress& naddr() { return (netaddr_); }
00168 protected:
00169     double gents(pcap_pkthdr*) {
00170         return Scheduler::instance().clock();
00171     }
00172 
00173     int devtonaddr(const char* name, NetworkAddress&);
00174 
00175     int open(int mode);
00176     int open(int mode, const char*);
00177     int command(int argc, const char*const* argv);
00178     int skiphdr();
00179     const char* autodevname();
00180     void        bindvars();
00181 
00182     int snaplen_;       // # of bytes to grab
00183     int promisc_;       // put intf into promisc mode?
00184     double timeout_;
00185     NetworkAddress  linkaddr_;  // link-layer address
00186     NetworkAddress  netaddr_;   // network-layer (IP) address
00187 
00188     unsigned int local_net_;
00189     int dlink_type_;        // data link type (see pcap)
00190 private:
00191     // XXX somewhat specific to bpf-- this stuff is  a hack until pcap
00192     // can be fixed to allow for opening the bpf r/w
00193 #ifdef MT_OWN_PCAP
00194     pcap_t * pcap_open_live(char *, int slen, int prom, int, char *, int);
00195     int bpf_open(pcap_t *p, char *errbuf, int how);
00196 #endif
00197 };
00198 
00199 class PcapFileNetwork : public PcapNetwork {
00200 public:
00201     int open(int mode, const char *);
00202     int skiphdr() { return 0; } // XXX check me
00203 protected:
00204     double gents(pcap_pkthdr* p) {
00205         // time stamp of packet is its relative time
00206         // in the trace file, plus sim start time, plus offset
00207         double pts = p->ts.tv_sec + p->ts.tv_usec * 0.000001;
00208         pts -= t_firstpkt_;
00209         pts += offset_ + Scheduler::instance().clock();
00210         return (pts);
00211     }
00212     void bindvars();
00213     int command(int argc, const char*const* argv);
00214 };
00215 
00216 static class PcapLiveNetworkClass : public TclClass {
00217 public:
00218     PcapLiveNetworkClass() : TclClass("Network/Pcap/Live") {}
00219     TclObject* create(int, const char*const*) {
00220         return (new PcapLiveNetwork);
00221     }
00222 } net_pcaplive;
00223 
00224 static class PcapFileNetworkClass : public TclClass {
00225 public:
00226     PcapFileNetworkClass() : TclClass("Network/Pcap/File") {}
00227     TclObject* create(int, const char*const*) {
00228         return (new PcapFileNetwork);
00229     }
00230 } net_pcapfile;
00231 
00232 //
00233 // defs for base PcapNetwork class
00234 //
00235 
00236 void
00237 PcapNetwork::bindvars()
00238 {
00239     bind_bool("optimize_", &optimize_);
00240 }
00241 
00242 void
00243 PcapNetwork::reset()
00244 {
00245     state_ = PNET_PSTATE_INACTIVE;
00246     pfd_ = -1;
00247     pcap_ = NULL;
00248     *errbuf_ = '\0';
00249     *srcname_ = '\0';
00250     pcnt_ = 0;
00251 }
00252 
00253 void
00254 PcapNetwork::close()
00255 {
00256     if (state_ == PNET_PSTATE_ACTIVE && pcap_)
00257         pcap_close(pcap_);
00258     reset();
00259 }
00260 
00261 /* compile up a bpf program */
00262 /* XXXwe aren't using 'bcast', so don't care about mask... sigh */
00263 
00264 int
00265 PcapNetwork::filter(const char *pgm)
00266 {
00267     if (pcap_compile(pcap_, &bpfpgm_, (char *)pgm,
00268         optimize_, local_netmask_) < 0) {
00269         fprintf(stderr, "pcapnet obj(%s): couldn't compile filter pgm",
00270             name());
00271         return -1;
00272     }
00273     if (pcap_setfilter(pcap_, &bpfpgm_) < 0) {
00274         fprintf(stderr, "pcapnet obj(%s): couldn't set filter pgm",
00275             name());
00276         return -1;
00277     }
00278     return(bpfpgm_.bf_len);
00279 }
00280 
00281 /* return number of pkts received */
00282 int
00283 PcapNetwork::stat_pkts()
00284 {
00285     if (pcap_stats(pcap_, &pcs_) < 0)
00286         return (-1);
00287 
00288     return (pcs_.ps_recv);
00289 }
00290 
00291 /* return number of pkts dropped */
00292 int
00293 PcapNetwork::stat_pdrops()
00294 {
00295     if (pcap_stats(pcap_, &pcs_) < 0)
00296         return (-1);
00297 
00298     return (pcs_.ps_drop);
00299 }
00300 
00301 #ifndef MIN
00302 #define MIN(x, y) ((x)<(y) ? (x) : (y))
00303 #endif
00304 
00305 #include "ether.h"
00306 /* recv is what others call to grab a packet from the pfilter */
00307 
00308 struct pcap_singleton {
00309         struct pcap_pkthdr *hdr;
00310         const u_char *pkt;
00311 };   
00312 
00313 struct pcap_singleton_callback {
00314         netpkt_handler callback;
00315         void *clientdata;
00316         PcapNetwork *net;
00317 };   
00318 
00319 void
00320 PcapNetwork::phandler(u_char* userdata, const pcap_pkthdr* ph, const u_char* pkt)
00321 {
00322     pcap_singleton *ps = (pcap_singleton*) userdata;
00323     ps->hdr = (pcap_pkthdr*)ph;
00324     ps->pkt = (u_char*)pkt;
00325 }
00326 
00327 void
00328 PcapNetwork::phandler_callback(u_char* userdata, const pcap_pkthdr* ph, const u_char* pkt)
00329 {
00330     pcap_singleton_callback *ps = (pcap_singleton_callback*) userdata;  
00331 
00332     Packet *p = Packet::alloc(ph->caplen);
00333     PcapNetwork *inst = ps->net;
00334     
00335     if (++(inst->pcnt_) == 1) {
00336         // mark time stamp of first pkt
00337         inst->t_firstpkt_ = ph->ts.tv_sec + ph->ts.tv_usec * 0.000001;
00338     }
00339 
00340     // link layer header will be placed at the beginning from pcap
00341     int s = inst->skiphdr();    // go to IP header
00342     memcpy(p->accessdata(), pkt + s, ph->caplen - s);
00343 
00344     ps->callback(ps->clientdata, p, ph->ts);
00345 }
00346 
00347 int
00348 PcapNetwork::recv(u_char *buf, int len, sockaddr& /*fromaddr*/, double &ts)
00349 {
00350 
00351     if (state_ != PNET_PSTATE_ACTIVE) {
00352         fprintf(stderr, "warning: net/pcap obj(%s) read-- not active\n",
00353             name());
00354         return -1;
00355     }
00356 
00357     int pktcnt = 1;     // all in buffer, or until error
00358     int np;         // counts # of pkts dispatched
00359     pcap_singleton ps = { 0, 0 };
00360     np = pcap_dispatch(pcap_, pktcnt, phandler, (u_char*) &ps);
00361     if (np < 0) {
00362         fprintf(stderr,
00363             "PcapNetwork(%s): recv: pcap_dispatch: %s\n",
00364                 name(), pcap_strerror(errno));
00365         return (np);
00366     } else if (np == 0) {
00367         /* we get here on EOF of a Pcap/File Network */
00368         return (np);
00369     } else if (np != pktcnt) {
00370         fprintf(stderr,
00371             "PcapNetwork(%s): warning: recv: pcap_dispatch: requested pktcnt (%d) doesn't match actual (%d)\n",
00372                 name(), pktcnt, np);
00373     }
00374 
00375     pcap_pkthdr* ph = ps.hdr;
00376 
00377     if (ph == NULL || ps.pkt == NULL) {
00378         fprintf(stderr,
00379             "PcapNetwork(%s): recv: pcap_dispatch: no packet present\n",
00380                 name());
00381         return (-1);
00382     }
00383 
00384     if (++pcnt_ == 1) {
00385         // mark time stamp of first pkt
00386         t_firstpkt_ = ph->ts.tv_sec + ph->ts.tv_usec * 0.000001;
00387     }
00388 
00389     int n = MIN(ph->caplen, (unsigned)len);
00390     ts = gents(ph); // mark with timestamp
00391     // link layer header will be placed at the beginning from pcap
00392     int s = skiphdr();  // go to IP header
00393     memcpy(buf, ps.pkt + s, n - s);
00394     return n - s;
00395 }
00396 
00397 int
00398 PcapNetwork::recv(netpkt_handler callback, void *clientdata)
00399 {
00400     if (state_ != PNET_PSTATE_ACTIVE) {
00401         fprintf(stderr, "warning: net/pcap obj(%s) read-- not active\n",
00402             name());
00403         return -1;
00404     }
00405 
00406     int pktcnt = -1;        // all in buffer, or until error
00407     int np;         // counts # of pkts dispatched
00408     pcap_singleton_callback ps = { callback, clientdata, this };
00409     
00410     np = pcap_dispatch(pcap_, pktcnt, phandler_callback, (u_char *)&ps);
00411 
00412 #ifdef MY_OWN_PCAP  // directly access pcap_t's member
00413     assert( pcap_->cc == 0 ); // i.e. we have emptied pcap's buffer
00414 #endif // MY_OWN_PCAP
00415     return np;
00416 }
00417 
00418 /* send a packet out through the packet filter */
00419 int
00420 PcapNetwork::send(u_char *buf, int len)
00421 {
00422     int n;
00423 
00424     if ((n = write(pfd_, buf, len)) < 0)
00425         perror("write to pcap fd");
00426 
00427     return n;
00428 }
00429 
00430 int PcapNetwork::command(int argc, const char*const* argv)
00431 {
00432     Tcl& tcl = Tcl::instance();
00433     if (argc == 2) {
00434         if (strcmp(argv[1], "close") == 0) {
00435             close();
00436             return (TCL_OK);
00437         }
00438         if (strcmp(argv[1], "srcname") == 0) {
00439             tcl.result(srcname_);
00440             return (TCL_OK);
00441         }
00442         if (strcmp(argv[1], "pkts") == 0) {
00443             tcl.resultf("%d", stat_pkts());
00444             return (TCL_OK);
00445         }
00446         if (strcmp(argv[1], "pdrops") == 0) {
00447             tcl.resultf("%d", stat_pdrops());
00448             return (TCL_OK);
00449         }
00450     } else if (argc == 3) {
00451         if (strcmp(argv[1], "filter") == 0) {
00452             if (state_ != PNET_PSTATE_ACTIVE) {
00453                 fprintf(stderr, "net/pcap obj(%s): can't install filter prior to opening data source\n",
00454                     name());
00455                 return (TCL_ERROR);
00456             }
00457             int plen;
00458             if ((plen = filter(argv[2])) < 0) {
00459                 fprintf(stderr, "problem compiling/installing filter program\n");
00460                 return (TCL_ERROR);
00461             }
00462             tcl.resultf("%d", plen);
00463             return (TCL_OK);
00464         }
00465     }
00466     return (Network::command(argc, argv));
00467 }
00468 
00469 //
00470 // defs for PcapLiveNetwork
00471 //
00472 
00473 #include <fcntl.h>
00474 
00475 #include <net/if.h>
00476 int
00477 PcapLiveNetwork::open(int mode, const char *devname)
00478 {
00479     close();
00480 #ifdef MY_OWN_PCAP
00481     pcap_ = pcap_open_live((char*) devname, snaplen_, promisc_,
00482                    int(timeout_ * 1000.), errbuf_, mode);
00483 #else
00484     pcap_ = pcap_open_live((char*) devname, snaplen_, promisc_,
00485                    int(timeout_ * 1000.), errbuf_);
00486 #endif // MY_OWN_PCAP
00487     if (pcap_ == NULL) {
00488         fprintf(stderr,
00489           "pcap/live object (%s) couldn't open packet source %s: %s\n",
00490             name(), devname, errbuf_);
00491         return -1;
00492     }
00493     mode_ = mode;
00494     dlink_type_ = pcap_datalink(pcap_);
00495     pfd_ = pcap_fileno(pcap_);
00496     strncpy(srcname_, devname, sizeof(srcname_)-1);
00497     {
00498         // use SIOCGIFADDR hook in bpf to get link addr
00499         struct ifreq ifr;
00500         struct sockaddr *sa = &ifr.ifr_addr;
00501 #ifdef HAVE_SIOCGIFHWADDR
00502         memset(&ifr, 0, sizeof(struct ifreq));
00503         strcpy(ifr.ifr_name, devname);
00504         if (ioctl(pfd_, SIOCGIFHWADDR, &ifr) < 0) {
00505             fprintf(stderr,
00506               "pcap/live (%s) SIOCGIFHWADDR on bpf fd %d\n",
00507               name(), pfd_);
00508         }
00509 #else
00510         if (ioctl(pfd_, SIOCGIFADDR, &ifr) < 0) {
00511             fprintf(stderr,
00512               "pcap/live (%s) SIOCGIFADDR on bpf fd %d\n",
00513               name(), pfd_);
00514         }
00515 #endif
00516         if (dlink_type_ != DLT_EN10MB) {
00517             fprintf(stderr,
00518                 "sorry, only ethernet supported\n");
00519             return -1;
00520         }
00521         linkaddr_.len_ = ETHER_ADDR_LEN;    // for now
00522         memcpy(linkaddr_.addr_, sa->sa_data, linkaddr_.len_);
00523     }
00524 
00525     (void) devtonaddr(devname, netaddr_);
00526 
00527     state_ = PNET_PSTATE_ACTIVE;
00528 
00529     if (pcap_lookupnet(srcname_, &local_net_, &local_netmask_, errbuf_) < 0) {
00530         fprintf(stderr,
00531           "warning: pcap/live (%s) couldn't get local IP network info: %s\n",
00532           name(), errbuf_) ;
00533     }
00534 #if !defined(__linux__)&&!defined(__APPLE__)
00535     {
00536         int immed = 1;
00537         if (ioctl(pfd_, BIOCIMMEDIATE, &immed) < 0) {
00538             fprintf(stderr,
00539                 "warning: pcap/live (%s) couldn't set immed\n",
00540                 name());
00541             perror("ioctl(BIOCIMMEDIATE)");
00542         }
00543     }
00544 #endif
00545     return 0;
00546 }
00547 
00548 /*
00549  * how many bytes of link-hdr to skip before net-layer hdr
00550  */
00551 
00552 int
00553 PcapLiveNetwork::skiphdr()
00554 {
00555     switch (dlink_type_) {
00556     case DLT_NULL:
00557         return 0;
00558 
00559     case DLT_EN10MB:
00560         return ETHER_HDR_LEN;
00561 
00562     default:
00563         fprintf(stderr,
00564             "Network/Pcap/Live(%s): unknown link type: %d\n",
00565             name(), dlink_type_);
00566     }
00567     return -1;
00568 }
00569 
00570 const char *
00571 PcapLiveNetwork::autodevname()
00572 {
00573     const char *dname;
00574     if ((dname = pcap_lookupdev(errbuf_)) == NULL) {
00575         fprintf(stderr, "warning: PcapNet/Live(%s) : %s\n",
00576             name(), errbuf_);
00577         return (NULL);
00578     }
00579     return (dname); // ptr to static data in pcap library
00580 }
00581 
00582 /*
00583  * devtonaddr -- map device name to its IP/Network layer address
00584  * this routine wouldn't be necessary if pcap_lookupnet gave
00585  * out the info it gets anyhow
00586  */
00587 
00588 #include <netinet/in.h>
00589 
00590 int
00591 PcapLiveNetwork::devtonaddr(const char *devname, NetworkAddress& na)
00592 {
00593         register int fd;
00594         ifreq ifr;
00595                                 
00596         fd = socket(AF_INET, SOCK_DGRAM, 0);
00597         if (fd < 0) {       
00598                 fprintf(stderr,
00599             "PcapLiveNet(%s): devtoaddr: couldn't create sock\n",
00600             name());
00601                 return (-1);
00602         }
00603         memset(&ifr, 0, sizeof(ifr));
00604 #ifdef linux
00605         /* XXX Work around Linux kernel bug */
00606         ifr.ifr_addr.sa_family = AF_INET;
00607 #endif   
00608         (void)strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name));
00609         if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) {
00610                 fprintf(stderr, "PcapLiveNetwork(%s): devtoaddr: no addr\n",
00611             name());
00612                 (void)::close(fd);   
00613                 return (-1);
00614         }               
00615     sockaddr* sa = &ifr.ifr_addr;
00616     if (sa->sa_family != AF_INET) {
00617                 fprintf(stderr,
00618             "PcapLiveNet(%s): af not AF_INET (%d)\n",
00619             name(), sa->sa_family);
00620     }
00621     sockaddr_in* sin = (sockaddr_in*) sa;
00622     na.len_ = 4;                // for now, assump IPv4
00623     memset(na.addr_, 0, sizeof(na.addr_));
00624     unsigned sz = sizeof(na.addr_);
00625     if (sizeof(ifr) < sz)
00626         sz = sizeof(ifr);
00627     memcpy(na.addr_, &sin->sin_addr, sz);
00628     return (0);
00629 }
00630 
00631 
00632 void
00633 PcapLiveNetwork::bindvars()
00634 {
00635     bind("snaplen_", &snaplen_);
00636     bind_bool("promisc_", &promisc_);
00637     bind_time("timeout_", &timeout_);
00638     bind("offset_", &offset_);
00639     PcapNetwork::bindvars();
00640 }
00641 
00642 void
00643 PcapFileNetwork::bindvars()
00644 {
00645     bind("offset_", &offset_);
00646 }
00647 
00648 int
00649 PcapLiveNetwork::open(int mode)
00650 {
00651     return (open(mode, autodevname()));
00652 }
00653 
00654 int PcapLiveNetwork::command(int argc, const char*const* argv)
00655 {
00656 
00657     Tcl& tcl = Tcl::instance();
00658     if (argc == 2) {
00659         if (strcmp(argv[1], "linkaddr") == 0) {
00661             tcl.result(Ethernet::etheraddr_string(linkaddr_.addr_));
00662             return (TCL_OK);
00663         }
00664         if (strcmp(argv[1], "netaddr") == 0) {
00665             if (netaddr_.len_ != 4) {
00666                 fprintf(stderr,
00667                   "PcapLive(%s): net addr not len 4 (%d)\n",
00668                     name(), netaddr_.len_);
00669                 return (TCL_ERROR);
00670             }
00671             tcl.resultf("%d.%d.%d.%d",
00672                 netaddr_.addr_[0],
00673                 netaddr_.addr_[1],
00674                 netaddr_.addr_[2],
00675                 netaddr_.addr_[3]);
00676             return (TCL_OK);
00677         }
00678     } else if (argc == 3) {
00679         // $obj open mode
00680         if (strcmp(argv[1], "open") == 0) {
00681             int mode = parsemode(argv[2]);
00682             if (open(mode) < 0)
00683                 return (TCL_ERROR);
00684             tcl.result(srcname_);
00685             return (TCL_OK);
00686         }
00687     } else if (argc == 4) {
00688         // $obj open mode devicename
00689         if (strcmp(argv[1], "open") == 0) {
00690             int mode = parsemode(argv[2]);
00691             if (open(mode, argv[3]) < 0)
00692                 return (TCL_ERROR);
00693             tcl.result(srcname_);
00694             return (TCL_OK);
00695         }
00696     }
00697     return (PcapNetwork::command(argc, argv));
00698 }
00699 
00700 //
00701 // defs for PcapFileNetwork
00702 // use a file instead of a live network
00703 //
00704 
00705 int
00706 PcapFileNetwork::open(int /*mode*/, const char *filename)
00707 {
00708 
00709     close();
00710     pcap_ = pcap_open_offline((char*) filename, errbuf_);
00711     if (pcap_ == NULL) {
00712         fprintf(stderr,
00713           "pcap/file object (%s) couldn't open packet source %s: %s\n",
00714             name(), filename, errbuf_);
00715         return -1;
00716     }
00717     mode_ = O_RDONLY;   // sorry, that's all for now
00718     //
00719     // pcap only ever puts -1 in the pcap_fileno, which
00720     // isn't so convenient, so do this instead:
00721     // pfd_ = pcap_fileno(pcap_);
00722     pfd_ = fileno(pcap_file(pcap_));
00723     strncpy(srcname_, filename, sizeof(srcname_)-1);
00724     state_ = PNET_PSTATE_ACTIVE;
00725     return 0;
00726 }
00727 
00728 int PcapFileNetwork::command(int argc, const char*const* argv)
00729 {
00730 
00731     Tcl& tcl = Tcl::instance();
00732     if (argc == 4) {
00733         // $obj open mode filename
00734         if (strcmp(argv[1], "open") == 0) {
00735             int mode = parsemode(argv[2]);
00736             if (open(mode, argv[3]) < 0)
00737                 return (TCL_ERROR);
00738             tcl.resultf("%s", argv[3]);
00739             return (TCL_OK);
00740         }
00741     }
00742     return (PcapNetwork::command(argc, argv));
00743 }
00744 
00745 //
00746 // XXX: the following routines are unfortunately necessary, 
00747 //  because libpcap has no obvious was of making the bpf fd
00748 //  be read-write :(.  The implication here is nasty:
00749 //      our own version of bpf_open and pcap_open_live
00750 //      and the later routine requires the struct pcap internal state
00751 
00752 /*   
00753  * Savefile
00754  */  
00755 struct pcap_sf {
00756         FILE *rfile; 
00757         int swapped;
00758         int version_major;
00759         int version_minor;
00760         u_char *base;
00761 };   
00762      
00763 struct pcap_md {
00764         struct pcap_stat stat;
00765         /*XXX*/ 
00766         int use_bpf;
00767         u_long  TotPkts;        /* can't oflow for 79 hrs on ether */
00768         u_long  TotAccepted;    /* count accepted by filter */
00769         u_long  TotDrops;       /* count of dropped packets */
00770         long    TotMissed;      /* missed by i/f during this run */
00771         long    OrigMissed;     /* missed by i/f before this run */
00772 #ifdef linux
00773         int pad;
00774         int skip;
00775         char *device;
00776 #endif
00777 };   
00778 
00779 
00780 struct pcap {
00781         int fd;
00782         int snapshot;
00783         int linktype;
00784         int tzoff;              /* timezone offset */
00785         int offset;             /* offset for proper alignment */
00786 
00787         struct pcap_sf sf;
00788         struct pcap_md md;
00789 
00790         /*
00791          * Read buffer.
00792          */
00793         int bufsize;
00794         u_char *buffer;
00795         u_char *bp;
00796         int cc;
00797 
00798         /*
00799          * Place holder for pcap_next().
00800          */
00801         u_char *pkt;
00802 
00803         
00804         /*
00805          * Placeholder for filter code if bpf not in kernel.
00806          */
00807         struct bpf_program fcode;
00808 
00809         char errbuf[PCAP_ERRBUF_SIZE];
00810 };
00811 
00812 
00813 /*
00814  * the routines bpf_open and pcap_open_live really
00815  * should not be here, and instead should be part of the
00816  * pcap library.  Unfortunately, if we ever want to writes to
00817  * the bpf fd, we need to open it r/w, and the normal pcap
00818  * library does not permit us to do this.  So for now, here
00819  * are these routines.
00820  */
00821 
00822 #include <net/if.h>
00823 
00824 #ifdef MY_OWN_PCAP
00825 int
00826 PcapLiveNetwork::bpf_open(pcap_t *, char *errbuf, int how)
00827 {
00828         int fd;
00829         int n = 0;
00830         char device[sizeof "/dev/bpf000"];
00831 
00832         /*
00833          * Go through all the minors and find one that isn't in use.
00834          */
00835         do {
00836                 (void)sprintf(device, "/dev/bpf%d", n++);
00837                 fd = ::open(device, how, 0);
00838         } while (fd < 0 && n < 1000 && errno == EBUSY);
00839 
00840         /*
00841          * XXX better message for all minors used
00842          */
00843         if (fd < 0)
00844                 sprintf(errbuf, "%s: %s", device, pcap_strerror(errno));
00845 
00846         return (fd);
00847 }
00848 
00849 pcap_t *
00850 PcapLiveNetwork::pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf, int how)
00851 {
00852         int fd;
00853         struct ifreq ifr;
00854         struct bpf_version bv;
00855         u_int v;
00856         pcap_t *p;
00857 
00858         p = (pcap_t *)malloc(sizeof(*p));
00859         if (p == NULL) {
00860                 sprintf(ebuf, "malloc: %s", pcap_strerror(errno));
00861                 return (NULL);
00862         }
00863         bzero(p, sizeof(*p));
00864         fd = bpf_open(p, ebuf, how);
00865         if (fd < 0)
00866                 goto bad;
00867 
00868         p->fd = fd;
00869         p->snapshot = snaplen;
00870 
00871         if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
00872                 sprintf(ebuf, "BIOCVERSION: %s", pcap_strerror(errno));
00873                 goto bad;
00874         }
00875         if (bv.bv_major != BPF_MAJOR_VERSION ||
00876             bv.bv_minor < BPF_MINOR_VERSION) {
00877                 sprintf(ebuf, "kernel bpf filter out of date");
00878                 goto bad;
00879         }
00880         (void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
00881         if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
00882                 sprintf(ebuf, "%s: %s", device, pcap_strerror(errno));
00883                 goto bad;
00884         }
00885         /* Get the data link layer type. */
00886         if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) {
00887                 sprintf(ebuf, "BIOCGDLT: %s", pcap_strerror(errno));
00888                 goto bad;
00889         }
00890 #if _BSDI_VERSION - 0 >= 199510
00891         /* The SLIP and PPP link layer header changed in BSD/OS 2.1 */
00892         switch (v) {
00893 
00894         case DLT_SLIP:
00895                 v = DLT_SLIP_BSDOS;
00896                 break;
00897         case DLT_PPP:
00898                 v = DLT_PPP_BSDOS;
00899                 break;
00900         }
00901 #endif  
00902         p->linktype = v;
00903 
00904         /* set timeout */
00905         if (to_ms != 0) {
00906                 struct timeval to;
00907                 to.tv_sec = to_ms / 1000;
00908                 to.tv_usec = (to_ms * 1000) % 1000000;
00909                 if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&to) < 0) {
00910                         sprintf(ebuf, "BIOCSRTIMEOUT: %s",
00911                                 pcap_strerror(errno));
00912                         goto bad;
00913                 }
00914         }
00915         if (promisc)
00916                 /* set promiscuous mode, okay if it fails */
00917                 (void)ioctl(p->fd, BIOCPROMISC, NULL); 
00918 
00919         if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) {
00920                 sprintf(ebuf, "BIOCGBLEN: %s", pcap_strerror(errno));
00921                 goto bad;
00922         }   
00923         p->bufsize = v;
00924         p->buffer = (u_char *)malloc(p->bufsize);
00925         if (p->buffer == NULL) {
00926                 sprintf(ebuf, "malloc: %s", pcap_strerror(errno));
00927                 goto bad;
00928         }       
00929 
00930         return (p);
00931  bad:   
00932         ::close(fd);
00933         free(p);
00934         return (NULL);
00935 }
00936 #endif // MY_OWN_PCAP

Generated on Tue Mar 6 16:47:47 2007 for ns2 Network Simulator 2.29 by  doxygen 1.4.6