Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / cast / test / utility / tap_proxy.cc
blobad7057ab530ad7ce1b9ac41424b8b2c8d1ec9280
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.
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>
18 #include <deque>
19 #include <map>
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/single_thread_task_runner.h"
27 #include "base/synchronization/waitable_event.h"
28 #include "base/thread_task_runner_handle.h"
29 #include "base/threading/thread.h"
30 #include "base/time/default_tick_clock.h"
31 #include "media/cast/test/utility/udp_proxy.h"
32 #include "net/base/io_buffer.h"
33 #include "net/base/net_errors.h"
34 #include "net/udp/udp_socket.h"
36 namespace media {
37 namespace cast {
38 namespace test {
40 const size_t kMaxPacketSize = 4096;
42 class SendToFDPipe : public PacketPipe {
43 public:
44 explicit SendToFDPipe(int fd) : fd_(fd) {
46 void Send(scoped_ptr<Packet> packet) final {
47 while (1) {
48 int written = write(
49 fd_,
50 reinterpret_cast<char*>(&packet->front()),
51 packet->size());
52 if (written < 0) {
53 if (errno == EINTR) continue;
54 perror("write");
55 exit(1);
57 if (written != static_cast<int>(packet->size())) {
58 fprintf(stderr, "Truncated write!\n");
59 exit(1);
61 break;
64 private:
65 int fd_;
68 class QueueManager : public base::MessageLoopForIO::Watcher {
69 public:
70 QueueManager(int input_fd,
71 int output_fd,
72 scoped_ptr<PacketPipe> pipe) :
73 input_fd_(input_fd),
74 packet_pipe_(pipe.Pass()) {
76 CHECK(base::MessageLoopForIO::current()->WatchFileDescriptor(
77 input_fd_, true, base::MessageLoopForIO::WATCH_READ,
78 &read_socket_watcher_, this));
80 scoped_ptr<PacketPipe> tmp(new SendToFDPipe(output_fd));
81 if (packet_pipe_) {
82 packet_pipe_->AppendToPipe(tmp.Pass());
83 } else {
84 packet_pipe_ = tmp.Pass();
86 packet_pipe_->InitOnIOThread(base::ThreadTaskRunnerHandle::Get(),
87 &tick_clock_);
90 ~QueueManager() final {}
92 // MessageLoopForIO::Watcher methods
93 void OnFileCanReadWithoutBlocking(int fd) final {
94 scoped_ptr<Packet> packet(new Packet(kMaxPacketSize));
95 int nread = read(input_fd_,
96 reinterpret_cast<char*>(&packet->front()),
97 kMaxPacketSize);
98 if (nread < 0) {
99 if (errno == EINTR) return;
100 perror("read");
101 exit(1);
103 if (nread == 0) return;
104 packet->resize(nread);
105 packet_pipe_->Send(packet.Pass());
107 void OnFileCanWriteWithoutBlocking(int fd) final { NOTREACHED(); }
109 private:
110 int input_fd_;
111 scoped_ptr<PacketPipe> packet_pipe_;
112 base::MessageLoopForIO::FileDescriptorWatcher read_socket_watcher_;
113 base::DefaultTickClock tick_clock_;
116 } // namespace test
117 } // namespace cast
118 } // namespace media
120 base::TimeTicks last_printout;
122 class ByteCounter {
123 public:
124 ByteCounter() : bytes_(0), packets_(0) {
125 push(base::TimeTicks::Now());
128 base::TimeDelta time_range() {
129 return time_data_.back() - time_data_.front();
132 void push(base::TimeTicks now) {
133 byte_data_.push_back(bytes_);
134 packet_data_.push_back(packets_);
135 time_data_.push_back(now);
136 while (time_range().InSeconds() > 10) {
137 byte_data_.pop_front();
138 packet_data_.pop_front();
139 time_data_.pop_front();
143 double megabits_per_second() {
144 double megabits = (byte_data_.back() - byte_data_.front()) * 8 / 1E6;
145 return megabits / time_range().InSecondsF();
148 double packets_per_second() {
149 double packets = packet_data_.back()- packet_data_.front();
150 return packets / time_range().InSecondsF();
153 void Increment(uint64 x) {
154 bytes_ += x;
155 packets_ ++;
158 private:
159 uint64 bytes_;
160 uint64 packets_;
161 std::deque<uint64> byte_data_;
162 std::deque<uint64> packet_data_;
163 std::deque<base::TimeTicks> time_data_;
166 ByteCounter in_pipe_input_counter;
167 ByteCounter in_pipe_output_counter;
168 ByteCounter out_pipe_input_counter;
169 ByteCounter out_pipe_output_counter;
171 class ByteCounterPipe : public media::cast::test::PacketPipe {
172 public:
173 ByteCounterPipe(ByteCounter* counter) : counter_(counter) {}
174 void Send(scoped_ptr<media::cast::Packet> packet) final {
175 counter_->Increment(packet->size());
176 pipe_->Send(packet.Pass());
178 private:
179 ByteCounter* counter_;
182 void SetupByteCounters(scoped_ptr<media::cast::test::PacketPipe>* pipe,
183 ByteCounter* pipe_input_counter,
184 ByteCounter* pipe_output_counter) {
185 media::cast::test::PacketPipe* new_pipe =
186 new ByteCounterPipe(pipe_input_counter);
187 new_pipe->AppendToPipe(pipe->Pass());
188 new_pipe->AppendToPipe(
189 scoped_ptr<media::cast::test::PacketPipe>(
190 new ByteCounterPipe(pipe_output_counter)).Pass());
191 pipe->reset(new_pipe);
194 void CheckByteCounters() {
195 base::TimeTicks now = base::TimeTicks::Now();
196 in_pipe_input_counter.push(now);
197 in_pipe_output_counter.push(now);
198 out_pipe_input_counter.push(now);
199 out_pipe_output_counter.push(now);
200 if ((now - last_printout).InSeconds() >= 5) {
201 fprintf(stderr, "Sending : %5.2f / %5.2f mbps %6.2f / %6.2f packets / s\n",
202 in_pipe_output_counter.megabits_per_second(),
203 in_pipe_input_counter.megabits_per_second(),
204 in_pipe_output_counter.packets_per_second(),
205 in_pipe_input_counter.packets_per_second());
206 fprintf(stderr, "Receiving: %5.2f / %5.2f mbps %6.2f / %6.2f packets / s\n",
207 out_pipe_output_counter.megabits_per_second(),
208 out_pipe_input_counter.megabits_per_second(),
209 out_pipe_output_counter.packets_per_second(),
210 out_pipe_input_counter.packets_per_second());
212 last_printout = now;
214 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
215 FROM_HERE,
216 base::Bind(&CheckByteCounters),
217 base::TimeDelta::FromMilliseconds(100));
220 int tun_alloc(char *dev, int flags) {
221 struct ifreq ifr;
222 int fd, err;
223 const char *clonedev = "/dev/net/tun";
225 /* Arguments taken by the function:
227 * char *dev: the name of an interface (or '\0'). MUST have enough
228 * space to hold the interface name if '\0' is passed
229 * int flags: interface flags (eg, IFF_TUN etc.)
232 /* open the clone device */
233 if( (fd = open(clonedev, O_RDWR)) < 0 ) {
234 return fd;
237 /* preparation of the struct ifr, of type "struct ifreq" */
238 memset(&ifr, 0, sizeof(ifr));
240 ifr.ifr_flags = flags; /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */
242 if (*dev) {
243 /* if a device name was specified, put it in the structure; otherwise,
244 * the kernel will try to allocate the "next" device of the
245 * specified type */
246 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
249 /* try to create the device */
250 if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
251 close(fd);
252 return err;
255 if (!*dev) {
256 /* if the operation was successful, write back the name of the
257 * interface to the variable "dev", so the caller can know
258 * it. Note that the caller MUST reserve space in *dev (see calling
259 * code below) */
260 strcpy(dev, ifr.ifr_name);
263 /* this is the special file descriptor that the caller will use to talk
264 * with the virtual interface */
265 return fd;
269 int main(int argc, char **argv) {
270 base::AtExitManager exit_manager;
271 base::CommandLine::Init(argc, argv);
272 InitLogging(logging::LoggingSettings());
274 if (argc < 4) {
275 fprintf(stderr, "Usage: tap_proxy tap1 tap2 type\n");
276 fprintf(stderr,
277 "Where 'type' is one of perfect, good, wifi, bad or evil\n");
278 exit(1);
281 scoped_ptr<media::cast::test::PacketPipe> in_pipe, out_pipe;
282 std::string network_type = argv[3];
283 if (network_type == "perfect") {
284 // No action needed.
285 } else if (network_type == "good") {
286 in_pipe = media::cast::test::GoodNetwork().Pass();
287 out_pipe = media::cast::test::GoodNetwork().Pass();
288 } else if (network_type == "wifi") {
289 in_pipe = media::cast::test::WifiNetwork().Pass();
290 out_pipe = media::cast::test::WifiNetwork().Pass();
291 } else if (network_type == "bad") {
292 in_pipe = media::cast::test::BadNetwork().Pass();
293 out_pipe = media::cast::test::BadNetwork().Pass();
294 } else if (network_type == "evil") {
295 in_pipe = media::cast::test::EvilNetwork().Pass();
296 out_pipe = media::cast::test::EvilNetwork().Pass();
297 } else {
298 fprintf(stderr, "Unknown network type.\n");
299 exit(1);
302 SetupByteCounters(&in_pipe, &in_pipe_input_counter, &in_pipe_output_counter);
303 SetupByteCounters(
304 &out_pipe, &out_pipe_input_counter, &out_pipe_output_counter);
306 int fd1 = tun_alloc(argv[1], IFF_TAP);
307 int fd2 = tun_alloc(argv[2], IFF_TAP);
309 base::MessageLoopForIO message_loop;
310 last_printout = base::TimeTicks::Now();
311 media::cast::test::QueueManager qm1(fd1, fd2, in_pipe.Pass());
312 media::cast::test::QueueManager qm2(fd2, fd1, out_pipe.Pass());
313 CheckByteCounters();
314 printf("Press Ctrl-C when done.\n");
315 message_loop.Run();