Add first part of the network_tester functionality.

BUG=webrtc:7426

Review-Url: https://codereview.webrtc.org/2779233002
Cr-Commit-Position: refs/heads/master@{#17635}
diff --git a/resources/network_tester/client_config.dat.sha1 b/resources/network_tester/client_config.dat.sha1
new file mode 100644
index 0000000..925be430
--- /dev/null
+++ b/resources/network_tester/client_config.dat.sha1
@@ -0,0 +1 @@
+8a7265ba397c621574b321a451605ac94f58b1d3
\ No newline at end of file
diff --git a/resources/network_tester/server_config.dat.sha1 b/resources/network_tester/server_config.dat.sha1
new file mode 100644
index 0000000..925be430
--- /dev/null
+++ b/resources/network_tester/server_config.dat.sha1
@@ -0,0 +1 @@
+8a7265ba397c621574b321a451605ac94f58b1d3
\ No newline at end of file
diff --git a/webrtc/tools/BUILD.gn b/webrtc/tools/BUILD.gn
index 5a4c1af..05b7adb 100644
--- a/webrtc/tools/BUILD.gn
+++ b/webrtc/tools/BUILD.gn
@@ -36,6 +36,7 @@
       public_deps += [
         ":event_log_visualizer",
         ":rtp_analyzer",
+        "network_tester",
       ]
     }
   }
@@ -311,6 +312,10 @@
       "//testing/gtest",
     ]
 
+    if (rtc_enable_protobuf) {
+      deps += [ "network_tester:network_tester_unittests" ]
+    }
+
     data = tools_unittests_resources
     if (is_android) {
       deps += [ "//testing/android/native_test:native_test_support" ]
diff --git a/webrtc/tools/DEPS b/webrtc/tools/DEPS
index 84bf153..92a068c 100644
--- a/webrtc/tools/DEPS
+++ b/webrtc/tools/DEPS
@@ -10,5 +10,6 @@
   "+webrtc/modules/congestion_controller",
   "+webrtc/modules/rtp_rtcp",
   "+webrtc/system_wrappers",
+  "+webrtc/p2p",
 ]
 
diff --git a/webrtc/tools/network_tester/BUILD.gn b/webrtc/tools/network_tester/BUILD.gn
new file mode 100644
index 0000000..6cdedf9
--- /dev/null
+++ b/webrtc/tools/network_tester/BUILD.gn
@@ -0,0 +1,93 @@
+# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS.  All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+import("../../webrtc.gni")
+import("//third_party/protobuf/proto_library.gni")
+
+if (rtc_enable_protobuf) {
+  proto_library("network_tester_config_proto") {
+    sources = [
+      "network_tester_config.proto",
+    ]
+    proto_out_dir = "webrtc/tools/network_tester"
+  }
+
+  proto_library("network_tester_packet_proto") {
+    sources = [
+      "network_tester_packet.proto",
+    ]
+    proto_out_dir = "webrtc/tools/network_tester"
+  }
+
+  rtc_static_library("network_tester") {
+    sources = [
+      "config_reader.cc",
+      "config_reader.h",
+      "packet_sender.cc",
+      "packet_sender.h",
+      "test_controller.cc",
+      "test_controller.h",
+    ]
+
+    defines = [ "WEBRTC_NETWORK_TESTER_PROTO" ]
+
+    deps = [
+      ":network_tester_config_proto",
+      ":network_tester_packet_proto",
+      "../../base:rtc_task_queue",
+      "../../p2p",
+    ]
+
+    if (!build_with_chromium && is_clang) {
+      # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+      suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+    }
+  }
+
+  network_tester_unittests_resources = [
+    "//resources/network_tester/client_config.dat",
+    "//resources/network_tester/server_config.dat",
+  ]
+
+  if (is_ios) {
+    bundle_data("network_tester_unittests_bundle_data") {
+      testonly = true
+      sources = network_tester_unittests_resources
+      outputs = [
+        "{{bundle_resources_dir}}/{{source_file_part}}",
+      ]
+    }
+  }
+
+  rtc_source_set("network_tester_unittests") {
+    sources = [
+      "network_tester_unittest.cc",
+    ]
+
+    testonly = true
+    deps = [
+      ":network_tester",
+      "//testing/gtest",
+      "//webrtc/base:rtc_base_tests_utils",
+      "//webrtc/test:test_support",
+    ]
+
+    if (is_ios) {
+      deps += [ ":network_tester_unittests_bundle_data" ]
+    }
+
+    defines = [ "GTEST_RELATIVE_PATH" ]
+
+    data = network_tester_unittests_resources
+
+    if (!build_with_chromium && is_clang) {
+      # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+      suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+    }
+  }
+}
diff --git a/webrtc/tools/network_tester/config_reader.cc b/webrtc/tools/network_tester/config_reader.cc
new file mode 100644
index 0000000..2718524
--- /dev/null
+++ b/webrtc/tools/network_tester/config_reader.cc
@@ -0,0 +1,50 @@
+/*
+ *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "webrtc/tools/network_tester/config_reader.h"
+
+#include <string>
+#include <vector>
+
+namespace webrtc {
+
+ConfigReader::ConfigReader(const std::string& config_file_path)
+    : proto_config_index_(0) {
+  std::ifstream config_stream(config_file_path,
+                              std::ios_base::in | std::ios_base::binary);
+  RTC_DCHECK(config_stream.is_open());
+  RTC_DCHECK(config_stream.good());
+  std::string config_data((std::istreambuf_iterator<char>(config_stream)),
+                          (std::istreambuf_iterator<char>()));
+  if (config_data.size() > 0) {
+    proto_all_configs_.ParseFromString(config_data);
+  }
+}
+
+ConfigReader::~ConfigReader() = default;
+
+rtc::Optional<ConfigReader::Config> ConfigReader::GetNextConfig() {
+#ifdef WEBRTC_NETWORK_TESTER_PROTO
+  if (proto_config_index_ >= proto_all_configs_.configs_size())
+    return rtc::Optional<Config>();
+  auto proto_config = proto_all_configs_.configs(proto_config_index_++);
+  RTC_DCHECK(proto_config.has_packet_send_interval_ms());
+  RTC_DCHECK(proto_config.has_packet_size());
+  RTC_DCHECK(proto_config.has_execution_time_ms());
+  Config config;
+  config.packet_send_interval_ms = proto_config.packet_send_interval_ms();
+  config.packet_size = proto_config.packet_size();
+  config.execution_time_ms = proto_config.execution_time_ms();
+  return rtc::Optional<Config>(config);
+#else
+  return rtc::Optional<Config>();
+#endif  //  WEBRTC_NETWORK_TESTER_PROTO
+}
+
+}  // namespace webrtc
diff --git a/webrtc/tools/network_tester/config_reader.h b/webrtc/tools/network_tester/config_reader.h
new file mode 100644
index 0000000..62e03da
--- /dev/null
+++ b/webrtc/tools/network_tester/config_reader.h
@@ -0,0 +1,53 @@
+/*
+ *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_TOOLS_NETWORK_TESTER_CONFIG_READER_H_
+#define WEBRTC_TOOLS_NETWORK_TESTER_CONFIG_READER_H_
+
+#include <fstream>
+#include <string>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/optional.h"
+
+#include "webrtc/base/ignore_wundef.h"
+
+#ifdef WEBRTC_NETWORK_TESTER_PROTO
+RTC_PUSH_IGNORING_WUNDEF()
+#include "webrtc/tools/network_tester/network_tester_config.pb.h"
+RTC_POP_IGNORING_WUNDEF()
+using webrtc::network_tester::config::NetworkTesterAllConfigs;
+#else
+class NetworkTesterConfigs;
+#endif  // WEBRTC_NETWORK_TESTER_PROTO
+
+namespace webrtc {
+
+class ConfigReader {
+ public:
+  struct Config {
+    int packet_send_interval_ms;
+    int packet_size;
+    int execution_time_ms;
+  };
+  explicit ConfigReader(const std::string& config_file_path);
+  ~ConfigReader();
+
+  rtc::Optional<Config> GetNextConfig();
+
+ private:
+  NetworkTesterAllConfigs proto_all_configs_;
+  int proto_config_index_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(ConfigReader);
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_TOOLS_NETWORK_TESTER_CONFIG_READER_H_
diff --git a/webrtc/tools/network_tester/create_network_tester_config.py b/webrtc/tools/network_tester/create_network_tester_config.py
new file mode 100644
index 0000000..57c1eb8
--- /dev/null
+++ b/webrtc/tools/network_tester/create_network_tester_config.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS.  All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+import network_tester_config_pb2
+
+
+def AddConfig(all_configs,
+              packet_send_interval_ms,
+              packet_size,
+              execution_time_ms):
+  config = all_configs.configs.add()
+  config.packet_send_interval_ms = packet_send_interval_ms
+  config.packet_size = packet_size
+  config.execution_time_ms = execution_time_ms
+
+def main():
+  all_configs = network_tester_config_pb2.NetworkTesterAllConfigs()
+  AddConfig(all_configs, 10, 50, 200)
+  AddConfig(all_configs, 10, 100, 200)
+  with open("network_tester_config.dat", 'wb') as f:
+    f.write(all_configs.SerializeToString())
+
+if __name__ == "__main__":
+  main()
diff --git a/webrtc/tools/network_tester/network_tester_config.proto b/webrtc/tools/network_tester/network_tester_config.proto
new file mode 100644
index 0000000..54a23a2
--- /dev/null
+++ b/webrtc/tools/network_tester/network_tester_config.proto
@@ -0,0 +1,13 @@
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+package webrtc.network_tester.config;
+
+message NetworkTesterConfig {
+  optional int32 packet_send_interval_ms = 1;
+  optional float packet_size = 2;
+  optional int32 execution_time_ms = 3;
+}
+
+message NetworkTesterAllConfigs {
+  repeated NetworkTesterConfig configs = 1;
+}
\ No newline at end of file
diff --git a/webrtc/tools/network_tester/network_tester_packet.proto b/webrtc/tools/network_tester/network_tester_packet.proto
new file mode 100644
index 0000000..c3bcec5
--- /dev/null
+++ b/webrtc/tools/network_tester/network_tester_packet.proto
@@ -0,0 +1,18 @@
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+package webrtc.network_tester.packet;
+
+message NetworkTesterPacket {
+  enum Type {
+    HAND_SHAKING = 0;
+    TEST_START = 1;
+    TEST_DATA = 2;
+    TEST_DONE = 3;
+  }
+
+  optional Type type = 1;
+  optional int64 send_timestamp = 2;
+  optional int64 arrival_timestamp = 3;
+  optional int64 sequence_number = 4;
+  optional int32 packet_size = 5;
+}
diff --git a/webrtc/tools/network_tester/network_tester_unittest.cc b/webrtc/tools/network_tester/network_tester_unittest.cc
new file mode 100644
index 0000000..b7fceda
--- /dev/null
+++ b/webrtc/tools/network_tester/network_tester_unittest.cc
@@ -0,0 +1,29 @@
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/tools/network_tester/test_controller.h"
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/test/gtest.h"
+#include "webrtc/test/testsupport/fileutils.h"
+
+namespace webrtc {
+
+TEST(NetworkTesterTest, ServerClient) {
+  TestController client(
+      0, 0, webrtc::test::ResourcePath("network_tester/client_config", "dat"));
+  TestController server(
+      9090, 9090,
+      webrtc::test::ResourcePath("network_tester/server_config", "dat"));
+  client.SendConnectTo("127.0.0.1", 9090);
+  EXPECT_TRUE_WAIT(server.IsTestDone() && client.IsTestDone(), 2000);
+}
+
+}  // namespace webrtc
diff --git a/webrtc/tools/network_tester/packet_sender.cc b/webrtc/tools/network_tester/packet_sender.cc
new file mode 100644
index 0000000..5d72806
--- /dev/null
+++ b/webrtc/tools/network_tester/packet_sender.cc
@@ -0,0 +1,126 @@
+/*
+ *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/tools/network_tester/packet_sender.h"
+
+#include <string>
+#include <utility>
+
+#include "webrtc/base/timeutils.h"
+#include "webrtc/tools/network_tester/config_reader.h"
+#include "webrtc/tools/network_tester/test_controller.h"
+
+namespace webrtc {
+
+namespace {
+
+class SendPacketTask : public rtc::QueuedTask {
+ public:
+  explicit SendPacketTask(PacketSender* packet_sender)
+      : packet_sender_(packet_sender) {}
+
+ private:
+  bool Run() override {
+    if (packet_sender_->IsSending()) {
+      packet_sender_->SendPacket();
+      rtc::TaskQueue::Current()->PostDelayedTask(
+          std::unique_ptr<QueuedTask>(this),
+          packet_sender_->GetSendIntervalMs());
+      return false;
+    } else {
+      return true;
+    }
+  }
+  PacketSender* const packet_sender_;
+};
+
+class UpdateTestSettingTask : public rtc::QueuedTask {
+ public:
+  UpdateTestSettingTask(PacketSender* packet_sender,
+                        std::unique_ptr<ConfigReader> config_reader)
+      : packet_sender_(packet_sender),
+        config_reader_(std::move(config_reader)) {}
+
+ private:
+  bool Run() override {
+    auto config = config_reader_->GetNextConfig();
+    if (config) {
+      packet_sender_->UpdateTestSetting((*config).packet_size,
+                                        (*config).packet_send_interval_ms);
+      rtc::TaskQueue::Current()->PostDelayedTask(
+          std::unique_ptr<QueuedTask>(this), (*config).execution_time_ms);
+      return false;
+    } else {
+      packet_sender_->StopSending();
+      return true;
+    }
+  }
+  PacketSender* const packet_sender_;
+  const std::unique_ptr<ConfigReader> config_reader_;
+};
+
+}  // namespace
+
+PacketSender::PacketSender(TestController* test_controller,
+                           const std::string& config_file_path)
+    : worker_queue_("Packet Sender", rtc::TaskQueue::Priority::HIGH),
+      packet_size_(0),
+      send_interval_ms_(0),
+      sequence_number_(0),
+      sending_(false),
+      config_file_path_(config_file_path),
+      test_controller_(test_controller) {}
+
+PacketSender::~PacketSender() = default;
+
+void PacketSender::StartSending() {
+  sending_ = true;
+  worker_queue_checker_.Detach();
+  worker_queue_.PostTask(
+      std::unique_ptr<rtc::QueuedTask>(new UpdateTestSettingTask(
+          this,
+          std::unique_ptr<ConfigReader>(new ConfigReader(config_file_path_)))));
+  worker_queue_.PostTask(
+      std::unique_ptr<rtc::QueuedTask>(new SendPacketTask(this)));
+}
+
+void PacketSender::StopSending() {
+  RTC_DCHECK_CALLED_SEQUENTIALLY(&worker_queue_checker_);
+  sending_ = false;
+  test_controller_->OnTestDone();
+}
+
+bool PacketSender::IsSending() const {
+  RTC_DCHECK_CALLED_SEQUENTIALLY(&worker_queue_checker_);
+  return sending_;
+}
+
+void PacketSender::SendPacket() {
+  RTC_DCHECK_CALLED_SEQUENTIALLY(&worker_queue_checker_);
+  NetworkTesterPacket packet;
+  packet.set_type(NetworkTesterPacket::TEST_DATA);
+  packet.set_sequence_number(sequence_number_++);
+  packet.set_send_timestamp(rtc::TimeMicros());
+  test_controller_->SendData(packet, rtc::Optional<size_t>(packet_size_));
+}
+
+int64_t PacketSender::GetSendIntervalMs() const {
+  RTC_DCHECK_CALLED_SEQUENTIALLY(&worker_queue_checker_);
+  return send_interval_ms_;
+}
+
+void PacketSender::UpdateTestSetting(size_t packet_size,
+                                     int64_t send_interval_ms) {
+  RTC_DCHECK_CALLED_SEQUENTIALLY(&worker_queue_checker_);
+  send_interval_ms_ = send_interval_ms;
+  packet_size_ = packet_size;
+}
+
+}  // namespace webrtc
diff --git a/webrtc/tools/network_tester/packet_sender.h b/webrtc/tools/network_tester/packet_sender.h
new file mode 100644
index 0000000..7a26535
--- /dev/null
+++ b/webrtc/tools/network_tester/packet_sender.h
@@ -0,0 +1,65 @@
+/*
+ *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_TOOLS_NETWORK_TESTER_PACKET_SENDER_H_
+#define WEBRTC_TOOLS_NETWORK_TESTER_PACKET_SENDER_H_
+
+#include <memory>
+#include <string>
+
+#include "webrtc/base/ignore_wundef.h"
+#include "webrtc/base/sequenced_task_checker.h"
+#include "webrtc/base/task_queue.h"
+
+#ifdef WEBRTC_NETWORK_TESTER_PROTO
+RTC_PUSH_IGNORING_WUNDEF()
+#include "webrtc/tools/network_tester/network_tester_packet.pb.h"
+RTC_POP_IGNORING_WUNDEF()
+using webrtc::network_tester::packet::NetworkTesterPacket;
+#else
+class NetworkTesterPacket;
+#endif  // WEBRTC_NETWORK_TESTER_PROTO
+
+namespace webrtc {
+
+class TestController;
+
+class PacketSender {
+ public:
+  PacketSender(TestController* test_controller,
+               const std::string& config_file_path);
+  ~PacketSender();
+
+  void StartSending();
+  void StopSending();
+  bool IsSending() const;
+
+  void SendPacket();
+
+  int64_t GetSendIntervalMs() const;
+  void UpdateTestSetting(size_t packet_size, int64_t send_interval_ms);
+
+ private:
+  rtc::TaskQueue worker_queue_;
+  rtc::SequencedTaskChecker worker_queue_checker_;
+
+  size_t packet_size_;
+  int64_t send_interval_ms_;
+  int64_t sequence_number_;
+  bool sending_;
+  const std::string config_file_path_;
+  TestController* const test_controller_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(PacketSender);
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_TOOLS_NETWORK_TESTER_PACKET_SENDER_H_
diff --git a/webrtc/tools/network_tester/test_controller.cc b/webrtc/tools/network_tester/test_controller.cc
new file mode 100644
index 0000000..713a974
--- /dev/null
+++ b/webrtc/tools/network_tester/test_controller.cc
@@ -0,0 +1,118 @@
+/*
+ *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/tools/network_tester/test_controller.h"
+
+namespace webrtc {
+
+TestController::TestController(int min_port,
+                               int max_port,
+                               const std::string& config_file_path)
+    : config_file_path_(config_file_path),
+      local_test_done_(false),
+      remote_test_done_(false) {
+  RTC_DCHECK_RUN_ON(&test_controller_thread_checker_);
+  send_data_.fill(42);
+  packet_sender_checker_.Detach();
+  auto socket =
+      std::unique_ptr<rtc::AsyncPacketSocket>(socket_factory_.CreateUdpSocket(
+          rtc::SocketAddress(rtc::GetAnyIP(AF_INET), 0), min_port, max_port));
+  socket->SignalReadPacket.connect(this, &TestController::OnReadPacket);
+  udp_transport_.reset(
+      new cricket::UdpTransport("network tester transport", std::move(socket)));
+}
+
+void TestController::SendConnectTo(const std::string& hostname, int port) {
+  RTC_DCHECK_RUN_ON(&test_controller_thread_checker_);
+  udp_transport_->SetRemoteAddress(rtc::SocketAddress(hostname, port));
+  NetworkTesterPacket packet;
+  packet.set_type(NetworkTesterPacket::HAND_SHAKING);
+  SendData(packet, rtc::Optional<size_t>());
+  rtc::CritScope scoped_lock(&local_test_done_lock_);
+  local_test_done_ = false;
+  remote_test_done_ = false;
+}
+
+void TestController::Run() {
+  RTC_DCHECK_RUN_ON(&test_controller_thread_checker_);
+  rtc::Thread::Current()->ProcessMessages(0);
+}
+
+void TestController::SendData(const NetworkTesterPacket& packet,
+                              rtc::Optional<size_t> data_size) {
+  // Can be call from packet_sender or from test_controller thread.
+  size_t packet_size = packet.ByteSize();
+  send_data_[0] = packet_size;
+  packet.SerializeToArray(&send_data_[1], std::numeric_limits<char>::max());
+  if (data_size && *data_size > packet_size)
+    packet_size = *data_size;
+  udp_transport_->SendPacket(send_data_.data(), packet_size + 1,
+                             rtc::PacketOptions(), 0);
+}
+
+void TestController::OnTestDone() {
+  RTC_DCHECK_CALLED_SEQUENTIALLY(&packet_sender_checker_);
+  NetworkTesterPacket packet;
+  packet.set_type(NetworkTesterPacket::TEST_DONE);
+  SendData(packet, rtc::Optional<size_t>());
+  rtc::CritScope scoped_lock(&local_test_done_lock_);
+  local_test_done_ = true;
+}
+
+bool TestController::IsTestDone() {
+  RTC_DCHECK_RUN_ON(&test_controller_thread_checker_);
+  rtc::CritScope scoped_lock(&local_test_done_lock_);
+  return local_test_done_ && remote_test_done_;
+}
+
+void TestController::OnReadPacket(rtc::AsyncPacketSocket* socket,
+                                  const char* data,
+                                  size_t len,
+                                  const rtc::SocketAddress& remote_addr,
+                                  const rtc::PacketTime& packet_time) {
+  RTC_DCHECK_RUN_ON(&test_controller_thread_checker_);
+  size_t packet_size = data[0];
+  std::string receive_data(&data[1], packet_size);
+  NetworkTesterPacket packet;
+  packet.ParseFromString(receive_data);
+  RTC_CHECK(packet.has_type());
+  switch (packet.type()) {
+    case NetworkTesterPacket::HAND_SHAKING: {
+      NetworkTesterPacket packet;
+      packet.set_type(NetworkTesterPacket::TEST_START);
+      udp_transport_->SetRemoteAddress(remote_addr);
+      SendData(packet, rtc::Optional<size_t>());
+      packet_sender_.reset(new PacketSender(this, config_file_path_));
+      packet_sender_->StartSending();
+      rtc::CritScope scoped_lock(&local_test_done_lock_);
+      local_test_done_ = false;
+      remote_test_done_ = false;
+      break;
+    }
+    case NetworkTesterPacket::TEST_START: {
+      packet_sender_.reset(new PacketSender(this, config_file_path_));
+      packet_sender_->StartSending();
+      break;
+    }
+    case NetworkTesterPacket::TEST_DATA: {
+      packet.set_arrival_timestamp(packet_time.timestamp);
+      packet.set_packet_size(len);
+      // log packet
+      break;
+    }
+    case NetworkTesterPacket::TEST_DONE: {
+      remote_test_done_ = true;
+      break;
+    }
+    default: { RTC_NOTREACHED(); }
+  }
+}
+
+}  // namespace webrtc
diff --git a/webrtc/tools/network_tester/test_controller.h b/webrtc/tools/network_tester/test_controller.h
new file mode 100644
index 0000000..5dae3ea
--- /dev/null
+++ b/webrtc/tools/network_tester/test_controller.h
@@ -0,0 +1,75 @@
+/*
+ *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_TOOLS_NETWORK_TESTER_TEST_CONTROLLER_H_
+#define WEBRTC_TOOLS_NETWORK_TESTER_TEST_CONTROLLER_H_
+
+#include <array>
+#include <limits>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "webrtc/base/ignore_wundef.h"
+#include "webrtc/p2p/base/basicpacketsocketfactory.h"
+#include "webrtc/p2p/base/udptransport.h"
+#include "webrtc/tools/network_tester/packet_sender.h"
+
+#ifdef WEBRTC_NETWORK_TESTER_PROTO
+RTC_PUSH_IGNORING_WUNDEF()
+#include "webrtc/tools/network_tester/network_tester_packet.pb.h"
+RTC_POP_IGNORING_WUNDEF()
+using webrtc::network_tester::packet::NetworkTesterPacket;
+#else
+class NetworkTesterPacket;
+#endif  // WEBRTC_NETWORK_TESTER_PROTO
+
+namespace webrtc {
+
+constexpr size_t kEthernetMtu = 1500;
+
+class TestController : public sigslot::has_slots<> {
+ public:
+  TestController(int min_port,
+                 int max_port,
+                 const std::string& config_file_path);
+
+  void Run();
+
+  void SendConnectTo(const std::string& hostname, int port);
+
+  void SendData(const NetworkTesterPacket& packet,
+                rtc::Optional<size_t> data_size);
+
+  void OnTestDone();
+
+  bool IsTestDone();
+
+ private:
+  void OnReadPacket(rtc::AsyncPacketSocket* socket,
+                    const char* data,
+                    size_t len,
+                    const rtc::SocketAddress& remote_addr,
+                    const rtc::PacketTime& packet_time);
+  rtc::ThreadChecker test_controller_thread_checker_;
+  rtc::SequencedTaskChecker packet_sender_checker_;
+  rtc::BasicPacketSocketFactory socket_factory_;
+  const std::string config_file_path_;
+  rtc::CriticalSection local_test_done_lock_;
+  bool local_test_done_ GUARDED_BY(local_test_done_lock_);
+  bool remote_test_done_;
+  std::array<char, kEthernetMtu> send_data_;
+  std::unique_ptr<cricket::UdpTransport> udp_transport_;
+  std::unique_ptr<PacketSender> packet_sender_;
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_TOOLS_NETWORK_TESTER_TEST_CONTROLLER_H_