cSocketRTScheduler.cc

Go to the documentation of this file.
00001 //
00002 // Copyright (C) 2005-2009 Andras Varga,
00003 //                         Christian Dankbar,
00004 //                         Irene Ruengeler,
00005 //                         Michael Tuexen
00006 //
00007 // This program is free software; you can redistribute it and/or
00008 // modify it under the terms of the GNU General Public License
00009 // as published by the Free Software Foundation; either version 2
00010 // of the License, or (at your option) any later version.
00011 //
00012 // This program is distributed in the hope that it will be useful,
00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 // GNU General Public License for more details.
00016 //
00017 // You should have received a copy of the GNU General Public License
00018 // along with this program; if not, see <http://www.gnu.org/licenses/>.
00019 //
00020 
00021 // This file is based on the cSocketRTScheduler.cc of OMNeT++ written by
00022 // Andras Varga.
00023 
00024 #include "cSocketRTScheduler.h"
00025 
00026 #ifndef IPPROTO_SCTP
00027 #define IPPROTO_SCTP 132
00028 #endif
00029 
00030 #include <headers/ethernet.h>
00031 
00032 #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__) || defined(_WIN64)
00033 #include <ws2tcpip.h>
00034 #endif
00035 
00036 #define PCAP_SNAPLEN 65536 /* capture all data packets with up to pcap_snaplen bytes */
00037 #define PCAP_TIMEOUT 10    /* Timeout in ms */
00038 
00039 #ifdef HAVE_PCAP
00040 std::vector<cModule *>cSocketRTScheduler::modules;
00041 std::vector<pcap_t *>cSocketRTScheduler::pds;
00042 std::vector<int32>cSocketRTScheduler::datalinks;
00043 std::vector<int32>cSocketRTScheduler::headerLengths;
00044 #endif
00045 timeval cSocketRTScheduler::baseTime;
00046 
00047 Register_Class(cSocketRTScheduler);
00048 
00049 inline std::ostream& operator<<(std::ostream& out, const timeval& tv)
00050 {
00051     return out << (uint32)tv.tv_sec << "s" << tv.tv_usec << "us";
00052 }
00053 
00054 
00055 cSocketRTScheduler::cSocketRTScheduler() : cScheduler()
00056 {
00057     fd = INVALID_SOCKET;
00058 }
00059 
00060 cSocketRTScheduler::~cSocketRTScheduler()
00061 {
00062 }
00063 
00064 void cSocketRTScheduler::startRun()
00065 {
00066 #ifdef HAVE_PCAP
00067     const int32 on = 1;
00068 
00069 #endif
00070     gettimeofday(&baseTime, NULL);
00071 #ifdef HAVE_PCAP
00072     // Enabling sending makes no sense when we can't receive...
00073     fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
00074     if (fd == INVALID_SOCKET)
00075         throw cRuntimeError("cSocketRTScheduler: Root priviledges needed");
00076     if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0)
00077         throw cRuntimeError("cSocketRTScheduler: couldn't set sockopt for raw socket");
00078 #endif
00079 }
00080 
00081 
00082 void cSocketRTScheduler::endRun()
00083 {
00084 #ifdef HAVE_PCAP
00085     pcap_stat ps;
00086 
00087 #endif
00088     close(fd);
00089     fd = INVALID_SOCKET;
00090 #ifdef HAVE_PCAP
00091 
00092     for (uint16 i=0; i<pds.size(); i++)
00093     {
00094         if (pcap_stats(pds.at(i), &ps) < 0)
00095             throw cRuntimeError("cSocketRTScheduler::endRun(): Can not get pcap statistics: %s", pcap_geterr(pds.at(i)));
00096         else
00097             EV << modules.at(i)->getFullPath() << ": Received Packets: " << ps.ps_recv << " Dropped Packets: " << ps.ps_drop << ".\n";
00098         pcap_close(pds.at(i));
00099     }
00100 
00101     pds.clear();
00102     modules.clear();
00103     pds.clear();
00104     datalinks.clear();
00105     headerLengths.clear();
00106 #endif
00107 }
00108 
00109 void cSocketRTScheduler::executionResumed()
00110 {
00111     gettimeofday(&baseTime, NULL);
00112     baseTime = timeval_substract(baseTime, sim->getSimTime().dbl());
00113 }
00114 
00115 void cSocketRTScheduler::setInterfaceModule(cModule *mod, const char *dev, const char *filter)
00116 {
00117 #ifdef HAVE_PCAP
00118     char errbuf[PCAP_ERRBUF_SIZE];
00119     struct bpf_program fcode;
00120     pcap_t * pd;
00121     int32 datalink;
00122     int32 headerLength;
00123 
00124     if (!mod || !dev || !filter)
00125         throw cRuntimeError("cSocketRTScheduler::setInterfaceModule(): arguments must be non-NULL");
00126 
00127     /* get pcap handle */
00128     memset(&errbuf, 0, sizeof(errbuf));
00129     if ((pd = pcap_open_live(dev, PCAP_SNAPLEN, 0, PCAP_TIMEOUT, errbuf)) == NULL)
00130         throw cRuntimeError("cSocketRTScheduler::setInterfaceModule(): Can not open pcap device, error = %s", errbuf);
00131     else if(strlen(errbuf) > 0)
00132         EV << "cSocketRTScheduler::setInterfaceModule: pcap_open_live returned waring: " << errbuf << "\n";
00133 
00134     /* compile this command into a filter program */
00135     if (pcap_compile(pd, &fcode, (char *)filter, 0, 0) < 0)
00136         throw cRuntimeError("cSocketRTScheduler::setInterfaceModule(): Can not compile filter: %s", pcap_geterr(pd));
00137 
00138     /* apply the compiled filter to the packet capture device */
00139     if (pcap_setfilter(pd, &fcode) < 0)
00140         throw cRuntimeError("cSocketRTScheduler::setInterfaceModule(): Can not apply compiled filter: %s", pcap_geterr(pd));
00141 
00142     if ((datalink = pcap_datalink(pd)) < 0)
00143         throw cRuntimeError("cSocketRTScheduler::setInterfaceModule(): Can not get datalink: %s", pcap_geterr(pd));
00144 
00145 #ifndef LINUX
00146     if (pcap_setnonblock(pd, 1, errbuf) < 0)
00147         throw cRuntimeError("cSocketRTScheduler::pcap_setnonblock(): Can not put pcap device into non-blocking mode, error = %s", errbuf);
00148 #endif
00149 
00150     switch (datalink) {
00151     case DLT_NULL:
00152         headerLength = 4;
00153         break;
00154     case DLT_EN10MB:
00155         headerLength = 14;
00156         break;
00157     case DLT_SLIP:
00158         headerLength = 24;
00159         break;
00160     case DLT_PPP:
00161         headerLength = 24;
00162         break;
00163     default:
00164         throw cRuntimeError("cSocketRTScheduler::setInterfaceModule(): Unsupported datalink: %d", datalink);
00165     }
00166     modules.push_back(mod);
00167     pds.push_back(pd);
00168     datalinks.push_back(datalink);
00169     headerLengths.push_back(headerLength);
00170 
00171     EV << "Opened pcap device " << dev << " with filter " << filter << " and datalink " << datalink << ".\n";
00172 #else
00173     throw cRuntimeError("cSocketRTScheduler::setInterfaceModule(): pcap devices not supported");
00174 #endif
00175 }
00176 
00177 #ifdef HAVE_PCAP
00178 static void packet_handler(u_char *user, const struct pcap_pkthdr *hdr, const u_char *bytes)
00179 {
00180     unsigned i;
00181     int32 headerLength;
00182     int32 datalink;
00183     cModule *module;
00184     struct ether_header *ethernet_hdr;
00185 
00186     i = *(uint16 *)user;
00187     datalink = cSocketRTScheduler::datalinks.at(i);
00188     headerLength = cSocketRTScheduler::headerLengths.at(i);
00189     module = cSocketRTScheduler::modules.at(i);
00190 
00191     // skip ethernet frames not encapsulating an IP packet.
00192     if (datalink == DLT_EN10MB)
00193     {
00194         ethernet_hdr = (struct ether_header *)bytes;
00195         if (ntohs(ethernet_hdr->ether_type) != ETHERTYPE_IP)
00196             return;
00197     }
00198 
00199     // put the IP packet from wire into data[] array of ExtFrame
00200     ExtFrame *notificationMsg = new ExtFrame("rtEvent");
00201     notificationMsg->setDataArraySize(hdr->caplen - headerLength);
00202     for (uint16 j=0; j< hdr->caplen - headerLength; j++)
00203         notificationMsg->setData(j, bytes[j + headerLength]);
00204 
00205     // signalize new incoming packet to the interface via cMessage
00206     EV << "Captured " << hdr->caplen - headerLength << " bytes for an IP packet.\n";
00207     timeval curTime;
00208     gettimeofday(&curTime, NULL);
00209     curTime = timeval_substract(curTime, cSocketRTScheduler::baseTime);
00210     simtime_t t = curTime.tv_sec + curTime.tv_usec*1e-6;
00211     // TBD assert that it's somehow not smaller than previous event's time
00212     notificationMsg->setArrival(module, -1, t);
00213 
00214     simulation.msgQueue.insert(notificationMsg);
00215 }
00216 #endif
00217 
00218 bool cSocketRTScheduler::receiveWithTimeout()
00219 {
00220     bool found;
00221     struct timeval timeout;
00222 #ifdef HAVE_PCAP
00223     int32 n;
00224 #ifdef LINUX
00225     int32 fd[FD_SETSIZE], maxfd;
00226     fd_set rdfds;
00227 #endif
00228 #endif
00229 
00230     found = false;
00231     timeout.tv_sec  = 0;
00232     timeout.tv_usec = PCAP_TIMEOUT * 1000;
00233 #ifdef HAVE_PCAP
00234 #ifdef LINUX
00235     FD_ZERO(&rdfds);
00236     maxfd = -1;
00237     for (uint16 i = 0; i < pds.size(); i++)
00238     {
00239         fd[i] = pcap_get_selectable_fd(pds.at(i));
00240         if (fd[i] > maxfd)
00241             maxfd = fd[i];
00242         FD_SET(fd[i], &rdfds);
00243     }
00244     if (select(maxfd + 1, &rdfds, NULL, NULL, &timeout) < 0)
00245     {
00246         return found;
00247     }
00248 #endif
00249     for (uint16 i = 0; i < pds.size(); i++)
00250     {
00251 #ifdef LINUX
00252         if (!(FD_ISSET(fd[i], &rdfds)))
00253             continue;
00254 #endif
00255         if ((n = pcap_dispatch(pds.at(i), 1, packet_handler, (uint8 *)&i)) < 0)
00256             throw cRuntimeError("cSocketRTScheduler::pcap_dispatch(): An error occired: %s", pcap_geterr(pds.at(i)));
00257         if (n > 0)
00258             found = true;
00259     }
00260 #ifndef LINUX
00261     if (!found)
00262         select(0, NULL, NULL, NULL, &timeout);
00263 #endif
00264 #else
00265     select(0, NULL, NULL, NULL, &timeout);
00266 #endif
00267     return found;
00268 }
00269 
00270 int32 cSocketRTScheduler::receiveUntil(const timeval& targetTime)
00271 {
00272     // wait until targetTime or a bit longer, wait in PCAP_TIMEOUT chunks
00273     // in order to keep UI responsiveness by invoking ev.idle()
00274     timeval curTime;
00275     gettimeofday(&curTime, NULL);
00276     while (timeval_greater(targetTime, curTime))
00277     {
00278         if (receiveWithTimeout())
00279             return 1;
00280         if (ev.idle())
00281             return -1;
00282         gettimeofday(&curTime, NULL);
00283     }
00284     return 0;
00285 }
00286 
00287 cMessage *cSocketRTScheduler::getNextEvent()
00288 {
00289     timeval targetTime, curTime, diffTime;
00290 
00291     // calculate target time
00292     cMessage *msg = sim->msgQueue.peekFirst();
00293     if (!msg)
00294     {
00295         targetTime.tv_sec = LONG_MAX;
00296         targetTime.tv_usec = 0;
00297     }
00298     else
00299     {
00300         simtime_t eventSimtime = msg->getArrivalTime();
00301         targetTime = timeval_add(baseTime, eventSimtime.dbl());
00302     }
00303 
00304     gettimeofday(&curTime, NULL);
00305     if (timeval_greater(targetTime, curTime))
00306     {
00307         int32 status = receiveUntil(targetTime);
00308         if (status == -1)
00309             return NULL; // interrupted by user
00310         if (status == 1)
00311             msg = sim->msgQueue.peekFirst(); // received something
00312     }
00313     else
00314     {
00315         // we're behind -- customized versions of this class may
00316         // alert if we're too much behind, whatever that means
00317         diffTime = timeval_substract(curTime, targetTime);
00318         EV << "We are behind: " << diffTime.tv_sec + diffTime.tv_usec * 1e-6 << " seconds\n";
00319     }
00320     return msg;
00321 }
00322 
00323 void cSocketRTScheduler::sendBytes(uint8 *buf, size_t numBytes, struct sockaddr *to, socklen_t addrlen)
00324 {
00325     if (fd == INVALID_SOCKET)
00326         throw cRuntimeError("cSocketRTScheduler::sendBytes(): no raw socket.");
00327 
00328     int sent = sendto(fd, (char *)buf, numBytes, 0, to, addrlen);  //note: no ssize_t on MSVC
00329 
00330     if (sent == numBytes)
00331         EV << "Sent an IP packet with length of " << sent << " bytes.\n";
00332     else
00333         EV << "Sending of an IP packet FAILED! (sendto returned " << sent << " (" << strerror(errno) << ") instead of " << numBytes << ").\n";
00334 }