blob: 2d73f9443ca8e1f590eb7725039e9e10a40c53a1 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <fcntl.h>
#include <linux/if_tun.h>
#include <linux/types.h>
#include <math.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <deque>
#include <map>
#include <memory>
#include <utility>
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_descriptor_watcher_posix.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_tick_clock.h"
#include "media/cast/test/utility/udp_proxy.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/udp/udp_socket.h"
#include "starboard/memory.h"
#include "starboard/string.h"
#include "starboard/types.h"
namespace cobalt {
namespace media {
namespace cast {
namespace test {
const size_t kMaxPacketSize = 4096;
class SendToFDPipe : public PacketPipe {
public:
explicit SendToFDPipe(int fd) : fd_(fd) {}
void Send(std::unique_ptr<Packet> packet) final {
while (1) {
int written =
write(fd_, reinterpret_cast<char*>(&packet->front()), packet->size());
if (written < 0) {
if (errno == EINTR) continue;
perror("write");
exit(1);
}
if (written != static_cast<int>(packet->size())) {
fprintf(stderr, "Truncated write!\n");
exit(1);
}
break;
}
}
private:
int fd_;
};
class QueueManager {
public:
QueueManager(int input_fd, int output_fd, std::unique_ptr<PacketPipe> pipe)
: input_fd_(input_fd), packet_pipe_(std::move(pipe)) {
read_socket_watch_controller_ = base::FileDescriptorWatcher::WatchReadable(
input_fd_, base::Bind(&QueueManager::OnFileCanReadWithoutBlocking,
base::Unretained(this)));
std::unique_ptr<PacketPipe> tmp(new SendToFDPipe(output_fd));
if (packet_pipe_) {
packet_pipe_->AppendToPipe(std::move(tmp));
} else {
packet_pipe_ = std::move(tmp);
}
packet_pipe_->InitOnIOThread(base::ThreadTaskRunnerHandle::Get(),
&tick_clock_);
}
private:
void OnFileCanReadWithoutBlocking() {
std::unique_ptr<Packet> packet(new Packet(kMaxPacketSize));
int nread = read(input_fd_, reinterpret_cast<char*>(&packet->front()),
kMaxPacketSize);
if (nread < 0) {
if (errno == EINTR) return;
perror("read");
exit(1);
}
if (nread == 0) return;
packet->resize(nread);
packet_pipe_->Send(std::move(packet));
}
int input_fd_;
std::unique_ptr<PacketPipe> packet_pipe_;
std::unique_ptr<base::FileDescriptorWatcher::Controller>
read_socket_watch_controller_;
base::DefaultTickClock tick_clock_;
};
} // namespace test
} // namespace cast
} // namespace media
} // namespace cobalt
base::TimeTicks last_printout;
class ByteCounter {
public:
ByteCounter() : bytes_(0), packets_(0) { push(base::TimeTicks::Now()); }
base::TimeDelta time_range() {
return time_data_.back() - time_data_.front();
}
void push(base::TimeTicks now) {
byte_data_.push_back(bytes_);
packet_data_.push_back(packets_);
time_data_.push_back(now);
while (time_range().InSeconds() > 10) {
byte_data_.pop_front();
packet_data_.pop_front();
time_data_.pop_front();
}
}
double megabits_per_second() {
double megabits = (byte_data_.back() - byte_data_.front()) * 8 / 1E6;
return megabits / time_range().InSecondsF();
}
double packets_per_second() {
double packets = packet_data_.back() - packet_data_.front();
return packets / time_range().InSecondsF();
}
void Increment(uint64_t x) {
bytes_ += x;
packets_++;
}
private:
uint64_t bytes_;
uint64_t packets_;
std::deque<uint64_t> byte_data_;
std::deque<uint64_t> packet_data_;
std::deque<base::TimeTicks> time_data_;
};
ByteCounter in_pipe_input_counter;
ByteCounter in_pipe_output_counter;
ByteCounter out_pipe_input_counter;
ByteCounter out_pipe_output_counter;
class ByteCounterPipe : public media::cast::test::PacketPipe {
public:
explicit ByteCounterPipe(ByteCounter* counter) : counter_(counter) {}
void Send(std::unique_ptr<media::cast::Packet> packet) final {
counter_->Increment(packet->size());
pipe_->Send(std::move(packet));
}
private:
ByteCounter* counter_;
};
void SetupByteCounters(std::unique_ptr<media::cast::test::PacketPipe>* pipe,
ByteCounter* pipe_input_counter,
ByteCounter* pipe_output_counter) {
media::cast::test::PacketPipe* new_pipe =
new ByteCounterPipe(pipe_input_counter);
new_pipe->AppendToPipe(std::move(*pipe));
new_pipe->AppendToPipe(std::unique_ptr<media::cast::test::PacketPipe>(
new ByteCounterPipe(pipe_output_counter)));
pipe->reset(new_pipe);
}
void CheckByteCounters() {
base::TimeTicks now = base::TimeTicks::Now();
in_pipe_input_counter.push(now);
in_pipe_output_counter.push(now);
out_pipe_input_counter.push(now);
out_pipe_output_counter.push(now);
if ((now - last_printout).InSeconds() >= 5) {
fprintf(stderr, "Sending : %5.2f / %5.2f mbps %6.2f / %6.2f packets / s\n",
in_pipe_output_counter.megabits_per_second(),
in_pipe_input_counter.megabits_per_second(),
in_pipe_output_counter.packets_per_second(),
in_pipe_input_counter.packets_per_second());
fprintf(stderr, "Receiving: %5.2f / %5.2f mbps %6.2f / %6.2f packets / s\n",
out_pipe_output_counter.megabits_per_second(),
out_pipe_input_counter.megabits_per_second(),
out_pipe_output_counter.packets_per_second(),
out_pipe_input_counter.packets_per_second());
last_printout = now;
}
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::Bind(&CheckByteCounters),
base::TimeDelta::FromMilliseconds(100));
}
int tun_alloc(char* dev, int flags) {
struct ifreq ifr;
int fd, err;
const char* clonedev = "/dev/net/tun";
/* Arguments taken by the function:
*
* char *dev: the name of an interface (or '\0'). MUST have enough
* space to hold the interface name if '\0' is passed
* int flags: interface flags (eg, IFF_TUN etc.)
*/
/* open the clone device */
if ((fd = open(clonedev, O_RDWR)) < 0) {
return fd;
}
/* preparation of the struct ifr, of type "struct ifreq" */
SbMemorySet(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = flags; /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */
if (*dev) {
/* if a device name was specified, put it in the structure; otherwise,
* the kernel will try to allocate the "next" device of the
* specified type */
SbStringCopy(ifr.ifr_name, dev, IFNAMSIZ);
}
/* try to create the device */
if ((err = ioctl(fd, TUNSETIFF, &ifr)) < 0) {
close(fd);
return err;
}
if (!*dev) {
/* if the operation was successful, write back the name of the
* interface to the variable "dev", so the caller can know
* it. Note that the caller MUST reserve space in *dev (see calling
* code below) */
SbStringCopyUnsafe(dev, ifr.ifr_name);
}
/* this is the special file descriptor that the caller will use to talk
* with the virtual interface */
return fd;
}
int main(int argc, char** argv) {
base::AtExitManager exit_manager;
base::CommandLine::Init(argc, argv);
InitLogging(logging::LoggingSettings());
if (argc < 4) {
fprintf(stderr, "Usage: tap_proxy tap1 tap2 type\n");
fprintf(stderr,
"Where 'type' is one of perfect, good, wifi, bad or evil\n");
exit(1);
}
std::unique_ptr<media::cast::test::PacketPipe> in_pipe, out_pipe;
std::string network_type = argv[3];
if (network_type == "perfect") {
// No action needed.
} else if (network_type == "good") {
in_pipe = media::cast::test::GoodNetwork();
out_pipe = media::cast::test::GoodNetwork();
} else if (network_type == "wifi") {
in_pipe = media::cast::test::WifiNetwork();
out_pipe = media::cast::test::WifiNetwork();
} else if (network_type == "bad") {
in_pipe = media::cast::test::BadNetwork();
out_pipe = media::cast::test::BadNetwork();
} else if (network_type == "evil") {
in_pipe = media::cast::test::EvilNetwork();
out_pipe = media::cast::test::EvilNetwork();
} else {
fprintf(stderr, "Unknown network type.\n");
exit(1);
}
SetupByteCounters(&in_pipe, &in_pipe_input_counter, &in_pipe_output_counter);
SetupByteCounters(&out_pipe, &out_pipe_input_counter,
&out_pipe_output_counter);
int fd1 = tun_alloc(argv[1], IFF_TAP);
int fd2 = tun_alloc(argv[2], IFF_TAP);
base::MessageLoopForIO message_loop;
base::FileDescriptorWatcher file_descriptor_watcher(&message_loop);
last_printout = base::TimeTicks::Now();
media::cast::test::QueueManager qm1(fd1, fd2, std::move(in_pipe));
media::cast::test::QueueManager qm2(fd2, fd1, std::move(out_pipe));
CheckByteCounters();
printf("Press Ctrl-C when done.\n");
base::RunLoop().Run();
}