Home | History | Annotate | Download | only in utility
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <fcntl.h>
      6 #include <linux/if_tun.h>
      7 #include <linux/types.h>
      8 #include <math.h>
      9 #include <net/if.h>
     10 #include <netinet/in.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <sys/ioctl.h>
     14 #include <sys/stat.h>
     15 #include <sys/types.h>
     16 #include <unistd.h>
     17 
     18 #include <deque>
     19 #include <map>
     20 
     21 #include "base/at_exit.h"
     22 #include "base/bind.h"
     23 #include "base/command_line.h"
     24 #include "base/logging.h"
     25 #include "base/rand_util.h"
     26 #include "base/synchronization/waitable_event.h"
     27 #include "base/threading/thread.h"
     28 #include "base/time/default_tick_clock.h"
     29 #include "media/cast/test/utility/udp_proxy.h"
     30 #include "net/base/io_buffer.h"
     31 #include "net/base/net_errors.h"
     32 #include "net/udp/udp_socket.h"
     33 
     34 namespace media {
     35 namespace cast {
     36 namespace test {
     37 
     38 const size_t kMaxPacketSize = 4096;
     39 
     40 class SendToFDPipe : public PacketPipe {
     41  public:
     42   explicit SendToFDPipe(int fd) : fd_(fd) {
     43   }
     44   virtual void Send(scoped_ptr<Packet> packet) OVERRIDE {
     45     while (1) {
     46       int written = write(
     47           fd_,
     48           reinterpret_cast<char*>(&packet->front()),
     49           packet->size());
     50       if (written < 0) {
     51         if (errno == EINTR) continue;
     52         perror("write");
     53         exit(1);
     54       }
     55       if (written != static_cast<int>(packet->size())) {
     56         fprintf(stderr, "Truncated write!\n");
     57         exit(1);
     58       }
     59       break;
     60     }
     61   }
     62  private:
     63   int fd_;
     64 };
     65 
     66 class QueueManager : public base::MessageLoopForIO::Watcher {
     67  public:
     68   QueueManager(int input_fd,
     69                int output_fd,
     70                scoped_ptr<PacketPipe> pipe) :
     71       input_fd_(input_fd),
     72       packet_pipe_(pipe.Pass()) {
     73 
     74     CHECK(base::MessageLoopForIO::current()->WatchFileDescriptor(
     75         input_fd_, true, base::MessageLoopForIO::WATCH_READ,
     76         &read_socket_watcher_, this));
     77 
     78     scoped_ptr<PacketPipe> tmp(new SendToFDPipe(output_fd));
     79     if (packet_pipe_) {
     80       packet_pipe_->AppendToPipe(tmp.Pass());
     81     } else {
     82       packet_pipe_ = tmp.Pass();
     83     }
     84     packet_pipe_->InitOnIOThread(base::MessageLoopProxy::current(),
     85                                  &tick_clock_);
     86   }
     87 
     88   virtual ~QueueManager() {
     89   }
     90 
     91   // MessageLoopForIO::Watcher methods
     92   virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
     93     scoped_ptr<Packet> packet(new Packet(kMaxPacketSize));
     94     int nread = read(input_fd_,
     95                      reinterpret_cast<char*>(&packet->front()),
     96                      kMaxPacketSize);
     97     if (nread < 0) {
     98       if (errno == EINTR) return;
     99       perror("read");
    100       exit(1);
    101     }
    102     if (nread == 0) return;
    103     packet->resize(nread);
    104     packet_pipe_->Send(packet.Pass());
    105   }
    106   virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {
    107     NOTREACHED();
    108   }
    109 
    110  private:
    111   int input_fd_;
    112   scoped_ptr<PacketPipe> packet_pipe_;
    113   base::MessageLoopForIO::FileDescriptorWatcher read_socket_watcher_;
    114   base::DefaultTickClock tick_clock_;
    115 };
    116 
    117 }  // namespace test
    118 }  // namespace cast
    119 }  // namespace media
    120 
    121 base::TimeTicks last_printout;
    122 
    123 class ByteCounter {
    124  public:
    125   ByteCounter() : bytes_(0), packets_(0) {
    126     push(base::TimeTicks::Now());
    127   }
    128 
    129   base::TimeDelta time_range() {
    130     return time_data_.back() - time_data_.front();
    131   }
    132 
    133   void push(base::TimeTicks now) {
    134     byte_data_.push_back(bytes_);
    135     packet_data_.push_back(packets_);
    136     time_data_.push_back(now);
    137     while (time_range().InSeconds() > 10) {
    138       byte_data_.pop_front();
    139       packet_data_.pop_front();
    140       time_data_.pop_front();
    141     }
    142   }
    143 
    144   double megabits_per_second() {
    145     double megabits = (byte_data_.back() - byte_data_.front()) * 8 / 1E6;
    146     return megabits / time_range().InSecondsF();
    147   }
    148 
    149   double packets_per_second() {
    150     double packets = packet_data_.back()- packet_data_.front();
    151     return packets / time_range().InSecondsF();
    152   }
    153 
    154   void Increment(uint64 x) {
    155     bytes_ += x;
    156     packets_ ++;
    157   }
    158 
    159  private:
    160   uint64 bytes_;
    161   uint64 packets_;
    162   std::deque<uint64> byte_data_;
    163   std::deque<uint64> packet_data_;
    164   std::deque<base::TimeTicks> time_data_;
    165 };
    166 
    167 ByteCounter in_pipe_input_counter;
    168 ByteCounter in_pipe_output_counter;
    169 ByteCounter out_pipe_input_counter;
    170 ByteCounter out_pipe_output_counter;
    171 
    172 class ByteCounterPipe : public media::cast::test::PacketPipe {
    173  public:
    174   ByteCounterPipe(ByteCounter* counter) : counter_(counter) {}
    175   virtual void Send(scoped_ptr<media::cast::Packet> packet)
    176       OVERRIDE {
    177     counter_->Increment(packet->size());
    178     pipe_->Send(packet.Pass());
    179   }
    180  private:
    181   ByteCounter* counter_;
    182 };
    183 
    184 void SetupByteCounters(scoped_ptr<media::cast::test::PacketPipe>* pipe,
    185                        ByteCounter* pipe_input_counter,
    186                        ByteCounter* pipe_output_counter) {
    187   media::cast::test::PacketPipe* new_pipe =
    188       new ByteCounterPipe(pipe_input_counter);
    189   new_pipe->AppendToPipe(pipe->Pass());
    190   new_pipe->AppendToPipe(
    191       scoped_ptr<media::cast::test::PacketPipe>(
    192           new ByteCounterPipe(pipe_output_counter)).Pass());
    193   pipe->reset(new_pipe);
    194 }
    195 
    196 void CheckByteCounters() {
    197   base::TimeTicks now = base::TimeTicks::Now();
    198   in_pipe_input_counter.push(now);
    199   in_pipe_output_counter.push(now);
    200   out_pipe_input_counter.push(now);
    201   out_pipe_output_counter.push(now);
    202   if ((now - last_printout).InSeconds() >= 5) {
    203     fprintf(stderr, "Sending  : %5.2f / %5.2f mbps %6.2f / %6.2f packets / s\n",
    204             in_pipe_output_counter.megabits_per_second(),
    205             in_pipe_input_counter.megabits_per_second(),
    206             in_pipe_output_counter.packets_per_second(),
    207             in_pipe_input_counter.packets_per_second());
    208     fprintf(stderr, "Receiving: %5.2f / %5.2f mbps %6.2f / %6.2f packets / s\n",
    209             out_pipe_output_counter.megabits_per_second(),
    210             out_pipe_input_counter.megabits_per_second(),
    211             out_pipe_output_counter.packets_per_second(),
    212             out_pipe_input_counter.packets_per_second());
    213 
    214     last_printout = now;
    215   }
    216   base::MessageLoopProxy::current()->PostDelayedTask(
    217       FROM_HERE,
    218       base::Bind(&CheckByteCounters),
    219       base::TimeDelta::FromMilliseconds(100));
    220 }
    221 
    222 int tun_alloc(char *dev, int flags) {
    223   struct ifreq ifr;
    224   int fd, err;
    225   const char *clonedev = "/dev/net/tun";
    226 
    227   /* Arguments taken by the function:
    228    *
    229    * char *dev: the name of an interface (or '\0'). MUST have enough
    230    *   space to hold the interface name if '\0' is passed
    231    * int flags: interface flags (eg, IFF_TUN etc.)
    232    */
    233 
    234   /* open the clone device */
    235   if( (fd = open(clonedev, O_RDWR)) < 0 ) {
    236     return fd;
    237   }
    238 
    239   /* preparation of the struct ifr, of type "struct ifreq" */
    240   memset(&ifr, 0, sizeof(ifr));
    241 
    242   ifr.ifr_flags = flags;   /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */
    243 
    244   if (*dev) {
    245     /* if a device name was specified, put it in the structure; otherwise,
    246      * the kernel will try to allocate the "next" device of the
    247      * specified type */
    248     strncpy(ifr.ifr_name, dev, IFNAMSIZ);
    249   }
    250 
    251   /* try to create the device */
    252   if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
    253     close(fd);
    254     return err;
    255   }
    256 
    257   if (!*dev) {
    258     /* if the operation was successful, write back the name of the
    259      * interface to the variable "dev", so the caller can know
    260      * it. Note that the caller MUST reserve space in *dev (see calling
    261      * code below) */
    262     strcpy(dev, ifr.ifr_name);
    263   }
    264 
    265   /* this is the special file descriptor that the caller will use to talk
    266    * with the virtual interface */
    267   return fd;
    268 }
    269 
    270 
    271 int main(int argc, char **argv) {
    272   base::AtExitManager exit_manager;
    273   CommandLine::Init(argc, argv);
    274   InitLogging(logging::LoggingSettings());
    275 
    276   if (argc < 4) {
    277     fprintf(stderr, "Usage: tap_proxy tap1 tap2 type\n");
    278     fprintf(stderr,
    279             "Where 'type' is one of perfect, good, wifi, bad or evil\n");
    280     exit(1);
    281   }
    282 
    283   scoped_ptr<media::cast::test::PacketPipe> in_pipe, out_pipe;
    284   std::string network_type = argv[3];
    285   if (network_type == "perfect") {
    286     // No action needed.
    287   } else if (network_type == "good") {
    288     in_pipe = media::cast::test::GoodNetwork().Pass();
    289     out_pipe = media::cast::test::GoodNetwork().Pass();
    290   } else if (network_type == "wifi") {
    291     in_pipe = media::cast::test::WifiNetwork().Pass();
    292     out_pipe = media::cast::test::WifiNetwork().Pass();
    293   } else if (network_type == "bad") {
    294     in_pipe = media::cast::test::BadNetwork().Pass();
    295     out_pipe = media::cast::test::BadNetwork().Pass();
    296   } else if (network_type == "evil") {
    297     in_pipe = media::cast::test::EvilNetwork().Pass();
    298     out_pipe = media::cast::test::EvilNetwork().Pass();
    299   } else {
    300     fprintf(stderr, "Unknown network type.\n");
    301     exit(1);
    302   }
    303 
    304   SetupByteCounters(&in_pipe, &in_pipe_input_counter, &in_pipe_output_counter);
    305   SetupByteCounters(
    306       &out_pipe, &out_pipe_input_counter, &out_pipe_output_counter);
    307 
    308   int fd1 = tun_alloc(argv[1], IFF_TAP);
    309   int fd2 = tun_alloc(argv[2], IFF_TAP);
    310 
    311   base::MessageLoopForIO message_loop;
    312   last_printout = base::TimeTicks::Now();
    313   media::cast::test::QueueManager qm1(fd1, fd2, in_pipe.Pass());
    314   media::cast::test::QueueManager qm2(fd2, fd1, out_pipe.Pass());
    315   CheckByteCounters();
    316   printf("Press Ctrl-C when done.\n");
    317   message_loop.Run();
    318 }
    319