[overnet] Get things ready for use.

This is relanding a previous change, with host side fuzzing fixed.

Allow FIDL messages (including channel handles) to be propagated between
overnet instances.

Add an example suite in Rust.

Refactor the overnet app to better separate concerns.

Propagate service & routing information between nodes.

Clean up tracing API's.

Initial crypto support.

Enable one fuzzer.

Reorganize files in lib/overnet.

Rewrite/fix packet loss handling.

Test: all unit tests pass, compiled locally with fx set x64 to verify host side fuzzers no longer cause problems.

Change-Id: Id30f4dbb1567e4e8d0d4efb7955b01a76f158ddb
diff --git a/bin/overnet/overnetstack/BUILD.gn b/bin/overnet/overnetstack/BUILD.gn
index 78ef810..4baebcd 100644
--- a/bin/overnet/overnetstack/BUILD.gn
+++ b/bin/overnet/overnetstack/BUILD.gn
@@ -4,18 +4,63 @@
 
 import("//build/package.gni")
 
+source_set("overnet_fuchsia_compatibility") {
+  sources = [
+    "fuchsia_port.h",
+  ]
+
+  deps = [
+    "//garnet/lib/overnet",
+  ]
+}
+
+source_set("fidl_utils") {
+  sources = [
+    "fidl_utils.h",
+  ]
+
+  deps = [
+    ":overnet_fuchsia_compatibility",
+    "//garnet/lib/overnet",
+    "//garnet/public/lib/fidl/cpp",
+  ]
+}
+
+executable("overnetstack_unittests") {
+  testonly = true
+
+  sources = [
+    "run_all_tests.cc",
+    "fidl_utils_test.cc",
+  ]
+
+  deps = [
+    ":fidl_utils",
+    "//garnet/public/fidl/fuchsia.overnet",
+    "//third_party/googletest:gtest",
+    "//third_party/googletest:gmock",
+  ]
+}
+
 executable("bin") {
   output_name = "overnetstack"
 
   sources = [
-    "fuchsia_port.h",
+    "bound_channel.h",
+    "bound_channel.cc",
     "main.cc",
     "mdns.h",
     "mdns.cc",
-    "udp_nub.h"
+    "service.h",
+    "service.cc",
+    "overnet_app.h",
+    "overnet_app.cc",
+    "udp_nub.h",
   ]
 
   deps = [
+    ":overnet_fuchsia_compatibility",
+    ":fidl_utils",
     "//garnet/lib/overnet",
     "//garnet/public/fidl/fuchsia.mdns",
     "//garnet/public/fidl/fuchsia.overnet",
@@ -42,3 +87,17 @@
     },
   ]
 }
+
+package("overnetstack_tests") {
+  testonly = true
+
+  deps = [
+    ":overnetstack_unittests",
+  ]
+
+  tests = [
+    {
+       name = "overnetstack_unittests"
+    }
+  ]
+}
diff --git a/bin/overnet/overnetstack/bound_channel.cc b/bin/overnet/overnetstack/bound_channel.cc
new file mode 100644
index 0000000..3114f7d
--- /dev/null
+++ b/bin/overnet/overnetstack/bound_channel.cc
@@ -0,0 +1,331 @@
+// Copyright 2018 The Fuchsia 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 "bound_channel.h"
+#include <zircon/assert.h>
+#include "fuchsia_port.h"
+#include "garnet/lib/overnet/endpoint/message_builder.h"
+#include "overnet_app.h"
+
+namespace overnetstack {
+
+BoundChannel::BoundChannel(OvernetApp* app,
+                           overnet::RouterEndpoint::NewStream ns,
+                           zx::channel channel)
+    : app_(app),
+      overnet_stream_(std::move(ns)),
+      zx_channel_(std::move(channel)) {
+  assert(zx_channel_.is_valid());
+  // Kick off the two read loops: one from the network and the other from the zx
+  // channel. Each proceeds much the same: as data is read, it's written and
+  // then the next read is begun.
+  StartNetRead();
+  StartChannelRead();
+}
+
+// Overnet MessageReceiver that builds up a FIDL channel message
+class BoundChannel::FidlMessageBuilder final : public overnet::MessageReceiver {
+ public:
+  FidlMessageBuilder(BoundChannel* stream)
+      : stream_(stream),
+        message_(fidl::BytePart(
+                     bytes_, ZX_CHANNEL_MAX_MSG_BYTES,
+                     // We start with enough space just for the FIDL header
+                     sizeof(fidl_message_header_t)),
+                 fidl::HandlePart(handles_, ZX_CHANNEL_MAX_MSG_HANDLES)) {
+    // Zero out header to start with
+    message_.header().txid = 0;
+    message_.header().reserved0 = 0;
+    message_.header().flags = 0;
+  }
+
+  FidlMessageBuilder(const FidlMessageBuilder&) = delete;
+  FidlMessageBuilder& operator=(const FidlMessageBuilder&) = delete;
+
+  ~FidlMessageBuilder() {}
+
+  const fidl::Message& message() const { return message_; }
+  fidl::Message& message() { return message_; }
+
+  overnet::Status SetTransactionId(uint32_t txid) override {
+    message_.set_txid(txid);
+    return overnet::Status::Ok();
+  }
+
+  overnet::Status SetOrdinal(uint32_t ordinal) override {
+    message_.header().ordinal = ordinal;
+    return overnet::Status::Ok();
+  }
+
+  overnet::Status SetBody(overnet::Slice body) override {
+    // For now we copy the body into the message.
+    // TODO(ctiller): consider other schemes to eliminate this copy?
+    const auto new_actual = sizeof(fidl_message_header_t) + body.length();
+    if (new_actual > message_.bytes().capacity()) {
+      return overnet::Status(overnet::StatusCode::FAILED_PRECONDITION,
+                             "Message too large");
+    }
+    message_.bytes().set_actual(new_actual);
+    memcpy(message_.bytes().begin(), body.begin(), body.length());
+    return overnet::Status::Ok();
+  }
+
+  overnet::Status AppendUnknownHandle() override { return AppendHandle(0); }
+
+  overnet::Status AppendChannelHandle(
+      overnet::RouterEndpoint::ReceivedIntroduction received_introduction)
+      override;
+
+  // Mark this message as sent, meaning that we no longer need to close
+  // contained handles.
+  void Sent() { message_.ClearHandlesUnsafe(); }
+
+ private:
+  overnet::Status AppendHandle(zx_handle_t hdl) {
+    auto& handles = message_.handles();
+    if (handles.actual() == handles.capacity()) {
+      zx_handle_close(hdl);
+      return overnet::Status(overnet::StatusCode::FAILED_PRECONDITION,
+                             "Too many handles in message");
+    }
+    handles.data()[handles.actual()] = hdl;
+    handles.set_actual(handles.actual() + 1);
+    return overnet::Status::Ok();
+  }
+
+  BoundChannel* stream_;
+  uint8_t bytes_[ZX_CHANNEL_MAX_MSG_BYTES];
+  zx_handle_t handles_[ZX_CHANNEL_MAX_MSG_HANDLES];
+  fidl::Message message_;
+};
+
+void BoundChannel::Close(const overnet::Status& status) {
+  OVERNET_TRACE(DEBUG) << "CLOSE: " << status << " closed=" << closed_;
+  if (closed_) {
+    return;
+  }
+  closed_ = true;
+  zx_channel_.reset();
+  if (net_recv_) {
+    net_recv_->Close(status);
+  }
+  overnet_stream_.Close(status, [this] { delete this; });
+}
+
+void BoundChannel::WriteToChannelAndStartNextRead(
+    std::unique_ptr<FidlMessageBuilder> builder) {
+  OVERNET_TRACE(DEBUG) << "WriteToChannelAndStartNextRead txid="
+                       << builder->message().txid()
+                       << " bytes_cnt=" << builder->message().bytes().actual()
+                       << " handles_cnt="
+                       << builder->message().handles().actual()
+                       << " hdl=" << zx_channel_.get();
+  auto err = builder->message().Write(zx_channel_.get(), 0);
+  switch (err) {
+    case ZX_OK:
+      builder->Sent();
+      StartNetRead();
+      break;
+    case ZX_ERR_SHOULD_WAIT:
+      // Kernel push back: capture the slice, and ask to be informed when we
+      // can write.
+      waiting_to_write_ = std::move(builder);
+      err = async_begin_wait(dispatcher_, &wait_send_.wait);
+      if (err != ZX_OK) {
+        Close(ToOvernetStatus(err).WithContext("Beginning wait for write"));
+      }
+      break;
+    default:
+      // If the write failed, close the stream.
+      Close(ToOvernetStatus(err).WithContext("Write"));
+  }
+}
+
+void BoundChannel::StartChannelRead() {
+  OVERNET_TRACE(DEBUG) << "StartChannelRead hdl=" << zx_channel_.get();
+  uint8_t message_buffer[ZX_CHANNEL_MAX_MSG_BYTES];
+  zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
+  fidl::Message message(
+      fidl::BytePart(message_buffer, ZX_CHANNEL_MAX_MSG_BYTES),
+      fidl::HandlePart(handles, ZX_CHANNEL_MAX_MSG_HANDLES));
+  auto err = message.Read(zx_channel_.get(), 0);
+  OVERNET_TRACE(DEBUG) << "StartChannelRead read result: "
+                       << ToOvernetStatus(err);
+  switch (err) {
+    case ZX_OK: {
+      // Successful read: build the output message.
+      OVERNET_TRACE(DEBUG) << "Convert message to overnet";
+      auto send_slice = ChannelMessageToOvernet(std::move(message));
+      OVERNET_TRACE(DEBUG) << "Convert message to overnet got: " << send_slice;
+      if (send_slice.is_error()) {
+        Close(send_slice.AsStatus());
+        break;
+      }
+      overnet::RouterEndpoint::Stream::SendOp(&overnet_stream_,
+                                              send_slice->length())
+          .Push(std::move(*send_slice), [this] { StartChannelRead(); });
+    } break;
+    case ZX_ERR_SHOULD_WAIT:
+      // Kernel push back: ask to be informed when we can try again.
+      err = async_begin_wait(dispatcher_, &wait_recv_.wait);
+      OVERNET_TRACE(DEBUG) << "async_begin_wait result: "
+                           << ToOvernetStatus(err);
+      if (err != ZX_OK) {
+        Close(ToOvernetStatus(err).WithContext("Beginning wait for read"));
+        break;
+      }
+      break;
+    default:
+      // If the read failed, close the stream.
+      Close(ToOvernetStatus(err).WithContext("Read"));
+      break;
+  }
+}
+
+void BoundChannel::SendReady(async_dispatcher_t* dispatcher, async_wait_t* wait,
+                             zx_status_t status,
+                             const zx_packet_signal_t* signal) {
+  // Trampoline back into writing.
+  static_assert(offsetof(BoundWait, wait) == 0,
+                "The wait must be the first member of BoundWait for this "
+                "cast to be valid.");
+  reinterpret_cast<BoundWait*>(wait)->stream->OnSendReady(status, signal);
+}
+
+void BoundChannel::OnSendReady(zx_status_t status,
+                               const zx_packet_signal_t* signal) {
+  OVERNET_TRACE(DEBUG) << "OnSendReady: status=" << ToOvernetStatus(status);
+  WriteToChannelAndStartNextRead(std::move(waiting_to_write_));
+}
+
+void BoundChannel::RecvReady(async_dispatcher_t* dispatcher, async_wait_t* wait,
+                             zx_status_t status,
+                             const zx_packet_signal_t* signal) {
+  // Trampoline back into reading.
+  static_assert(offsetof(BoundWait, wait) == 0,
+                "The wait must be the first member of BoundWait for this "
+                "cast to be valid.");
+  reinterpret_cast<BoundWait*>(wait)->stream->OnRecvReady(status, signal);
+}
+
+void BoundChannel::OnRecvReady(zx_status_t status,
+                               const zx_packet_signal_t* signal) {
+  OVERNET_TRACE(DEBUG) << "OnRecvReady: status=" << ToOvernetStatus(status)
+                       << " observed=" << signal->observed;
+
+  if (status != ZX_OK) {
+    Close(ToOvernetStatus(status).WithContext("OnRecvReady"));
+    return;
+  }
+
+  if (signal->observed & ZX_CHANNEL_READABLE) {
+    StartChannelRead();
+    return;
+  }
+
+  // Note: we flush all reads before propagating the close.
+  ZX_DEBUG_ASSERT(signal->observed & ZX_CHANNEL_PEER_CLOSED);
+  Close(overnet::Status::Ok());
+}
+
+void BoundChannel::StartNetRead() {
+  OVERNET_TRACE(DEBUG) << "StartNetRead";
+  net_recv_.Reset(&overnet_stream_);
+  net_recv_->PullAll(
+      [this](overnet::StatusOr<std::vector<overnet::Slice>> status) {
+        OVERNET_TRACE(DEBUG) << "StartNetRead got " << status;
+        if (status.is_error()) {
+          // If a read failed, close the stream.
+          Close(status.AsStatus());
+          return;
+        }
+
+        if (closed_) {
+          return;
+        }
+
+        // Write the message to the channel, then start reading again.
+        // Importantly: don't start reading until we've written to ensure
+        // that there's push back in the system.
+        auto builder = std::make_unique<FidlMessageBuilder>(this);
+        auto parse_status = overnet::ParseMessageInto(
+            overnet::Slice::Join(status->begin(), status->end()),
+            overnet_stream_.peer(), app_->endpoint(), builder.get());
+        WriteToChannelAndStartNextRead(std::move(builder));
+      });
+}
+
+overnet::StatusOr<overnet::Slice> BoundChannel::ChannelMessageToOvernet(
+    fidl::Message message) {
+  overnet::MessageWireEncoder builder(&overnet_stream_);
+  if (!message.has_header()) {
+    return overnet::Status(overnet::StatusCode::FAILED_PRECONDITION,
+                           "FIDL message without a header");
+  }
+  assert(message.flags() == 0);
+  auto status =
+      builder.SetTransactionId(message.txid())
+          .Then([&] { return builder.SetOrdinal(message.ordinal()); })
+          .Then([&] {
+            return builder.SetBody(overnet::Slice::ReferencingContainer(
+                message.bytes().begin(), message.bytes().end()));
+          });
+
+  // Keep track of failure.
+  // We cannot leave the loop below early or we risk leaking handles.
+  for (auto handle : message.handles()) {
+    zx::handle hdl(handle);
+    if (status.is_error()) {
+      continue;
+    }
+    zx_info_handle_basic_t info;
+    auto err = hdl.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr,
+                            nullptr);
+    if (err != ZX_OK) {
+      status = ToOvernetStatus(err).WithContext("Getting handle type");
+      continue;
+    }
+    switch (info.type) {
+      case ZX_OBJ_TYPE_CHANNEL: {
+        auto new_stream = builder.AppendChannelHandle(overnet::Introduction());
+        auto channel = zx::channel(hdl.release());
+        assert(channel.is_valid());
+        assert(!hdl.is_valid());
+        if (new_stream.is_error()) {
+          status = new_stream.AsStatus();
+        } else {
+          app_->BindStream(std::move(*new_stream), std::move(channel));
+          assert(!channel.is_valid());
+        }
+      } break;
+      default:
+        auto append_status = builder.AppendUnknownHandle().WithContext(
+            "Appending unknown channel");
+        if (append_status.is_error()) {
+          status = append_status;
+        }
+        break;
+    }
+  }
+  message.ClearHandlesUnsafe();
+  if (status.is_error()) {
+    return status;
+  }
+  return builder.Write(overnet::Border::None());
+}
+
+overnet::Status BoundChannel::FidlMessageBuilder::AppendChannelHandle(
+    overnet::RouterEndpoint::ReceivedIntroduction received_introduction) {
+  // TODO(ctiller): interpret received_introduction.introduction?
+  zx_handle_t a, b;
+  zx_status_t err = zx_channel_create(0, &a, &b);
+  if (err != ZX_OK) {
+    return ToOvernetStatus(err).WithContext("Appending channel");
+  }
+  stream_->app_->BindStream(std::move(received_introduction.new_stream),
+                            zx::channel(a));
+  return AppendHandle(b);
+}
+
+}  // namespace overnetstack
\ No newline at end of file
diff --git a/bin/overnet/overnetstack/bound_channel.h b/bin/overnet/overnetstack/bound_channel.h
new file mode 100644
index 0000000..84d75c6
--- /dev/null
+++ b/bin/overnet/overnetstack/bound_channel.h
@@ -0,0 +1,73 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/async/wait.h>
+#include <lib/fidl/cpp/message.h>
+#include <lib/zx/channel.h>
+#include "garnet/lib/overnet/endpoint/router_endpoint.h"
+
+namespace overnetstack {
+
+class OvernetApp;
+
+// Creates a stream by combining a zx::channel with an overnet DatagramStream.
+// Reads from the overnet stream become writes to the zx channel, and vice
+// versa. Errors are propagated.
+// TODO(ctiller): epitaph support.
+// TODO(ctiller): rewrite messages to:
+// - support some limited handle propagation across overnet.
+// - ensure system messages are never propagated.
+class BoundChannel {
+ public:
+  BoundChannel(OvernetApp* app, overnet::RouterEndpoint::NewStream ns,
+               zx::channel channel);
+
+ private:
+  class FidlMessageBuilder;
+
+  void Close(const overnet::Status& status);
+  void StartNetRead();
+  void WriteToChannelAndStartNextRead(
+      std::unique_ptr<FidlMessageBuilder> builder);
+
+  overnet::StatusOr<overnet::Slice> ChannelMessageToOvernet(
+      fidl::Message message);
+
+  void StartChannelRead();
+
+  struct BoundWait {
+    async_wait_t wait;
+    BoundChannel* stream;
+  };
+
+  static void SendReady(async_dispatcher_t* dispatcher, async_wait_t* wait,
+                        zx_status_t status, const zx_packet_signal_t* signal);
+  void OnSendReady(zx_status_t status, const zx_packet_signal_t* signal);
+  static void RecvReady(async_dispatcher_t* dispatcher, async_wait_t* wait,
+                        zx_status_t status, const zx_packet_signal_t* signal);
+  void OnRecvReady(zx_status_t status, const zx_packet_signal_t* signal);
+
+  OvernetApp* const app_;
+  async_dispatcher_t* const dispatcher_ = async_get_default_dispatcher();
+  bool closed_ = false;
+  overnet::RouterEndpoint::Stream overnet_stream_;
+  zx::channel zx_channel_;
+  overnet::Optional<overnet::RouterEndpoint::Stream::ReceiveOp> net_recv_;
+  std::unique_ptr<FidlMessageBuilder> waiting_to_write_;
+  BoundWait wait_send_{{{ASYNC_STATE_INIT},
+                        &BoundChannel::SendReady,
+                        zx_channel_.get(),
+                        ZX_CHANNEL_WRITABLE},
+                       this};
+  BoundWait wait_recv_{{{ASYNC_STATE_INIT},
+                        &BoundChannel::RecvReady,
+                        zx_channel_.get(),
+                        ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED},
+                       this};
+};
+
+}  // namespace overnetstack
diff --git a/bin/overnet/overnetstack/fidl_utils.h b/bin/overnet/overnetstack/fidl_utils.h
new file mode 100644
index 0000000..79117da
--- /dev/null
+++ b/bin/overnet/overnetstack/fidl_utils.h
@@ -0,0 +1,41 @@
+// Copyright 2018 The Fuchsia 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 "fuchsia_port.h"
+#include "garnet/lib/overnet/vocabulary/slice.h"
+#include "garnet/lib/overnet/vocabulary/status.h"
+#include "garnet/public/lib/fidl/cpp/coding_traits.h"
+#include "garnet/public/lib/fidl/cpp/decoder.h"
+#include "garnet/public/lib/fidl/cpp/encoder.h"
+
+namespace overnetstack {
+
+template <class T>
+overnet::Slice EncodeMessage(T* message) {
+  fidl::Encoder encoder(0);
+  message->Encode(&encoder, encoder.Alloc(fidl::CodingTraits<T>::encoded_size));
+  const auto& bytes = encoder.GetMessage().bytes();
+  // Omit 16-byte RPC header.
+  // TODO(ctiller): Fix FIDL API's for this use case.
+  return overnet::Slice::FromCopiedBuffer(bytes.begin() + 16,
+                                          bytes.actual() - 16);
+}
+
+template <class T>
+overnet::StatusOr<T> DecodeMessage(overnet::Slice message) {
+  fidl::Message fidl_msg(fidl::BytePart(const_cast<uint8_t*>(message.begin()),
+                                        message.length(), message.length()),
+                         fidl::HandlePart());
+  const char* err_msg;
+  auto status = fidl_msg.Decode(T::FidlType, &err_msg);
+  if (status != ZX_OK) {
+    return ToOvernetStatus(status).WithContext(err_msg);
+  }
+  fidl::Decoder decoder(std::move(fidl_msg));
+  T result;
+  T::Decode(&decoder, &result, 0);
+  return result;
+}
+
+}  // namespace overnetstack
diff --git a/bin/overnet/overnetstack/fidl_utils_test.cc b/bin/overnet/overnetstack/fidl_utils_test.cc
new file mode 100644
index 0000000..4ed9bac
--- /dev/null
+++ b/bin/overnet/overnetstack/fidl_utils_test.cc
@@ -0,0 +1,23 @@
+// Copyright 2018 The Fuchsia 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 "fidl_utils.h"
+#include <fuchsia/overnet/cpp/fidl.h>
+#include <gtest/gtest.h>
+
+namespace overnetstack {
+namespace fidl_utils_test {
+
+TEST(FidlUtils, EncodeDecode) {
+  fuchsia::overnet::Peer peer;
+  peer.id = 123;
+  peer.description.services.push_back("fuchsia.overnet.Overnet");
+  auto round_tripped_peer =
+      DecodeMessage<fuchsia::overnet::Peer>(EncodeMessage(&peer));
+  EXPECT_TRUE(round_tripped_peer.is_ok()) << round_tripped_peer.AsStatus();
+  EXPECT_EQ(round_tripped_peer->id, 123u);
+}
+
+}  // namespace fidl_utils_test
+}  // namespace overnetstack
diff --git a/bin/overnet/overnetstack/fuchsia_port.h b/bin/overnet/overnetstack/fuchsia_port.h
index bedf385..c970180 100644
--- a/bin/overnet/overnetstack/fuchsia_port.h
+++ b/bin/overnet/overnetstack/fuchsia_port.h
@@ -4,9 +4,11 @@
 
 #pragma once
 
-#include <garnet/lib/overnet/status.h>
-#include <garnet/lib/overnet/timer.h>
+#include <garnet/lib/overnet/environment/timer.h>
+#include <garnet/lib/overnet/vocabulary/status.h>
 #include <lib/async/cpp/time.h>
+#include <zircon/status.h>
+#include <sstream>
 
 namespace overnetstack {
 
@@ -33,8 +35,11 @@
       return overnet::Status::Ok();
     case ZX_ERR_CANCELED:
       return overnet::Status::Cancelled();
-    default:
-      return overnet::Status(overnet::StatusCode::UNKNOWN, "Unknown zx_status");
+    default: {
+      std::ostringstream out;
+      out << "zx_status:" << zx_status_get_string(status);
+      return overnet::Status(overnet::StatusCode::UNKNOWN, out.str());
+    }
   }
 }
 
diff --git a/bin/overnet/overnetstack/main.cc b/bin/overnet/overnetstack/main.cc
index 56de9a2..ab876d5 100644
--- a/bin/overnet/overnetstack/main.cc
+++ b/bin/overnet/overnetstack/main.cc
@@ -2,13 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <fuchsia/overnet/cpp/fidl.h>
-#include <lib/async-loop/cpp/loop.h>
-#include <lib/async/task.h>
 #include "fuchsia_port.h"
-#include "garnet/lib/overnet/router_endpoint.h"
-#include "lib/component/cpp/startup_context.h"
 #include "mdns.h"
+#include "overnet_app.h"
+#include "service.h"
+#include "udp_nub.h"
 
 namespace overnetstack {
 
@@ -29,7 +27,6 @@
                           zx_status_t status) {
     FireTimeout(static_cast<Task*>(task)->timeout, ToOvernetStatus(status));
   }
-
   void InitTimeout(overnet::Timeout* timeout,
                    overnet::TimeStamp when) override {
     auto* async_timeout = TimeoutStorage<Task>(timeout);
@@ -41,7 +38,6 @@
       FireTimeout(timeout, overnet::Status::Cancelled());
     }
   }
-
   void CancelTimeout(overnet::Timeout* timeout,
                      overnet::Status status) override {
     if (async_cancel_task(dispatcher_, TimeoutStorage<Task>(timeout)) ==
@@ -51,10 +47,9 @@
   }
 };
 
-class FuchsiaLog final : public overnet::TraceSinkInterface {
+class FuchsiaLog final : public overnet::TraceRenderer {
  public:
-  void Done() override {}
-  void Trace(overnet::TraceOutput output) override {
+  void Render(overnet::TraceOutput output) override {
     auto severity = [sev = output.severity] {
       switch (sev) {
         case overnet::Severity::DEBUG:
@@ -70,60 +65,21 @@
     fxl::LogMessage(severity, output.file, output.line, nullptr).stream()
         << output.message;
   }
-};
-
-static FuchsiaLog fuchsia_log;
-static overnet::TraceSink fuchsia_trace_sink(overnet::Severity::INFO,
-                                             &fuchsia_log);
-
-overnet::NodeId GenerateNodeId() {
-  uint64_t out;
-  zx_cprng_draw(&out, sizeof(out));
-  return overnet::NodeId(out);
-}
-
-class OvernetApp final : public fuchsia::overnet::Overnet {
- public:
-  OvernetApp() : context_(component::StartupContext::CreateFromStartupInfo()) {
-    context_->outgoing().AddPublicService(bindings_.GetHandler(this));
-  }
-
-  overnet::Status Start() {
-    return udp_nub_.Start().Then([this]() {
-      mdns_advert_ =
-          std::make_unique<MdnsAdvertisement>(context_.get(), &udp_nub_);
-      RunMdnsIntroducer(context_.get(), &udp_nub_);
-      return overnet::Status::Ok();
-    });
-  }
-
-  //////////////////////////////////////////////////////////////////////////////////////////////////
-  // Method implementations
-
-  void ListPeers(ListPeersCallback callback) override {
-    using Peer = fuchsia::overnet::Peer;
-    std::vector<Peer> response;
-    endpoint_.ForEachPeer([&response](overnet::NodeId node) {
-      response.emplace_back(Peer{node.get()});
-    });
-    callback(fidl::VectorPtr<Peer>(std::move(response)));
-  }
-
- private:
-  FuchsiaTimer timer_;
-  std::unique_ptr<component::StartupContext> context_;
-  fidl::BindingSet<fuchsia::overnet::Overnet> bindings_;
-  overnet::RouterEndpoint endpoint_{&timer_, fuchsia_trace_sink,
-                                    GenerateNodeId(), true};
-  UdpNub udp_nub_{&endpoint_, fuchsia_trace_sink};
-  std::unique_ptr<MdnsAdvertisement> mdns_advert_;
+  void NoteParentChild(overnet::Op, overnet::Op) override {}
 };
 
 }  // namespace overnetstack
 
 int main(int argc, const char** argv) {
   async::Loop loop(&kAsyncLoopConfigAttachToThread);
-  overnetstack::OvernetApp app;
+  overnetstack::FuchsiaLog fuchsia_log;
+  overnet::ScopedRenderer scoped_renderer(&fuchsia_log);
+  overnetstack::FuchsiaTimer fuchsia_timer;
+  overnetstack::OvernetApp app(&fuchsia_timer);
+  app.InstantiateActor<overnetstack::Service>();
+  auto* udp_nub = app.InstantiateActor<overnetstack::UdpNub>();
+  app.InstantiateActor<overnetstack::MdnsIntroducer>(udp_nub);
+  app.InstantiateActor<overnetstack::MdnsAdvertisement>(udp_nub);
   auto status = app.Start().Then([&]() {
     loop.Run();
     return overnet::Status::Ok();
diff --git a/bin/overnet/overnetstack/mdns.cc b/bin/overnet/overnetstack/mdns.cc
index 44f2bf2..8bb4579 100644
--- a/bin/overnet/overnetstack/mdns.cc
+++ b/bin/overnet/overnetstack/mdns.cc
@@ -5,7 +5,7 @@
 #include "mdns.h"
 #include <fuchsia/mdns/cpp/fidl.h>
 #include "fuchsia_port.h"
-#include "garnet/lib/overnet/node_id.h"
+#include "garnet/lib/overnet/labels/node_id.h"
 #include "garnet/public/lib/fostr/fidl/fuchsia/mdns/formatting.h"
 
 namespace overnetstack {
@@ -13,9 +13,9 @@
 static const char* kServiceName =
     "__overnet__mdns__test__1db2_6473_a3b1_500c__._udp.";
 
-class MdnsIntroducer : public fbl::RefCounted<MdnsIntroducer> {
+class MdnsIntroducer::Impl : public fbl::RefCounted<MdnsIntroducer> {
  public:
-  MdnsIntroducer(UdpNub* nub) : nub_(nub) {}
+  Impl(UdpNub* nub) : nub_(nub) {}
 
   void Begin(component::StartupContext* startup_context) {
     std::cerr << "Querying mDNS for overnet services [" << kServiceName
@@ -39,7 +39,7 @@
   void RunLoop(uint64_t version) {
     subscription_->GetInstances(
         version,
-        [self = fbl::RefPtr<MdnsIntroducer>(this)](
+        [self = fbl::RefPtr<Impl>(this)](
             uint64_t new_version,
             fidl::VectorPtr<fuchsia::mdns::MdnsServiceInstance> services) {
           // Convert list of services into a service map.
@@ -186,11 +186,18 @@
   ServiceMap last_result_;
 };
 
-void RunMdnsIntroducer(component::StartupContext* startup_context,
-                       UdpNub* nub) {
-  fbl::MakeRefCounted<MdnsIntroducer>(nub)->Begin(startup_context);
+MdnsIntroducer::MdnsIntroducer(OvernetApp* app, UdpNub* udp_nub)
+    : app_(app), udp_nub_(udp_nub) {}
+
+overnet::Status MdnsIntroducer::Start() {
+  auto impl = fbl::MakeRefCounted<Impl>(udp_nub_);
+  impl_ = std::move(impl);
+  impl_->Begin(app_->startup_context());
+  return overnet::Status::Ok();
 }
 
+MdnsIntroducer::~MdnsIntroducer() {}
+
 class MdnsAdvertisement::Impl {
  public:
   Impl(component::StartupContext* startup_context, UdpNub* nub)
@@ -217,9 +224,13 @@
   const overnet::NodeId node_id_;
 };
 
-MdnsAdvertisement::MdnsAdvertisement(component::StartupContext* startup_context,
-                                     UdpNub* nub)
-    : impl_(std::make_unique<Impl>(startup_context, nub)) {}
+MdnsAdvertisement::MdnsAdvertisement(OvernetApp* app, UdpNub* udp_nub)
+    : app_(app), udp_nub_(udp_nub) {}
+
+overnet::Status MdnsAdvertisement::Start() {
+  impl_.reset(new Impl(app_->startup_context(), udp_nub_));
+  return overnet::Status::Ok();
+}
 
 MdnsAdvertisement::~MdnsAdvertisement() {}
 
diff --git a/bin/overnet/overnetstack/mdns.h b/bin/overnet/overnetstack/mdns.h
index d1aadf2..6432c55 100644
--- a/bin/overnet/overnetstack/mdns.h
+++ b/bin/overnet/overnetstack/mdns.h
@@ -4,23 +4,36 @@
 
 #pragma once
 
-#include <garnet/lib/overnet/router_endpoint.h>
-#include "lib/component/cpp/startup_context.h"
+#include <fbl/ref_ptr.h>
+#include <garnet/lib/overnet/endpoint/router_endpoint.h>
+#include "overnet_app.h"
 #include "udp_nub.h"
 
 namespace overnetstack {
 
-void RunMdnsIntroducer(component::StartupContext* startup_context,
-                       UdpNub* udp_nub);
-
-class MdnsAdvertisement {
+class MdnsIntroducer : public OvernetApp::Actor {
  public:
-  MdnsAdvertisement(component::StartupContext* startup_context,
-                    UdpNub* udp_nub);
-  ~MdnsAdvertisement();
+  MdnsIntroducer(OvernetApp* app, UdpNub* udp_nub);
+  ~MdnsIntroducer();
+  overnet::Status Start() override;
 
  private:
   class Impl;
+  OvernetApp* const app_;
+  UdpNub* const udp_nub_;
+  fbl::RefPtr<Impl> impl_;
+};
+
+class MdnsAdvertisement : public OvernetApp::Actor {
+ public:
+  MdnsAdvertisement(OvernetApp* app, UdpNub* udp_nub);
+  ~MdnsAdvertisement();
+  overnet::Status Start() override;
+
+ private:
+  OvernetApp* const app_;
+  UdpNub* const udp_nub_;
+  class Impl;
   std::unique_ptr<Impl> impl_;
 };
 
diff --git a/bin/overnet/overnetstack/overnet_app.cc b/bin/overnet/overnetstack/overnet_app.cc
new file mode 100644
index 0000000..afbadec
--- /dev/null
+++ b/bin/overnet/overnetstack/overnet_app.cc
@@ -0,0 +1,106 @@
+// Copyright 2018 The Fuchsia 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 "overnet_app.h"
+#include <fuchsia/overnet/cpp/fidl.h>
+#include "bound_channel.h"
+#include "fidl_utils.h"
+#include "fuchsia_port.h"
+
+namespace overnetstack {
+
+OvernetApp::OvernetApp(overnet::Timer* timer)
+    : startup_context_(component::StartupContext::CreateFromStartupInfo()),
+      timer_(timer) {
+  UpdateDescription();
+}
+
+OvernetApp::~OvernetApp() {}
+
+overnet::NodeId OvernetApp::GenerateNodeId() {
+  uint64_t out;
+  zx_cprng_draw(&out, sizeof(out));
+  return overnet::NodeId(out);
+}
+
+overnet::Status OvernetApp::Start() {
+  for (size_t i = 0; i < actors_.size(); i++) {
+    auto status = actors_[i]->Start();
+    if (status.is_error()) {
+      actors_.resize(i);
+      return status.WithContext("Trying to start actor");
+    }
+  }
+  ReadNextIntroduction();
+  return overnet::Status::Ok();
+}
+
+void OvernetApp::RegisterServiceProvider(
+    const std::string& name, std::unique_ptr<ServiceProvider> provider) {
+  service_providers_.emplace(name, std::move(provider));
+  UpdateDescription();
+}
+
+void OvernetApp::UpdateDescription() {
+  fuchsia::overnet::PeerDescription desc;
+  desc.services = desc.services.New(0);
+  for (const auto& svc : service_providers_) {
+    desc.services.push_back(svc.first);
+  }
+  endpoint_.SetDescription(EncodeMessage(&desc));
+}
+
+void OvernetApp::BindStream(overnet::RouterEndpoint::NewStream ns,
+                            zx::channel channel) {
+  ZX_ASSERT(channel.is_valid());
+  zx_info_handle_basic_t info;
+  auto err = channel.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info),
+                              nullptr, nullptr);
+  ZX_ASSERT(err == ZX_OK);
+  ZX_ASSERT(info.type == ZX_OBJ_TYPE_CHANNEL);
+  new BoundChannel(this, std::move(ns), std::move(channel));
+  ZX_ASSERT(!channel.is_valid());
+}
+
+void OvernetApp::ConnectToLocalService(const overnet::Introduction& intro,
+                                       zx::channel channel) {
+  auto svc_slice = intro[overnet::Introduction::Key::ServiceName];
+  if (!svc_slice.has_value()) {
+    OVERNET_TRACE(DEBUG) << "No service name in local service request";
+    return;
+  }
+  const std::string svc(svc_slice->begin(), svc_slice->end());
+  auto it = service_providers_.find(svc);
+  if (it == service_providers_.end()) {
+    OVERNET_TRACE(DEBUG) << "Local service not found: " << svc;
+    return;
+  }
+  it->second->Connect(intro, std::move(channel));
+}
+
+void OvernetApp::ReadNextIntroduction() {
+  // Loop, reading service creation requests, and attempting to bind them to
+  // local services.
+  endpoint_.RecvIntro(
+      [this](overnet::StatusOr<overnet::RouterEndpoint::ReceivedIntroduction>
+                 status) {
+        if (status.is_error()) {
+          OVERNET_TRACE(ERROR)
+              << "Failed to read introduction: " << status.AsStatus();
+          return;
+        }
+        zx_handle_t a, b;
+        auto err = zx_channel_create(0, &a, &b);
+        if (err != ZX_OK) {
+          status->new_stream.Fail(
+              ToOvernetStatus(err).WithContext("ReadNextIntroduction"));
+          return;
+        }
+        BindStream(std::move(status->new_stream), zx::channel(a));
+        ConnectToLocalService(std::move(status->introduction), zx::channel(b));
+        ReadNextIntroduction();
+      });
+}
+
+}  // namespace overnetstack
\ No newline at end of file
diff --git a/bin/overnet/overnetstack/overnet_app.h b/bin/overnet/overnetstack/overnet_app.h
new file mode 100644
index 0000000..6cff8db
--- /dev/null
+++ b/bin/overnet/overnetstack/overnet_app.h
@@ -0,0 +1,88 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <zx/channel.h>
+#include <memory>
+#include "garnet/lib/overnet/endpoint/router_endpoint.h"
+#include "garnet/lib/overnet/environment/timer.h"
+#include "lib/component/cpp/startup_context.h"
+
+namespace overnetstack {
+
+// Main application object: provides common objects to Actors, which implement
+// the bulk of the functionality of the app.
+class OvernetApp final {
+ public:
+  OvernetApp(overnet::Timer* timer);
+  ~OvernetApp();
+  overnet::Status Start();
+
+  /////////////////////////////////////////////////////////////////////////////
+  // Some (usually asynchronous) service that mutates application state.
+  class Actor {
+   public:
+    virtual ~Actor() {}
+    virtual overnet::Status Start() = 0;
+  };
+
+  template <class T, class... Args>
+  T* InstantiateActor(Args&&... args) {
+    auto ptr = std::make_unique<T>(this, std::forward<Args>(args)...);
+    auto* out = ptr.get();
+    actors_.emplace_back(std::move(ptr));
+    return out;
+  }
+
+  /////////////////////////////////////////////////////////////////////////////
+  // Allows binding a zx::channel to some service denoted by an Introduction
+  // object.
+  class ServiceProvider {
+   public:
+    virtual ~ServiceProvider() {}
+    virtual void Connect(const overnet::Introduction& intro,
+                         zx::channel channel) = 0;
+  };
+
+  // Register a service provider for this app.
+  void RegisterServiceProvider(const std::string& name,
+                               std::unique_ptr<ServiceProvider> provider);
+
+  // Bind 'channel' to a local overnet service.
+  void ConnectToLocalService(const overnet::Introduction& intro,
+                             zx::channel channel);
+
+  /////////////////////////////////////////////////////////////////////////////
+  // Accessors for well known objects.
+
+  overnet::RouterEndpoint* endpoint() { return &endpoint_; }
+  component::StartupContext* startup_context() const {
+    return startup_context_.get();
+  }
+  overnet::Timer* timer() { return timer_; }
+  overnet::NodeId node_id() const { return node_id_; }
+
+  /////////////////////////////////////////////////////////////////////////////
+
+  // Bind together an overnet stream and a zx::channel and keep them
+  // communicating until one side closes.
+  void BindStream(overnet::RouterEndpoint::NewStream ns, zx::channel channel);
+
+ private:
+  void ReadNextIntroduction();
+  void UpdateDescription();
+
+  static overnet::NodeId GenerateNodeId();
+
+  const std::unique_ptr<component::StartupContext> startup_context_;
+  overnet::Timer* const timer_;
+  const overnet::NodeId node_id_ = GenerateNodeId();
+  overnet::RouterEndpoint endpoint_{timer_, node_id_, true};
+  std::vector<std::unique_ptr<Actor>> actors_;
+  std::unordered_map<std::string, std::unique_ptr<ServiceProvider>>
+      service_providers_;
+};
+
+}  // namespace overnetstack
diff --git a/lib/overnet/run_all_tests.cc b/bin/overnet/overnetstack/run_all_tests.cc
similarity index 100%
copy from lib/overnet/run_all_tests.cc
copy to bin/overnet/overnetstack/run_all_tests.cc
diff --git a/bin/overnet/overnetstack/service.cc b/bin/overnet/overnetstack/service.cc
new file mode 100644
index 0000000..ba4be0b
--- /dev/null
+++ b/bin/overnet/overnetstack/service.cc
@@ -0,0 +1,89 @@
+// Copyright 2018 The Fuchsia 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 "service.h"
+#include "fidl_utils.h"
+
+namespace overnetstack {
+
+Service::Service(OvernetApp* app) : app_(app) {}
+
+overnet::Status Service::Start() {
+  app_->startup_context()->outgoing().AddPublicService(
+      bindings_.GetHandler(this));
+  return overnet::Status::Ok();
+}
+
+void Service::ListPeers(ListPeersCallback callback) {
+  using Peer = fuchsia::overnet::Peer;
+  std::vector<Peer> response;
+  app_->endpoint()->ForEachNodeMetric(
+      [&response,
+       self_node = app_->endpoint()->node_id()](const overnet::NodeMetrics& m) {
+        auto desc_status =
+            DecodeMessage<fuchsia::overnet::PeerDescription>(m.description());
+        if (desc_status.is_error()) {
+          OVERNET_TRACE(WARNING) << "Omit peer with badly encoded description: "
+                                 << desc_status.AsStatus();
+          return;
+        }
+        response.emplace_back(Peer{m.node_id().get(), m.node_id() == self_node,
+                                   std::move(*desc_status)});
+      });
+  callback(fidl::VectorPtr<Peer>(std::move(response)));
+}
+
+void Service::RegisterService(
+    fidl::StringPtr service_name,
+    fidl::InterfaceHandle<fuchsia::overnet::ServiceProvider> provider) {
+  class ServiceProvider final : public OvernetApp::ServiceProvider {
+   public:
+    explicit ServiceProvider(fuchsia::overnet::ServiceProviderPtr provider)
+        : provider_(std::move(provider)) {}
+    void Connect(const overnet::Introduction& intro,
+                 zx::channel channel) final {
+      auto svc_slice = intro[overnet::Introduction::Key::ServiceName];
+      if (!svc_slice.has_value()) {
+        OVERNET_TRACE(DEBUG) << "No service name in local service request";
+        return;
+      }
+      const std::string svc(svc_slice->begin(), svc_slice->end());
+      provider_->ConnectToService(svc, std::move(channel));
+    }
+
+   private:
+    const fuchsia::overnet::ServiceProviderPtr provider_;
+  };
+  app_->RegisterServiceProvider(
+      service_name.get(), std::make_unique<ServiceProvider>(provider.Bind()));
+}
+
+void Service::ConnectToService(uint64_t node, fidl::StringPtr service_name,
+                               zx::channel channel) {
+  auto node_id = overnet::NodeId(node);
+  overnet::Introduction intro;
+  intro[overnet::Introduction::Key::ServiceName] =
+      overnet::Slice::FromContainer(service_name.get());
+  if (app_->endpoint()->node_id() == node_id) {
+    app_->ConnectToLocalService(intro, std::move(channel));
+  } else {
+    app_->endpoint()->SendIntro(
+        node_id, overnet::ReliabilityAndOrdering::ReliableOrdered,
+        std::move(intro),
+        overnet::StatusOrCallback<overnet::RouterEndpoint::NewStream>(
+            overnet::ALLOCATED_CALLBACK,
+            [this, channel = std::move(channel)](
+                overnet::StatusOr<overnet::RouterEndpoint::NewStream>
+                    ns) mutable {
+              if (ns.is_error()) {
+                OVERNET_TRACE(ERROR)
+                    << "ConnectToService failed: " << ns.AsStatus();
+              } else {
+                app_->BindStream(std::move(*ns), std::move(channel));
+              }
+            }));
+  }
+}
+
+}  // namespace overnetstack
\ No newline at end of file
diff --git a/bin/overnet/overnetstack/service.h b/bin/overnet/overnetstack/service.h
new file mode 100644
index 0000000..3c71907
--- /dev/null
+++ b/bin/overnet/overnetstack/service.h
@@ -0,0 +1,34 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <fuchsia/overnet/cpp/fidl.h>
+#include "overnet_app.h"
+
+namespace overnetstack {
+
+class Service final : public fuchsia::overnet::Overnet,
+                      public OvernetApp::Actor {
+ public:
+  Service(OvernetApp* app);
+
+  overnet::Status Start() override;
+
+  //////////////////////////////////////////////////////////////////////////////////////////////////
+  // Method implementations
+
+  void ListPeers(ListPeersCallback callback) override;
+  void RegisterService(fidl::StringPtr service_name,
+                       fidl::InterfaceHandle<fuchsia::overnet::ServiceProvider>
+                           provider) override;
+  void ConnectToService(uint64_t node, fidl::StringPtr service_name,
+                        zx::channel channel) override;
+
+ private:
+  OvernetApp* const app_;
+  fidl::BindingSet<fuchsia::overnet::Overnet> bindings_;
+};
+
+}  // namespace overnetstack
diff --git a/bin/overnet/overnetstack/udp_nub.h b/bin/overnet/overnetstack/udp_nub.h
index 819933c..176c0a7 100644
--- a/bin/overnet/overnetstack/udp_nub.h
+++ b/bin/overnet/overnetstack/udp_nub.h
@@ -8,9 +8,10 @@
 #include <lib/async-loop/cpp/loop.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
-#include "garnet/lib/overnet/packet_nub.h"
+#include "garnet/lib/overnet/links/packet_nub.h"
 #include "lib/fsl/tasks/fd_waiter.h"
 #include "lib/fxl/files/unique_fd.h"
+#include "overnet_app.h"
 
 namespace overnetstack {
 
@@ -80,16 +81,14 @@
 
 using UdpNubBase = overnet::PacketNub<UdpAddr, 1500, HashUdpAddr, EqUdpAddr>;
 
-class UdpNub final : public UdpNubBase {
+class UdpNub final : public UdpNubBase, public OvernetApp::Actor {
  public:
-  explicit UdpNub(overnet::RouterEndpoint* endpoint,
-                  overnet::TraceSink trace_sink)
-      : UdpNubBase(endpoint->router()->timer(), trace_sink,
-                   endpoint->node_id()),
-        endpoint_(endpoint),
-        timer_(endpoint->router()->timer()) {}
+  explicit UdpNub(OvernetApp* app)
+      : UdpNubBase(app->timer(), app->node_id()),
+        endpoint_(app->endpoint()),
+        timer_(app->timer()) {}
 
-  overnet::Status Start() {
+  overnet::Status Start() override {
     return CreateFD()
         .Then([this]() { return SetOptionSharePort(); })
         .Then([this]() { return SetOptionReceiveAnything(); })
@@ -118,24 +117,24 @@
       memcpy(addr6_addr_bytes + 12, &addr.ipv4.sin_addr, 4);
       addr = addr6;
     }
-    std::cout << "sending packet length " << slice.length() << " to " << addr
-              << "\n";
+    OVERNET_TRACE(DEBUG) << "sending packet length " << slice.length() << " to "
+                         << addr;
     int r = sendto(socket_fd_.get(), slice.begin(), slice.length(), 0,
                    &addr.addr, sizeof(addr));
     if (r == -1) {
       auto got_errno = errno;
-      std::cout << "sendto sets errno " << got_errno << "\n";
+      OVERNET_TRACE(WARNING) << "sendto sets errno " << got_errno;
     }
     assert(static_cast<size_t>(r) == slice.length());
   }
 
-  overnet::Router* GetRouter() override { return endpoint_->router(); }
+  overnet::Router* GetRouter() override { return endpoint_; }
 
   void Publish(overnet::LinkPtr<> link) override {
     overnet::NodeId node = link->GetLinkMetrics().to();
-    std::cout << "NewLink: " << node << "\n";
+    OVERNET_TRACE(DEBUG) << "NewLink: " << node << "\n";
     endpoint_->RegisterPeer(node);
-    endpoint_->router()->RegisterLink(std::move(link));
+    endpoint_->RegisterLink(std::move(link));
   }
 
  private:
@@ -150,15 +149,15 @@
     UdpAddr whoami;
     socklen_t whoami_len = sizeof(whoami.addr);
     if (getsockname(socket_fd_.get(), &whoami.addr, &whoami_len) < 0) {
-      std::cerr << StatusFromErrno("getsockname") << "\n";
+      OVERNET_TRACE(DEBUG) << StatusFromErrno("getsockname") << "\n";
     }
-    std::cerr << "WaitForInbound on " << whoami << "\n";
+    OVERNET_TRACE(DEBUG) << "WaitForInbound on " << whoami << "\n";
     if (!fd_waiter_.Wait(
             [this](zx_status_t status, uint32_t events) {
               InboundReady(status, events);
             },
             socket_fd_.get(), POLLIN)) {
-      std::cerr << "fd_waiter_.Wait() failed\n";
+      OVERNET_TRACE(DEBUG) << "fd_waiter_.Wait() failed\n";
     }
   }
 
@@ -172,7 +171,7 @@
         socket_fd_.get(), const_cast<uint8_t*>(inbound.begin()),
         inbound.length(), 0, &source_address.addr, &source_address_length);
     if (result < 0) {
-      FXL_LOG(ERROR) << "Failed to recvfrom, errno " << errno;
+      OVERNET_TRACE(ERROR) << "Failed to recvfrom, errno " << errno;
       // Wait a bit before trying again to avoid spamming the log.
       async::PostDelayedTask(async_get_default_dispatcher(),
                              [this]() { WaitForInbound(); }, zx::sec(10));
@@ -181,7 +180,9 @@
 
     inbound.TrimEnd(inbound.length() - result);
     assert(inbound.length() == (size_t)result);
-    std::cerr << "Got packet length " << result << "\n";
+    overnet::ScopedOp scoped_op(
+        overnet::Op::New(overnet::OpType::INCOMING_PACKET));
+    OVERNET_TRACE(DEBUG) << "Got packet length " << result;
     Process(now, source_address, std::move(inbound));
 
     WaitForInbound();
diff --git a/examples/overnet/echo/client/BUILD.gn b/examples/overnet/echo/client/BUILD.gn
new file mode 100644
index 0000000..879c7da
--- /dev/null
+++ b/examples/overnet/echo/client/BUILD.gn
@@ -0,0 +1,40 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/rust/rustc_binary.gni")
+import("//build/package.gni")
+
+rustc_binary("bin") {
+  name = "echo_client"
+  edition = "2018"
+
+  deps = [
+    "//garnet/examples/fidl/services:echo2-rustc",
+    "//garnet/public/fidl/fuchsia.overnet:fuchsia.overnet-rustc",
+    "//garnet/public/fidl/fuchsia.overnet:fuchsia.overnet-rustc",
+    "//garnet/public/lib/fidl/rust/fidl",
+    "//garnet/public/rust/fdio",
+    "//garnet/public/rust/fuchsia-app",
+    "//garnet/public/rust/fuchsia-async",
+    "//garnet/public/rust/fuchsia-zircon",
+    "//third_party/rust-crates/rustc_deps:clap",
+    "//third_party/rust-crates/rustc_deps:failure",
+    "//third_party/rust-crates/rustc_deps:futures-preview",
+  ]
+}
+
+package("overnet-echo-client") {
+  deps = [
+    ":bin",
+  ]
+
+  binary = "rust_crates/echo_client"
+
+  meta = [
+    {
+      path = rebase_path("../../meta/overnet_example.cmx")
+      dest = "echo_client.cmx"
+    },
+  ]
+}
diff --git a/examples/overnet/echo/client/src/main.rs b/examples/overnet/echo/client/src/main.rs
new file mode 100644
index 0000000..7259745
--- /dev/null
+++ b/examples/overnet/echo/client/src/main.rs
@@ -0,0 +1,72 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![deny(warnings)]
+#![feature(
+    async_await,
+    await_macro,
+    futures_api,
+    pin,
+    arbitrary_self_types
+)]
+
+use {clap::{App, Arg},
+     failure::{Error, ResultExt},
+     fidl::endpoints::ServiceMarker,
+     fidl_fidl_examples_echo as echo,
+     fidl_fuchsia_overnet::{OvernetMarker, OvernetProxy},
+     fuchsia_async as fasync, fuchsia_zircon as zx};
+
+fn app<'a, 'b>() -> App<'a, 'b> {
+    App::new("echo-client")
+        .version("0.1.0")
+        .about("Echo client example for overnet")
+        .author("Fuchsia Team")
+        .arg(
+            Arg::with_name("text")
+                .help("Text string to echo back and forth")
+                .takes_value(true),
+        )
+}
+
+async fn exec(svc: OvernetProxy, text: Option<&str>) -> Result<(), Error> {
+    loop {
+        let peers = await!(svc.list_peers())?;
+        for peer in peers {
+            let (s, p) = zx::Channel::create().context("failed to create zx channel")?;
+            if let Err(e) =
+                svc.connect_to_service(peer.id, echo::EchoMarker::NAME, s)
+            {
+                println!("{:?}", e);
+                continue;
+            }
+            let proxy = fasync::Channel::from_channel(p).context("failed to make async channel")?;
+            let cli = echo::EchoProxy::new(proxy);
+            println!("Sending {:?} to {}", text, peer.id);
+            match await!(cli.echo_string(text)) {
+                Ok(r) => {
+                    println!("SUCCESS: received {:?}", r);
+                    return Ok(());
+                }
+                Err(e) => {
+                    println!("ERROR: {:?}", e);
+                    continue;
+                }
+            };
+        }
+    }
+}
+
+fn main() -> Result<(), Error> {
+    let args = app().get_matches();
+
+    let mut executor = fasync::Executor::new().context("error creating event loop")?;
+    let svc = fuchsia_app::client::connect_to_service::<OvernetMarker>()
+        .context("Failed to connect to overnet service")?;
+
+    let text = args.value_of("text");
+    executor
+        .run_singlethreaded(exec(svc, text))
+        .map_err(Into::into)
+}
diff --git a/examples/overnet/echo/server/BUILD.gn b/examples/overnet/echo/server/BUILD.gn
new file mode 100644
index 0000000..dab50bb
--- /dev/null
+++ b/examples/overnet/echo/server/BUILD.gn
@@ -0,0 +1,39 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/rust/rustc_binary.gni")
+import("//build/package.gni")
+
+rustc_binary("bin") {
+  name = "echo_server"
+  edition = "2018"
+
+  deps = [
+    "//garnet/examples/fidl/services:echo2-rustc",
+    "//garnet/public/fidl/fuchsia.overnet:fuchsia.overnet-rustc",
+    "//garnet/public/lib/fidl/rust/fidl",
+    "//garnet/public/rust/fdio",
+    "//garnet/public/rust/fuchsia-app",
+    "//garnet/public/rust/fuchsia-async",
+    "//garnet/public/rust/fuchsia-zircon",
+    "//third_party/rust-crates/rustc_deps:clap",
+    "//third_party/rust-crates/rustc_deps:failure",
+    "//third_party/rust-crates/rustc_deps:futures-preview",
+  ]
+}
+
+package("overnet-echo-server") {
+  deps = [
+    ":bin",
+  ]
+
+  binary = "rust_crates/echo_server"
+
+  meta = [
+    {
+      path = rebase_path("../../meta/overnet_example.cmx")
+      dest = "echo_server.cmx"
+    },
+  ]
+}
diff --git a/examples/overnet/echo/server/src/main.rs b/examples/overnet/echo/server/src/main.rs
new file mode 100644
index 0000000..c5d0cd0
--- /dev/null
+++ b/examples/overnet/echo/server/src/main.rs
@@ -0,0 +1,89 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![deny(warnings)]
+#![feature(
+    async_await,
+    await_macro,
+    futures_api,
+    pin,
+    arbitrary_self_types
+)]
+
+use {clap::{App, Arg},
+     failure::{Error, ResultExt},
+     fidl::endpoints::{ClientEnd, RequestStream, ServiceMarker},
+     fidl_fidl_examples_echo::{EchoMarker, EchoRequest, EchoRequestStream},
+     fidl_fuchsia_overnet::{OvernetMarker, OvernetProxy, ServiceProviderRequest,
+                            ServiceProviderRequestStream},
+     fuchsia_async as fasync, fuchsia_zircon as zx,
+     futures::prelude::*};
+
+fn app<'a, 'b>() -> App<'a, 'b> {
+    App::new("echo-server")
+        .version("0.1.0")
+        .about("Echo server example for overnet")
+        .author("Fuchsia Team")
+        .arg(Arg::with_name("quiet").help("Should output be quiet"))
+}
+
+fn spawn_echo_server(chan: fasync::Channel, quiet: bool) {
+    fasync::spawn(
+        async move {
+            let mut stream = EchoRequestStream::from_channel(chan);
+            while let Some(EchoRequest::EchoString { value, responder }) =
+                await!(stream.try_next()).context("error running echo server")?
+            {
+                if !quiet {
+                    println!("Received echo request for string {:?}", value);
+                }
+                responder
+                    .send(value.as_ref().map(|s| &**s))
+                    .context("error sending response")?;
+                if !quiet {
+                    println!("echo response sent successfully");
+                }
+            }
+            Ok(())
+        }
+            .unwrap_or_else(|e: failure::Error| eprintln!("{:?}", e)),
+    );
+}
+
+async fn next_request(stream: &mut ServiceProviderRequestStream) -> Result<Option<ServiceProviderRequest>, Error> {
+    println!("Awaiting request");
+    Ok(await!(stream.try_next()).context("error running service provider server")?)
+}
+
+async fn exec(svc: OvernetProxy, quiet: bool) -> Result<(), Error> {
+    let (s, p) = zx::Channel::create().context("failed to create zx channel")?;
+    let chan = fasync::Channel::from_channel(s).context("failed to make async channel")?;
+    let mut stream = ServiceProviderRequestStream::from_channel(chan);
+    svc.register_service(EchoMarker::NAME, ClientEnd::new(p))?;
+    while let Some(ServiceProviderRequest::ConnectToService {
+        service_name,
+        chan,
+        control_handle: _control_handle,
+    }) = await!(next_request(&mut stream))?
+    {
+        if !quiet {
+            println!("Received service request for service {:?}", service_name);
+        }
+        let chan = fasync::Channel::from_channel(chan).context("failed to make async channel")?;
+        spawn_echo_server(chan, quiet);
+    }
+    Ok(())
+}
+
+fn main() -> Result<(), Error> {
+    let args = app().get_matches();
+
+    let mut executor = fasync::Executor::new().context("error creating event loop")?;
+    let svc = fuchsia_app::client::connect_to_service::<OvernetMarker>()
+        .context("Failed to connect to overnet service")?;
+
+    executor
+        .run_singlethreaded(exec(svc, args.is_present("quiet")))
+        .map_err(Into::into)
+}
diff --git a/examples/overnet/interface_passing/client/BUILD.gn b/examples/overnet/interface_passing/client/BUILD.gn
new file mode 100644
index 0000000..3e249af
--- /dev/null
+++ b/examples/overnet/interface_passing/client/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/rust/rustc_binary.gni")
+import("//build/package.gni")
+
+rustc_binary("bin") {
+  name = "interface_passing_client"
+  edition = "2018"
+
+  deps = [
+    "//garnet/examples/fidl/services:echo2-rustc",
+    "//garnet/examples/overnet/interface_passing/service:interface_passing-rustc",
+    "//garnet/public/fidl/fuchsia.overnet:fuchsia.overnet-rustc",
+    "//garnet/public/fidl/fuchsia.overnet:fuchsia.overnet-rustc",
+    "//garnet/public/lib/fidl/rust/fidl",
+    "//garnet/public/rust/fdio",
+    "//garnet/public/rust/fuchsia-app",
+    "//garnet/public/rust/fuchsia-async",
+    "//garnet/public/rust/fuchsia-zircon",
+    "//third_party/rust-crates/rustc_deps:clap",
+    "//third_party/rust-crates/rustc_deps:failure",
+    "//third_party/rust-crates/rustc_deps:futures-preview",
+  ]
+}
+
+package("overnet-interface-passing-client") {
+  deps = [
+    ":bin",
+  ]
+
+  binary = "rust_crates/interface_passing_client"
+
+  meta = [
+    {
+      path = rebase_path("../../meta/overnet_example.cmx")
+      dest = "interface_passing_client.cmx"
+    },
+  ]
+}
diff --git a/examples/overnet/interface_passing/client/src/main.rs b/examples/overnet/interface_passing/client/src/main.rs
new file mode 100644
index 0000000..20d9f3a
--- /dev/null
+++ b/examples/overnet/interface_passing/client/src/main.rs
@@ -0,0 +1,73 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![deny(warnings)]
+#![feature(
+    async_await,
+    await_macro,
+    futures_api,
+    pin,
+    arbitrary_self_types
+)]
+
+use {clap::{App, Arg},
+     failure::{Error, ResultExt},
+     fidl::endpoints::{ServerEnd, ServiceMarker},
+     fidl_fidl_examples_echo as echo,
+     fidl_fuchsia_overnet_examples_interfacepassing as interfacepassing,
+     fidl_fuchsia_overnet::{OvernetMarker, OvernetProxy},
+     fuchsia_async as fasync, fuchsia_zircon as zx};
+
+fn app<'a, 'b>() -> App<'a, 'b> {
+    App::new("echo-client")
+        .version("0.1.0")
+        .about("Echo client example for overnet")
+        .author("Fuchsia Team")
+        .arg(
+            Arg::with_name("text")
+                .help("Text string to echo back and forth")
+                .takes_value(true),
+        )
+}
+
+async fn exec(svc: OvernetProxy, text: Option<&str>) -> Result<(), Error> {
+    loop {
+        let peers = await!(svc.list_peers())?;
+        for peer in peers {
+            let (s, p) = zx::Channel::create().context("failed to create zx channel")?;
+            if let Err(e) =
+                svc.connect_to_service(peer.id, interfacepassing::ExampleMarker::NAME, s)
+            {
+                println!("{:?}", e);
+                continue;
+            }
+            let proxy = fasync::Channel::from_channel(p).context("failed to make async channel")?;
+            let cli = interfacepassing::ExampleProxy::new(proxy);
+
+            let (s1, p1) = zx::Channel::create().context("failed to create zx channel")?;
+            let proxy_echo = fasync::Channel::from_channel(p1).context("failed to make async channel")?;
+            let cli_echo = echo::EchoProxy::new(proxy_echo);
+            println!("Sending {:?} to {}", text, peer.id);
+            if let Err(e) = cli.request(ServerEnd::new(s1)) {
+                println!("ERROR REQUESTING INTERFACE: {:?}", e);
+                continue;
+            }
+            println!("received {:?}", await!(cli_echo.echo_string(text))?);
+            return Ok(());
+        }
+    }
+}
+
+fn main() -> Result<(), Error> {
+    let args = app().get_matches();
+
+    let mut executor = fasync::Executor::new().context("error creating event loop")?;
+    let svc = fuchsia_app::client::connect_to_service::<OvernetMarker>()
+        .context("Failed to connect to overnet service")?;
+
+    let text = args.value_of("text");
+    executor
+        .run_singlethreaded(exec(svc, text))
+        .map_err(Into::into)
+}
diff --git a/examples/overnet/interface_passing/server/BUILD.gn b/examples/overnet/interface_passing/server/BUILD.gn
new file mode 100644
index 0000000..b9dfe1b
--- /dev/null
+++ b/examples/overnet/interface_passing/server/BUILD.gn
@@ -0,0 +1,40 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/rust/rustc_binary.gni")
+import("//build/package.gni")
+
+rustc_binary("bin") {
+  name = "interface_passing_server"
+  edition = "2018"
+
+  deps = [
+    "//garnet/examples/fidl/services:echo2-rustc",
+    "//garnet/examples/overnet/interface_passing/service:interface_passing-rustc",
+    "//garnet/public/fidl/fuchsia.overnet:fuchsia.overnet-rustc",
+    "//garnet/public/lib/fidl/rust/fidl",
+    "//garnet/public/rust/fdio",
+    "//garnet/public/rust/fuchsia-app",
+    "//garnet/public/rust/fuchsia-async",
+    "//garnet/public/rust/fuchsia-zircon",
+    "//third_party/rust-crates/rustc_deps:clap",
+    "//third_party/rust-crates/rustc_deps:failure",
+    "//third_party/rust-crates/rustc_deps:futures-preview",
+  ]
+}
+
+package("overnet-interface-passing-server") {
+  deps = [
+    ":bin",
+  ]
+
+  binary = "rust_crates/interface_passing_server"
+
+  meta = [
+    {
+      path = rebase_path("../../meta/overnet_example.cmx")
+      dest = "interface_passing_server.cmx"
+    },
+  ]
+}
diff --git a/examples/overnet/interface_passing/server/src/main.rs b/examples/overnet/interface_passing/server/src/main.rs
new file mode 100644
index 0000000..f9ba0f8
--- /dev/null
+++ b/examples/overnet/interface_passing/server/src/main.rs
@@ -0,0 +1,112 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![deny(warnings)]
+#![feature(
+    async_await,
+    await_macro,
+    futures_api,
+    pin,
+    arbitrary_self_types
+)]
+
+use {clap::{App, Arg},
+     failure::{Error, ResultExt},
+     fidl::endpoints::{ClientEnd, RequestStream, ServiceMarker, ServerEnd},
+     fidl_fidl_examples_echo::{EchoMarker, EchoRequest},
+     fidl_fuchsia_overnet_examples_interfacepassing::{ExampleMarker, ExampleRequest, ExampleRequestStream},
+     fidl_fuchsia_overnet::{OvernetMarker, OvernetProxy, ServiceProviderRequest,
+                            ServiceProviderRequestStream},
+     fuchsia_async as fasync, fuchsia_zircon as zx,
+     futures::prelude::*};
+
+fn app<'a, 'b>() -> App<'a, 'b> {
+    App::new("echo-server")
+        .version("0.1.0")
+        .about("Echo server example for overnet")
+        .author("Fuchsia Team")
+        .arg(Arg::with_name("quiet").help("Should output be quiet"))
+}
+
+fn spawn_echo_server(chan: ServerEnd<EchoMarker>, quiet: bool) {
+    fasync::spawn(
+        async move {
+            let mut stream = chan.into_stream()?;
+            while let Some(EchoRequest::EchoString { value, responder }) =
+                await!(stream.try_next()).context("error running echo server")?
+            {
+                if !quiet {
+                    println!("Received echo request for string {:?}", value);
+                }
+                responder
+                    .send(value.as_ref().map(|s| &**s))
+                    .context("error sending response")?;
+                if !quiet {
+                    println!("echo response sent successfully");
+                }
+            }
+            Ok(())
+        }
+            .unwrap_or_else(|e: failure::Error| eprintln!("{:?}", e)),
+    );
+}
+
+fn spawn_example_server(chan: fasync::Channel, quiet: bool) {
+    fasync::spawn(
+        async move {
+            let mut stream = ExampleRequestStream::from_channel(chan);
+            while let Some(request) =
+                await!(stream.try_next()).context("error running echo server")?
+            {
+                match request {
+                    ExampleRequest::Request { iface, .. } => {
+                        if !quiet {
+                            println!("Received interface request");
+                        }
+                        spawn_echo_server(iface, quiet);
+                    }
+                }
+            }
+            Ok(())
+        }
+            .unwrap_or_else(|e: failure::Error| eprintln!("{:?}", e)),
+    );
+}
+
+async fn next_request(stream: &mut ServiceProviderRequestStream) -> Result<Option<ServiceProviderRequest>, Error> {
+    println!("Awaiting request");
+    Ok(await!(stream.try_next()).context("error running service provider server")?)
+}
+
+async fn exec(svc: OvernetProxy, quiet: bool) -> Result<(), Error> {
+    let (s, p) = zx::Channel::create().context("failed to create zx channel")?;
+    let chan = fasync::Channel::from_channel(s).context("failed to make async channel")?;
+    let mut stream = ServiceProviderRequestStream::from_channel(chan);
+    svc.register_service(ExampleMarker::NAME, ClientEnd::new(p))?;
+    while let Some(ServiceProviderRequest::ConnectToService {
+        service_name,
+        chan,
+        control_handle: _control_handle,
+    }) = await!(next_request(&mut stream))?
+    {
+        if !quiet {
+            println!("Received service request for service {:?}", service_name);
+        }
+        let chan = fasync::Channel::from_channel(chan).context("failed to make async channel")?;
+        spawn_example_server(chan, quiet);
+    }
+    Ok(())
+}
+
+fn main() -> Result<(), Error> {
+    let args = app().get_matches();
+
+    let mut executor = fasync::Executor::new().context("error creating event loop")?;
+    let svc = fuchsia_app::client::connect_to_service::<OvernetMarker>()
+        .context("Failed to connect to overnet service")?;
+
+    executor
+        .run_singlethreaded(exec(svc, args.is_present("quiet")))
+        .map_err(Into::into)
+}
diff --git a/examples/overnet/interface_passing/service/BUILD.gn b/examples/overnet/interface_passing/service/BUILD.gn
new file mode 100644
index 0000000..fdf10b7
--- /dev/null
+++ b/examples/overnet/interface_passing/service/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/fidl/fidl.gni")
+
+fidl("interface_passing") {
+  name = "fuchsia.overnet.examples.interfacepassing"
+
+  sources = [
+    "interface_passing.fidl",
+  ]
+
+  deps = [
+    "//garnet/examples/fidl/services:echo2"
+  ]
+}
diff --git a/examples/overnet/interface_passing/service/interface_passing.fidl b/examples/overnet/interface_passing/service/interface_passing.fidl
new file mode 100644
index 0000000..0706ef1
--- /dev/null
+++ b/examples/overnet/interface_passing/service/interface_passing.fidl
@@ -0,0 +1,13 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library fuchsia.overnet.examples.interfacepassing;
+
+using fidl.examples.echo;
+
+[Discoverable]
+interface Example {
+  1: Request(request<fidl.examples.echo.Echo> iface);
+//  2: Push(fidl.examples.echo.Echo iface) -> ();
+};
diff --git a/examples/overnet/meta/overnet_example.cmx b/examples/overnet/meta/overnet_example.cmx
new file mode 100644
index 0000000..0aa4dfc
--- /dev/null
+++ b/examples/overnet/meta/overnet_example.cmx
@@ -0,0 +1,8 @@
+{
+   "program": {
+       "binary": "bin/app"
+   },
+   "sandbox": {
+       "services": [ "fuchsia.overnet.Overnet" ]
+   }
+}
diff --git a/lib/overnet/BUILD.gn b/lib/overnet/BUILD.gn
index b82f625..b77003e 100644
--- a/lib/overnet/BUILD.gn
+++ b/lib/overnet/BUILD.gn
@@ -2,78 +2,38 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/fuzzing/fuzzer.gni")
 import("//build/package.gni")
 
 source_set("overnet") {
-  sources = [
-    "ack_frame.h",
-    "ack_frame.cc",
-    "bandwidth.h",
-    "bbr.h",
-    "bbr.cc",
-    "callback.h",
-    "closed_ptr.h",
-    "datagram_stream.h",
-    "datagram_stream.cc",
-    "fork_frame.h",
-    "fork_frame.cc",
-    "lazy_slice.h",
-    "linearizer.h",
-    "linearizer.cc",
-    "internal_list.h",
-    "manual_constructor.h",
-    "node_id.h",
-    "node_id.cc",
-    "once_fn.h",
-    "optional.h",
-    "packet_link.h",
-    "packet_link.cc",
-    "packet_nub.h",
-    "packet_protocol.h",
-    "packet_protocol.cc",
-    "receive_mode.h",
-    "receive_mode.cc",
-    "reliability_and_ordering.h",
-    "reliability_and_ordering.cc",
-    "routable_message.h",
-    "routable_message.cc",
-    "router.h",
-    "router.cc",
-    "router_endpoint.h",
-    "router_endpoint.cc",
-    "routing_table.h",
-    "routing_table.cc",
-    "seq_num.h",
-    "seq_num.cc",
-    "serialization_helpers.h",
-    "sink.h",
-    "slice.h",
-    "slice.cc",
-    "status.h",
-    "status.cc",
-    "stream_id.h",
-    "stream_id.cc",
-    "timer.h",
-    "timer.cc",
-    "trace.h",
-    "varint.h",
-    "varint.cc",
-    "windowed_filter.h",
+  public_deps = [
+    "datagram_stream:lib",
+    "endpoint:lib",
+    "environment:lib",
+    "labels:lib",
+    "links:lib",
+    "packet_protocol:lib",
+    "protocol:lib",
+    "routing:lib",
+    "vocabulary:lib",
   ]
 }
 
-source_set("test_util") {
+executable("overnet_unittests") {
   testonly = true
 
-  sources = [
-    "csv_writer.h",
-    "test_timer.h",
-    "test_timer.cc",
-    "trace_cout.h",
-  ]
-
   deps = [
-    ":overnet",
+    "datagram_stream:tests",
+    "endpoint:tests",
+    "environment:tests",
+    "labels:tests",
+    "links:tests",
+    "packet_protocol:tests",
+    "protocol:tests",
+    "routing:tests",
+    "testing:run_all_tests",
+    "testing:tests",
+    "vocabulary:tests",
   ]
 }
 
@@ -84,49 +44,6 @@
   testonly = true
 }
 
-executable("overnet_unittests") {
-  testonly = true
-
-  sources = [
-    "run_all_tests.cc",
-    "ack_frame_test.cc",
-    "bbr_test.cc",
-    "callback_test.cc",
-    "datagram_stream_test.cc",
-    "fork_frame_test.cc",
-    "internal_list_fuzzer_helpers.h",
-    "internal_list_test.cc",
-    "linearizer_fuzzer_helpers.h",
-    "linearizer_test.cc",
-    "node_id_test.cc",
-    "once_fn_test.cc",
-    "optional_test.cc",
-    "packet_link_test.cc",
-    "packet_nub_test.cc",
-    "packet_protocol_fuzzer_helpers.h",
-    "packet_protocol_test.cc",
-    "receive_mode_fuzzer_helpers.h",
-    "receive_mode_test.cc",
-    "routable_message_test.cc",
-    "router_test.cc",
-    "router_endpoint_2node_test.cc",
-    "seq_num_test.cc",
-    "sink_test.cc",
-    "slice_test.cc",
-    "status_test.cc",
-    "test_timer_test.cc",
-    "trace_test.cc",
-    "varint_test.cc",
-  ]
-
-  deps = [
-    ":overnet",
-    ":test_util",
-    "//third_party/googletest:gtest",
-    "//third_party/googletest:gmock",
-  ]
-}
-
 package("overnet_tests") {
   testonly = true
 
@@ -136,7 +53,13 @@
 
   tests = [
     {
-       name = "overnet_unittests"
-    }
+      name = "overnet_unittests"
+    },
   ]
 }
+
+fuzz_package("overnet_fuzzers") {
+  targets = [ "packet_protocol:packet_protocol_fuzzer" ]
+  sanitizers = [ "asan" ]
+  fuzz_host = true
+}
diff --git a/lib/overnet/datagram_stream.cc b/lib/overnet/datagram_stream.cc
deleted file mode 100644
index 46c59d3..0000000
--- a/lib/overnet/datagram_stream.cc
+++ /dev/null
@@ -1,591 +0,0 @@
-// Copyright 2018 The Fuchsia 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 "datagram_stream.h"
-#include <sstream>
-
-namespace overnet {
-
-////////////////////////////////////////////////////////////////////////////////
-// MessageFragment
-
-Slice MessageFragment::Write(uint64_t desired_prefix) const {
-  const auto message_length = varint::WireSizeFor(message_);
-  uint8_t flags = static_cast<uint8_t>(type_);
-  assert((flags & kFlagTypeMask) == flags);
-  switch (type_) {
-    case Type::Chunk: {
-      const Chunk& chunk = this->chunk();
-      if (chunk.end_of_message) {
-        flags |= kFlagEndOfMessage;
-      }
-      const auto chunk_offset_length = varint::WireSizeFor(chunk.offset);
-      return chunk.slice.WithPrefix(
-          message_length + chunk_offset_length + 1, [&](uint8_t* const bytes) {
-            uint8_t* p = bytes;
-            *p++ = flags;
-            p = varint::Write(message_, message_length, p);
-            p = varint::Write(chunk.offset, chunk_offset_length, p);
-            assert(p == bytes + message_length + chunk_offset_length + 1);
-          });
-    }
-    case Type::MessageAbort:
-    case Type::StreamEnd: {
-      const Status& status = this->status();
-      const auto& reason = status.reason();
-      const auto reason_length_length = varint::WireSizeFor(reason.length());
-      const auto frame_length =
-          1 + message_length + 1 + reason_length_length + reason.length();
-      return Slice::WithInitializerAndPrefix(
-          frame_length, desired_prefix, [&](uint8_t* bytes) {
-            uint8_t* p = bytes;
-            *p++ = flags;
-            p = varint::Write(message_, message_length, p);
-            *p++ = static_cast<uint8_t>(status.code());
-            p = varint::Write(reason.length(), reason_length_length, p);
-            assert(p + reason.length() == bytes + frame_length);
-            memcpy(p, reason.data(), reason.length());
-          });
-    }
-  }
-  abort();
-}
-
-StatusOr<MessageFragment> MessageFragment::Parse(Slice slice) {
-  const uint8_t* p = slice.begin();
-  const uint8_t* const end = slice.end();
-  uint64_t message;
-  uint64_t chunk_offset;
-  if (p == end) {
-    return StatusOr<MessageFragment>(
-        StatusCode::INVALID_ARGUMENT,
-        "Failed to read flags from message fragment");
-  }
-  const uint8_t flags = *p++;
-  if (flags & kReservedFlags) {
-    return StatusOr<MessageFragment>(
-        StatusCode::INVALID_ARGUMENT,
-        "Reserved flags set on message fragment flags field");
-  }
-  if (!varint::Read(&p, end, &message)) {
-    return StatusOr<MessageFragment>(
-        StatusCode::INVALID_ARGUMENT,
-        "Failed to read message id from message fragment");
-  }
-  if (message == 0) {
-    return StatusOr<MessageFragment>(StatusCode::INVALID_ARGUMENT,
-                                     "Message id 0 is invalid");
-  }
-  const Type type = static_cast<Type>(flags & kFlagTypeMask);
-  switch (type) {
-    case Type::Chunk:
-      if (!varint::Read(&p, end, &chunk_offset)) {
-        return StatusOr<MessageFragment>(
-            StatusCode::INVALID_ARGUMENT,
-            "Failed to read chunk offset from message fragment");
-      }
-      return MessageFragment{
-          message, Chunk{chunk_offset, (flags & kFlagEndOfMessage) != 0,
-                         slice.FromPointer(p)}};
-    case Type::MessageAbort:
-    case Type::StreamEnd: {
-      if (p == end) {
-        return StatusOr<MessageFragment>(
-            StatusCode::INVALID_ARGUMENT,
-            "Failed to read status code from message fragment");
-      }
-      const uint8_t code = *p++;
-      uint64_t reason_length;
-      if (!varint::Read(&p, end, &reason_length)) {
-        return StatusOr<MessageFragment>(
-            StatusCode::INVALID_ARGUMENT,
-            "Failed to read status reason length from message fragment");
-      }
-      return MessageFragment(message, type,
-                             Status(static_cast<StatusCode>(code),
-                                    std::string(p, p + reason_length)));
-    } break;
-    default:
-      return StatusOr<MessageFragment>(StatusCode::INVALID_ARGUMENT,
-                                       "Unknown message fragment type");
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// DatagramStream proper
-
-DatagramStream::DatagramStream(Router* router, TraceSink trace_sink,
-                               NodeId peer,
-                               ReliabilityAndOrdering reliability_and_ordering,
-                               StreamId stream_id)
-    : timer_(router->timer()),
-      router_(router),
-      trace_sink_(trace_sink.Decorate([this, peer, stream_id](std::string msg) {
-        std::ostringstream out;
-        out << "DGS[" << this << ";peer=" << peer << ";stream=" << stream_id
-            << "] " << msg;
-        return out.str();
-      })),
-      peer_(peer),
-      stream_id_(stream_id),
-      reliability_and_ordering_(reliability_and_ordering),
-      receive_mode_(reliability_and_ordering),
-      // TODO(ctiller): What should mss be? Hardcoding to 65536 for now.
-      packet_protocol_(timer_, this, trace_sink_, 65536) {
-  if (router_->RegisterStream(peer_, stream_id_, this).is_error()) {
-    abort();
-  }
-}
-
-DatagramStream::~DatagramStream() {
-  assert(close_state_ == CloseState::CLOSED);
-}
-
-void DatagramStream::Close(const Status& status, Callback<void> quiesced) {
-  OVERNET_TRACE(DEBUG, trace_sink_) << "Close: state=" << close_state_;
-
-  switch (close_state_) {
-    case CloseState::CLOSED:
-      return;
-    case CloseState::CLOSING_PROTOCOL:
-    case CloseState::LOCAL_CLOSE_REQUESTED:
-      on_quiesced_.emplace_back(std::move(quiesced));
-      return;
-    case CloseState::REMOTE_CLOSED:
-      on_quiesced_.emplace_back(std::move(quiesced));
-      FinishClosing();
-      return;
-    case CloseState::OPEN:
-      on_quiesced_.emplace_back(std::move(quiesced));
-      receive_mode_.Close(status);
-      close_state_ = CloseState::LOCAL_CLOSE_REQUESTED;
-      SendCloseAndFlushQuiesced(status, 0);
-      return;
-  }
-}
-
-void DatagramStream::SendCloseAndFlushQuiesced(const Status& status,
-                                               int retry_number) {
-  assert(close_state_ == CloseState::LOCAL_CLOSE_REQUESTED);
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "SendClose: status=" << status << " retry=" << retry_number
-      << " state=" << close_state_;
-  packet_protocol_.Send(
-      [this, status](auto args) {
-        return MessageFragment::EndOfStream(next_message_id_, status)
-            .Write(args.desired_prefix);
-      },
-      [status, this, retry_number](const Status& send_status) mutable {
-        OVERNET_TRACE(DEBUG, trace_sink_)
-            << "SendClose ACK status=" << send_status
-            << " retry=" << retry_number;
-        switch (close_state_) {
-          case CloseState::OPEN:
-          case CloseState::REMOTE_CLOSED:
-            assert(false);
-            break;
-          case CloseState::CLOSED:
-          case CloseState::CLOSING_PROTOCOL:
-            break;
-          case CloseState::LOCAL_CLOSE_REQUESTED:
-            if (send_status.code() == StatusCode::UNAVAILABLE) {
-              SendCloseAndFlushQuiesced(status, retry_number + 1);
-            } else {
-              FinishClosing();
-            }
-            break;
-        }
-      });
-}
-
-void DatagramStream::FinishClosing() {
-  assert(close_state_ == CloseState::LOCAL_CLOSE_REQUESTED ||
-         close_state_ == CloseState::REMOTE_CLOSED);
-  close_state_ = CloseState::CLOSING_PROTOCOL;
-  packet_protocol_.Close([this]() {
-    close_state_ = CloseState::CLOSED;
-
-    auto unregister_status = router_->UnregisterStream(peer_, stream_id_, this);
-    assert(unregister_status.is_ok());
-
-    std::vector<Callback<void>> on_quiesced;
-    on_quiesced.swap(on_quiesced_);
-    on_quiesced.clear();
-  });
-}
-
-void DatagramStream::HandleMessage(SeqNum seq, TimeStamp received, Slice data) {
-  switch (close_state_) {
-    // In these states we process messages:
-    case CloseState::OPEN:
-    case CloseState::LOCAL_CLOSE_REQUESTED:
-    case CloseState::REMOTE_CLOSED:
-      break;
-    // In these states we do not:
-    case CloseState::CLOSING_PROTOCOL:
-    case CloseState::CLOSED:
-      return;
-  }
-
-  auto pkt_status = packet_protocol_.Process(received, seq, std::move(data));
-  if (pkt_status.status.is_error()) {
-    OVERNET_TRACE(WARNING, trace_sink_)
-        << "Failed to process packet: " << pkt_status.status.AsStatus();
-    return;
-  }
-  auto payload = std::move(pkt_status.status.value());
-  if (!payload || !payload->length()) {
-    return;
-  }
-  OVERNET_TRACE(DEBUG, trace_sink_) << "Process payload " << payload;
-  auto msg_status = MessageFragment::Parse(std::move(*payload));
-  if (msg_status.is_error()) {
-    OVERNET_TRACE(WARNING, trace_sink_)
-        << "Failed to parse message: " << msg_status.AsStatus();
-    return;
-  }
-  auto msg = std::move(*msg_status.get());
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "Payload type=" << static_cast<int>(msg.type())
-      << " msg=" << msg.message();
-  switch (msg.type()) {
-    case MessageFragment::Type::Chunk: {
-      OVERNET_TRACE(DEBUG, trace_sink_)
-          << "chunk offset=" << msg.chunk().offset
-          << " length=" << msg.chunk().slice.length()
-          << " end-of-message=" << msg.chunk().end_of_message;
-      // Got a chunk of data: add it to the relevant incoming message.
-      largest_incoming_message_id_seen_ =
-          std::max(largest_incoming_message_id_seen_, msg.message());
-      auto it = messages_.find(msg.message());
-      if (it == messages_.end()) {
-        it = messages_
-                 .emplace(std::piecewise_construct,
-                          std::forward_as_tuple(msg.message()),
-                          std::forward_as_tuple(trace_sink_))
-                 .first;
-        receive_mode_.Begin(msg.message(), [this, msg = std::move(msg)](
-                                               const Status& status) mutable {
-          if (status.is_error()) {
-            OVERNET_TRACE(WARNING, trace_sink_) << "Receive failed: " << status;
-            return;
-          }
-          auto it = messages_.find(msg.message());
-          if (it == messages_.end()) {
-            return;
-          }
-          it->second.Push(std::move(*msg.mutable_chunk()));
-          unclaimed_messages_.PushBack(&it->second);
-          MaybeContinueReceive();
-        });
-      } else {
-        it->second.Push(std::move(*msg.mutable_chunk()));
-      }
-    } break;
-    case MessageFragment::Type::MessageAbort: {
-      // Aborting a message: this is like a close to the incoming message.
-      largest_incoming_message_id_seen_ =
-          std::max(largest_incoming_message_id_seen_, msg.message());
-      auto it = messages_.find(msg.message());
-      if (it == messages_.end()) {
-        it = messages_
-                 .emplace(std::piecewise_construct,
-                          std::forward_as_tuple(msg.message()),
-                          std::forward_as_tuple(trace_sink_))
-                 .first;
-      }
-      it->second.Close(msg.status());
-    } break;
-    case MessageFragment::Type::StreamEnd:
-      // TODO(ctiller): handle case of ok termination with outstanding
-      // messages.
-      RequestedClose requested_close{msg.message(), msg.status()};
-      OVERNET_TRACE(DEBUG, trace_sink_)
-          << "peer requests close with status " << msg.status();
-      if (requested_close_.has_value()) {
-        if (*requested_close_ != requested_close) {
-          OVERNET_TRACE(WARNING, trace_sink_)
-              << "Non-duplicate last message id received: previously got "
-              << requested_close_->last_message_id << " with status "
-              << requested_close_->status << " now have " << msg.message()
-              << " with status " << msg.status();
-        }
-        return;
-      }
-      requested_close_ = requested_close;
-      auto enact_remote_close = [this](const Status& status) {
-        packet_protocol_.RequestSendAck();
-        switch (close_state_) {
-          case CloseState::OPEN:
-            close_state_ = CloseState::REMOTE_CLOSED;
-            receive_mode_.Close(status);
-            break;
-          case CloseState::REMOTE_CLOSED:
-            assert(false);
-            break;
-          case CloseState::LOCAL_CLOSE_REQUESTED:
-            FinishClosing();
-            break;
-          case CloseState::CLOSED:
-          case CloseState::CLOSING_PROTOCOL:
-            break;
-        }
-      };
-      if (requested_close_->status.is_error()) {
-        enact_remote_close(requested_close_->status);
-      } else {
-        receive_mode_.Begin(msg.message(), std::move(enact_remote_close));
-      }
-      break;
-  }
-}
-
-void DatagramStream::MaybeContinueReceive() {
-  if (unclaimed_messages_.Empty())
-    return;
-  if (unclaimed_receives_.Empty())
-    return;
-
-  auto incoming_message = unclaimed_messages_.PopFront();
-  auto receive_op = unclaimed_receives_.PopFront();
-
-  receive_op->incoming_message_ = incoming_message;
-  if (receive_op->pending_close_reason_) {
-    incoming_message->Close(*receive_op->pending_close_reason_);
-  } else if (!receive_op->pending_pull_.empty()) {
-    incoming_message->Pull(std::move(receive_op->pending_pull_));
-  } else if (!receive_op->pending_pull_all_.empty()) {
-    incoming_message->PullAll(std::move(receive_op->pending_pull_all_));
-  }
-}
-
-void DatagramStream::SendPacket(SeqNum seq, LazySlice data,
-                                Callback<void> done) {
-  router_->Forward(
-      Message{std::move(RoutableMessage(router_->node_id())
-                            .AddDestination(peer_, stream_id_, seq)),
-              std::move(data), timer_->Now()});
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// SendOp
-
-DatagramStream::SendOp::SendOp(DatagramStream* stream, uint64_t payload_length)
-    : stream_(stream),
-      trace_sink_(stream->trace_sink_.Decorate([this](const std::string& msg) {
-        std::ostringstream out;
-        out << "SendOp[" << this << "] " << msg;
-        return out.str();
-      })),
-      payload_length_(payload_length),
-      message_id_(stream_->next_message_id_++),
-      message_id_length_(varint::WireSizeFor(message_id_)) {}
-
-void DatagramStream::SendOp::SetClosed(const Status& status) {
-  if (state_ != State::OPEN) {
-    return;
-  }
-  if (status.is_ok() && payload_length_ != push_offset_) {
-    std::ostringstream out;
-    out << "Insufficient bytes for message presented: expected "
-        << payload_length_ << " but got " << push_offset_;
-    SetClosed(Status(StatusCode::INVALID_ARGUMENT, out.str()));
-    return;
-  }
-  OVERNET_TRACE(DEBUG, trace_sink_) << "SET CLOSED: " << status;
-  if (status.is_error()) {
-    state_ = State::CLOSED_WITH_ERROR;
-    SendError(status);
-  } else {
-    state_ = State::CLOSED_OK;
-  }
-}
-
-void DatagramStream::SendOp::SendError(const overnet::Status& status) {
-  stream_->packet_protocol_.Send(
-      [this, status](auto arg) {
-        return MessageFragment::Abort(message_id_, status)
-            .Write(arg.desired_prefix);
-      },
-      [self = OutstandingOp(this), status](const Status& send_status) {
-        if (send_status.code() == StatusCode::UNAVAILABLE) {
-          self->SendError(status);
-        }
-      });
-}
-
-void DatagramStream::SendOp::Close(const Status& status,
-                                   Callback<void> quiesced) {
-  SetClosed(status);
-  if (outstanding_ops_ == 0) {
-    quiesced();
-  } else {
-    quiesced_ = std::move(quiesced);
-  }
-}
-
-void DatagramStream::SendOp::Push(Slice item) {
-  assert(state_ == State::OPEN);
-  if (state_ != State::OPEN) {
-    return;
-  }
-  const auto chunk_start = push_offset_;
-  const auto chunk_length = item.length();
-  const auto end_byte = chunk_start + chunk_length;
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "Push: chunk_start=" << chunk_start << " chunk_length=" << chunk_length
-      << " end_byte=" << end_byte << " payload_length=" << payload_length_;
-  if (end_byte > payload_length_) {
-    SetClosed(Status(StatusCode::INVALID_ARGUMENT,
-                     "Exceeded message payload length"));
-    return;
-  }
-  push_offset_ += chunk_length;
-  Chunk chunk{chunk_start, end_byte == payload_length_, std::move(item)};
-  SendChunk(std::move(chunk));
-}
-
-void DatagramStream::SendOp::SendChunk(Chunk chunk) {
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "SendChunk: ofs=" << chunk.offset << " len=" << chunk.slice.length();
-  auto on_ack = MakeAckCallback(chunk);
-  stream_->packet_protocol_.Send(
-      [self = OutstandingOp(this),
-       chunk = std::move(chunk)](auto args) mutable {
-        OVERNET_TRACE(DEBUG, self->trace_sink_)
-            << "SendChunk::format: ofs=" << chunk.offset
-            << " len=" << chunk.slice.length()
-            << " desired_prefix=" << args.desired_prefix
-            << " max_length=" << args.max_length
-            << " message_id_length=" << (int)self->message_id_length_;
-        assert(args.max_length >
-               self->message_id_length_ + varint::WireSizeFor(chunk.offset));
-        uint64_t take_len = varint::MaximumLengthWithPrefix(
-            args.max_length - self->message_id_length_);
-        OVERNET_TRACE(DEBUG, self->trace_sink_) << "TAKE " << take_len;
-        if (take_len < chunk.slice.length()) {
-          Chunk first = chunk.TakeUntilSliceOffset(take_len);
-          self->SendChunk(std::move(chunk));
-          chunk = std::move(first);
-        }
-        return MessageFragment(self->message_id_, std::move(chunk))
-            .Write(args.desired_prefix);
-      },
-      std::move(on_ack));
-}
-
-PacketProtocol::SendCallback DatagramStream::SendOp::MakeAckCallback(
-    const Chunk& chunk) {
-  switch (stream_->reliability_and_ordering_) {
-    case ReliabilityAndOrdering::ReliableOrdered:
-    case ReliabilityAndOrdering::ReliableUnordered:
-      return [chunk, self = OutstandingOp(this)](const Status& status) mutable {
-        self->CompleteReliable(status, std::move(chunk));
-      };
-    case ReliabilityAndOrdering::UnreliableOrdered:
-    case ReliabilityAndOrdering::UnreliableUnordered:
-      return [self = OutstandingOp(this)](const Status& status) {
-        self->CompleteUnreliable(status);
-      };
-    case ReliabilityAndOrdering::TailReliable:
-      return [chunk, self = OutstandingOp(this)](const Status& status) mutable {
-        if (self->message_id_ + 1 == self->stream_->next_message_id_) {
-          self->CompleteReliable(status, std::move(chunk));
-        } else {
-          self->CompleteUnreliable(status);
-        }
-      };
-  }
-}
-
-void DatagramStream::SendOp::CompleteReliable(const Status& status,
-                                              Chunk chunk) {
-  if (state_ == State::CLOSED_WITH_ERROR) {
-    return;
-  }
-  if (status.code() == StatusCode::UNAVAILABLE) {
-    // Send failed, still open, and retryable: retry.
-    SendChunk(std::move(chunk));
-  }
-}
-
-void DatagramStream::SendOp::CompleteUnreliable(const Status& status) {
-  if (status.is_error()) {
-    SetClosed(status);
-  }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// ReceiveOp
-
-DatagramStream::ReceiveOp::ReceiveOp(DatagramStream* stream)
-    : trace_sink_(stream->trace_sink_.Decorate([this](const std::string& msg) {
-        std::ostringstream out;
-        out << "ReceiveOp[" << this << "] " << msg;
-        return out.str();
-      })) {
-  stream->unclaimed_receives_.PushBack(this);
-  stream->MaybeContinueReceive();
-}
-
-void DatagramStream::ReceiveOp::Pull(StatusOrCallback<Optional<Slice>> ready) {
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "Pull incoming_message=" << incoming_message_
-      << " pending_close_reason=" << pending_close_reason_;
-  if (incoming_message_ == nullptr) {
-    if (pending_close_reason_) {
-      ready(*pending_close_reason_);
-    } else {
-      assert(pending_pull_all_.empty());
-      pending_pull_ = std::move(ready);
-    }
-  } else {
-    incoming_message_->Pull(std::move(ready));
-  }
-}
-
-void DatagramStream::ReceiveOp::PullAll(
-    StatusOrCallback<std::vector<Slice>> ready) {
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "PullAll incoming_message=" << incoming_message_
-      << " pending_close_reason=" << pending_close_reason_;
-  if (incoming_message_ == nullptr) {
-    if (pending_close_reason_) {
-      ready(*pending_close_reason_);
-    } else {
-      assert(pending_pull_.empty());
-      pending_pull_all_ = std::move(ready);
-    }
-  } else {
-    incoming_message_->PullAll(std::move(ready));
-  }
-}
-
-void DatagramStream::ReceiveOp::Close(const Status& status) {
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "Close incoming_message=" << incoming_message_
-      << " pending_close_reason=" << pending_close_reason_
-      << " status=" << status;
-  if (incoming_message_ == nullptr) {
-    pending_close_reason_ = status;
-    if (!pending_pull_.empty()) {
-      if (status.is_error()) {
-        pending_pull_(status);
-      } else {
-        pending_pull_(Nothing);
-      }
-    }
-    if (!pending_pull_all_.empty()) {
-      if (status.is_error()) {
-        pending_pull_all_(status);
-      } else {
-        pending_pull_all_(std::vector<Slice>{});
-      }
-    }
-  } else {
-    incoming_message_->Close(status);
-  }
-}
-
-}  // namespace overnet
diff --git a/lib/overnet/datagram_stream.h b/lib/overnet/datagram_stream.h
deleted file mode 100644
index 338a742..0000000
--- a/lib/overnet/datagram_stream.h
+++ /dev/null
@@ -1,332 +0,0 @@
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#pragma once
-
-#include <queue>  // TODO(ctiller): switch to a short queue (inlined 1-2 elems, linked list)
-#include "ack_frame.h"
-#include "internal_list.h"
-#include "linearizer.h"
-#include "packet_protocol.h"
-#include "receive_mode.h"
-#include "reliability_and_ordering.h"
-#include "router.h"
-#include "seq_num.h"
-#include "sink.h"
-#include "slice.h"
-#include "timer.h"
-#include "trace.h"
-
-namespace overnet {
-
-class MessageFragment {
- public:
-  enum class Type : uint8_t { Chunk = 0, MessageAbort = 1, StreamEnd = 2 };
-
-  MessageFragment(const MessageFragment&) = delete;
-  MessageFragment& operator=(const MessageFragment&) = delete;
-
-  MessageFragment(uint64_t message, Chunk chunk)
-      : message_(message), type_(Type::Chunk) {
-    assert(message > 0);
-    new (&payload_.chunk) Chunk(std::move(chunk));
-  }
-
-  MessageFragment(MessageFragment&& other)
-      : message_(other.message_), type_(other.type_) {
-    switch (type_) {
-      case Type::Chunk:
-        new (&payload_.chunk) Chunk(std::move(other.payload_.chunk));
-        break;
-      case Type::MessageAbort:
-      case Type::StreamEnd:
-        new (&payload_.status) Status(std::move(other.payload_.status));
-        break;
-    }
-  }
-
-  MessageFragment& operator=(MessageFragment&& other) {
-    if (type_ != other.type_) {
-      this->~MessageFragment();
-      new (this) MessageFragment(std::forward<MessageFragment>(other));
-    } else {
-      switch (type_) {
-        case Type::Chunk:
-          payload_.chunk = std::move(other.payload_.chunk);
-          break;
-        case Type::MessageAbort:
-        case Type::StreamEnd:
-          payload_.status = std::move(other.payload_.status);
-          break;
-      }
-    }
-    return *this;
-  }
-
-  ~MessageFragment() {
-    switch (type_) {
-      case Type::Chunk:
-        payload_.chunk.~Chunk();
-        break;
-      case Type::MessageAbort:
-      case Type::StreamEnd:
-        payload_.status.~Status();
-        break;
-    }
-  }
-
-  static MessageFragment Abort(uint64_t message_id, Status status) {
-    return MessageFragment(message_id, Type::MessageAbort, std::move(status));
-  }
-
-  static MessageFragment EndOfStream(uint64_t last_message_id, Status status) {
-    return MessageFragment(last_message_id, Type::StreamEnd, std::move(status));
-  }
-
-  Slice Write(uint64_t desired_prefix) const;
-  static StatusOr<MessageFragment> Parse(Slice incoming);
-
-  Type type() const { return type_; }
-
-  uint64_t message() const { return message_; }
-
-  const Chunk& chunk() const {
-    assert(type_ == Type::Chunk);
-    return payload_.chunk;
-  }
-  Chunk* mutable_chunk() {
-    assert(type_ == Type::Chunk);
-    return &payload_.chunk;
-  }
-
-  const Status& status() const {
-    assert(type_ == Type::MessageAbort || type_ == Type::StreamEnd);
-    return payload_.status;
-  }
-
- private:
-  static constexpr uint8_t kFlagEndOfMessage = 0x80;
-  static constexpr uint8_t kFlagTypeMask = 0x0f;
-  static constexpr uint8_t kReservedFlags =
-      static_cast<uint8_t>(~(kFlagTypeMask | kFlagEndOfMessage));
-
-  MessageFragment(uint64_t message, Type type, Status status)
-      : message_(message), type_(type) {
-    assert(type == Type::MessageAbort || type == Type::StreamEnd);
-    assert(message != 0);
-    new (&payload_.status) Status(std::move(status));
-  }
-
-  uint64_t message_;
-  union Payload {
-    Payload() {}
-    ~Payload() {}
-
-    Chunk chunk;
-    Status status;
-  };
-  Type type_;
-  Payload payload_;
-};
-
-class DatagramStream : private Router::StreamHandler,
-                       private PacketProtocol::PacketSender {
-  class IncomingMessage {
-   public:
-    // TODO(ctiller): 1MB stubbed in for the moment until something better
-    explicit IncomingMessage(TraceSink trace_sink)
-        : linearizer_(1024 * 1024,
-                      trace_sink.Decorate([this](const std::string& msg) {
-                        std::ostringstream out;
-                        out << "Msg[" << this << "] " << msg;
-                        return out.str();
-                      })) {}
-
-    void Pull(StatusOrCallback<Optional<Slice>>&& done) {
-      linearizer_.Pull(std::forward<StatusOrCallback<Optional<Slice>>>(done));
-    }
-    void PullAll(StatusOrCallback<std::vector<Slice>>&& done) {
-      linearizer_.PullAll(
-          std::forward<StatusOrCallback<std::vector<Slice>>>(done));
-    }
-
-    void Push(Chunk&& chunk) { linearizer_.Push(std::forward<Chunk>(chunk)); }
-
-    void Close(const Status& status) { linearizer_.Close(status); }
-
-    InternalListNode<IncomingMessage> incoming_link;
-
-   private:
-    Linearizer linearizer_;
-  };
-
- public:
-  DatagramStream(Router* router, TraceSink sink, NodeId peer,
-                 ReliabilityAndOrdering reliability_and_ordering,
-                 StreamId stream_id);
-  ~DatagramStream();
-
-  DatagramStream(const DatagramStream&) = delete;
-  DatagramStream& operator=(const DatagramStream&) = delete;
-  DatagramStream(DatagramStream&&) = delete;
-  DatagramStream& operator=(DatagramStream&&) = delete;
-
-  void Close(const Status& status, Callback<void> quiesced);
-  void Close(Callback<void> quiesced) override {
-    Close(Status::Ok(), std::move(quiesced));
-  }
-
-  class SendOp final : public Sink<Slice> {
-   public:
-    SendOp(DatagramStream* stream, uint64_t payload_length);
-
-    void Push(Slice item) override;
-    void Close(const Status& status, Callback<void> quiesced) override;
-    void Close(Callback<void> quiesced) {
-      Close(Status::Ok(), std::move(quiesced));
-    }
-
-   private:
-    void SetClosed(const Status& status);
-    void SendChunk(Chunk chunk);
-    void SendError(const Status& status);
-
-    void CompleteReliable(const Status& status, Chunk chunk);
-    void CompleteUnreliable(const Status& status);
-
-    void BeginOp() { ++outstanding_ops_; }
-    void EndOp() {
-      if (0 == --outstanding_ops_) {
-        if (!quiesced_.empty())
-          quiesced_();
-      }
-    }
-
-    PacketProtocol::SendCallback MakeAckCallback(const Chunk& chunk);
-
-    class OutstandingOp {
-     public:
-      OutstandingOp() = delete;
-      OutstandingOp(SendOp* op) : op_(op) { op_->BeginOp(); }
-      OutstandingOp(const OutstandingOp& other) : op_(other.op_) {
-        op_->BeginOp();
-      }
-      OutstandingOp& operator=(OutstandingOp other) {
-        other.Swap(this);
-        return *this;
-      }
-      void Swap(OutstandingOp* other) { std::swap(op_, other->op_); }
-
-      ~OutstandingOp() { op_->EndOp(); }
-      SendOp* operator->() const { return op_; }
-
-     private:
-      // TODO(ctiller): perhaps consider packing these into one word.
-      SendOp* op_;
-    };
-
-    enum class State : uint8_t {
-      OPEN,
-      CLOSED_OK,
-      CLOSED_WITH_ERROR,
-    };
-
-    DatagramStream* const stream_;
-    const TraceSink trace_sink_;
-    int outstanding_ops_ = 0;
-    const uint64_t payload_length_;
-    const uint64_t message_id_;
-    const uint8_t message_id_length_;
-    State state_ = State::OPEN;
-    uint64_t push_offset_ = 0;
-    Callback<void> quiesced_;
-  };
-
-  class ReceiveOp final : public Source<Slice> {
-    friend class DatagramStream;
-
-   public:
-    explicit ReceiveOp(DatagramStream* stream);
-
-    void Pull(StatusOrCallback<Optional<Slice>> ready) override;
-    void PullAll(StatusOrCallback<std::vector<Slice>> ready) override;
-    void Close(const Status& status) override;
-
-   private:
-    const TraceSink trace_sink_;
-    IncomingMessage* incoming_message_ = nullptr;
-    StatusOrCallback<Optional<Slice>> pending_pull_;
-    StatusOrCallback<std::vector<Slice>> pending_pull_all_;
-    Optional<Status> pending_close_reason_;
-    InternalListNode<ReceiveOp> waiting_link_;
-  };
-
-  NodeId peer() const { return peer_; }
-
- private:
-  void HandleMessage(SeqNum seq, TimeStamp received, Slice data) override final;
-  void SendPacket(SeqNum seq, LazySlice data,
-                  Callback<void> done) override final;
-  void SendCloseAndFlushQuiesced(const Status& status, int retry_number);
-  void FinishClosing();
-
-  void MaybeContinueReceive();
-
-  Timer* const timer_;
-  Router* const router_;
-  TraceSink const trace_sink_;
-  const NodeId peer_;
-  const StreamId stream_id_;
-  const ReliabilityAndOrdering reliability_and_ordering_;
-  uint64_t next_message_id_ = 1;
-  uint64_t largest_incoming_message_id_seen_ = 0;
-  receive_mode::ParameterizedReceiveMode receive_mode_;
-  PacketProtocol packet_protocol_;
-  enum class CloseState : uint8_t {
-    OPEN,
-    LOCAL_CLOSE_REQUESTED,
-    REMOTE_CLOSED,
-    CLOSING_PROTOCOL,
-    CLOSED,
-  };
-  friend inline std::ostream& operator<<(std::ostream& out, CloseState state) {
-    switch (state) {
-      case CloseState::OPEN:
-        return out << "OPEN";
-      case CloseState::LOCAL_CLOSE_REQUESTED:
-        return out << "LOCAL_CLOSE_REQUESTED";
-      case CloseState::REMOTE_CLOSED:
-        return out << "REMOTE_CLOSED";
-      case CloseState::CLOSING_PROTOCOL:
-        return out << "CLOSING_PROTOCOL";
-      case CloseState::CLOSED:
-        return out << "CLOSED";
-    }
-    return out << "UNKNOWN";
-  }
-  CloseState close_state_ = CloseState::OPEN;
-  struct RequestedClose {
-    uint64_t last_message_id;
-    Status status;
-    bool operator==(const RequestedClose& other) const {
-      return last_message_id == other.last_message_id &&
-             status.code() == other.status.code();
-    }
-    bool operator!=(const RequestedClose& other) const {
-      return !operator==(other);
-    }
-  };
-  Optional<RequestedClose> requested_close_;
-
-  // TODO(ctiller): a custom allocator here would be worthwhile, especially one
-  // that could remove allocations for the common case of few entries.
-  std::unordered_map<uint64_t, IncomingMessage> messages_;
-  InternalList<IncomingMessage, &IncomingMessage::incoming_link>
-      unclaimed_messages_;
-  InternalList<ReceiveOp, &ReceiveOp::waiting_link_> unclaimed_receives_;
-
-  std::vector<Callback<void>> on_quiesced_;
-};
-
-}  // namespace overnet
diff --git a/lib/overnet/datagram_stream/BUILD.gn b/lib/overnet/datagram_stream/BUILD.gn
new file mode 100644
index 0000000..42a9794
--- /dev/null
+++ b/lib/overnet/datagram_stream/BUILD.gn
@@ -0,0 +1,127 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+###############################################################################
+# AGGREGATE LIBRARIES
+
+source_set("lib") {
+}
+
+source_set("tests") {
+  testonly = true
+  deps = [
+    ":datagram_stream_test",
+    ":linearizer_test",
+    ":receive_mode_test",
+  ]
+}
+
+###############################################################################
+
+# datagram_stream
+source_set("datagram_stream") {
+  sources = [
+    "datagram_stream.cc",
+    "datagram_stream.h",
+  ]
+  deps = [
+    ":linearizer",
+    ":receive_mode",
+    "//garnet/lib/overnet/environment:timer",
+    "//garnet/lib/overnet/environment:trace",
+    "//garnet/lib/overnet/labels:reliability_and_ordering",
+    "//garnet/lib/overnet/labels:seq_num",
+    "//garnet/lib/overnet/packet_protocol",
+    "//garnet/lib/overnet/routing:router",
+    "//garnet/lib/overnet/vocabulary:internal_list",
+    "//garnet/lib/overnet/vocabulary:slice",
+  ]
+}
+
+source_set("datagram_stream_test") {
+  testonly = true
+  sources = [
+    "datagram_stream_test.cc",
+  ]
+  deps = [
+    ":datagram_stream",
+    "//garnet/lib/overnet/testing:test_timer",
+    "//garnet/lib/overnet/testing:trace_cout",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# linearizer
+source_set("linearizer") {
+  sources = [
+    "linearizer.cc",
+    "linearizer.h",
+  ]
+  deps = [
+    "//garnet/lib/overnet/environment:trace",
+    "//garnet/lib/overnet/vocabulary:callback",
+    "//garnet/lib/overnet/vocabulary:optional",
+    "//garnet/lib/overnet/vocabulary:slice",
+    "//garnet/lib/overnet/vocabulary:status",
+  ]
+}
+
+source_set("linearizer_fuzzer_helpers") {
+  sources = [
+    "linearizer_fuzzer_helpers.h",
+  ]
+  deps = [
+    ":linearizer",
+    "//garnet/lib/overnet/vocabulary:optional",
+  ]
+}
+
+source_set("linearizer_test") {
+  testonly = true
+  sources = [
+    "linearizer_test.cc",
+  ]
+  deps = [
+    ":linearizer",
+    ":linearizer_fuzzer_helpers",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# receive_mode
+source_set("receive_mode") {
+  sources = [
+    "receive_mode.cc",
+    "receive_mode.h",
+  ]
+  deps = [
+    "//garnet/lib/overnet/labels:reliability_and_ordering",
+    "//garnet/lib/overnet/vocabulary:callback",
+    "//garnet/lib/overnet/vocabulary:optional",
+    "//garnet/lib/overnet/vocabulary:status",
+  ]
+}
+
+source_set("receive_mode_fuzzer_helpers") {
+  sources = [
+    "receive_mode_fuzzer_helpers.h",
+  ]
+  deps = [
+    ":receive_mode",
+  ]
+}
+
+source_set("receive_mode_test") {
+  testonly = true
+  sources = [
+    "receive_mode_test.cc",
+  ]
+  deps = [
+    ":receive_mode",
+    ":receive_mode_fuzzer_helpers",
+    "//third_party/googletest:gtest",
+  ]
+}
diff --git a/lib/overnet/datagram_stream/datagram_stream.cc b/lib/overnet/datagram_stream/datagram_stream.cc
new file mode 100644
index 0000000..cff44cf
--- /dev/null
+++ b/lib/overnet/datagram_stream/datagram_stream.cc
@@ -0,0 +1,908 @@
+// Copyright 2018 The Fuchsia 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 "datagram_stream.h"
+#include <sstream>
+
+namespace overnet {
+
+////////////////////////////////////////////////////////////////////////////////
+// MessageFragment
+
+Slice MessageFragment::Write(Border desired_border) const {
+  const auto message_length = varint::WireSizeFor(message_);
+  uint8_t flags = static_cast<uint8_t>(type_);
+  assert((flags & kFlagTypeMask) == flags);
+  switch (type_) {
+    case Type::Chunk: {
+      const Chunk& chunk = this->chunk();
+      if (chunk.end_of_message) {
+        flags |= kFlagEndOfMessage;
+      }
+      const auto chunk_offset_length = varint::WireSizeFor(chunk.offset);
+      return chunk.slice.WithPrefix(
+          message_length + chunk_offset_length + 1, [&](uint8_t* const bytes) {
+            uint8_t* p = bytes;
+            *p++ = flags;
+            p = varint::Write(message_, message_length, p);
+            p = varint::Write(chunk.offset, chunk_offset_length, p);
+            assert(p == bytes + message_length + chunk_offset_length + 1);
+          });
+    }
+    case Type::MessageCancel:
+    case Type::StreamEnd: {
+      const Status& status = this->status();
+      const auto& reason = status.reason();
+      const auto reason_length_length = varint::WireSizeFor(reason.length());
+      const auto frame_length =
+          1 + message_length + 1 + reason_length_length + reason.length();
+      return Slice::WithInitializerAndBorders(
+          frame_length, desired_border, [&](uint8_t* bytes) {
+            uint8_t* p = bytes;
+            *p++ = flags;
+            p = varint::Write(message_, message_length, p);
+            *p++ = static_cast<uint8_t>(status.code());
+            p = varint::Write(reason.length(), reason_length_length, p);
+            assert(p + reason.length() == bytes + frame_length);
+            memcpy(p, reason.data(), reason.length());
+          });
+    }
+  }
+  abort();
+}
+
+StatusOr<MessageFragment> MessageFragment::Parse(Slice slice) {
+  const uint8_t* p = slice.begin();
+  const uint8_t* const end = slice.end();
+  uint64_t message;
+  uint64_t chunk_offset;
+  if (p == end) {
+    return StatusOr<MessageFragment>(
+        StatusCode::INVALID_ARGUMENT,
+        "Failed to read flags from message fragment");
+  }
+  const uint8_t flags = *p++;
+  if (flags & kReservedFlags) {
+    return StatusOr<MessageFragment>(
+        StatusCode::INVALID_ARGUMENT,
+        "Reserved flags set on message fragment flags field");
+  }
+  if (!varint::Read(&p, end, &message)) {
+    return StatusOr<MessageFragment>(
+        StatusCode::INVALID_ARGUMENT,
+        "Failed to read message id from message fragment");
+  }
+  if (message == 0) {
+    return StatusOr<MessageFragment>(StatusCode::INVALID_ARGUMENT,
+                                     "Message id 0 is invalid");
+  }
+  const Type type = static_cast<Type>(flags & kFlagTypeMask);
+  switch (type) {
+    case Type::Chunk:
+      if (!varint::Read(&p, end, &chunk_offset)) {
+        return StatusOr<MessageFragment>(
+            StatusCode::INVALID_ARGUMENT,
+            "Failed to read chunk offset from message fragment");
+      }
+      return MessageFragment{
+          message, Chunk{chunk_offset, (flags & kFlagEndOfMessage) != 0,
+                         slice.FromPointer(p)}};
+    case Type::MessageCancel:
+    case Type::StreamEnd: {
+      if (p == end) {
+        return StatusOr<MessageFragment>(
+            StatusCode::INVALID_ARGUMENT,
+            "Failed to read status code from message fragment");
+      }
+      const uint8_t code = *p++;
+      uint64_t reason_length;
+      if (!varint::Read(&p, end, &reason_length)) {
+        return StatusOr<MessageFragment>(
+            StatusCode::INVALID_ARGUMENT,
+            "Failed to read status reason length from message fragment");
+      }
+      return MessageFragment(message, type,
+                             Status(static_cast<StatusCode>(code),
+                                    std::string(p, p + reason_length)));
+    } break;
+    default:
+      return StatusOr<MessageFragment>(StatusCode::INVALID_ARGUMENT,
+                                       "Unknown message fragment type");
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DatagramStream proper
+
+DatagramStream::DatagramStream(Router* router, NodeId peer,
+                               ReliabilityAndOrdering reliability_and_ordering,
+                               StreamId stream_id)
+    : timer_(router->timer()),
+      router_(router),
+      peer_(peer),
+      stream_id_(stream_id),
+      reliability_and_ordering_(reliability_and_ordering),
+      receive_mode_(reliability_and_ordering),
+      // TODO(ctiller): What should mss be? Hardcoding to 2048 for now.
+      packet_protocol_(timer_, [router] { return (*router->rng())(); }, this,
+                       PacketProtocol::NullCodec(), 2048) {}
+
+void DatagramStream::Register() {
+  ScopedModule<DatagramStream> scoped_module(this);
+  if (router_->RegisterStream(peer_, stream_id_, this).is_error()) {
+    abort();
+  }
+}
+
+DatagramStream::~DatagramStream() {
+  ScopedModule<DatagramStream> scoped_module(this);
+  assert(close_state_ == CloseState::CLOSED);
+}
+
+void DatagramStream::Close(const Status& status, Callback<void> quiesced) {
+  ScopedModule<DatagramStream> scoped_module(this);
+  OVERNET_TRACE(DEBUG) << "Close: state=" << close_state_
+                       << " status=" << status << " peer=" << peer_
+                       << " stream_id=" << stream_id_;
+
+  switch (close_state_) {
+    case CloseState::CLOSED:
+      return;
+    case CloseState::LOCAL_CLOSE_REQUESTED_OK:
+      if (status.is_error()) {
+        close_state_ = CloseState::LOCAL_CLOSE_REQUESTED_WITH_ERROR;
+        local_close_status_ = status;
+        CancelReceives();
+      }
+      [[fallthrough]];
+    case CloseState::CLOSING_PROTOCOL:
+    case CloseState::LOCAL_CLOSE_REQUESTED_WITH_ERROR:
+      on_quiesced_.emplace_back(std::move(quiesced));
+      return;
+    case CloseState::DRAINING_LOCAL_CLOSED_OK:
+      on_quiesced_.emplace_back(std::move(quiesced));
+      if (status.is_error()) {
+        close_state_ = CloseState::LOCAL_CLOSE_REQUESTED_WITH_ERROR;
+        FinishClosing();
+      }
+      break;
+    case CloseState::REMOTE_CLOSED:
+      on_quiesced_.emplace_back(std::move(quiesced));
+      FinishClosing();
+      return;
+    case CloseState::OPEN:
+      if (status.is_error()) {
+        CancelReceives();
+      }
+      local_close_status_ = status;
+      on_quiesced_.emplace_back(std::move(quiesced));
+      receive_mode_.Close(status);
+      close_state_ = status.is_error()
+                         ? CloseState::LOCAL_CLOSE_REQUESTED_WITH_ERROR
+                         : CloseState::LOCAL_CLOSE_REQUESTED_OK;
+      SendCloseAndFlushQuiesced(0);
+      return;
+  }
+}
+
+void DatagramStream::SendCloseAndFlushQuiesced(int retry_number) {
+  assert(close_state_ == CloseState::LOCAL_CLOSE_REQUESTED_OK ||
+         close_state_ == CloseState::LOCAL_CLOSE_REQUESTED_WITH_ERROR);
+  OVERNET_TRACE(DEBUG) << "SendClose: status=" << *local_close_status_
+                       << " retry=" << retry_number
+                       << " state=" << close_state_;
+  packet_protocol_.Send(
+      [this](auto args) {
+        ScopedModule<DatagramStream> scoped_module(this);
+        return MessageFragment::EndOfStream(next_message_id_,
+                                            *local_close_status_)
+            .Write(args.desired_border);
+      },
+      [this, retry_number](const Status& send_status) mutable {
+        ScopedModule<DatagramStream> scoped_module(this);
+        OVERNET_TRACE(DEBUG) << "SendClose ACK status=" << send_status
+                             << " retry=" << retry_number;
+        switch (close_state_) {
+          case CloseState::OPEN:
+          case CloseState::REMOTE_CLOSED:
+          case CloseState::DRAINING_LOCAL_CLOSED_OK:
+            assert(false);
+            break;
+          case CloseState::CLOSED:
+          case CloseState::CLOSING_PROTOCOL:
+            break;
+          case CloseState::LOCAL_CLOSE_REQUESTED_OK:
+          case CloseState::LOCAL_CLOSE_REQUESTED_WITH_ERROR:
+            if (send_status.code() == StatusCode::UNAVAILABLE &&
+                retry_number != 5) {
+              SendCloseAndFlushQuiesced(retry_number + 1);
+            } else {
+              if (send_status.is_error()) {
+                close_state_ = CloseState::LOCAL_CLOSE_REQUESTED_WITH_ERROR;
+              } else {
+                close_state_ = CloseState::DRAINING_LOCAL_CLOSED_OK;
+              }
+              MaybeFinishClosing();
+            }
+            break;
+        }
+      });
+}
+
+void DatagramStream::MaybeFinishClosing() {
+  OVERNET_TRACE(DEBUG) << "MaybeFinishClosing: state=" << close_state_
+                       << " message_state.size()==" << message_state_.size();
+  switch (close_state_) {
+    case CloseState::OPEN:
+    case CloseState::REMOTE_CLOSED:
+    case CloseState::CLOSED:
+    case CloseState::CLOSING_PROTOCOL:
+    case CloseState::LOCAL_CLOSE_REQUESTED_OK:
+      return;
+    case CloseState::DRAINING_LOCAL_CLOSED_OK:
+      if (message_state_.empty()) {
+        FinishClosing();
+      }
+      break;
+    case CloseState::LOCAL_CLOSE_REQUESTED_WITH_ERROR:
+      FinishClosing();
+      break;
+  }
+}
+
+void DatagramStream::CancelReceives() {
+  while (!unclaimed_receives_.Empty()) {
+    unclaimed_receives_.begin()->Close(Status::Cancelled());
+  }
+}
+
+void DatagramStream::FinishClosing() {
+  OVERNET_TRACE(DEBUG) << "FinishClosing: state=" << close_state_;
+  assert(close_state_ == CloseState::DRAINING_LOCAL_CLOSED_OK ||
+         close_state_ == CloseState::LOCAL_CLOSE_REQUESTED_OK ||
+         close_state_ == CloseState::LOCAL_CLOSE_REQUESTED_WITH_ERROR ||
+         close_state_ == CloseState::REMOTE_CLOSED);
+  close_state_ = CloseState::CLOSING_PROTOCOL;
+  CancelReceives();
+  packet_protocol_.Close([this]() {
+    close_state_ = CloseState::CLOSED;
+
+    auto unregister_status = router_->UnregisterStream(peer_, stream_id_, this);
+    assert(unregister_status.is_ok());
+
+    std::vector<PendingSend> pending_send;
+    pending_send.swap(pending_send_);
+    pending_send.clear();
+
+    assert(message_state_.empty());
+
+    std::vector<Callback<void>> on_quiesced;
+    on_quiesced.swap(on_quiesced_);
+    on_quiesced.clear();
+  });
+}
+
+template <typename F>
+struct NoDiscard {
+  F f;
+  NoDiscard(F const& f) : f(f) {}
+  template <typename... T>
+  [[nodiscard]] constexpr auto operator()(T&&... t) noexcept(
+      noexcept(f(std::forward<T>(t)...))) {
+    return f(std::forward<T>(t)...);
+  }
+};
+
+void DatagramStream::HandleMessage(SeqNum seq, TimeStamp received, Slice data) {
+  ScopedModule<DatagramStream> scoped_module(this);
+  switch (close_state_) {
+    // In these states we process messages:
+    case CloseState::OPEN:
+    case CloseState::LOCAL_CLOSE_REQUESTED_OK:
+    case CloseState::REMOTE_CLOSED:
+    case CloseState::DRAINING_LOCAL_CLOSED_OK:
+      break;
+    // In these states we do not:
+    case CloseState::CLOSING_PROTOCOL:
+    case CloseState::CLOSED:
+    case CloseState::LOCAL_CLOSE_REQUESTED_WITH_ERROR:
+      return;
+  }
+
+  auto pkt_status = packet_protocol_.Process(received, seq, std::move(data));
+  if (pkt_status.status.is_error()) {
+    OVERNET_TRACE(WARNING) << "Failed to process packet: "
+                           << pkt_status.status.AsStatus();
+    return;
+  }
+  auto payload = std::move(pkt_status.status.value());
+  if (!payload || !payload->length()) {
+    return;
+  }
+  OVERNET_TRACE(DEBUG) << "Process payload " << payload;
+  auto msg_status = MessageFragment::Parse(std::move(*payload));
+  if (msg_status.is_error()) {
+    OVERNET_TRACE(WARNING) << "Failed to parse message: "
+                           << msg_status.AsStatus();
+    return;
+  }
+  auto msg = std::move(*msg_status.get());
+  OVERNET_TRACE(DEBUG) << "Payload type=" << static_cast<int>(msg.type())
+                       << " msg=" << msg.message();
+  switch (msg.type()) {
+    case MessageFragment::Type::Chunk: {
+      OVERNET_TRACE(DEBUG) << "chunk offset=" << msg.chunk().offset
+                           << " length=" << msg.chunk().slice.length()
+                           << " end-of-message=" << msg.chunk().end_of_message;
+      // Got a chunk of data: add it to the relevant incoming message.
+      largest_incoming_message_id_seen_ =
+          std::max(largest_incoming_message_id_seen_, msg.message());
+      auto it = messages_.find(msg.message());
+      if (it == messages_.end()) {
+        it = messages_
+                 .emplace(std::piecewise_construct,
+                          std::forward_as_tuple(msg.message()),
+                          std::forward_as_tuple(this))
+                 .first;
+        if (!it->second.Push(std::move(*msg.mutable_chunk()))) {
+          pkt_status.Nack();
+        }
+        receive_mode_.Begin(msg.message(), [this, msg = std::move(msg)](
+                                               const Status& status) mutable {
+          if (status.is_error()) {
+            OVERNET_TRACE(WARNING) << "Receive failed: " << status;
+            return;
+          }
+          auto it = messages_.find(msg.message());
+          if (it == messages_.end()) {
+            return;
+          }
+          unclaimed_messages_.PushBack(&it->second);
+          MaybeContinueReceive();
+        });
+      } else {
+        if (!it->second.Push(std::move(*msg.mutable_chunk()))) {
+          pkt_status.Nack();
+        }
+      }
+    } break;
+    case MessageFragment::Type::MessageCancel: {
+      // Aborting a message: this is like a close to the incoming message.
+      largest_incoming_message_id_seen_ =
+          std::max(largest_incoming_message_id_seen_, msg.message());
+      auto it = messages_.find(msg.message());
+      if (it == messages_.end()) {
+        it = messages_
+                 .emplace(std::piecewise_construct,
+                          std::forward_as_tuple(msg.message()),
+                          std::forward_as_tuple(this))
+                 .first;
+      }
+      it->second.Close(msg.status());
+    } break;
+    case MessageFragment::Type::StreamEnd:
+      // TODO(ctiller): handle case of ok termination with outstanding
+      // messages.
+      RequestedClose requested_close{msg.message(), msg.status()};
+      OVERNET_TRACE(DEBUG) << "peer requests close with status "
+                           << msg.status();
+      if (requested_close_.has_value()) {
+        if (*requested_close_ != requested_close) {
+          OVERNET_TRACE(WARNING)
+              << "Non-duplicate last message id received: previously got "
+              << requested_close_->last_message_id << " with status "
+              << requested_close_->status << " now have " << msg.message()
+              << " with status " << msg.status();
+        }
+        return;
+      }
+      requested_close_ = requested_close;
+      auto enact_remote_close = [this](const Status& status) {
+        packet_protocol_.RequestSendAck();
+        switch (close_state_) {
+          case CloseState::OPEN:
+            close_state_ = CloseState::REMOTE_CLOSED;
+            receive_mode_.Close(status);
+            break;
+          case CloseState::REMOTE_CLOSED:
+            assert(false);
+            break;
+          case CloseState::LOCAL_CLOSE_REQUESTED_OK:
+          case CloseState::LOCAL_CLOSE_REQUESTED_WITH_ERROR:
+          case CloseState::DRAINING_LOCAL_CLOSED_OK:
+            FinishClosing();
+            break;
+          case CloseState::CLOSED:
+          case CloseState::CLOSING_PROTOCOL:
+            break;
+        }
+      };
+      if (requested_close_->status.is_error()) {
+        enact_remote_close(requested_close_->status);
+      } else {
+        receive_mode_.Begin(msg.message(), std::move(enact_remote_close));
+      }
+      break;
+  }
+}
+
+void DatagramStream::MaybeContinueReceive() {
+  if (unclaimed_messages_.Empty())
+    return;
+  if (unclaimed_receives_.Empty())
+    return;
+
+  auto incoming_message = unclaimed_messages_.PopFront();
+  auto receive_op = unclaimed_receives_.PopFront();
+
+  receive_op->incoming_message_ = incoming_message;
+  if (!receive_op->pending_pull_.empty()) {
+    incoming_message->Pull(std::move(receive_op->pending_pull_));
+  } else if (!receive_op->pending_pull_all_.empty()) {
+    incoming_message->PullAll(
+        [receive_op](const StatusOr<std::vector<Slice>>& status) {
+          receive_op->Close(status.AsStatus());
+          receive_op->pending_pull_all_(status);
+        });
+  }
+}
+
+void DatagramStream::SendPacket(SeqNum seq, LazySlice data,
+                                Callback<void> done) {
+  router_->Forward(
+      Message{std::move(RoutableMessage(router_->node_id())
+                            .AddDestination(peer_, stream_id_, seq)),
+              std::move(data), timer_->Now()});
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// SendOp
+
+DatagramStream::SendOp::SendOp(DatagramStream* stream, uint64_t payload_length)
+    : StateRef(stream,
+               stream->message_state_
+                   .emplace(std::piecewise_construct,
+                            std::forward_as_tuple(stream->next_message_id_++),
+                            std::forward_as_tuple())
+                   .first),
+      payload_length_(payload_length) {
+  ScopedModule<SendOp> in_send_op(this);
+  OVERNET_TRACE(DEBUG) << "SendOp created";
+}
+
+DatagramStream::SendOp::~SendOp() {
+  ScopedModule<SendOp> in_send_op(this);
+  OVERNET_TRACE(DEBUG) << "SendOp destroyed";
+}
+
+void DatagramStream::SendError(StateRef state, const overnet::Status& status) {
+  ScopedModule<DatagramStream> in_dgs(this);
+  OVERNET_TRACE(DEBUG) << "SendError: " << status;
+  packet_protocol_.Send(
+      [message_id = state.message_id(), status](auto arg) {
+        return MessageFragment::Abort(message_id, status)
+            .Write(arg.desired_border);
+      },
+      [state, status](const Status& send_status) {
+        if (send_status.code() == StatusCode::UNAVAILABLE &&
+            state.stream()->close_state_ == CloseState::OPEN) {
+          state.stream()->SendError(state, status);
+        }
+        OVERNET_TRACE(DEBUG) << "SendError: ACK " << status;
+      });
+}
+
+void DatagramStream::SendOp::Close(const Status& status) {
+  ScopedModule<SendOp> in_send_op(this);
+  if (status.is_ok() && payload_length_ != push_offset_) {
+    std::ostringstream out;
+    out << "Insufficient bytes for message presented: expected "
+        << payload_length_ << " but got " << push_offset_;
+    SetClosed(Status(StatusCode::INVALID_ARGUMENT, out.str()));
+  } else {
+    SetClosed(status);
+  }
+}
+
+void DatagramStream::StateRef::SetClosed(const Status& status) {
+  if (state() != SendState::OPEN) {
+    return;
+  }
+  if (status.is_ok()) {
+    set_state(SendState::CLOSED_OK);
+  } else {
+    set_state(SendState::CLOSED_WITH_ERROR);
+    stream()->SendError(*this, status);
+  }
+}
+
+void DatagramStream::SendOp::Push(Slice item, Callback<void> started) {
+  ScopedModule<SendOp> in_send_op(this);
+  assert(state() == SendState::OPEN);
+  if (state() != SendState::OPEN || stream()->IsClosedForSending()) {
+    return;
+  }
+  const auto chunk_start = push_offset_;
+  const auto chunk_length = item.length();
+  const auto end_byte = chunk_start + chunk_length;
+  OVERNET_TRACE(DEBUG) << "Push: chunk_start=" << chunk_start
+                       << " chunk_length=" << chunk_length
+                       << " end_byte=" << end_byte
+                       << " payload_length=" << payload_length_;
+  if (end_byte > payload_length_) {
+    Close(Status(StatusCode::INVALID_ARGUMENT,
+                 "Exceeded message payload length"));
+    return;
+  }
+  push_offset_ += chunk_length;
+  Chunk chunk{chunk_start, end_byte == payload_length_, std::move(item)};
+  stream()->SendChunk(*this, std::move(chunk), std::move(started));
+}
+
+void DatagramStream::SendChunk(StateRef state, Chunk chunk,
+                               Callback<void> started) {
+  ScopedModule<DatagramStream> in_dgs(this);
+  OVERNET_TRACE(DEBUG) << "SchedOutChunk: msg=" << state.message_id()
+                       << " ofs=" << chunk.offset
+                       << " len=" << chunk.slice.length()
+                       << " pending=" << pending_send_.size()
+                       << " sending=" << sending_;
+  auto it = std::upper_bound(
+      pending_send_.begin(), pending_send_.end(),
+      std::make_tuple(state.message_id(), chunk.offset),
+      [](const std::tuple<uint64_t, uint64_t>& label, const PendingSend& ps) {
+        return label < std::make_tuple(ps.what.state.message_id(),
+                                       ps.what.chunk.offset);
+      });
+
+  if (it != pending_send_.end()) {
+    OVERNET_TRACE(DEBUG) << "  prior to msg=" << it->what.state.message_id()
+                         << " ofs=" << it->what.chunk.offset
+                         << " len=" << it->what.chunk.slice.length();
+    if (state.message_id() == it->what.state.message_id()) {
+      if (auto joined =
+              Chunk::JoinIfSameUnderlyingMemory(chunk, it->what.chunk)) {
+        OVERNET_TRACE(DEBUG) << "Merged previously separated chunks";
+        it->what.chunk = *joined;
+        goto done;
+      }
+    }
+  }
+  if (it != pending_send_.begin()) {
+    OVERNET_TRACE(DEBUG) << "  after msg=" << (it - 1)->what.state.message_id()
+                         << " ofs=" << (it - 1)->what.chunk.offset
+                         << " len=" << (it - 1)->what.chunk.slice.length();
+    if (state.message_id() == (it - 1)->what.state.message_id()) {
+      if (auto joined =
+              Chunk::JoinIfSameUnderlyingMemory((it - 1)->what.chunk, chunk)) {
+        OVERNET_TRACE(DEBUG) << "Merged previously separated chunks";
+        (it - 1)->what.chunk = *joined;
+        goto done;
+      }
+    }
+  }
+  if (it == pending_send_.begin()) {
+    OVERNET_TRACE(DEBUG) << "  at start of queue";
+  }
+  if (it == pending_send_.end()) {
+    OVERNET_TRACE(DEBUG) << "  at end of queue";
+  }
+  if (chunk.slice.length() == 0 && it == pending_send_.begin()) {
+    // Skip adding zero-length chunks at the start of the queue.
+    // These are probes anyway that we've reached that point, and so
+    // there's no need to do any further work (and this simplifies later logic
+    // in the pipeline).
+    return;
+  }
+  pending_send_.emplace(
+      it, PendingSend{{std::move(chunk), state}, std::move(started)});
+
+done:
+  OVERNET_TRACE(DEBUG) << "Send queue: " << PendingSendString();
+  if (!sending_) {
+    SendNextChunk();
+  }
+}
+
+std::string DatagramStream::PendingSendString() {
+  std::ostringstream out;
+  out << '[';
+  bool first = true;
+  for (const auto& ps : pending_send_) {
+    if (!first) {
+      out << ',';
+    }
+    first = false;
+    out << ps.what.state.message_id() << '/' << ps.what.chunk;
+  }
+  return out.str();
+}
+
+void DatagramStream::SendNextChunk() {
+  ScopedModule<DatagramStream> in_dgs(this);
+  assert(close_state_ == CloseState::OPEN ||
+         close_state_ == CloseState::LOCAL_CLOSE_REQUESTED_OK ||
+         close_state_ == CloseState::DRAINING_LOCAL_CLOSED_OK);
+  assert(!sending_);
+
+  OVERNET_TRACE(DEBUG) << "SendNextChunk: pending=" << pending_send_.size();
+
+  auto first_real_pending = pending_send_.end();
+  for (auto it = pending_send_.begin(); it != pending_send_.end(); ++it) {
+    if (it->what.chunk.slice.length() > 0) {
+      first_real_pending = it;
+      break;
+    }
+    OVERNET_TRACE(DEBUG) << "Skip empty send: " << it->what.chunk;
+  }
+  pending_send_.erase(pending_send_.begin(), first_real_pending);
+  OVERNET_TRACE(DEBUG) << "SendNextChunk': pending=" << pending_send_.size();
+  if (pending_send_.empty()) {
+    OVERNET_TRACE(DEBUG) << "no need to send";
+    return;
+  }
+
+  sending_ = true;
+
+  class PullChunk {
+   public:
+    PullChunk(DatagramStream* stream, const LazySliceArgs* args)
+        : send_(Pull(stream, args)), args_(args) {
+      assert(this->stream() == stream);
+    }
+    ~PullChunk() {
+      if (!stream()->IsClosedForSending()) {
+        stream()->sending_ = false;
+        stream()->SendNextChunk();
+      }
+    }
+    Slice Finish() {
+      return MessageFragment(message_id(), std::move(send_.chunk))
+          .Write(args_->desired_border);
+    }
+
+    DatagramStream* stream() const { return send_.state.stream(); }
+    uint64_t message_id() const { return send_.state.message_id(); }
+    const ChunkAndState& chunk_and_state() const { return send_; }
+
+   private:
+    ChunkAndState send_;
+    const LazySliceArgs* const args_;
+
+    static ChunkAndState Pull(DatagramStream* stream,
+                              const LazySliceArgs* args) {
+      ScopedModule<DatagramStream> in_dgs(stream);
+      auto fst = stream->pending_send_.begin();
+      auto pending_send = std::move(fst->what);
+      stream->pending_send_.erase(fst);
+      // We should remove zero-length chunks before arriving here.
+      // Otherwise we cannot ensure that there'll be an actual chunk in the
+      // queue.
+      assert(pending_send.chunk.slice.length() != 0);
+      const auto message_id_length =
+          varint::WireSizeFor(pending_send.state.message_id());
+      OVERNET_TRACE(DEBUG) << "Format: ofs=" << pending_send.chunk.offset
+                           << " len=" << pending_send.chunk.slice.length()
+                           << " desired_border=" << args->desired_border
+                           << " max_length=" << args->max_length
+                           << " message_id_length=" << (int)message_id_length;
+      assert(args->max_length >
+             message_id_length +
+                 varint::WireSizeFor(pending_send.chunk.offset));
+      uint64_t take_len =
+          varint::MaximumLengthWithPrefix(args->max_length - message_id_length);
+      OVERNET_TRACE(DEBUG) << "TAKE " << take_len;
+      if (take_len < pending_send.chunk.slice.length()) {
+        Chunk first = pending_send.chunk.TakeUntilSliceOffset(take_len);
+        stream->SendChunk(pending_send.state, std::move(pending_send.chunk),
+                          Callback<void>::Ignored());
+        pending_send.chunk = std::move(first);
+      }
+      return pending_send;
+    }
+  };
+
+  class ReliableChunkSend final : public PacketProtocol::SendRequest {
+   public:
+    ReliableChunkSend(DatagramStream* stream) : stream_(stream) {}
+
+    Slice GenerateBytes(LazySliceArgs args) override {
+      PullChunk pc(stream_, &args);
+      sent_ = pc.chunk_and_state();
+      return pc.Finish();
+    }
+
+    void Ack(const Status& status) override {
+      if (sent_) {
+        stream_->CompleteReliable(status, std::move(sent_->state),
+                                  std::move(sent_->chunk));
+      } else if (!stream_->IsClosedForSending()) {
+        stream_->sending_ = false;
+        stream_->SendNextChunk();
+      }
+      delete this;
+    }
+
+   private:
+    DatagramStream* const stream_;
+    Optional<ChunkAndState> sent_;
+  };
+
+  class UnreliableChunkSend final : public PacketProtocol::SendRequest {
+   public:
+    UnreliableChunkSend(DatagramStream* stream) : stream_(stream) {}
+
+    Slice GenerateBytes(LazySliceArgs args) override {
+      PullChunk pc(stream_, &args);
+      sent_ = pc.chunk_and_state().state;
+      return pc.Finish();
+    }
+
+    void Ack(const Status& status) override {
+      if (sent_) {
+        stream_->CompleteUnreliable(status, std::move(*sent_));
+      } else if (!stream_->IsClosedForSending()) {
+        stream_->sending_ = false;
+        stream_->SendNextChunk();
+      }
+      delete this;
+    }
+
+   private:
+    DatagramStream* const stream_;
+    Optional<StateRef> sent_;
+  };
+
+  class TailReliableChunkSend final : public PacketProtocol::SendRequest {
+   public:
+    TailReliableChunkSend(DatagramStream* stream) : stream_(stream) {}
+
+    Slice GenerateBytes(LazySliceArgs args) override {
+      PullChunk pc(stream_, &args);
+      sent_ = pc.chunk_and_state();
+      return pc.Finish();
+    }
+
+    void Ack(const Status& status) override {
+      if (sent_) {
+        if (sent_->state.message_id() + 1 == stream_->next_message_id_) {
+          stream_->CompleteReliable(status, std::move(sent_->state),
+                                    std::move(sent_->chunk));
+        } else {
+          stream_->CompleteUnreliable(status, std::move(sent_->state));
+        }
+      } else if (!stream_->IsClosedForSending()) {
+        stream_->sending_ = false;
+        stream_->SendNextChunk();
+      }
+      delete this;
+    }
+
+   private:
+    DatagramStream* const stream_;
+    Optional<ChunkAndState> sent_;
+  };
+
+  switch (reliability_and_ordering_) {
+    case ReliabilityAndOrdering::ReliableOrdered:
+    case ReliabilityAndOrdering::ReliableUnordered:
+      packet_protocol_.Send(
+          PacketProtocol::SendRequestHdl(new ReliableChunkSend(this)));
+      break;
+    case ReliabilityAndOrdering::UnreliableOrdered:
+    case ReliabilityAndOrdering::UnreliableUnordered:
+      packet_protocol_.Send(
+          PacketProtocol::SendRequestHdl(new UnreliableChunkSend(this)));
+      break;
+    case ReliabilityAndOrdering::TailReliable:
+      packet_protocol_.Send(
+          PacketProtocol::SendRequestHdl(new TailReliableChunkSend(this)));
+      break;
+  }
+}
+
+void DatagramStream::CompleteReliable(const Status& status, StateRef state,
+                                      Chunk chunk) {
+  ScopedModule<DatagramStream> in_dgs(this);
+  OVERNET_TRACE(DEBUG) << "CompleteReliable: status=" << status
+                       << " state=" << static_cast<int>(state.state())
+                       << " stream_state=" << state.stream()->close_state_;
+  if (state.state() == SendState::CLOSED_WITH_ERROR) {
+    return;
+  }
+  if (status.code() == StatusCode::UNAVAILABLE &&
+      !state.stream()->IsClosedForSending()) {
+    // Send failed, still open, and retryable: retry.
+    SendChunk(std::move(state), std::move(chunk), Callback<void>::Ignored());
+  }
+}
+
+void DatagramStream::CompleteUnreliable(const Status& status, StateRef state) {
+  ScopedModule<DatagramStream> in_dgs(this);
+  OVERNET_TRACE(DEBUG) << "CompleteReliable: status=" << status
+                       << " state=" << static_cast<int>(state.state());
+  if (status.is_error()) {
+    state.SetClosed(status);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ReceiveOp
+
+DatagramStream::ReceiveOp::ReceiveOp(DatagramStream* stream) : stream_(stream) {
+  ScopedModule<DatagramStream> in_dgs(stream_);
+  ScopedModule<ReceiveOp> in_recv_op(this);
+  stream->unclaimed_receives_.PushBack(this);
+  stream->MaybeContinueReceive();
+}
+
+void DatagramStream::ReceiveOp::Pull(StatusOrCallback<Optional<Slice>> ready) {
+  ScopedModule<DatagramStream> in_dgs(stream_);
+  ScopedModule<ReceiveOp> in_recv_op(this);
+  OVERNET_TRACE(DEBUG) << "Pull incoming_message=" << incoming_message_;
+  if (closed_) {
+    ready(Status::Cancelled());
+    return;
+  } else if (incoming_message_ == nullptr) {
+    assert(pending_pull_all_.empty());
+    pending_pull_ = std::move(ready);
+  } else {
+    incoming_message_->Pull(std::move(ready));
+  }
+}
+
+void DatagramStream::ReceiveOp::PullAll(
+    StatusOrCallback<std::vector<Slice>> ready) {
+  ScopedModule<DatagramStream> in_dgs(stream_);
+  ScopedModule<ReceiveOp> in_recv_op(this);
+  OVERNET_TRACE(DEBUG) << "PullAll incoming_message=" << incoming_message_;
+  if (closed_) {
+    ready(Status::Cancelled());
+  } else if (incoming_message_ == nullptr) {
+    assert(pending_pull_.empty());
+    pending_pull_all_ = std::move(ready);
+  } else {
+    pending_pull_all_ = std::move(ready);
+    incoming_message_->PullAll(
+        [this](const StatusOr<std::vector<Slice>>& status) {
+          Close(status.AsStatus());
+          pending_pull_all_(status);
+        });
+  }
+}
+
+void DatagramStream::ReceiveOp::Close(const Status& status) {
+  ScopedModule<DatagramStream> in_dgs(stream_);
+  ScopedModule<ReceiveOp> in_recv_op(this);
+  OVERNET_TRACE(DEBUG) << "Close incoming_message=" << incoming_message_
+                       << " status=" << status;
+  if (closed_) {
+    assert(!stream_->unclaimed_receives_.Contains(this));
+    return;
+  }
+  closed_ = true;
+  if (incoming_message_ == nullptr) {
+    assert(stream_->unclaimed_receives_.Contains(this));
+    stream_->unclaimed_receives_.Remove(this);
+    if (!pending_pull_.empty()) {
+      if (status.is_error()) {
+        pending_pull_(status);
+      } else {
+        pending_pull_(Nothing);
+      }
+    }
+    if (!pending_pull_all_.empty()) {
+      if (status.is_error()) {
+        pending_pull_all_(status);
+      } else {
+        pending_pull_all_(std::vector<Slice>{});
+      }
+    }
+  } else {
+    assert(!stream_->unclaimed_receives_.Contains(this));
+    incoming_message_->Close(status);
+  }
+}
+
+}  // namespace overnet
diff --git a/lib/overnet/datagram_stream/datagram_stream.h b/lib/overnet/datagram_stream/datagram_stream.h
new file mode 100644
index 0000000..11f5dfe
--- /dev/null
+++ b/lib/overnet/datagram_stream/datagram_stream.h
@@ -0,0 +1,420 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <queue>  // TODO(ctiller): switch to a short queue (inlined 1-2 elems, linked list)
+#include "garnet/lib/overnet/datagram_stream/linearizer.h"
+#include "garnet/lib/overnet/datagram_stream/receive_mode.h"
+#include "garnet/lib/overnet/environment/timer.h"
+#include "garnet/lib/overnet/environment/trace.h"
+#include "garnet/lib/overnet/labels/reliability_and_ordering.h"
+#include "garnet/lib/overnet/labels/seq_num.h"
+#include "garnet/lib/overnet/packet_protocol/packet_protocol.h"
+#include "garnet/lib/overnet/routing/router.h"
+#include "garnet/lib/overnet/vocabulary/internal_list.h"
+#include "garnet/lib/overnet/vocabulary/slice.h"
+
+//#define OVERNET_TRACE_STATEREF_REFCOUNT
+
+namespace overnet {
+
+class MessageFragment {
+ public:
+  enum class Type : uint8_t { Chunk = 0, MessageCancel = 1, StreamEnd = 2 };
+
+  MessageFragment(const MessageFragment&) = delete;
+  MessageFragment& operator=(const MessageFragment&) = delete;
+
+  MessageFragment(uint64_t message, Chunk chunk)
+      : message_(message), type_(Type::Chunk) {
+    assert(message > 0);
+    new (&payload_.chunk) Chunk(std::move(chunk));
+  }
+
+  MessageFragment(MessageFragment&& other)
+      : message_(other.message_), type_(other.type_) {
+    switch (type_) {
+      case Type::Chunk:
+        new (&payload_.chunk) Chunk(std::move(other.payload_.chunk));
+        break;
+      case Type::MessageCancel:
+      case Type::StreamEnd:
+        new (&payload_.status) Status(std::move(other.payload_.status));
+        break;
+    }
+  }
+
+  MessageFragment& operator=(MessageFragment&& other) {
+    if (type_ != other.type_) {
+      this->~MessageFragment();
+      new (this) MessageFragment(std::forward<MessageFragment>(other));
+    } else {
+      switch (type_) {
+        case Type::Chunk:
+          payload_.chunk = std::move(other.payload_.chunk);
+          break;
+        case Type::MessageCancel:
+        case Type::StreamEnd:
+          payload_.status = std::move(other.payload_.status);
+          break;
+      }
+    }
+    return *this;
+  }
+
+  ~MessageFragment() {
+    switch (type_) {
+      case Type::Chunk:
+        payload_.chunk.~Chunk();
+        break;
+      case Type::MessageCancel:
+      case Type::StreamEnd:
+        payload_.status.~Status();
+        break;
+    }
+  }
+
+  static MessageFragment Abort(uint64_t message_id, Status status) {
+    return MessageFragment(message_id, Type::MessageCancel, std::move(status));
+  }
+
+  static MessageFragment EndOfStream(uint64_t last_message_id, Status status) {
+    return MessageFragment(last_message_id, Type::StreamEnd, std::move(status));
+  }
+
+  Slice Write(Border desired_border) const;
+  static StatusOr<MessageFragment> Parse(Slice incoming);
+
+  Type type() const { return type_; }
+
+  uint64_t message() const { return message_; }
+
+  const Chunk& chunk() const {
+    assert(type_ == Type::Chunk);
+    return payload_.chunk;
+  }
+  Chunk* mutable_chunk() {
+    assert(type_ == Type::Chunk);
+    return &payload_.chunk;
+  }
+
+  const Status& status() const {
+    assert(type_ == Type::MessageCancel || type_ == Type::StreamEnd);
+    return payload_.status;
+  }
+
+ private:
+  static constexpr uint8_t kFlagEndOfMessage = 0x80;
+  static constexpr uint8_t kFlagTypeMask = 0x0f;
+  static constexpr uint8_t kReservedFlags =
+      static_cast<uint8_t>(~(kFlagTypeMask | kFlagEndOfMessage));
+
+  MessageFragment(uint64_t message, Type type, Status status)
+      : message_(message), type_(type) {
+    assert(type == Type::MessageCancel || type == Type::StreamEnd);
+    assert(message != 0);
+    new (&payload_.status) Status(std::move(status));
+  }
+
+  uint64_t message_;
+  union Payload {
+    Payload() {}
+    ~Payload() {}
+
+    Chunk chunk;
+    Status status;
+  };
+  Type type_;
+  Payload payload_;
+};
+
+class DatagramStream : private Router::StreamHandler,
+                       private PacketProtocol::PacketSender {
+  class IncomingMessage {
+   public:
+    inline static constexpr auto kModule =
+        Module::DATAGRAM_STREAM_INCOMING_MESSAGE;
+
+    IncomingMessage(DatagramStream* stream)
+        : linearizer_(2 * stream->packet_protocol_.mss()) {}
+
+    void Pull(StatusOrCallback<Optional<Slice>>&& done) {
+      ScopedModule<IncomingMessage> in_im(this);
+      linearizer_.Pull(std::forward<StatusOrCallback<Optional<Slice>>>(done));
+    }
+    void PullAll(StatusOrCallback<std::vector<Slice>>&& done) {
+      ScopedModule<IncomingMessage> in_im(this);
+      linearizer_.PullAll(
+          std::forward<StatusOrCallback<std::vector<Slice>>>(done));
+    }
+
+    [[nodiscard]] bool Push(Chunk&& chunk) {
+      ScopedModule<IncomingMessage> in_im(this);
+      return linearizer_.Push(std::forward<Chunk>(chunk));
+    }
+
+    void Close(const Status& status) {
+      ScopedModule<IncomingMessage> in_im(this);
+      linearizer_.Close(status);
+    }
+
+    bool IsComplete() const { return linearizer_.IsComplete(); }
+
+    InternalListNode<IncomingMessage> incoming_link;
+
+   private:
+    Linearizer linearizer_;
+  };
+
+  struct SendState {
+    enum State : uint8_t {
+      OPEN,
+      CLOSED_OK,
+      CLOSED_WITH_ERROR,
+    };
+    State state = State::OPEN;
+    int refs = 0;
+  };
+  using SendStateMap = std::unordered_map<uint64_t, SendState>;
+  using SendStateIt = SendStateMap::iterator;
+
+  class StateRef {
+   public:
+    StateRef() = delete;
+    StateRef(DatagramStream* stream, SendStateIt op)
+        : stream_(stream), op_(op) {
+      ScopedModule<DatagramStream> in_dgs(stream_);
+#ifdef OVERNET_TRACE_STATEREF_REFCOUNT
+      OVERNET_TRACE(DEBUG) << "StateRef:" << op_->first << " ADD "
+                           << op_->second.refs << " -> "
+                           << (op_->second.refs + 1);
+#endif
+      op->second.refs++;
+    }
+    StateRef(const StateRef& other) : stream_(other.stream_), op_(other.op_) {
+      ScopedModule<DatagramStream> in_dgs(stream_);
+#ifdef OVERNET_TRACE_STATEREF_REFCOUNT
+      OVERNET_TRACE(DEBUG) << "StateRef:" << op_->first << " ADD "
+                           << op_->second.refs << " -> "
+                           << (op_->second.refs + 1);
+#endif
+      op_->second.refs++;
+    }
+    StateRef& operator=(StateRef other) {
+      other.Swap(this);
+      return *this;
+    }
+    void Swap(StateRef* other) {
+      std::swap(op_, other->op_);
+      std::swap(stream_, other->stream_);
+    }
+
+    ~StateRef() {
+      ScopedModule<DatagramStream> in_dgs(stream_);
+#ifdef OVERNET_TRACE_STATEREF_REFCOUNT
+      OVERNET_TRACE(DEBUG) << "StateRef:" << op_->first << " DEL "
+                           << op_->second.refs << " -> "
+                           << (op_->second.refs - 1);
+#endif
+      if (0 == --op_->second.refs) {
+        stream_->message_state_.erase(op_);
+        stream_->MaybeFinishClosing();
+      }
+    }
+
+    void SetClosed(const Status& status);
+
+    DatagramStream* stream() const { return stream_; }
+    SendState::State state() const { return op_->second.state; }
+    void set_state(SendState::State state) { op_->second.state = state; }
+    uint64_t message_id() const { return op_->first; }
+
+   private:
+    DatagramStream* stream_;
+    SendStateIt op_;
+  };
+
+ public:
+  inline static constexpr auto kModule = Module::DATAGRAM_STREAM;
+
+  DatagramStream(Router* router, NodeId peer,
+                 ReliabilityAndOrdering reliability_and_ordering,
+                 StreamId stream_id);
+  ~DatagramStream();
+
+  DatagramStream(const DatagramStream&) = delete;
+  DatagramStream& operator=(const DatagramStream&) = delete;
+  DatagramStream(DatagramStream&&) = delete;
+  DatagramStream& operator=(DatagramStream&&) = delete;
+
+  virtual void Close(const Status& status, Callback<void> quiesced);
+  void Close(Callback<void> quiesced) {
+    Close(Status::Ok(), std::move(quiesced));
+  }
+  void RouterClose(Callback<void> quiesced) override final {
+    Close(Status::Cancelled(), std::move(quiesced));
+  }
+
+  class SendOp final : private StateRef {
+   public:
+    inline static constexpr auto kModule = Module::DATAGRAM_STREAM_SEND_OP;
+
+    SendOp(DatagramStream* stream, uint64_t payload_length);
+    ~SendOp();
+
+    void Push(Slice item, Callback<void> started);
+    void Close(const Status& status);
+
+   private:
+    const uint64_t payload_length_;
+    uint64_t push_offset_ = 0;
+  };
+
+  class ReceiveOp final {
+    friend class DatagramStream;
+
+   public:
+    inline static constexpr auto kModule = Module::DATAGRAM_STREAM_RECV_OP;
+
+    explicit ReceiveOp(DatagramStream* stream);
+    ~ReceiveOp() {
+      if (!closed_) {
+        Close(incoming_message_ && incoming_message_->IsComplete()
+                  ? Status::Ok()
+                  : Status::Cancelled());
+      }
+      assert(closed_);
+    }
+    ReceiveOp(const ReceiveOp& rhs) = delete;
+    ReceiveOp& operator=(const ReceiveOp& rhs) = delete;
+
+    void Pull(StatusOrCallback<Optional<Slice>> ready);
+    void PullAll(StatusOrCallback<std::vector<Slice>> ready);
+    void Close(const Status& status);
+
+   private:
+    DatagramStream* const stream_;
+    IncomingMessage* incoming_message_ = nullptr;
+    bool closed_ = false;
+    StatusOrCallback<Optional<Slice>> pending_pull_;
+    StatusOrCallback<std::vector<Slice>> pending_pull_all_;
+    InternalListNode<ReceiveOp> waiting_link_;
+  };
+
+  NodeId peer() const { return peer_; }
+
+ protected:
+  // Must be called by derived classes, after construction and before any other
+  // methods.
+  void Register();
+
+ private:
+  void HandleMessage(SeqNum seq, TimeStamp received, Slice data) override final;
+  void SendPacket(SeqNum seq, LazySlice data,
+                  Callback<void> done) override final;
+  void SendCloseAndFlushQuiesced(int retry_number);
+  void FinishClosing();
+  void MaybeFinishClosing();
+
+  void MaybeContinueReceive();
+
+  void SendChunk(StateRef state, Chunk chunk, Callback<void> started);
+  void SendNextChunk();
+  void SendError(StateRef state, const Status& status);
+  void CompleteReliable(const Status& status, StateRef state, Chunk chunk);
+  void CompleteUnreliable(const Status& status, StateRef state);
+  void CancelReceives();
+  std::string PendingSendString();
+
+  Timer* const timer_;
+  Router* const router_;
+  const NodeId peer_;
+  const StreamId stream_id_;
+  const ReliabilityAndOrdering reliability_and_ordering_;
+  uint64_t next_message_id_ = 1;
+  uint64_t largest_incoming_message_id_seen_ = 0;
+  receive_mode::ParameterizedReceiveMode receive_mode_;
+  PacketProtocol packet_protocol_;
+  enum class CloseState : uint8_t {
+    OPEN,
+    LOCAL_CLOSE_REQUESTED_OK,
+    LOCAL_CLOSE_REQUESTED_WITH_ERROR,
+    REMOTE_CLOSED,
+    DRAINING_LOCAL_CLOSED_OK,
+    CLOSING_PROTOCOL,
+    CLOSED,
+  };
+  friend inline std::ostream& operator<<(std::ostream& out, CloseState state) {
+    switch (state) {
+      case CloseState::OPEN:
+        return out << "OPEN";
+      case CloseState::LOCAL_CLOSE_REQUESTED_OK:
+        return out << "LOCAL_CLOSE_REQUESTED_OK";
+      case CloseState::LOCAL_CLOSE_REQUESTED_WITH_ERROR:
+        return out << "LOCAL_CLOSE_REQUESTED_WITH_ERROR";
+      case CloseState::REMOTE_CLOSED:
+        return out << "REMOTE_CLOSED";
+      case CloseState::DRAINING_LOCAL_CLOSED_OK:
+        return out << "DRAINING_LOCAL_CLOSED_OK";
+      case CloseState::CLOSING_PROTOCOL:
+        return out << "CLOSING_PROTOCOL";
+      case CloseState::CLOSED:
+        return out << "CLOSED";
+    }
+    return out << "UNKNOWN";
+  }
+  bool IsClosedForSending() {
+    switch (close_state_) {
+      case CloseState::LOCAL_CLOSE_REQUESTED_WITH_ERROR:
+      case CloseState::REMOTE_CLOSED:
+      case CloseState::CLOSED:
+      case CloseState::CLOSING_PROTOCOL:
+        return true;
+      case CloseState::DRAINING_LOCAL_CLOSED_OK:
+      case CloseState::LOCAL_CLOSE_REQUESTED_OK:
+      case CloseState::OPEN:
+        return false;
+    }
+  }
+  CloseState close_state_ = CloseState::OPEN;
+  Optional<Status> local_close_status_;
+  struct RequestedClose {
+    uint64_t last_message_id;
+    Status status;
+    bool operator==(const RequestedClose& other) const {
+      return last_message_id == other.last_message_id &&
+             status.code() == other.status.code();
+    }
+    bool operator!=(const RequestedClose& other) const {
+      return !operator==(other);
+    }
+  };
+  Optional<RequestedClose> requested_close_;
+
+  struct ChunkAndState {
+    Chunk chunk;
+    StateRef state;
+  };
+
+  struct PendingSend {
+    ChunkAndState what;
+    Callback<void> started;
+  };
+  std::vector<PendingSend> pending_send_;
+  bool sending_ = false;
+
+  SendStateMap message_state_;
+
+  // TODO(ctiller): a custom allocator here would be worthwhile, especially one
+  // that could remove allocations for the common case of few entries.
+  std::unordered_map<uint64_t, IncomingMessage> messages_;
+  InternalList<IncomingMessage, &IncomingMessage::incoming_link>
+      unclaimed_messages_;
+  InternalList<ReceiveOp, &ReceiveOp::waiting_link_> unclaimed_receives_;
+
+  std::vector<Callback<void>> on_quiesced_;
+};
+
+}  // namespace overnet
diff --git a/lib/overnet/datagram_stream_test.cc b/lib/overnet/datagram_stream/datagram_stream_test.cc
similarity index 74%
rename from lib/overnet/datagram_stream_test.cc
rename to lib/overnet/datagram_stream/datagram_stream_test.cc
index 7113bda..573f001 100644
--- a/lib/overnet/datagram_stream_test.cc
+++ b/lib/overnet/datagram_stream/datagram_stream_test.cc
@@ -6,8 +6,8 @@
 #include <memory>
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "test_timer.h"
-#include "trace_cout.h"
+#include "garnet/lib/overnet/testing/test_timer.h"
+#include "garnet/lib/overnet/testing/trace_cout.h"
 
 using testing::_;
 using testing::Mock;
@@ -28,8 +28,8 @@
      public:
       LinkInst(MockLink* link, NodeId src, NodeId peer)
           : link_(link),
-            fake_link_metrics_(src, peer, 1, reinterpret_cast<uint64_t>(this)) {
-      }
+            fake_link_metrics_(src, peer, 1, reinterpret_cast<uint64_t>(this),
+                               true) {}
 
       void Close(Callback<void> quiesced) override {}
 
@@ -67,34 +67,44 @@
   }
 };
 
+// Wrapper that calls Register() automatically (as required by the
+// DatagramStream contract).
+class DGStream : public DatagramStream {
+ public:
+  template <class... Arg>
+  DGStream(Arg&&... args) : DatagramStream(std::forward<Arg>(args)...) {
+    Register();
+  }
+};
+
 TEST(DatagramStream, UnreliableSend) {
   StrictMock<MockLink> link;
   StrictMock<MockDoneCB> done_cb;
 
   TestTimer timer;
-  auto trace_sink = TraceCout(&timer);
+  TraceCout renderer(&timer);
+  ScopedRenderer scoped_render(&renderer);
 
-  auto router = MakeClosedPtr<Router>(&timer, trace_sink, NodeId(1), true);
+  auto router = MakeClosedPtr<Router>(&timer, NodeId(1), true);
   router->RegisterLink(link.MakeLink(NodeId(1), NodeId(2)));
   while (!router->HasRouteTo(NodeId(2))) {
     router->BlockUntilNoBackgroundUpdatesProcessing();
     timer.StepUntilNextEvent();
   }
 
-  auto ds1 = MakeClosedPtr<DatagramStream>(
-      router.get(), trace_sink, NodeId(2),
-      ReliabilityAndOrdering::UnreliableUnordered, StreamId(1));
+  auto ds1 = MakeClosedPtr<DGStream>(
+      router.get(), NodeId(2), ReliabilityAndOrdering::UnreliableUnordered,
+      StreamId(1));
 
-  auto send_op = MakeClosedPtr<DatagramStream::SendOp>(ds1.get(), 3);
   std::shared_ptr<Message> message;
   EXPECT_CALL(link, Forward(_)).WillOnce(SaveArg<0>(&message));
   // Packet will still be outstanding at destruction.
-  send_op->Push(Slice::FromContainer({1, 2, 3}));
+  DatagramStream::SendOp(ds1.get(), 3)
+      .Push(Slice::FromContainer({1, 2, 3}), Callback<void>::Ignored());
   Mock::VerifyAndClearExpectations(&link);
 
-  TimeStamp when = timer.Now();
   EXPECT_EQ(message->make_payload(LazySliceArgs{
-                0, std::numeric_limits<uint32_t>::max(), false, &when}),
+                Border::None(), std::numeric_limits<uint32_t>::max(), false}),
             Slice::FromContainer({0, 0x80, 1, 0, 1, 2, 3}));
   EXPECT_EQ(message->header.src(), NodeId(1));
   EXPECT_EQ(message->header.destinations().size(), size_t(1));
@@ -106,9 +116,10 @@
 
 TEST(DatagramStream, ReadThenRecv) {
   TestTimer timer;
-  auto trace_sink = TraceCout(&timer);
+  TraceCout renderer(&timer);
+  ScopedRenderer scoped_render(&renderer);
 
-  StrictMock<MockLink> link;
+  MockLink link;
   StrictMock<MockPullCB> pull_cb;
 
   auto expect_all_done = [&]() {
@@ -116,16 +127,16 @@
     EXPECT_TRUE(Mock::VerifyAndClearExpectations(&pull_cb));
   };
 
-  auto router = MakeClosedPtr<Router>(&timer, trace_sink, NodeId(1), true);
+  auto router = MakeClosedPtr<Router>(&timer, NodeId(1), true);
   router->RegisterLink(link.MakeLink(NodeId(1), NodeId(2)));
   while (!router->HasRouteTo(NodeId(2))) {
     router->BlockUntilNoBackgroundUpdatesProcessing();
     timer.StepUntilNextEvent();
   }
 
-  auto ds1 = MakeClosedPtr<DatagramStream>(
-      router.get(), trace_sink, NodeId(2),
-      ReliabilityAndOrdering::ReliableUnordered, StreamId(1));
+  auto ds1 = MakeClosedPtr<DGStream>(router.get(), NodeId(2),
+                                     ReliabilityAndOrdering::ReliableUnordered,
+                                     StreamId(1));
 
   router->Forward(Message{
       std::move(RoutableMessage(NodeId(2)).AddDestination(
@@ -143,16 +154,14 @@
   expect_all_done();
 
   recv_op.Close(Status::Ok());
-
-  // Stream will send a close.
-  EXPECT_CALL(link, Forward(_));
 }
 
 TEST(DatagramStream, RecvThenRead) {
   TestTimer timer;
-  auto trace_sink = TraceCout(&timer);
+  TraceCout renderer(&timer);
+  ScopedRenderer scoped_render(&renderer);
 
-  StrictMock<MockLink> link;
+  MockLink link;
   StrictMock<MockPullCB> pull_cb;
 
   auto expect_all_done = [&]() {
@@ -160,16 +169,16 @@
     EXPECT_TRUE(Mock::VerifyAndClearExpectations(&pull_cb));
   };
 
-  auto router = MakeClosedPtr<Router>(&timer, trace_sink, NodeId(1), true);
+  auto router = MakeClosedPtr<Router>(&timer, NodeId(1), true);
   router->RegisterLink(link.MakeLink(NodeId(1), NodeId(2)));
   while (!router->HasRouteTo(NodeId(2))) {
     router->BlockUntilNoBackgroundUpdatesProcessing();
     timer.StepUntilNextEvent();
   }
 
-  auto ds1 = MakeClosedPtr<DatagramStream>(
-      router.get(), trace_sink, NodeId(2),
-      ReliabilityAndOrdering::ReliableUnordered, StreamId(1));
+  auto ds1 = MakeClosedPtr<DGStream>(router.get(), NodeId(2),
+                                     ReliabilityAndOrdering::ReliableUnordered,
+                                     StreamId(1));
 
   DatagramStream::ReceiveOp recv_op(ds1.get());
 
@@ -187,9 +196,6 @@
   expect_all_done();
 
   recv_op.Close(Status::Ok());
-
-  // Stream will send a close.
-  EXPECT_CALL(link, Forward(_));
 }
 
 }  // namespace datagram_stream_tests
diff --git a/lib/overnet/linearizer.cc b/lib/overnet/datagram_stream/linearizer.cc
similarity index 79%
rename from lib/overnet/linearizer.cc
rename to lib/overnet/datagram_stream/linearizer.cc
index d7e7011..2984f16 100644
--- a/lib/overnet/linearizer.cc
+++ b/lib/overnet/datagram_stream/linearizer.cc
@@ -4,12 +4,16 @@
 
 #include "linearizer.h"
 
+#ifndef NDEBUG
+#define SCOPED_CHECK_VALID \
+  CheckValid check_valid(this, __PRETTY_FUNCTION__, __FILE__, __LINE__)
+#else
+#define SCOPED_CHECK_VALID
+#endif
+
 namespace overnet {
 
-Linearizer::Linearizer(uint64_t max_buffer, TraceSink trace_sink)
-    : max_buffer_(max_buffer),
-      trace_sink_(trace_sink.Decorate(
-          [](const std::string& msg) { return "Linearizer " + msg; })) {}
+Linearizer::Linearizer(uint64_t max_buffer) : max_buffer_(max_buffer) {}
 
 Linearizer::~Linearizer() {
   switch (read_mode_) {
@@ -27,8 +31,11 @@
   }
 }
 
-void Linearizer::ValidateInternals() const {
+void Linearizer::AssertValid(const char* marker, const char* pretty_function,
+                             const char* file, int line) const {
 #ifndef NDEBUG
+  OVERNET_TRACE(DEBUG) << "CHECKVALID:" << marker << " " << pretty_function
+                       << " @ " << file << ":" << line;
   // If closed, nothing should be pending
   if (read_mode_ == ReadMode::Closed) {
     assert(pending_push_.empty());
@@ -46,11 +53,23 @@
     assert(seen_to <= el.first);
     seen_to = el.first + el.second.length();
   }
-  // Should not exceed our buffering limits.
+  // Should not exceed our buffering limits (or there should just be one pending
+  // item on an idle read)
   if (!pending_push_.empty()) {
     auto last = std::prev(pending_push_.end());
-    assert(last->first + last->second.length() <= offset_ + max_buffer_);
+    assert(last->first + last->second.length() <= offset_ + max_buffer_ ||
+           (read_mode_ == ReadMode::Idle && pending_push_.size() == 1 &&
+            pending_push_.begin()->first == offset_));
   }
+
+  std::ostringstream rep;
+  rep << "MODE:" << read_mode_ << " LENGTH:" << length_ << " OFFSET:" << offset_
+      << " PENDING:";
+  for (const auto& pending : pending_push_) {
+    rep << "<" << pending.first << "-"
+        << (pending.first + pending.second.length()) << ">";
+  }
+  OVERNET_TRACE(DEBUG) << "OK: " << rep.str();
 #endif
 }
 
@@ -59,8 +78,7 @@
 }
 
 void Linearizer::Close(const Status& status, Callback<void> quiesced) {
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "Close " << status << " mode=" << read_mode_;
+  OVERNET_TRACE(DEBUG) << "Close " << status << " mode=" << read_mode_;
   if (status.is_ok() && !pending_push_.empty()) {
     Close(Status(StatusCode::CANCELLED, "Gaps existed at close time"));
     return;
@@ -95,24 +113,22 @@
   }
 }
 
-void Linearizer::Push(Chunk chunk) {
-  ValidateInternals();
+bool Linearizer::Push(Chunk chunk) {
+  SCOPED_CHECK_VALID;
 
   uint64_t chunk_start = chunk.offset;
   const uint64_t chunk_end = chunk_start + chunk.slice.length();
 
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "Push start=" << chunk_start << " end=" << chunk_end
-      << " end-of-message=" << chunk.end_of_message
-      << " lin-offset=" << offset_;
+  OVERNET_TRACE(DEBUG) << "Push start=" << chunk_start << " end=" << chunk_end
+                       << " end-of-message=" << chunk.end_of_message
+                       << " lin-offset=" << offset_;
 
   // Check whether the chunk is within our buffering limits
   // (if not we can reject and hope for a resend.)
-  if (chunk_end > offset_ + max_buffer_) {
-    OVERNET_TRACE(DEBUG, trace_sink_)
-        << "Push reject: past end of buffering window;"
-        << " max_buffer=" << max_buffer_;
-    return;
+  if (chunk_start > offset_ && chunk_end > offset_ + max_buffer_) {
+    OVERNET_TRACE(DEBUG) << "Push reject: past end of buffering window;"
+                         << " max_buffer=" << max_buffer_;
+    return false;
   }
 
   if (length_) {
@@ -142,12 +158,12 @@
   }
 
   if (read_mode_ == ReadMode::Closed) {
-    OVERNET_TRACE(DEBUG, trace_sink_) << "Push reject: closed";
-    return;
+    OVERNET_TRACE(DEBUG) << "Push reject: closed";
+    return false;
   }
 
   if (chunk.end_of_message && !length_) {
-    OVERNET_TRACE(DEBUG, trace_sink_) << "Push: record length";
+    OVERNET_TRACE(DEBUG) << "Push: record length";
     length_ = chunk_end;
   }
 
@@ -155,7 +171,7 @@
   // we're waiting for, and overlaps with nothing.
   if (read_mode_ == ReadMode::ReadSlice && chunk_start == offset_ &&
       (pending_push_.empty() || pending_push_.begin()->first > chunk_end)) {
-    OVERNET_TRACE(DEBUG, trace_sink_) << "Push: fast-path";
+    OVERNET_TRACE(DEBUG) << "Push: fast-path";
     offset_ += chunk.slice.length();
     auto push = std::move(ReadSliceToIdle().done);
     if (length_) {
@@ -165,7 +181,7 @@
       }
     }
     push(std::move(chunk.slice));
-    return;
+    return true;
   }
 
   // If the chunk is partially before the start of what we've delivered, we can
@@ -173,12 +189,12 @@
   // If it's wholly before, then we can discard.
   if (chunk_start < offset_) {
     if (chunk_end > offset_) {
-      OVERNET_TRACE(DEBUG, trace_sink_) << "Push: trim begin";
+      OVERNET_TRACE(DEBUG) << "Push: trim begin";
       chunk.TrimBegin(offset_ - chunk_start);
       chunk_start = chunk.offset;
     } else {
-      OVERNET_TRACE(DEBUG, trace_sink_) << "Push: all prior";
-      return;
+      OVERNET_TRACE(DEBUG) << "Push: all prior";
+      return true;
     }
   }
 
@@ -187,7 +203,7 @@
   // We break out the integration into a separate function since it has many
   // exit conditions, and we've got some common checks to do once it's finished.
   if (pending_push_.empty()) {
-    OVERNET_TRACE(DEBUG, trace_sink_) << "Push: first pending";
+    OVERNET_TRACE(DEBUG) << "Push: first pending";
     pending_push_.emplace(chunk.offset, std::move(chunk.slice));
   } else {
     IntegratePush(std::move(chunk));
@@ -204,18 +220,16 @@
       ContinueReadAll();
       break;
   }
+
+  return true;
 }
 
 void Linearizer::IntegratePush(Chunk chunk) {
   assert(!pending_push_.empty());
 
-  auto trace_sink = trace_sink_.Decorate(
-      [start = chunk.offset,
-       end = chunk.offset + chunk.slice.length()](const std::string& msg) {
-        std::ostringstream out;
-        out << "IntegratePush: start=" << start << " end=" << end << " " << msg;
-        return out.str();
-      });
+  ScopedOp op(Op::New(OpType::LINEARIZER_INTEGRATION));
+  OVERNET_TRACE(DEBUG) << "Start:" << chunk.offset
+                       << " End:" << chunk.offset + chunk.slice.length();
 
   auto lb = pending_push_.lower_bound(chunk.offset);
   if (lb != pending_push_.end() && lb->first == chunk.offset) {
@@ -223,8 +237,8 @@
     // First check whether the common bytes are the same.
     const size_t common_length =
         std::min(chunk.slice.length(), lb->second.length());
-    OVERNET_TRACE(DEBUG, trace_sink)
-        << "coincident with existing; common_length=" << common_length;
+    OVERNET_TRACE(DEBUG) << "coincident with existing; common_length="
+                         << common_length;
     if (0 != memcmp(chunk.slice.begin(), lb->second.begin(), common_length)) {
       Close(Status(StatusCode::DATA_LOSS,
                    "Linearizer received different bytes for the same span"));
@@ -247,15 +261,15 @@
     assert(before->first < chunk.offset);
     // Check to see if that chunk overlaps with this one.
     const size_t before_end = before->first + before->second.length();
-    OVERNET_TRACE(DEBUG, trace_sink)
-        << "prior chunk start=" << before->first << " end=" << before_end;
+    OVERNET_TRACE(DEBUG) << "prior chunk start=" << before->first
+                         << " end=" << before_end;
     if (before_end > chunk.offset) {
       // Prior chunk overlaps with this one.
       // First check whether the common bytes are the same.
       const size_t common_length =
           std::min(before_end - chunk.offset, uint64_t(chunk.slice.length()));
-      OVERNET_TRACE(DEBUG, trace_sink)
-          << "overlap with prior; common_length=" << common_length;
+      OVERNET_TRACE(DEBUG) << "overlap with prior; common_length="
+                           << common_length;
       if (0 != memcmp(before->second.begin() + (chunk.offset - before->first),
                       chunk.slice.begin(), common_length)) {
         Close(Status(StatusCode::DATA_LOSS,
@@ -277,15 +291,14 @@
     const auto after = lb;
     assert(after->first > chunk.offset);
     // Check to see if that chunk overlaps with this one.
-    OVERNET_TRACE(DEBUG, trace_sink)
-        << "subsequent chunk start=" << after->first
-        << " end=" << (after->first + after->second.length());
+    OVERNET_TRACE(DEBUG) << "subsequent chunk start=" << after->first
+                         << " end=" << (after->first + after->second.length());
     if (after->first < chunk.offset + chunk.slice.length()) {
       const size_t common_length =
           std::min(chunk.offset + chunk.slice.length() - after->first,
                    uint64_t(after->second.length()));
-      OVERNET_TRACE(DEBUG, trace_sink)
-          << "overlap with subsequent; common_length=" << common_length;
+      OVERNET_TRACE(DEBUG) << "overlap with subsequent; common_length="
+                           << common_length;
       if (0 != memcmp(after->second.begin(),
                       chunk.slice.begin() + (after->first - chunk.offset),
                       common_length)) {
@@ -294,7 +307,7 @@
         return;
       } else if (after->first + after->second.length() <
                  chunk.offset + chunk.slice.length()) {
-        OVERNET_TRACE(DEBUG, trace_sink) << "Split and integrate separately";
+        OVERNET_TRACE(DEBUG) << "Split and integrate separately";
         // Split chunk into two and integrate each separately
         Chunk tail = chunk;
         chunk.TrimEnd(chunk.offset + chunk.slice.length() - after->first);
@@ -310,14 +323,13 @@
   }
 
   // We now have a non-overlapping chunk that we can insert.
-  OVERNET_TRACE(DEBUG, trace_sink)
-      << "add pending start=" << chunk.offset
-      << " end=" << (chunk.offset + chunk.slice.length());
+  OVERNET_TRACE(DEBUG) << "add pending start=" << chunk.offset
+                       << " end=" << (chunk.offset + chunk.slice.length());
   pending_push_.emplace_hint(lb, chunk.offset, std::move(chunk.slice));
 }
 
 void Linearizer::Pull(StatusOrCallback<Optional<Slice>> push) {
-  ValidateInternals();
+  SCOPED_CHECK_VALID;
   switch (read_mode_) {
     case ReadMode::Closed:
       if (read_data_.closed.status.is_ok()) {
@@ -351,11 +363,10 @@
       }
     } break;
   }
-  ValidateInternals();
 }
 
 void Linearizer::PullAll(StatusOrCallback<std::vector<Slice>> push) {
-  ValidateInternals();
+  SCOPED_CHECK_VALID;
   switch (read_mode_) {
     case ReadMode::Closed:
       if (read_data_.closed.status.is_ok()) {
@@ -371,7 +382,6 @@
       IdleToReadAll(std::move(push));
       ContinueReadAll();
   }
-  ValidateInternals();
 }
 
 void Linearizer::ContinueReadAll() {
diff --git a/lib/overnet/datagram_stream/linearizer.h b/lib/overnet/datagram_stream/linearizer.h
new file mode 100644
index 0000000..fd0a6f2
--- /dev/null
+++ b/lib/overnet/datagram_stream/linearizer.h
@@ -0,0 +1,120 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <map>
+#include "garnet/lib/overnet/environment/trace.h"
+#include "garnet/lib/overnet/vocabulary/callback.h"
+#include "garnet/lib/overnet/vocabulary/optional.h"
+#include "garnet/lib/overnet/vocabulary/slice.h"
+#include "garnet/lib/overnet/vocabulary/status.h"
+
+namespace overnet {
+
+class Linearizer final {
+ public:
+  explicit Linearizer(uint64_t max_buffer);
+  ~Linearizer();
+
+  // Input interface.
+
+  // Add a new slice to the input queue.
+  // Returns true if successful, false on failure.
+  [[nodiscard]] bool Push(Chunk chunk);
+
+  // Output interface.
+  void Pull(StatusOrCallback<Optional<Slice>> ready);
+  void PullAll(StatusOrCallback<std::vector<Slice>> ready);
+
+  void Close(const Status& status, Callback<void> quiesced);
+  void Close(const Status& status);
+
+  bool IsComplete() const { return offset_ == length_; }
+
+ private:
+  void IntegratePush(Chunk chunk);
+  void AssertValid(const char* marker, const char* pretty_function,
+                   const char* file, int line) const;
+
+#ifndef NDEBUG
+  class CheckValid {
+   public:
+    CheckValid(const Linearizer* linearizer, const char* pretty_function,
+               const char* file, int line)
+        : linearizer_(linearizer),
+          pretty_function_(pretty_function),
+          file_(file),
+          line_(line) {
+      linearizer_->AssertValid("BEGIN", pretty_function_, file_, line_);
+    }
+
+    ~CheckValid() {
+      linearizer_->AssertValid("END", pretty_function_, file_, line_);
+    }
+
+   private:
+    const Linearizer* const linearizer_;
+    const char* const pretty_function_;
+    const char* const file_;
+    int line_;
+  };
+#endif
+
+  const uint64_t max_buffer_;
+  uint64_t offset_ = 0;
+  Optional<uint64_t> length_;
+  std::map<uint64_t, Slice> pending_push_;
+
+  enum class ReadMode {
+    Closed,
+    Idle,
+    ReadSlice,
+    ReadAll,
+  };
+
+  inline friend std::ostream& operator<<(std::ostream& out, ReadMode m) {
+    switch (m) {
+      case ReadMode::Closed:
+        return out << "Closed";
+      case ReadMode::Idle:
+        return out << "Idle";
+      case ReadMode::ReadSlice:
+        return out << "ReadSlice";
+      case ReadMode::ReadAll:
+        return out << "ReadAll";
+    }
+  }
+
+  struct Closed {
+    Status status;
+  };
+  struct ReadSlice {
+    StatusOrCallback<Optional<Slice>> done;
+  };
+  struct ReadAll {
+    std::vector<Slice> building;
+    StatusOrCallback<std::vector<Slice>> done;
+  };
+  union ReadData {
+    ReadData() {}
+    ~ReadData() {}
+
+    Closed closed;
+    ReadSlice read_slice;
+    ReadAll read_all;
+  };
+
+  ReadMode read_mode_ = ReadMode::Idle;
+  ReadData read_data_;
+
+  void IdleToClosed(const Status& status);
+  void IdleToReadSlice(StatusOrCallback<Optional<Slice>> done);
+  void IdleToReadAll(StatusOrCallback<std::vector<Slice>> done);
+  ReadSlice ReadSliceToIdle();
+  ReadAll ReadAllToIdle();
+  void ContinueReadAll();
+};
+
+}  // namespace overnet
diff --git a/lib/overnet/linearizer_fuzzer.cc b/lib/overnet/datagram_stream/linearizer_fuzzer.cc
similarity index 100%
rename from lib/overnet/linearizer_fuzzer.cc
rename to lib/overnet/datagram_stream/linearizer_fuzzer.cc
diff --git a/lib/overnet/linearizer_fuzzer_corpus_to_code.py b/lib/overnet/datagram_stream/linearizer_fuzzer_corpus_to_code.py
similarity index 100%
rename from lib/overnet/linearizer_fuzzer_corpus_to_code.py
rename to lib/overnet/datagram_stream/linearizer_fuzzer_corpus_to_code.py
diff --git a/lib/overnet/linearizer_fuzzer_helpers.h b/lib/overnet/datagram_stream/linearizer_fuzzer_helpers.h
similarity index 94%
rename from lib/overnet/linearizer_fuzzer_helpers.h
rename to lib/overnet/datagram_stream/linearizer_fuzzer_helpers.h
index d6cffa6..bfb2d31 100644
--- a/lib/overnet/linearizer_fuzzer_helpers.h
+++ b/lib/overnet/datagram_stream/linearizer_fuzzer_helpers.h
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include <vector>
+#include "garnet/lib/overnet/vocabulary/optional.h"
 #include "linearizer.h"
-#include "optional.h"
 
 namespace overnet {
 namespace linearizer_fuzzer {
@@ -57,8 +57,9 @@
         }
       }
     }
-    linearizer_.Push(
-        Chunk{offset, end_of_message, Slice::FromCopiedBuffer(data, length)});
+    auto ignore = [](bool) {};
+    ignore(linearizer_.Push(
+        Chunk{offset, end_of_message, Slice::FromCopiedBuffer(data, length)}));
   }
 
   void Pull() {
@@ -108,7 +109,7 @@
   StatusCode closed_status_;
   bool waiting_for_pull_ = false;
 
-  Linearizer linearizer_{kBuffer, TraceSink()};
+  Linearizer linearizer_{kBuffer};
 };
 
 }  // namespace linearizer_fuzzer
diff --git a/lib/overnet/linearizer_test.cc b/lib/overnet/datagram_stream/linearizer_test.cc
similarity index 90%
rename from lib/overnet/linearizer_test.cc
rename to lib/overnet/datagram_stream/linearizer_test.cc
index 800d12e..4756fd6 100644
--- a/lib/overnet/linearizer_test.cc
+++ b/lib/overnet/datagram_stream/linearizer_test.cc
@@ -16,6 +16,9 @@
 namespace overnet {
 namespace linearizer_test {
 
+template <class T>
+void Ignore(T x) {}
+
 class MockCallbacks {
  public:
   MOCK_METHOD1(PullDone, void(const StatusOr<Optional<Slice>>&));
@@ -28,14 +31,14 @@
   }
 };
 
-TEST(Linearizer, NoOp) { Linearizer(1024, TraceSink()); }
+TEST(Linearizer, NoOp) { Linearizer(1024); }
 
 TEST(Linearizer, Push0_Pull0) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
   // Push at offset 0 then a Pull should complete immediately
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")});
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")}));
   EXPECT_CALL(
       cb, PullDone(Property(&StatusOr<Optional<Slice>>::get,
                             Pointee(Pointee(Slice::FromStaticString("a"))))));
@@ -44,22 +47,22 @@
 
 TEST(Linearizer, Pull0_Push0) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
   // Push at offset 0 then a Pull should complete immediately
   linearizer.Pull(cb.NewPull());
   EXPECT_CALL(
       cb, PullDone(Property(&StatusOr<Optional<Slice>>::get,
                             Pointee(Pointee(Slice::FromStaticString("a"))))));
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")});
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")}));
 }
 
 TEST(Linearizer, Push0_Push1_Pull0_Pull1) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")});
-  linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")});
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")}));
+  Ignore(linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")}));
 
   EXPECT_CALL(
       cb, PullDone(Property(&StatusOr<Optional<Slice>>::get,
@@ -75,10 +78,10 @@
 
 TEST(Linearizer, Push1_Push0_Pull0_Pull1) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
-  linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")});
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")});
+  Ignore(linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")}));
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")}));
 
   EXPECT_CALL(
       cb, PullDone(Property(&StatusOr<Optional<Slice>>::get,
@@ -94,9 +97,9 @@
 
 TEST(Linearizer, Push0_Pull0_Push1_Pull1) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")});
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")}));
 
   EXPECT_CALL(
       cb, PullDone(Property(&StatusOr<Optional<Slice>>::get,
@@ -104,7 +107,7 @@
   linearizer.Pull(cb.NewPull());
   Mock::VerifyAndClearExpectations(&cb);
 
-  linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")});
+  Ignore(linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")}));
   EXPECT_CALL(
       cb, PullDone(Property(&StatusOr<Optional<Slice>>::get,
                             Pointee(Pointee(Slice::FromStaticString("b"))))));
@@ -113,15 +116,15 @@
 
 TEST(Linearizer, Push1_Pull0_Push0_Pull1) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
-  linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")});
+  Ignore(linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")}));
   linearizer.Pull(cb.NewPull());
 
   EXPECT_CALL(
       cb, PullDone(Property(&StatusOr<Optional<Slice>>::get,
                             Pointee(Pointee(Slice::FromStaticString("a"))))));
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")});
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")}));
   Mock::VerifyAndClearExpectations(&cb);
 
   EXPECT_CALL(
@@ -132,15 +135,15 @@
 
 TEST(Linearizer, Pull0_Push1_Push0_Pull1) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
   linearizer.Pull(cb.NewPull());
-  linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")});
+  Ignore(linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")}));
 
   EXPECT_CALL(
       cb, PullDone(Property(&StatusOr<Optional<Slice>>::get,
                             Pointee(Pointee(Slice::FromStaticString("a"))))));
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")});
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")}));
   Mock::VerifyAndClearExpectations(&cb);
 
   EXPECT_CALL(
@@ -151,11 +154,11 @@
 
 TEST(Linearizer, Push0_Push0_Pull0) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")});
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")}));
 
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")});
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")}));
   Mock::VerifyAndClearExpectations(&cb);
 
   EXPECT_CALL(
@@ -166,10 +169,10 @@
 
 TEST(Linearizer, Push0_PushBad0_Pull0) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")});
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("b")});
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")}));
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("b")}));
   Mock::VerifyAndClearExpectations(&cb);
 
   EXPECT_CALL(cb, PullDone(Property(&StatusOr<Optional<Slice>>::is_ok, false)));
@@ -178,10 +181,10 @@
 
 TEST(Linearizer, Push1_Push01_Pull0_Pull1) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
-  linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")});
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("ab")});
+  Ignore(linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")}));
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("ab")}));
 
   EXPECT_CALL(
       cb, PullDone(Property(&StatusOr<Optional<Slice>>::get,
@@ -197,11 +200,11 @@
 
 TEST(Linearizer, Push01_Push1_Pull0_Pull1) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("ab")});
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("ab")}));
 
-  linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")});
+  Ignore(linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")}));
   Mock::VerifyAndClearExpectations(&cb);
 
   EXPECT_CALL(
@@ -212,10 +215,10 @@
 
 TEST(Linearizer, Push12_Push01_Pull0_Pull1_Pull2) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
-  linearizer.Push(Chunk{1, false, Slice::FromStaticString("bc")});
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("ab")});
+  Ignore(linearizer.Push(Chunk{1, false, Slice::FromStaticString("bc")}));
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("ab")}));
 
   EXPECT_CALL(
       cb, PullDone(Property(&StatusOr<Optional<Slice>>::get,
@@ -231,10 +234,10 @@
 
 TEST(Linearizer, Push01_Push12_Pull0_Pull1_Pull2) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("ab")});
-  linearizer.Push(Chunk{1, false, Slice::FromStaticString("bc")});
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("ab")}));
+  Ignore(linearizer.Push(Chunk{1, false, Slice::FromStaticString("bc")}));
 
   EXPECT_CALL(
       cb, PullDone(Property(&StatusOr<Optional<Slice>>::get,
@@ -250,19 +253,19 @@
 
 TEST(Linearizer, Push_Close) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")});
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("a")}));
 
   linearizer.Close(Status::Ok());
 }
 
 TEST(Linearizer, Push1_Push012_Pull0_Pull1_Pull2) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
-  linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")});
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("abc")});
+  Ignore(linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")}));
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("abc")}));
 
   EXPECT_CALL(
       cb, PullDone(Property(&StatusOr<Optional<Slice>>::get,
@@ -284,10 +287,10 @@
 
 TEST(Linearizer, Push012_Push1_Pull0) {
   StrictMock<MockCallbacks> cb;
-  Linearizer linearizer(128, TraceSink());
+  Linearizer linearizer(128);
 
-  linearizer.Push(Chunk{0, false, Slice::FromStaticString("abc")});
-  linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")});
+  Ignore(linearizer.Push(Chunk{0, false, Slice::FromStaticString("abc")}));
+  Ignore(linearizer.Push(Chunk{1, false, Slice::FromStaticString("b")}));
   Mock::VerifyAndClearExpectations(&cb);
 
   EXPECT_CALL(
diff --git a/lib/overnet/receive_mode.cc b/lib/overnet/datagram_stream/receive_mode.cc
similarity index 100%
rename from lib/overnet/receive_mode.cc
rename to lib/overnet/datagram_stream/receive_mode.cc
diff --git a/lib/overnet/receive_mode.h b/lib/overnet/datagram_stream/receive_mode.h
similarity index 95%
rename from lib/overnet/receive_mode.h
rename to lib/overnet/datagram_stream/receive_mode.h
index eef4dde..9734b4f 100644
--- a/lib/overnet/receive_mode.h
+++ b/lib/overnet/datagram_stream/receive_mode.h
@@ -7,11 +7,10 @@
 #include <bitset>
 #include <map>
 #include <unordered_map>
-#include "ack_frame.h"
-#include "callback.h"
-#include "optional.h"
-#include "reliability_and_ordering.h"
-#include "status.h"
+#include "garnet/lib/overnet/labels/reliability_and_ordering.h"
+#include "garnet/lib/overnet/vocabulary/callback.h"
+#include "garnet/lib/overnet/vocabulary/optional.h"
+#include "garnet/lib/overnet/vocabulary/status.h"
 
 namespace overnet {
 namespace receive_mode {
diff --git a/lib/overnet/receive_mode_fuzzer.cc b/lib/overnet/datagram_stream/receive_mode_fuzzer.cc
similarity index 100%
rename from lib/overnet/receive_mode_fuzzer.cc
rename to lib/overnet/datagram_stream/receive_mode_fuzzer.cc
diff --git a/lib/overnet/receive_mode_fuzzer_corpus_to_code.py b/lib/overnet/datagram_stream/receive_mode_fuzzer_corpus_to_code.py
similarity index 100%
rename from lib/overnet/receive_mode_fuzzer_corpus_to_code.py
rename to lib/overnet/datagram_stream/receive_mode_fuzzer_corpus_to_code.py
diff --git a/lib/overnet/receive_mode_fuzzer_helpers.h b/lib/overnet/datagram_stream/receive_mode_fuzzer_helpers.h
similarity index 100%
rename from lib/overnet/receive_mode_fuzzer_helpers.h
rename to lib/overnet/datagram_stream/receive_mode_fuzzer_helpers.h
diff --git a/lib/overnet/receive_mode_test.cc b/lib/overnet/datagram_stream/receive_mode_test.cc
similarity index 100%
rename from lib/overnet/receive_mode_test.cc
rename to lib/overnet/datagram_stream/receive_mode_test.cc
diff --git a/lib/overnet/endpoint/BUILD.gn b/lib/overnet/endpoint/BUILD.gn
new file mode 100644
index 0000000..b77f841
--- /dev/null
+++ b/lib/overnet/endpoint/BUILD.gn
@@ -0,0 +1,64 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+###############################################################################
+# AGGREGATE LIBRARIES
+
+source_set("lib") {
+  public_deps = [
+    ":message_builder",
+    ":router_endpoint",
+  ]
+}
+
+source_set("tests") {
+  testonly = true
+  deps = [
+    ":router_endpoint_integration_test",
+  ]
+}
+
+###############################################################################
+
+# message_builder
+source_set("message_builder") {
+  sources = [
+    "message_builder.cc",
+    "message_builder.h",
+  ]
+  deps = [
+    ":router_endpoint",
+  ]
+}
+
+# router_endpoint
+source_set("router_endpoint") {
+  sources = [
+    "router_endpoint.cc",
+    "router_endpoint.h",
+  ]
+  deps = [
+    "//garnet/lib/overnet/datagram_stream",
+    "//garnet/lib/overnet/labels:reliability_and_ordering",
+    "//garnet/lib/overnet/protocol:fork_frame",
+    "//garnet/lib/overnet/routing:router",
+    "//garnet/lib/overnet/vocabulary:manual_constructor",
+    "//garnet/lib/overnet/vocabulary:optional",
+    "//garnet/lib/overnet/vocabulary:slice",
+  ]
+}
+
+source_set("router_endpoint_integration_test") {
+  testonly = true
+  sources = [
+    "router_endpoint_integration_test.cc",
+  ]
+  deps = [
+    ":router_endpoint",
+    "//garnet/lib/overnet/links:packet_link",
+    "//garnet/lib/overnet/testing:test_timer",
+    "//garnet/lib/overnet/testing:trace_cout",
+    "//third_party/googletest:gtest",
+  ]
+}
diff --git a/lib/overnet/endpoint/message_builder.cc b/lib/overnet/endpoint/message_builder.cc
new file mode 100644
index 0000000..4ce81c7
--- /dev/null
+++ b/lib/overnet/endpoint/message_builder.cc
@@ -0,0 +1,191 @@
+// Copyright 2018 The Fuchsia 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 "message_builder.h"
+namespace overnet {
+
+namespace {
+static const uint8_t kFirstRequiredParse = 1;
+static const uint8_t kFirstSkippableParse = 65;
+static const uint8_t kFirstHandle = 128;
+
+enum class MessageFragmentType : uint8_t {
+  kTxId = 1,
+  kOrdinal = 2,
+  kBody = 127,
+  kChannel = 128,
+};
+}  // namespace
+
+StatusOr<RouterEndpoint::NewStream> MessageWireEncoder::AppendChannelHandle(
+    Introduction introduction) {
+  auto fork_status =
+      stream_->Fork(ReliabilityAndOrdering::ReliableOrdered, Introduction());
+  if (fork_status.is_error()) {
+    return fork_status.AsStatus();
+  }
+  auto fork_frame = fork_status->fork_frame.Write(Border::None());
+  auto fork_frame_len = fork_frame.length();
+  auto fork_frame_len_len = varint::WireSizeFor(fork_frame_len);
+  tail_.emplace_back(Slice::WithInitializer(
+      1 + fork_frame_len_len + fork_frame_len, [&](uint8_t* p) {
+        *p++ = static_cast<uint8_t>(MessageFragmentType::kChannel);
+        p = varint::Write(fork_frame_len, fork_frame_len_len, p);
+        memcpy(p, fork_frame.begin(), fork_frame_len);
+      }));
+  return std::move(fork_status->new_stream);
+}
+
+Slice MessageWireEncoder::Write(Border desired_border) const {
+  // Wire format:
+  // (1-byte fragment id, fragment length varint, fragment data)*
+  // fragment id's must be in ordinal order (except for handles, which must be
+  // last and be placed in the order which they should be appended)
+
+  const auto txid_len = txid_ ? varint::WireSizeFor(txid_) : 0;
+  const auto txid_len_len = txid_ ? varint::WireSizeFor(txid_len) : 0;
+  assert(ordinal_ != 0);
+  const auto ordinal_len = varint::WireSizeFor(ordinal_);
+  const auto ordinal_len_len = varint::WireSizeFor(ordinal_len);
+  const auto body_len = body_.length();
+  const auto body_len_len = varint::WireSizeFor(body_len);
+
+  const auto message_length_without_tail =
+      // space for txid
+      (txid_ ? 1 + txid_len_len + txid_len : 0) +
+      // space for ordinal
+      1 + ordinal_len_len + ordinal_len +
+      // space for body
+      1 + body_len_len + body_len;
+
+  return Slice::Join(
+             tail_.begin(), tail_.end(),
+             desired_border.WithAddedPrefix(message_length_without_tail))
+      .WithPrefix(message_length_without_tail, [&](uint8_t* const data) {
+        uint8_t* p = data;
+        if (txid_) {
+          *p++ = static_cast<uint8_t>(MessageFragmentType::kTxId);
+          p = varint::Write(txid_len, txid_len_len, p);
+          p = varint::Write(txid_, txid_len, p);
+        }
+        *p++ = static_cast<uint8_t>(MessageFragmentType::kOrdinal);
+        p = varint::Write(ordinal_len_len, ordinal_len, p);
+        p = varint::Write(ordinal_, ordinal_len, p);
+        *p++ = static_cast<uint8_t>(MessageFragmentType::kBody);
+        p = varint::Write(body_len, body_len_len, p);
+        memcpy(p, body_.begin(), body_len);
+        p += body_len;
+        assert(p == data + message_length_without_tail);
+      });
+}
+
+Status ParseMessageInto(Slice slice, NodeId peer,
+                        RouterEndpoint* router_endpoint,
+                        MessageReceiver* builder) {
+  const uint8_t* p = slice.begin();
+  const uint8_t* end = slice.end();
+
+  MessageFragmentType largest_fragment_id_seen =
+      static_cast<MessageFragmentType>(0);
+
+  while (p != end) {
+    const MessageFragmentType fragment_type =
+        static_cast<MessageFragmentType>(*p++);
+    if (fragment_type >= static_cast<MessageFragmentType>(kFirstHandle)) {
+      largest_fragment_id_seen = static_cast<MessageFragmentType>(kFirstHandle);
+    } else {
+      if (fragment_type <= largest_fragment_id_seen) {
+        return Status(StatusCode::FAILED_PRECONDITION,
+                      "Message fragments must be written in ascending order of "
+                      "fragment ordinal");
+      }
+      largest_fragment_id_seen = fragment_type;
+    }
+    uint64_t fragment_length;
+    if (!varint::Read(&p, end, &fragment_length)) {
+      return Status(StatusCode::FAILED_PRECONDITION,
+                    "Failed to read message fragment length");
+    }
+    if (fragment_length > uint64_t(p - end)) {
+      return Status(StatusCode::FAILED_PRECONDITION,
+                    "Fragment length is longer than total message");
+    }
+    const uint8_t* next_fragment = p + fragment_length;
+    switch (fragment_type) {
+      case MessageFragmentType::kTxId: {
+        uint64_t txid;
+        if (!varint::Read(&p, end, &txid)) {
+          return Status(StatusCode::FAILED_PRECONDITION,
+                        "Failed to parse txid");
+        }
+        if (txid >= 0x80000000u) {
+          return Status(StatusCode::FAILED_PRECONDITION, "Txid out of range");
+        }
+        auto set_status = builder->SetTransactionId(txid);
+        if (set_status.is_error()) {
+          return set_status;
+        }
+      } break;
+      case MessageFragmentType::kOrdinal: {
+        uint64_t ordinal;
+        if (!varint::Read(&p, end, &ordinal)) {
+          return Status(StatusCode::FAILED_PRECONDITION,
+                        "Failed to parse ordinal");
+        }
+        if (ordinal > std::numeric_limits<uint32_t>::max() || ordinal == 0) {
+          return Status(StatusCode::FAILED_PRECONDITION,
+                        "Ordinal out of range");
+        }
+        auto set_status = builder->SetOrdinal(ordinal);
+        if (set_status.is_error()) {
+          return set_status;
+        }
+      } break;
+      case MessageFragmentType::kBody: {
+        auto set_status =
+            builder->SetBody(slice.FromPointer(p).ToOffset(fragment_length));
+        if (set_status.is_error()) {
+          return set_status;
+        }
+        p = next_fragment;
+      } break;
+      case MessageFragmentType::kChannel: {
+        auto fork_frame =
+            ForkFrame::Parse(slice.FromPointer(p).ToOffset(fragment_length));
+        if (fork_frame.is_error()) {
+          return fork_frame.AsStatus();
+        }
+        auto intro =
+            router_endpoint->UnwrapForkFrame(peer, std::move(*fork_frame));
+        auto append_status = builder->AppendChannelHandle(std::move(intro));
+        if (append_status.is_error()) {
+          return append_status;
+        }
+        p = next_fragment;
+      } break;
+      default: {
+        const uint8_t type_byte = static_cast<uint8_t>(fragment_type);
+        if (type_byte >= kFirstRequiredParse &&
+            type_byte < kFirstSkippableParse) {
+          return Status(StatusCode::FAILED_PRECONDITION,
+                        "Failed to parse a fragment that is required: "
+                        "version mismatch?");
+        } else {
+          if (type_byte >= kFirstHandle) {
+            auto set_status = builder->AppendUnknownHandle();
+            if (set_status.is_error()) {
+              return set_status;
+            }
+          }
+          p = next_fragment;
+        }
+      } break;
+    }
+    assert(p == next_fragment);
+    p = next_fragment;
+  }
+  return Status::Ok();
+}
+
+}  // namespace overnet
diff --git a/lib/overnet/endpoint/message_builder.h b/lib/overnet/endpoint/message_builder.h
new file mode 100644
index 0000000..8cf7f91
--- /dev/null
+++ b/lib/overnet/endpoint/message_builder.h
@@ -0,0 +1,80 @@
+// Copyright 2018 The Fuchsia 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 "garnet/lib/overnet/endpoint/router_endpoint.h"
+
+namespace overnet {
+
+// Messages contain:
+// - a prelude, indicating which message type is contained
+// - text, usually FIDL bytes representing the content of a message
+// - proxied handles: on fuchsia, these are zircon handles that overnet
+//   maintains a proxy for
+//
+// Utilities in this file assist in translating between these rich messages
+// and overnet message bodies (which are simply slices).
+
+// Target for building an outgoing message (to the network).
+class MessageSender {
+ public:
+  virtual Status SetTransactionId(uint32_t txid) = 0;
+  virtual Status SetOrdinal(uint32_t ordinal) = 0;
+  virtual Status SetBody(Slice body) = 0;
+  virtual Status AppendUnknownHandle() = 0;
+  virtual StatusOr<RouterEndpoint::NewStream> AppendChannelHandle(
+      Introduction introduction) = 0;
+};
+
+// Target for building an incoming message (from the network).
+class MessageReceiver {
+ public:
+  virtual Status SetTransactionId(uint32_t txid) = 0;
+  virtual Status SetOrdinal(uint32_t ordinal) = 0;
+  virtual Status SetBody(Slice body) = 0;
+  virtual Status AppendUnknownHandle() = 0;
+  virtual Status AppendChannelHandle(
+      RouterEndpoint::ReceivedIntroduction stream) = 0;
+};
+
+// Concrete implementation of a MessageSender that creates a Slice that can be
+// interpreted by ParseMessageInto.
+class MessageWireEncoder final : public MessageSender {
+ public:
+  MessageWireEncoder(RouterEndpoint::Stream* stream) : stream_(stream) {}
+
+  Status SetTransactionId(uint32_t txid) override {
+    txid_ = txid;
+    return Status::Ok();
+  }
+  Status SetOrdinal(uint32_t ordinal) override {
+    ordinal_ = ordinal;
+    return Status::Ok();
+  }
+  Status SetBody(Slice body) override {
+    body_ = std::move(body);
+    return Status::Ok();
+  }
+  Status AppendUnknownHandle() override {
+    return Status(StatusCode::FAILED_PRECONDITION,
+                  "Unknown handle types not supported for encoding");
+  }
+  StatusOr<RouterEndpoint::NewStream> AppendChannelHandle(
+      Introduction introduction) override;
+
+  Slice Write(Border desired_border) const;
+
+ private:
+  RouterEndpoint::Stream* const stream_;
+
+  uint32_t txid_ = 0;
+  uint32_t ordinal_ = 0;
+  Slice body_;
+  std::vector<Slice> tail_;
+};
+
+Status ParseMessageInto(Slice slice, NodeId peer,
+                        RouterEndpoint* router_endpoint,
+                        MessageReceiver* builder);
+
+}  // namespace overnet
diff --git a/lib/overnet/endpoint/router_endpoint.cc b/lib/overnet/endpoint/router_endpoint.cc
new file mode 100644
index 0000000..acf36c2
--- /dev/null
+++ b/lib/overnet/endpoint/router_endpoint.cc
@@ -0,0 +1,405 @@
+// Copyright 2018 The Fuchsia 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 "router_endpoint.h"
+#include <iostream>
+#include <memory>
+#include "garnet/lib/overnet/protocol/fork_frame.h"
+
+namespace overnet {
+
+static const auto kOvernetSystemNamespace =
+    Slice::FromStaticString("fuchsia.overnet.system.");
+static const auto kOvernetGossipService =
+    Slice::FromStaticString("fuchsia.overnet.system.gossip");
+
+void RouterEndpoint::NewStream::Fail(const Status& status) {
+  auto* s = new Stream(std::move(*this));
+  s->Close(status, [s] { delete s; });
+}
+
+RouterEndpoint::RouterEndpoint(Timer* timer, NodeId node_id,
+                               bool allow_non_determinism)
+    : Router(timer, node_id, allow_non_determinism) {
+  StartGossipTimer();
+}
+
+RouterEndpoint::~RouterEndpoint() { assert(connection_streams_.empty()); }
+
+void RouterEndpoint::StartGossipTimer() {
+  Timer* timer = this->timer();
+  gossip_timer_.Reset(
+      timer, timer->Now() + gossip_interval_, [this](const Status& status) {
+        if (status.is_error())
+          return;
+        auto node = SelectGossipPeer();
+        if (!node) {
+          gossip_interval_ =
+              std::min(3 * gossip_interval_ / 2, TimeDelta::FromMinutes(30));
+          StartGossipTimer();
+        } else {
+          gossip_interval_ = InitialGossipInterval();
+          SendGossipTo(*node, [this] {
+            if (!closing_) {
+              StartGossipTimer();
+            }
+          });
+        }
+      });
+}
+
+void RouterEndpoint::SendGossipTo(NodeId target, Callback<void> done) {
+  // Are we still gossiping?
+  if (!gossip_timer_.get()) {
+    return;
+  }
+  auto con = connection_streams_.find(target);
+  if (con == connection_streams_.end()) {
+    return;
+  }
+  auto* stream = con->second.GossipStream();
+  if (stream == nullptr) {
+    return;
+  }
+  auto slice = WriteGossipUpdate(Border::None(), target);
+  OVERNET_TRACE(DEBUG) << "SEND_GOSSIP_TO:" << target << " " << slice;
+  Stream::SendOp(stream, slice.length()).Push(slice, std::move(done));
+}
+
+void RouterEndpoint::Close(Callback<void> done) {
+  closing_ = true;
+  gossip_timer_.Reset();
+  if (connection_streams_.empty()) {
+    Router::Close(std::move(done));
+    return;
+  }
+  auto it = connection_streams_.begin();
+  OVERNET_TRACE(INFO) << "Closing peer " << it->first;
+  Callback<void> after_close(
+      ALLOCATED_CALLBACK, [this, it, done = std::move(done)]() mutable {
+        OVERNET_TRACE(INFO) << "Closed peer " << it->first;
+        connection_streams_.erase(it);
+        Close(std::move(done));
+      });
+  it->second.Close(Status::Cancelled(), std::move(after_close));
+}
+
+void RouterEndpoint::RegisterPeer(NodeId peer) {
+  GetOrCreateConnectionStream(peer);
+}
+
+RouterEndpoint::ConnectionStream* RouterEndpoint::GetOrCreateConnectionStream(
+    NodeId peer) {
+  assert(peer != node_id());
+  auto it = connection_streams_.find(peer);
+  if (it != connection_streams_.end()) {
+    return &it->second;
+  }
+  OVERNET_TRACE(DEBUG) << "Creating connection stream for peer " << peer;
+  auto* stream =
+      &connection_streams_
+           .emplace(std::piecewise_construct, std::forward_as_tuple(peer),
+                    std::forward_as_tuple(this, peer))
+           .first->second;
+  stream->Register();
+  return stream;
+}
+
+RouterEndpoint::Stream::Stream(NewStream introduction)
+    : DatagramStream(introduction.creator_, introduction.peer_,
+                     introduction.reliability_and_ordering_,
+                     introduction.stream_id_) {
+  auto it = introduction.creator_->connection_streams_.find(introduction.peer_);
+  if (it == introduction.creator_->connection_streams_.end()) {
+    OVERNET_TRACE(DEBUG) << "Failed to find connection " << introduction.peer_;
+    Close(Status(StatusCode::FAILED_PRECONDITION,
+                 "Connection closed before stream creation"),
+          Callback<void>::Ignored());
+  } else {
+    connection_stream_ = &it->second;
+    connection_stream_->forked_streams_.PushBack(this);
+  }
+  introduction.creator_ = nullptr;
+  Register();
+}
+
+void RouterEndpoint::Stream::Close(const Status& status,
+                                   Callback<void> quiesced) {
+  if (connection_stream_ != nullptr) {
+    connection_stream_->forked_streams_.Remove(this);
+    connection_stream_ = nullptr;
+  }
+  DatagramStream::Close(status, std::move(quiesced));
+}
+
+RouterEndpoint::ConnectionStream::ConnectionStream(RouterEndpoint* endpoint,
+                                                   NodeId peer)
+    : DatagramStream(endpoint, peer, ReliabilityAndOrdering::ReliableUnordered,
+                     StreamId(0)),
+      endpoint_(endpoint),
+      next_stream_id_(peer < endpoint->node_id() ? 2 : 1) {
+  BeginForkRead();
+}
+
+RouterEndpoint::ConnectionStream::~ConnectionStream() {
+  if (fork_read_state_ == ReadState::Reading) {
+    fork_read_->Close(Status::Cancelled());
+  }
+  assert(fork_read_state_ == ReadState::Stopped);
+  fork_read_.Destroy();
+  if (gossip_read_state_ == ReadState::Reading) {
+    gossip_read_->Close(Status::Cancelled());
+  }
+  if (gossip_read_state_ != ReadState::Waiting) {
+    assert(gossip_read_state_ == ReadState::Stopped);
+    gossip_read_.Destroy();
+  }
+}
+
+void RouterEndpoint::ConnectionStream::BeginGossipRead() {
+  OVERNET_TRACE(DEBUG) << "BEGIN_GOSSIP_READ";
+  gossip_read_state_ = ReadState::Reading;
+  gossip_read_.Init(gossip_stream_.get());
+  gossip_read_->PullAll(StatusOrCallback<std::vector<Slice>>(
+      [this](StatusOr<std::vector<Slice>>&& read_status) {
+        OVERNET_TRACE(DEBUG) << "GOSSIP_READ:" << read_status;
+        assert(gossip_read_state_ == ReadState::Reading);
+        if (read_status.is_error()) {
+          gossip_read_state_ = ReadState::Stopped;
+          Close(read_status.AsStatus(), Callback<void>::Ignored());
+          return;
+        } else if (read_status->size() == 0) {
+          gossip_read_state_ = ReadState::Stopped;
+          Close(Status::Ok(), Callback<void>::Ignored());
+          return;
+        }
+        auto apply_status = endpoint_->ApplyGossipUpdate(
+            Slice::Join(read_status->begin(), read_status->end()), peer());
+        if (apply_status.is_error()) {
+          gossip_read_state_ = ReadState::Stopped;
+          Close(apply_status, Callback<void>::Ignored());
+          return;
+        }
+        gossip_read_.Destroy();
+        gossip_read_state_ = ReadState::Waiting;
+        BeginGossipRead();
+      }));
+}
+
+void RouterEndpoint::ConnectionStream::BeginForkRead() {
+  fork_read_state_ = ReadState::Reading;
+  fork_read_.Init(this);
+  fork_read_->PullAll(StatusOrCallback<std::vector<Slice>>(
+      [this](StatusOr<std::vector<Slice>>&& read_status) {
+        assert(fork_read_state_ == ReadState::Reading);
+        if (read_status.is_error()) {
+          fork_read_state_ = ReadState::Stopped;
+          Close(read_status.AsStatus(), Callback<void>::Ignored());
+          return;
+        } else if (read_status->size() == 0) {
+          fork_read_state_ = ReadState::Stopped;
+          Close(Status::Ok(), Callback<void>::Ignored());
+          return;
+        }
+        auto fork_frame_status = ForkFrame::Parse(
+            Slice::Join(read_status->begin(), read_status->end()));
+        if (fork_frame_status.is_error()) {
+          fork_read_state_ = ReadState::Stopped;
+          Close(fork_frame_status.AsStatus(), Callback<void>::Ignored());
+          return;
+        }
+        const auto& svc =
+            fork_frame_status->introduction()[Introduction::Key::ServiceName];
+        if (svc.has_value() && svc->StartsWith(kOvernetSystemNamespace)) {
+          enum class SystemService {
+            NO_IDEA,
+            GOSSIP,
+          };
+          auto svc_type = SystemService::NO_IDEA;
+          if (svc == kOvernetGossipService) {
+            svc_type = SystemService::GOSSIP;
+          }
+          // svc is no longer valid after this line
+          auto received_intro =
+              endpoint_->UnwrapForkFrame(peer(), std::move(*fork_frame_status));
+          switch (svc_type) {
+            case SystemService::NO_IDEA:
+              received_intro.new_stream.Fail(
+                  Status(StatusCode::FAILED_PRECONDITION, "Unknown service"));
+              break;
+            case SystemService::GOSSIP:
+              if (IsGossipStreamInitiator()) {
+                received_intro.new_stream.Fail(
+                    Status(StatusCode::FAILED_PRECONDITION,
+                           "Not gossip stream initiator"));
+              } else if (gossip_stream_) {
+                received_intro.new_stream.Fail(
+                    Status(StatusCode::FAILED_PRECONDITION,
+                           "Gossip channel already exists"));
+              } else {
+                InstantiateGossipStream(std::move(received_intro.new_stream));
+              }
+              break;
+          }
+          fork_read_.Destroy();
+          fork_read_state_ = ReadState::Waiting;
+          BeginForkRead();
+        } else {
+          fork_frame_.Init(std::move(*fork_frame_status));
+          endpoint_->incoming_forks_.PushBack(this);
+          fork_read_.Destroy();
+          fork_read_state_ = ReadState::Waiting;
+          if (this == endpoint_->incoming_forks_.Front()) {
+            endpoint_->MaybeContinueIncomingForks();
+          }
+        }
+      }));
+}
+
+void RouterEndpoint::SendIntro(NodeId peer,
+                               ReliabilityAndOrdering reliability_and_ordering,
+                               Introduction introduction,
+                               StatusOrCallback<NewStream> new_stream_ready) {
+  GetOrCreateConnectionStream(peer)->Fork(reliability_and_ordering,
+                                          std::move(introduction),
+                                          std::move(new_stream_ready));
+}
+
+StatusOr<RouterEndpoint::OutgoingFork> RouterEndpoint::Stream::Fork(
+    ReliabilityAndOrdering reliability_and_ordering,
+    Introduction introduction) {
+  if (connection_stream_ == nullptr) {
+    return StatusOr<OutgoingFork>(StatusCode::FAILED_PRECONDITION,
+                                  "Closed stream");
+  }
+  return connection_stream_->MakeFork(reliability_and_ordering,
+                                      std::move(introduction));
+}
+
+void RouterEndpoint::ConnectionStream::Close(const Status& status,
+                                             Callback<void> quiesced) {
+  if (status.is_error()) {
+    OVERNET_TRACE(ERROR) << "Connection to " << peer()
+                         << " closed with error: " << status;
+  }
+  if (gossip_read_state_ == ReadState::Reading) {
+    gossip_read_->Close(status);
+  }
+  if (gossip_stream_) {
+    gossip_stream_->Close(status, Callback<void>::Ignored());
+  }
+  gossip_stream_.reset();
+  if (!closing_status_) {
+    closing_status_.Reset(status);
+  }
+  if (forked_streams_.Empty()) {
+    DatagramStream::Close(status, std::move(quiesced));
+  } else {
+    forked_streams_.Front()->Close(
+        status,
+        Callback<void>(ALLOCATED_CALLBACK,
+                       [this, status, quiesced{std::move(quiesced)}]() mutable {
+                         this->Close(status, std::move(quiesced));
+                       }));
+  }
+}
+
+RouterEndpoint::Stream* RouterEndpoint::ConnectionStream::GossipStream() {
+  if (gossip_stream_ == nullptr && !closing_status_.has_value() &&
+      IsGossipStreamInitiator() && !forking_gossip_stream_) {
+    forking_gossip_stream_ = true;
+    Introduction introduction;
+    introduction[Introduction::Key::ServiceName] = kOvernetGossipService;
+    Fork(ReliabilityAndOrdering::UnreliableUnordered, std::move(introduction),
+         [this](StatusOr<NewStream> new_stream) {
+           assert(forking_gossip_stream_);
+           forking_gossip_stream_ = false;
+           if (new_stream.is_error()) {
+             Close(new_stream.AsStatus().WithContext("Opening gossip stream"),
+                   Callback<void>::Ignored());
+           } else {
+             InstantiateGossipStream(std::move(*new_stream));
+           }
+         });
+  }
+  return gossip_stream_.get();
+}
+
+void RouterEndpoint::ConnectionStream::InstantiateGossipStream(NewStream ns) {
+  assert(gossip_stream_ == nullptr);
+  gossip_stream_.reset(new Stream(std::move(ns)));
+  BeginGossipRead();
+}
+
+StatusOr<RouterEndpoint::OutgoingFork>
+RouterEndpoint::ConnectionStream::MakeFork(
+    ReliabilityAndOrdering reliability_and_ordering,
+    Introduction introduction) {
+  if (closing_status_) {
+    return *closing_status_;
+  }
+
+  StreamId id(next_stream_id_);
+  next_stream_id_ += 2;
+
+  return OutgoingFork{
+      NewStream{endpoint_, peer(), reliability_and_ordering, id},
+      ForkFrame(id, reliability_and_ordering, std::move(introduction))};
+}
+
+void RouterEndpoint::ConnectionStream::Fork(
+    ReliabilityAndOrdering reliability_and_ordering, Introduction introduction,
+    StatusOrCallback<NewStream> new_stream_ready) {
+  auto outgoing_fork =
+      MakeFork(reliability_and_ordering, std::move(introduction));
+  if (outgoing_fork.is_error()) {
+    new_stream_ready(outgoing_fork.AsStatus());
+    return;
+  }
+  Slice payload = outgoing_fork->fork_frame.Write(Border::None());
+
+  SendOp send_op(this, payload.length());
+  send_op.Push(payload, Callback<void>::Ignored());
+  send_op.Push(Slice(),
+               Callback<void>(
+                   ALLOCATED_CALLBACK,
+                   [new_stream = std::move(outgoing_fork->new_stream),
+                    new_stream_ready = std::move(new_stream_ready)]() mutable {
+                     new_stream_ready(std::move(new_stream));
+                   }));
+}
+
+void RouterEndpoint::RecvIntro(StatusOrCallback<ReceivedIntroduction> ready) {
+  recv_intro_ready_ = std::move(ready);
+  MaybeContinueIncomingForks();
+}
+
+void RouterEndpoint::MaybeContinueIncomingForks() {
+  if (recv_intro_ready_.empty() || incoming_forks_.Empty())
+    return;
+  auto* incoming_fork = incoming_forks_.Front();
+  incoming_forks_.Remove(incoming_fork);
+  assert(incoming_fork->fork_read_state_ ==
+         ConnectionStream::ReadState::Waiting);
+  recv_intro_ready_(UnwrapForkFrame(
+      incoming_fork->peer(), std::move(*incoming_fork->fork_frame_.get())));
+  incoming_fork->fork_frame_.Destroy();
+  incoming_fork->BeginForkRead();
+}
+
+RouterEndpoint::ReceivedIntroduction RouterEndpoint::UnwrapForkFrame(
+    NodeId peer, ForkFrame fork_frame) {
+  return ReceivedIntroduction{
+      NewStream{this, peer, fork_frame.reliability_and_ordering(),
+                fork_frame.stream_id()},
+      std::move(fork_frame.introduction())};
+}
+
+void RouterEndpoint::OnUnknownStream(NodeId node_id, StreamId stream_id) {
+  if (stream_id == StreamId(0)) {
+    GetOrCreateConnectionStream(node_id);
+  }
+}
+
+}  // namespace overnet
diff --git a/lib/overnet/endpoint/router_endpoint.h b/lib/overnet/endpoint/router_endpoint.h
new file mode 100644
index 0000000..9e80e48
--- /dev/null
+++ b/lib/overnet/endpoint/router_endpoint.h
@@ -0,0 +1,193 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <queue>
+#include "garnet/lib/overnet/datagram_stream/datagram_stream.h"
+#include "garnet/lib/overnet/labels/reliability_and_ordering.h"
+#include "garnet/lib/overnet/protocol/fork_frame.h"
+#include "garnet/lib/overnet/routing/router.h"
+#include "garnet/lib/overnet/vocabulary/manual_constructor.h"
+#include "garnet/lib/overnet/vocabulary/optional.h"
+#include "garnet/lib/overnet/vocabulary/slice.h"
+
+namespace overnet {
+
+class RouterEndpoint final : public Router {
+ private:
+  class ConnectionStream;
+
+ public:
+  class NewStream final {
+   public:
+    NewStream(const NewStream&) = delete;
+    NewStream& operator=(const NewStream&) = delete;
+
+    NewStream(NewStream&& other)
+        : creator_(other.creator_),
+          peer_(other.peer_),
+          reliability_and_ordering_(other.reliability_and_ordering_),
+          stream_id_(other.stream_id_) {
+      other.creator_ = nullptr;
+    }
+    NewStream& operator=(NewStream&& other) {
+      creator_ = other.creator_;
+      peer_ = other.peer_;
+      reliability_and_ordering_ = other.reliability_and_ordering_;
+      stream_id_ = other.stream_id_;
+      other.creator_ = nullptr;
+      return *this;
+    }
+
+    void Fail(const Status& status);
+    ~NewStream() { assert(creator_ == nullptr); }
+
+    friend std::ostream& operator<<(std::ostream& out, const NewStream& s) {
+      return out << "NewStream{node=" << s.peer_ << ",reliability_and_ordering="
+                 << ReliabilityAndOrderingString(s.reliability_and_ordering_)
+                 << ",stream_id=" << s.stream_id_ << "}";
+    }
+
+   private:
+    friend class RouterEndpoint;
+    NewStream(RouterEndpoint* creator, NodeId peer,
+              ReliabilityAndOrdering reliability_and_ordering,
+              StreamId stream_id)
+        : creator_(creator),
+          peer_(peer),
+          reliability_and_ordering_(reliability_and_ordering),
+          stream_id_(stream_id) {}
+
+    RouterEndpoint* creator_;
+    NodeId peer_;
+    ReliabilityAndOrdering reliability_and_ordering_;
+    StreamId stream_id_;
+  };
+
+  struct ReceivedIntroduction final {
+    NewStream new_stream;
+    Introduction introduction;
+  };
+
+  struct OutgoingFork {
+    NewStream new_stream;
+    ForkFrame fork_frame;
+  };
+
+  class Stream final : public DatagramStream {
+    friend class ConnectionStream;
+
+   public:
+    Stream(NewStream introduction);
+
+    StatusOr<OutgoingFork> Fork(ReliabilityAndOrdering reliability_and_ordering,
+                                Introduction introduction);
+    using DatagramStream::Close;
+    void Close(const Status& status, Callback<void> quiesced) override;
+
+   private:
+    ConnectionStream* connection_stream_ = nullptr;
+    InternalListNode<Stream> connection_stream_link_;
+  };
+
+  ReceivedIntroduction UnwrapForkFrame(NodeId peer, ForkFrame fork_frame);
+
+  using SendOp = Stream::SendOp;
+  using ReceiveOp = Stream::ReceiveOp;
+
+  explicit RouterEndpoint(Timer* timer, NodeId node_id,
+                          bool allow_non_determinism);
+  ~RouterEndpoint();
+  void Close(Callback<void> done) override;
+
+  void RegisterPeer(NodeId peer);
+
+  template <class F>
+  void ForEachConnectedPeer(F f) {
+    for (const auto& peer : connection_streams_) {
+      f(peer.first);
+    }
+  }
+
+  void RecvIntro(StatusOrCallback<ReceivedIntroduction> ready);
+  void SendIntro(NodeId peer, ReliabilityAndOrdering reliability_and_ordering,
+                 Introduction introduction,
+                 StatusOrCallback<NewStream> new_stream);
+
+ private:
+  void MaybeContinueIncomingForks();
+  void OnUnknownStream(NodeId peer, StreamId stream) override;
+
+  class ConnectionStream final : public DatagramStream {
+    friend class RouterEndpoint;
+    friend class Stream;
+
+   public:
+    ConnectionStream(RouterEndpoint* endpoint, NodeId peer);
+    ~ConnectionStream();
+
+    using DatagramStream::Close;
+    void Close(const Status& status, Callback<void> quiesced) override;
+
+    void Register() { DatagramStream::Register(); }
+
+    StatusOr<OutgoingFork> MakeFork(
+        ReliabilityAndOrdering reliability_and_ordering,
+        Introduction introduction);
+
+    void Fork(ReliabilityAndOrdering reliability_and_ordering,
+              Introduction introduction,
+              StatusOrCallback<NewStream> new_stream);
+
+    Stream* GossipStream();
+
+   private:
+    void BeginForkRead();
+    void BeginGossipRead();
+    bool IsGossipStreamInitiator() { return endpoint_->node_id() < peer(); }
+    void InstantiateGossipStream(NewStream ns);
+
+    enum class ReadState {
+      Reading,
+      Waiting,
+      Stopped,
+    };
+
+    RouterEndpoint* const endpoint_;
+    uint64_t next_stream_id_;
+
+    ReadState fork_read_state_ = ReadState::Waiting;
+    ReadState gossip_read_state_ = ReadState::Waiting;
+    ManualConstructor<ReceiveOp> fork_read_;
+    ManualConstructor<ReceiveOp> gossip_read_;
+    InternalListNode<ConnectionStream> forking_ready_;
+    InternalList<Stream, &Stream::connection_stream_link_> forked_streams_;
+    ManualConstructor<ForkFrame> fork_frame_;
+    Optional<Status> closing_status_;
+    bool forking_gossip_stream_ = false;
+    ClosedPtr<Stream> gossip_stream_;
+  };
+
+  StatusOr<OutgoingFork> ForkImpl(
+      ReliabilityAndOrdering reliability_and_ordering,
+      Introduction introduction);
+  ConnectionStream* GetOrCreateConnectionStream(NodeId peer);
+
+  static constexpr TimeDelta InitialGossipInterval() {
+    return TimeDelta::FromMilliseconds(42);
+  }
+  void StartGossipTimer();
+  void SendGossipTo(NodeId target, Callback<void> done);
+
+  std::unordered_map<NodeId, ConnectionStream> connection_streams_;
+  InternalList<ConnectionStream, &ConnectionStream::forking_ready_>
+      incoming_forks_;
+  StatusOrCallback<ReceivedIntroduction> recv_intro_ready_;
+  Optional<Timeout> gossip_timer_;
+  TimeDelta gossip_interval_ = InitialGossipInterval();
+  bool closing_ = false;
+};
+
+}  // namespace overnet
diff --git a/lib/overnet/endpoint/router_endpoint_integration_test.cc b/lib/overnet/endpoint/router_endpoint_integration_test.cc
new file mode 100644
index 0000000..9fd58c6
--- /dev/null
+++ b/lib/overnet/endpoint/router_endpoint_integration_test.cc
@@ -0,0 +1,597 @@
+// Copyright 2018 The Fuchsia 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 <array>
+#include <tuple>
+#include "garnet/lib/overnet/links/packet_link.h"
+#include "garnet/lib/overnet/testing/test_timer.h"
+#include "garnet/lib/overnet/testing/trace_cout.h"
+#include "gtest/gtest.h"
+#include "router_endpoint.h"
+
+//////////////////////////////////////////////////////////////////////////////
+// Two node fling
+
+namespace overnet {
+namespace router_endpoint2node {
+
+static const bool kTraceEverything = false;
+
+struct LinkState {
+  uint64_t id;
+  int outstanding_packets;
+};
+
+class DeliverySimulator {
+ public:
+  virtual ~DeliverySimulator() {}
+  // Returns Nothing if the slice should be dropped, or a delay if it should be
+  // delivered.
+  virtual Optional<TimeDelta> ChoosePacketDelivery(LinkState link_state,
+                                                   size_t slice_size) const = 0;
+
+  virtual std::string name() = 0;
+
+  virtual Bandwidth SimulatedBandwidth() const = 0;
+};
+
+// Very fast reliable packet delivery: it's often easier to debug problems
+// with HappyDelivery than with packet loss enabled (assuming it shows up).
+class HappyDelivery : public DeliverySimulator {
+ public:
+  Optional<TimeDelta> ChoosePacketDelivery(LinkState link_state,
+                                           size_t slice_size) const override {
+    return TimeDelta::FromMicroseconds(1);
+  }
+
+  std::string name() override { return "HappyDelivery"; }
+
+  Bandwidth SimulatedBandwidth() const override {
+    return Bandwidth::FromKilobitsPerSecond(1024 * 1024);
+  }
+};
+
+// Windowed number of outstanding packets
+class WindowedDelivery : public DeliverySimulator {
+ public:
+  WindowedDelivery(int max_outstanding, TimeDelta window)
+      : max_outstanding_(max_outstanding), window_(window) {}
+
+  Optional<TimeDelta> ChoosePacketDelivery(LinkState link_state,
+                                           size_t slice_size) const override {
+    if (link_state.outstanding_packets >= max_outstanding_)
+      return Nothing;
+    return window_;
+  }
+
+  std::string name() override {
+    std::ostringstream out;
+    out << "WindowedDelivery{max:" << max_outstanding_ << " over " << window_
+        << "}";
+    return out.str();
+  }
+
+  Bandwidth SimulatedBandwidth() const override {
+    return Bandwidth::BytesPerTime(256 * max_outstanding_, window_);
+  }
+
+ private:
+  const int max_outstanding_;
+  const TimeDelta window_;
+};
+
+class InProcessLinkImpl final
+    : public PacketLink,
+      public std::enable_shared_from_this<InProcessLinkImpl> {
+ public:
+  InProcessLinkImpl(RouterEndpoint* src, RouterEndpoint* dest, uint64_t link_id,
+                    const DeliverySimulator* simulator)
+      : PacketLink(src, dest->node_id(), 256, link_id),
+        timer_(dest->timer()),
+        link_id_(link_id),
+        simulator_(simulator),
+        from_(src->node_id()) {
+    src->RegisterPeer(dest->node_id());
+  }
+
+  ~InProcessLinkImpl() {
+    auto strong_partner = partner_.lock();
+    if (strong_partner != nullptr) {
+      strong_partner->partner_.reset();
+    }
+  }
+
+  void Partner(std::shared_ptr<InProcessLinkImpl> other) {
+    partner_ = other;
+    other->partner_ = shared_from_this();
+  }
+
+  void Emit(Slice packet) {
+    auto delay = simulator_->ChoosePacketDelivery(
+        LinkState{link_id_, outstanding_packets_}, packet.length());
+    OVERNET_TRACE(DEBUG) << "Packet sim says " << delay << " for " << packet;
+    if (!delay.has_value()) {
+      return;
+    }
+    outstanding_packets_++;
+    timer_->At(
+        timer_->Now() + *delay,
+        Callback<void>(ALLOCATED_CALLBACK, [self = shared_from_this(),
+                                            now = timer_->Now(), packet]() {
+          ScopedOp scoped_op(Op::New(OpType::INCOMING_PACKET));
+          auto strong_partner = self->partner_.lock();
+          OVERNET_TRACE(DEBUG)
+              << (strong_partner == nullptr ? "DROP" : "EMIT")
+              << " PACKET from " << self->from_ << " " << packet;
+          self->outstanding_packets_--;
+          if (strong_partner) {
+            strong_partner->Process(now, packet);
+          }
+        }));
+  }
+
+ private:
+  Timer* const timer_;
+  const uint64_t link_id_;
+  const DeliverySimulator* const simulator_;
+  std::weak_ptr<InProcessLinkImpl> partner_;
+  const NodeId from_;
+  int outstanding_packets_ = 0;
+};
+
+class InProcessLink final : public Link {
+ public:
+  InProcessLink(RouterEndpoint* src, RouterEndpoint* dest, uint64_t link_id,
+                const DeliverySimulator* simulator)
+      : impl_(new InProcessLinkImpl(src, dest, link_id, simulator)) {}
+
+  std::shared_ptr<InProcessLinkImpl> get() { return impl_; }
+
+  void Close(Callback<void> quiesced) {
+    impl_->Close(Callback<void>(
+        ALLOCATED_CALLBACK,
+        [this, quiesced = std::move(quiesced)]() mutable { impl_.reset(); }));
+  }
+  void Forward(Message message) { impl_->Forward(std::move(message)); }
+  LinkMetrics GetLinkMetrics() { return impl_->GetLinkMetrics(); }
+
+ private:
+  std::shared_ptr<InProcessLinkImpl> impl_;
+};
+
+class Env {
+ public:
+  virtual ~Env() {}
+
+  virtual TimeDelta AllowedTime(uint64_t length) const = 0;
+
+  virtual RouterEndpoint* endpoint1() = 0;
+  virtual RouterEndpoint* endpoint2() = 0;
+
+  void AwaitConnected() {
+    while (!endpoint1()->HasRouteTo(endpoint2()->node_id()) ||
+           !endpoint2()->HasRouteTo(endpoint1()->node_id())) {
+      endpoint1()->BlockUntilNoBackgroundUpdatesProcessing();
+      endpoint2()->BlockUntilNoBackgroundUpdatesProcessing();
+      test_timer_.StepUntilNextEvent();
+    }
+  }
+
+  void FlushTodo(
+      std::function<bool()> until,
+      TimeStamp deadline = TimeStamp::AfterEpoch(TimeDelta::FromHours(1))) {
+    bool stepped = false;
+    while (test_timer_.Now() < deadline + TimeDelta::FromHours(1)) {
+      if (until())
+        break;
+      if (!test_timer_.StepUntilNextEvent())
+        break;
+      stepped = true;
+    }
+    if (!stepped) {
+      test_timer_.Step(TimeDelta::FromMilliseconds(1).as_us());
+    }
+    ASSERT_LT(test_timer_.Now(), deadline);
+  }
+
+  Timer* timer() { return &test_timer_; }
+
+ private:
+  TestTimer test_timer_;
+  TraceCout trace_cout_{&test_timer_};
+  ScopedRenderer scoped_renderer{&trace_cout_};
+  ScopedSeverity scoped_severity{kTraceEverything ? Severity::DEBUG
+                                                  : Severity::INFO};
+};
+
+class TwoNode final : public Env {
+ public:
+  TwoNode(const DeliverySimulator* simulator, uint64_t node_id_1,
+          uint64_t node_id_2)
+      : simulator_(simulator) {
+    endpoint1_ = new RouterEndpoint(timer(), NodeId(node_id_1), false);
+    endpoint2_ = new RouterEndpoint(timer(), NodeId(node_id_2), false);
+    auto link1 = MakeLink<InProcessLink>(endpoint1_, endpoint2_, 1, simulator);
+    auto link2 = MakeLink<InProcessLink>(endpoint2_, endpoint1_, 2, simulator);
+    link1->get()->Partner(link2->get());
+    endpoint1_->RegisterLink(std::move(link1));
+    endpoint2_->RegisterLink(std::move(link2));
+  }
+
+  virtual ~TwoNode() {
+    if (!testing::Test::HasFailure()) {
+      bool done = false;
+      endpoint1_->Close(Callback<void>(ALLOCATED_CALLBACK, [&done, this]() {
+        endpoint2_->Close(Callback<void>(ALLOCATED_CALLBACK, [&done, this]() {
+          delete endpoint1_;
+          delete endpoint2_;
+          done = true;
+        }));
+      }));
+      FlushTodo([&done] { return done; });
+    }
+  }
+
+  TimeDelta AllowedTime(uint64_t data_length) const override {
+    // TODO(ctiller): make this just
+    // 'simulator_->SimulatedBandwidth().SendTimeForBytes(data_length)'
+    return TimeDelta::FromSeconds(1) +
+           simulator_->SimulatedBandwidth().SendTimeForBytes(3 * data_length);
+  }
+
+  RouterEndpoint* endpoint1() override { return endpoint1_; }
+  RouterEndpoint* endpoint2() override { return endpoint2_; }
+
+ private:
+  const DeliverySimulator* const simulator_;
+  RouterEndpoint* endpoint1_;
+  RouterEndpoint* endpoint2_;
+};
+
+class ThreeNode final : public Env {
+ public:
+  ThreeNode(const DeliverySimulator* simulator, uint64_t node_id_1,
+            uint64_t node_id_2, uint64_t node_id_h)
+      : simulator_(simulator) {
+    endpoint1_ = new RouterEndpoint(timer(), NodeId(node_id_1), false);
+    endpointH_ = new RouterEndpoint(timer(), NodeId(node_id_h), false);
+    endpoint2_ = new RouterEndpoint(timer(), NodeId(node_id_2), false);
+    auto link1H = MakeLink<InProcessLink>(endpoint1_, endpointH_, 1, simulator);
+    auto linkH1 = MakeLink<InProcessLink>(endpointH_, endpoint1_, 2, simulator);
+    auto link2H = MakeLink<InProcessLink>(endpoint2_, endpointH_, 3, simulator);
+    auto linkH2 = MakeLink<InProcessLink>(endpointH_, endpoint2_, 4, simulator);
+    link1H->get()->Partner(linkH1->get());
+    link2H->get()->Partner(linkH2->get());
+    endpoint1_->RegisterLink(std::move(link1H));
+    endpoint2_->RegisterLink(std::move(link2H));
+    endpointH_->RegisterLink(std::move(linkH1));
+    endpointH_->RegisterLink(std::move(linkH2));
+  }
+
+  virtual ~ThreeNode() {
+    if (!testing::Test::HasFailure()) {
+      bool done = false;
+      endpointH_->Close(Callback<void>(ALLOCATED_CALLBACK, [this, &done]() {
+        endpoint1_->Close(Callback<void>(ALLOCATED_CALLBACK, [this, &done]() {
+          endpoint2_->Close(Callback<void>(ALLOCATED_CALLBACK, [this, &done]() {
+            delete endpoint1_;
+            delete endpoint2_;
+            delete endpointH_;
+            done = true;
+          }));
+        }));
+      }));
+      FlushTodo([&done] { return done; });
+    }
+  }
+
+  TimeDelta AllowedTime(uint64_t data_length) const override {
+    // TODO(ctiller): make this just
+    // 'simulator_->SimulatedBandwidth().SendTimeForBytes(data_length)'
+    return TimeDelta::FromSeconds(2) +
+           simulator_->SimulatedBandwidth().SendTimeForBytes(5 * data_length);
+  }
+
+  RouterEndpoint* endpoint1() override { return endpoint1_; }
+  RouterEndpoint* endpoint2() override { return endpoint2_; }
+
+ private:
+  const DeliverySimulator* const simulator_;
+  RouterEndpoint* endpoint1_;
+  RouterEndpoint* endpointH_;
+  RouterEndpoint* endpoint2_;
+};
+
+class MakeEnvInterface {
+ public:
+  virtual const char* name() const = 0;
+  virtual std::shared_ptr<Env> Make() const = 0;
+};
+
+using MakeEnv = std::shared_ptr<MakeEnvInterface>;
+
+template <class T, class... Arg>
+MakeEnv MakeMakeEnv(const char* name, Arg&&... args) {
+  class Impl final : public MakeEnvInterface {
+   public:
+    Impl(const char* name, std::tuple<Arg...> args)
+        : name_(name), args_(args) {}
+    const char* name() const { return name_.c_str(); }
+    std::shared_ptr<Env> Make() const {
+      return std::apply(
+          [](Arg... args) { return std::make_shared<T>(args...); }, args_);
+    }
+
+   private:
+    const std::string name_;
+    const std::tuple<Arg...> args_;
+  };
+  return MakeEnv(
+      new Impl(name, std::tuple<Arg...>(std::forward<Arg>(args)...)));
+}
+
+static const auto kSimulators = [] {
+  std::vector<std::unique_ptr<DeliverySimulator>> out;
+  out.emplace_back(new HappyDelivery());
+  out.emplace_back(new WindowedDelivery(3, TimeDelta::FromMilliseconds(3)));
+  return out;
+}();
+
+template <class Env, int N>
+void AddVariations(const char* base_name, std::vector<MakeEnv>* envs) {
+  for (const auto& simulator : kSimulators) {
+    std::array<int, N> ary;
+    for (int i = 0; i < N; i++) {
+      ary[i] = i + 1;
+    }
+    do {
+      std::ostringstream name;
+      name << base_name;
+      for (auto i : ary)
+        name << i;
+      name << ":";
+      name << simulator->name();
+      envs->emplace_back(std::apply(
+          [&name, simulator = simulator.get()](auto... args) {
+            return MakeMakeEnv<Env>(
+                name.str().c_str(),
+                std::forward<DeliverySimulator* const>(simulator),
+                std::forward<int>(args)...);
+          },
+          ary));
+    } while (std::next_permutation(ary.begin(), ary.end()));
+  }
+}
+
+const auto kEnvVariations = [] {
+  std::vector<MakeEnv> envs;
+  AddVariations<TwoNode, 2>("TwoNode", &envs);
+  AddVariations<ThreeNode, 3>("ThreeNode", &envs);
+  return envs;
+}();
+
+class RouterEndpoint_IntegrationEnv : public ::testing::TestWithParam<MakeEnv> {
+};
+
+std::ostream& operator<<(std::ostream& out, MakeEnv env) {
+  return out << env->name();
+}
+
+TEST_P(RouterEndpoint_IntegrationEnv, NoOp) {
+  std::cout << "Param: " << GetParam() << std::endl;
+  GetParam()->Make()->AwaitConnected();
+}
+
+TEST_P(RouterEndpoint_IntegrationEnv, NodeDescriptionPropagation) {
+  std::cout << "Param: " << GetParam() << std::endl;
+  auto env = GetParam()->Make();
+  env->endpoint1()->SetDescription(Slice::FromStaticString("#ff00ff"));
+  env->AwaitConnected();
+  auto start_wait = env->timer()->Now();
+  auto idle_time_done = [&] {
+    return env->timer()->Now() - start_wait >= TimeDelta::FromSeconds(5);
+  };
+  while (!idle_time_done()) {
+    env->FlushTodo(idle_time_done);
+  }
+  bool found = false;
+  env->endpoint2()->ForEachNodeMetric(
+      [env = env.get(), &found](const NodeMetrics& m) {
+        if (m.node_id() == env->endpoint1()->node_id()) {
+          found = true;
+          EXPECT_EQ(m.description(), Slice::FromStaticString("#ff00ff"));
+        }
+      });
+  EXPECT_TRUE(found);
+}
+
+INSTANTIATE_TEST_CASE_P(RouterEndpoint_IntegrationEnv_Instance,
+                        RouterEndpoint_IntegrationEnv,
+                        ::testing::ValuesIn(kEnvVariations.begin(),
+                                            kEnvVariations.end()));
+
+struct OneMessageArgs {
+  MakeEnv make_env;
+  Introduction intro;
+  Slice body;
+};
+
+std::ostream& operator<<(std::ostream& out, OneMessageArgs args) {
+  return out << "env=" << args.make_env->name()
+             << " intro=" << args.intro.Write(Border::None())
+             << " body=" << args.body;
+}
+
+class RouterEndpoint_OneMessageIntegration
+    : public ::testing::TestWithParam<OneMessageArgs> {};
+
+TEST_P(RouterEndpoint_OneMessageIntegration, Works) {
+  std::cout << "Param: " << GetParam() << std::endl;
+  auto env = GetParam().make_env->Make();
+
+  const TimeDelta kAllowedTime =
+      env->AllowedTime(GetParam().intro.Write(Border::None()).length() +
+                       GetParam().body.length());
+  std::cout << "Allowed time for body: " << kAllowedTime << std::endl;
+
+  env->AwaitConnected();
+
+  auto got_pull_cb = std::make_shared<bool>(false);
+  auto got_push_cb = std::make_shared<bool>(false);
+
+  env->endpoint2()->RecvIntro(
+      StatusOrCallback<RouterEndpoint::ReceivedIntroduction>(
+          ALLOCATED_CALLBACK,
+          [this, got_pull_cb](
+              StatusOr<RouterEndpoint::ReceivedIntroduction>&& status) {
+            OVERNET_TRACE(INFO)
+                << "ep2: recv_intro status=" << status.AsStatus();
+            ASSERT_TRUE(status.is_ok()) << status.AsStatus();
+            auto intro = std::move(*status);
+            EXPECT_EQ(GetParam().intro, intro.introduction)
+                << intro.introduction.Write(Border::None());
+            auto stream = MakeClosedPtr<RouterEndpoint::Stream>(
+                std::move(intro.new_stream));
+            OVERNET_TRACE(INFO) << "ep2: start pull_all";
+            auto* op = new RouterEndpoint::ReceiveOp(stream.get());
+            OVERNET_TRACE(INFO) << "ep2: op=" << op;
+            op->PullAll(StatusOrCallback<std::vector<Slice>>(
+                ALLOCATED_CALLBACK,
+                [this, got_pull_cb, stream{std::move(stream)},
+                 op](const StatusOr<std::vector<Slice>>& status) mutable {
+                  OVERNET_TRACE(INFO)
+                      << "ep2: pull_all status=" << status.AsStatus();
+                  EXPECT_TRUE(status.is_ok()) << status.AsStatus();
+                  auto pull_text = Slice::Join(status->begin(), status->end());
+                  EXPECT_EQ(GetParam().body, pull_text)
+                      << pull_text.AsStdString();
+                  delete op;
+                  *got_pull_cb = true;
+                }));
+          }));
+
+  ScopedOp scoped_op(Op::New(OpType::OUTGOING_REQUEST));
+  env->endpoint1()->SendIntro(
+      env->endpoint2()->node_id(), ReliabilityAndOrdering::ReliableOrdered,
+      GetParam().intro,
+      StatusOrCallback<RouterEndpoint::NewStream>(
+          ALLOCATED_CALLBACK,
+          [this,
+           got_push_cb](StatusOr<RouterEndpoint::NewStream> intro_status) {
+            OVERNET_TRACE(INFO) << "ep1: send_intro status=" << intro_status;
+            ASSERT_TRUE(intro_status.is_ok());
+            auto stream = MakeClosedPtr<RouterEndpoint::Stream>(
+                std::move(*intro_status.get()));
+            RouterEndpoint::SendOp(stream.get(), GetParam().body.length())
+                .Push(GetParam().body, Callback<void>::Ignored());
+            *got_push_cb = true;
+          }));
+
+  env->FlushTodo(
+      [got_pull_cb, got_push_cb]() { return *got_pull_cb && *got_push_cb; },
+      env->timer()->Now() + kAllowedTime);
+
+  ASSERT_TRUE(*got_push_cb);
+  ASSERT_TRUE(*got_pull_cb);
+}
+
+const std::vector<OneMessageArgs> kOneMessageArgTests = [] {
+  std::vector<OneMessageArgs> out;
+  for (MakeEnv make_env : kEnvVariations) {
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::FromStaticString("123")});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(3, 'a')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(128, 'a')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(240, 'a')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(256, 'a')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(512, 'a')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(1024, 'b')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(2 * 1024, 'b')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(4 * 1024, 'b')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(8 * 1024, 'b')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(16 * 1024, 'b')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(32 * 1024, 'b')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(64 * 1024, 'b')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(128 * 1024, 'b')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(256 * 1024, 'b')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(512 * 1024, 'b')});
+    out.emplace_back(OneMessageArgs{
+        make_env,
+        Introduction(
+            {{Introduction::Key::ServiceName, Slice::FromStaticString("abc")}}),
+        Slice::RepeatedChar(1024 * 1024, 'b')});
+  }
+  return out;
+}();
+
+INSTANTIATE_TEST_CASE_P(RouterEndpoint_OneMessageIntegration_Instance,
+                        RouterEndpoint_OneMessageIntegration,
+                        ::testing::ValuesIn(kOneMessageArgTests.begin(),
+                                            kOneMessageArgTests.end()));
+
+}  // namespace router_endpoint2node
+}  // namespace overnet
diff --git a/lib/overnet/environment/BUILD.gn b/lib/overnet/environment/BUILD.gn
new file mode 100644
index 0000000..2cc337b
--- /dev/null
+++ b/lib/overnet/environment/BUILD.gn
@@ -0,0 +1,56 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+###############################################################################
+# AGGREGATE LIBRARIES
+
+source_set("lib") {
+  public_deps = [
+    ":timer",
+    ":trace",
+  ]
+}
+
+source_set("tests") {
+  testonly = true
+  deps = [
+    ":trace_test",
+  ]
+}
+
+###############################################################################
+
+# timer
+source_set("timer") {
+  sources = [
+    "timer.cc",
+    "timer.h",
+  ]
+  deps = [
+    "//garnet/lib/overnet/vocabulary:callback",
+    "//garnet/lib/overnet/vocabulary:optional",
+    "//garnet/lib/overnet/vocabulary:time",
+  ]
+}
+
+# trace
+source_set("trace") {
+  sources = [
+    "trace.cc",
+    "trace.h",
+  ]
+}
+
+source_set("trace_test") {
+  testonly = true
+  sources = [
+    "trace_test.cc",
+  ]
+  deps = [
+    ":trace",
+    "//garnet/lib/overnet/vocabulary:lib",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
diff --git a/lib/overnet/timer.cc b/lib/overnet/environment/timer.cc
similarity index 100%
rename from lib/overnet/timer.cc
rename to lib/overnet/environment/timer.cc
diff --git a/lib/overnet/environment/timer.h b/lib/overnet/environment/timer.h
new file mode 100644
index 0000000..f0ef44e
--- /dev/null
+++ b/lib/overnet/environment/timer.h
@@ -0,0 +1,105 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <stdint.h>
+#include <limits>
+#include <ostream>
+#include <type_traits>
+#include "garnet/lib/overnet/vocabulary/callback.h"
+#include "garnet/lib/overnet/vocabulary/optional.h"
+#include "garnet/lib/overnet/vocabulary/time.h"
+
+namespace overnet {
+
+class Timeout;
+
+class Timer {
+  friend class Timeout;
+
+ public:
+  virtual TimeStamp Now() = 0;
+
+  template <class F>
+  void At(TimeStamp t, F f);
+  void At(TimeStamp t, StatusCallback cb);
+
+ protected:
+  template <class T>
+  T* TimeoutStorage(Timeout* timeout);
+
+  static void FireTimeout(Timeout* timeout, Status status);
+
+ private:
+  virtual void InitTimeout(Timeout* timeout, TimeStamp when) = 0;
+  virtual void CancelTimeout(Timeout* timeout, Status status) = 0;
+};
+
+class Timeout {
+  friend class Timer;
+
+ public:
+  static constexpr uint64_t kMaxTimerStorage = 5 * sizeof(void*);
+
+  Timeout(const Timeout&) = delete;
+  Timeout& operator=(const Timeout&) = delete;
+
+  // Initialize a timeout for timestamp when. cb will be called when the timeout
+  // expires (with status OK) or when the timeout is cancelled (with non-OK
+  // status).
+  Timeout(Timer* timer, TimeStamp when, StatusCallback cb)
+      : timer_(timer), cb_(std::move(cb)) {
+    assert(!cb_.empty());
+    timer_->InitTimeout(this, when);
+  }
+
+  ~Timeout() {
+#ifndef NDEBUG
+    assert(!destroyed_);
+    destroyed_ = true;
+#endif
+    if (!cb_.empty())
+      Cancel();
+    assert(cb_.empty());
+  }
+
+  void Cancel(Status status = Status::Cancelled()) {
+    timer_->CancelTimeout(this, std::move(status));
+  }
+
+ private:
+#ifndef NDEBUG
+  bool destroyed_ = false;
+#endif
+  Timer* const timer_;
+  StatusCallback cb_;
+  typename std::aligned_storage<kMaxTimerStorage>::type storage_;
+};
+
+template <class T>
+T* Timer::TimeoutStorage(Timeout* timeout) {
+  static_assert(Timeout::kMaxTimerStorage >= sizeof(T),
+                "Must have enough storage in Timeout for Timer data");
+  return reinterpret_cast<T*>(&timeout->storage_);
+}
+
+inline void Timer::FireTimeout(Timeout* timeout, Status status) {
+  if (timeout->cb_.empty())
+    return;
+  auto cb = std::move(timeout->cb_);
+  cb(status);
+}
+
+template <class F>
+void Timer::At(TimeStamp t, F f) {
+  At(t, StatusCallback(ALLOCATED_CALLBACK,
+                       [f = std::move(f)](const Status& status) mutable {
+                         if (status.is_ok()) {
+                           f();
+                         }
+                       }));
+}
+
+}  // namespace overnet
diff --git a/lib/overnet/environment/trace.cc b/lib/overnet/environment/trace.cc
new file mode 100644
index 0000000..de12dc7
--- /dev/null
+++ b/lib/overnet/environment/trace.cc
@@ -0,0 +1,64 @@
+// Copyright 2018 The Fuchsia 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 "trace.h"
+
+namespace overnet {
+
+class NullTrace final : public TraceRenderer {
+ public:
+  void Render(TraceOutput output) override {}
+  void NoteParentChild(Op, Op) override {}
+};
+
+static NullTrace null_trace;
+
+thread_local Scopes g_trace_scopes;
+thread_local Op ScopedOp::current_ = Op::Invalid();
+thread_local Severity ScopedSeverity::current_ = Severity::DEBUG;
+thread_local TraceRenderer* ScopedRenderer::current_ = &null_trace;
+
+std::ostream& operator<<(std::ostream& out, Module module) {
+  switch (module) {
+    case Module::MODULE_COUNT:
+      return out << "<<ERROR>>";
+    case Module::NUB:
+      return out << "nub";
+    case Module::PACKET_LINK:
+      return out << "packet_link";
+    case Module::DATAGRAM_STREAM:
+      return out << "dg_stream";
+    case Module::DATAGRAM_STREAM_SEND_OP:
+      return out << "send_op";
+    case Module::DATAGRAM_STREAM_RECV_OP:
+      return out << "recv_op";
+    case Module::DATAGRAM_STREAM_INCOMING_MESSAGE:
+      return out << "inc_msg";
+    case Module::PACKET_PROTOCOL:
+      return out << "packet_protocol";
+    case Module::BBR:
+      return out << "bbr";
+    case Module::ROUTER:
+      return out << "router";
+  }
+}
+
+std::ostream& operator<<(std::ostream& out, OpType type) {
+  switch (type) {
+    case OpType::INVALID:
+      return out << "INVALID";
+    case OpType::INCOMING_PACKET:
+      return out << "IncomingPacket";
+    case OpType::OUTGOING_REQUEST:
+      return out << "OutgoingRequest";
+    case OpType::LINEARIZER_INTEGRATION:
+      return out << "LinearizerIntegration";
+  }
+}
+
+std::ostream& operator<<(std::ostream& out, Op op) {
+  return out << op.type() << "#" << op.somewhat_unique_id();
+}
+
+}  // namespace overnet
diff --git a/lib/overnet/environment/trace.h b/lib/overnet/environment/trace.h
new file mode 100644
index 0000000..d75a8a1
--- /dev/null
+++ b/lib/overnet/environment/trace.h
@@ -0,0 +1,203 @@
+// Copyright 2018 The Fuchsia 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 <functional>
+#include <sstream>
+#include <string>
+
+#pragma once
+
+//#define OVERNET_DEBUG_TRACE_SINK_REFS
+
+namespace overnet {
+
+enum class Module {
+  NUB,
+  ROUTER,
+  PACKET_LINK,
+  DATAGRAM_STREAM,
+  DATAGRAM_STREAM_SEND_OP,
+  DATAGRAM_STREAM_RECV_OP,
+  DATAGRAM_STREAM_INCOMING_MESSAGE,
+  PACKET_PROTOCOL,
+  BBR,
+  MODULE_COUNT  // Must be last.
+};
+
+std::ostream& operator<<(std::ostream& out, Module module);
+
+class Scopes {
+ public:
+  void* SetScope(Module module, void* instance) {
+    assert(module != Module::MODULE_COUNT);
+    void** p = &current_module_[static_cast<size_t>(module)];
+    void* out = *p;
+    *p = instance;
+    return out;
+  }
+
+  template <class F>
+  void Visit(F f) {
+    for (size_t i = 0; i < static_cast<size_t>(Module::MODULE_COUNT); i++) {
+      if (current_module_[i] != nullptr) {
+        f(static_cast<Module>(i), current_module_[i]);
+      }
+    }
+  }
+
+ private:
+  void* current_module_[static_cast<size_t>(Module::MODULE_COUNT)];
+};
+
+extern thread_local Scopes g_trace_scopes;
+
+template <class T>
+class ScopedModule {
+ public:
+  explicit ScopedModule(T* instance)
+      : prev_(g_trace_scopes.SetScope(T::kModule, instance)) {}
+  ~ScopedModule() { g_trace_scopes.SetScope(T::kModule, prev_); }
+  ScopedModule(const ScopedModule&) = delete;
+  ScopedModule& operator=(const ScopedModule&) = delete;
+
+ private:
+  void* const prev_;
+};
+
+enum class Severity : uint8_t {
+  DEBUG,
+  INFO,
+  WARNING,
+  ERROR,
+};
+
+enum class OpType : uint8_t {
+  INVALID,
+  OUTGOING_REQUEST,
+  INCOMING_PACKET,
+  LINEARIZER_INTEGRATION,
+};
+
+std::ostream& operator<<(std::ostream& out, OpType type);
+
+class Op {
+ public:
+  Op() = delete;
+
+  static Op Invalid() { return Op(OpType::INVALID, 0); }
+  static Op New(OpType type);
+
+  OpType type() const { return static_cast<OpType>(id_ >> 56); }
+  uint64_t somewhat_unique_id() const { return id_ & 0x00ff'ffff'ffff'ffff; }
+
+ private:
+  Op(OpType type, uint64_t id)
+      : id_((id & 0x00ff'ffff'ffff'ffff) |
+            (static_cast<uint64_t>(type) << 56)) {}
+
+  static uint64_t AllocateId() {
+    static std::atomic<uint64_t> ctr;
+    return ++ctr;
+  }
+
+  uint64_t id_;
+};
+
+std::ostream& operator<<(std::ostream& out, Op op);
+
+class ScopedOp {
+ public:
+  explicit ScopedOp(Op op) : prev_(current_) { current_ = op; }
+  ~ScopedOp() { current_ = prev_; }
+  ScopedOp(const ScopedOp&) = delete;
+  ScopedOp& operator=(const ScopedOp&) = delete;
+  static Op current() { return current_; }
+
+ private:
+  const Op prev_;
+  static thread_local Op current_;
+};
+
+struct TraceOutput {
+  Op op;
+  const char* message;
+  Scopes scopes;
+  const char* file;
+  int line;
+  Severity severity;
+};
+
+class TraceRenderer {
+ public:
+  virtual void Render(TraceOutput output) = 0;
+  virtual void NoteParentChild(Op parent, Op child) = 0;
+};
+
+class ScopedRenderer {
+ public:
+  explicit ScopedRenderer(TraceRenderer* renderer) : prev_(current_) {
+    current_ = renderer;
+  }
+  ~ScopedRenderer() { current_ = prev_; }
+  ScopedRenderer(const ScopedRenderer&) = delete;
+  ScopedRenderer& operator=(const ScopedRenderer&) = delete;
+
+  static TraceRenderer* current() {
+    assert(current_ != nullptr);
+    return current_;
+  }
+
+ private:
+  TraceRenderer* const prev_;
+  static thread_local TraceRenderer* current_;
+};
+
+class ScopedSeverity {
+ public:
+  explicit ScopedSeverity(Severity severity) : prev_(current_) {
+    current_ = std::max(current_, severity);
+  }
+  ~ScopedSeverity() { current_ = prev_; }
+  ScopedSeverity(const ScopedSeverity&) = delete;
+  ScopedSeverity& operator=(const ScopedSeverity&) = delete;
+  static Severity current() { return current_; }
+
+ private:
+  const Severity prev_;
+  static thread_local Severity current_;
+};
+
+class Trace : public std::ostringstream {
+ public:
+  Trace(const char* file, int line, Severity severity)
+      : file_(file), line_(line), severity_(severity) {}
+  ~Trace() {
+    ScopedRenderer::current()->Render(TraceOutput{ScopedOp::current(),
+                                                  str().c_str(), g_trace_scopes,
+                                                  file_, line_, severity_});
+  }
+
+ private:
+  const char* const file_;
+  const int line_;
+  const Severity severity_;
+};
+
+inline Op Op::New(OpType type) {
+  assert(type != OpType::INVALID);
+  auto out = Op(type, AllocateId());
+  if (ScopedOp::current().type() != OpType::INVALID) {
+    ScopedRenderer::current()->NoteParentChild(ScopedOp::current(), out);
+  }
+  return out;
+}
+
+}  // namespace overnet
+
+#define OVERNET_TRACE(trace_severity)        \
+  if (::overnet::ScopedSeverity::current() > \
+      ::overnet::Severity::trace_severity)   \
+    ;                                        \
+  else                                       \
+    ::overnet::Trace(__FILE__, __LINE__, ::overnet::Severity::trace_severity)
diff --git a/lib/overnet/environment/trace_test.cc b/lib/overnet/environment/trace_test.cc
new file mode 100644
index 0000000..1bf41da
--- /dev/null
+++ b/lib/overnet/environment/trace_test.cc
@@ -0,0 +1,56 @@
+// Copyright 2018 The Fuchsia 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 "trace.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include "garnet/lib/overnet/vocabulary/optional.h"
+
+using testing::Field;
+using testing::Mock;
+using testing::StrEq;
+using testing::StrictMock;
+
+namespace overnet {
+namespace trace_test {
+
+class MockRenderer : public TraceRenderer {
+ public:
+  MOCK_METHOD1(Render, void(TraceOutput));
+  MOCK_METHOD2(NoteParentChild, void(Op, Op));
+};
+
+TEST(Trace, Simple) {
+  StrictMock<MockRenderer> sink_impl;
+  ScopedRenderer renderer(&sink_impl);
+
+  auto outputs = [&](Optional<const char*> message, auto fn) {
+    if (message) {
+      EXPECT_CALL(sink_impl,
+                  Render(Field(&TraceOutput::message, StrEq(*message))));
+    }
+    fn();
+    Mock::VerifyAndClearExpectations(&sink_impl);
+  };
+
+  ScopedSeverity{Severity::DEBUG}, outputs("Hello World", [&] {
+    OVERNET_TRACE(DEBUG) << "Hello "
+                         << "World";
+  });
+  ScopedSeverity{Severity::DEBUG}, outputs("Hello World", [&] {
+    OVERNET_TRACE(ERROR) << "Hello "
+                         << "World";
+  });
+  ScopedSeverity{Severity::ERROR}, outputs("Hello World", [&] {
+    OVERNET_TRACE(ERROR) << "Hello "
+                         << "World";
+  });
+  ScopedSeverity{Severity::ERROR}, outputs(Nothing, [&] {
+    OVERNET_TRACE(DEBUG) << "Hello "
+                         << "World";
+  });
+}
+
+}  // namespace trace_test
+}  // namespace overnet
diff --git a/lib/overnet/gen_bazel.py b/lib/overnet/gen_bazel.py
deleted file mode 100755
index a71112e..0000000
--- a/lib/overnet/gen_bazel.py
+++ /dev/null
@@ -1,308 +0,0 @@
-#!/usr/bin/env python2.7
-
-# Copyright 2018 The Fuchsia Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import collections
-import os
-import sys
-
-# This program generates BUILD.bazel, WORKSPACE, .bazelrc from BUILD.gn
-
-####################################################################################################
-# TOKENIZER
-
-Tok = collections.namedtuple('Tok', ['tok', 'value'])
-
-
-def is_ident_start(c):
-    return (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z') or c == '_'
-
-
-def is_ident_char(c):
-    return is_ident_start(c) or is_digit(c)
-
-
-def is_digit(c):
-    return c >= '0' and c <= '9'
-
-
-def is_whitespace(c):
-    return c in ' \t\r\n'
-
-
-sym_name = {
-    ',': 'comma',
-    '(': 'left_paren',
-    ')': 'right_paren',
-    '{': 'left_mustache',
-    '}': 'right_mustache',
-    '[': 'left_square',
-    ']': 'right_square',
-    '=': 'equals',
-}
-
-
-def is_symbol(c):
-    return c in sym_name.keys()
-
-
-def tok(s):
-    if s == '':
-        return [], s
-    c = s[0]
-    if is_ident_start(c):
-        return tok_ident(s)
-    if c == '#':
-        return tok_comment(s)
-    if is_whitespace(c):
-        return tok_whitespace(s)
-    if is_symbol(c):
-        return tok_cont(Tok(sym_name[c], c), s[1:])
-    if c == '"':
-        return tok_string(s[1:])
-    print 'bad character: ' + s[0]
-    sys.exit(1)
-
-
-def tok_cont(token, s):
-    toks, rest = tok(s)
-    return [token] + toks, rest
-
-
-def tok_comment(s):
-    while s != '' and s[0] != '\n':
-        s = s[1:]
-    return tok(s[1:])
-
-
-def tok_ident(s):
-    ident = ''
-    while s and is_ident_char(s[0]):
-        ident += s[0]
-        s = s[1:]
-    return tok_cont(Tok('ident', ident), s)
-
-
-def tok_string(s):
-    string = ''
-    while s[0] != '"':
-        string += s[0]
-        s = s[1:]
-    return tok_cont(Tok('string', string), s[1:])
-
-
-def tok_whitespace(s):
-    while s and is_whitespace(s[0]):
-        s = s[1:]
-    return tok(s)
-
-
-def tokenize(s):
-    toks, rest = tok(s)
-    if rest != '':
-        print 'dangling: ' + rest
-        sys.exit(1)
-    return toks
-
-
-####################################################################################################
-# PARSER
-
-Bundle = collections.namedtuple('Bundle', ['rule', 'name', 'values'])
-
-
-def take(toks, tok):
-    if toks[0].tok != tok:
-        print 'expected %s, got %s' % (tok, toks[0].tok)
-        sys.exit(1)
-    return toks.pop(0).value
-
-
-def maybe_take(toks, tok):
-    if toks[0].tok != tok:
-        return None
-    return toks.pop(0).value
-
-
-def parse_dict(toks):
-    d = {}
-    while not maybe_take(toks, 'right_mustache'):
-        key = take(toks, 'ident')
-        take(toks, 'equals')
-        value = parse_value(toks)
-        d[key] = value
-    return d
-
-
-def parse_list(toks):
-    l = []
-    while not maybe_take(toks, 'right_square'):
-        l.append(parse_value(toks))
-        if not maybe_take(toks, 'comma'):
-            take(toks, 'right_square')
-            break
-    return l
-
-
-def parse_value(toks):
-    if maybe_take(toks, 'left_mustache'):
-        return parse_dict(toks)
-    if maybe_take(toks, 'left_square'):
-        return parse_list(toks)
-    s = maybe_take(toks, 'string')
-    if s is not None:
-        return s
-    s = maybe_take(toks, 'ident')
-    if s is not None:
-        if s == 'true':
-            return True
-        if s == 'false':
-            return False
-        print 'bad ident in value position: ' + s
-    print 'bad value token: %r' % toks
-
-
-def parse(toks):
-    bundles = []
-    while toks:
-        rule = take(toks, 'ident')
-        take(toks, 'left_paren')
-        name = take(toks, 'string')
-        take(toks, 'right_paren')
-        body = None
-        if maybe_take(toks, 'left_mustache'):
-            body = parse_dict(toks)
-        bundles.append(Bundle(rule, name, body))
-    return bundles
-
-
-####################################################################################################
-# CODEGEN
-
-def mapdep(n):
-    if n[0] == ':':
-        return n
-    m = {
-        '//third_party/googletest:gtest': '@com_google_googletest//:gtest',
-        '//third_party/googletest:gmock': None,
-    }
-    return m[n]
-
-
-FUZZERS = ['bbr', 'internal_list', 'linearizer',
-           'packet_protocol', 'receive_mode', 'routing_header']
-
-assert FUZZERS == sorted(FUZZERS)
-
-
-with open('BUILD.bazel', 'w') as o:
-    with open('BUILD.gn') as f:
-        for bundle in parse(tokenize(f.read())):
-            if bundle.rule == 'source_set':
-                print >>o, 'cc_library('
-                print >>o, '  name="%s",' % bundle.name
-                print >>o, '  srcs=[%s],' % ','.join(
-                    '"%s"' % s for s in bundle.values['sources'])
-                if 'deps' in bundle.values:
-                    print >>o, '  deps=[%s],' % ','.join(
-                        '"%s"' % mapdep(s) for s in bundle.values['deps'] if mapdep(s) is not None)
-                print >>o, ')'
-            if bundle.rule == 'executable':
-                if bundle.values.get('testonly', False):
-                    print >>o, 'cc_test(shard_count=50,'
-                else:
-                    print >>o, 'cc_binary('
-                print >>o, '  name="%s",' % bundle.name
-                print >>o, '  srcs=[%s],' % ','.join(
-                    '"%s"' % s for s in bundle.values['sources'])
-                print >>o, '  deps=[%s],' % ','.join(
-                    '"%s"' % mapdep(s) for s in bundle.values['deps'] if mapdep(s) is not None)
-                print >>o, ')'
-    for fuzzer in FUZZERS:
-        print >>o, 'cc_binary('
-        print >>o, '  name="%s_fuzzer",' % fuzzer
-        srcs = ['%s_fuzzer.cc' % fuzzer]
-        helpers_h = '%s_fuzzer_helpers.h' % fuzzer
-        if os.path.exists(helpers_h):
-            srcs.append(helpers_h)
-        print >>o, '  srcs=[%s],' % ', '.join('"%s"' % s for s in srcs)
-        print >>o, '  deps=[":overnet", ":test_util"],'
-        print >>o, ')'
-
-WORKSPACE = """
-# This file is not checked in, but generated by gen_bazel.py
-# Make changes there
-
-git_repository(
-    name = 'com_google_googletest',
-    remote = 'https://github.com/google/googletest.git',
-    commit = 'd5266326752f0a1dadbd310932d8f4fd8c3c5e7d',
-)
-"""
-
-BAZELRC = """
-# This file is not checked in, but generated by gen_bazel.py
-# Make changes there
-
-build --client_env=CC=clang
-build --copt -std=c++14
-
-build:asan --strip=never
-build:asan --copt -fsanitize=address
-build:asan --copt -O0
-build:asan --copt -fno-omit-frame-pointer
-build:asan --linkopt -fsanitize=address
-build:asan --action_env=ASAN_OPTIONS=detect_leaks=1:color=always
-build:asan --action_env=LSAN_OPTIONS=report_objects=1
-
-build:asan-fuzzer --strip=never
-build:asan-fuzzer --copt -fsanitize=fuzzer,address
-build:asan-fuzzer --copt -fsanitize-coverage=trace-cmp
-build:asan-fuzzer --copt -O0
-build:asan-fuzzer --copt -fno-omit-frame-pointer
-build:asan-fuzzer --linkopt -fsanitize=fuzzer,address
-build:asan-fuzzer --action_env=ASAN_OPTIONS=detect_leaks=1:color=always
-build:asan-fuzzer --action_env=LSAN_OPTIONS=report_objects=1
-
-build:msan --strip=never
-build:msan --copt -fsanitize=memory
-build:msan --copt -O0
-build:msan --copt -fsanitize-memory-track-origins
-build:msan --copt -fsanitize-memory-use-after-dtor
-build:msan --copt -fno-omit-frame-pointer
-build:msan --copt -fPIC
-build:msan --linkopt -fsanitize=memory
-build:msan --linkopt -fPIC
-build:msan --action_env=MSAN_OPTIONS=poison_in_dtor=1
-
-build:tsan --strip=never
-build:tsan --copt -fsanitize=thread
-build:tsan --copt -fno-omit-frame-pointer
-build:tsan --copt -DNDEBUG
-build:tsan --linkopt -fsanitize=thread
-build:tsan --action_env=TSAN_OPTIONS=halt_on_error=1
-
-build:ubsan --strip=never
-build:ubsan --copt -fsanitize=undefined
-build:ubsan --copt -fno-omit-frame-pointer
-build:ubsan --copt -DNDEBUG
-build:ubsan --copt -fno-sanitize=function,vptr
-build:ubsan --linkopt -fsanitize=undefined
-build:ubsan --action_env=UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1
-
-build:ubsan-fuzzer --strip=never
-build:ubsan-fuzzer --copt -fsanitize=fuzzer,undefined
-build:ubsan-fuzzer --copt -fno-omit-frame-pointer
-build:ubsan-fuzzer --copt -DNDEBUG
-build:ubsan-fuzzer --copt -fno-sanitize=function,vptr
-build:ubsan-fuzzer --linkopt -fsanitize=fuzzer,undefined
-build:ubsan-fuzzer --action_env=UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1
-"""
-
-with open('WORKSPACE', 'w') as o:
-    o.write(WORKSPACE)
-
-with open('.bazelrc', 'w') as o:
-    o.write(BAZELRC)
diff --git a/lib/overnet/labels/BUILD.gn b/lib/overnet/labels/BUILD.gn
new file mode 100644
index 0000000..ffd9fc9
--- /dev/null
+++ b/lib/overnet/labels/BUILD.gn
@@ -0,0 +1,89 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+###############################################################################
+# AGGREGATE LIBRARIES
+
+source_set("lib") {
+  public_deps = [
+    ":node_id",
+    ":reliability_and_ordering",
+    ":seq_num",
+    ":stream_id",
+  ]
+}
+
+source_set("tests") {
+  testonly = true
+  deps = [
+    ":node_id_test",
+    ":seq_num_test",
+  ]
+}
+
+###############################################################################
+
+# node_id
+source_set("node_id") {
+  sources = [
+    "node_id.cc",
+    "node_id.h",
+  ]
+  deps = [
+    "//garnet/lib/overnet/protocol:serialization_helpers",
+    "//garnet/lib/overnet/vocabulary:status",
+  ]
+}
+
+source_set("node_id_test") {
+  testonly = true
+  sources = [
+    "node_id_test.cc",
+  ]
+  deps = [
+    ":node_id",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# reliability_and_ordering
+source_set("reliability_and_ordering") {
+  sources = [
+    "reliability_and_ordering.cc",
+    "reliability_and_ordering.h",
+  ]
+}
+
+# seq_num
+source_set("seq_num") {
+  sources = [
+    "seq_num.cc",
+    "seq_num.h",
+  ]
+  deps = [
+    "//garnet/lib/overnet/vocabulary:status",
+  ]
+}
+
+source_set("seq_num_test") {
+  testonly = true
+  sources = [
+    "seq_num_test.cc",
+  ]
+  deps = [
+    ":seq_num",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# stream_id
+source_set("stream_id") {
+  sources = [
+    "stream_id.cc",
+    "stream_id.h",
+  ]
+  deps = [
+    "//garnet/lib/overnet/protocol:varint",
+  ]
+}
diff --git a/lib/overnet/node_id.cc b/lib/overnet/labels/node_id.cc
similarity index 100%
rename from lib/overnet/node_id.cc
rename to lib/overnet/labels/node_id.cc
diff --git a/lib/overnet/node_id.h b/lib/overnet/labels/node_id.h
similarity index 91%
rename from lib/overnet/node_id.h
rename to lib/overnet/labels/node_id.h
index 24cc2d7..4ee81f9 100644
--- a/lib/overnet/node_id.h
+++ b/lib/overnet/labels/node_id.h
@@ -7,8 +7,8 @@
 #include <stdint.h>
 #include <functional>
 #include <iosfwd>
-#include "serialization_helpers.h"
-#include "status.h"
+#include "garnet/lib/overnet/protocol/serialization_helpers.h"
+#include "garnet/lib/overnet/vocabulary/status.h"
 
 namespace overnet {
 
diff --git a/lib/overnet/node_id_test.cc b/lib/overnet/labels/node_id_test.cc
similarity index 100%
rename from lib/overnet/node_id_test.cc
rename to lib/overnet/labels/node_id_test.cc
diff --git a/lib/overnet/reliability_and_ordering.cc b/lib/overnet/labels/reliability_and_ordering.cc
similarity index 100%
rename from lib/overnet/reliability_and_ordering.cc
rename to lib/overnet/labels/reliability_and_ordering.cc
diff --git a/lib/overnet/reliability_and_ordering.h b/lib/overnet/labels/reliability_and_ordering.h
similarity index 100%
rename from lib/overnet/reliability_and_ordering.h
rename to lib/overnet/labels/reliability_and_ordering.h
diff --git a/lib/overnet/seq_num.cc b/lib/overnet/labels/seq_num.cc
similarity index 100%
rename from lib/overnet/seq_num.cc
rename to lib/overnet/labels/seq_num.cc
diff --git a/lib/overnet/seq_num.h b/lib/overnet/labels/seq_num.h
similarity index 96%
rename from lib/overnet/seq_num.h
rename to lib/overnet/labels/seq_num.h
index 3051e94..adf1999 100644
--- a/lib/overnet/seq_num.h
+++ b/lib/overnet/labels/seq_num.h
@@ -6,8 +6,7 @@
 
 #include <stdint.h>
 #include <iosfwd>
-#include "status.h"
-#include "string.h"
+#include "garnet/lib/overnet/vocabulary/status.h"
 
 namespace overnet {
 
diff --git a/lib/overnet/seq_num_test.cc b/lib/overnet/labels/seq_num_test.cc
similarity index 100%
rename from lib/overnet/seq_num_test.cc
rename to lib/overnet/labels/seq_num_test.cc
diff --git a/lib/overnet/stream_id.cc b/lib/overnet/labels/stream_id.cc
similarity index 100%
rename from lib/overnet/stream_id.cc
rename to lib/overnet/labels/stream_id.cc
diff --git a/lib/overnet/stream_id.h b/lib/overnet/labels/stream_id.h
similarity index 95%
rename from lib/overnet/stream_id.h
rename to lib/overnet/labels/stream_id.h
index 071a813..383bc60 100644
--- a/lib/overnet/stream_id.h
+++ b/lib/overnet/labels/stream_id.h
@@ -7,7 +7,7 @@
 #include <stdint.h>
 #include <functional>
 #include <iosfwd>
-#include "varint.h"
+#include "garnet/lib/overnet/protocol/varint.h"
 
 namespace overnet {
 
diff --git a/lib/overnet/linearizer.h b/lib/overnet/linearizer.h
deleted file mode 100644
index e1ac586..0000000
--- a/lib/overnet/linearizer.h
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#pragma once
-
-#include <map>
-#include "optional.h"
-#include "sink.h"
-#include "slice.h"
-#include "trace.h"
-
-namespace overnet {
-
-class Linearizer final : public Sink<Chunk>, public Source<Slice> {
- public:
-  explicit Linearizer(uint64_t max_buffer, TraceSink trace_sink);
-  ~Linearizer();
-
-  // Override Sink<Chunk>.
-  void Push(Chunk chunk) override;
-  void Close(const Status& status, Callback<void> quiesced) override;
-
-  // Override Source<Slice>.
-  void Pull(StatusOrCallback<Optional<Slice>> ready) override;
-  void PullAll(StatusOrCallback<std::vector<Slice>> ready) override;
-  void Close(const Status& status) override;
-
- private:
-  void IntegratePush(Chunk chunk);
-  void ValidateInternals() const;
-
-  const uint64_t max_buffer_;
-  const TraceSink trace_sink_;
-  uint64_t offset_ = 0;
-  Optional<uint64_t> length_;
-  std::map<uint64_t, Slice> pending_push_;
-
-  enum class ReadMode {
-    Closed,
-    Idle,
-    ReadSlice,
-    ReadAll,
-  };
-
-  inline friend std::ostream& operator<<(std::ostream& out, ReadMode m) {
-    switch (m) {
-      case ReadMode::Closed:
-        return out << "Closed";
-      case ReadMode::Idle:
-        return out << "Idle";
-      case ReadMode::ReadSlice:
-        return out << "ReadSlice";
-      case ReadMode::ReadAll:
-        return out << "ReadAll";
-    }
-  }
-
-  struct Closed {
-    Status status;
-  };
-  struct ReadSlice {
-    StatusOrCallback<Optional<Slice>> done;
-  };
-  struct ReadAll {
-    std::vector<Slice> building;
-    StatusOrCallback<std::vector<Slice>> done;
-  };
-  union ReadData {
-    ReadData() {}
-    ~ReadData() {}
-
-    Closed closed;
-    ReadSlice read_slice;
-    ReadAll read_all;
-  };
-
-  ReadMode read_mode_ = ReadMode::Idle;
-  ReadData read_data_;
-
-  void IdleToClosed(const Status& status);
-  void IdleToReadSlice(StatusOrCallback<Optional<Slice>> done);
-  void IdleToReadAll(StatusOrCallback<std::vector<Slice>> done);
-  ReadSlice ReadSliceToIdle();
-  ReadAll ReadAllToIdle();
-  void ContinueReadAll();
-};
-
-// Expects one Close() from Sink, and one from Source, then deletes itself.
-class ReffedLinearizer final : public Sink<Chunk>, public Source<Slice> {
- public:
-  static ReffedLinearizer* Make(uint64_t max_buffer, TraceSink trace_sink) {
-    return new ReffedLinearizer(max_buffer, trace_sink);
-  }
-
-  // Override Sink<Chunk> and Source<Slice>.
-
-  // Override Sink<Chunk>.
-  void Push(Chunk chunk) override { impl_.Push(std::move(chunk)); }
-
-  void Close(const Status& status, Callback<void> quiesced) override {
-    assert(quiesced_.empty());
-    assert(!quiesced.empty());
-    quiesced_ = std::move(quiesced);
-    Close(status);
-  }
-
-  // Override Souce<Slice>.
-  void Pull(StatusOrCallback<Optional<Slice>> ready) override {
-    impl_.Pull(std::move(ready));
-  }
-
-  void Close(const Status& status) override {
-    impl_.Close(status);
-    if (--refs_ == 0) {
-      quiesced_();
-      delete this;
-    }
-  }
-
- private:
-  explicit ReffedLinearizer(uint64_t max_buffer, TraceSink trace_sink)
-      : impl_(max_buffer, trace_sink) {}
-
-  int refs_ = 2;
-  Linearizer impl_;
-  Callback<void> quiesced_;
-};
-
-}  // namespace overnet
diff --git a/lib/overnet/links/BUILD.gn b/lib/overnet/links/BUILD.gn
new file mode 100644
index 0000000..539f892
--- /dev/null
+++ b/lib/overnet/links/BUILD.gn
@@ -0,0 +1,77 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+###############################################################################
+# AGGREGATE LIBRARIES
+
+source_set("lib") {
+  public_deps = [
+    ":packet_link",
+    ":packet_nub",
+  ]
+}
+
+source_set("tests") {
+  testonly = true
+  deps = [
+    ":packet_link_test",
+    ":packet_nub_test",
+  ]
+}
+
+###############################################################################
+
+# packet_link
+source_set("packet_link") {
+  sources = [
+    "packet_link.cc",
+    "packet_link.h",
+  ]
+  deps = [
+    "//garnet/lib/overnet/environment:trace",
+    "//garnet/lib/overnet/packet_protocol",
+    "//garnet/lib/overnet/routing:router",
+  ]
+}
+
+source_set("packet_link_test") {
+  testonly = true
+  sources = [
+    "packet_link_test.cc",
+  ]
+  deps = [
+    ":packet_link",
+    "//garnet/lib/overnet/testing:test_timer",
+    "//garnet/lib/overnet/testing:trace_cout",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# packet_nub
+source_set("packet_nub") {
+  sources = [
+    "packet_nub.h",
+  ]
+  deps = [
+    ":packet_link",
+    "//garnet/lib/overnet/environment:timer",
+    "//garnet/lib/overnet/labels:node_id",
+    "//garnet/lib/overnet/vocabulary:slice",
+  ]
+}
+
+source_set("packet_nub_test") {
+  testonly = true
+  sources = [
+    "packet_nub_test.cc",
+  ]
+  deps = [
+    ":packet_nub",
+    "//garnet/lib/overnet/testing:test_timer",
+    "//garnet/lib/overnet/testing:trace_cout",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
diff --git a/lib/overnet/packet_link.cc b/lib/overnet/links/packet_link.cc
similarity index 68%
rename from lib/overnet/packet_link.cc
rename to lib/overnet/links/packet_link.cc
index 76ad1d9..083b45e 100644
--- a/lib/overnet/packet_link.cc
+++ b/lib/overnet/links/packet_link.cc
@@ -8,25 +8,17 @@
 
 namespace overnet {
 
-static uint64_t GenerateLabel() {
-  static uint64_t next_label = 1;
-  return next_label++;
-}
-
-PacketLink::PacketLink(Router* router, TraceSink trace_sink, NodeId peer,
-                       uint32_t mss)
+PacketLink::PacketLink(Router* router, NodeId peer, uint32_t mss,
+                       uint64_t label)
     : router_(router),
       timer_(router->timer()),
-      trace_sink_(trace_sink.Decorate([this](const std::string& msg) {
-        std::ostringstream out;
-        out << "PktLnk[" << this << "] " << msg;
-        return out.str();
-      })),
       peer_(peer),
-      label_(GenerateLabel()),
-      protocol_{router_->timer(), this, trace_sink_, mss} {}
+      label_(label),
+      protocol_{router_->timer(), [router] { return (*router->rng())(); }, this,
+                PacketProtocol::NullCodec(), mss} {}
 
 void PacketLink::Close(Callback<void> quiesced) {
+  ScopedModule<PacketLink> scoped_module(this);
   stashed_.Reset();
   while (!outgoing_.empty()) {
     outgoing_.pop();
@@ -38,10 +30,11 @@
 }
 
 void PacketLink::Forward(Message message) {
+  ScopedModule<PacketLink> scoped_module(this);
   bool send_immediately = !sending_ && outgoing_.empty();
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "Forward sending=" << sending_ << " outgoing=" << outgoing_.size()
-      << " imm=" << send_immediately;
+  OVERNET_TRACE(DEBUG) << "Forward sending=" << sending_
+                       << " outgoing=" << outgoing_.size()
+                       << " imm=" << send_immediately;
   outgoing_.emplace(std::move(message));
   if (send_immediately) {
     SchedulePacket();
@@ -49,10 +42,11 @@
 }
 
 LinkMetrics PacketLink::GetLinkMetrics() {
-  LinkMetrics m(router_->node_id(), peer_, metrics_version_++, label_);
+  ScopedModule<PacketLink> scoped_module(this);
+  LinkMetrics m(router_->node_id(), peer_, metrics_version_++, label_, true);
   m.set_bw_link(protocol_.BottleneckBandwidth());
   m.set_rtt(protocol_.RoundTripTime());
-  m.set_mss(std::max(8u, protocol_.mss()) - 8);
+  m.set_mss(std::max(32u, protocol_.mss()) - 32);
   return m;
 }
 
@@ -60,10 +54,11 @@
   assert(!sending_);
   assert(!outgoing_.empty() || stashed_.has_value());
   sending_ = true;
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "SchedulePacket outgoing=" << outgoing_.size();
+  OVERNET_TRACE(DEBUG) << "Schedule";
   protocol_.Send(
-      [this](auto arg) {
+      [this, op = ScopedOp::current()](auto arg) mutable {
+        ScopedModule<PacketLink> scoped_module(this);
+        ScopedOp scoped_op(op);
         auto pkt = BuildPacket(arg);
         sending_ = false;
         if (stashed_.has_value() || !outgoing_.empty()) {
@@ -71,12 +66,12 @@
         }
         return pkt;
       },
-      PacketProtocol::SendCallback::Ignored());
+      StatusCallback::Ignored());
 }
 
 Slice PacketLink::BuildPacket(LazySliceArgs args) {
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "BuildPacket outgoing=" << outgoing_.size() << " stashed="
+  OVERNET_TRACE(DEBUG)
+      << "Build outgoing=" << outgoing_.size() << " stashed="
       << (stashed_ ? [&](){ 
         std::ostringstream fmt;
         fmt << stashed_->message << "+" << stashed_->payload.length() << "b";
@@ -90,9 +85,11 @@
     const auto serialized_length = serialized.length();
     const auto length_length = varint::WireSizeFor(serialized_length);
     const auto segment_length = length_length + serialized_length;
-    OVERNET_TRACE(DEBUG, trace_sink_)
-        << "BuildPacket/AddMsg segment_length=" << segment_length
-        << " remaining_length=" << remaining_length;
+    OVERNET_TRACE(DEBUG) << "AddMsg segment_length=" << segment_length
+                         << " remaining_length=" << remaining_length
+                         << (segment_length > remaining_length ? "  => SKIP"
+                                                               : "")
+                         << "; serialized:" << serialized;
     if (segment_length > remaining_length) {
       return false;
     }
@@ -115,8 +112,9 @@
         remaining_length = 0;
       } else {
         // There's no chance we'll ever send this message: drop it.
+        abort();
         stashed_.Reset();
-        OVERNET_TRACE(DEBUG, trace_sink_) << " drop stashed";
+        OVERNET_TRACE(DEBUG) << "drop stashed";
       }
     }
   }
@@ -134,24 +132,25 @@
     Message msg = std::move(outgoing_.front());
     outgoing_.pop();
     // Serialize it.
-    auto payload = msg.make_payload(
-        LazySliceArgs{0, static_cast<uint32_t>(max_len),
-                      args.has_other_content || !send_slices_.empty(),
-                      args.delay_until_time});
-    OVERNET_TRACE(DEBUG, trace_sink_)
-        << "delay -> " << (*args.delay_until_time - timer_->Now());
+    auto payload = msg.make_payload(LazySliceArgs{
+        Border::None(), std::min(msg.mss, static_cast<uint32_t>(max_len)),
+        args.has_other_content || !send_slices_.empty()});
+    if (payload.length() == 0) {
+      continue;
+    }
     // Add the serialized version to the outgoing queue.
     if (!add_serialized_msg(msg.header, payload)) {
       // If it fails, stash it, and retry the next loop around.
       // This may happen if the sender is unable to trim to the maximum length.
-      OVERNET_TRACE(DEBUG, trace_sink_) << " stash too long";
+      OVERNET_TRACE(DEBUG) << "stash too long";
       stashed_.Reset(std::move(msg.header), std::move(payload));
       break;
     }
   }
 
-  Slice send = Slice::Join(send_slices_.begin(), send_slices_.end(),
-                           args.desired_prefix + SeqNum::kMaxWireLength);
+  Slice send =
+      Slice::Join(send_slices_.begin(), send_slices_.end(),
+                  args.desired_border.WithAddedPrefix(SeqNum::kMaxWireLength));
   send_slices_.clear();
 
   return send;
@@ -160,40 +159,28 @@
 void PacketLink::SendPacket(SeqNum seq, LazySlice data, Callback<void> done) {
   assert(!emitting_);
   const auto prefix_length = 1 + seq.wire_length();
-  const TimeStamp now = timer_->Now();
-  TimeStamp send_time = now;
-  auto data_slice = data(LazySliceArgs{
-      prefix_length, protocol_.mss() - prefix_length, false, &send_time});
+  auto data_slice = data(LazySliceArgs{Border::Prefix(prefix_length),
+                                       protocol_.mss() - prefix_length, false});
   auto send_slice = data_slice.WithPrefix(prefix_length, [seq](uint8_t* p) {
     *p++ = 0;
     seq.Write(p);
   });
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "StartEmit " << send_slice << " delay=" << (send_time - now);
-  emitting_.Reset(timer_, send_time, std::move(send_slice), std::move(done),
-                  [this](const Status& status) {
-                    OVERNET_TRACE(DEBUG, trace_sink_)
-                        << "Emit status=" << status;
-                    if (status.is_ok()) {
-                      Emit(std::move(emitting_->slice));
-                    }
-                    auto cb = std::move(emitting_->done);
-                    emitting_.Reset();
-                  });
+  OVERNET_TRACE(DEBUG) << "Emit " << send_slice;
+  Emit(std::move(send_slice));
 }
 
 void PacketLink::Process(TimeStamp received, Slice packet) {
+  ScopedModule<PacketLink> scoped_module(this);
   const uint8_t* const begin = packet.begin();
   const uint8_t* p = begin;
   const uint8_t* const end = packet.end();
 
   if (p == end) {
-    OVERNET_TRACE(WARNING, trace_sink_) << "Short packet received (no op code)";
+    OVERNET_TRACE(WARNING) << "Empty packet";
     return;
   }
   if (*p != 0) {
-    OVERNET_TRACE(WARNING, trace_sink_)
-        << "Non-zero op-code received in PacketLink";
+    OVERNET_TRACE(WARNING) << "Non-zero op-code received in PacketLink";
     return;
   }
   ++p;
@@ -204,8 +191,8 @@
 
   auto seq_status = SeqNum::Parse(&p, end);
   if (seq_status.is_error()) {
-    OVERNET_TRACE(WARNING, trace_sink_)
-        << "Packet seqnum parse failure: " << seq_status.AsStatus();
+    OVERNET_TRACE(WARNING) << "Packet seqnum parse failure: "
+                           << seq_status.AsStatus();
     return;
   }
   packet.TrimBegin(p - begin);
@@ -213,15 +200,15 @@
   auto packet_status =
       protocol_.Process(received, *seq_status.get(), std::move(packet));
   if (packet_status.status.is_error()) {
-    OVERNET_TRACE(WARNING, trace_sink_)
-        << "Packet header parse failure: " << packet_status.status.AsStatus();
+    OVERNET_TRACE(WARNING) << "Packet header parse failure: "
+                           << packet_status.status.AsStatus();
     return;
   }
   if (*packet_status.status.get()) {
     auto body_status =
         ProcessBody(received, std::move(*packet_status.status.get()->get()));
     if (body_status.is_error()) {
-      OVERNET_TRACE(WARNING, trace_sink_)
+      OVERNET_TRACE(WARNING)
           << "Packet body parse failure: " << body_status << std::endl;
       return;
     }
diff --git a/lib/overnet/packet_link.h b/lib/overnet/links/packet_link.h
similarity index 83%
rename from lib/overnet/packet_link.h
rename to lib/overnet/links/packet_link.h
index b8fead2..bc7c5f4 100644
--- a/lib/overnet/packet_link.h
+++ b/lib/overnet/links/packet_link.h
@@ -5,15 +5,17 @@
 #pragma once
 
 #include <queue>
-#include "packet_protocol.h"
-#include "router.h"
-#include "trace.h"
+#include "garnet/lib/overnet/environment/trace.h"
+#include "garnet/lib/overnet/packet_protocol/packet_protocol.h"
+#include "garnet/lib/overnet/routing/router.h"
 
 namespace overnet {
 
 class PacketLink : public Link, private PacketProtocol::PacketSender {
  public:
-  PacketLink(Router* router, TraceSink trace_sink, NodeId peer, uint32_t mss);
+  static inline constexpr auto kModule = Module::PACKET_LINK;
+
+  PacketLink(Router* router, NodeId peer, uint32_t mss, uint64_t label);
   void Close(Callback<void> quiesced) override final;
   void Forward(Message message) override final;
   void Process(TimeStamp received, Slice packet);
@@ -29,7 +31,6 @@
 
   Router* const router_;
   Timer* const timer_;
-  const TraceSink trace_sink_;
   const NodeId peer_;
   const uint64_t label_;
   uint64_t metrics_version_ = 1;
diff --git a/lib/overnet/packet_link_test.cc b/lib/overnet/links/packet_link_test.cc
similarity index 79%
rename from lib/overnet/packet_link_test.cc
rename to lib/overnet/links/packet_link_test.cc
index ed5e3b7..48160b0 100644
--- a/lib/overnet/packet_link_test.cc
+++ b/lib/overnet/links/packet_link_test.cc
@@ -3,10 +3,10 @@
 // found in the LICENSE file.
 
 #include "packet_link.h"
+#include "garnet/lib/overnet/testing/test_timer.h"
+#include "garnet/lib/overnet/testing/trace_cout.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "test_timer.h"
-#include "trace_cout.h"
 
 using testing::_;
 using testing::Mock;
@@ -31,7 +31,7 @@
 class MockStreamHandler : public Router::StreamHandler {
  public:
   MOCK_METHOD3(HandleMessage, void(SeqNum, TimeStamp, Slice));
-  void Close(Callback<void> quiesced) override {}
+  void RouterClose(Callback<void> quiesced) override {}
 };
 
 class MockPacketLink {
@@ -39,12 +39,12 @@
   MOCK_METHOD1(Emit, void(Slice));
 
   LinkPtr<PacketLink> MakeLink(Router* router, NodeId peer, uint32_t mss) {
+    static uint64_t next_label = 1;
     class PacketLinkImpl final : public PacketLink {
      public:
       PacketLinkImpl(MockPacketLink* mock, Router* router, NodeId peer,
                      uint32_t mss)
-          : PacketLink(router, TraceCout(router->timer()), peer, mss),
-            mock_(mock) {}
+          : PacketLink(router, peer, mss, next_label++), mock_(mock) {}
       void Emit(Slice packet) { mock_->Emit(std::move(packet)); }
 
      private:
@@ -56,20 +56,24 @@
 
 TEST(PacketLink, NoOp) {
   TestTimer timer;
+  TraceCout renderer(&timer);
+  ScopedRenderer scoped_renderer(&renderer);
   StrictMock<MockPacketLink> mock_link;
-  Router router(&timer, TraceCout(&timer), NodeId(1), true);
+  Router router(&timer, NodeId(1), true);
   router.RegisterLink(mock_link.MakeLink(&router, NodeId(2), kTestMSS));
 }
 
 TEST(PacketLink, SendOne) {
   TestTimer timer;
   StrictMock<MockPacketLink> mock_link;
+  TraceCout renderer(&timer);
+  ScopedRenderer scoped_renderer(&renderer);
 
   auto verify_all = [&]() {
     EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_link));
   };
 
-  Router router(&timer, TraceCout(&timer), NodeId(1), true);
+  Router router(&timer, NodeId(1), true);
   router.RegisterLink(mock_link.MakeLink(&router, NodeId(2), kTestMSS));
   while (!router.HasRouteTo(NodeId(2))) {
     router.BlockUntilNoBackgroundUpdatesProcessing();
@@ -93,8 +97,10 @@
   TestTimer timer;
   StrictMock<MockPacketLink> mock_link;
   StrictMock<MockStreamHandler> mock_stream_handler;
+  TraceCout renderer(&timer);
+  ScopedRenderer scoped_renderer(&renderer);
 
-  Router router(&timer, TraceCout(&timer), NodeId(2), true);
+  Router router(&timer, NodeId(2), true);
   auto link_unique = mock_link.MakeLink(&router, NodeId(1), kTestMSS);
   auto* link = link_unique.get();
   router.RegisterLink(std::move(link_unique));
@@ -102,7 +108,9 @@
     router.BlockUntilNoBackgroundUpdatesProcessing();
     timer.StepUntilNextEvent();
   }
-  router.RegisterStream(NodeId(1), StreamId(1), &mock_stream_handler);
+  EXPECT_TRUE(
+      router.RegisterStream(NodeId(1), StreamId(1), &mock_stream_handler)
+          .is_ok());
 
   EXPECT_CALL(mock_stream_handler, HandleMessage(_, _, _));
   link->Process(timer.Now(),
diff --git a/lib/overnet/packet_nub.h b/lib/overnet/links/packet_nub.h
similarity index 72%
rename from lib/overnet/packet_nub.h
rename to lib/overnet/links/packet_nub.h
index e72c0a2..e0788d3 100644
--- a/lib/overnet/packet_nub.h
+++ b/lib/overnet/links/packet_nub.h
@@ -5,10 +5,10 @@
 #pragma once
 
 #include <random>
-#include "node_id.h"
-#include "packet_link.h"
-#include "slice.h"
-#include "timer.h"
+#include "garnet/lib/overnet/environment/timer.h"
+#include "garnet/lib/overnet/labels/node_id.h"
+#include "garnet/lib/overnet/links/packet_link.h"
+#include "garnet/lib/overnet/vocabulary/slice.h"
 
 namespace overnet {
 
@@ -16,24 +16,20 @@
           class EqAddress = std::equal_to<Address>>
 class PacketNub {
  public:
+  static constexpr inline auto kModule = Module::NUB;
+
   static constexpr size_t kCallMeMaybeSize = 256;
   static constexpr size_t kHelloSize = 256;
   static constexpr uint64_t kAnnounceResendMillis = 1000;
 
-  PacketNub(Timer* timer, TraceSink trace_sink, NodeId node)
-      : timer_(timer),
-        trace_sink_(trace_sink.Decorate([this](const std::string& msg) {
-          std::ostringstream out;
-          out << "Nub[" << this << "] " << msg;
-          return out.str();
-        })),
-        local_node_(node) {}
+  PacketNub(Timer* timer, NodeId node) : timer_(timer), local_node_(node) {}
 
   virtual void SendTo(Address dest, Slice slice) = 0;
   virtual Router* GetRouter() = 0;
   virtual void Publish(LinkPtr<> link) = 0;
 
   void Process(TimeStamp received, Address src, Slice slice) {
+    ScopedModule<PacketNub> in_nub(this);
     // Extract node id and op from slice... this code must be identical with
     // PacketLink.
     const uint8_t* const begin = slice.begin();
@@ -41,7 +37,7 @@
     const uint8_t* const end = slice.end();
 
     if (p == end) {
-      OVERNET_TRACE(INFO, trace_sink_) << "Short packet received (no op code)";
+      OVERNET_TRACE(INFO) << "Short packet received (no op code)";
       return;
     }
     const PacketOp op = static_cast<PacketOp>(*p++);
@@ -49,7 +45,7 @@
     while (true) {
       Link* link = link_for(src);
       uint64_t node_id;
-      OVERNET_TRACE(DEBUG, link->trace_sink) << " op=" << static_cast<int>(op);
+      OVERNET_TRACE(DEBUG) << " op=" << static_cast<int>(op);
       switch (OpState(op, link->state)) {
         case OpState(PacketOp::Connected, LinkState::AckingHello):
           if (!link->SetState(LinkState::Connected)) {
@@ -75,19 +71,16 @@
           return;
         case OpState(PacketOp::CallMeMaybe, LinkState::Initial):
           if (end - begin != kCallMeMaybeSize) {
-            OVERNET_TRACE(INFO, link->trace_sink)
-                << "Received a mis-sized CallMeMaybe packet";
+            OVERNET_TRACE(INFO) << "Received a mis-sized CallMeMaybe packet";
           } else if (!ParseLE64(&p, end, &node_id)) {
-            OVERNET_TRACE(INFO, link->trace_sink)
+            OVERNET_TRACE(INFO)
                 << "Failed to parse node id from CallMeMaybe packet";
           } else if (!AllZeros(p, end)) {
-            OVERNET_TRACE(INFO, link->trace_sink)
-                << "CallMeMaybe padding should be all zeros";
+            OVERNET_TRACE(INFO) << "CallMeMaybe padding should be all zeros";
           } else if (NodeId(node_id) == local_node_) {
-            OVERNET_TRACE(INFO, link->trace_sink)
-                << "CallMeMaybe received for local node";
+            OVERNET_TRACE(INFO) << "CallMeMaybe received for local node";
           } else if (NodeId(node_id) < local_node_) {
-            OVERNET_TRACE(INFO, link->trace_sink)
+            OVERNET_TRACE(INFO)
                 << "CallMeMaybe received from a smaller numbered node id";
           } else {
             StartHello(src, NodeId(node_id));
@@ -96,28 +89,23 @@
         case OpState(PacketOp::Hello, LinkState::Initial):
         case OpState(PacketOp::Hello, LinkState::Announcing):
           if (end - begin != kHelloSize) {
-            OVERNET_TRACE(INFO, link->trace_sink)
-                << "Received a mis-sized Hello packet";
+            OVERNET_TRACE(INFO) << "Received a mis-sized Hello packet";
           } else if (!ParseLE64(&p, end, &node_id)) {
-            OVERNET_TRACE(INFO, link->trace_sink)
-                << "Failed to parse node id from Hello packet";
+            OVERNET_TRACE(INFO) << "Failed to parse node id from Hello packet";
           } else if (!AllZeros(p, end)) {
-            OVERNET_TRACE(INFO, link->trace_sink)
-                << "Hello padding should be all zeros";
+            OVERNET_TRACE(INFO) << "Hello padding should be all zeros";
           } else if (local_node_ < NodeId(node_id)) {
-            OVERNET_TRACE(INFO, link->trace_sink)
+            OVERNET_TRACE(INFO)
                 << "Hello received from a larger numbered node id";
           } else if (NodeId(node_id) == local_node_) {
-            OVERNET_TRACE(INFO, link->trace_sink)
-                << "Hello received for local node";
+            OVERNET_TRACE(INFO) << "Hello received for local node";
           } else {
             StartHelloAck(src, NodeId(node_id));
           }
           return;
         case OpState(PacketOp::HelloAck, LinkState::SayingHello):
           if (end != p) {
-            OVERNET_TRACE(INFO, link->trace_sink)
-                << "Received a mis-sized HelloAck packet";
+            OVERNET_TRACE(INFO) << "Received a mis-sized HelloAck packet";
           } else {
             // Must BecomePublished *AFTER* the state change, else the link
             // could be dropped immediately during publishing forcing an
@@ -127,18 +115,18 @@
           }
           return;
         default:
-          OVERNET_TRACE(INFO, link->trace_sink)
-              << "Received packet op " << static_cast<int>(op)
-              << " on link state " << static_cast<int>(link->state)
-              << ": ignoring.";
+          OVERNET_TRACE(INFO) << "Received packet op " << static_cast<int>(op)
+                              << " on link state "
+                              << static_cast<int>(link->state) << ": ignoring.";
           return;
       }
     }
   }
 
   void Initiate(Address peer, NodeId node) {
+    ScopedModule<PacketNub> in_nub(this);
     Link* link = link_for(peer);
-    OVERNET_TRACE(DEBUG, link->trace_sink) << "Initiate";
+    OVERNET_TRACE(DEBUG) << "Initiate";
     assert(node != local_node_);
     if (link->state == LinkState::Initial) {
       if (node < local_node_) {
@@ -154,15 +142,8 @@
  private:
   class NubLink final : public PacketLink {
    public:
-    NubLink(PacketNub* nub, Address address, NodeId peer)
-        : PacketLink(
-              nub->GetRouter(),
-              nub->trace_sink_.Decorate([address](const std::string& message) {
-                std::ostringstream out;
-                out << "addr:" << address << " " << message;
-                return out.str();
-              }),
-              peer, kMSS),
+    NubLink(PacketNub* nub, Address address, NodeId peer, uint64_t label)
+        : PacketLink(nub->GetRouter(), peer, kMSS, label),
           nub_(nub),
           address_(address) {}
 
@@ -206,15 +187,6 @@
   };
 
   struct Link {
-    explicit Link(Address addr, TraceSink trace_sink)
-        : trace_sink(
-              trace_sink.Decorate([this, addr](const std::string& message) {
-                std::ostringstream out;
-                out << "Link[" << this << ";addr=" << addr
-                    << ";node=" << node_id << ";st=" << static_cast<int>(state)
-                    << "] " << message;
-                return out.str();
-              })) {}
     ~Link() { assert(link == nullptr); }
 
     LinkState state = LinkState::Initial;
@@ -222,11 +194,9 @@
     NubLink* link = nullptr;
     int ticks = -1;
     Optional<Timeout> next_timeout;
-    const TraceSink trace_sink;
 
     Optional<int> SetState(LinkState st) {
-      OVERNET_TRACE(DEBUG, trace_sink)
-          << "SetState to " << static_cast<int>(st);
+      OVERNET_TRACE(DEBUG) << "SetState to " << static_cast<int>(st);
       next_timeout.Reset();
       if (state != st) {
         state = st;
@@ -243,7 +213,7 @@
       if (node) {
         if (node_id) {
           if (*node_id != *node) {
-            OVERNET_TRACE(DEBUG, trace_sink) << "Node id changed to " << *node;
+            OVERNET_TRACE(DEBUG) << "Node id changed to " << *node;
             return Nothing;
           }
         } else {
@@ -272,16 +242,7 @@
                initial_millis, millis)(rng_));
   }
 
-  Link* link_for(Address address) {
-    auto it = links_.find(address);
-    if (it != links_.end())
-      return &it->second;
-    return &links_
-                .emplace(std::piecewise_construct,
-                         std::forward_as_tuple(address),
-                         std::forward_as_tuple(address, trace_sink_))
-                .first->second;
-  }
+  Link* link_for(Address address) { return &links_[address]; }
 
   template <class F>
   void StartSimpleState(Address address, Optional<NodeId> node, LinkState state,
@@ -338,15 +299,15 @@
     Link* link = link_for(address);
     assert(link->link == nullptr);
     assert(link->node_id);
-    link->link = new NubLink(this, address, *link->node_id);
+    link->link = new NubLink(this, address, *link->node_id, next_label_++);
     Publish(LinkPtr<>(link->link));
   }
 
   Timer* const timer_;
-  const TraceSink trace_sink_;
   const NodeId local_node_;
   std::unordered_map<Address, Link, HashAddress, EqAddress> links_;
   std::mt19937_64 rng_;
+  uint64_t next_label_ = 1;
 };
 
 }  // namespace overnet
diff --git a/lib/overnet/packet_nub_test.cc b/lib/overnet/links/packet_nub_test.cc
similarity index 92%
rename from lib/overnet/packet_nub_test.cc
rename to lib/overnet/links/packet_nub_test.cc
index 2b0437f..3c30eeb 100644
--- a/lib/overnet/packet_nub_test.cc
+++ b/lib/overnet/links/packet_nub_test.cc
@@ -3,10 +3,10 @@
 // found in the LICENSE file.
 
 #include "packet_nub.h"
+#include "garnet/lib/overnet/testing/test_timer.h"
+#include "garnet/lib/overnet/testing/trace_cout.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "test_timer.h"
-#include "trace_cout.h"
 
 using testing::_;
 using testing::InvokeWithoutArgs;
@@ -22,8 +22,7 @@
 class MockPacketNub : public PacketNub<FakeAddress, 1024> {
  public:
   MockPacketNub(Timer* timer, NodeId node)
-      : PacketNub<FakeAddress, 1024>(timer, TraceCout(timer), node),
-        router_(timer, TraceCout(timer), node, true) {}
+      : PacketNub<FakeAddress, 1024>(timer, node), router_(timer, node, true) {}
 
   MOCK_METHOD2(SendTo, void(FakeAddress, Slice));
   MOCK_METHOD1(PublishMock, void(std::shared_ptr<LinkPtr<>>));
@@ -40,6 +39,8 @@
 
 TEST(PacketNub, NoOp) {
   TestTimer timer;
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
   StrictMock<MockPacketNub> nub(&timer, NodeId(1));
 }
 
@@ -70,6 +71,9 @@
 
 TEST(PacketNub, InitiateSmallerNodeId) {
   TestTimer timer;
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
+
   StrictMock<MockPacketNub> nub(&timer, NodeId(2));
 
   const auto kAnnounce =
@@ -96,6 +100,9 @@
 
 TEST(PacketNub, ProcessHandshakeFromAnnounce) {
   TestTimer timer;
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
+
   StrictMock<MockPacketNub> nub(&timer, NodeId(1));
 
   EXPECT_CALL(
@@ -130,6 +137,9 @@
 
 TEST(PacketNub, ProcessHandshakeFromHello) {
   TestTimer timer;
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
+
   StrictMock<MockPacketNub> nub(&timer, NodeId(2));
 
   EXPECT_CALL(nub,
@@ -152,6 +162,9 @@
 
 TEST(PacketNub, ProcessHandshakeFromAnnounceAndVerifyLink) {
   TestTimer timer;
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
+
   StrictMock<MockPacketNub> nub(&timer, NodeId(1));
 
   EXPECT_CALL(
@@ -191,6 +204,9 @@
 
 TEST(PacketNub, ProcessHandshakeFromHelloAndVerifyLink) {
   TestTimer timer;
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
+
   StrictMock<MockPacketNub> nub(&timer, NodeId(2));
 
   EXPECT_CALL(nub,
diff --git a/lib/overnet/manual_constructor.h b/lib/overnet/manual_constructor.h
deleted file mode 100644
index dd548df..0000000
--- a/lib/overnet/manual_constructor.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#pragma once
-
-#include <type_traits>
-#include <utility>
-
-namespace overnet {
-
-template <typename Type>
-class ManualConstructor {
- public:
-  ManualConstructor() {}
-  ~ManualConstructor() {}
-  ManualConstructor(const ManualConstructor&) = delete;
-  ManualConstructor& operator=(const ManualConstructor&) = delete;
-
-  Type* get() { return reinterpret_cast<Type*>(&space_); }
-  const Type* get() const { return reinterpret_cast<const Type*>(&space_); }
-
-  Type* operator->() { return get(); }
-  const Type* operator->() const { return get(); }
-
-  Type& operator*() { return *get(); }
-  const Type& operator*() const { return *get(); }
-
-  void Init() { new (&space_) Type; }
-
-  // Init() constructs the Type instance using the given arguments
-  // (which are forwarded to Type's constructor).
-  //
-  // Note that Init() with no arguments performs default-initialization,
-  // not zero-initialization (i.e it behaves the same as "new Type;", not
-  // "new Type();"), so it will leave non-class types uninitialized.
-  template <typename... Ts>
-  void Init(Ts&&... args) {
-    new (&space_) Type(std::forward<Ts>(args)...);
-  }
-
-  // Init() that is equivalent to copy and move construction.
-  // Enables usage like this:
-  //   ManualConstructor<std::vector<int>> v;
-  //   v.Init({1, 2, 3});
-  void Init(const Type& x) { new (&space_) Type(x); }
-  void Init(Type&& x) { new (&space_) Type(std::move(x)); }
-
-  void Destroy() { get()->~Type(); }
-
- private:
-  union {
-    Type space_;
-  };
-};
-
-}  // namespace overnet
diff --git a/lib/overnet/packet_protocol.cc b/lib/overnet/packet_protocol.cc
deleted file mode 100644
index 5a638b4..0000000
--- a/lib/overnet/packet_protocol.cc
+++ /dev/null
@@ -1,473 +0,0 @@
-// Copyright 2018 The Fuchsia 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 "packet_protocol.h"
-#include <iostream>
-
-namespace overnet {
-
-static const char kClosing[] = "Closing";
-static const char kMaybeScheduleAck[] = "MaybeScheduleAck";
-static const char kMaybeSendAck[] = "MaybeSendAck";
-static const char kRequestSendAck[] = "RequestSendAck";
-static const char kRequestTransmit[] = "RequestTransmit";
-static const char kScheduleRTO[] = "ScheduleRTO";
-static const char kStartNext[] = "StartNext";
-static const char kTransmitPacket[] = "TransmitPacket";
-
-const char PacketProtocol::kProcessedPacket[] = "ProcessedPacket";
-
-void PacketProtocol::Close(Callback<void> quiesced) {
-  assert(state_ == State::READY);
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "Close outstanding_ops=" << outstanding_ops_;
-  OutstandingOp<kClosing> op(this);
-  state_ = State::CLOSING;
-  quiesced_ = std::move(quiesced);
-  // Stop waiting for things.
-  rto_scheduler_.Reset();
-  ack_scheduler_.Reset();
-  outgoing_bbr_.CancelRequestTransmit();
-  NackAll();
-  decltype(queued_) queued;
-  queued.swap(queued_);
-  queued.clear();
-}
-
-void PacketProtocol::RequestSendAck() {
-  // Prevent quiescing during ack generation (otherwise cancelling a scheduled
-  // ack might cause us to quiesce).
-  OutstandingOp<kRequestSendAck> op(this);
-  MaybeForceAck();
-}
-
-void PacketProtocol::Send(LazySlice make_payload, SendCallback on_ack) {
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "Send state=" << static_cast<int>(state_)
-      << " qsize=" << queued_.size() << " outstanding=" << outstanding_.size()
-      << " sending=" << sending_.has_value();
-  if (state_ != State::READY) {
-    // Discard result, forcing callbacks to be made
-    return;
-  }
-  MaybeSendSlice(QueuedPacket{std::move(make_payload), std::move(on_ack)});
-}
-
-void PacketProtocol::MaybeSendSlice(QueuedPacket&& packet) {
-  if (!queued_.empty() || sending_) {
-    queued_.emplace_back(std::forward<QueuedPacket>(packet));
-    return;
-  }
-  SendSlice(std::forward<QueuedPacket>(packet));
-}
-
-void PacketProtocol::SendSlice(QueuedPacket&& packet) {
-  sending_.Reset(std::forward<QueuedPacket>(packet));
-  OVERNET_TRACE(DEBUG, trace_sink_) << "SendSlice send_tip=" << send_tip_
-                                    << " outstanding=" << outstanding_.size();
-  outgoing_bbr_.RequestTransmit([self = OutstandingOp<kRequestTransmit>(this)](
-                                    const Status& status) mutable {
-    if (status.is_error()) {
-      self->sending_->on_ack(status);
-      self->sending_.Reset();
-      return;
-    }
-    self->TransmitPacket();
-  });
-}
-
-void PacketProtocol::TransmitPacket() {
-  const uint64_t seq_idx = send_tip_ + outstanding_.size();
-  if (seq_idx - send_tip_ > max_outstanding_size_) {
-    max_outstanding_size_ = seq_idx - send_tip_;
-  }
-  SeqNum seq_num(seq_idx, max_outstanding_size_);
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "TransmitPacket seq=" << seq_idx << " -> " << seq_num
-      << " (send_tip=" << send_tip_ << ")";
-  if (outstanding_.empty()) {
-    KeepAlive();
-  }
-  outstanding_.emplace_back(
-      OutstandingPacket{max_seen_, Nothing, std::move(sending_->on_ack)});
-  auto send_fn = std::move(sending_->payload_factory);
-  send_fn.AddMutator([seq_idx, self = OutstandingOp<kTransmitPacket>(this)](
-                         auto payload, LazySliceArgs args) {
-    OVERNET_TRACE(DEBUG, self->trace_sink_) << "GeneratePacket seq=" << seq_idx;
-    const auto outstanding_idx = seq_idx - self->send_tip_;
-    if (outstanding_idx >= self->outstanding_.size())
-      return Slice();
-    if (self->outstanding_[outstanding_idx].on_ack.empty())
-      return Slice();
-    auto slice = self->GeneratePacket(std::move(payload), args);
-    assert(!self->outstanding_[outstanding_idx].bbr_sent_packet.has_value());
-    self->outstanding_[outstanding_idx].bbr_sent_packet =
-        self->outgoing_bbr_.ScheduleTransmit(
-            args.delay_until_time,
-            BBR::OutgoingPacket{seq_idx, slice.length()});
-    return slice;
-  });
-  packet_sender_->SendPacket(seq_num, std::move(send_fn),
-                             [self = OutstandingOp<kStartNext>(this)]() {
-                               self->sending_.Reset();
-                               self->ContinueSending();
-                             });
-}
-
-Slice PacketProtocol::GeneratePacket(LazySlice payload, LazySliceArgs args) {
-  auto ack = GenerateAck();
-  if (ack) {
-    AckFrame::Writer ack_writer(ack.get());
-    const uint8_t ack_length_length =
-        varint::WireSizeFor(ack_writer.wire_length());
-    const uint64_t prefix_length = ack_length_length + ack_writer.wire_length();
-    auto payload_slice = payload(LazySliceArgs{
-        args.desired_prefix + prefix_length, args.max_length - prefix_length,
-        true, args.delay_until_time});
-    return payload_slice.WithPrefix(
-        prefix_length, [&ack_writer, ack_length_length](uint8_t* p) {
-          ack_writer.Write(
-              varint::Write(ack_writer.wire_length(), ack_length_length, p));
-        });
-  } else {
-    auto payload_slice =
-        payload(LazySliceArgs{args.desired_prefix + 1, args.max_length - 1,
-                              args.has_other_content, args.delay_until_time});
-    return payload_slice.WithPrefix(1, [](uint8_t* p) { *p = 0; });
-  }
-}
-
-Status PacketProtocol::HandleAck(const AckFrame& ack) {
-  OVERNET_TRACE(DEBUG, trace_sink_) << "HandleAck: " << ack;
-
-  // TODO(ctiller): inline vectors to avoid allocations.
-  std::vector<SendCallback> acks;
-  std::vector<SendCallback> nacks;
-  BBR::Ack bbr_ack;
-
-  // Validate ack, and ignore if it's old.
-  if (ack.ack_to_seq() < send_tip_)
-    return Status::Ok();
-  if (ack.ack_to_seq() >= send_tip_ + outstanding_.size()) {
-    return Status(StatusCode::INVALID_ARGUMENT,
-                  "Ack packet past sending sequence");
-  }
-  // Move receive window forward.
-  auto new_recv_tip = outstanding_[ack.ack_to_seq() - send_tip_].ack_to_seq;
-  if (new_recv_tip != recv_tip_) {
-    assert(new_recv_tip > recv_tip_);
-    while (!received_packets_.empty()) {
-      auto it = received_packets_.begin();
-      if (it->first >= new_recv_tip) {
-        break;
-      }
-      received_packets_.erase(it);
-    }
-    recv_tip_ = new_recv_tip;
-  }
-  // Fail any nacked packets.
-  for (auto nack_seq : ack.nack_seqs()) {
-    if (nack_seq < send_tip_) {
-      continue;
-    }
-    if (nack_seq >= send_tip_ + outstanding_.size()) {
-      return Status(StatusCode::INVALID_ARGUMENT, "Nack past sending sequence");
-    }
-    OutstandingPacket& pkt = outstanding_[nack_seq - send_tip_];
-    auto cb = std::move(pkt.on_ack);
-    if (!cb.empty()) {
-      nacks.emplace_back(std::move(cb));
-    }
-    if (pkt.bbr_sent_packet.has_value()) {
-      bbr_ack.nacked_packets.push_back(*pkt.bbr_sent_packet);
-    }
-  }
-  // Clear out outstanding packet references, propagating acks.
-  while (send_tip_ <= ack.ack_to_seq()) {
-    OutstandingPacket& pkt = outstanding_.front();
-    auto cb = std::move(pkt.on_ack);
-    send_tip_++;
-    if (!cb.empty()) {
-      bbr_ack.acked_packets.push_back(*pkt.bbr_sent_packet);
-      outstanding_.pop_front();
-      acks.emplace_back(std::move(cb));
-    } else {
-      outstanding_.pop_front();
-    }
-  }
-  outgoing_bbr_.OnAck(bbr_ack);
-
-  for (auto& cb : nacks) {
-    cb(Status::Cancelled());
-  }
-  for (auto& cb : acks) {
-    cb(Status::Ok());
-  }
-
-  // Continue sending if we can
-  ContinueSending();
-
-  return Status::Ok();
-}
-
-void PacketProtocol::ContinueSending() {
-  while (!queued_.empty() && !sending_ && state_ == State::READY) {
-    QueuedPacket p = std::move(queued_.front());
-    queued_.pop_front();
-    SendSlice(std::move(p));
-  }
-  if (ack_after_sending_) {
-    ack_after_sending_ = false;
-    MaybeSendAck();
-  }
-}
-
-PacketProtocol::ProcessedPacket PacketProtocol::Process(TimeStamp received,
-                                                        SeqNum seq_num,
-                                                        Slice slice) {
-  OVERNET_TRACE(DEBUG, trace_sink_) << "Process: " << slice;
-
-  using StatusType = StatusOr<Optional<Slice>>;
-  OutstandingOp<kProcessedPacket> op(this);
-
-  // Validate sequence number, ignore if it's old.
-  const auto seq_idx = seq_num.Reconstruct(recv_tip_);
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "Receive sequence " << seq_num << "=" << seq_idx << " recv_tip "
-      << recv_tip_ << " max_seen=" << max_seen_;
-  if (seq_idx < recv_tip_) {
-    return ProcessedPacket(op, ProcessedPacket::Ack::NONE, Nothing);
-  }
-
-  // Keep track of the biggest valid sequence we've seen.
-  if (seq_idx > max_seen_) {
-    OVERNET_TRACE(DEBUG, trace_sink_) << "new max_seen";
-    max_seen_ = seq_idx;
-    max_seen_time_ = received;
-  }
-
-  KeepAlive();
-
-  const uint8_t* p = slice.begin();
-  const uint8_t* end = slice.end();
-
-  if (p == end) {
-    return ProcessedPacket(op, ProcessedPacket::Ack::NONE, Nothing);
-  }
-
-  uint64_t ack_length;
-  if (!varint::Read(&p, end, &ack_length)) {
-    return ProcessedPacket(
-        op, ProcessedPacket::Ack::NONE,
-        StatusType(StatusCode::INVALID_ARGUMENT,
-                   "Failed to parse ack length from message"));
-  }
-  slice.TrimBegin(p - slice.begin());
-
-  OVERNET_TRACE(DEBUG, trace_sink_) << "ack_length=" << ack_length;
-
-  if (ack_length > slice.length()) {
-    return ProcessedPacket(
-        op, ProcessedPacket::Ack::NONE,
-        StatusType(StatusCode::INVALID_ARGUMENT,
-                   "Ack frame claimed to be past end of message"));
-  }
-
-  ProcessedPacket::Ack ack = ProcessedPacket::Ack::NONE;
-
-  auto it = received_packets_.lower_bound(seq_idx);
-  if (it == received_packets_.end() || it->first != seq_idx) {
-    it = received_packets_.insert(
-        it, std::make_pair(seq_idx, ReceivedPacket{true, false}));
-  } else {
-    OVERNET_TRACE(DEBUG, trace_sink_)
-        << "frozen as " << (it->second.received ? "received" : "nack");
-    return ProcessedPacket(op, ProcessedPacket::Ack::NONE, Nothing);
-  }
-
-  const bool is_pure_ack = ack_length > 0 && ack_length == slice.length();
-  bool suppress_ack = is_pure_ack;
-  bool prev_was_also_suppressed = false;
-  bool prev_was_discontiguous = false;
-  if (suppress_ack && it != received_packets_.begin()) {
-    auto prev = std::prev(it);
-    if (prev->first != it->first - 1) {
-      suppress_ack = false;
-      prev_was_discontiguous = true;
-    } else if (prev->second.suppressed_ack) {
-      suppress_ack = false;
-      prev_was_also_suppressed = true;
-    }
-  }
-  if (suppress_ack && it->first != received_packets_.rbegin()->first) {
-    suppress_ack = false;
-  }
-  it->second.suppressed_ack = suppress_ack;
-
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "pure_ack=" << is_pure_ack << " suppress_ack=" << suppress_ack
-      << " is_last=" << (it->first == received_packets_.rbegin()->first)
-      << " prev_was_also_suppressed=" << prev_was_also_suppressed
-      << " prev_was_discontiguous=" << prev_was_discontiguous;
-
-  if (suppress_ack) {
-    OVERNET_TRACE(DEBUG, trace_sink_) << "ack suppressed";
-  } else {
-    if (seq_idx >= kMaxUnackedReceives &&
-        max_acked_ <= seq_idx - kMaxUnackedReceives) {
-      ack = ProcessedPacket::Ack::FORCE;
-    } else {
-      ack = ProcessedPacket::Ack::SCHEDULE;
-    }
-  }
-
-  if (ack_length == 0) {
-    return ProcessedPacket(op, ack, slice);
-  }
-
-  return ProcessedPacket(
-      op, ack,
-      AckFrame::Parse(slice.TakeUntilOffset(ack_length))
-          .Then([this](const AckFrame& frame) { return HandleAck(frame); })
-          .Then([&slice]() -> StatusType { return slice; }));
-}  // namespace overnet
-
-bool PacketProtocol::AckIsNeeded() const { return max_seen_ > recv_tip_; }
-
-Optional<AckFrame> PacketProtocol::GenerateAck() {
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "GenerateAck: max_seen=" << max_seen_ << " recv_tip=" << recv_tip_
-      << " n=" << (max_seen_ - recv_tip_);
-  if (!AckIsNeeded()) {
-    return Nothing;
-  }
-  if (last_ack_send_ + QuarterRTT() > timer_->Now()) {
-    MaybeScheduleAck();
-    return Nothing;
-  }
-  const auto now = timer_->Now();
-  last_ack_send_ = now;
-  assert(max_seen_time_ <= now);
-  AckFrame ack(max_seen_, (now - max_seen_time_).as_us());
-  if (max_seen_ >= 1) {
-    for (uint64_t seq = max_seen_ - 1; seq > recv_tip_; seq--) {
-      auto it = received_packets_.lower_bound(seq);
-      if (it == received_packets_.end() || it->first != seq) {
-        received_packets_.insert(
-            it, std::make_pair(seq, ReceivedPacket{false, false}));
-        ack.AddNack(seq);
-      } else if (!it->second.received) {
-        ack.AddNack(seq);
-      }
-    }
-  }
-  OVERNET_TRACE(DEBUG, trace_sink_) << "GenerateAck generates:" << ack;
-  return std::move(ack);
-}
-
-void PacketProtocol::MaybeForceAck() {
-  if (ack_scheduler_.has_value()) {
-    ack_scheduler_->Cancel();
-    ack_scheduler_.Reset();
-  }
-  MaybeSendAck();
-}
-
-TimeDelta PacketProtocol::QuarterRTT() const {
-  auto est = outgoing_bbr_.rtt();
-  if (est == TimeDelta::PositiveInf()) {
-    est = TimeDelta::FromMilliseconds(100);
-  }
-  return est / 4;
-}
-
-void PacketProtocol::MaybeScheduleAck() {
-  if (!ack_scheduler_.has_value()) {
-    ack_scheduler_.Reset(
-        timer_, timer_->Now() + QuarterRTT(),
-        [self = OutstandingOp<kMaybeScheduleAck>(this)](const Status& status) {
-          if (status.is_error())
-            return;
-          self->ack_scheduler_.Reset();
-          self->MaybeSendAck();
-        });
-  }
-}
-
-void PacketProtocol::MaybeSendAck() {
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "MaybeSendAck: max_seen=" << max_seen_ << " recv_tip=" << recv_tip_
-      << " n=" << (max_seen_ - recv_tip_) << " last_ack_send=" << last_ack_send_
-      << " 1/4-rtt=" << QuarterRTT() << " now=" << timer_->Now()
-      << " sending=" << (sending_ ? "true" : "false");
-  if (AckIsNeeded()) {
-    if (sending_) {
-      ack_after_sending_ = true;
-    } else if (last_ack_send_ + QuarterRTT() > timer_->Now()) {
-      MaybeScheduleAck();
-    } else {
-      Send([](auto) { return Slice(); },
-           [self = OutstandingOp<kMaybeSendAck>(this)](const Status& status) {
-             if (status.is_error() && self->state_ == State::READY) {
-               self->MaybeScheduleAck();
-             }
-           });
-    }
-  }
-}
-
-void PacketProtocol::KeepAlive() {
-  last_keepalive_event_ = timer_->Now();
-  OVERNET_TRACE(DEBUG, trace_sink_) << "KeepAlive " << last_keepalive_event_
-                                    << " rto=" << RetransmissionDeadline();
-  if (!rto_scheduler_ && state_ == State::READY) {
-    ScheduleRTO();
-  }
-}
-
-void PacketProtocol::ScheduleRTO() {
-  assert(state_ == State::READY);
-  rto_scheduler_.Reset(
-      timer_, RetransmissionDeadline(),
-      StatusCallback(
-          ALLOCATED_CALLBACK,  // TODO(ctiller): remove allocated
-          [self = OutstandingOp<kScheduleRTO>(this)](const Status& status) {
-            auto now = self->timer_->Now();
-            OVERNET_TRACE(DEBUG, self->trace_sink_)
-                << " RTO check: now=" << now << " status=" << status
-                << " rto=" << self->RetransmissionDeadline();
-            if (status.is_error()) {
-              if (now.after_epoch() == TimeDelta::PositiveInf()) {
-                // Shutting down - help by nacking everything.
-                self->NackAll();
-              }
-              return;
-            }
-            if (now >= self->RetransmissionDeadline()) {
-              self->rto_scheduler_.Reset();
-              self->NackAll();
-            } else {
-              self->ScheduleRTO();
-            }
-          }));
-}
-
-void PacketProtocol::NackAll() {
-  if (outstanding_.empty()) {
-    return;
-  }
-  auto last_sent = send_tip_ + outstanding_.size() - 1;
-  AckFrame f(last_sent, 0);
-  for (uint64_t i = last_sent; i >= send_tip_; i--) {
-    f.AddNack(i);
-  }
-  HandleAck(f);
-}
-
-TimeStamp PacketProtocol::RetransmissionDeadline() const {
-  auto rtt = std::min(outgoing_bbr_.rtt(), TimeDelta::FromSeconds(3));
-  return last_keepalive_event_ + 4 * rtt;
-}
-
-}  // namespace overnet
diff --git a/lib/overnet/packet_protocol.h b/lib/overnet/packet_protocol.h
deleted file mode 100644
index 3d71753..0000000
--- a/lib/overnet/packet_protocol.h
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#pragma once
-
-#include <deque>
-#include <map>
-#include "ack_frame.h"
-#include "bbr.h"
-#include "callback.h"
-#include "lazy_slice.h"
-#include "once_fn.h"
-#include "optional.h"
-#include "seq_num.h"
-#include "slice.h"
-#include "status.h"
-#include "timer.h"
-#include "trace.h"
-#include "varint.h"
-
-// Enable for indepth refcount debugging for packet protocol ops.
-// #define OVERNET_TRACE_PACKET_PROTOCOL_OPS
-
-namespace overnet {
-
-class PacketProtocol {
- public:
-  class PacketSender {
-   public:
-    virtual void SendPacket(SeqNum seq, LazySlice data,
-                            Callback<void> done) = 0;
-  };
-
-  static constexpr size_t kMaxUnackedReceives = 3;
-
-  PacketProtocol(Timer* timer, PacketSender* packet_sender,
-                 TraceSink trace_sink, uint64_t mss)
-      : timer_(timer),
-        packet_sender_(packet_sender),
-        trace_sink_(trace_sink.Decorate([this](const std::string& msg) {
-          std::ostringstream out;
-          out << "PktProto[" << this << "] " << msg;
-          return out.str();
-        })),
-        mss_(mss),
-        outgoing_bbr_(timer_, trace_sink_, mss_, Nothing) {}
-
-  void Close(Callback<void> quiesced);
-
-  ~PacketProtocol() { assert(state_ == State::CLOSED); }
-
-  uint32_t mss() const { return mss_; }
-
-  using SendCallback = Callback<Status, 16 * sizeof(void*)>;
-
-  void Send(LazySlice make_payload, SendCallback on_ack);
-
-  void RequestSendAck();
-
-  Bandwidth BottleneckBandwidth() {
-    return outgoing_bbr_.bottleneck_bandwidth();
-  }
-
-  TimeDelta RoundTripTime() { return outgoing_bbr_.rtt(); }
-
- private:
-  // Placing an OutstandingOp on a PacketProtocol object prevents it from
-  // quiescing
-  template <const char* kWTF>
-  class OutstandingOp {
-   public:
-    OutstandingOp() = delete;
-    OutstandingOp(PacketProtocol* pp) : pp_(pp) { pp_->BeginOp(why(), this); }
-    OutstandingOp(const OutstandingOp& other) : pp_(other.pp_) {
-      pp_->BeginOp(why(), this);
-    }
-    OutstandingOp& operator=(OutstandingOp other) {
-      other.Swap(this);
-      return *this;
-    }
-    void Swap(OutstandingOp* other) { std::swap(pp_, other->pp_); }
-
-    const char* why() { return kWTF; }
-
-    ~OutstandingOp() { pp_->EndOp(why(), this); }
-    PacketProtocol* operator->() const { return pp_; }
-    PacketProtocol* get() const { return pp_; }
-
-   private:
-    PacketProtocol* pp_;
-  };
-
- public:
-  static const char kProcessedPacket[];
-
-  class ProcessedPacket {
-   public:
-    ProcessedPacket(const ProcessedPacket&) = delete;
-    ProcessedPacket& operator=(const ProcessedPacket&) = delete;
-    ProcessedPacket(ProcessedPacket&&) = default;
-    ProcessedPacket& operator=(ProcessedPacket&&) = default;
-
-    StatusOr<Optional<Slice>> status;
-
-    ~ProcessedPacket() {
-      switch (ack_) {
-        case Ack::NONE:
-          break;
-        case Ack::FORCE:
-          protocol_->MaybeForceAck();
-          break;
-        case Ack::SCHEDULE:
-          protocol_->MaybeScheduleAck();
-          break;
-      }
-    }
-
-   private:
-    enum class Ack { NONE, FORCE, SCHEDULE };
-
-    friend class PacketProtocol;
-    ProcessedPacket(OutstandingOp<kProcessedPacket> protocol, Ack ack,
-                    StatusOr<Optional<Slice>> result)
-        : status(std::move(result)), ack_(ack), protocol_(protocol) {}
-
-    Ack ack_;
-    OutstandingOp<kProcessedPacket> protocol_;
-  };
-
-  ProcessedPacket Process(TimeStamp received, SeqNum seq, Slice slice);
-
- private:
-  struct OutstandingPacket {
-    uint64_t ack_to_seq;
-    Optional<BBR::SentPacket> bbr_sent_packet;
-    SendCallback on_ack;
-  };
-
-  struct QueuedPacket {
-    LazySlice payload_factory;
-    SendCallback on_ack;
-  };
-
-  bool AckIsNeeded() const;
-  TimeDelta QuarterRTT() const;
-  void MaybeForceAck();
-  void MaybeScheduleAck();
-  void MaybeSendAck();
-  void MaybeSendSlice(QueuedPacket&& packet);
-  void SendSlice(QueuedPacket&& packet);
-  void TransmitPacket();
-  Status HandleAck(const AckFrame& ack);
-  void ContinueSending();
-  void KeepAlive();
-  TimeStamp RetransmissionDeadline() const;
-  void ScheduleRTO();
-  void NackAll();
-  void BeginOp(const char* name, void* whom) {
-#ifdef OVERNET_TRACE_PACKET_PROTOCOL_OPS
-    OVERNET_TRACE(DEBUG, trace_sink_) << " BEG " << name << " " << whom;
-#endif
-    ++outstanding_ops_;
-  }
-  void EndOp(const char* name, void* whom) {
-#ifdef OVERNET_TRACE_PACKET_PROTOCOL_OPS
-    OVERNET_TRACE(DEBUG, trace_sink_) << " END " << name << " " << whom;
-#endif
-    if (0 == --outstanding_ops_ && state_ == State::CLOSING) {
-      state_ = State::CLOSED;
-      auto cb = std::move(quiesced_);
-      cb();
-    }
-  }
-
-  Optional<AckFrame> GenerateAck();
-  Slice GeneratePacket(LazySlice payload, LazySliceArgs args);
-
-  Timer* const timer_;
-  PacketSender* const packet_sender_;
-  const TraceSink trace_sink_;
-  const uint64_t mss_;
-
-  enum class State { READY, CLOSING, CLOSED };
-
-  State state_ = State::READY;
-  Callback<void> quiesced_;
-
-  BBR outgoing_bbr_;
-
-  // TODO(ctiller): can we move to a ring buffer here? - the idea would be to
-  // just finished(RESOURCE_EXHAUSTED) if the ring is full
-  uint64_t send_tip_ = 1;
-  std::deque<OutstandingPacket> outstanding_;
-  std::deque<QueuedPacket> queued_;
-  Optional<QueuedPacket> sending_;
-
-  uint64_t recv_tip_ = 0;
-  uint64_t max_seen_ = 0;
-  TimeStamp max_seen_time_ = TimeStamp::Epoch();
-  uint64_t max_acked_ = 0;
-  uint64_t max_outstanding_size_ = 0;
-
-  // TODO(ctiller): Find a more efficient data structure.
-  struct ReceivedPacket {
-    bool received;
-    bool suppressed_ack;
-  };
-  std::map<uint64_t, ReceivedPacket> received_packets_;
-
-  TimeStamp last_keepalive_event_ = TimeStamp::Epoch();
-  TimeStamp last_ack_send_ = TimeStamp::Epoch();
-  bool ack_after_sending_ = false;
-
-  int outstanding_ops_ = 0;
-
-  Optional<Timeout> ack_scheduler_;
-  Optional<Timeout> rto_scheduler_;
-};
-
-}  // namespace overnet
diff --git a/lib/overnet/packet_protocol/BUILD.gn b/lib/overnet/packet_protocol/BUILD.gn
new file mode 100644
index 0000000..b559685
--- /dev/null
+++ b/lib/overnet/packet_protocol/BUILD.gn
@@ -0,0 +1,160 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/fuzzing/fuzzer.gni")
+
+###############################################################################
+# AGGREGATE LIBRARIES
+
+source_set("lib") {
+}
+
+source_set("tests") {
+  testonly = true
+  deps = [
+    ":aead_codec_test",
+    ":bbr_test",
+    ":packet_protocol_test",
+  ]
+}
+
+###############################################################################
+
+# aead_codec
+source_set("aead_codec") {
+  sources = [
+    "aead_codec.cc",
+    "aead_codec.h",
+  ]
+  public_deps = [
+    "//third_party/boringssl:crypto",
+  ]
+  deps = [
+    ":packet_protocol",
+    "//garnet/lib/overnet/protocol:serialization_helpers",
+  ]
+}
+
+source_set("aead_codec_test") {
+  testonly = true
+  sources = [
+    "aead_codec_test.cc",
+  ]
+  deps = [
+    ":aead_codec",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# bbr
+source_set("bbr") {
+  sources = [
+    "bbr.cc",
+    "bbr.h",
+  ]
+  deps = [
+    ":windowed_filter",
+    "//garnet/lib/overnet/environment:timer",
+    "//garnet/lib/overnet/environment:trace",
+    "//garnet/lib/overnet/vocabulary:bandwidth",
+    "//garnet/lib/overnet/vocabulary:callback",
+    "//garnet/lib/overnet/vocabulary:optional",
+  ]
+}
+
+source_set("bbr_fuzzer_helpers") {
+  sources = [
+    "bbr_fuzzer_helpers.h",
+  ]
+  deps = [
+    ":bbr",
+  ]
+}
+
+source_set("bbr_test") {
+  testonly = true
+  sources = [
+    "bbr_test.cc",
+  ]
+  deps = [
+    ":bbr",
+    ":bbr_fuzzer_helpers",
+    "//garnet/lib/overnet/testing:csv_writer",
+    "//garnet/lib/overnet/testing:test_timer",
+    "//garnet/lib/overnet/testing:trace_cout",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# packet_protocol
+source_set("packet_protocol") {
+  sources = [
+    "packet_protocol.cc",
+    "packet_protocol.h",
+  ]
+  deps = [
+    ":bbr",
+    "//garnet/lib/overnet/environment:timer",
+    "//garnet/lib/overnet/environment:trace",
+    "//garnet/lib/overnet/labels:seq_num",
+    "//garnet/lib/overnet/protocol:ack_frame",
+    "//garnet/lib/overnet/protocol:varint",
+    "//garnet/lib/overnet/vocabulary:callback",
+    "//garnet/lib/overnet/vocabulary:lazy_slice",
+    "//garnet/lib/overnet/vocabulary:once_fn",
+    "//garnet/lib/overnet/vocabulary:optional",
+    "//garnet/lib/overnet/vocabulary:slice",
+    "//garnet/lib/overnet/vocabulary:status",
+  ]
+}
+
+source_set("packet_protocol_fuzzer_helpers") {
+  testonly = true
+  sources = [
+    "packet_protocol_fuzzer_helpers.h",
+  ]
+  deps = [
+    ":packet_protocol",
+    "//garnet/lib/overnet/testing:test_timer",
+    "//garnet/lib/overnet/testing:trace_cout",
+    "//garnet/lib/overnet/vocabulary:closed_ptr",
+  ]
+}
+
+source_set("packet_protocol_test") {
+  testonly = true
+  sources = [
+    "packet_protocol_test.cc",
+  ]
+  deps = [
+    ":aead_codec",
+    ":packet_protocol",
+    ":packet_protocol_fuzzer_helpers",
+    "//garnet/lib/overnet/protocol:serialization_helpers",
+    "//garnet/lib/overnet/testing:test_timer",
+    "//garnet/lib/overnet/testing:trace_cout",
+    "//garnet/lib/overnet/vocabulary:closed_ptr",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+fuzz_target("packet_protocol_fuzzer") {
+  testonly = true
+  sources = [
+    "packet_protocol_fuzzer.cc",
+  ]
+  deps = [
+    ":packet_protocol_fuzzer_helpers",
+  ]
+}
+
+# windowed_filter
+source_set("windowed_filter") {
+  sources = [
+    "windowed_filter.h",
+  ]
+}
diff --git a/lib/overnet/packet_protocol/aead_codec.cc b/lib/overnet/packet_protocol/aead_codec.cc
new file mode 100644
index 0000000..433bf82
--- /dev/null
+++ b/lib/overnet/packet_protocol/aead_codec.cc
@@ -0,0 +1,68 @@
+// Copyright 2018 The Fuchsia 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 "aead_codec.h"
+#include <openssl/err.h>
+
+namespace overnet {
+
+StatusOr<Slice> AEADCodec::Encode(uint64_t seq_idx, Slice data) const {
+  EVP_AEAD_CTX ctx;
+  if (EVP_AEAD_CTX_init(&ctx, aead_, key_.data(), key_len_,
+                        EVP_AEAD_DEFAULT_TAG_LENGTH, nullptr) != 1) {
+    return PopError();
+  }
+  size_t new_length;
+  size_t orig_length = data.length();
+  bool ssl_error = false;
+  data = data.WithBorders(
+      Border::Suffix(border.suffix),
+      [this, seq_idx, orig_length, &ctx, &new_length, &ssl_error](uint8_t* p) {
+        auto nonce = Noncify(seq_idx);
+        ssl_error =
+            EVP_AEAD_CTX_seal(&ctx, p, &new_length, orig_length + border.suffix,
+                              nonce.data(), nonce_len_, p, orig_length,
+                              ad_.data(), ad_len_) != 1;
+      });
+  EVP_AEAD_CTX_cleanup(&ctx);
+  if (ssl_error) {
+    return PopError();
+  }
+  return data.ToOffset(new_length);
+}
+
+StatusOr<Slice> AEADCodec::Decode(uint64_t seq_idx, Slice data) const {
+  EVP_AEAD_CTX ctx;
+  if (EVP_AEAD_CTX_init(&ctx, aead_, key_.data(), key_len_,
+                        EVP_AEAD_DEFAULT_TAG_LENGTH, nullptr) != 1) {
+    return PopError();
+  }
+  size_t new_length;
+  size_t orig_length = data.length();
+  bool ssl_error = false;
+  data = data.MutateUnique([this, seq_idx, orig_length, &ctx, &new_length,
+                            &ssl_error](uint8_t* p) {
+    auto nonce = Noncify(seq_idx);
+    ssl_error =
+        EVP_AEAD_CTX_open(&ctx, p, &new_length, orig_length, nonce.data(),
+                          nonce_len_, p, orig_length, ad_.data(), ad_len_) != 1;
+  });
+  EVP_AEAD_CTX_cleanup(&ctx);
+  if (ssl_error) {
+    return PopError();
+  }
+  return data.ToOffset(new_length);
+}
+
+Status AEADCodec::PopError() {
+  const char* file;
+  int line;
+  uint32_t err = ERR_get_error_line(&file, &line);
+  std::ostringstream msg;
+  msg << "SSL_error:" << err << " from " << file << ":" << line << ": "
+      << ERR_reason_error_string(err);
+  return Status(StatusCode::UNKNOWN, msg.str());
+}
+
+}  // namespace overnet
\ No newline at end of file
diff --git a/lib/overnet/packet_protocol/aead_codec.h b/lib/overnet/packet_protocol/aead_codec.h
new file mode 100644
index 0000000..37b53be
--- /dev/null
+++ b/lib/overnet/packet_protocol/aead_codec.h
@@ -0,0 +1,63 @@
+// Copyright 2018 The Fuchsia 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 <openssl/aead.h>
+#include <array>
+#include "garnet/lib/overnet/packet_protocol/packet_protocol.h"
+#include "garnet/lib/overnet/protocol/serialization_helpers.h"
+
+namespace overnet {
+
+class AEADCodec final : public PacketProtocol::Codec {
+ public:
+  static const auto inline kMaxKeyLength = EVP_AEAD_MAX_KEY_LENGTH;
+  static const auto inline kMaxADLength = 32;
+
+  AEADCodec(const EVP_AEAD* aead, const uint8_t* key, size_t key_len,
+            const uint8_t* ad, size_t ad_len)
+      : Codec(Border::Suffix(EVP_AEAD_max_overhead(aead))),
+        aead_(aead),
+        key_(Copy<uint8_t, kMaxKeyLength>(key, key_len)),
+        ad_(Copy<uint8_t, kMaxADLength>(ad, ad_len)),
+        key_len_(key_len),
+        ad_len_(ad_len),
+        nonce_len_(EVP_AEAD_nonce_length(aead_)) {}
+
+  AEADCodec(const AEADCodec&) = delete;
+  AEADCodec& operator=(const AEADCodec&) = delete;
+
+  StatusOr<Slice> Encode(uint64_t seq_idx, Slice data) const;
+  StatusOr<Slice> Decode(uint64_t seq_idx, Slice data) const;
+
+ private:
+  static Status PopError();
+
+  template <class T, size_t kLength>
+  static std::array<T, kLength> Copy(const T* data, size_t length) {
+    if (data == nullptr) {
+      assert(length == 0);
+      return {};
+    }
+    assert(length <= kLength);
+    std::array<T, kLength> out;
+    std::copy(data, data + length, out.begin());
+    return out;
+  }
+
+  std::array<uint8_t, EVP_AEAD_MAX_NONCE_LENGTH> Noncify(uint64_t x) const {
+    std::array<uint8_t, EVP_AEAD_MAX_NONCE_LENGTH> out;
+    std::fill(out.begin(), out.begin() + nonce_len_ - sizeof(uint64_t), 0);
+    WriteLE64(x, out.begin() + nonce_len_ - sizeof(uint64_t));
+    return out;
+  }
+
+  const EVP_AEAD* const aead_;
+  const std::array<uint8_t, kMaxKeyLength> key_;
+  const std::array<uint8_t, kMaxADLength> ad_;
+  const uint8_t key_len_;
+  const uint8_t ad_len_;
+  const uint8_t nonce_len_;
+};
+
+}  // namespace overnet
diff --git a/lib/overnet/packet_protocol/aead_codec_test.cc b/lib/overnet/packet_protocol/aead_codec_test.cc
new file mode 100644
index 0000000..d719cc6
--- /dev/null
+++ b/lib/overnet/packet_protocol/aead_codec_test.cc
@@ -0,0 +1,80 @@
+// Copyright 2018 The Fuchsia 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 "aead_codec.h"
+#include <random>
+#include "gtest/gtest.h"
+
+namespace overnet {
+namespace aead_codec_test {
+
+struct TestArgs {
+  const EVP_AEAD* aead;
+  std::vector<uint64_t> seqs;
+  Slice payload;
+};
+
+class AEADCodec : public ::testing::TestWithParam<TestArgs> {};
+
+TEST_P(AEADCodec, Basics) {
+  std::vector<uint8_t> key;
+  std::random_device rng;
+  for (size_t i = 0; i < EVP_AEAD_key_length(GetParam().aead); i++) {
+    key.push_back(rng());
+  }
+
+  const char* ad = "HELLO!";
+
+  overnet::AEADCodec codec1(GetParam().aead, key.data(), key.size(),
+                            reinterpret_cast<const uint8_t*>(ad), strlen(ad));
+  overnet::AEADCodec codec2(GetParam().aead, key.data(), key.size(),
+                            reinterpret_cast<const uint8_t*>(ad), strlen(ad));
+
+  std::vector<Slice> encoded;
+  for (uint64_t seq : GetParam().seqs) {
+    auto enc = codec1.Encode(seq, GetParam().payload);
+    ASSERT_TRUE(enc.is_ok()) << enc;
+    encoded.push_back(*enc);
+    EXPECT_NE(*enc, GetParam().payload);
+    auto dec = codec2.Decode(seq, *enc);
+    ASSERT_TRUE(dec.is_ok()) << dec;
+    EXPECT_NE(*dec, encoded.back());
+    EXPECT_EQ(*dec, GetParam().payload);
+  }
+
+  for (size_t i = 0; i < encoded.size(); i++) {
+    for (size_t j = i + 1; j < encoded.size(); j++) {
+      EXPECT_NE(encoded[i], encoded[j]) << "i=" << i << " j=" << j;
+    }
+  }
+}
+
+const auto kTestCases = [] {
+  std::vector<TestArgs> out;
+  for (auto aead : {
+           EVP_aead_aes_128_gcm(),
+           EVP_aead_aes_256_gcm(),
+           EVP_aead_chacha20_poly1305(),
+           EVP_aead_xchacha20_poly1305(),
+           EVP_aead_aes_128_ctr_hmac_sha256(),
+           EVP_aead_aes_256_ctr_hmac_sha256(),
+           EVP_aead_aes_128_gcm_siv(),
+           EVP_aead_aes_256_gcm_siv(),
+       }) {
+    out.push_back(TestArgs{aead,
+                           {1, 2, 3, 5, 8, 13, 21, 34},
+                           Slice::FromContainer({1, 2, 3, 4, 5, 6, 7, 8})});
+    out.push_back(TestArgs{aead,
+                           {0x123456789abcdefull, 123, 321},
+                           Slice::RepeatedChar(1024 * 1024, 'a')});
+  }
+  return out;
+}();
+
+INSTANTIATE_TEST_CASE_P(AEADCodecTest, AEADCodec,
+                        ::testing::ValuesIn(kTestCases.begin(),
+                                            kTestCases.end()));
+
+}  // namespace aead_codec_test
+}  // namespace overnet
diff --git a/lib/overnet/bbr.cc b/lib/overnet/packet_protocol/bbr.cc
similarity index 72%
rename from lib/overnet/bbr.cc
rename to lib/overnet/packet_protocol/bbr.cc
index 27ebe64..329cfc4 100644
--- a/lib/overnet/bbr.cc
+++ b/lib/overnet/packet_protocol/bbr.cc
@@ -25,14 +25,12 @@
   return sum;
 }
 
-BBR::BBR(Timer* timer, TraceSink trace_sink, uint32_t mss,
-         Optional<TimeDelta> srtt)
+BBR::BBR(Timer* timer, RandFunc rand, uint32_t mss, Optional<TimeDelta> srtt)
     : timer_(timer),
-      trace_sink_(trace_sink.Decorate(
-          [](const std::string& message) { return "BBR " + message; })),
       rtprop_(srtt.ValueOr(TimeDelta::PositiveInf())),
       rtprop_stamp_(timer->Now()),
-      mss_(mss) {
+      mss_(mss),
+      rand_(rand) {
   UpdateTargetCwnd();
   ValidateState();
 }
@@ -55,7 +53,7 @@
   state_ = State::ProbeBW;
   pacing_gain_ = UnitGain();
   cwnd_gain_ = Gain{2, 1};
-  cycle_index_ = 1 + rand() % (kProbeBWGainCycleLength - 1);
+  cycle_index_ = 1 + rand_() % (kProbeBWGainCycleLength - 1);
   AdvanceCyclePhase(now, ack);
 }
 
@@ -166,14 +164,14 @@
 }
 
 void BBR::OnAck(const Ack& ack) {
+  ScopedModule<BBR> in_bbr(this);
   ValidateState();
   const auto now = timer_->Now();
   prior_inflight_ = Inflight(UnitGain());
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "ack " << ack.acked_packets.size() << " nack "
-      << ack.nacked_packets.size()
-      << " packets_in_flight=" << packets_in_flight_
-      << " bytes_in_flight=" << bytes_in_flight_;
+  OVERNET_TRACE(DEBUG) << "ack " << ack.acked_packets.size() << " nack "
+                       << ack.nacked_packets.size()
+                       << " packets_in_flight=" << packets_in_flight_
+                       << " bytes_in_flight=" << bytes_in_flight_;
   assert(packets_in_flight_ >=
          ack.acked_packets.size() + ack.nacked_packets.size());
   packets_in_flight_ -= ack.acked_packets.size();
@@ -184,9 +182,9 @@
   bytes_in_flight_ -= SumBytes(ack.nacked_packets);
   UpdateModelAndState(now, ack);
   UpdateControlParameters(ack);
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "end-ack packets_in_flight=" << packets_in_flight_
-      << " bytes_in_flight=" << bytes_in_flight_ << " cwnd=" << cwnd_bytes_;
+  OVERNET_TRACE(DEBUG) << "end-ack packets_in_flight=" << packets_in_flight_
+                       << " bytes_in_flight=" << bytes_in_flight_
+                       << " cwnd=" << cwnd_bytes_;
   if (bytes_in_flight_ < cwnd_bytes_ && queued_packet_) {
     QueuedPacketReady();
   }
@@ -194,12 +192,17 @@
 }
 
 void BBR::RequestTransmit(StatusCallback ready) {
+  ScopedModule<BBR> in_bbr(this);
   ValidateState();
-  if (queued_packet_)
+  if (queued_packet_) {
+    assert(false);
     return;
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "RequestTransmit: packets_in_flight=" << packets_in_flight_
-      << " bytes_in_flight=" << bytes_in_flight_ << " cwnd=" << cwnd_bytes_;
+  }
+  OVERNET_TRACE(DEBUG) << "RequestTransmit: packets_in_flight="
+                       << packets_in_flight_
+                       << " bytes_in_flight=" << bytes_in_flight_
+                       << " cwnd=" << cwnd_bytes_
+                       << (bytes_in_flight_ >= cwnd_bytes_ ? " PAUSED" : "");
   queued_packet_.Reset(std::move(ready));
   queued_packet_paused_ = bytes_in_flight_ >= cwnd_bytes_;
   if (!queued_packet_paused_) {
@@ -209,6 +212,11 @@
 }
 
 void BBR::QueuedPacketReady() {
+  OVERNET_TRACE(DEBUG) << "QueuedPacketReady: packets_in_flight="
+                       << packets_in_flight_
+                       << " bytes_in_flight=" << bytes_in_flight_
+                       << " cwnd=" << cwnd_bytes_
+                       << " last_send_time=" << last_send_time_;
   assert(queued_packet_);
   HandleRestartFromIdle();
   packets_in_flight_++;
@@ -217,39 +225,53 @@
   // This prevents accidental floods of messages getting queued in lower
   // layers.
   bytes_in_flight_ += mss_;
-  queued_packet_.Take()(Status::Ok());
+  queued_packet_timeout_.Reset(timer_, last_send_time_, queued_packet_.Take());
 }
 
 void BBR::CancelRequestTransmit() {
+  ScopedModule<BBR> in_bbr(this);
   ValidateState();
+  queued_packet_timeout_.Reset();
   queued_packet_.Reset();
   ValidateState();
 }
 
-BBR::SentPacket BBR::ScheduleTransmit(TimeStamp* overall_send_time,
-                                      OutgoingPacket packet) {
-  assert(overall_send_time != nullptr);
+BBR::SentPacket BBR::ScheduleTransmit(OutgoingPacket packet) {
+  ScopedModule<BBR> in_bbr(this);
 
   ValidateState();
 
+  if (packet.sequence == 1) {
+    delivered_time_ = timer_->Now();
+  }
+
   // Subtract out the reserved mss packet length that we applied in
   // QueuedPacketReady first.
   assert(bytes_in_flight_ >= mss_);
   bytes_in_flight_ -= mss_;
   bytes_in_flight_ += packet.size;
 
-  assert(packet.sequence > last_sent_packet_);
+  OVERNET_TRACE(DEBUG) << "ScheduleTransmit packet.sequence=" << packet.sequence
+                       << " packet.size=" << packet.size
+                       << " last_sent_packet=" << last_sent_packet_;
+
+  assert(packet.sequence == last_sent_packet_ + 1);
   last_sent_packet_ = packet.sequence;
 
   const auto now = timer_->Now();
   TimeStamp send_time =
-      last_send_time_ + PacingRate().SendTimeForBytes(packet.size);
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "ScheduleTransmit bytes_in_flight=" << bytes_in_flight_
-      << " mss=" << mss_ << " packet_size=" << packet.size << " now=" << now
-      << " pacing_rate=" << PacingRate()
-      << " last_send_time=" << last_send_time_
-      << " initial_send_time=" << send_time;
+      last_send_time_ + std::max(TimeDelta::FromMicroseconds(1),
+                                 PacingRate().SendTimeForBytes(packet.size));
+  OVERNET_TRACE(DEBUG) << "ScheduleTransmit bytes_in_flight="
+                       << bytes_in_flight_ << " mss=" << mss_
+                       << " packet_size=" << packet.size
+                       << " pacing_rate=" << PacingRate()
+                       << " send_time_for_bytes="
+                       << PacingRate().SendTimeForBytes(packet.size)
+                       << " last_send_time=" << last_send_time_ << " ("
+                       << (timer_->Now() - last_send_time_) << " ago)"
+                       << " initial_send_time=" << send_time
+                       << " rtt=" << rtt();
   if (send_time < now) {
     send_time = now;
   } else if (queued_packet_paused_) {
@@ -257,10 +279,14 @@
         delivered_seq_ + std::max(packets_in_flight_, uint64_t(1));
   }
   std::swap(last_send_time_, send_time);
-  *overall_send_time = std::max(send_time, *overall_send_time);
 
   ValidateState();
 
+  if (bytes_in_flight_ < cwnd_bytes_ && queued_packet_) {
+    // Releasing the reservation might mean another packet can come through!
+    QueuedPacketReady();
+  }
+
   return SentPacket{packet,
                     delivered_bytes_,
                     recovery_ == Recovery::Fast,
@@ -286,7 +312,10 @@
 
   for (const SentPacket& p : ack.acked_packets) {
     delivered_bytes_ += p.outgoing.size;
-    assert(now >= p.send_time);
+    OVERNET_TRACE(DEBUG) << "ack: sent=" << p.send_time
+                         << " delivered_time_at_send="
+                         << p.delivered_time_at_send
+                         << " size=" << p.outgoing.size;
   }
 
   const SentPacket& back = ack.acked_packets.back();
@@ -302,12 +331,19 @@
 
   const uint64_t delivered = delivered_bytes_ - back.delivered_bytes_at_send;
 
+  OVERNET_TRACE(DEBUG) << "SampleBandwidth: interval=" << interval
+                       << " delivered=" << delivered
+                       << " delivered_time_at_send="
+                       << back.delivered_time_at_send;
+
   if (interval < kMinRTT) {
     return RateSample{Bandwidth::Zero(), TimeDelta::NegativeInf(), false};
   }
+  OVERNET_TRACE(DEBUG) << "  => BW="
+                       << Bandwidth::BytesPerTime(delivered, interval);
   return RateSample{
       Bandwidth::BytesPerTime(delivered, interval),
-      now - back.send_time,
+      std::max(TimeDelta::Zero(), now - back.send_time),
       back.is_app_limited,
   };
 }
@@ -319,24 +355,38 @@
 }
 
 void BBR::UpdateBtlBw(const Ack& ack, const RateSample& rs) {
-  UpdateRound(ack);
+  if (!UpdateRound(ack)) {
+    return;
+  }
+  OVERNET_TRACE(DEBUG)
+      << "Add BW est?: round_count=" << round_count_
+      << " delivery_rate=" << rs.delivery_rate
+      << " app_limited=" << rs.is_app_limited << " current_best_estimate="
+      << bottleneck_bandwidth_filter_.best_estimate() << ' '
+      << (rs.delivery_rate >= bottleneck_bandwidth_filter_.best_estimate() ||
+                  !rs.is_app_limited
+              ? "YES"
+              : "NO");
   if (rs.delivery_rate >= bottleneck_bandwidth_filter_.best_estimate() ||
       !rs.is_app_limited) {
     bottleneck_bandwidth_filter_.Update(round_count_, rs.delivery_rate);
   }
 }
 
-void BBR::UpdateRound(const Ack& ack) {
-  if (ack.acked_packets.empty())
-    return;
+bool BBR::UpdateRound(const Ack& ack) {
+  if (ack.acked_packets.empty()) {
+    return false;
+  }
   const auto& last_packet_acked = ack.acked_packets.back();
   if (last_packet_acked.delivered_bytes_at_send >=
       next_round_delivered_bytes_) {
     next_round_delivered_bytes_ = delivered_bytes_;
     round_count_++;
     round_start_ = true;
+    return true;
   } else {
     round_start_ = false;
+    return false;
   }
 }
 
@@ -366,25 +416,24 @@
           std::min(cwnd_bytes_ + SumBytes(ack.acked_packets),
                    target_cwnd_bytes_),
           [this] {
-            OVERNET_TRACE(DEBUG, trace_sink_)
+            OVERNET_TRACE(DEBUG)
                 << "SetCwnd, no packet conservation, filled pipe; target_cwnd="
                 << target_cwnd_bytes_ << " new=" << cwnd_bytes_;
           });
     } else if (cwnd_bytes_ < target_cwnd_bytes_ ||
                SumBytes(ack.acked_packets) < 3 * mss_) {
       SetCwndBytes(cwnd_bytes_ + SumBytes(ack.acked_packets), [this]() {
-        OVERNET_TRACE(DEBUG, trace_sink_)
+        OVERNET_TRACE(DEBUG)
             << "SetCwnd, no packet conservation, unfilled pipe; target_cwnd="
             << target_cwnd_bytes_ << " delivered_bytes=" << delivered_bytes_
             << " mss=" << mss_ << " new=" << cwnd_bytes_;
       });
     }
-    SetCwndBytes(std::max(target_cwnd_bytes_, kMinPipeCwndSegments * mss_),
-                 [this] {
-                   OVERNET_TRACE(DEBUG, trace_sink_)
-                       << "SetCwnd, adjust for kMinPipeCwndSegments"
-                       << " new=" << cwnd_bytes_;
-                 });
+    SetCwndBytes(
+        std::max(target_cwnd_bytes_, kMinPipeCwndSegments * mss_), [this] {
+          OVERNET_TRACE(DEBUG) << "SetCwnd, adjust for kMinPipeCwndSegments"
+                               << " new=" << cwnd_bytes_;
+        });
   }
   if (state_ == State::ProbeRTT) {
     ModulateCwndForProbeRTT();
@@ -397,13 +446,11 @@
     auto nacked_bytes = SumBytes(ack.nacked_packets);
     if (cwnd_bytes_ > nacked_bytes + mss_) {
       SetCwndBytes(cwnd_bytes_ - nacked_bytes, [this] {
-        OVERNET_TRACE(DEBUG, trace_sink_)
-            << "ModulateCwndForRecovery new=" << cwnd_bytes_;
+        OVERNET_TRACE(DEBUG) << "ModulateCwndForRecovery new=" << cwnd_bytes_;
       });
     } else {
       SetCwndBytes(mss_, [this] {
-        OVERNET_TRACE(DEBUG, trace_sink_)
-            << "ModulateCwndForRecovery new=" << cwnd_bytes_;
+        OVERNET_TRACE(DEBUG) << "ModulateCwndForRecovery new=" << cwnd_bytes_;
       });
     }
   } else if (ack.acked_packets.size() > 0 &&
@@ -429,7 +476,7 @@
     SetCwndBytes(
         std::max(cwnd_bytes_, bytes_in_flight_ + SumBytes(ack.acked_packets)),
         [this] {
-          OVERNET_TRACE(DEBUG, trace_sink_)
+          OVERNET_TRACE(DEBUG)
               << "ModulateCwndForRecovery packet_conservation new="
               << cwnd_bytes_;
         });
@@ -437,13 +484,16 @@
 }
 
 void BBR::ModulateCwndForProbeRTT() {
-  SetCwndBytes(std::min(cwnd_bytes_, kMinPipeCwndSegments * mss_), [this] {
-    OVERNET_TRACE(DEBUG, trace_sink_) << "ModulateCwndForProbeRTT";
-  });
+  SetCwndBytes(std::min(cwnd_bytes_, kMinPipeCwndSegments * mss_),
+               [] { OVERNET_TRACE(DEBUG) << "ModulateCwndForProbeRTT"; });
 }
 
 void BBR::SetPacingRateWithGain(Gain gain) {
   auto rate = gain * bottleneck_bandwidth_filter_.best_estimate();
+  OVERNET_TRACE(DEBUG) << "SetPacingRateWithGain: gain=" << gain << " best_est="
+                       << bottleneck_bandwidth_filter_.best_estimate()
+                       << " rate=" << rate << " filled_pipe=" << filled_pipe_
+                       << " pacing_rate=" << pacing_rate_;
   if (rate != Bandwidth::Zero() &&
       (filled_pipe_ || !pacing_rate_ || rate > *pacing_rate_)) {
     pacing_rate_ = rate;
@@ -456,8 +506,7 @@
   SetCwndBytes(
       bytes_in_flight_ + std::max(SumBytes(ack.acked_packets), uint64_t(mss_)),
       [this] {
-        OVERNET_TRACE(DEBUG, trace_sink_)
-            << "SetFastRecovery new=" << cwnd_bytes_;
+        OVERNET_TRACE(DEBUG) << "SetFastRecovery new=" << cwnd_bytes_;
       });
   packet_conservation_ = true;
   recovery_ = Recovery::Fast;
@@ -502,7 +551,7 @@
 
 void BBR::RestoreCwnd() {
   SetCwndBytes(std::max(cwnd_bytes_, prior_cwnd_bytes_), [this] {
-    OVERNET_TRACE(DEBUG, trace_sink_) << "RestoreCwnd new=" << cwnd_bytes_;
+    OVERNET_TRACE(DEBUG) << "RestoreCwnd new=" << cwnd_bytes_;
   });
 }
 
diff --git a/lib/overnet/bbr.h b/lib/overnet/packet_protocol/bbr.h
similarity index 92%
rename from lib/overnet/bbr.h
rename to lib/overnet/packet_protocol/bbr.h
index bda9558..e704d23 100644
--- a/lib/overnet/bbr.h
+++ b/lib/overnet/packet_protocol/bbr.h
@@ -5,17 +5,19 @@
 #pragma once
 
 #include <vector>
-#include "bandwidth.h"
-#include "callback.h"
-#include "optional.h"
-#include "timer.h"
-#include "trace.h"
-#include "windowed_filter.h"
+#include "garnet/lib/overnet/environment/timer.h"
+#include "garnet/lib/overnet/environment/trace.h"
+#include "garnet/lib/overnet/packet_protocol/windowed_filter.h"
+#include "garnet/lib/overnet/vocabulary/bandwidth.h"
+#include "garnet/lib/overnet/vocabulary/callback.h"
+#include "garnet/lib/overnet/vocabulary/optional.h"
 
 namespace overnet {
 
 class BBR {
  public:
+  inline static constexpr auto kModule = Module::BBR;
+
   struct OutgoingPacket {
     uint64_t sequence;
     uint64_t size;
@@ -36,13 +38,13 @@
   };
 
   static constexpr uint32_t kMaxMSS = 1024 * 1024;
+  using RandFunc = std::function<uint32_t()>;
 
-  BBR(Timer* timer, TraceSink trace_sink, uint32_t mss,
-      Optional<TimeDelta> srtt);
+  BBR(Timer* timer, RandFunc rand, uint32_t mss, Optional<TimeDelta> srtt);
 
   void RequestTransmit(StatusCallback ready);
   void CancelRequestTransmit();
-  SentPacket ScheduleTransmit(TimeStamp* send_time, OutgoingPacket packet);
+  SentPacket ScheduleTransmit(OutgoingPacket packet);
   void OnAck(const Ack& ack);
 
   uint64_t mss() const { return mss_; }
@@ -155,7 +157,7 @@
   void HandleRestartFromIdle();
   void HandleProbeRTT(TimeStamp now, const Ack& ack);
 
-  void UpdateRound(const Ack& ack);
+  bool UpdateRound(const Ack& ack);
   void UpdateTargetCwnd() {
     target_cwnd_bytes_ = std::max(uint64_t(3 * mss_), Inflight(cwnd_gain_));
   }
@@ -196,7 +198,6 @@
   static const Gain kProbeBWGainCycle[kProbeBWGainCycleLength];
 
   Timer* const timer_;
-  const TraceSink trace_sink_;
   TimeDelta rtprop_;
   TimeStamp rtprop_stamp_;
   TimeStamp last_send_time_ = TimeStamp::Epoch();
@@ -238,6 +239,7 @@
   }
 
   Optional<StatusCallback> queued_packet_;
+  Optional<Timeout> queued_packet_timeout_;
 
   const uint32_t mss_;
   WindowedFilter<uint64_t, Bandwidth, MaxFilter> bottleneck_bandwidth_filter_{
@@ -274,6 +276,7 @@
   TimeStamp probe_rtt_done_stamp_ = TimeStamp::Epoch();
   TimeStamp first_sent_time_ = TimeStamp::Epoch();
   uint64_t prior_inflight_;
+  RandFunc rand_;
 };
 
 }  // namespace overnet
diff --git a/lib/overnet/bbr_fuzzer.cc b/lib/overnet/packet_protocol/bbr_fuzzer.cc
similarity index 100%
rename from lib/overnet/bbr_fuzzer.cc
rename to lib/overnet/packet_protocol/bbr_fuzzer.cc
diff --git a/lib/overnet/bbr_fuzzer_helpers.h b/lib/overnet/packet_protocol/bbr_fuzzer_helpers.h
similarity index 100%
rename from lib/overnet/bbr_fuzzer_helpers.h
rename to lib/overnet/packet_protocol/bbr_fuzzer_helpers.h
diff --git a/lib/overnet/bbr_test.cc b/lib/overnet/packet_protocol/bbr_test.cc
similarity index 88%
rename from lib/overnet/bbr_test.cc
rename to lib/overnet/packet_protocol/bbr_test.cc
index 8c428db..b404c60 100644
--- a/lib/overnet/bbr_test.cc
+++ b/lib/overnet/packet_protocol/bbr_test.cc
@@ -6,11 +6,12 @@
 #include <fstream>
 #include <functional>
 #include <queue>
-#include "csv_writer.h"
+#include <random>
+#include "garnet/lib/overnet/testing/csv_writer.h"
+#include "garnet/lib/overnet/testing/test_timer.h"
+#include "garnet/lib/overnet/testing/trace_cout.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "test_timer.h"
-#include "trace_cout.h"
 
 using testing::AllOf;
 using testing::Ge;
@@ -91,11 +92,16 @@
   bool pushing_ = false;
 };
 
-class Simulator {
+class Simulator : private TestTimer,
+                  private TraceCout,
+                  private ScopedRenderer,
+                  private ScopedSeverity {
  public:
   Simulator(uint32_t mss, Optional<TimeDelta> srtt)
-      : bbr_(&timer_, kTraceOutput ? TraceCout(&timer_) : TraceSink(), mss,
-             srtt),
+      : TraceCout(this),
+        ScopedRenderer(this),
+        ScopedSeverity(kTraceOutput ? Severity::DEBUG : Severity::INFO),
+        bbr_(this, [this] { return rng_(); }, mss, srtt),
         outgoing_meter_(TimeDelta::FromSeconds(5)) {}
 
   void SetBottleneckBandwidth(Bandwidth bandwidth) {
@@ -114,24 +120,22 @@
   }
   void AddContinuousTraffic(int packet_size, Bandwidth bandwidth,
                             TimeStamp end) {
-    auto next_packet = timer_.Now() + bandwidth.SendTimeForBytes(packet_size);
+    auto next_packet = Now() + bandwidth.SendTimeForBytes(packet_size);
     SendPacket(packet_size, [=]() {
-      auto now = timer_.Now();
+      auto now = Now();
       if (now > end)
         return;
-      timer_.At(next_packet,
-                [=]() { AddContinuousTraffic(packet_size, bandwidth, end); });
+      At(next_packet,
+         [=]() { AddContinuousTraffic(packet_size, bandwidth, end); });
     });
   }
 
-  void Step() { timer_.StepUntilNextEvent(); }
+  void Step() { StepUntilNextEvent(); }
 
-  Timer* timer() { return &timer_; }
+  Timer* timer() { return this; }
   BBR* bbr() { return &bbr_; }
 
-  Bandwidth outgoing_bandwidth() {
-    return outgoing_meter_.Evaluate(timer_.Now());
-  }
+  Bandwidth outgoing_bandwidth() { return outgoing_meter_.Evaluate(Now()); }
 
   size_t outgoing_bandwidth_samples() const {
     return outgoing_meter_.Samples();
@@ -149,24 +153,16 @@
     bbr_.RequestTransmit(
         StatusCallback(ALLOCATED_CALLBACK, [=](const Status& status) {
           if (status.is_ok()) {
-            TimeStamp now = timer_.Now();
-            TimeStamp send_time = now;
             auto sent_packet = bbr_.ScheduleTransmit(
-                &send_time,
                 BBR::OutgoingPacket{next_seq_++, uint64_t(packet_size)});
-            timer_.At(send_time, StatusCallback(ALLOCATED_CALLBACK,
-                                                [=](const Status& status2) {
-                                                  if (status2.is_ok()) {
-                                                    then();
-                                                    SimulatePacket(sent_packet);
-                                                  }
-                                                }));
+            then();
+            SimulatePacket(sent_packet);
           }
         }));
   }
 
   void SimulatePacket(BBR::SentPacket pkt) {
-    auto now = timer_.Now();
+    auto now = Now();
     outgoing_meter_.Push(now, pkt.outgoing.size);
     // Push the packet onto the bottleneck link, and wait for it to pass through
     // or be dropped.
@@ -174,7 +170,7 @@
         pkt.outgoing.size,
         StatusCallback(ALLOCATED_CALLBACK, [this, pkt](const Status& status) {
           bool allow = status.is_ok();
-          TimeStamp now = timer_.Now();
+          TimeStamp now = Now();
           // Count statistics.
           if (allow) {
             packets_passed_++;
@@ -183,12 +179,12 @@
           }
           // After 1/2-rtt the packet will return to sender, notify the sender
           // with an ack or nack.
-          timer_.At(now + half_rtt_, [this, pkt, allow]() {
+          At(now + half_rtt_, [this, pkt, allow]() {
             (allow ? &ack_packets_ : &nack_packets_)->push_back(pkt);
             // Batch up acks and nacks a little bit to simulate real networks.
             if (!ack_packets_.empty() && !ack_scheduled_) {
               ack_scheduled_ = true;
-              timer_.At(timer_.Now() + ack_delay_, [this]() {
+              At(Now() + ack_delay_, [this]() {
                 ack_scheduled_ = false;
                 BBR::Ack ack{std::move(ack_packets_), std::move(nack_packets_)};
                 std::sort(
@@ -213,10 +209,10 @@
   std::vector<BBR::SentPacket> ack_packets_;
   std::vector<BBR::SentPacket> nack_packets_;
 
-  TestTimer timer_;
+  std::mt19937 rng_{12345};
   BBR bbr_;
 
-  BandwidthGate bottleneck_{&timer_};
+  BandwidthGate bottleneck_{this};
   Meter outgoing_meter_;
   uint64_t packets_dropped_ = 0;
   uint64_t packets_passed_ = 0;
diff --git a/lib/overnet/packet_protocol/packet_protocol.cc b/lib/overnet/packet_protocol/packet_protocol.cc
new file mode 100644
index 0000000..d759a3a
--- /dev/null
+++ b/lib/overnet/packet_protocol/packet_protocol.cc
@@ -0,0 +1,838 @@
+// Copyright 2018 The Fuchsia 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 "packet_protocol.h"
+#include <iostream>
+
+namespace overnet {
+
+static const char kClosing[] = "Closing";
+static const char kMaybeScheduleAck[] = "MaybeScheduleAck";
+static const char kRequestSendAck[] = "RequestSendAck";
+static const char kRequestTransmit[] = "RequestTransmit";
+static const char kScheduleRTO[] = "ScheduleRTO";
+static const char kStartNext[] = "StartNext";
+static const char kTransmitPacket[] = "TransmitPacket";
+
+const char PacketProtocol::kProcessedPacket[] = "ProcessedPacket";
+
+template <class T>
+void Drain(T* container) {
+  T clean;
+  container->swap(clean);
+  clean.clear();
+}
+
+void PacketProtocol::Close(Callback<void> quiesced) {
+  ScopedModule<PacketProtocol> scoped_module(this);
+  assert(state_ == State::READY);
+  OVERNET_TRACE(DEBUG) << "Close outstanding_ops=" << outstanding_ops_;
+  OutstandingOp<kClosing> op(this);
+  state_ = State::CLOSING;
+  quiesced_ = std::move(quiesced);
+  // Stop waiting for things.
+  rto_scheduler_.Reset();
+  ack_scheduler_.Reset();
+  outgoing_bbr_.CancelRequestTransmit();
+  NackBefore(TimeStamp::AfterEpoch(TimeDelta::PositiveInf()),
+             Status::Cancelled());
+  send_tip_ = std::numeric_limits<decltype(send_tip_)>::max();
+  Drain(&outstanding_);
+  Drain(&queued_);
+}
+
+void PacketProtocol::RequestSendAck() {
+  // Prevent quiescing during ack generation (otherwise cancelling a scheduled
+  // ack might cause us to quiesce).
+  OutstandingOp<kRequestSendAck> op(this);
+  ScopedModule<PacketProtocol> scoped_module(this);
+  MaybeForceAck();
+}
+
+void PacketProtocol::Send(SendRequestHdl send_request) {
+  ScopedModule<PacketProtocol> scoped_module(this);
+  OVERNET_TRACE(DEBUG) << "Send state=" << static_cast<int>(state_)
+                       << " qsize=" << queued_.size()
+                       << " outstanding=" << outstanding_.size()
+                       << " sending=" << sending_.has_value();
+  if (state_ != State::READY) {
+    // Discard result, forcing callbacks to be made
+    return;
+  }
+  MaybeSendSlice(QueuedPacket{ScopedOp::current(), std::move(send_request)});
+}
+
+void PacketProtocol::MaybeSendSlice(QueuedPacket&& packet) {
+  OVERNET_TRACE(DEBUG) << "MaybeSendSlice: queued=" << queued_.size()
+                       << " sending=" << sending_.has_value()
+                       << " transmitting=" << transmitting_;
+  if (!queued_.empty() || sending_ || transmitting_) {
+    queued_.emplace_back(std::forward<QueuedPacket>(packet));
+    return;
+  }
+  SendSlice(std::forward<QueuedPacket>(packet));
+}
+
+void PacketProtocol::SendSlice(QueuedPacket&& packet) {
+  assert(!transmitting_);
+  assert(!sending_);
+  sending_.Reset(std::forward<QueuedPacket>(packet));
+  assert(!sending_->request.empty());
+  OVERNET_TRACE(DEBUG) << "SendSlice send_tip=" << send_tip_
+                       << " outstanding=" << outstanding_.size();
+  outgoing_bbr_.RequestTransmit([self = OutstandingOp<kRequestTransmit>(this)](
+                                    const Status& status) mutable {
+    ScopedModule<PacketProtocol> scoped_module(self.get());
+    if (status.is_error()) {
+      self->sending_->request.Ack(status);
+      self->sending_.Reset();
+      return;
+    }
+    self->TransmitPacket();
+  });
+}
+
+void PacketProtocol::TransmitPacket() {
+  ScopedOp op(sending_->op);
+  assert(!transmitting_);
+  const uint64_t seq_idx = send_tip_ + outstanding_.size();
+  if (seq_idx - send_tip_ > max_outstanding_size_) {
+    max_outstanding_size_ = seq_idx - send_tip_;
+  }
+  SeqNum seq_num(seq_idx, max_outstanding_size_);
+  OVERNET_TRACE(DEBUG) << "TransmitPacket seq=" << seq_idx << " -> " << seq_num
+                       << " (send_tip=" << send_tip_ << ")"
+                       << " outstanding=" << outstanding_.size()
+                       << " rto_scheduler?=" << (rto_scheduler_ ? "YES" : "NO");
+  if (outstanding_.empty() || !rto_scheduler_) {
+    KeepAlive();
+  }
+  outstanding_.emplace_back(OutstandingPacket{
+      timer_->Now(), OutstandingPacketState::PENDING, false, false, max_seen_,
+      Nothing, std::move(sending_.Take().request)});
+  assert(!sending_.has_value());
+  transmitting_ = true;
+  packet_sender_->SendPacket(
+      seq_num,
+      [request = outstanding_.back().request.borrow(), op = ScopedOp::current(),
+       self = OutstandingOp<kTransmitPacket>(this),
+       seq_idx](LazySliceArgs args) {
+        ScopedModule<PacketProtocol> scoped_module(self.get());
+        ScopedOp scoped_op(op);
+        auto outstanding_packet = [&]() -> OutstandingPacket* {
+          if (seq_idx < self->send_tip_) {
+            // Frame was nacked before sending (probably due to shutdown).
+            OVERNET_TRACE(DEBUG)
+                << "Seq " << seq_idx << " nacked before sending";
+            return nullptr;
+          }
+          const auto outstanding_idx = seq_idx - self->send_tip_;
+          OVERNET_TRACE(DEBUG)
+              << "GeneratePacket seq=" << seq_idx
+              << " send_tip=" << self->send_tip_
+              << " outstanding_idx=" << outstanding_idx
+              << " outstanding_size=" << self->outstanding_.size();
+          assert(outstanding_idx < self->outstanding_.size());
+          return &self->outstanding_[outstanding_idx];
+        };
+        if (auto* pkt = outstanding_packet()) {
+          if (pkt->request.empty()) {
+            return Slice();
+          }
+        } else {
+          return Slice();
+        }
+        auto gen_pkt = self->GeneratePacket(request, args);
+        auto encoded_payload =
+            self->codec_->Encode(seq_idx, std::move(gen_pkt.payload));
+        if (encoded_payload.is_error()) {
+          OVERNET_TRACE(ERROR)
+              << "Failed to encode packet: " << encoded_payload.AsStatus();
+        }
+        if (auto* pkt = outstanding_packet()) {
+          assert(!pkt->bbr_sent_packet.has_value());
+          assert(pkt->state == OutstandingPacketState::PENDING);
+          pkt->state = OutstandingPacketState::SENT;
+          pkt->has_ack = gen_pkt.has_ack;
+          pkt->is_pure_ack = gen_pkt.is_pure_ack;
+          pkt->bbr_sent_packet = self->outgoing_bbr_.ScheduleTransmit(
+              BBR::OutgoingPacket{seq_idx, encoded_payload->length()});
+        } else {
+          return Slice();
+        }
+        if (gen_pkt.has_ack) {
+          self->last_sent_ack_ = seq_idx;
+        }
+        return std::move(*encoded_payload);
+      },
+      [self = OutstandingOp<kStartNext>(this)]() {
+        self->transmitting_ = false;
+        self->ContinueSending();
+      });
+}
+
+PacketProtocol::GeneratedPacket PacketProtocol::GeneratePacket(
+    SendRequest* request, LazySliceArgs args) {
+  auto ack =
+      GenerateAck(std::min(static_cast<uint64_t>(args.max_length), mss_));
+  auto make_args = [=](uint64_t prefix_length, bool has_other_content) {
+    auto max_length = std::min(static_cast<uint64_t>(args.max_length), mss_);
+    auto subtract_from_max =
+        std::min(max_length,
+                 prefix_length + codec_->border.prefix + codec_->border.suffix);
+    max_length -= subtract_from_max;
+    return LazySliceArgs{args.desired_border.WithAddedPrefix(
+                             prefix_length + codec_->border.prefix),
+                         max_length,
+                         has_other_content || args.has_other_content};
+  };
+  if (ack) {
+    sent_ack_ = true;
+    AckFrame::Writer ack_writer(ack.get());
+    const uint8_t ack_length_length =
+        varint::WireSizeFor(ack_writer.wire_length());
+    const uint64_t prefix_length = ack_length_length + ack_writer.wire_length();
+    auto payload_slice = request->GenerateBytes(make_args(prefix_length, true));
+    return GeneratedPacket{
+        payload_slice.WithPrefix(prefix_length,
+                                 [&ack_writer, ack_length_length](uint8_t* p) {
+                                   ack_writer.Write(
+                                       varint::Write(ack_writer.wire_length(),
+                                                     ack_length_length, p));
+                                 }),
+        true, payload_slice.length() == 0};
+  } else {
+    auto payload_slice = request->GenerateBytes(make_args(1, false));
+    return GeneratedPacket{
+        payload_slice.WithPrefix(1, [](uint8_t* p) { *p = 0; }), false, false};
+  }
+}
+
+StatusOr<PacketProtocol::AckActions> PacketProtocol::HandleAck(
+    const AckFrame& ack, bool is_synthetic) {
+  OVERNET_TRACE(DEBUG) << "HandleAck: " << ack << " synthetic=" << is_synthetic;
+
+  // TODO(ctiller): inline vectors to avoid allocations.
+  AckActions actions;
+
+  // Validate ack, and ignore if it's old.
+  if (ack.ack_to_seq() < send_tip_) {
+    return actions;
+  }
+  if (ack.ack_to_seq() >= send_tip_ + outstanding_.size()) {
+    return Status(StatusCode::INVALID_ARGUMENT,
+                  "Ack packet past sending sequence");
+  }
+  BBR::Ack bbr_ack;
+  // Move receive window forward.
+  auto new_recv_tip = outstanding_[ack.ack_to_seq() - send_tip_].ack_to_seq;
+  if (new_recv_tip != recv_tip_) {
+    assert(new_recv_tip > recv_tip_);
+    while (!received_packets_.empty()) {
+      auto it = received_packets_.begin();
+      if (it->first >= new_recv_tip) {
+        break;
+      }
+      received_packets_.erase(it);
+    }
+    recv_tip_ = new_recv_tip;
+  }
+  // Fail any nacked packets.
+  // Nacks are received in descending order of sequence number. We iterate the
+  // callbacks here in reverse order then so that the OLDEST nacked message is
+  // the most likely to be sent first. This has the important consequence that
+  // if the packet was a fragment of a large message that was rejected due to
+  // buffering, the earlier pieces (that are more likely to fit) are
+  // retransmitted first.
+  bool nacked_last_ack = false;
+  for (auto it = ack.nack_seqs().rbegin(); it != ack.nack_seqs().rend(); ++it) {
+    auto nack_seq = *it;
+    if (nack_seq < send_tip_) {
+      continue;
+    }
+    if (nack_seq >= send_tip_ + outstanding_.size()) {
+      return Status(StatusCode::INVALID_ARGUMENT, "Nack past sending sequence");
+    }
+    OutstandingPacket& pkt = outstanding_[nack_seq - send_tip_];
+    auto request = std::move(pkt.request);
+    if (!request.empty()) {
+      if (!pkt.bbr_sent_packet.has_value()) {
+        if (is_synthetic) {
+          OVERNET_TRACE(DEBUG)
+              << "Nack unsent packet: fake out bbr scheduling for seq "
+              << nack_seq;
+          pkt.bbr_sent_packet =
+              outgoing_bbr_.ScheduleTransmit(BBR::OutgoingPacket{nack_seq, 0});
+        } else {
+          return Status(StatusCode::INVALID_ARGUMENT, "Nack unsent sequence");
+        }
+      }
+      assert(pkt.bbr_sent_packet->outgoing.sequence == nack_seq);
+      OVERNET_TRACE(DEBUG) << "NACK: " << nack_seq
+                           << " size=" << pkt.bbr_sent_packet->outgoing.size
+                           << " send_time=" << pkt.bbr_sent_packet->send_time;
+      actions.nacks.emplace_back(std::move(request));
+      bbr_ack.nacked_packets.push_back(*pkt.bbr_sent_packet);
+      if (pkt.has_ack && nack_seq == last_sent_ack_) {
+        nacked_last_ack = true;
+      }
+      switch (pkt.state) {
+        case OutstandingPacketState::PENDING:
+        case OutstandingPacketState::SENT:
+          pkt.state = OutstandingPacketState::NACKED;
+          break;
+        case OutstandingPacketState::NACKED:
+          break;
+        case OutstandingPacketState::ACKED:
+          // Previously acked packet becomes nacked: this is an error.
+          return Status(StatusCode::INVALID_ARGUMENT,
+                        "Previously acked packet becomes nacked");
+      }
+    }
+  }
+  // Clear out outstanding packet references, propagating acks.
+  while (send_tip_ <= ack.ack_to_seq()) {
+    OutstandingPacket& pkt = outstanding_.front();
+    auto request = std::move(pkt.request);
+    if (!pkt.bbr_sent_packet.has_value()) {
+      return Status(StatusCode::INVALID_ARGUMENT, "Ack unsent sequence");
+    }
+    assert(pkt.bbr_sent_packet->outgoing.sequence == send_tip_);
+    send_tip_++;
+    if (!request.empty()) {
+      OVERNET_TRACE(DEBUG) << "ACK: " << pkt.bbr_sent_packet->outgoing.sequence
+                           << " size=" << pkt.bbr_sent_packet->outgoing.size
+                           << " send_time=" << pkt.bbr_sent_packet->send_time;
+      switch (pkt.state) {
+        case OutstandingPacketState::PENDING:
+        case OutstandingPacketState::NACKED:
+        case OutstandingPacketState::ACKED:
+          abort();
+        case OutstandingPacketState::SENT:
+          pkt.state = OutstandingPacketState::ACKED;
+          break;
+      }
+      bbr_ack.acked_packets.push_back(*pkt.bbr_sent_packet);
+      outstanding_.pop_front();
+      actions.acks.emplace_back(std::move(request));
+    } else {
+      outstanding_.pop_front();
+    }
+  }
+  const auto delay = TimeDelta::FromMicroseconds(ack.ack_delay_us());
+  // Offset send_time to account for queuing delay on peer.
+  for (auto& pkt : bbr_ack.acked_packets) {
+    pkt.send_time = pkt.send_time + delay;
+  }
+  for (auto& pkt : bbr_ack.nacked_packets) {
+    pkt.send_time = pkt.send_time + delay;
+  }
+  if (nacked_last_ack) {
+    MaybeSendAck();
+  }
+
+  actions.bbr_ack = std::move(bbr_ack);
+
+  return actions;
+}
+
+void PacketProtocol::ContinueSending() {
+  ScopedModule<PacketProtocol> in_pp(this);
+  OVERNET_TRACE(DEBUG) << "ContinueSending: queued=" << queued_.size()
+                       << " sending=" << sending_.has_value()
+                       << " transmitting=" << transmitting_
+                       << " state=" << static_cast<int>(state_);
+  if (!queued_.empty() && !sending_ && !transmitting_ &&
+      state_ == State::READY) {
+    QueuedPacket p = std::move(queued_.front());
+    queued_.pop_front();
+    SendSlice(std::move(p));
+  } else if (ack_after_sending_) {
+    ack_after_sending_ = false;
+    MaybeSendAck();
+  }
+}
+
+PacketProtocol::ProcessedPacket PacketProtocol::Process(TimeStamp received,
+                                                        SeqNum seq_num,
+                                                        Slice slice) {
+  ScopedModule<PacketProtocol> scoped_module(this);
+  using StatusType = StatusOr<Optional<Slice>>;
+  OutstandingOp<kProcessedPacket> op(this);
+
+  OVERNET_TRACE(DEBUG) << "Process: " << slice;
+
+  // Validate sequence number, ignore if it's old.
+  const auto seq_idx = seq_num.Reconstruct(recv_tip_);
+  OVERNET_TRACE(DEBUG) << "Receive sequence " << seq_num << "=" << seq_idx
+                       << " recv_tip " << recv_tip_
+                       << " max_seen=" << max_seen_;
+  if (state_ == State::CLOSED) {
+    return ProcessedPacket(op, seq_idx, ProcessedPacket::SendAck::NONE,
+                           ReceiveState::UNKNOWN, Nothing, Nothing);
+  }
+  if (seq_idx < recv_tip_) {
+    return ProcessedPacket(op, seq_idx, ProcessedPacket::SendAck::NONE,
+                           ReceiveState::UNKNOWN, Nothing, Nothing);
+  }
+
+  // Keep track of the biggest valid sequence we've seen.
+  if (seq_idx > max_seen_) {
+    OVERNET_TRACE(DEBUG) << "new max_seen";
+    max_seen_ = seq_idx;
+  }
+
+  KeepAlive();
+
+  auto slice_status = codec_->Decode(seq_idx, std::move(slice));
+  if (slice_status.is_error()) {
+    OVERNET_TRACE(ERROR) << "Failed to decode packet: "
+                         << slice_status.AsStatus();
+    return ProcessedPacket(op, seq_idx, ProcessedPacket::SendAck::NONE,
+                           ReceiveState::UNKNOWN, Nothing, Nothing);
+  }
+  slice = std::move(*slice_status);
+
+  const uint8_t* p = slice.begin();
+  const uint8_t* end = slice.end();
+
+  if (p == end) {
+    return ProcessedPacket(op, seq_idx, ProcessedPacket::SendAck::NONE,
+                           ReceiveState::UNKNOWN, Nothing, Nothing);
+  }
+
+  uint64_t ack_length;
+  if (!varint::Read(&p, end, &ack_length)) {
+    return ProcessedPacket(
+        op, seq_idx, ProcessedPacket::SendAck::NONE, ReceiveState::UNKNOWN,
+        StatusType(StatusCode::INVALID_ARGUMENT,
+                   "Failed to parse ack length from message"),
+        Nothing);
+  }
+  slice.TrimBegin(p - slice.begin());
+
+  OVERNET_TRACE(DEBUG) << "ack_length=" << ack_length;
+
+  if (ack_length > slice.length()) {
+    return ProcessedPacket(
+        op, seq_idx, ProcessedPacket::SendAck::NONE, ReceiveState::UNKNOWN,
+        StatusType(StatusCode::INVALID_ARGUMENT,
+                   "Ack frame claimed to be past end of message"),
+        Nothing);
+  }
+
+  ProcessedPacket::SendAck ack = ProcessedPacket::SendAck::NONE;
+
+  auto it = received_packets_.lower_bound(seq_idx);
+  if (it == received_packets_.end() || it->first != seq_idx) {
+    it = received_packets_.insert(
+        it, std::make_pair(seq_idx,
+                           ReceivedPacket{ReceiveState::UNKNOWN, received}));
+  } else {
+    OVERNET_TRACE(DEBUG) << "frozen as " << static_cast<int>(it->second.state);
+    return ProcessedPacket(op, seq_idx, ProcessedPacket::SendAck::NONE,
+                           ReceiveState::UNKNOWN, Nothing, Nothing);
+  }
+
+  const bool is_pure_ack = ack_length > 0 && ack_length == slice.length();
+  bool suppress_ack = is_pure_ack;
+  bool prev_was_also_suppressed = false;
+  bool prev_was_discontiguous = false;
+  if (suppress_ack && it != received_packets_.begin()) {
+    auto prev = std::prev(it);
+    if (prev->first != it->first - 1) {
+      suppress_ack = false;
+      prev_was_discontiguous = true;
+    } else {
+      switch (prev->second.state) {
+        case ReceiveState::UNKNOWN:
+          assert(false);
+          break;
+        case ReceiveState::NOT_RECEIVED:
+        case ReceiveState::RECEIVED:
+          break;
+        case ReceiveState::RECEIVED_AND_SUPPRESSED_ACK:
+          suppress_ack = false;
+          prev_was_also_suppressed = true;
+          break;
+      }
+    }
+  }
+  if (suppress_ack && it->first != received_packets_.rbegin()->first) {
+    suppress_ack = false;
+  }
+  const auto final_receive_state =
+      suppress_ack ? ReceiveState::RECEIVED_AND_SUPPRESSED_ACK
+                   : ReceiveState::RECEIVED;
+
+  OVERNET_TRACE(DEBUG) << "pure_ack=" << is_pure_ack
+                       << " suppress_ack=" << suppress_ack << " is_last="
+                       << (it->first == received_packets_.rbegin()->first)
+                       << " prev_was_also_suppressed="
+                       << prev_was_also_suppressed
+                       << " prev_was_discontiguous=" << prev_was_discontiguous;
+
+  if (suppress_ack) {
+    OVERNET_TRACE(DEBUG) << "ack suppressed";
+  } else {
+    if (seq_idx >= kMaxUnackedReceives &&
+        max_acked_ <= seq_idx - kMaxUnackedReceives) {
+      OVERNET_TRACE(DEBUG) << "Select force ack";
+      ack = ProcessedPacket::SendAck::FORCE;
+    } else if (!sent_ack_) {
+      OVERNET_TRACE(DEBUG) << "Select force first ack";
+      ack = ProcessedPacket::SendAck::FORCE;
+      sent_ack_ = true;
+    } else {
+      OVERNET_TRACE(DEBUG) << "Select schedule ack";
+      ack = ProcessedPacket::SendAck::SCHEDULE;
+    }
+  }
+
+  if (ack_length == 0) {
+    return ProcessedPacket(op, seq_idx, ack, final_receive_state, slice,
+                           Nothing);
+  }
+
+  auto ack_action_status = AckFrame::Parse(slice.TakeUntilOffset(ack_length))
+                               .Then([this](const AckFrame& ack_frame) {
+                                 return HandleAck(ack_frame, false);
+                               });
+  if (ack_action_status.is_error()) {
+    return ProcessedPacket(op, seq_idx, ack, final_receive_state,
+                           ack_action_status.AsStatus(), Nothing);
+  }
+
+  return ProcessedPacket(op, seq_idx, ack, final_receive_state, slice,
+                         std::move(*ack_action_status));
+}
+
+void PacketProtocol::ProcessedPacket::Nack() {
+  ScopedModule<PacketProtocol> in_pp(protocol_.get());
+  if (final_receive_state_ != ReceiveState::UNKNOWN) {
+    OVERNET_TRACE(DEBUG) << "Forcefully nack received packet: seq=" << seq_idx_;
+    final_receive_state_ = ReceiveState::NOT_RECEIVED;
+  }
+}
+
+PacketProtocol::ProcessedPacket::~ProcessedPacket() {
+  ScopedModule<PacketProtocol> in_pp(protocol_.get());
+  if (final_receive_state_ != ReceiveState::UNKNOWN) {
+    auto it = protocol_->received_packets_.find(seq_idx_);
+    assert(it != protocol_->received_packets_.end());
+    assert(it->second.state == ReceiveState::UNKNOWN);
+    it->second.state = final_receive_state_;
+    if (final_receive_state_ == ReceiveState::NOT_RECEIVED) {
+      // Clear ack pacing state to force transmission of nack ASAP.
+      protocol_->last_ack_send_ = TimeStamp::Epoch();
+      send_ack_ = SendAck::FORCE;
+    }
+  }
+  switch (send_ack_) {
+    case SendAck::NONE:
+      break;
+    case SendAck::FORCE:
+      protocol_->MaybeForceAck();
+      break;
+    case SendAck::SCHEDULE:
+      protocol_->MaybeScheduleAck();
+      break;
+  }
+  if (ack_actions_.has_value()) {
+    protocol_->RunAckActions(ack_actions_.get(), Status::Unavailable());
+  }
+  OVERNET_TRACE(DEBUG) << "Finish processing packet";
+}
+
+void PacketProtocol::RunAckActions(AckActions* ack_actions,
+                                   const Status& nack_status) {
+  if (ack_actions->bbr_ack.has_value()) {
+    outgoing_bbr_.OnAck(*ack_actions->bbr_ack);
+  }
+
+  for (auto& rq : ack_actions->nacks) {
+    OVERNET_TRACE(DEBUG) << "Nack message";
+    rq.Ack(nack_status);
+  }
+  for (auto& rq : ack_actions->acks) {
+    OVERNET_TRACE(DEBUG) << "Ack message";
+    rq.Ack(Status::Ok());
+  }
+
+  OVERNET_TRACE(DEBUG) << "ContinueSending?";
+
+  // Continue sending if we can
+  ContinueSending();
+}
+
+bool PacketProtocol::AckIsNeeded() const { return max_seen_ > recv_tip_; }
+
+std::string PacketProtocol::AckDebugText() {
+  std::ostringstream out;
+  bool first = true;
+  for (const auto& r : received_packets_) {
+    if (!first) {
+      out << ", ";
+    }
+    first = false;
+    out << r.first << ":";
+    switch (r.second.state) {
+      case ReceiveState::UNKNOWN:
+        out << "UNKNOWN";
+        break;
+      case ReceiveState::NOT_RECEIVED:
+        out << "NOT_RECEIVED";
+        break;
+      case ReceiveState::RECEIVED:
+        out << "RECEIVED";
+        break;
+      case ReceiveState::RECEIVED_AND_SUPPRESSED_ACK:
+        out << "RECEIVED_AND_SUPPRESSED_ACK";
+        break;
+    }
+    out << r.second.when;
+  }
+  return out.str();
+}
+
+Optional<AckFrame> PacketProtocol::GenerateAck(uint32_t max_length) {
+  TimeStamp now = timer_->Now();
+  OVERNET_TRACE(DEBUG) << "GenerateAck: " << AckDebugText();
+  auto frame = GenerateAckTo(now, max_seen_);
+  if (frame.has_value()) {
+    const bool adjusted_ack =
+        frame->AdjustForMSS(max_length, [this, now](uint64_t seq_idx) {
+          return DelayForReceivedPacket(now, seq_idx).as_us();
+        });
+    if (adjusted_ack) {
+      MaybeScheduleAck();
+    }
+  }
+  OVERNET_TRACE(DEBUG) << "GenerateAck generates:" << frame;
+  return frame;
+}
+
+TimeDelta PacketProtocol::DelayForReceivedPacket(TimeStamp now,
+                                                 uint64_t seq_idx) {
+  auto it = received_packets_.lower_bound(seq_idx);
+  if (it == received_packets_.end()) {
+    return TimeDelta::PositiveInf();
+  }
+  return now - it->second.when;
+}
+
+Optional<AckFrame> PacketProtocol::GenerateAckTo(TimeStamp now,
+                                                 uint64_t max_seen) {
+  OVERNET_TRACE(DEBUG) << "GenerateAckTo: max_seen=" << max_seen
+                       << " recv_tip=" << recv_tip_
+                       << " n=" << (max_seen_ - recv_tip_);
+  if (max_seen <= recv_tip_) {
+    return Nothing;
+  }
+  if (last_ack_send_ + QuarterRTT() > now) {
+    MaybeScheduleAck();
+    return Nothing;
+  }
+  AckFrame ack(max_seen, DelayForReceivedPacket(now, max_seen).as_us());
+  if (max_seen >= 1) {
+    for (uint64_t seq = max_seen; seq > recv_tip_; seq--) {
+      auto it = received_packets_.lower_bound(seq);
+      if (it == received_packets_.end()) {
+        OVERNET_TRACE(DEBUG)
+            << "Mark unseen packet " << seq << " as NOT_RECEIVED";
+        received_packets_.insert(
+            it, std::make_pair(
+                    seq, ReceivedPacket{ReceiveState::NOT_RECEIVED, now}));
+        ack.AddNack(seq);
+      } else if (it->first != seq) {
+        OVERNET_TRACE(DEBUG)
+            << "Mark unseen packet " << seq
+            << " as NOT_RECEIVED (looking at seq " << it->first << ")";
+        received_packets_.insert(
+            it, std::make_pair(
+                    seq, ReceivedPacket{ReceiveState::NOT_RECEIVED, now}));
+        ack.AddNack(seq);
+      } else {
+        switch (it->second.state) {
+          case ReceiveState::UNKNOWN: {
+            OVERNET_TRACE(DEBUG)
+                << "Excluding processing packet from generated ack";
+            auto out = GenerateAckTo(now, seq - 1);
+            MaybeScheduleAck();
+            return out;
+          }
+          case ReceiveState::NOT_RECEIVED:
+            ack.AddNack(seq);
+            break;
+          case ReceiveState::RECEIVED:
+          case ReceiveState::RECEIVED_AND_SUPPRESSED_ACK:
+            break;
+        }
+      }
+    }
+  }
+  last_ack_send_ = now;
+  return std::move(ack);
+}
+
+void PacketProtocol::MaybeForceAck() {
+  if (ack_scheduler_.has_value()) {
+    ack_scheduler_->Cancel();
+    ack_scheduler_.Reset();
+  }
+  MaybeSendAck();
+}
+
+TimeDelta PacketProtocol::QuarterRTT() const {
+  auto est = outgoing_bbr_.rtt();
+  if (est == TimeDelta::PositiveInf()) {
+    est = TimeDelta::FromMilliseconds(100);
+  }
+  return est / 4;
+}
+
+void PacketProtocol::MaybeScheduleAck() {
+  if (!ack_scheduler_.has_value()) {
+    OVERNET_TRACE(DEBUG) << "Schedule ack";
+    ack_scheduler_.Reset(
+        timer_, timer_->Now() + QuarterRTT(),
+        [self = OutstandingOp<kMaybeScheduleAck>(this)](const Status& status) {
+          if (status.is_error())
+            return;
+          self->ack_scheduler_.Reset();
+          self->MaybeSendAck();
+        });
+  } else {
+    OVERNET_TRACE(DEBUG) << "Ack already scheduled";
+  }
+}
+
+void PacketProtocol::MaybeSendAck() {
+  OVERNET_TRACE(DEBUG) << "MaybeSendAck: max_seen=" << max_seen_
+                       << " recv_tip=" << recv_tip_
+                       << " n=" << (max_seen_ - recv_tip_)
+                       << " last_ack_send=" << last_ack_send_
+                       << " 1/4-rtt=" << QuarterRTT()
+                       << " sending=" << (sending_ ? "true" : "false");
+  if (AckIsNeeded()) {
+    if (sending_) {
+      ack_after_sending_ = true;
+    } else if (last_ack_send_ + QuarterRTT() > timer_->Now()) {
+      MaybeScheduleAck();
+    } else if (!ack_only_message_outstanding_) {
+      ack_only_message_outstanding_ = true;
+      Send(SendRequestHdl(&ack_only_send_request_));
+    }
+  }
+}
+
+void PacketProtocol::KeepAlive() {
+  last_keepalive_event_ = timer_->Now();
+  OVERNET_TRACE(DEBUG) << "KeepAlive " << last_keepalive_event_
+                       << " rto=" << RetransmissionDeadline();
+  if (!rto_scheduler_ && state_ == State::READY) {
+    ScheduleRTO();
+  }
+}
+
+void PacketProtocol::ScheduleRTO() {
+  assert(state_ == State::READY);
+  rto_scheduler_.Reset(
+      timer_, RetransmissionDeadline(),
+      StatusCallback(
+          ALLOCATED_CALLBACK,  // TODO(ctiller): remove allocated
+          [self = OutstandingOp<kScheduleRTO>(this)](const Status& status) {
+            ScopedModule<PacketProtocol> in_pp(self.get());
+            auto now = self->timer_->Now();
+            OVERNET_TRACE(DEBUG)
+                << "RTO check: now=" << now << " status=" << status
+                << " rto=" << self->RetransmissionDeadline();
+            if (status.is_error()) {
+              if (now.after_epoch() == TimeDelta::PositiveInf()) {
+                // Shutting down - help by nacking everything.
+                self->NackBefore(now, Status::Cancelled());
+              }
+              return;
+            }
+            if (now >= self->RetransmissionDeadline()) {
+              self->rto_scheduler_.Reset();
+              self->NackBefore(self->last_keepalive_event_,
+                               Status::Unavailable());
+              if (self->LastRTOableSequence(
+                      TimeStamp::AfterEpoch(TimeDelta::PositiveInf()))) {
+                self->KeepAlive();
+              }
+            } else {
+              self->ScheduleRTO();
+            }
+          }));
+}
+
+Optional<uint64_t> PacketProtocol::LastRTOableSequence(TimeStamp epoch) {
+  OVERNET_TRACE(DEBUG) << "Check last RTO before " << epoch;
+  auto last_sent = send_tip_ + outstanding_.size() - 1;
+  // Some packets may be outstanding but not sent: there's no reason to nack
+  // them!
+  while (last_sent >= send_tip_) {
+    const auto& outstanding_packet = outstanding_[last_sent - send_tip_];
+    OVERNET_TRACE(DEBUG) << "Check seq:" << last_sent
+                         << " (idx:" << (last_sent - send_tip_) << ") bbr_send="
+                         << outstanding_packet.bbr_sent_packet.Map(
+                                [](const auto& bbr_pkt) {
+                                  return bbr_pkt.send_time;
+                                })
+                         << " pure_ack=" << outstanding_packet.is_pure_ack
+                         << " has_req=" << !outstanding_packet.request.empty();
+    if (outstanding_packet.scheduled <= epoch &&
+        !outstanding_packet.is_pure_ack) {
+      break;
+    }
+    last_sent--;
+  }
+  if (last_sent < send_tip_) {
+    return Nothing;
+  }
+  return last_sent;
+}
+
+void PacketProtocol::NackBefore(TimeStamp epoch, const Status& nack_status) {
+  auto last_sent = LastRTOableSequence(epoch);
+  if (!last_sent.has_value()) {
+    return;
+  }
+  AckFrame f(*last_sent, 0);
+  for (uint64_t i = *last_sent; i >= send_tip_; i--) {
+    f.AddNack(i);
+  }
+  auto r = HandleAck(f, true);
+  assert(r.is_ok());
+  RunAckActions(r.get(), nack_status);
+}
+
+TimeStamp PacketProtocol::RetransmissionDeadline() const {
+  auto rtt =
+      std::max(TimeDelta::FromMilliseconds(1),
+               std::min(outgoing_bbr_.rtt(), TimeDelta::FromMilliseconds(250)));
+  OVERNET_TRACE(DEBUG) << "RTTDL: last_keepalive=" << last_keepalive_event_
+                       << " rtt=" << rtt << " => "
+                       << (last_keepalive_event_ + 4 * rtt);
+  return last_keepalive_event_ + 4 * rtt;
+}
+
+PacketProtocol::Codec* PacketProtocol::NullCodec() {
+  class NullCodec final : public Codec {
+   public:
+    NullCodec() : Codec(Border::None()) {}
+    StatusOr<Slice> Encode(uint64_t seq_idx, Slice src) const override {
+      return src;
+    }
+    StatusOr<Slice> Decode(uint64_t seq_idx, Slice src) const override {
+      return src;
+    }
+  };
+  static NullCodec null_codec;
+  return &null_codec;
+}
+
+}  // namespace overnet
diff --git a/lib/overnet/packet_protocol/packet_protocol.h b/lib/overnet/packet_protocol/packet_protocol.h
new file mode 100644
index 0000000..8354c9d
--- /dev/null
+++ b/lib/overnet/packet_protocol/packet_protocol.h
@@ -0,0 +1,382 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <deque>
+#include <map>
+#include "garnet/lib/overnet/environment/timer.h"
+#include "garnet/lib/overnet/environment/trace.h"
+#include "garnet/lib/overnet/labels/seq_num.h"
+#include "garnet/lib/overnet/packet_protocol/bbr.h"
+#include "garnet/lib/overnet/protocol/ack_frame.h"
+#include "garnet/lib/overnet/protocol/varint.h"
+#include "garnet/lib/overnet/vocabulary/callback.h"
+#include "garnet/lib/overnet/vocabulary/lazy_slice.h"
+#include "garnet/lib/overnet/vocabulary/once_fn.h"
+#include "garnet/lib/overnet/vocabulary/optional.h"
+#include "garnet/lib/overnet/vocabulary/slice.h"
+#include "garnet/lib/overnet/vocabulary/status.h"
+
+// Enable for indepth refcount debugging for packet protocol ops.
+//#define OVERNET_TRACE_PACKET_PROTOCOL_OPS
+
+namespace overnet {
+
+class PacketProtocol {
+ public:
+  static constexpr inline auto kModule = Module::PACKET_PROTOCOL;
+
+  class PacketSender {
+   public:
+    virtual void SendPacket(SeqNum seq, LazySlice data,
+                            Callback<void> done) = 0;
+  };
+
+  class Codec {
+   public:
+    // How much will this codec expand a message? (maximums only).
+    const Border border;
+    virtual StatusOr<Slice> Encode(uint64_t seq_idx, Slice src) const = 0;
+    virtual StatusOr<Slice> Decode(uint64_t seq_idx, Slice src) const = 0;
+
+   protected:
+    explicit Codec(Border border) : border(border) {}
+  };
+
+  class SendRequestHdl;
+
+  class SendRequest {
+   public:
+    // Called at most once, and always before Ack.
+    virtual Slice GenerateBytes(LazySliceArgs args) = 0;
+    // Called exactly once.
+    virtual void Ack(const Status& status) = 0;
+
+    template <class GB, class A>
+    static SendRequestHdl FromFunctors(GB gb, A a);
+  };
+
+  class SendRequestHdl {
+   public:
+    explicit SendRequestHdl(SendRequest* req) : req_(req) {}
+    SendRequestHdl() : SendRequestHdl(nullptr) {}
+    SendRequestHdl(const SendRequestHdl&) = delete;
+    SendRequestHdl& operator=(const SendRequestHdl&) = delete;
+    SendRequestHdl(SendRequestHdl&& other) : req_(other.req_) {
+      other.req_ = nullptr;
+    }
+    SendRequestHdl& operator=(SendRequestHdl&& other) {
+      this->~SendRequestHdl();
+      req_ = other.req_;
+      other.req_ = nullptr;
+      return *this;
+    }
+    ~SendRequestHdl() {
+      if (req_) {
+        req_->Ack(Status::Cancelled());
+      }
+    }
+
+    bool empty() const { return req_ == nullptr; }
+
+    Slice GenerateBytes(LazySliceArgs args) {
+      return req_->GenerateBytes(args);
+    }
+
+    void Ack(const Status& status) {
+      SendRequest* req = req_;
+      req_ = nullptr;
+      req->Ack(status);
+    }
+
+    SendRequest* borrow() { return req_; }
+
+   private:
+    SendRequest* req_;
+  };
+
+  static Codec* NullCodec();
+
+  static constexpr size_t kMaxUnackedReceives = 3;
+  using RandFunc = BBR::RandFunc;
+
+  PacketProtocol(Timer* timer, RandFunc rand, PacketSender* packet_sender,
+                 const Codec* codec, uint64_t mss)
+      : timer_(timer),
+        packet_sender_(packet_sender),
+        codec_(codec),
+        mss_(mss),
+        outgoing_bbr_(timer_, rand, mss_, Nothing),
+        ack_only_send_request_(this) {}
+
+  void Close(Callback<void> quiesced);
+
+  ~PacketProtocol() { assert(state_ == State::CLOSED); }
+
+  uint32_t mss() const {
+    auto codec_expansion = codec_->border.prefix + codec_->border.suffix;
+    if (codec_expansion > mss_)
+      return 0;
+    return mss_ - codec_expansion;
+  }
+
+  void Send(SendRequestHdl request);
+  template <class GB, class A>
+  void Send(GB gb, A a) {
+    Send(SendRequest::FromFunctors(std::move(gb), std::move(a)));
+  }
+  void RequestSendAck();
+
+  Bandwidth BottleneckBandwidth() {
+    return outgoing_bbr_.bottleneck_bandwidth();
+  }
+
+  TimeDelta RoundTripTime() { return outgoing_bbr_.rtt(); }
+
+ private:
+  // Placing an OutstandingOp on a PacketProtocol object prevents it from
+  // quiescing
+  template <const char* kWTF>
+  class OutstandingOp {
+   public:
+    OutstandingOp() = delete;
+    OutstandingOp(PacketProtocol* pp) : pp_(pp) { pp_->BeginOp(why(), this); }
+    OutstandingOp(const OutstandingOp& other) : pp_(other.pp_) {
+      pp_->BeginOp(why(), this);
+    }
+    OutstandingOp& operator=(OutstandingOp other) {
+      other.Swap(this);
+      return *this;
+    }
+    void Swap(OutstandingOp* other) { std::swap(pp_, other->pp_); }
+
+    const char* why() { return kWTF; }
+
+    ~OutstandingOp() { pp_->EndOp(why(), this); }
+    PacketProtocol* operator->() const { return pp_; }
+    PacketProtocol* get() const { return pp_; }
+
+   private:
+    PacketProtocol* pp_;
+  };
+
+  enum class ReceiveState : uint8_t {
+    UNKNOWN,
+    NOT_RECEIVED,
+    RECEIVED,
+    RECEIVED_AND_SUPPRESSED_ACK
+  };
+
+  struct AckActions {
+    AckActions() = default;
+    AckActions(const AckActions&) = delete;
+    AckActions& operator=(const AckActions&) = delete;
+    AckActions(AckActions&&) = default;
+    AckActions& operator=(AckActions&&) = default;
+
+    std::vector<SendRequestHdl> acks;
+    std::vector<SendRequestHdl> nacks;
+    Optional<BBR::Ack> bbr_ack;
+  };
+
+  void RunAckActions(AckActions* ack_actions, const Status& status);
+
+ public:
+  static const char kProcessedPacket[];
+
+  class ProcessedPacket {
+   public:
+    ProcessedPacket(const ProcessedPacket&) = delete;
+    ProcessedPacket& operator=(const ProcessedPacket&) = delete;
+
+    StatusOr<Optional<Slice>> status;
+
+    // Force this packet to be nacked
+    void Nack();
+
+    ~ProcessedPacket();
+
+   private:
+    enum class SendAck : uint8_t { NONE, FORCE, SCHEDULE };
+
+    friend class PacketProtocol;
+    ProcessedPacket(OutstandingOp<kProcessedPacket> protocol, uint64_t seq_idx,
+                    SendAck send_ack, ReceiveState final_receive_state,
+                    StatusOr<Optional<Slice>> result,
+                    Optional<AckActions> ack_actions)
+        : status(std::move(result)),
+          seq_idx_(seq_idx),
+          final_receive_state_(final_receive_state),
+          send_ack_(send_ack),
+          protocol_(protocol),
+          ack_actions_(std::move(ack_actions)) {}
+
+    uint64_t seq_idx_;
+    ReceiveState final_receive_state_;
+    SendAck send_ack_;
+    OutstandingOp<kProcessedPacket> protocol_;
+    Optional<AckActions> ack_actions_;
+  };
+
+  ProcessedPacket Process(TimeStamp received, SeqNum seq, Slice slice);
+
+ private:
+  enum class OutstandingPacketState : uint8_t {
+    PENDING,
+    SENT,
+    ACKED,
+    NACKED,
+  };
+
+  struct OutstandingPacket {
+    TimeStamp scheduled;
+    OutstandingPacketState state;
+    bool has_ack;
+    bool is_pure_ack;
+    uint64_t ack_to_seq;
+    Optional<BBR::SentPacket> bbr_sent_packet;
+    SendRequestHdl request;
+  };
+
+  struct QueuedPacket {
+    Op op;
+    SendRequestHdl request;
+  };
+
+  bool AckIsNeeded() const;
+  TimeDelta QuarterRTT() const;
+  void MaybeForceAck();
+  void MaybeScheduleAck();
+  void MaybeSendAck();
+  void MaybeSendSlice(QueuedPacket&& packet);
+  void SendSlice(QueuedPacket&& packet);
+  void TransmitPacket();
+  StatusOr<AckActions> HandleAck(const AckFrame& ack, bool is_synthetic);
+  void ContinueSending();
+  void KeepAlive();
+  TimeStamp RetransmissionDeadline() const;
+  void ScheduleRTO();
+  void NackBefore(TimeStamp epoch, const Status& nack_status);
+  std::string AckDebugText();
+  void BeginOp(const char* name, void* whom) {
+#ifdef OVERNET_TRACE_PACKET_PROTOCOL_OPS
+    ScopedModule<PacketProtocol> mod(this);
+    OVERNET_TRACE(DEBUG) << " BEG " << name << " " << whom << " "
+                         << outstanding_ops_ << " -> "
+                         << (outstanding_ops_ + 1);
+#endif
+    ++outstanding_ops_;
+  }
+  void EndOp(const char* name, void* whom) {
+#ifdef OVERNET_TRACE_PACKET_PROTOCOL_OPS
+    ScopedModule<PacketProtocol> mod(this);
+    OVERNET_TRACE(DEBUG) << " END " << name << " " << whom << " "
+                         << outstanding_ops_ << " -> "
+                         << (outstanding_ops_ - 1);
+#endif
+    if (0 == --outstanding_ops_ && state_ == State::CLOSING) {
+      state_ = State::CLOSED;
+      auto cb = std::move(quiesced_);
+      cb();
+    }
+  }
+
+  Optional<AckFrame> GenerateAck(uint32_t max_length);
+  Optional<AckFrame> GenerateAckTo(TimeStamp now, uint64_t max_seen);
+  struct GeneratedPacket {
+    Slice payload;
+    bool has_ack;
+    bool is_pure_ack;
+  };
+  GeneratedPacket GeneratePacket(SendRequest* send_request, LazySliceArgs args);
+  Optional<uint64_t> LastRTOableSequence(TimeStamp epoch);
+  TimeDelta DelayForReceivedPacket(TimeStamp now, uint64_t seq_idx);
+
+  Timer* const timer_;
+  PacketSender* const packet_sender_;
+  const Codec* const codec_;
+  const uint64_t mss_;
+
+  enum class State { READY, CLOSING, CLOSED };
+
+  State state_ = State::READY;
+  Callback<void> quiesced_;
+
+  BBR outgoing_bbr_;
+
+  // TODO(ctiller): can we move to a ring buffer here? - the idea would be to
+  // just finished(RESOURCE_EXHAUSTED) if the ring is full
+  uint64_t send_tip_ = 1;
+  std::deque<OutstandingPacket> outstanding_;
+  std::deque<QueuedPacket> queued_;
+  Optional<QueuedPacket> sending_;
+  bool transmitting_ = false;
+
+  uint64_t recv_tip_ = 0;
+  uint64_t max_seen_ = 0;
+  uint64_t max_acked_ = 0;
+  uint64_t max_outstanding_size_ = 0;
+  uint64_t last_sent_ack_ = 0;
+
+  // TODO(ctiller): Find a more efficient data structure.
+  struct ReceivedPacket {
+    ReceiveState state;
+    TimeStamp when;
+  };
+  std::map<uint64_t, ReceivedPacket> received_packets_;
+
+  TimeStamp last_keepalive_event_ = TimeStamp::Epoch();
+  TimeStamp last_ack_send_ = TimeStamp::Epoch();
+  bool ack_after_sending_ = false;
+  bool ack_only_message_outstanding_ = false;
+  bool sent_ack_ = false;
+
+  int outstanding_ops_ = 0;
+
+  Optional<Timeout> ack_scheduler_;
+  Optional<Timeout> rto_scheduler_;
+
+  class AckOnlySendRequest : public SendRequest {
+   public:
+    AckOnlySendRequest(PacketProtocol* pp) : pp_(pp) {}
+    AckOnlySendRequest(const AckOnlySendRequest&) = delete;
+    AckOnlySendRequest& operator=(const AckOnlySendRequest&) = delete;
+    Slice GenerateBytes(LazySliceArgs args) {
+      pp_->ack_only_message_outstanding_ = false;
+      return Slice();
+    }
+    void Ack(const Status& status) {}
+
+   private:
+    PacketProtocol* const pp_;
+  };
+
+  AckOnlySendRequest ack_only_send_request_;
+};
+
+template <class GB, class A>
+PacketProtocol::SendRequestHdl PacketProtocol::SendRequest::FromFunctors(GB gb,
+                                                                         A a) {
+  class Send final : public SendRequest {
+   public:
+    Send(GB generate_bytes, A ack)
+        : generate_bytes_(std::move(generate_bytes)), ack_(std::move(ack)) {}
+
+    Slice GenerateBytes(LazySliceArgs args) { return generate_bytes_(args); }
+
+    void Ack(const Status& status) {
+      ack_(status);
+      delete this;
+    }
+
+   private:
+    GB generate_bytes_;
+    A ack_;
+  };
+
+  return SendRequestHdl(new Send(std::move(gb), std::move(a)));
+}
+
+}  // namespace overnet
diff --git a/lib/overnet/packet_protocol_fuzzer.cc b/lib/overnet/packet_protocol/packet_protocol_fuzzer.cc
similarity index 73%
rename from lib/overnet/packet_protocol_fuzzer.cc
rename to lib/overnet/packet_protocol/packet_protocol_fuzzer.cc
index 7a4b197..012fac5 100644
--- a/lib/overnet/packet_protocol_fuzzer.cc
+++ b/lib/overnet/packet_protocol/packet_protocol_fuzzer.cc
@@ -28,12 +28,12 @@
   Slice NextSlice() {
     auto len = NextByte();
     auto prefix = NextByte();
-    return Slice::WithInitializerAndPrefix(len, prefix,
-                                           [this, len](uint8_t* p) {
-                                             for (uint8_t i = 0; i < len; i++) {
-                                               *p++ = NextByte();
-                                             }
-                                           });
+    return Slice::WithInitializerAndBorders(
+        len, Border::Prefix(prefix), [this, len](uint8_t* p) {
+          for (uint8_t i = 0; i < len; i++) {
+            *p++ = NextByte();
+          }
+        });
   }
 
  private:
@@ -44,7 +44,7 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   InputStream input(data, size);
-  PacketProtocolFuzzer fuzzer;
+  PacketProtocolFuzzer fuzzer(PacketProtocol::NullCodec(), false);
   for (;;) {
     switch (input.NextByte()) {
       default:
@@ -58,9 +58,8 @@
       } break;
       case 2: {
         auto sender = input.NextByte();
-        auto send = input.Next64();
         auto status = input.NextByte();
-        if (!fuzzer.CompleteSend(sender, send, status))
+        if (!fuzzer.CompleteSend(sender, status))
           return 0;
       } break;
       case 3:
diff --git a/lib/overnet/packet_protocol_fuzzer_corpus_to_code.py b/lib/overnet/packet_protocol/packet_protocol_fuzzer_corpus_to_code.py
similarity index 100%
rename from lib/overnet/packet_protocol_fuzzer_corpus_to_code.py
rename to lib/overnet/packet_protocol/packet_protocol_fuzzer_corpus_to_code.py
diff --git a/lib/overnet/packet_protocol/packet_protocol_fuzzer_helpers.h b/lib/overnet/packet_protocol/packet_protocol_fuzzer_helpers.h
new file mode 100644
index 0000000..515f525
--- /dev/null
+++ b/lib/overnet/packet_protocol/packet_protocol_fuzzer_helpers.h
@@ -0,0 +1,132 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <iostream>
+#include <map>
+#include <random>
+#include "garnet/lib/overnet/packet_protocol/packet_protocol.h"
+#include "garnet/lib/overnet/testing/test_timer.h"
+#include "garnet/lib/overnet/testing/trace_cout.h"
+#include "garnet/lib/overnet/vocabulary/closed_ptr.h"
+
+namespace overnet {
+
+class PacketProtocolFuzzer {
+ public:
+  PacketProtocolFuzzer(const PacketProtocol::Codec* codec, bool log_stuff)
+      : logging_(log_stuff ? new Logging(&timer_) : nullptr), codec_(codec) {}
+  ~PacketProtocolFuzzer() { done_ = true; }
+
+  bool BeginSend(uint8_t sender_idx, Slice data) {
+    return packet_protocol(sender_idx).Then([this, data](PacketProtocol* pp) {
+      pp->Send(
+          [data](auto arg) { return data; },
+          [this](const Status& status) {
+            if (done_)
+              return;
+            if (!status.is_ok() && status.code() != StatusCode::CANCELLED) {
+              std::cerr << "Expected each send to be ok or cancelled, got: "
+                        << status << "\n";
+              abort();
+            }
+          });
+      return true;
+    });
+  }
+
+  bool CompleteSend(uint8_t sender_idx, uint8_t status) {
+    Optional<Sender::PendingSend> send =
+        sender(sender_idx).Then([status](Sender* sender) {
+          return sender->CompleteSend(status);
+        });
+    if (!send)
+      return false;
+    if (status == 0) {
+      auto slice = send->data(LazySliceArgs{
+          Border::None(), std::numeric_limits<uint32_t>::max(), false});
+      auto process_status = (*packet_protocol(3 - sender_idx))
+                                ->Process(timer_.Now(), send->seq, slice);
+      if (process_status.status.is_error()) {
+        std::cerr << "Expected Process() to return ok, got: "
+                  << process_status.status.AsStatus() << "\n";
+        abort();
+      }
+    }
+    return true;
+  }
+
+  bool StepTime(uint64_t microseconds) { return timer_.Step(microseconds); }
+
+ private:
+  enum { kMSS = 1500 };
+
+  class Sender final : public PacketProtocol::PacketSender {
+   public:
+    void SendPacket(SeqNum seq, LazySlice data, Callback<void> done) override {
+      pending_sends_.emplace(next_send_id_++,
+                             PendingSend{seq, std::move(data)});
+    }
+
+    struct PendingSend {
+      SeqNum seq;
+      LazySlice data;
+    };
+
+    Optional<PendingSend> CompleteSend(uint8_t status) {
+      auto it = pending_sends_.begin();
+      if (it == pending_sends_.end())
+        return Nothing;
+      auto ps = std::move(it->second);
+      pending_sends_.erase(it);
+      return std::move(ps);
+    }
+
+   private:
+    std::map<uint64_t, PendingSend> pending_sends_;
+    uint64_t next_send_id_ = 0;
+  };
+
+  Optional<Sender*> sender(uint8_t idx) {
+    switch (idx) {
+      case 1:
+        return &sender1_;
+      case 2:
+        return &sender2_;
+      default:
+        return Nothing;
+    }
+  }
+
+  Optional<PacketProtocol*> packet_protocol(uint8_t idx) {
+    switch (idx) {
+      case 1:
+        return pp1_.get();
+      case 2:
+        return pp2_.get();
+      default:
+        return Nothing;
+    }
+  }
+
+  bool done_ = false;
+  TestTimer timer_;
+  struct Logging {
+    Logging(Timer* timer) : tracer(timer) {}
+    TraceCout tracer;
+    ScopedRenderer set_tracer{&tracer};
+  };
+  std::unique_ptr<Logging> logging_;
+  Sender sender1_;
+  Sender sender2_;
+  std::mt19937 rng_{12345};
+  const PacketProtocol::Codec* const codec_;
+  ClosedPtr<PacketProtocol> pp1_ = MakeClosedPtr<PacketProtocol>(
+      &timer_, [this] { return rng_(); }, &sender1_, codec_, kMSS);
+  ClosedPtr<PacketProtocol> pp2_ = MakeClosedPtr<PacketProtocol>(
+      &timer_, [this] { return rng_(); }, &sender2_, codec_, kMSS);
+};  // namespace overnet
+
+}  // namespace overnet
diff --git a/lib/overnet/packet_protocol/packet_protocol_test.cc b/lib/overnet/packet_protocol/packet_protocol_test.cc
new file mode 100644
index 0000000..a847dbc
--- /dev/null
+++ b/lib/overnet/packet_protocol/packet_protocol_test.cc
@@ -0,0 +1,3913 @@
+// Copyright 2018 The Fuchsia 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 "garnet/lib/overnet/packet_protocol/packet_protocol.h"
+#include <memory>
+#include "garnet/lib/overnet/packet_protocol/aead_codec.h"
+#include "garnet/lib/overnet/protocol/serialization_helpers.h"
+#include "garnet/lib/overnet/testing/test_timer.h"
+#include "garnet/lib/overnet/testing/trace_cout.h"
+#include "garnet/lib/overnet/vocabulary/closed_ptr.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "packet_protocol_fuzzer_helpers.h"
+
+using testing::_;
+using testing::Mock;
+using testing::Pointee;
+using testing::Property;
+using testing::SaveArg;
+using testing::StrictMock;
+
+namespace overnet {
+namespace packet_protocol_test {
+
+// Some default MSS for tests
+static const uint64_t kMSS = 1500;
+
+class MockPacketSender : public PacketProtocol::PacketSender {
+ public:
+  MockPacketSender(Timer* timer) : timer_(timer) {}
+
+  MOCK_METHOD2(SendPacketMock, void(SeqNum, Slice));
+
+  void SendPacket(SeqNum seq, LazySlice slice, Callback<void> done) override {
+    TimeStamp now = timer_->Now();
+    TimeStamp when = now;
+    SendPacketMock(seq, slice(LazySliceArgs{Border::None(), kMSS, false}));
+    EXPECT_GE(when, now);
+  }
+
+  MOCK_METHOD1(SendCallback, void(const Status&));
+
+  auto NewSendCallback() {
+    return [this](const Status& status) { this->SendCallback(status); };
+  }
+
+ private:
+  Timer* const timer_;
+};
+
+class DummyCodec final : public PacketProtocol::Codec {
+ public:
+  DummyCodec() : Codec(Border{9, 9}) {}
+  StatusOr<Slice> Encode(uint64_t seq_idx, Slice slice) const {
+    return Slice::WithInitializer(slice.length() + 18, [&](uint8_t* p) {
+      *p++ = '[';
+      p = WriteLE64(seq_idx, p);
+      memcpy(p, slice.begin(), slice.length());
+      p += slice.length();
+      p = WriteLE64(seq_idx, p);
+      *p++ = ']';
+    });
+  }
+  StatusOr<Slice> Decode(uint64_t seq_idx, Slice slice) const {
+    const uint8_t* p = slice.begin();
+    EXPECT_EQ(*p, '[');
+    p++;
+    uint64_t seq1;
+    EXPECT_TRUE(ParseLE64(&p, slice.end(), &seq1));
+    EXPECT_EQ(seq_idx, seq1);
+    p = slice.end() - 9;
+    uint64_t seq2;
+    EXPECT_TRUE(ParseLE64(&p, slice.end(), &seq2));
+    EXPECT_EQ(seq_idx, seq2);
+    EXPECT_EQ(*p, ']');
+    return slice.Cut(9, slice.length() - 9);
+  }
+};
+static DummyCodec dummy_codec;
+
+static std::unique_ptr<AEADCodec> MakeTestAEADCodec(const EVP_AEAD* fn) {
+  std::vector<uint8_t> key;
+  std::random_device rng;
+  for (size_t i = 0; i < EVP_AEAD_key_length(fn); i++) {
+    key.push_back(rng());
+  }
+
+  return std::make_unique<AEADCodec>(fn, key.data(), key.size(), nullptr, 0);
+}
+static std::unique_ptr<AEADCodec> aead_codec_chacha =
+    MakeTestAEADCodec(EVP_aead_chacha20_poly1305());
+static std::unique_ptr<AEADCodec> aead_codec_hmac =
+    MakeTestAEADCodec(EVP_aead_aes_256_ctr_hmac_sha256());
+
+class PacketProtocolTest
+    : public ::testing::TestWithParam<const PacketProtocol::Codec*> {};
+
+TEST_P(PacketProtocolTest, NoOp) {
+  TestTimer timer;
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
+
+  StrictMock<MockPacketSender> ps(&timer);
+  std::mt19937 rng{123};
+  MakeClosedPtr<PacketProtocol>(&timer, [&rng] { return rng(); }, &ps,
+                                GetParam(), kMSS);
+}
+
+TEST_P(PacketProtocolTest, SendOnePacket) {
+  TestTimer timer;
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
+
+  StrictMock<MockPacketSender> ps(&timer);
+  std::mt19937 rng{123};
+  auto packet_protocol = MakeClosedPtr<PacketProtocol>(
+      &timer, [&rng] { return rng(); }, &ps, GetParam(), kMSS);
+
+  // Send some dummy data: we expect to see a packet emitted immediately
+  Slice got_slice;
+  EXPECT_CALL(
+      ps, SendPacketMock(Property(&SeqNum::ReconstructFromZero_TestOnly, 1), _))
+      .WillOnce(SaveArg<1>(&got_slice));
+
+  packet_protocol->Send(
+      [](auto arg) {
+        EXPECT_LT(arg.desired_border.prefix, kMSS);
+        EXPECT_LT(arg.desired_border.suffix, kMSS);
+        EXPECT_LE(arg.max_length, kMSS);
+        EXPECT_GT(arg.max_length, uint64_t(0));
+        return Slice::FromContainer({1, 2, 3, 4, 5});
+      },
+      ps.NewSendCallback());
+  Mock::VerifyAndClearExpectations(&ps);
+
+  auto status = GetParam()->Decode(1, got_slice);
+  EXPECT_TRUE(status.is_ok());
+  EXPECT_EQ(*status, Slice::FromContainer({0, 1, 2, 3, 4, 5}));
+
+  // Build a fake ack packet.
+  Slice ack = Slice::FromWritable(AckFrame(1, 1));
+  status = GetParam()->Encode(
+      1, ack.WithPrefix(1, [len = ack.length()](uint8_t* p) { *p = len; }));
+  EXPECT_TRUE(status.is_ok());
+  ack = std::move(*status);
+
+  // Calling Process on it should succeed and trigger the completion callback
+  // for the send.
+  EXPECT_CALL(ps, SendCallback(Property(&Status::is_ok, true)));
+  EXPECT_THAT(
+      packet_protocol->Process(TimeStamp::Epoch(), SeqNum(1, 1), ack).status,
+      Pointee(Slice()));
+}
+
+// Exposed some bugs in the fuzzer, and a bug whereby empty ack frames caused a
+// failure.
+TEST_P(PacketProtocolTest, _02ef5d596c101ce01181a7dcd0a294ed81c88dbd) {
+  PacketProtocolFuzzer fuzzer(GetParam(), true);
+  static const uint8_t block0[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block0, 1); }))) {
+    return;
+  }
+  static const uint8_t block1[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block1, 1); }))) {
+    return;
+  }
+  static const uint8_t block2[] = {0x01, 0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               2, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block2, 2); }))) {
+    return;
+  }
+  static const uint8_t block3[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block3, 1); }))) {
+    return;
+  }
+  static const uint8_t block4[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(3),
+                               [](uint8_t* p) { memcpy(p, block4, 1); }))) {
+    return;
+  }
+  static const uint8_t block5[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block5, 1); }))) {
+    return;
+  }
+  static const uint8_t block6[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block6, 1); }))) {
+    return;
+  }
+  static const uint8_t block7[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block7, 1); }))) {
+    return;
+  }
+  static const uint8_t block8[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(255),
+                               [](uint8_t* p) { memcpy(p, block8, 1); }))) {
+    return;
+  }
+  if (!fuzzer.CompleteSend(1, 0)) {
+    return;
+  }
+}
+
+// Exposed a bug in the fuzzer.
+TEST_P(PacketProtocolTest, _d9c8d575a34f511dfae936725f8a6752b910e258) {
+  PacketProtocolFuzzer fuzzer(GetParam(), true);
+  static const uint8_t block0[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block0, 1); }))) {
+    return;
+  }
+  static const uint8_t block1[] = {0x00};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block1, 1); }))) {
+    return;
+  }
+  if (!fuzzer.CompleteSend(1, 1)) {
+    return;
+  }
+  static const uint8_t block2[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block2, 1); }))) {
+    return;
+  }
+  static const uint8_t block3[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block3, 1); }))) {
+    return;
+  }
+  static const uint8_t block4[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block4, 1); }))) {
+    return;
+  }
+  if (!fuzzer.StepTime(506ull)) {
+    return;
+  }
+  if (!fuzzer.StepTime(1ull)) {
+    return;
+  }
+  static const uint8_t block5[] = {
+      0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0xfa, 0x03,
+      0x03, 0xfa, 0xff, 0xff, 0x03, 0x03, 0xfa, 0xff, 0xff, 0x01, 0x02, 0xff,
+      0xff, 0xff, 0x01, 0xff, 0xff, 0x00, 0x54, 0xff, 0x28, 0xff, 0xff, 0x97,
+      0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xbb, 0x01, 0x00, 0xff, 0x00,
+      0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xff, 0xff, 0x34, 0x34,
+      0x34, 0x34, 0x34, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0xff, 0xff, 0x03, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               250, Border::Prefix(255),
+                               [](uint8_t* p) { memcpy(p, block5, 250); }))) {
+    return;
+  }
+}
+
+// Found a bug with ack ordering on writes.
+TEST_P(PacketProtocolTest, _da3f40d81b8c5d0609dc83e2edeb576054c106b8) {
+  PacketProtocolFuzzer fuzzer(GetParam(), true);
+  static const uint8_t block0[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block0, 1); }))) {
+    return;
+  }
+  static const uint8_t block1[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block1, 1); }))) {
+    return;
+  }
+  static const uint8_t block2[] = {0x00};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block2, 1); }))) {
+    return;
+  }
+  static const uint8_t block3[] = {0xee};
+  if (!fuzzer.BeginSend(2, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block3, 1); }))) {
+    return;
+  }
+  if (!fuzzer.CompleteSend(1, 0)) {
+    return;
+  }
+  static const uint8_t block4[] = {0xee};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block4, 1); }))) {
+    return;
+  }
+  if (!fuzzer.CompleteSend(0, 0)) {
+    return;
+  }
+}
+
+// Found a wraparound bug in tiemr code.
+TEST_P(PacketProtocolTest, _87b138d6497b8f037691af618f319455c6f5a3b0) {
+  PacketProtocolFuzzer fuzzer(GetParam(), true);
+  static const uint8_t block0[] = {0x01, 0x01, 0x01, 0x01, 0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               5, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block0, 5); }))) {
+    return;
+  }
+  static const uint8_t block1[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block1, 1); }))) {
+    return;
+  }
+  static const uint8_t block2[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block2, 1); }))) {
+    return;
+  }
+  static const uint8_t block3[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block3, 1); }))) {
+    return;
+  }
+  static const uint8_t block4[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block4, 1); }))) {
+    return;
+  }
+  static const uint8_t block5[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block5, 1); }))) {
+    return;
+  }
+  static const uint8_t block6[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block6, 1); }))) {
+    return;
+  }
+  static const uint8_t block7[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block7, 1); }))) {
+    return;
+  }
+  static const uint8_t block8[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block8, 1); }))) {
+    return;
+  }
+  static const uint8_t block9[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block9, 1); }))) {
+    return;
+  }
+  static const uint8_t block10[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block10, 1); }))) {
+    return;
+  }
+  static const uint8_t block11[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block11, 1); }))) {
+    return;
+  }
+  static const uint8_t block12[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block12, 1); }))) {
+    return;
+  }
+  static const uint8_t block13[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block13, 1); }))) {
+    return;
+  }
+  static const uint8_t block14[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block14, 1); }))) {
+    return;
+  }
+  static const uint8_t block15[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block15, 1); }))) {
+    return;
+  }
+  static const uint8_t block16[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block16, 1); }))) {
+    return;
+  }
+  static const uint8_t block17[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block17, 1); }))) {
+    return;
+  }
+  static const uint8_t block18[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block18, 1); }))) {
+    return;
+  }
+  static const uint8_t block19[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block19, 1); }))) {
+    return;
+  }
+  static const uint8_t block20[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block20, 1); }))) {
+    return;
+  }
+  static const uint8_t block21[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block21, 1); }))) {
+    return;
+  }
+  static const uint8_t block22[] = {};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               0, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block22, 0); }))) {
+    return;
+  }
+  if (!fuzzer.CompleteSend(1, 0)) {
+    return;
+  }
+  static const uint8_t block23[] = {0x02, 0x00};
+  if (!fuzzer.BeginSend(2, Slice::WithInitializerAndBorders(
+                               2, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block23, 2); }))) {
+    return;
+  }
+  static const uint8_t block24[] = {0x01};
+  if (!fuzzer.BeginSend(2, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block24, 1); }))) {
+    return;
+  }
+  static const uint8_t block25[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block25, 1); }))) {
+    return;
+  }
+  static const uint8_t block26[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block26, 1); }))) {
+    return;
+  }
+  static const uint8_t block27[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block27, 1); }))) {
+    return;
+  }
+  static const uint8_t block28[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block28, 1); }))) {
+    return;
+  }
+  static const uint8_t block29[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block29, 1); }))) {
+    return;
+  }
+  static const uint8_t block30[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block30, 1); }))) {
+    return;
+  }
+  static const uint8_t block31[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block31, 1); }))) {
+    return;
+  }
+  static const uint8_t block32[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block32, 1); }))) {
+    return;
+  }
+  static const uint8_t block33[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block33, 1); }))) {
+    return;
+  }
+  static const uint8_t block34[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block34, 1); }))) {
+    return;
+  }
+  static const uint8_t block35[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block35, 1); }))) {
+    return;
+  }
+  static const uint8_t block36[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block36, 1); }))) {
+    return;
+  }
+  static const uint8_t block37[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block37, 1); }))) {
+    return;
+  }
+  static const uint8_t block38[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block38, 1); }))) {
+    return;
+  }
+  static const uint8_t block39[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block39, 1); }))) {
+    return;
+  }
+  static const uint8_t block40[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block40, 1); }))) {
+    return;
+  }
+  static const uint8_t block41[] = {
+      0xfe, 0xfa, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
+      0x07, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               255, Border::Prefix(254),
+                               [](uint8_t* p) { memcpy(p, block41, 255); }))) {
+    return;
+  }
+  static const uint8_t block42[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block42, 1); }))) {
+    return;
+  }
+  static const uint8_t block43[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block43, 1); }))) {
+    return;
+  }
+  static const uint8_t block44[] = {0x01};
+  if (!fuzzer.BeginSend(2, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block44, 1); }))) {
+    return;
+  }
+  static const uint8_t block45[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block45, 1); }))) {
+    return;
+  }
+  static const uint8_t block46[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block46, 1); }))) {
+    return;
+  }
+  static const uint8_t block47[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block47, 1); }))) {
+    return;
+  }
+  static const uint8_t block48[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block48, 1); }))) {
+    return;
+  }
+  static const uint8_t block49[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block49, 1); }))) {
+    return;
+  }
+  static const uint8_t block50[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block50, 1); }))) {
+    return;
+  }
+  static const uint8_t block51[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block51, 1); }))) {
+    return;
+  }
+  static const uint8_t block52[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block52, 1); }))) {
+    return;
+  }
+  static const uint8_t block53[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block53, 1); }))) {
+    return;
+  }
+  static const uint8_t block54[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block54, 1); }))) {
+    return;
+  }
+  static const uint8_t block55[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block55, 1); }))) {
+    return;
+  }
+  static const uint8_t block56[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block56, 1); }))) {
+    return;
+  }
+  static const uint8_t block57[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block57, 1); }))) {
+    return;
+  }
+  static const uint8_t block58[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block58, 1); }))) {
+    return;
+  }
+  static const uint8_t block59[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block59, 1); }))) {
+    return;
+  }
+  static const uint8_t block60[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block60, 1); }))) {
+    return;
+  }
+  static const uint8_t block61[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block61, 1); }))) {
+    return;
+  }
+  static const uint8_t block62[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block62, 1); }))) {
+    return;
+  }
+  static const uint8_t block63[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block63, 1); }))) {
+    return;
+  }
+  static const uint8_t block64[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block64, 1); }))) {
+    return;
+  }
+  static const uint8_t block65[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block65, 1); }))) {
+    return;
+  }
+  static const uint8_t block66[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block66, 1); }))) {
+    return;
+  }
+  static const uint8_t block67[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block67, 1); }))) {
+    return;
+  }
+  static const uint8_t block68[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block68, 1); }))) {
+    return;
+  }
+  static const uint8_t block69[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block69, 1); }))) {
+    return;
+  }
+  static const uint8_t block70[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block70, 1); }))) {
+    return;
+  }
+  if (!fuzzer.StepTime(18446744073709551612ull)) {
+    return;
+  }
+  static const uint8_t block71[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block71, 1); }))) {
+    return;
+  }
+  static const uint8_t block72[] = {0x00};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block72, 1); }))) {
+    return;
+  }
+}
+
+// Exposed a bug with too many outstanding sends.
+TEST_P(PacketProtocolTest, _ffaebbd1370c62dee2d0c6f85553d47a88e6c320) {
+  PacketProtocolFuzzer fuzzer(GetParam(), true);
+  static const uint8_t block0[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block0, 1); }))) {
+    return;
+  }
+  static const uint8_t block1[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block1, 1); }))) {
+    return;
+  }
+  static const uint8_t block2[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block2, 1); }))) {
+    return;
+  }
+  static const uint8_t block3[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block3, 1); }))) {
+    return;
+  }
+  static const uint8_t block4[] = {
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x28, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x27, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               129, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block4, 129); }))) {
+    return;
+  }
+  static const uint8_t block5[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block5, 1); }))) {
+    return;
+  }
+  static const uint8_t block6[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block6, 1); }))) {
+    return;
+  }
+  static const uint8_t block7[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block7, 1); }))) {
+    return;
+  }
+  static const uint8_t block8[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block8, 1); }))) {
+    return;
+  }
+  static const uint8_t block9[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block9, 1); }))) {
+    return;
+  }
+  static const uint8_t block10[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(5),
+                               [](uint8_t* p) { memcpy(p, block10, 1); }))) {
+    return;
+  }
+  static const uint8_t block11[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block11, 1); }))) {
+    return;
+  }
+  static const uint8_t block12[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block12, 1); }))) {
+    return;
+  }
+  static const uint8_t block13[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(0),
+                               [](uint8_t* p) { memcpy(p, block13, 1); }))) {
+    return;
+  }
+  static const uint8_t block14[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block14, 1); }))) {
+    return;
+  }
+  static const uint8_t block15[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block15, 1); }))) {
+    return;
+  }
+  static const uint8_t block16[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block16, 1); }))) {
+    return;
+  }
+  static const uint8_t block17[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block17, 1); }))) {
+    return;
+  }
+  static const uint8_t block18[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block18, 1); }))) {
+    return;
+  }
+  static const uint8_t block19[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block19, 1); }))) {
+    return;
+  }
+  static const uint8_t block20[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block20, 1); }))) {
+    return;
+  }
+  static const uint8_t block21[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block21, 1); }))) {
+    return;
+  }
+  static const uint8_t block22[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block22, 1); }))) {
+    return;
+  }
+  static const uint8_t block23[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block23, 1); }))) {
+    return;
+  }
+  static const uint8_t block24[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block24, 1); }))) {
+    return;
+  }
+  static const uint8_t block25[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block25, 1); }))) {
+    return;
+  }
+  static const uint8_t block26[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block26, 1); }))) {
+    return;
+  }
+  static const uint8_t block27[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block27, 1); }))) {
+    return;
+  }
+  static const uint8_t block28[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block28, 1); }))) {
+    return;
+  }
+  static const uint8_t block29[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block29, 1); }))) {
+    return;
+  }
+  static const uint8_t block30[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block30, 1); }))) {
+    return;
+  }
+  static const uint8_t block31[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block31, 1); }))) {
+    return;
+  }
+  static const uint8_t block32[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block32, 1); }))) {
+    return;
+  }
+  static const uint8_t block33[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block33, 1); }))) {
+    return;
+  }
+  static const uint8_t block34[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block34, 1); }))) {
+    return;
+  }
+  static const uint8_t block35[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(0),
+                               [](uint8_t* p) { memcpy(p, block35, 1); }))) {
+    return;
+  }
+  static const uint8_t block36[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block36, 1); }))) {
+    return;
+  }
+  static const uint8_t block37[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block37, 1); }))) {
+    return;
+  }
+  static const uint8_t block38[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block38, 1); }))) {
+    return;
+  }
+  static const uint8_t block39[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block39, 1); }))) {
+    return;
+  }
+  static const uint8_t block40[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block40, 1); }))) {
+    return;
+  }
+  static const uint8_t block41[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block41, 1); }))) {
+    return;
+  }
+  static const uint8_t block42[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block42, 1); }))) {
+    return;
+  }
+  static const uint8_t block43[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block43, 1); }))) {
+    return;
+  }
+  static const uint8_t block44[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block44, 1); }))) {
+    return;
+  }
+  static const uint8_t block45[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block45, 1); }))) {
+    return;
+  }
+  static const uint8_t block46[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block46, 1); }))) {
+    return;
+  }
+  static const uint8_t block47[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block47, 1); }))) {
+    return;
+  }
+  static const uint8_t block48[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block48, 1); }))) {
+    return;
+  }
+  static const uint8_t block49[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block49, 1); }))) {
+    return;
+  }
+  static const uint8_t block50[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block50, 1); }))) {
+    return;
+  }
+  static const uint8_t block51[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block51, 1); }))) {
+    return;
+  }
+  static const uint8_t block52[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block52, 1); }))) {
+    return;
+  }
+  static const uint8_t block53[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block53, 1); }))) {
+    return;
+  }
+  static const uint8_t block54[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block54, 1); }))) {
+    return;
+  }
+  static const uint8_t block55[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block55, 1); }))) {
+    return;
+  }
+  static const uint8_t block56[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block56, 1); }))) {
+    return;
+  }
+  static const uint8_t block57[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block57, 1); }))) {
+    return;
+  }
+  static const uint8_t block58[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block58, 1); }))) {
+    return;
+  }
+  static const uint8_t block59[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block59, 1); }))) {
+    return;
+  }
+  static const uint8_t block60[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block60, 1); }))) {
+    return;
+  }
+  static const uint8_t block61[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block61, 1); }))) {
+    return;
+  }
+  static const uint8_t block62[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block62, 1); }))) {
+    return;
+  }
+  static const uint8_t block63[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block63, 1); }))) {
+    return;
+  }
+  static const uint8_t block64[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block64, 1); }))) {
+    return;
+  }
+  static const uint8_t block65[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block65, 1); }))) {
+    return;
+  }
+  static const uint8_t block66[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(0),
+                               [](uint8_t* p) { memcpy(p, block66, 1); }))) {
+    return;
+  }
+  static const uint8_t block67[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block67, 1); }))) {
+    return;
+  }
+  static const uint8_t block68[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block68, 1); }))) {
+    return;
+  }
+  static const uint8_t block69[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block69, 1); }))) {
+    return;
+  }
+  static const uint8_t block70[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block70, 1); }))) {
+    return;
+  }
+  static const uint8_t block71[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block71, 1); }))) {
+    return;
+  }
+  static const uint8_t block72[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block72, 1); }))) {
+    return;
+  }
+  static const uint8_t block73[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block73, 1); }))) {
+    return;
+  }
+  static const uint8_t block74[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block74, 1); }))) {
+    return;
+  }
+  static const uint8_t block75[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block75, 1); }))) {
+    return;
+  }
+  static const uint8_t block76[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block76, 1); }))) {
+    return;
+  }
+  static const uint8_t block77[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block77, 1); }))) {
+    return;
+  }
+  static const uint8_t block78[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block78, 1); }))) {
+    return;
+  }
+  static const uint8_t block79[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block79, 1); }))) {
+    return;
+  }
+  static const uint8_t block80[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block80, 1); }))) {
+    return;
+  }
+  static const uint8_t block81[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block81, 1); }))) {
+    return;
+  }
+  static const uint8_t block82[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block82, 1); }))) {
+    return;
+  }
+  static const uint8_t block83[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block83, 1); }))) {
+    return;
+  }
+  static const uint8_t block84[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block84, 1); }))) {
+    return;
+  }
+  static const uint8_t block85[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block85, 1); }))) {
+    return;
+  }
+  static const uint8_t block86[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block86, 1); }))) {
+    return;
+  }
+  static const uint8_t block87[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block87, 1); }))) {
+    return;
+  }
+  static const uint8_t block88[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block88, 1); }))) {
+    return;
+  }
+  static const uint8_t block89[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block89, 1); }))) {
+    return;
+  }
+  static const uint8_t block90[] = {0x3a};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block90, 1); }))) {
+    return;
+  }
+  static const uint8_t block91[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block91, 1); }))) {
+    return;
+  }
+  static const uint8_t block92[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block92, 1); }))) {
+    return;
+  }
+  static const uint8_t block93[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block93, 1); }))) {
+    return;
+  }
+  static const uint8_t block94[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block94, 1); }))) {
+    return;
+  }
+  static const uint8_t block95[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block95, 1); }))) {
+    return;
+  }
+  static const uint8_t block96[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block96, 1); }))) {
+    return;
+  }
+  static const uint8_t block97[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block97, 1); }))) {
+    return;
+  }
+  static const uint8_t block98[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block98, 1); }))) {
+    return;
+  }
+  static const uint8_t block99[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block99, 1); }))) {
+    return;
+  }
+  static const uint8_t block100[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block100, 1); }))) {
+    return;
+  }
+  static const uint8_t block101[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block101, 1); }))) {
+    return;
+  }
+  static const uint8_t block102[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block102, 1); }))) {
+    return;
+  }
+  static const uint8_t block103[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block103, 1); }))) {
+    return;
+  }
+  static const uint8_t block104[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block104, 1); }))) {
+    return;
+  }
+  static const uint8_t block105[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block105, 1); }))) {
+    return;
+  }
+  static const uint8_t block106[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block106, 1); }))) {
+    return;
+  }
+  static const uint8_t block107[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block107, 1); }))) {
+    return;
+  }
+  static const uint8_t block108[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block108, 1); }))) {
+    return;
+  }
+  static const uint8_t block109[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block109, 1); }))) {
+    return;
+  }
+  static const uint8_t block110[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block110, 1); }))) {
+    return;
+  }
+  static const uint8_t block111[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block111, 1); }))) {
+    return;
+  }
+  static const uint8_t block112[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block112, 1); }))) {
+    return;
+  }
+  static const uint8_t block113[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block113, 1); }))) {
+    return;
+  }
+  static const uint8_t block114[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block114, 1); }))) {
+    return;
+  }
+  static const uint8_t block115[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block115, 1); }))) {
+    return;
+  }
+  static const uint8_t block116[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block116, 1); }))) {
+    return;
+  }
+  static const uint8_t block117[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block117, 1); }))) {
+    return;
+  }
+  static const uint8_t block118[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block118, 1); }))) {
+    return;
+  }
+  static const uint8_t block119[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block119, 1); }))) {
+    return;
+  }
+  static const uint8_t block120[] = {};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               0, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block120, 0); }))) {
+    return;
+  }
+  static const uint8_t block121[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block121, 1); }))) {
+    return;
+  }
+  static const uint8_t block122[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block122, 1); }))) {
+    return;
+  }
+  static const uint8_t block123[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block123, 1); }))) {
+    return;
+  }
+  static const uint8_t block124[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block124, 1); }))) {
+    return;
+  }
+  static const uint8_t block125[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block125, 1); }))) {
+    return;
+  }
+  static const uint8_t block126[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block126, 1); }))) {
+    return;
+  }
+  static const uint8_t block127[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block127, 1); }))) {
+    return;
+  }
+  static const uint8_t block128[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block128, 1); }))) {
+    return;
+  }
+  static const uint8_t block129[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block129, 1); }))) {
+    return;
+  }
+  static const uint8_t block130[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block130, 1); }))) {
+    return;
+  }
+  static const uint8_t block131[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block131, 1); }))) {
+    return;
+  }
+  static const uint8_t block132[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block132, 1); }))) {
+    return;
+  }
+  static const uint8_t block133[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block133, 1); }))) {
+    return;
+  }
+  static const uint8_t block134[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block134, 1); }))) {
+    return;
+  }
+  static const uint8_t block135[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block135, 1); }))) {
+    return;
+  }
+  static const uint8_t block136[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block136, 1); }))) {
+    return;
+  }
+  static const uint8_t block137[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block137, 1); }))) {
+    return;
+  }
+  static const uint8_t block138[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block138, 1); }))) {
+    return;
+  }
+  static const uint8_t block139[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block139, 1); }))) {
+    return;
+  }
+  static const uint8_t block140[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block140, 1); }))) {
+    return;
+  }
+  static const uint8_t block141[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block141, 1); }))) {
+    return;
+  }
+  static const uint8_t block142[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block142, 1); }))) {
+    return;
+  }
+  static const uint8_t block143[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block143, 1); }))) {
+    return;
+  }
+  static const uint8_t block144[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block144, 1); }))) {
+    return;
+  }
+  static const uint8_t block145[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block145, 1); }))) {
+    return;
+  }
+  static const uint8_t block146[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block146, 1); }))) {
+    return;
+  }
+  static const uint8_t block147[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block147, 1); }))) {
+    return;
+  }
+  static const uint8_t block148[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block148, 1); }))) {
+    return;
+  }
+  static const uint8_t block149[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block149, 1); }))) {
+    return;
+  }
+  static const uint8_t block150[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block150, 1); }))) {
+    return;
+  }
+  static const uint8_t block151[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(0),
+                               [](uint8_t* p) { memcpy(p, block151, 1); }))) {
+    return;
+  }
+  static const uint8_t block152[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block152, 1); }))) {
+    return;
+  }
+  static const uint8_t block153[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block153, 1); }))) {
+    return;
+  }
+  static const uint8_t block154[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block154, 1); }))) {
+    return;
+  }
+  static const uint8_t block155[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block155, 1); }))) {
+    return;
+  }
+  static const uint8_t block156[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block156, 1); }))) {
+    return;
+  }
+  static const uint8_t block157[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block157, 1); }))) {
+    return;
+  }
+  static const uint8_t block158[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block158, 1); }))) {
+    return;
+  }
+  static const uint8_t block159[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block159, 1); }))) {
+    return;
+  }
+  static const uint8_t block160[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block160, 1); }))) {
+    return;
+  }
+  static const uint8_t block161[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block161, 1); }))) {
+    return;
+  }
+  static const uint8_t block162[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block162, 1); }))) {
+    return;
+  }
+  static const uint8_t block163[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block163, 1); }))) {
+    return;
+  }
+  static const uint8_t block164[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block164, 1); }))) {
+    return;
+  }
+  static const uint8_t block165[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block165, 1); }))) {
+    return;
+  }
+  static const uint8_t block166[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block166, 1); }))) {
+    return;
+  }
+  static const uint8_t block167[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block167, 1); }))) {
+    return;
+  }
+  static const uint8_t block168[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block168, 1); }))) {
+    return;
+  }
+  static const uint8_t block169[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block169, 1); }))) {
+    return;
+  }
+  static const uint8_t block170[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block170, 1); }))) {
+    return;
+  }
+  static const uint8_t block171[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block171, 1); }))) {
+    return;
+  }
+  static const uint8_t block172[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block172, 1); }))) {
+    return;
+  }
+  static const uint8_t block173[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block173, 1); }))) {
+    return;
+  }
+  static const uint8_t block174[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block174, 1); }))) {
+    return;
+  }
+  static const uint8_t block175[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block175, 1); }))) {
+    return;
+  }
+  static const uint8_t block176[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block176, 1); }))) {
+    return;
+  }
+  static const uint8_t block177[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block177, 1); }))) {
+    return;
+  }
+  static const uint8_t block178[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block178, 1); }))) {
+    return;
+  }
+  static const uint8_t block179[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block179, 1); }))) {
+    return;
+  }
+  static const uint8_t block180[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block180, 1); }))) {
+    return;
+  }
+  static const uint8_t block181[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block181, 1); }))) {
+    return;
+  }
+  static const uint8_t block182[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block182, 1); }))) {
+    return;
+  }
+  static const uint8_t block183[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block183, 1); }))) {
+    return;
+  }
+  static const uint8_t block184[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block184, 1); }))) {
+    return;
+  }
+  static const uint8_t block185[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block185, 1); }))) {
+    return;
+  }
+  static const uint8_t block186[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block186, 1); }))) {
+    return;
+  }
+  static const uint8_t block187[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block187, 1); }))) {
+    return;
+  }
+  static const uint8_t block188[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block188, 1); }))) {
+    return;
+  }
+  static const uint8_t block189[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block189, 1); }))) {
+    return;
+  }
+  static const uint8_t block190[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block190, 1); }))) {
+    return;
+  }
+  static const uint8_t block191[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block191, 1); }))) {
+    return;
+  }
+  static const uint8_t block192[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block192, 1); }))) {
+    return;
+  }
+  static const uint8_t block193[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block193, 1); }))) {
+    return;
+  }
+  static const uint8_t block194[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block194, 1); }))) {
+    return;
+  }
+  static const uint8_t block195[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block195, 1); }))) {
+    return;
+  }
+  static const uint8_t block196[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block196, 1); }))) {
+    return;
+  }
+  static const uint8_t block197[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block197, 1); }))) {
+    return;
+  }
+  static const uint8_t block198[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block198, 1); }))) {
+    return;
+  }
+  static const uint8_t block199[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block199, 1); }))) {
+    return;
+  }
+  static const uint8_t block200[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block200, 1); }))) {
+    return;
+  }
+  static const uint8_t block201[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block201, 1); }))) {
+    return;
+  }
+  static const uint8_t block202[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block202, 1); }))) {
+    return;
+  }
+  static const uint8_t block203[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block203, 1); }))) {
+    return;
+  }
+  static const uint8_t block204[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block204, 1); }))) {
+    return;
+  }
+  static const uint8_t block205[] = {0x00};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block205, 1); }))) {
+    return;
+  }
+  static const uint8_t block206[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block206, 1); }))) {
+    return;
+  }
+  static const uint8_t block207[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block207, 1); }))) {
+    return;
+  }
+  static const uint8_t block208[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block208, 1); }))) {
+    return;
+  }
+  static const uint8_t block209[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block209, 1); }))) {
+    return;
+  }
+  static const uint8_t block210[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block210, 1); }))) {
+    return;
+  }
+  static const uint8_t block211[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block211, 1); }))) {
+    return;
+  }
+  static const uint8_t block212[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block212, 1); }))) {
+    return;
+  }
+  static const uint8_t block213[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block213, 1); }))) {
+    return;
+  }
+  static const uint8_t block214[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block214, 1); }))) {
+    return;
+  }
+  static const uint8_t block215[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block215, 1); }))) {
+    return;
+  }
+  static const uint8_t block216[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block216, 1); }))) {
+    return;
+  }
+  static const uint8_t block217[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block217, 1); }))) {
+    return;
+  }
+  static const uint8_t block218[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block218, 1); }))) {
+    return;
+  }
+  static const uint8_t block219[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block219, 1); }))) {
+    return;
+  }
+  static const uint8_t block220[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block220, 1); }))) {
+    return;
+  }
+  static const uint8_t block221[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block221, 1); }))) {
+    return;
+  }
+  static const uint8_t block222[] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+                                     0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+                                     0x01, 0x01, 0x01, 0x01, 0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               17, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block222, 17); }))) {
+    return;
+  }
+  static const uint8_t block223[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block223, 1); }))) {
+    return;
+  }
+  static const uint8_t block224[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block224, 1); }))) {
+    return;
+  }
+  static const uint8_t block225[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block225, 1); }))) {
+    return;
+  }
+  static const uint8_t block226[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block226, 1); }))) {
+    return;
+  }
+  static const uint8_t block227[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block227, 1); }))) {
+    return;
+  }
+  static const uint8_t block228[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block228, 1); }))) {
+    return;
+  }
+  static const uint8_t block229[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block229, 1); }))) {
+    return;
+  }
+  static const uint8_t block230[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block230, 1); }))) {
+    return;
+  }
+  static const uint8_t block231[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block231, 1); }))) {
+    return;
+  }
+  static const uint8_t block232[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block232, 1); }))) {
+    return;
+  }
+  static const uint8_t block233[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block233, 1); }))) {
+    return;
+  }
+  static const uint8_t block234[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block234, 1); }))) {
+    return;
+  }
+  static const uint8_t block235[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block235, 1); }))) {
+    return;
+  }
+  static const uint8_t block236[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block236, 1); }))) {
+    return;
+  }
+  static const uint8_t block237[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block237, 1); }))) {
+    return;
+  }
+  static const uint8_t block238[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block238, 1); }))) {
+    return;
+  }
+  static const uint8_t block239[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block239, 1); }))) {
+    return;
+  }
+  static const uint8_t block240[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block240, 1); }))) {
+    return;
+  }
+  static const uint8_t block241[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block241, 1); }))) {
+    return;
+  }
+  static const uint8_t block242[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block242, 1); }))) {
+    return;
+  }
+  static const uint8_t block243[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block243, 1); }))) {
+    return;
+  }
+  static const uint8_t block244[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block244, 1); }))) {
+    return;
+  }
+  static const uint8_t block245[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block245, 1); }))) {
+    return;
+  }
+  static const uint8_t block246[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block246, 1); }))) {
+    return;
+  }
+  static const uint8_t block247[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block247, 1); }))) {
+    return;
+  }
+  static const uint8_t block248[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block248, 1); }))) {
+    return;
+  }
+  static const uint8_t block249[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block249, 1); }))) {
+    return;
+  }
+  static const uint8_t block250[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block250, 1); }))) {
+    return;
+  }
+  static const uint8_t block251[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block251, 1); }))) {
+    return;
+  }
+  static const uint8_t block252[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block252, 1); }))) {
+    return;
+  }
+  static const uint8_t block253[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block253, 1); }))) {
+    return;
+  }
+  static const uint8_t block254[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block254, 1); }))) {
+    return;
+  }
+  static const uint8_t block255[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block255, 1); }))) {
+    return;
+  }
+  static const uint8_t block256[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block256, 1); }))) {
+    return;
+  }
+  static const uint8_t block257[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block257, 1); }))) {
+    return;
+  }
+  static const uint8_t block258[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block258, 1); }))) {
+    return;
+  }
+  static const uint8_t block259[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block259, 1); }))) {
+    return;
+  }
+  static const uint8_t block260[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block260, 1); }))) {
+    return;
+  }
+  static const uint8_t block261[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block261, 1); }))) {
+    return;
+  }
+  static const uint8_t block262[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block262, 1); }))) {
+    return;
+  }
+  static const uint8_t block263[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block263, 1); }))) {
+    return;
+  }
+  static const uint8_t block264[] = {};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               0, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block264, 0); }))) {
+    return;
+  }
+  static const uint8_t block265[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block265, 1); }))) {
+    return;
+  }
+  static const uint8_t block266[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block266, 1); }))) {
+    return;
+  }
+  static const uint8_t block267[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block267, 1); }))) {
+    return;
+  }
+  static const uint8_t block268[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block268, 1); }))) {
+    return;
+  }
+  static const uint8_t block269[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block269, 1); }))) {
+    return;
+  }
+  static const uint8_t block270[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block270, 1); }))) {
+    return;
+  }
+  static const uint8_t block271[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block271, 1); }))) {
+    return;
+  }
+  static const uint8_t block272[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block272, 1); }))) {
+    return;
+  }
+  static const uint8_t block273[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block273, 1); }))) {
+    return;
+  }
+  static const uint8_t block274[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block274, 1); }))) {
+    return;
+  }
+  static const uint8_t block275[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block275, 1); }))) {
+    return;
+  }
+  static const uint8_t block276[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block276, 1); }))) {
+    return;
+  }
+  static const uint8_t block277[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block277, 1); }))) {
+    return;
+  }
+  static const uint8_t block278[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block278, 1); }))) {
+    return;
+  }
+  static const uint8_t block279[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block279, 1); }))) {
+    return;
+  }
+  static const uint8_t block280[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block280, 1); }))) {
+    return;
+  }
+  static const uint8_t block281[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block281, 1); }))) {
+    return;
+  }
+  static const uint8_t block282[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block282, 1); }))) {
+    return;
+  }
+  static const uint8_t block283[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block283, 1); }))) {
+    return;
+  }
+  static const uint8_t block284[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block284, 1); }))) {
+    return;
+  }
+  static const uint8_t block285[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block285, 1); }))) {
+    return;
+  }
+  static const uint8_t block286[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block286, 1); }))) {
+    return;
+  }
+  static const uint8_t block287[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block287, 1); }))) {
+    return;
+  }
+  static const uint8_t block288[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block288, 1); }))) {
+    return;
+  }
+  static const uint8_t block289[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block289, 1); }))) {
+    return;
+  }
+  static const uint8_t block290[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block290, 1); }))) {
+    return;
+  }
+  static const uint8_t block291[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block291, 1); }))) {
+    return;
+  }
+  static const uint8_t block292[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block292, 1); }))) {
+    return;
+  }
+  static const uint8_t block293[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block293, 1); }))) {
+    return;
+  }
+  static const uint8_t block294[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block294, 1); }))) {
+    return;
+  }
+  static const uint8_t block295[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block295, 1); }))) {
+    return;
+  }
+  static const uint8_t block296[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block296, 1); }))) {
+    return;
+  }
+  static const uint8_t block297[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block297, 1); }))) {
+    return;
+  }
+  static const uint8_t block298[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block298, 1); }))) {
+    return;
+  }
+  static const uint8_t block299[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block299, 1); }))) {
+    return;
+  }
+  static const uint8_t block300[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block300, 1); }))) {
+    return;
+  }
+  static const uint8_t block301[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block301, 1); }))) {
+    return;
+  }
+  static const uint8_t block302[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block302, 1); }))) {
+    return;
+  }
+  static const uint8_t block303[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block303, 1); }))) {
+    return;
+  }
+  static const uint8_t block304[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block304, 1); }))) {
+    return;
+  }
+  static const uint8_t block305[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(0),
+                               [](uint8_t* p) { memcpy(p, block305, 1); }))) {
+    return;
+  }
+  static const uint8_t block306[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block306, 1); }))) {
+    return;
+  }
+  static const uint8_t block307[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block307, 1); }))) {
+    return;
+  }
+  static const uint8_t block308[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block308, 1); }))) {
+    return;
+  }
+  static const uint8_t block309[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block309, 1); }))) {
+    return;
+  }
+  static const uint8_t block310[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block310, 1); }))) {
+    return;
+  }
+  static const uint8_t block311[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block311, 1); }))) {
+    return;
+  }
+  static const uint8_t block312[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block312, 1); }))) {
+    return;
+  }
+  static const uint8_t block313[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block313, 1); }))) {
+    return;
+  }
+  static const uint8_t block314[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block314, 1); }))) {
+    return;
+  }
+  static const uint8_t block315[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block315, 1); }))) {
+    return;
+  }
+  static const uint8_t block316[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block316, 1); }))) {
+    return;
+  }
+  static const uint8_t block317[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block317, 1); }))) {
+    return;
+  }
+  static const uint8_t block318[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block318, 1); }))) {
+    return;
+  }
+  static const uint8_t block319[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block319, 1); }))) {
+    return;
+  }
+  static const uint8_t block320[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block320, 1); }))) {
+    return;
+  }
+  static const uint8_t block321[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block321, 1); }))) {
+    return;
+  }
+  static const uint8_t block322[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block322, 1); }))) {
+    return;
+  }
+  static const uint8_t block323[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block323, 1); }))) {
+    return;
+  }
+  static const uint8_t block324[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block324, 1); }))) {
+    return;
+  }
+  static const uint8_t block325[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block325, 1); }))) {
+    return;
+  }
+  static const uint8_t block326[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block326, 1); }))) {
+    return;
+  }
+  static const uint8_t block327[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block327, 1); }))) {
+    return;
+  }
+  static const uint8_t block328[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block328, 1); }))) {
+    return;
+  }
+  static const uint8_t block329[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block329, 1); }))) {
+    return;
+  }
+  static const uint8_t block330[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block330, 1); }))) {
+    return;
+  }
+  static const uint8_t block331[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block331, 1); }))) {
+    return;
+  }
+  static const uint8_t block332[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block332, 1); }))) {
+    return;
+  }
+  static const uint8_t block333[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block333, 1); }))) {
+    return;
+  }
+  static const uint8_t block334[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block334, 1); }))) {
+    return;
+  }
+  static const uint8_t block335[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block335, 1); }))) {
+    return;
+  }
+  static const uint8_t block336[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block336, 1); }))) {
+    return;
+  }
+  static const uint8_t block337[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block337, 1); }))) {
+    return;
+  }
+  static const uint8_t block338[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block338, 1); }))) {
+    return;
+  }
+  static const uint8_t block339[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block339, 1); }))) {
+    return;
+  }
+  static const uint8_t block340[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block340, 1); }))) {
+    return;
+  }
+  static const uint8_t block341[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block341, 1); }))) {
+    return;
+  }
+  static const uint8_t block342[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block342, 1); }))) {
+    return;
+  }
+  static const uint8_t block343[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block343, 1); }))) {
+    return;
+  }
+  static const uint8_t block344[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block344, 1); }))) {
+    return;
+  }
+  static const uint8_t block345[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block345, 1); }))) {
+    return;
+  }
+  static const uint8_t block346[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block346, 1); }))) {
+    return;
+  }
+  static const uint8_t block347[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block347, 1); }))) {
+    return;
+  }
+  static const uint8_t block348[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block348, 1); }))) {
+    return;
+  }
+  static const uint8_t block349[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block349, 1); }))) {
+    return;
+  }
+  static const uint8_t block350[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block350, 1); }))) {
+    return;
+  }
+  static const uint8_t block351[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block351, 1); }))) {
+    return;
+  }
+  static const uint8_t block352[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block352, 1); }))) {
+    return;
+  }
+  static const uint8_t block353[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block353, 1); }))) {
+    return;
+  }
+  static const uint8_t block354[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block354, 1); }))) {
+    return;
+  }
+  static const uint8_t block355[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block355, 1); }))) {
+    return;
+  }
+  static const uint8_t block356[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block356, 1); }))) {
+    return;
+  }
+  static const uint8_t block357[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block357, 1); }))) {
+    return;
+  }
+  static const uint8_t block358[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block358, 1); }))) {
+    return;
+  }
+  static const uint8_t block359[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block359, 1); }))) {
+    return;
+  }
+  static const uint8_t block360[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block360, 1); }))) {
+    return;
+  }
+  static const uint8_t block361[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block361, 1); }))) {
+    return;
+  }
+  static const uint8_t block362[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block362, 1); }))) {
+    return;
+  }
+  static const uint8_t block363[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block363, 1); }))) {
+    return;
+  }
+  static const uint8_t block364[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block364, 1); }))) {
+    return;
+  }
+  static const uint8_t block365[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block365, 1); }))) {
+    return;
+  }
+  static const uint8_t block366[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block366, 1); }))) {
+    return;
+  }
+  static const uint8_t block367[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block367, 1); }))) {
+    return;
+  }
+  static const uint8_t block368[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block368, 1); }))) {
+    return;
+  }
+  static const uint8_t block369[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block369, 1); }))) {
+    return;
+  }
+  static const uint8_t block370[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block370, 1); }))) {
+    return;
+  }
+  static const uint8_t block371[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block371, 1); }))) {
+    return;
+  }
+  static const uint8_t block372[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block372, 1); }))) {
+    return;
+  }
+  static const uint8_t block373[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block373, 1); }))) {
+    return;
+  }
+  static const uint8_t block374[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block374, 1); }))) {
+    return;
+  }
+  static const uint8_t block375[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block375, 1); }))) {
+    return;
+  }
+  static const uint8_t block376[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block376, 1); }))) {
+    return;
+  }
+  static const uint8_t block377[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block377, 1); }))) {
+    return;
+  }
+  static const uint8_t block378[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block378, 1); }))) {
+    return;
+  }
+  static const uint8_t block379[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block379, 1); }))) {
+    return;
+  }
+  static const uint8_t block380[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block380, 1); }))) {
+    return;
+  }
+  static const uint8_t block381[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block381, 1); }))) {
+    return;
+  }
+  static const uint8_t block382[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block382, 1); }))) {
+    return;
+  }
+  static const uint8_t block383[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block383, 1); }))) {
+    return;
+  }
+  static const uint8_t block384[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block384, 1); }))) {
+    return;
+  }
+  static const uint8_t block385[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block385, 1); }))) {
+    return;
+  }
+  static const uint8_t block386[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block386, 1); }))) {
+    return;
+  }
+  static const uint8_t block387[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block387, 1); }))) {
+    return;
+  }
+  static const uint8_t block388[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block388, 1); }))) {
+    return;
+  }
+  static const uint8_t block389[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block389, 1); }))) {
+    return;
+  }
+  static const uint8_t block390[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block390, 1); }))) {
+    return;
+  }
+  static const uint8_t block391[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block391, 1); }))) {
+    return;
+  }
+  static const uint8_t block392[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block392, 1); }))) {
+    return;
+  }
+  static const uint8_t block393[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block393, 1); }))) {
+    return;
+  }
+  static const uint8_t block394[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block394, 1); }))) {
+    return;
+  }
+  static const uint8_t block395[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block395, 1); }))) {
+    return;
+  }
+  static const uint8_t block396[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block396, 1); }))) {
+    return;
+  }
+  static const uint8_t block397[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block397, 1); }))) {
+    return;
+  }
+  static const uint8_t block398[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block398, 1); }))) {
+    return;
+  }
+  static const uint8_t block399[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block399, 1); }))) {
+    return;
+  }
+  static const uint8_t block400[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block400, 1); }))) {
+    return;
+  }
+  static const uint8_t block401[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block401, 1); }))) {
+    return;
+  }
+  static const uint8_t block402[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block402, 1); }))) {
+    return;
+  }
+  static const uint8_t block403[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block403, 1); }))) {
+    return;
+  }
+  static const uint8_t block404[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block404, 1); }))) {
+    return;
+  }
+  static const uint8_t block405[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block405, 1); }))) {
+    return;
+  }
+  static const uint8_t block406[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block406, 1); }))) {
+    return;
+  }
+  static const uint8_t block407[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block407, 1); }))) {
+    return;
+  }
+  static const uint8_t block408[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block408, 1); }))) {
+    return;
+  }
+  static const uint8_t block409[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block409, 1); }))) {
+    return;
+  }
+  static const uint8_t block410[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block410, 1); }))) {
+    return;
+  }
+  static const uint8_t block411[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(0),
+                               [](uint8_t* p) { memcpy(p, block411, 1); }))) {
+    return;
+  }
+  static const uint8_t block412[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block412, 1); }))) {
+    return;
+  }
+  static const uint8_t block413[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block413, 1); }))) {
+    return;
+  }
+  static const uint8_t block414[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block414, 1); }))) {
+    return;
+  }
+  static const uint8_t block415[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block415, 1); }))) {
+    return;
+  }
+  static const uint8_t block416[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block416, 1); }))) {
+    return;
+  }
+  static const uint8_t block417[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block417, 1); }))) {
+    return;
+  }
+  static const uint8_t block418[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block418, 1); }))) {
+    return;
+  }
+  static const uint8_t block419[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block419, 1); }))) {
+    return;
+  }
+  static const uint8_t block420[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block420, 1); }))) {
+    return;
+  }
+  static const uint8_t block421[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block421, 1); }))) {
+    return;
+  }
+  static const uint8_t block422[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block422, 1); }))) {
+    return;
+  }
+  static const uint8_t block423[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block423, 1); }))) {
+    return;
+  }
+  static const uint8_t block424[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block424, 1); }))) {
+    return;
+  }
+  static const uint8_t block425[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block425, 1); }))) {
+    return;
+  }
+  static const uint8_t block426[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block426, 1); }))) {
+    return;
+  }
+  static const uint8_t block427[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block427, 1); }))) {
+    return;
+  }
+  static const uint8_t block428[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block428, 1); }))) {
+    return;
+  }
+  static const uint8_t block429[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block429, 1); }))) {
+    return;
+  }
+  static const uint8_t block430[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block430, 1); }))) {
+    return;
+  }
+  static const uint8_t block431[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block431, 1); }))) {
+    return;
+  }
+  static const uint8_t block432[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block432, 1); }))) {
+    return;
+  }
+  static const uint8_t block433[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block433, 1); }))) {
+    return;
+  }
+  static const uint8_t block434[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block434, 1); }))) {
+    return;
+  }
+  static const uint8_t block435[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block435, 1); }))) {
+    return;
+  }
+  static const uint8_t block436[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block436, 1); }))) {
+    return;
+  }
+  static const uint8_t block437[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block437, 1); }))) {
+    return;
+  }
+  static const uint8_t block438[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block438, 1); }))) {
+    return;
+  }
+  static const uint8_t block439[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block439, 1); }))) {
+    return;
+  }
+  static const uint8_t block440[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block440, 1); }))) {
+    return;
+  }
+  static const uint8_t block441[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block441, 1); }))) {
+    return;
+  }
+  static const uint8_t block442[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block442, 1); }))) {
+    return;
+  }
+  static const uint8_t block443[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block443, 1); }))) {
+    return;
+  }
+  static const uint8_t block444[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block444, 1); }))) {
+    return;
+  }
+  static const uint8_t block445[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block445, 1); }))) {
+    return;
+  }
+  static const uint8_t block446[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block446, 1); }))) {
+    return;
+  }
+  static const uint8_t block447[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block447, 1); }))) {
+    return;
+  }
+  static const uint8_t block448[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block448, 1); }))) {
+    return;
+  }
+  static const uint8_t block449[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block449, 1); }))) {
+    return;
+  }
+  static const uint8_t block450[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block450, 1); }))) {
+    return;
+  }
+  static const uint8_t block451[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block451, 1); }))) {
+    return;
+  }
+  static const uint8_t block452[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block452, 1); }))) {
+    return;
+  }
+  static const uint8_t block453[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block453, 1); }))) {
+    return;
+  }
+  static const uint8_t block454[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block454, 1); }))) {
+    return;
+  }
+  static const uint8_t block455[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block455, 1); }))) {
+    return;
+  }
+  static const uint8_t block456[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block456, 1); }))) {
+    return;
+  }
+  static const uint8_t block457[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block457, 1); }))) {
+    return;
+  }
+  static const uint8_t block458[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block458, 1); }))) {
+    return;
+  }
+  static const uint8_t block459[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block459, 1); }))) {
+    return;
+  }
+  static const uint8_t block460[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block460, 1); }))) {
+    return;
+  }
+  static const uint8_t block461[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block461, 1); }))) {
+    return;
+  }
+  static const uint8_t block462[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block462, 1); }))) {
+    return;
+  }
+  static const uint8_t block463[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block463, 1); }))) {
+    return;
+  }
+  static const uint8_t block464[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block464, 1); }))) {
+    return;
+  }
+  static const uint8_t block465[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block465, 1); }))) {
+    return;
+  }
+  static const uint8_t block466[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block466, 1); }))) {
+    return;
+  }
+  static const uint8_t block467[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block467, 1); }))) {
+    return;
+  }
+  static const uint8_t block468[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block468, 1); }))) {
+    return;
+  }
+  static const uint8_t block469[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block469, 1); }))) {
+    return;
+  }
+  static const uint8_t block470[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block470, 1); }))) {
+    return;
+  }
+  static const uint8_t block471[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block471, 1); }))) {
+    return;
+  }
+  static const uint8_t block472[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block472, 1); }))) {
+    return;
+  }
+  static const uint8_t block473[] = {};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               0, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block473, 0); }))) {
+    return;
+  }
+  static const uint8_t block474[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block474, 1); }))) {
+    return;
+  }
+  static const uint8_t block475[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block475, 1); }))) {
+    return;
+  }
+  static const uint8_t block476[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block476, 1); }))) {
+    return;
+  }
+  static const uint8_t block477[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block477, 1); }))) {
+    return;
+  }
+  static const uint8_t block478[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block478, 1); }))) {
+    return;
+  }
+  static const uint8_t block479[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block479, 1); }))) {
+    return;
+  }
+  static const uint8_t block480[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block480, 1); }))) {
+    return;
+  }
+  static const uint8_t block481[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block481, 1); }))) {
+    return;
+  }
+  static const uint8_t block482[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block482, 1); }))) {
+    return;
+  }
+  static const uint8_t block483[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block483, 1); }))) {
+    return;
+  }
+  static const uint8_t block484[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block484, 1); }))) {
+    return;
+  }
+  static const uint8_t block485[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block485, 1); }))) {
+    return;
+  }
+  static const uint8_t block486[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block486, 1); }))) {
+    return;
+  }
+  static const uint8_t block487[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block487, 1); }))) {
+    return;
+  }
+  static const uint8_t block488[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block488, 1); }))) {
+    return;
+  }
+  static const uint8_t block489[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block489, 1); }))) {
+    return;
+  }
+  static const uint8_t block490[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block490, 1); }))) {
+    return;
+  }
+  static const uint8_t block491[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block491, 1); }))) {
+    return;
+  }
+  static const uint8_t block492[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block492, 1); }))) {
+    return;
+  }
+  static const uint8_t block493[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block493, 1); }))) {
+    return;
+  }
+  static const uint8_t block494[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block494, 1); }))) {
+    return;
+  }
+  static const uint8_t block495[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block495, 1); }))) {
+    return;
+  }
+  static const uint8_t block496[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block496, 1); }))) {
+    return;
+  }
+  static const uint8_t block497[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block497, 1); }))) {
+    return;
+  }
+  static const uint8_t block498[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block498, 1); }))) {
+    return;
+  }
+  static const uint8_t block499[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block499, 1); }))) {
+    return;
+  }
+  static const uint8_t block500[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block500, 1); }))) {
+    return;
+  }
+  static const uint8_t block501[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block501, 1); }))) {
+    return;
+  }
+  static const uint8_t block502[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block502, 1); }))) {
+    return;
+  }
+  static const uint8_t block503[] = {0x28};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block503, 1); }))) {
+    return;
+  }
+  static const uint8_t block504[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block504, 1); }))) {
+    return;
+  }
+  static const uint8_t block505[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block505, 1); }))) {
+    return;
+  }
+  static const uint8_t block506[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block506, 1); }))) {
+    return;
+  }
+  static const uint8_t block507[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block507, 1); }))) {
+    return;
+  }
+  static const uint8_t block508[] = {0x01, 0x02};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               2, Border::Prefix(0),
+                               [](uint8_t* p) { memcpy(p, block508, 2); }))) {
+    return;
+  }
+  if (!fuzzer.CompleteSend(1, 0)) {
+    return;
+  }
+  // Snipped... irrelevant to reproduction.
+}
+
+// Found a bug in the fuzzer around nack handling.
+TEST_P(PacketProtocolTest, _9bfa77589cb379397dafc6661fee887af34c03de) {
+  PacketProtocolFuzzer fuzzer(GetParam(), true);
+  static const uint8_t block0[] = {0x01};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block0, 1); }))) {
+    return;
+  }
+  static const uint8_t block1[] = {};
+  if (!fuzzer.BeginSend(1, Slice::WithInitializerAndBorders(
+                               0, Border::Prefix(2),
+                               [](uint8_t* p) { memcpy(p, block1, 0); }))) {
+    return;
+  }
+  if (!fuzzer.CompleteSend(1, 0)) {
+    return;
+  }
+  static const uint8_t block2[] = {0x00};
+  if (!fuzzer.BeginSend(2, Slice::WithInitializerAndBorders(
+                               1, Border::Prefix(1),
+                               [](uint8_t* p) { memcpy(p, block2, 1); }))) {
+    return;
+  }
+  if (!fuzzer.CompleteSend(2, 0)) {
+    return;
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(PacketProtocol, PacketProtocolTest,
+                        ::testing::Values(PacketProtocol::NullCodec(),
+                                          &dummy_codec, aead_codec_chacha.get(),
+                                          aead_codec_hmac.get()));
+
+}  // namespace packet_protocol_test
+}  // namespace overnet
diff --git a/lib/overnet/windowed_filter.h b/lib/overnet/packet_protocol/windowed_filter.h
similarity index 100%
rename from lib/overnet/windowed_filter.h
rename to lib/overnet/packet_protocol/windowed_filter.h
diff --git a/lib/overnet/packet_protocol_fuzzer_helpers.h b/lib/overnet/packet_protocol_fuzzer_helpers.h
deleted file mode 100644
index e925bf2..0000000
--- a/lib/overnet/packet_protocol_fuzzer_helpers.h
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#pragma once
-
-#include <iostream>
-#include <map>
-#include "packet_protocol.h"
-#include "test_timer.h"
-
-namespace overnet {
-
-class PacketProtocolFuzzer {
- public:
-  ~PacketProtocolFuzzer() { done_ = true; }
-
-  bool BeginSend(uint8_t sender_idx, Slice data) {
-    return packet_protocol(sender_idx).Then([this, data](PacketProtocol* pp) {
-      pp->Send(
-          [data](auto arg) { return data; },
-          [this](const Status& status) {
-            if (done_)
-              return;
-            if (!status.is_ok() && status.code() != StatusCode::CANCELLED) {
-              std::cerr << "Expected each send to be ok or cancelled, got: "
-                        << status << "\n";
-              abort();
-            }
-          });
-      return true;
-    });
-  }
-
-  bool CompleteSend(uint8_t sender_idx, uint64_t send_idx, uint8_t status) {
-    Optional<Sender::PendingSend> send =
-        sender(sender_idx).Then([send_idx, status](Sender* sender) {
-          return sender->CompleteSend(send_idx, status);
-        });
-    if (!send)
-      return false;
-    if (status == 0) {
-      auto now = timer_.Now();
-      auto when = now;
-      auto slice = send->data(
-          LazySliceArgs{0, std::numeric_limits<uint32_t>::max(), false, &when});
-      timer_.At(when, [=, seq = send->seq] {
-        auto process_status = (*packet_protocol(3 - sender_idx))
-                                  ->Process(timer_.Now(), seq, slice);
-        if (process_status.status.is_error()) {
-          std::cerr << "Expected Process() to return ok, got: "
-                    << process_status.status.AsStatus() << "\n";
-          abort();
-        }
-      });
-    }
-    return true;
-  }
-
-  bool StepTime(uint64_t microseconds) { return timer_.Step(microseconds); }
-
- private:
-  enum { kMSS = 1500 };
-
-  class Sender final : public PacketProtocol::PacketSender {
-   public:
-    void SendPacket(SeqNum seq, LazySlice data, Callback<void> done) override {
-      pending_sends_.emplace(next_send_id_++,
-                             PendingSend{seq, std::move(data)});
-    }
-
-    struct PendingSend {
-      SeqNum seq;
-      LazySlice data;
-    };
-
-    Optional<PendingSend> CompleteSend(uint64_t send_idx, uint8_t status) {
-      auto it = pending_sends_.find(send_idx);
-      if (it == pending_sends_.end())
-        return Nothing;
-      auto ps = std::move(it->second);
-      pending_sends_.erase(it);
-      return std::move(ps);
-    }
-
-   private:
-    std::map<uint64_t, PendingSend> pending_sends_;
-    uint64_t next_send_id_ = 0;
-  };
-
-  Optional<Sender*> sender(uint8_t idx) {
-    switch (idx) {
-      case 1:
-        return &sender1_;
-      case 2:
-        return &sender2_;
-      default:
-        return Nothing;
-    }
-  }
-
-  Optional<PacketProtocol*> packet_protocol(uint8_t idx) {
-    switch (idx) {
-      case 1:
-        return pp1_.get();
-      case 2:
-        return pp2_.get();
-      default:
-        return Nothing;
-    }
-  }
-
-  bool done_ = false;
-  TestTimer timer_;
-  Sender sender1_;
-  Sender sender2_;
-  ClosedPtr<PacketProtocol> pp1_ =
-      MakeClosedPtr<PacketProtocol>(&timer_, &sender1_, TraceSink(), kMSS);
-  ClosedPtr<PacketProtocol> pp2_ =
-      MakeClosedPtr<PacketProtocol>(&timer_, &sender2_, TraceSink(), kMSS);
-};  // namespace overnet
-
-}  // namespace overnet
diff --git a/lib/overnet/packet_protocol_test.cc b/lib/overnet/packet_protocol_test.cc
deleted file mode 100644
index 2a98035..0000000
--- a/lib/overnet/packet_protocol_test.cc
+++ /dev/null
@@ -1,3843 +0,0 @@
-// Copyright 2018 The Fuchsia 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 "packet_protocol.h"
-#include <memory>
-#include "closed_ptr.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "packet_protocol_fuzzer_helpers.h"
-#include "test_timer.h"
-#include "trace_cout.h"
-
-using testing::_;
-using testing::Mock;
-using testing::Pointee;
-using testing::Property;
-using testing::SaveArg;
-using testing::StrictMock;
-
-namespace overnet {
-namespace packet_protocol_test {
-
-// Some default MSS for tests
-static const uint64_t kMSS = 1500;
-
-class MockPacketSender : public PacketProtocol::PacketSender {
- public:
-  MockPacketSender(Timer* timer) : timer_(timer) {}
-
-  MOCK_METHOD2(SendPacketMock, void(SeqNum, Slice));
-
-  void SendPacket(SeqNum seq, LazySlice slice, Callback<void> done) override {
-    TimeStamp now = timer_->Now();
-    TimeStamp when = now;
-    SendPacketMock(seq, slice(LazySliceArgs{0, kMSS, false, &when}));
-    EXPECT_GE(when, now);
-  }
-
-  MOCK_METHOD1(SendCallback, void(const Status&));
-
-  PacketProtocol::SendCallback NewSendCallback() {
-    return PacketProtocol::SendCallback(
-        ALLOCATED_CALLBACK,
-        [this](const Status& status) { this->SendCallback(status); });
-  }
-
- private:
-  Timer* const timer_;
-};
-
-TEST(PacketProtocol, NoOp) {
-  TestTimer timer;
-  StrictMock<MockPacketSender> ps(&timer);
-  MakeClosedPtr<PacketProtocol>(&timer, &ps, TraceCout(&timer), kMSS);
-}
-
-TEST(PacketProtocol, SendOnePacket) {
-  TestTimer timer;
-  StrictMock<MockPacketSender> ps(&timer);
-  auto packet_protocol =
-      MakeClosedPtr<PacketProtocol>(&timer, &ps, TraceCout(&timer), kMSS);
-
-  // Send some dummy data: we expect to see a packet emitted immediately
-  EXPECT_CALL(ps,
-              SendPacketMock(Property(&SeqNum::ReconstructFromZero_TestOnly, 1),
-                             Slice::FromContainer({0, 1, 2, 3, 4, 5})));
-
-  packet_protocol->Send(
-      [](auto arg) {
-        EXPECT_LT(arg.desired_prefix, kMSS);
-        EXPECT_LE(arg.max_length, kMSS);
-        EXPECT_GT(arg.max_length, uint64_t(0));
-        return Slice::FromContainer({1, 2, 3, 4, 5});
-      },
-      ps.NewSendCallback());
-  Mock::VerifyAndClearExpectations(&ps);
-
-  // Build a fake ack packet.
-  Slice ack = Slice::FromWritable(AckFrame(1, 1));
-  ack = ack.WithPrefix(1, [len = ack.length()](uint8_t* p) { *p = len; });
-
-  // Calling Process on it should succeed and trigger the completion callback
-  // for the send.
-  EXPECT_CALL(ps, SendCallback(Property(&Status::is_ok, true)));
-  EXPECT_THAT(
-      packet_protocol->Process(TimeStamp::Epoch(), SeqNum(1, 1), ack).status,
-      Pointee(Slice()));
-}
-
-// Exposed some bugs in the fuzzer, and a bug whereby empty ack frames caused a
-// failure.
-TEST(PacketProtocolFuzzed, _02ef5d596c101ce01181a7dcd0a294ed81c88dbd) {
-  PacketProtocolFuzzer fuzzer;
-  static const uint8_t block0[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block0, 1); }))) {
-    return;
-  }
-  static const uint8_t block1[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block1, 1); }))) {
-    return;
-  }
-  static const uint8_t block2[] = {0x01, 0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            2, 1, [](uint8_t* p) { memcpy(p, block2, 2); }))) {
-    return;
-  }
-  static const uint8_t block3[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block3, 1); }))) {
-    return;
-  }
-  static const uint8_t block4[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 3, [](uint8_t* p) { memcpy(p, block4, 1); }))) {
-    return;
-  }
-  static const uint8_t block5[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block5, 1); }))) {
-    return;
-  }
-  static const uint8_t block6[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block6, 1); }))) {
-    return;
-  }
-  static const uint8_t block7[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block7, 1); }))) {
-    return;
-  }
-  static const uint8_t block8[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 255, [](uint8_t* p) { memcpy(p, block8, 1); }))) {
-    return;
-  }
-  if (!fuzzer.CompleteSend(1, 0ull, 0)) {
-    return;
-  }
-}
-
-// Exposed a bug in the fuzzer.
-TEST(PacketProtocolFuzzed, _d9c8d575a34f511dfae936725f8a6752b910e258) {
-  PacketProtocolFuzzer fuzzer;
-  static const uint8_t block0[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block0, 1); }))) {
-    return;
-  }
-  static const uint8_t block1[] = {0x00};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block1, 1); }))) {
-    return;
-  }
-  if (!fuzzer.CompleteSend(1, 1ull, 1)) {
-    return;
-  }
-  static const uint8_t block2[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block2, 1); }))) {
-    return;
-  }
-  static const uint8_t block3[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block3, 1); }))) {
-    return;
-  }
-  static const uint8_t block4[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block4, 1); }))) {
-    return;
-  }
-  if (!fuzzer.StepTime(506ull)) {
-    return;
-  }
-  if (!fuzzer.StepTime(1ull)) {
-    return;
-  }
-  static const uint8_t block5[] = {
-      0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0xfa, 0x03,
-      0x03, 0xfa, 0xff, 0xff, 0x03, 0x03, 0xfa, 0xff, 0xff, 0x01, 0x02, 0xff,
-      0xff, 0xff, 0x01, 0xff, 0xff, 0x00, 0x54, 0xff, 0x28, 0xff, 0xff, 0x97,
-      0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xbb, 0x01, 0x00, 0xff, 0x00,
-      0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xff, 0xff, 0x34, 0x34,
-      0x34, 0x34, 0x34, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0xff, 0xff, 0x03, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 250, 255, [](uint8_t* p) { memcpy(p, block5, 250); }))) {
-    return;
-  }
-}
-
-// Found a bug with ack ordering on writes.
-TEST(PacketProtocolFuzzed, _da3f40d81b8c5d0609dc83e2edeb576054c106b8) {
-  PacketProtocolFuzzer fuzzer;
-  static const uint8_t block0[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block0, 1); }))) {
-    return;
-  }
-  static const uint8_t block1[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block1, 1); }))) {
-    return;
-  }
-  static const uint8_t block2[] = {0x00};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block2, 1); }))) {
-    return;
-  }
-  static const uint8_t block3[] = {0xee};
-  if (!fuzzer.BeginSend(2,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block3, 1); }))) {
-    return;
-  }
-  if (!fuzzer.CompleteSend(1, 2ull, 0)) {
-    return;
-  }
-  static const uint8_t block4[] = {0xee};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block4, 1); }))) {
-    return;
-  }
-  if (!fuzzer.CompleteSend(0, 0ull, 0)) {
-    return;
-  }
-}
-
-// Found a wraparound bug in tiemr code.
-TEST(PacketProtocolFuzzed, _87b138d6497b8f037691af618f319455c6f5a3b0) {
-  PacketProtocolFuzzer fuzzer;
-  static const uint8_t block0[] = {0x01, 0x01, 0x01, 0x01, 0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            5, 1, [](uint8_t* p) { memcpy(p, block0, 5); }))) {
-    return;
-  }
-  static const uint8_t block1[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block1, 1); }))) {
-    return;
-  }
-  static const uint8_t block2[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block2, 1); }))) {
-    return;
-  }
-  static const uint8_t block3[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block3, 1); }))) {
-    return;
-  }
-  static const uint8_t block4[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block4, 1); }))) {
-    return;
-  }
-  static const uint8_t block5[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block5, 1); }))) {
-    return;
-  }
-  static const uint8_t block6[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block6, 1); }))) {
-    return;
-  }
-  static const uint8_t block7[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block7, 1); }))) {
-    return;
-  }
-  static const uint8_t block8[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block8, 1); }))) {
-    return;
-  }
-  static const uint8_t block9[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block9, 1); }))) {
-    return;
-  }
-  static const uint8_t block10[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block10, 1); }))) {
-    return;
-  }
-  static const uint8_t block11[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block11, 1); }))) {
-    return;
-  }
-  static const uint8_t block12[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block12, 1); }))) {
-    return;
-  }
-  static const uint8_t block13[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block13, 1); }))) {
-    return;
-  }
-  static const uint8_t block14[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block14, 1); }))) {
-    return;
-  }
-  static const uint8_t block15[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block15, 1); }))) {
-    return;
-  }
-  static const uint8_t block16[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block16, 1); }))) {
-    return;
-  }
-  static const uint8_t block17[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block17, 1); }))) {
-    return;
-  }
-  static const uint8_t block18[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block18, 1); }))) {
-    return;
-  }
-  static const uint8_t block19[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block19, 1); }))) {
-    return;
-  }
-  static const uint8_t block20[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block20, 1); }))) {
-    return;
-  }
-  static const uint8_t block21[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block21, 1); }))) {
-    return;
-  }
-  static const uint8_t block22[] = {};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            0, 1, [](uint8_t* p) { memcpy(p, block22, 0); }))) {
-    return;
-  }
-  if (!fuzzer.CompleteSend(1, 2ull, 0)) {
-    return;
-  }
-  static const uint8_t block23[] = {0x02, 0x00};
-  if (!fuzzer.BeginSend(2,
-                        Slice::WithInitializerAndPrefix(
-                            2, 1, [](uint8_t* p) { memcpy(p, block23, 2); }))) {
-    return;
-  }
-  static const uint8_t block24[] = {0x01};
-  if (!fuzzer.BeginSend(2,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block24, 1); }))) {
-    return;
-  }
-  static const uint8_t block25[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block25, 1); }))) {
-    return;
-  }
-  static const uint8_t block26[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block26, 1); }))) {
-    return;
-  }
-  static const uint8_t block27[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block27, 1); }))) {
-    return;
-  }
-  static const uint8_t block28[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block28, 1); }))) {
-    return;
-  }
-  static const uint8_t block29[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block29, 1); }))) {
-    return;
-  }
-  static const uint8_t block30[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block30, 1); }))) {
-    return;
-  }
-  static const uint8_t block31[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block31, 1); }))) {
-    return;
-  }
-  static const uint8_t block32[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block32, 1); }))) {
-    return;
-  }
-  static const uint8_t block33[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block33, 1); }))) {
-    return;
-  }
-  static const uint8_t block34[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block34, 1); }))) {
-    return;
-  }
-  static const uint8_t block35[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block35, 1); }))) {
-    return;
-  }
-  static const uint8_t block36[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block36, 1); }))) {
-    return;
-  }
-  static const uint8_t block37[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block37, 1); }))) {
-    return;
-  }
-  static const uint8_t block38[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block38, 1); }))) {
-    return;
-  }
-  static const uint8_t block39[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block39, 1); }))) {
-    return;
-  }
-  static const uint8_t block40[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block40, 1); }))) {
-    return;
-  }
-  static const uint8_t block41[] = {
-      0xfe, 0xfa, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
-      0x07, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 255, 254, [](uint8_t* p) { memcpy(p, block41, 255); }))) {
-    return;
-  }
-  static const uint8_t block42[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block42, 1); }))) {
-    return;
-  }
-  static const uint8_t block43[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block43, 1); }))) {
-    return;
-  }
-  static const uint8_t block44[] = {0x01};
-  if (!fuzzer.BeginSend(2,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block44, 1); }))) {
-    return;
-  }
-  static const uint8_t block45[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block45, 1); }))) {
-    return;
-  }
-  static const uint8_t block46[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block46, 1); }))) {
-    return;
-  }
-  static const uint8_t block47[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block47, 1); }))) {
-    return;
-  }
-  static const uint8_t block48[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block48, 1); }))) {
-    return;
-  }
-  static const uint8_t block49[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block49, 1); }))) {
-    return;
-  }
-  static const uint8_t block50[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block50, 1); }))) {
-    return;
-  }
-  static const uint8_t block51[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block51, 1); }))) {
-    return;
-  }
-  static const uint8_t block52[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block52, 1); }))) {
-    return;
-  }
-  static const uint8_t block53[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block53, 1); }))) {
-    return;
-  }
-  static const uint8_t block54[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block54, 1); }))) {
-    return;
-  }
-  static const uint8_t block55[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block55, 1); }))) {
-    return;
-  }
-  static const uint8_t block56[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block56, 1); }))) {
-    return;
-  }
-  static const uint8_t block57[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block57, 1); }))) {
-    return;
-  }
-  static const uint8_t block58[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block58, 1); }))) {
-    return;
-  }
-  static const uint8_t block59[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block59, 1); }))) {
-    return;
-  }
-  static const uint8_t block60[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block60, 1); }))) {
-    return;
-  }
-  static const uint8_t block61[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block61, 1); }))) {
-    return;
-  }
-  static const uint8_t block62[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block62, 1); }))) {
-    return;
-  }
-  static const uint8_t block63[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block63, 1); }))) {
-    return;
-  }
-  static const uint8_t block64[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block64, 1); }))) {
-    return;
-  }
-  static const uint8_t block65[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block65, 1); }))) {
-    return;
-  }
-  static const uint8_t block66[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block66, 1); }))) {
-    return;
-  }
-  static const uint8_t block67[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block67, 1); }))) {
-    return;
-  }
-  static const uint8_t block68[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block68, 1); }))) {
-    return;
-  }
-  static const uint8_t block69[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block69, 1); }))) {
-    return;
-  }
-  static const uint8_t block70[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block70, 1); }))) {
-    return;
-  }
-  if (!fuzzer.StepTime(18446744073709551612ull)) {
-    return;
-  }
-  static const uint8_t block71[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block71, 1); }))) {
-    return;
-  }
-  static const uint8_t block72[] = {0x00};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block72, 1); }))) {
-    return;
-  }
-}
-
-// Exposed a bug with too many outstanding sends.
-TEST(PacketProtocolFuzzed, _ffaebbd1370c62dee2d0c6f85553d47a88e6c320) {
-  PacketProtocolFuzzer fuzzer;
-  static const uint8_t block0[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block0, 1); }))) {
-    return;
-  }
-  static const uint8_t block1[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block1, 1); }))) {
-    return;
-  }
-  static const uint8_t block2[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block2, 1); }))) {
-    return;
-  }
-  static const uint8_t block3[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block3, 1); }))) {
-    return;
-  }
-  static const uint8_t block4[] = {
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x28, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x27, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 129, 1, [](uint8_t* p) { memcpy(p, block4, 129); }))) {
-    return;
-  }
-  static const uint8_t block5[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block5, 1); }))) {
-    return;
-  }
-  static const uint8_t block6[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block6, 1); }))) {
-    return;
-  }
-  static const uint8_t block7[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block7, 1); }))) {
-    return;
-  }
-  static const uint8_t block8[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block8, 1); }))) {
-    return;
-  }
-  static const uint8_t block9[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block9, 1); }))) {
-    return;
-  }
-  static const uint8_t block10[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 5, [](uint8_t* p) { memcpy(p, block10, 1); }))) {
-    return;
-  }
-  static const uint8_t block11[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block11, 1); }))) {
-    return;
-  }
-  static const uint8_t block12[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block12, 1); }))) {
-    return;
-  }
-  static const uint8_t block13[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 0, [](uint8_t* p) { memcpy(p, block13, 1); }))) {
-    return;
-  }
-  static const uint8_t block14[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block14, 1); }))) {
-    return;
-  }
-  static const uint8_t block15[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block15, 1); }))) {
-    return;
-  }
-  static const uint8_t block16[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block16, 1); }))) {
-    return;
-  }
-  static const uint8_t block17[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block17, 1); }))) {
-    return;
-  }
-  static const uint8_t block18[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block18, 1); }))) {
-    return;
-  }
-  static const uint8_t block19[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block19, 1); }))) {
-    return;
-  }
-  static const uint8_t block20[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block20, 1); }))) {
-    return;
-  }
-  static const uint8_t block21[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block21, 1); }))) {
-    return;
-  }
-  static const uint8_t block22[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block22, 1); }))) {
-    return;
-  }
-  static const uint8_t block23[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block23, 1); }))) {
-    return;
-  }
-  static const uint8_t block24[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block24, 1); }))) {
-    return;
-  }
-  static const uint8_t block25[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block25, 1); }))) {
-    return;
-  }
-  static const uint8_t block26[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block26, 1); }))) {
-    return;
-  }
-  static const uint8_t block27[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block27, 1); }))) {
-    return;
-  }
-  static const uint8_t block28[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block28, 1); }))) {
-    return;
-  }
-  static const uint8_t block29[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block29, 1); }))) {
-    return;
-  }
-  static const uint8_t block30[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block30, 1); }))) {
-    return;
-  }
-  static const uint8_t block31[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block31, 1); }))) {
-    return;
-  }
-  static const uint8_t block32[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block32, 1); }))) {
-    return;
-  }
-  static const uint8_t block33[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block33, 1); }))) {
-    return;
-  }
-  static const uint8_t block34[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block34, 1); }))) {
-    return;
-  }
-  static const uint8_t block35[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 0, [](uint8_t* p) { memcpy(p, block35, 1); }))) {
-    return;
-  }
-  static const uint8_t block36[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block36, 1); }))) {
-    return;
-  }
-  static const uint8_t block37[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block37, 1); }))) {
-    return;
-  }
-  static const uint8_t block38[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block38, 1); }))) {
-    return;
-  }
-  static const uint8_t block39[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block39, 1); }))) {
-    return;
-  }
-  static const uint8_t block40[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block40, 1); }))) {
-    return;
-  }
-  static const uint8_t block41[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block41, 1); }))) {
-    return;
-  }
-  static const uint8_t block42[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block42, 1); }))) {
-    return;
-  }
-  static const uint8_t block43[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block43, 1); }))) {
-    return;
-  }
-  static const uint8_t block44[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block44, 1); }))) {
-    return;
-  }
-  static const uint8_t block45[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block45, 1); }))) {
-    return;
-  }
-  static const uint8_t block46[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block46, 1); }))) {
-    return;
-  }
-  static const uint8_t block47[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block47, 1); }))) {
-    return;
-  }
-  static const uint8_t block48[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block48, 1); }))) {
-    return;
-  }
-  static const uint8_t block49[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block49, 1); }))) {
-    return;
-  }
-  static const uint8_t block50[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block50, 1); }))) {
-    return;
-  }
-  static const uint8_t block51[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block51, 1); }))) {
-    return;
-  }
-  static const uint8_t block52[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block52, 1); }))) {
-    return;
-  }
-  static const uint8_t block53[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block53, 1); }))) {
-    return;
-  }
-  static const uint8_t block54[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block54, 1); }))) {
-    return;
-  }
-  static const uint8_t block55[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block55, 1); }))) {
-    return;
-  }
-  static const uint8_t block56[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block56, 1); }))) {
-    return;
-  }
-  static const uint8_t block57[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block57, 1); }))) {
-    return;
-  }
-  static const uint8_t block58[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block58, 1); }))) {
-    return;
-  }
-  static const uint8_t block59[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block59, 1); }))) {
-    return;
-  }
-  static const uint8_t block60[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block60, 1); }))) {
-    return;
-  }
-  static const uint8_t block61[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block61, 1); }))) {
-    return;
-  }
-  static const uint8_t block62[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block62, 1); }))) {
-    return;
-  }
-  static const uint8_t block63[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block63, 1); }))) {
-    return;
-  }
-  static const uint8_t block64[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block64, 1); }))) {
-    return;
-  }
-  static const uint8_t block65[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block65, 1); }))) {
-    return;
-  }
-  static const uint8_t block66[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 0, [](uint8_t* p) { memcpy(p, block66, 1); }))) {
-    return;
-  }
-  static const uint8_t block67[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block67, 1); }))) {
-    return;
-  }
-  static const uint8_t block68[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block68, 1); }))) {
-    return;
-  }
-  static const uint8_t block69[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block69, 1); }))) {
-    return;
-  }
-  static const uint8_t block70[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block70, 1); }))) {
-    return;
-  }
-  static const uint8_t block71[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block71, 1); }))) {
-    return;
-  }
-  static const uint8_t block72[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block72, 1); }))) {
-    return;
-  }
-  static const uint8_t block73[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block73, 1); }))) {
-    return;
-  }
-  static const uint8_t block74[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block74, 1); }))) {
-    return;
-  }
-  static const uint8_t block75[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block75, 1); }))) {
-    return;
-  }
-  static const uint8_t block76[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block76, 1); }))) {
-    return;
-  }
-  static const uint8_t block77[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block77, 1); }))) {
-    return;
-  }
-  static const uint8_t block78[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block78, 1); }))) {
-    return;
-  }
-  static const uint8_t block79[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block79, 1); }))) {
-    return;
-  }
-  static const uint8_t block80[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block80, 1); }))) {
-    return;
-  }
-  static const uint8_t block81[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block81, 1); }))) {
-    return;
-  }
-  static const uint8_t block82[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block82, 1); }))) {
-    return;
-  }
-  static const uint8_t block83[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block83, 1); }))) {
-    return;
-  }
-  static const uint8_t block84[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block84, 1); }))) {
-    return;
-  }
-  static const uint8_t block85[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block85, 1); }))) {
-    return;
-  }
-  static const uint8_t block86[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block86, 1); }))) {
-    return;
-  }
-  static const uint8_t block87[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block87, 1); }))) {
-    return;
-  }
-  static const uint8_t block88[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block88, 1); }))) {
-    return;
-  }
-  static const uint8_t block89[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block89, 1); }))) {
-    return;
-  }
-  static const uint8_t block90[] = {0x3a};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block90, 1); }))) {
-    return;
-  }
-  static const uint8_t block91[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block91, 1); }))) {
-    return;
-  }
-  static const uint8_t block92[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block92, 1); }))) {
-    return;
-  }
-  static const uint8_t block93[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block93, 1); }))) {
-    return;
-  }
-  static const uint8_t block94[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block94, 1); }))) {
-    return;
-  }
-  static const uint8_t block95[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block95, 1); }))) {
-    return;
-  }
-  static const uint8_t block96[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block96, 1); }))) {
-    return;
-  }
-  static const uint8_t block97[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block97, 1); }))) {
-    return;
-  }
-  static const uint8_t block98[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block98, 1); }))) {
-    return;
-  }
-  static const uint8_t block99[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block99, 1); }))) {
-    return;
-  }
-  static const uint8_t block100[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block100, 1); }))) {
-    return;
-  }
-  static const uint8_t block101[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block101, 1); }))) {
-    return;
-  }
-  static const uint8_t block102[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block102, 1); }))) {
-    return;
-  }
-  static const uint8_t block103[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block103, 1); }))) {
-    return;
-  }
-  static const uint8_t block104[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block104, 1); }))) {
-    return;
-  }
-  static const uint8_t block105[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block105, 1); }))) {
-    return;
-  }
-  static const uint8_t block106[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block106, 1); }))) {
-    return;
-  }
-  static const uint8_t block107[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block107, 1); }))) {
-    return;
-  }
-  static const uint8_t block108[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block108, 1); }))) {
-    return;
-  }
-  static const uint8_t block109[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block109, 1); }))) {
-    return;
-  }
-  static const uint8_t block110[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block110, 1); }))) {
-    return;
-  }
-  static const uint8_t block111[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block111, 1); }))) {
-    return;
-  }
-  static const uint8_t block112[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block112, 1); }))) {
-    return;
-  }
-  static const uint8_t block113[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block113, 1); }))) {
-    return;
-  }
-  static const uint8_t block114[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block114, 1); }))) {
-    return;
-  }
-  static const uint8_t block115[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block115, 1); }))) {
-    return;
-  }
-  static const uint8_t block116[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block116, 1); }))) {
-    return;
-  }
-  static const uint8_t block117[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block117, 1); }))) {
-    return;
-  }
-  static const uint8_t block118[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block118, 1); }))) {
-    return;
-  }
-  static const uint8_t block119[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block119, 1); }))) {
-    return;
-  }
-  static const uint8_t block120[] = {};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 0, 1, [](uint8_t* p) { memcpy(p, block120, 0); }))) {
-    return;
-  }
-  static const uint8_t block121[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block121, 1); }))) {
-    return;
-  }
-  static const uint8_t block122[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block122, 1); }))) {
-    return;
-  }
-  static const uint8_t block123[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block123, 1); }))) {
-    return;
-  }
-  static const uint8_t block124[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block124, 1); }))) {
-    return;
-  }
-  static const uint8_t block125[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block125, 1); }))) {
-    return;
-  }
-  static const uint8_t block126[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block126, 1); }))) {
-    return;
-  }
-  static const uint8_t block127[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block127, 1); }))) {
-    return;
-  }
-  static const uint8_t block128[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block128, 1); }))) {
-    return;
-  }
-  static const uint8_t block129[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block129, 1); }))) {
-    return;
-  }
-  static const uint8_t block130[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block130, 1); }))) {
-    return;
-  }
-  static const uint8_t block131[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block131, 1); }))) {
-    return;
-  }
-  static const uint8_t block132[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block132, 1); }))) {
-    return;
-  }
-  static const uint8_t block133[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block133, 1); }))) {
-    return;
-  }
-  static const uint8_t block134[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block134, 1); }))) {
-    return;
-  }
-  static const uint8_t block135[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block135, 1); }))) {
-    return;
-  }
-  static const uint8_t block136[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block136, 1); }))) {
-    return;
-  }
-  static const uint8_t block137[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block137, 1); }))) {
-    return;
-  }
-  static const uint8_t block138[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block138, 1); }))) {
-    return;
-  }
-  static const uint8_t block139[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block139, 1); }))) {
-    return;
-  }
-  static const uint8_t block140[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block140, 1); }))) {
-    return;
-  }
-  static const uint8_t block141[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block141, 1); }))) {
-    return;
-  }
-  static const uint8_t block142[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block142, 1); }))) {
-    return;
-  }
-  static const uint8_t block143[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block143, 1); }))) {
-    return;
-  }
-  static const uint8_t block144[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block144, 1); }))) {
-    return;
-  }
-  static const uint8_t block145[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block145, 1); }))) {
-    return;
-  }
-  static const uint8_t block146[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block146, 1); }))) {
-    return;
-  }
-  static const uint8_t block147[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block147, 1); }))) {
-    return;
-  }
-  static const uint8_t block148[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block148, 1); }))) {
-    return;
-  }
-  static const uint8_t block149[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block149, 1); }))) {
-    return;
-  }
-  static const uint8_t block150[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block150, 1); }))) {
-    return;
-  }
-  static const uint8_t block151[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 0, [](uint8_t* p) { memcpy(p, block151, 1); }))) {
-    return;
-  }
-  static const uint8_t block152[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block152, 1); }))) {
-    return;
-  }
-  static const uint8_t block153[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block153, 1); }))) {
-    return;
-  }
-  static const uint8_t block154[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block154, 1); }))) {
-    return;
-  }
-  static const uint8_t block155[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block155, 1); }))) {
-    return;
-  }
-  static const uint8_t block156[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block156, 1); }))) {
-    return;
-  }
-  static const uint8_t block157[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block157, 1); }))) {
-    return;
-  }
-  static const uint8_t block158[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block158, 1); }))) {
-    return;
-  }
-  static const uint8_t block159[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block159, 1); }))) {
-    return;
-  }
-  static const uint8_t block160[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block160, 1); }))) {
-    return;
-  }
-  static const uint8_t block161[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block161, 1); }))) {
-    return;
-  }
-  static const uint8_t block162[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block162, 1); }))) {
-    return;
-  }
-  static const uint8_t block163[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block163, 1); }))) {
-    return;
-  }
-  static const uint8_t block164[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block164, 1); }))) {
-    return;
-  }
-  static const uint8_t block165[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block165, 1); }))) {
-    return;
-  }
-  static const uint8_t block166[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block166, 1); }))) {
-    return;
-  }
-  static const uint8_t block167[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block167, 1); }))) {
-    return;
-  }
-  static const uint8_t block168[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block168, 1); }))) {
-    return;
-  }
-  static const uint8_t block169[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block169, 1); }))) {
-    return;
-  }
-  static const uint8_t block170[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block170, 1); }))) {
-    return;
-  }
-  static const uint8_t block171[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block171, 1); }))) {
-    return;
-  }
-  static const uint8_t block172[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block172, 1); }))) {
-    return;
-  }
-  static const uint8_t block173[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block173, 1); }))) {
-    return;
-  }
-  static const uint8_t block174[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block174, 1); }))) {
-    return;
-  }
-  static const uint8_t block175[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block175, 1); }))) {
-    return;
-  }
-  static const uint8_t block176[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block176, 1); }))) {
-    return;
-  }
-  static const uint8_t block177[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block177, 1); }))) {
-    return;
-  }
-  static const uint8_t block178[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block178, 1); }))) {
-    return;
-  }
-  static const uint8_t block179[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block179, 1); }))) {
-    return;
-  }
-  static const uint8_t block180[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block180, 1); }))) {
-    return;
-  }
-  static const uint8_t block181[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block181, 1); }))) {
-    return;
-  }
-  static const uint8_t block182[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block182, 1); }))) {
-    return;
-  }
-  static const uint8_t block183[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block183, 1); }))) {
-    return;
-  }
-  static const uint8_t block184[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block184, 1); }))) {
-    return;
-  }
-  static const uint8_t block185[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block185, 1); }))) {
-    return;
-  }
-  static const uint8_t block186[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block186, 1); }))) {
-    return;
-  }
-  static const uint8_t block187[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block187, 1); }))) {
-    return;
-  }
-  static const uint8_t block188[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block188, 1); }))) {
-    return;
-  }
-  static const uint8_t block189[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block189, 1); }))) {
-    return;
-  }
-  static const uint8_t block190[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block190, 1); }))) {
-    return;
-  }
-  static const uint8_t block191[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block191, 1); }))) {
-    return;
-  }
-  static const uint8_t block192[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block192, 1); }))) {
-    return;
-  }
-  static const uint8_t block193[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block193, 1); }))) {
-    return;
-  }
-  static const uint8_t block194[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block194, 1); }))) {
-    return;
-  }
-  static const uint8_t block195[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block195, 1); }))) {
-    return;
-  }
-  static const uint8_t block196[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block196, 1); }))) {
-    return;
-  }
-  static const uint8_t block197[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block197, 1); }))) {
-    return;
-  }
-  static const uint8_t block198[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block198, 1); }))) {
-    return;
-  }
-  static const uint8_t block199[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block199, 1); }))) {
-    return;
-  }
-  static const uint8_t block200[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block200, 1); }))) {
-    return;
-  }
-  static const uint8_t block201[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block201, 1); }))) {
-    return;
-  }
-  static const uint8_t block202[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block202, 1); }))) {
-    return;
-  }
-  static const uint8_t block203[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block203, 1); }))) {
-    return;
-  }
-  static const uint8_t block204[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block204, 1); }))) {
-    return;
-  }
-  static const uint8_t block205[] = {0x00};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block205, 1); }))) {
-    return;
-  }
-  static const uint8_t block206[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block206, 1); }))) {
-    return;
-  }
-  static const uint8_t block207[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block207, 1); }))) {
-    return;
-  }
-  static const uint8_t block208[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block208, 1); }))) {
-    return;
-  }
-  static const uint8_t block209[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block209, 1); }))) {
-    return;
-  }
-  static const uint8_t block210[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block210, 1); }))) {
-    return;
-  }
-  static const uint8_t block211[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block211, 1); }))) {
-    return;
-  }
-  static const uint8_t block212[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block212, 1); }))) {
-    return;
-  }
-  static const uint8_t block213[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block213, 1); }))) {
-    return;
-  }
-  static const uint8_t block214[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block214, 1); }))) {
-    return;
-  }
-  static const uint8_t block215[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block215, 1); }))) {
-    return;
-  }
-  static const uint8_t block216[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block216, 1); }))) {
-    return;
-  }
-  static const uint8_t block217[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block217, 1); }))) {
-    return;
-  }
-  static const uint8_t block218[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block218, 1); }))) {
-    return;
-  }
-  static const uint8_t block219[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block219, 1); }))) {
-    return;
-  }
-  static const uint8_t block220[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block220, 1); }))) {
-    return;
-  }
-  static const uint8_t block221[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block221, 1); }))) {
-    return;
-  }
-  static const uint8_t block222[] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-                                     0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-                                     0x01, 0x01, 0x01, 0x01, 0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 17, 1, [](uint8_t* p) { memcpy(p, block222, 17); }))) {
-    return;
-  }
-  static const uint8_t block223[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block223, 1); }))) {
-    return;
-  }
-  static const uint8_t block224[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block224, 1); }))) {
-    return;
-  }
-  static const uint8_t block225[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block225, 1); }))) {
-    return;
-  }
-  static const uint8_t block226[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block226, 1); }))) {
-    return;
-  }
-  static const uint8_t block227[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block227, 1); }))) {
-    return;
-  }
-  static const uint8_t block228[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block228, 1); }))) {
-    return;
-  }
-  static const uint8_t block229[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block229, 1); }))) {
-    return;
-  }
-  static const uint8_t block230[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block230, 1); }))) {
-    return;
-  }
-  static const uint8_t block231[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block231, 1); }))) {
-    return;
-  }
-  static const uint8_t block232[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block232, 1); }))) {
-    return;
-  }
-  static const uint8_t block233[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block233, 1); }))) {
-    return;
-  }
-  static const uint8_t block234[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block234, 1); }))) {
-    return;
-  }
-  static const uint8_t block235[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block235, 1); }))) {
-    return;
-  }
-  static const uint8_t block236[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block236, 1); }))) {
-    return;
-  }
-  static const uint8_t block237[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block237, 1); }))) {
-    return;
-  }
-  static const uint8_t block238[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block238, 1); }))) {
-    return;
-  }
-  static const uint8_t block239[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block239, 1); }))) {
-    return;
-  }
-  static const uint8_t block240[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block240, 1); }))) {
-    return;
-  }
-  static const uint8_t block241[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block241, 1); }))) {
-    return;
-  }
-  static const uint8_t block242[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block242, 1); }))) {
-    return;
-  }
-  static const uint8_t block243[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block243, 1); }))) {
-    return;
-  }
-  static const uint8_t block244[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block244, 1); }))) {
-    return;
-  }
-  static const uint8_t block245[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block245, 1); }))) {
-    return;
-  }
-  static const uint8_t block246[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block246, 1); }))) {
-    return;
-  }
-  static const uint8_t block247[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block247, 1); }))) {
-    return;
-  }
-  static const uint8_t block248[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block248, 1); }))) {
-    return;
-  }
-  static const uint8_t block249[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block249, 1); }))) {
-    return;
-  }
-  static const uint8_t block250[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block250, 1); }))) {
-    return;
-  }
-  static const uint8_t block251[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block251, 1); }))) {
-    return;
-  }
-  static const uint8_t block252[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block252, 1); }))) {
-    return;
-  }
-  static const uint8_t block253[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block253, 1); }))) {
-    return;
-  }
-  static const uint8_t block254[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block254, 1); }))) {
-    return;
-  }
-  static const uint8_t block255[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block255, 1); }))) {
-    return;
-  }
-  static const uint8_t block256[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block256, 1); }))) {
-    return;
-  }
-  static const uint8_t block257[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block257, 1); }))) {
-    return;
-  }
-  static const uint8_t block258[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block258, 1); }))) {
-    return;
-  }
-  static const uint8_t block259[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block259, 1); }))) {
-    return;
-  }
-  static const uint8_t block260[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block260, 1); }))) {
-    return;
-  }
-  static const uint8_t block261[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block261, 1); }))) {
-    return;
-  }
-  static const uint8_t block262[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block262, 1); }))) {
-    return;
-  }
-  static const uint8_t block263[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block263, 1); }))) {
-    return;
-  }
-  static const uint8_t block264[] = {};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 0, 1, [](uint8_t* p) { memcpy(p, block264, 0); }))) {
-    return;
-  }
-  static const uint8_t block265[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block265, 1); }))) {
-    return;
-  }
-  static const uint8_t block266[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block266, 1); }))) {
-    return;
-  }
-  static const uint8_t block267[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block267, 1); }))) {
-    return;
-  }
-  static const uint8_t block268[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block268, 1); }))) {
-    return;
-  }
-  static const uint8_t block269[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block269, 1); }))) {
-    return;
-  }
-  static const uint8_t block270[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block270, 1); }))) {
-    return;
-  }
-  static const uint8_t block271[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block271, 1); }))) {
-    return;
-  }
-  static const uint8_t block272[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block272, 1); }))) {
-    return;
-  }
-  static const uint8_t block273[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block273, 1); }))) {
-    return;
-  }
-  static const uint8_t block274[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block274, 1); }))) {
-    return;
-  }
-  static const uint8_t block275[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block275, 1); }))) {
-    return;
-  }
-  static const uint8_t block276[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block276, 1); }))) {
-    return;
-  }
-  static const uint8_t block277[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block277, 1); }))) {
-    return;
-  }
-  static const uint8_t block278[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block278, 1); }))) {
-    return;
-  }
-  static const uint8_t block279[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block279, 1); }))) {
-    return;
-  }
-  static const uint8_t block280[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block280, 1); }))) {
-    return;
-  }
-  static const uint8_t block281[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block281, 1); }))) {
-    return;
-  }
-  static const uint8_t block282[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block282, 1); }))) {
-    return;
-  }
-  static const uint8_t block283[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block283, 1); }))) {
-    return;
-  }
-  static const uint8_t block284[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block284, 1); }))) {
-    return;
-  }
-  static const uint8_t block285[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block285, 1); }))) {
-    return;
-  }
-  static const uint8_t block286[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block286, 1); }))) {
-    return;
-  }
-  static const uint8_t block287[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block287, 1); }))) {
-    return;
-  }
-  static const uint8_t block288[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block288, 1); }))) {
-    return;
-  }
-  static const uint8_t block289[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block289, 1); }))) {
-    return;
-  }
-  static const uint8_t block290[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block290, 1); }))) {
-    return;
-  }
-  static const uint8_t block291[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block291, 1); }))) {
-    return;
-  }
-  static const uint8_t block292[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block292, 1); }))) {
-    return;
-  }
-  static const uint8_t block293[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block293, 1); }))) {
-    return;
-  }
-  static const uint8_t block294[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block294, 1); }))) {
-    return;
-  }
-  static const uint8_t block295[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block295, 1); }))) {
-    return;
-  }
-  static const uint8_t block296[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block296, 1); }))) {
-    return;
-  }
-  static const uint8_t block297[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block297, 1); }))) {
-    return;
-  }
-  static const uint8_t block298[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block298, 1); }))) {
-    return;
-  }
-  static const uint8_t block299[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block299, 1); }))) {
-    return;
-  }
-  static const uint8_t block300[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block300, 1); }))) {
-    return;
-  }
-  static const uint8_t block301[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block301, 1); }))) {
-    return;
-  }
-  static const uint8_t block302[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block302, 1); }))) {
-    return;
-  }
-  static const uint8_t block303[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block303, 1); }))) {
-    return;
-  }
-  static const uint8_t block304[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block304, 1); }))) {
-    return;
-  }
-  static const uint8_t block305[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 0, [](uint8_t* p) { memcpy(p, block305, 1); }))) {
-    return;
-  }
-  static const uint8_t block306[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block306, 1); }))) {
-    return;
-  }
-  static const uint8_t block307[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block307, 1); }))) {
-    return;
-  }
-  static const uint8_t block308[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block308, 1); }))) {
-    return;
-  }
-  static const uint8_t block309[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block309, 1); }))) {
-    return;
-  }
-  static const uint8_t block310[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block310, 1); }))) {
-    return;
-  }
-  static const uint8_t block311[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block311, 1); }))) {
-    return;
-  }
-  static const uint8_t block312[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block312, 1); }))) {
-    return;
-  }
-  static const uint8_t block313[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block313, 1); }))) {
-    return;
-  }
-  static const uint8_t block314[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block314, 1); }))) {
-    return;
-  }
-  static const uint8_t block315[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block315, 1); }))) {
-    return;
-  }
-  static const uint8_t block316[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block316, 1); }))) {
-    return;
-  }
-  static const uint8_t block317[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block317, 1); }))) {
-    return;
-  }
-  static const uint8_t block318[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block318, 1); }))) {
-    return;
-  }
-  static const uint8_t block319[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block319, 1); }))) {
-    return;
-  }
-  static const uint8_t block320[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block320, 1); }))) {
-    return;
-  }
-  static const uint8_t block321[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block321, 1); }))) {
-    return;
-  }
-  static const uint8_t block322[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block322, 1); }))) {
-    return;
-  }
-  static const uint8_t block323[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block323, 1); }))) {
-    return;
-  }
-  static const uint8_t block324[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block324, 1); }))) {
-    return;
-  }
-  static const uint8_t block325[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block325, 1); }))) {
-    return;
-  }
-  static const uint8_t block326[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block326, 1); }))) {
-    return;
-  }
-  static const uint8_t block327[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block327, 1); }))) {
-    return;
-  }
-  static const uint8_t block328[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block328, 1); }))) {
-    return;
-  }
-  static const uint8_t block329[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block329, 1); }))) {
-    return;
-  }
-  static const uint8_t block330[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block330, 1); }))) {
-    return;
-  }
-  static const uint8_t block331[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block331, 1); }))) {
-    return;
-  }
-  static const uint8_t block332[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block332, 1); }))) {
-    return;
-  }
-  static const uint8_t block333[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block333, 1); }))) {
-    return;
-  }
-  static const uint8_t block334[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block334, 1); }))) {
-    return;
-  }
-  static const uint8_t block335[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block335, 1); }))) {
-    return;
-  }
-  static const uint8_t block336[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block336, 1); }))) {
-    return;
-  }
-  static const uint8_t block337[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block337, 1); }))) {
-    return;
-  }
-  static const uint8_t block338[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block338, 1); }))) {
-    return;
-  }
-  static const uint8_t block339[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block339, 1); }))) {
-    return;
-  }
-  static const uint8_t block340[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block340, 1); }))) {
-    return;
-  }
-  static const uint8_t block341[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block341, 1); }))) {
-    return;
-  }
-  static const uint8_t block342[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block342, 1); }))) {
-    return;
-  }
-  static const uint8_t block343[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block343, 1); }))) {
-    return;
-  }
-  static const uint8_t block344[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block344, 1); }))) {
-    return;
-  }
-  static const uint8_t block345[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block345, 1); }))) {
-    return;
-  }
-  static const uint8_t block346[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block346, 1); }))) {
-    return;
-  }
-  static const uint8_t block347[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block347, 1); }))) {
-    return;
-  }
-  static const uint8_t block348[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block348, 1); }))) {
-    return;
-  }
-  static const uint8_t block349[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block349, 1); }))) {
-    return;
-  }
-  static const uint8_t block350[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block350, 1); }))) {
-    return;
-  }
-  static const uint8_t block351[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block351, 1); }))) {
-    return;
-  }
-  static const uint8_t block352[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block352, 1); }))) {
-    return;
-  }
-  static const uint8_t block353[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block353, 1); }))) {
-    return;
-  }
-  static const uint8_t block354[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block354, 1); }))) {
-    return;
-  }
-  static const uint8_t block355[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block355, 1); }))) {
-    return;
-  }
-  static const uint8_t block356[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block356, 1); }))) {
-    return;
-  }
-  static const uint8_t block357[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block357, 1); }))) {
-    return;
-  }
-  static const uint8_t block358[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block358, 1); }))) {
-    return;
-  }
-  static const uint8_t block359[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block359, 1); }))) {
-    return;
-  }
-  static const uint8_t block360[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block360, 1); }))) {
-    return;
-  }
-  static const uint8_t block361[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block361, 1); }))) {
-    return;
-  }
-  static const uint8_t block362[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block362, 1); }))) {
-    return;
-  }
-  static const uint8_t block363[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block363, 1); }))) {
-    return;
-  }
-  static const uint8_t block364[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block364, 1); }))) {
-    return;
-  }
-  static const uint8_t block365[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block365, 1); }))) {
-    return;
-  }
-  static const uint8_t block366[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block366, 1); }))) {
-    return;
-  }
-  static const uint8_t block367[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block367, 1); }))) {
-    return;
-  }
-  static const uint8_t block368[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block368, 1); }))) {
-    return;
-  }
-  static const uint8_t block369[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block369, 1); }))) {
-    return;
-  }
-  static const uint8_t block370[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block370, 1); }))) {
-    return;
-  }
-  static const uint8_t block371[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block371, 1); }))) {
-    return;
-  }
-  static const uint8_t block372[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block372, 1); }))) {
-    return;
-  }
-  static const uint8_t block373[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block373, 1); }))) {
-    return;
-  }
-  static const uint8_t block374[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block374, 1); }))) {
-    return;
-  }
-  static const uint8_t block375[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block375, 1); }))) {
-    return;
-  }
-  static const uint8_t block376[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block376, 1); }))) {
-    return;
-  }
-  static const uint8_t block377[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block377, 1); }))) {
-    return;
-  }
-  static const uint8_t block378[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block378, 1); }))) {
-    return;
-  }
-  static const uint8_t block379[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block379, 1); }))) {
-    return;
-  }
-  static const uint8_t block380[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block380, 1); }))) {
-    return;
-  }
-  static const uint8_t block381[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block381, 1); }))) {
-    return;
-  }
-  static const uint8_t block382[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block382, 1); }))) {
-    return;
-  }
-  static const uint8_t block383[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block383, 1); }))) {
-    return;
-  }
-  static const uint8_t block384[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block384, 1); }))) {
-    return;
-  }
-  static const uint8_t block385[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block385, 1); }))) {
-    return;
-  }
-  static const uint8_t block386[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block386, 1); }))) {
-    return;
-  }
-  static const uint8_t block387[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block387, 1); }))) {
-    return;
-  }
-  static const uint8_t block388[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block388, 1); }))) {
-    return;
-  }
-  static const uint8_t block389[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block389, 1); }))) {
-    return;
-  }
-  static const uint8_t block390[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block390, 1); }))) {
-    return;
-  }
-  static const uint8_t block391[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block391, 1); }))) {
-    return;
-  }
-  static const uint8_t block392[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block392, 1); }))) {
-    return;
-  }
-  static const uint8_t block393[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block393, 1); }))) {
-    return;
-  }
-  static const uint8_t block394[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block394, 1); }))) {
-    return;
-  }
-  static const uint8_t block395[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block395, 1); }))) {
-    return;
-  }
-  static const uint8_t block396[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block396, 1); }))) {
-    return;
-  }
-  static const uint8_t block397[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block397, 1); }))) {
-    return;
-  }
-  static const uint8_t block398[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block398, 1); }))) {
-    return;
-  }
-  static const uint8_t block399[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block399, 1); }))) {
-    return;
-  }
-  static const uint8_t block400[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block400, 1); }))) {
-    return;
-  }
-  static const uint8_t block401[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block401, 1); }))) {
-    return;
-  }
-  static const uint8_t block402[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block402, 1); }))) {
-    return;
-  }
-  static const uint8_t block403[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block403, 1); }))) {
-    return;
-  }
-  static const uint8_t block404[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block404, 1); }))) {
-    return;
-  }
-  static const uint8_t block405[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block405, 1); }))) {
-    return;
-  }
-  static const uint8_t block406[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block406, 1); }))) {
-    return;
-  }
-  static const uint8_t block407[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block407, 1); }))) {
-    return;
-  }
-  static const uint8_t block408[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block408, 1); }))) {
-    return;
-  }
-  static const uint8_t block409[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block409, 1); }))) {
-    return;
-  }
-  static const uint8_t block410[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block410, 1); }))) {
-    return;
-  }
-  static const uint8_t block411[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 0, [](uint8_t* p) { memcpy(p, block411, 1); }))) {
-    return;
-  }
-  static const uint8_t block412[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block412, 1); }))) {
-    return;
-  }
-  static const uint8_t block413[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block413, 1); }))) {
-    return;
-  }
-  static const uint8_t block414[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block414, 1); }))) {
-    return;
-  }
-  static const uint8_t block415[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block415, 1); }))) {
-    return;
-  }
-  static const uint8_t block416[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block416, 1); }))) {
-    return;
-  }
-  static const uint8_t block417[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block417, 1); }))) {
-    return;
-  }
-  static const uint8_t block418[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block418, 1); }))) {
-    return;
-  }
-  static const uint8_t block419[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block419, 1); }))) {
-    return;
-  }
-  static const uint8_t block420[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block420, 1); }))) {
-    return;
-  }
-  static const uint8_t block421[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block421, 1); }))) {
-    return;
-  }
-  static const uint8_t block422[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block422, 1); }))) {
-    return;
-  }
-  static const uint8_t block423[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block423, 1); }))) {
-    return;
-  }
-  static const uint8_t block424[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block424, 1); }))) {
-    return;
-  }
-  static const uint8_t block425[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block425, 1); }))) {
-    return;
-  }
-  static const uint8_t block426[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block426, 1); }))) {
-    return;
-  }
-  static const uint8_t block427[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block427, 1); }))) {
-    return;
-  }
-  static const uint8_t block428[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block428, 1); }))) {
-    return;
-  }
-  static const uint8_t block429[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block429, 1); }))) {
-    return;
-  }
-  static const uint8_t block430[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block430, 1); }))) {
-    return;
-  }
-  static const uint8_t block431[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block431, 1); }))) {
-    return;
-  }
-  static const uint8_t block432[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block432, 1); }))) {
-    return;
-  }
-  static const uint8_t block433[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block433, 1); }))) {
-    return;
-  }
-  static const uint8_t block434[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block434, 1); }))) {
-    return;
-  }
-  static const uint8_t block435[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block435, 1); }))) {
-    return;
-  }
-  static const uint8_t block436[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block436, 1); }))) {
-    return;
-  }
-  static const uint8_t block437[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block437, 1); }))) {
-    return;
-  }
-  static const uint8_t block438[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block438, 1); }))) {
-    return;
-  }
-  static const uint8_t block439[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block439, 1); }))) {
-    return;
-  }
-  static const uint8_t block440[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block440, 1); }))) {
-    return;
-  }
-  static const uint8_t block441[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block441, 1); }))) {
-    return;
-  }
-  static const uint8_t block442[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block442, 1); }))) {
-    return;
-  }
-  static const uint8_t block443[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block443, 1); }))) {
-    return;
-  }
-  static const uint8_t block444[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block444, 1); }))) {
-    return;
-  }
-  static const uint8_t block445[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block445, 1); }))) {
-    return;
-  }
-  static const uint8_t block446[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block446, 1); }))) {
-    return;
-  }
-  static const uint8_t block447[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block447, 1); }))) {
-    return;
-  }
-  static const uint8_t block448[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block448, 1); }))) {
-    return;
-  }
-  static const uint8_t block449[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block449, 1); }))) {
-    return;
-  }
-  static const uint8_t block450[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block450, 1); }))) {
-    return;
-  }
-  static const uint8_t block451[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block451, 1); }))) {
-    return;
-  }
-  static const uint8_t block452[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block452, 1); }))) {
-    return;
-  }
-  static const uint8_t block453[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block453, 1); }))) {
-    return;
-  }
-  static const uint8_t block454[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block454, 1); }))) {
-    return;
-  }
-  static const uint8_t block455[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block455, 1); }))) {
-    return;
-  }
-  static const uint8_t block456[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block456, 1); }))) {
-    return;
-  }
-  static const uint8_t block457[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block457, 1); }))) {
-    return;
-  }
-  static const uint8_t block458[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block458, 1); }))) {
-    return;
-  }
-  static const uint8_t block459[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block459, 1); }))) {
-    return;
-  }
-  static const uint8_t block460[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block460, 1); }))) {
-    return;
-  }
-  static const uint8_t block461[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block461, 1); }))) {
-    return;
-  }
-  static const uint8_t block462[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block462, 1); }))) {
-    return;
-  }
-  static const uint8_t block463[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block463, 1); }))) {
-    return;
-  }
-  static const uint8_t block464[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block464, 1); }))) {
-    return;
-  }
-  static const uint8_t block465[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block465, 1); }))) {
-    return;
-  }
-  static const uint8_t block466[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block466, 1); }))) {
-    return;
-  }
-  static const uint8_t block467[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block467, 1); }))) {
-    return;
-  }
-  static const uint8_t block468[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block468, 1); }))) {
-    return;
-  }
-  static const uint8_t block469[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block469, 1); }))) {
-    return;
-  }
-  static const uint8_t block470[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block470, 1); }))) {
-    return;
-  }
-  static const uint8_t block471[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block471, 1); }))) {
-    return;
-  }
-  static const uint8_t block472[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block472, 1); }))) {
-    return;
-  }
-  static const uint8_t block473[] = {};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 0, 1, [](uint8_t* p) { memcpy(p, block473, 0); }))) {
-    return;
-  }
-  static const uint8_t block474[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block474, 1); }))) {
-    return;
-  }
-  static const uint8_t block475[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block475, 1); }))) {
-    return;
-  }
-  static const uint8_t block476[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block476, 1); }))) {
-    return;
-  }
-  static const uint8_t block477[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block477, 1); }))) {
-    return;
-  }
-  static const uint8_t block478[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block478, 1); }))) {
-    return;
-  }
-  static const uint8_t block479[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block479, 1); }))) {
-    return;
-  }
-  static const uint8_t block480[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block480, 1); }))) {
-    return;
-  }
-  static const uint8_t block481[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block481, 1); }))) {
-    return;
-  }
-  static const uint8_t block482[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block482, 1); }))) {
-    return;
-  }
-  static const uint8_t block483[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block483, 1); }))) {
-    return;
-  }
-  static const uint8_t block484[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block484, 1); }))) {
-    return;
-  }
-  static const uint8_t block485[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block485, 1); }))) {
-    return;
-  }
-  static const uint8_t block486[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block486, 1); }))) {
-    return;
-  }
-  static const uint8_t block487[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block487, 1); }))) {
-    return;
-  }
-  static const uint8_t block488[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block488, 1); }))) {
-    return;
-  }
-  static const uint8_t block489[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block489, 1); }))) {
-    return;
-  }
-  static const uint8_t block490[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block490, 1); }))) {
-    return;
-  }
-  static const uint8_t block491[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block491, 1); }))) {
-    return;
-  }
-  static const uint8_t block492[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block492, 1); }))) {
-    return;
-  }
-  static const uint8_t block493[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block493, 1); }))) {
-    return;
-  }
-  static const uint8_t block494[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block494, 1); }))) {
-    return;
-  }
-  static const uint8_t block495[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block495, 1); }))) {
-    return;
-  }
-  static const uint8_t block496[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block496, 1); }))) {
-    return;
-  }
-  static const uint8_t block497[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block497, 1); }))) {
-    return;
-  }
-  static const uint8_t block498[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block498, 1); }))) {
-    return;
-  }
-  static const uint8_t block499[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block499, 1); }))) {
-    return;
-  }
-  static const uint8_t block500[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block500, 1); }))) {
-    return;
-  }
-  static const uint8_t block501[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block501, 1); }))) {
-    return;
-  }
-  static const uint8_t block502[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block502, 1); }))) {
-    return;
-  }
-  static const uint8_t block503[] = {0x28};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block503, 1); }))) {
-    return;
-  }
-  static const uint8_t block504[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block504, 1); }))) {
-    return;
-  }
-  static const uint8_t block505[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block505, 1); }))) {
-    return;
-  }
-  static const uint8_t block506[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block506, 1); }))) {
-    return;
-  }
-  static const uint8_t block507[] = {0x01};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 1, 1, [](uint8_t* p) { memcpy(p, block507, 1); }))) {
-    return;
-  }
-  static const uint8_t block508[] = {0x01, 0x02};
-  if (!fuzzer.BeginSend(
-          1, Slice::WithInitializerAndPrefix(
-                 2, 0, [](uint8_t* p) { memcpy(p, block508, 2); }))) {
-    return;
-  }
-  if (!fuzzer.CompleteSend(1, 346ull, 0)) {
-    return;
-  }
-  // Snipped... irrelevant to reproduction.
-}
-
-// Found a bug in the fuzzer around nack handling.
-TEST(PacketProtocolFuzzed, _9bfa77589cb379397dafc6661fee887af34c03de) {
-  PacketProtocolFuzzer fuzzer;
-  static const uint8_t block0[] = {0x01};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block0, 1); }))) {
-    return;
-  }
-  static const uint8_t block1[] = {};
-  if (!fuzzer.BeginSend(1,
-                        Slice::WithInitializerAndPrefix(
-                            0, 2, [](uint8_t* p) { memcpy(p, block1, 0); }))) {
-    return;
-  }
-  if (!fuzzer.CompleteSend(1, 1ull, 0)) {
-    return;
-  }
-  static const uint8_t block2[] = {0x00};
-  if (!fuzzer.BeginSend(2,
-                        Slice::WithInitializerAndPrefix(
-                            1, 1, [](uint8_t* p) { memcpy(p, block2, 1); }))) {
-    return;
-  }
-  if (!fuzzer.CompleteSend(2, 0ull, 0)) {
-    return;
-  }
-}
-
-}  // namespace packet_protocol_test
-}  // namespace overnet
diff --git a/lib/overnet/protocol/BUILD.gn b/lib/overnet/protocol/BUILD.gn
new file mode 100644
index 0000000..045b1f4
--- /dev/null
+++ b/lib/overnet/protocol/BUILD.gn
@@ -0,0 +1,153 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+###############################################################################
+# AGGREGATE LIBRARIES
+
+source_set("lib") {
+}
+
+source_set("tests") {
+  testonly = true
+  deps = [
+    ":ack_frame_test",
+    ":fork_frame_test",
+    ":introduction_test",
+    ":routable_message_test",
+    ":varint_test",
+  ]
+}
+
+###############################################################################
+
+# ack_frame
+source_set("ack_frame") {
+  sources = [
+    "ack_frame.cc",
+    "ack_frame.h",
+  ]
+  deps = [
+    ":varint",
+    "//garnet/lib/overnet/environment:trace",
+    "//garnet/lib/overnet/vocabulary:slice",
+    "//garnet/lib/overnet/vocabulary:status",
+  ]
+}
+
+source_set("ack_frame_test") {
+  testonly = true
+  sources = [
+    "ack_frame_test.cc",
+  ]
+  deps = [
+    ":ack_frame",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# fork_frame
+# TODO(ctiller): switch to FIDL
+source_set("fork_frame") {
+  sources = [
+    "fork_frame.cc",
+    "fork_frame.h",
+  ]
+  deps = [
+    ":introduction",  # TODO(ctiller): remove once we switch to FIDL
+    "//garnet/lib/overnet/labels:reliability_and_ordering",
+    "//garnet/lib/overnet/labels:stream_id",
+    "//garnet/lib/overnet/vocabulary:slice",
+    "//garnet/lib/overnet/vocabulary:status",
+  ]
+}
+
+source_set("fork_frame_test") {
+  testonly = true
+  sources = [
+    "fork_frame_test.cc",
+  ]
+  deps = [
+    ":fork_frame",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# introduction
+# TODO(ctiller): switch to FIDL
+source_set("introduction") {
+  sources = [
+    "introduction.h",
+  ]
+  deps = [
+    ":varint",
+    "//garnet/lib/overnet/vocabulary:optional",
+    "//garnet/lib/overnet/vocabulary:slice",
+    "//garnet/lib/overnet/vocabulary:status",
+  ]
+}
+
+source_set("introduction_test") {
+  testonly = true
+  sources = [
+    "introduction_test.cc",
+  ]
+  deps = [
+    ":introduction",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# routable_message
+source_set("routable_message") {
+  sources = [
+    "routable_message.cc",
+    "routable_message.h",
+  ]
+  deps = [
+    ":varint",
+    "//garnet/lib/overnet/labels:node_id",
+    "//garnet/lib/overnet/labels:seq_num",
+    "//garnet/lib/overnet/labels:stream_id",
+    "//garnet/lib/overnet/vocabulary:optional",
+    "//garnet/lib/overnet/vocabulary:slice",
+    "//garnet/lib/overnet/vocabulary:status",
+  ]
+}
+
+source_set("routable_message_test") {
+  testonly = true
+  sources = [
+    "routable_message_test.cc",
+  ]
+  deps = [
+    ":routable_message",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# serialization_helpers
+source_set("serialization_helpers") {
+  sources = [
+    "serialization_helpers.h",
+  ]
+}
+
+# varint
+source_set("varint") {
+  sources = [
+    "varint.cc",
+    "varint.h",
+  ]
+}
+
+source_set("varint_test") {
+  testonly = true
+  sources = [
+    "varint_test.cc",
+  ]
+  deps = [
+    ":varint",
+    "//third_party/googletest:gtest",
+  ]
+}
diff --git a/lib/overnet/ack_frame.cc b/lib/overnet/protocol/ack_frame.cc
similarity index 88%
rename from lib/overnet/ack_frame.cc
rename to lib/overnet/protocol/ack_frame.cc
index e7f6323..28d0bb8 100644
--- a/lib/overnet/ack_frame.cc
+++ b/lib/overnet/protocol/ack_frame.cc
@@ -22,6 +22,18 @@
     nack_length_.push_back(l);
     base = n;
   }
+  assert(ack_frame->WrittenLength() == wire_length_);
+}
+
+uint64_t AckFrame::WrittenLength() const {
+  uint64_t wire_length =
+      varint::WireSizeFor(ack_to_seq_) + varint::WireSizeFor(ack_delay_us_);
+  uint64_t base = ack_to_seq_;
+  for (auto n : nack_seqs_) {
+    wire_length += varint::WireSizeFor(base - n);
+    base = n;
+  }
+  return wire_length;
 }
 
 uint8_t* AckFrame::Writer::Write(uint8_t* out) const {
diff --git a/lib/overnet/ack_frame.h b/lib/overnet/protocol/ack_frame.h
similarity index 65%
rename from lib/overnet/ack_frame.h
rename to lib/overnet/protocol/ack_frame.h
index d48e133..44fb8ed 100644
--- a/lib/overnet/ack_frame.h
+++ b/lib/overnet/protocol/ack_frame.h
@@ -7,8 +7,9 @@
 #include <assert.h>
 #include <tuple>
 #include <vector>
-#include "slice.h"
-#include "status.h"
+#include "garnet/lib/overnet/environment/trace.h"
+#include "garnet/lib/overnet/vocabulary/slice.h"
+#include "garnet/lib/overnet/vocabulary/status.h"
 
 namespace overnet {
 
@@ -77,7 +78,34 @@
   uint64_t ack_delay_us() const { return ack_delay_us_; }
   const std::vector<uint64_t>& nack_seqs() const { return nack_seqs_; }
 
+  // Move ack_to_seq back in time such that the total ack frame will fit
+  // within mss. DelayFn is a function uint64_t -> TimeDelta that returns the
+  // ack delay (in microseconds) for a given sequence number.
+  template <class DelayFn>
+  bool AdjustForMSS(uint32_t mss, DelayFn delay_fn) {
+    bool adjusted = false;
+    while (!nack_seqs_.empty() && WrittenLength() > mss) {
+      adjusted = true;
+      if (ack_to_seq_ != nack_seqs_[0]) {
+        OVERNET_TRACE(DEBUG) << "Trim too long ack (" << WrittenLength()
+                             << " > " << mss << " by moving ack " << ack_to_seq_
+                             << " to first nack " << nack_seqs_[0];
+        ack_to_seq_ = nack_seqs_[0];
+      } else {
+        OVERNET_TRACE(DEBUG)
+            << "Trim too long ack (" << WrittenLength() << " > " << mss
+            << " by trimming first nack " << nack_seqs_[0];
+        nack_seqs_.erase(nack_seqs_.begin());
+        ack_to_seq_--;
+      }
+      ack_delay_us_ = delay_fn(ack_to_seq_);
+    }
+    return adjusted;
+  }
+
  private:
+  uint64_t WrittenLength() const;
+
   // All messages with sequence number prior to ack_to_seq_ are implicitly
   // acknowledged.
   uint64_t ack_to_seq_;
@@ -85,8 +113,8 @@
   // structure.
   uint64_t ack_delay_us_;
   // All messages contained in nack_seqs_ need to be resent.
-  // NOTE: it's assumed that nack_seqs_ is in-order and all value are greater
-  // than or equal to ack_to_seq_.
+  // NOTE: it's assumed that nack_seqs_ is in-order descending and all value are
+  // less than or equal to ack_to_seq_.
   std::vector<uint64_t> nack_seqs_;
 };
 
diff --git a/lib/overnet/ack_frame_test.cc b/lib/overnet/protocol/ack_frame_test.cc
similarity index 100%
rename from lib/overnet/ack_frame_test.cc
rename to lib/overnet/protocol/ack_frame_test.cc
diff --git a/lib/overnet/fork_frame.cc b/lib/overnet/protocol/fork_frame.cc
similarity index 63%
rename from lib/overnet/fork_frame.cc
rename to lib/overnet/protocol/fork_frame.cc
index 30ee825..87cabe2 100644
--- a/lib/overnet/fork_frame.cc
+++ b/lib/overnet/protocol/fork_frame.cc
@@ -6,13 +6,15 @@
 
 namespace overnet {
 
-Slice ForkFrame::Write() const {
+Slice ForkFrame::Write(Border desired_border) const {
   auto stream_id_length = stream_id_.wire_length();
-  return introduction_.WithPrefix(stream_id_length + 1, [=](uint8_t* bytes) {
-    uint8_t* p = bytes;
-    p = stream_id_.Write(stream_id_length, p);
-    *p++ = static_cast<uint8_t>(reliability_and_ordering_);
-  });
+  return introduction_
+      .Write(desired_border.WithAddedPrefix(stream_id_length + 1))
+      .WithPrefix(stream_id_length + 1, [=](uint8_t* bytes) {
+        uint8_t* p = bytes;
+        p = stream_id_.Write(stream_id_length, p);
+        *p++ = static_cast<uint8_t>(reliability_and_ordering_);
+      });
 }
 
 StatusOr<ForkFrame> ForkFrame::Parse(Slice slice) {
@@ -30,8 +32,12 @@
         "Failed to parse fork frame reliability and ordering byte");
   }
   auto reliability_and_ordering = static_cast<ReliabilityAndOrdering>(*p++);
+  auto introduction = Introduction::Parse(slice.FromPointer(p));
+  if (introduction.is_error()) {
+    return introduction.AsStatus();
+  }
   return ForkFrame(StreamId(stream_id), reliability_and_ordering,
-                   slice.FromOffset(p - begin));
+                   std::move(*introduction));
 }
 
 }  // namespace overnet
diff --git a/lib/overnet/fork_frame.h b/lib/overnet/protocol/fork_frame.h
similarity index 68%
rename from lib/overnet/fork_frame.h
rename to lib/overnet/protocol/fork_frame.h
index 2b4f014..0ff91fb 100644
--- a/lib/overnet/fork_frame.h
+++ b/lib/overnet/protocol/fork_frame.h
@@ -4,10 +4,11 @@
 
 #pragma once
 
-#include "reliability_and_ordering.h"
-#include "slice.h"
-#include "status.h"
-#include "stream_id.h"
+#include "garnet/lib/overnet/labels/reliability_and_ordering.h"
+#include "garnet/lib/overnet/labels/stream_id.h"
+#include "garnet/lib/overnet/protocol/introduction.h"
+#include "garnet/lib/overnet/vocabulary/slice.h"
+#include "garnet/lib/overnet/vocabulary/status.h"
 
 namespace overnet {
 
@@ -16,19 +17,20 @@
 class ForkFrame {
  public:
   ForkFrame(StreamId stream_id, ReliabilityAndOrdering reliability_and_ordering,
-            Slice introduction)
+            Introduction introduction)
       : stream_id_(stream_id),
         reliability_and_ordering_(reliability_and_ordering),
         introduction_(std::move(introduction)) {}
 
   static StatusOr<ForkFrame> Parse(Slice slice);
-  Slice Write() const;
+  Slice Write(Border desired_border) const;
 
   StreamId stream_id() const { return stream_id_; }
   ReliabilityAndOrdering reliability_and_ordering() const {
     return reliability_and_ordering_;
   }
-  const Slice& introduction() const { return introduction_; }
+  const Introduction& introduction() const { return introduction_; }
+  Introduction& introduction() { return introduction_; }
 
   friend bool operator==(const ForkFrame& a, const ForkFrame& b) {
     return std::tie(a.stream_id_, a.reliability_and_ordering_,
@@ -39,7 +41,7 @@
  private:
   StreamId stream_id_;
   ReliabilityAndOrdering reliability_and_ordering_;
-  Slice introduction_;
+  Introduction introduction_;
 };
 
 }  // namespace overnet
diff --git a/lib/overnet/fork_frame_test.cc b/lib/overnet/protocol/fork_frame_test.cc
similarity index 83%
rename from lib/overnet/fork_frame_test.cc
rename to lib/overnet/protocol/fork_frame_test.cc
index b7591ab..75cff74 100644
--- a/lib/overnet/fork_frame_test.cc
+++ b/lib/overnet/protocol/fork_frame_test.cc
@@ -10,7 +10,7 @@
 namespace fork_frame_test {
 
 void RoundTrip(const ForkFrame& h, const std::vector<uint8_t>& expect) {
-  auto e = h.Write();
+  auto e = h.Write(Border::None());
   EXPECT_EQ(Slice::FromCopiedBuffer(expect.data(), expect.size()), e);
   auto p = ForkFrame::Parse(e);
   ASSERT_TRUE(p.is_ok());
@@ -19,8 +19,8 @@
 
 TEST(ForkFrame, SomeFrame) {
   RoundTrip(ForkFrame(StreamId(1), ReliabilityAndOrdering::ReliableOrdered,
-                      Slice::FromStaticString("ABC")),
-            std::vector<uint8_t>{1, 1, 'A', 'B', 'C'});
+                      Introduction()),
+            std::vector<uint8_t>{1, 1});
 }
 
 }  // namespace fork_frame_test
diff --git a/lib/overnet/protocol/introduction.h b/lib/overnet/protocol/introduction.h
new file mode 100644
index 0000000..06a351a
--- /dev/null
+++ b/lib/overnet/protocol/introduction.h
@@ -0,0 +1,117 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <sstream>
+#include "garnet/lib/overnet/protocol/varint.h"
+#include "garnet/lib/overnet/vocabulary/optional.h"
+#include "garnet/lib/overnet/vocabulary/slice.h"
+#include "garnet/lib/overnet/vocabulary/status.h"
+
+namespace overnet {
+
+// An Introduction records a request to create a new stream in Overnet.
+// It's a key-value store, with a small fixed set of keys (up to 256 are
+// allowed).
+class Introduction {
+ public:
+  // Assigned key names
+  enum class Key : uint8_t {
+    ServiceName = 1,
+  };
+
+  Introduction() = default;
+  Introduction(std::initializer_list<std::pair<Key, Slice>> aggregate) {
+    for (auto &p : aggregate) {
+      (*this)[p.first] = p.second;
+    }
+  }
+
+  Optional<Slice> &operator[](Key key) {
+    return values_[static_cast<int>(key)];
+  }
+
+  const Optional<Slice> &operator[](Key key) const {
+    return values_[static_cast<int>(key)];
+  }
+
+  Slice Write(Border desired_border) const {
+    uint8_t key_len[256];
+    size_t value_len[256];
+    size_t frame_len = 0;
+    for (int i = 0; i < 256; i++) {
+      if (values_[i].has_value()) {
+        value_len[i] = values_[i]->length();
+        key_len[i] = varint::WireSizeFor(value_len[i]);
+        frame_len += 1;
+        frame_len += key_len[i];
+        frame_len += value_len[i];
+      } else {
+        key_len[i] = 0;
+      }
+    }
+    return Slice::WithInitializerAndBorders(
+        frame_len, desired_border, [&](uint8_t *p) {
+          for (int i = 0; i < 256; i++) {
+            if (key_len[i] != 0) {
+              *p++ = static_cast<uint8_t>(i);
+              p = varint::Write(value_len[i], key_len[i], p);
+              memcpy(p, values_[i]->begin(), value_len[i]);
+              p += value_len[i];
+            }
+          }
+        });
+  }
+
+  static StatusOr<Introduction> Parse(Slice slice) {
+    int max_seen_id = -1;
+    Introduction out;
+    const uint8_t *p = slice.begin();
+    const uint8_t *end = slice.end();
+    while (p != end) {
+      int id = *p++;
+      if (id <= max_seen_id) {
+        return StatusOr<Introduction>(
+            StatusCode::FAILED_PRECONDITION,
+            "Introduction chunks must be sent in ascending order");
+      }
+      uint64_t len;
+      if (!varint::Read(&p, end, &len)) {
+        return StatusOr<Introduction>(
+            StatusCode::FAILED_PRECONDITION,
+            "Failed to read value length from introduction slice");
+      }
+      const uint64_t remaining_bytes = uint64_t(end - p);
+      if (remaining_bytes < len) {
+        std::ostringstream out;
+        out << "Introduction value length runs past end of introduction slice: "
+               "had "
+            << remaining_bytes
+            << " left in stream, but encoded stream requested " << len
+            << " bytes: " << slice;
+        return StatusOr<Introduction>(StatusCode::FAILED_PRECONDITION,
+                                      out.str());
+      }
+      max_seen_id = id;
+      out.values_[id] = slice.FromPointer(p).ToOffset(len);
+      p += len;
+    }
+    return out;
+  }
+
+  bool operator==(const Introduction &other) const {
+    for (int i = 0; i < 256; i++) {
+      if (values_[i] != other.values_[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+ private:
+  Optional<Slice> values_[256];
+};
+
+}  // namespace overnet
\ No newline at end of file
diff --git a/lib/overnet/protocol/introduction_test.cc b/lib/overnet/protocol/introduction_test.cc
new file mode 100644
index 0000000..2f44c22
--- /dev/null
+++ b/lib/overnet/protocol/introduction_test.cc
@@ -0,0 +1,26 @@
+// Copyright 2018 The Fuchsia 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 "introduction.h"
+#include "gtest/gtest.h"
+
+namespace overnet {
+namespace introduction_test {
+
+TEST(Introduction, Empty) {
+  EXPECT_EQ(Slice::FromContainer({}), Introduction().Write(Border::None()));
+}
+
+TEST(Introduction, OneVal) {
+  Introduction intro;
+  intro[Introduction::Key::ServiceName] = Slice::FromStaticString("Hello!");
+  EXPECT_EQ(Slice::FromStaticString("\1\6Hello!"), intro.Write(Border::None()));
+  auto parsed = Introduction::Parse(intro.Write(Border::None()));
+  ASSERT_TRUE(parsed.is_ok()) << parsed.AsStatus();
+  EXPECT_EQ(Slice::FromStaticString("Hello!"),
+            *(*parsed.get())[Introduction::Key::ServiceName]);
+}
+
+}  // namespace introduction_test
+}  // namespace overnet
diff --git a/lib/overnet/routable_message.cc b/lib/overnet/protocol/routable_message.cc
similarity index 100%
rename from lib/overnet/routable_message.cc
rename to lib/overnet/protocol/routable_message.cc
diff --git a/lib/overnet/routable_message.h b/lib/overnet/protocol/routable_message.h
similarity index 90%
rename from lib/overnet/routable_message.h
rename to lib/overnet/protocol/routable_message.h
index f74fe61..46680b1 100644
--- a/lib/overnet/routable_message.h
+++ b/lib/overnet/protocol/routable_message.h
@@ -6,13 +6,13 @@
 
 #include <tuple>
 #include <vector>
-#include "node_id.h"
-#include "optional.h"
-#include "seq_num.h"
-#include "slice.h"
-#include "status.h"
-#include "stream_id.h"
-#include "varint.h"
+#include "garnet/lib/overnet/labels/node_id.h"
+#include "garnet/lib/overnet/labels/seq_num.h"
+#include "garnet/lib/overnet/labels/stream_id.h"
+#include "garnet/lib/overnet/protocol/varint.h"
+#include "garnet/lib/overnet/vocabulary/optional.h"
+#include "garnet/lib/overnet/vocabulary/slice.h"
+#include "garnet/lib/overnet/vocabulary/status.h"
 
 namespace overnet {
 
@@ -83,6 +83,9 @@
 
   Optional<size_t> MaxPayloadLength(NodeId writer, NodeId target,
                                     size_t remaining_space) const;
+  size_t MaxHeaderLength() {
+    return HeaderLength(NodeId(0), NodeId(0), nullptr);
+  }
 
   NodeId src() const { return src_; }
 
diff --git a/lib/overnet/routable_message_fuzzer.cc b/lib/overnet/protocol/routable_message_fuzzer.cc
similarity index 100%
rename from lib/overnet/routable_message_fuzzer.cc
rename to lib/overnet/protocol/routable_message_fuzzer.cc
diff --git a/lib/overnet/routable_message_test.cc b/lib/overnet/protocol/routable_message_test.cc
similarity index 100%
rename from lib/overnet/routable_message_test.cc
rename to lib/overnet/protocol/routable_message_test.cc
diff --git a/lib/overnet/serialization_helpers.h b/lib/overnet/protocol/serialization_helpers.h
similarity index 100%
rename from lib/overnet/serialization_helpers.h
rename to lib/overnet/protocol/serialization_helpers.h
diff --git a/lib/overnet/varint.cc b/lib/overnet/protocol/varint.cc
similarity index 100%
rename from lib/overnet/varint.cc
rename to lib/overnet/protocol/varint.cc
diff --git a/lib/overnet/varint.h b/lib/overnet/protocol/varint.h
similarity index 100%
rename from lib/overnet/varint.h
rename to lib/overnet/protocol/varint.h
diff --git a/lib/overnet/varint_test.cc b/lib/overnet/protocol/varint_test.cc
similarity index 100%
rename from lib/overnet/varint_test.cc
rename to lib/overnet/protocol/varint_test.cc
diff --git a/lib/overnet/router_endpoint.cc b/lib/overnet/router_endpoint.cc
deleted file mode 100644
index 6cc916d..0000000
--- a/lib/overnet/router_endpoint.cc
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2018 The Fuchsia 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 "router_endpoint.h"
-#include <iostream>
-#include <memory>
-#include "fork_frame.h"
-#include "linearizer.h"
-
-namespace overnet {
-
-namespace {
-template <class T>
-void ReadAllAndParse(Source<Slice>* src, StatusOrCallback<T> ready) {
-  src->PullAll(StatusOrCallback<std::vector<Slice>>(
-      ALLOCATED_CALLBACK,
-      [ready{std::move(ready)}](StatusOr<std::vector<Slice>>&& status) mutable {
-        if (status.is_error()) {
-          ready(status.AsStatus());
-          return;
-        }
-        ready(T::Parse(Slice::Join(status->begin(), status->end())));
-      }));
-}
-}  // namespace
-
-RouterEndpoint::RouterEndpoint(Timer* timer, TraceSink trace_sink,
-                               NodeId node_id, bool allow_threading)
-    : router_(timer, trace_sink, node_id, allow_threading) {}
-
-RouterEndpoint::~RouterEndpoint() { assert(connection_streams_.empty()); }
-
-void RouterEndpoint::Close(Callback<void> done) {
-  if (connection_streams_.empty()) {
-    router_.Close(std::move(done));
-    return;
-  }
-  auto it = connection_streams_.begin();
-  OVERNET_TRACE(INFO, router_.trace_sink()) << "Closing peer " << it->first;
-  Callback<void> after_close(ALLOCATED_CALLBACK,
-                             [this, it, done = std::move(done)]() mutable {
-                               OVERNET_TRACE(INFO, router_.trace_sink())
-                                   << "Closed peer " << it->first;
-                               connection_streams_.erase(it);
-                               Close(std::move(done));
-                             });
-  it->second.Close(Status::Ok(), std::move(after_close));
-}
-
-void RouterEndpoint::RegisterPeer(NodeId peer) {
-  assert(peer != router_.node_id());
-  if (connection_streams_.count(peer) != 0)
-    return;
-  connection_streams_.emplace(std::piecewise_construct,
-                              std::forward_as_tuple(peer),
-                              std::forward_as_tuple(this, peer));
-}
-
-RouterEndpoint::Stream::Stream(NewStream introduction, TraceSink trace_sink)
-    : DatagramStream(&introduction.creator_->router_, trace_sink,
-                     introduction.peer_, introduction.reliability_and_ordering_,
-                     introduction.stream_id_) {}
-
-RouterEndpoint::ConnectionStream::ConnectionStream(RouterEndpoint* endpoint,
-                                                   NodeId peer)
-    : DatagramStream(
-          &endpoint->router_,
-          endpoint->router_.trace_sink().Decorate(
-              [this, peer](const std::string& msg) {
-                std::ostringstream out;
-                out << "Con[" << this << ";peer=" << peer << "] " << msg;
-                return out.str();
-              }),
-          peer, ReliabilityAndOrdering::ReliableUnordered, StreamId(0)),
-      endpoint_(endpoint),
-      next_stream_id_(peer < endpoint->node_id() ? 2 : 1) {
-  BeginRead();
-}
-
-RouterEndpoint::ConnectionStream::~ConnectionStream() {
-  if (fork_read_state_ == ForkReadState::Reading) {
-    fork_read_->Close(Status::Cancelled());
-  }
-  assert(fork_read_state_ == ForkReadState::Stopped);
-}
-
-void RouterEndpoint::ConnectionStream::BeginRead() {
-  fork_read_state_ = ForkReadState::Reading;
-  fork_read_.Init(this);
-  fork_read_->PullAll(StatusOrCallback<std::vector<Slice>>(
-      [this](StatusOr<std::vector<Slice>>&& read_status) {
-        assert(fork_read_state_ == ForkReadState::Reading);
-        if (read_status.is_error()) {
-          fork_read_state_ = ForkReadState::Stopped;
-          Close(read_status.AsStatus(), Callback<void>::Ignored());
-          return;
-        } else if (read_status->size() == 0) {
-          fork_read_state_ = ForkReadState::Stopped;
-          Close(Status::Ok(), Callback<void>::Ignored());
-          return;
-        }
-        auto fork_frame_status = ForkFrame::Parse(
-            Slice::Join(read_status->begin(), read_status->end()));
-        if (fork_frame_status.is_error()) {
-          fork_read_state_ = ForkReadState::Stopped;
-          Close(fork_frame_status.AsStatus(), Callback<void>::Ignored());
-          return;
-        }
-        fork_frame_.Init(std::move(*fork_frame_status));
-        endpoint_->incoming_forks_.PushBack(this);
-        fork_read_.Destroy();
-        fork_read_state_ = ForkReadState::Waiting;
-        if (this == endpoint_->incoming_forks_.Front()) {
-          endpoint_->MaybeContinueIncomingForks();
-        }
-      }));
-}
-
-StatusOr<RouterEndpoint::NewStream> RouterEndpoint::SendIntro(
-    NodeId peer, ReliabilityAndOrdering reliability_and_ordering,
-    Slice introduction) {
-  auto it = connection_streams_.find(peer);
-  if (it == connection_streams_.end()) {
-    return StatusOr<NewStream>(StatusCode::FAILED_PRECONDITION,
-                               "Remote peer not registered with this endpoint");
-  }
-  return it->second.Fork(reliability_and_ordering, std::move(introduction));
-}
-
-StatusOr<RouterEndpoint::NewStream> RouterEndpoint::ConnectionStream::Fork(
-    ReliabilityAndOrdering reliability_and_ordering, Slice introduction) {
-  StreamId id(next_stream_id_);
-  next_stream_id_ += 2;
-  Slice payload =
-      ForkFrame(id, reliability_and_ordering, std::move(introduction)).Write();
-
-  // TODO(ctiller): Don't allocate.
-  auto* send_op = new SendOp(this, payload.length());
-  send_op->Push(payload);
-  send_op->Close(Status::Ok(), [send_op]() { delete send_op; });
-  return NewStream{endpoint_, peer(), reliability_and_ordering, id};
-}
-
-void RouterEndpoint::RecvIntro(StatusOrCallback<ReceivedIntroduction> ready) {
-  recv_intro_ready_ = std::move(ready);
-  MaybeContinueIncomingForks();
-}
-
-void RouterEndpoint::MaybeContinueIncomingForks() {
-  if (recv_intro_ready_.empty() || incoming_forks_.Empty())
-    return;
-  auto* incoming_fork = incoming_forks_.Front();
-  incoming_forks_.Remove(incoming_fork);
-  assert(incoming_fork->fork_read_state_ ==
-         ConnectionStream::ForkReadState::Waiting);
-  recv_intro_ready_(ReceivedIntroduction{
-      NewStream{this, incoming_fork->peer(),
-                incoming_fork->fork_frame_->reliability_and_ordering(),
-                incoming_fork->fork_frame_->stream_id()},
-      incoming_fork->fork_frame_->introduction()});
-  incoming_fork->fork_frame_.Destroy();
-  incoming_fork->BeginRead();
-}
-
-}  // namespace overnet
diff --git a/lib/overnet/router_endpoint.h b/lib/overnet/router_endpoint.h
deleted file mode 100644
index 746625b..0000000
--- a/lib/overnet/router_endpoint.h
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#pragma once
-
-#include <queue>
-#include "datagram_stream.h"
-#include "fork_frame.h"
-#include "manual_constructor.h"
-#include "receive_mode.h"
-#include "reliability_and_ordering.h"
-#include "router.h"
-#include "sink.h"
-#include "slice.h"
-
-namespace overnet {
-
-class RouterEndpoint final {
- public:
-  class NewStream final {
-   public:
-    NewStream(const NewStream&) = delete;
-    NewStream& operator=(const NewStream&) = delete;
-
-    NewStream(NewStream&& other)
-        : creator_(other.creator_),
-          peer_(other.peer_),
-          reliability_and_ordering_(other.reliability_and_ordering_),
-          stream_id_(other.stream_id_) {
-      other.creator_ = nullptr;
-    }
-    NewStream& operator=(NewStream&& other) {
-      creator_ = other.creator_;
-      peer_ = other.peer_;
-      reliability_and_ordering_ = other.reliability_and_ordering_;
-      stream_id_ = other.stream_id_;
-      other.creator_ = nullptr;
-      return *this;
-    }
-
-    friend std::ostream& operator<<(std::ostream& out, const NewStream& s) {
-      return out << "NewStream{node=" << s.peer_ << ",reliability_and_ordering="
-                 << ReliabilityAndOrderingString(s.reliability_and_ordering_)
-                 << ",stream_id=" << s.stream_id_ << "}";
-    }
-
-   private:
-    friend class RouterEndpoint;
-    NewStream(RouterEndpoint* creator, NodeId peer,
-              ReliabilityAndOrdering reliability_and_ordering,
-              StreamId stream_id)
-        : creator_(creator),
-          peer_(peer),
-          reliability_and_ordering_(reliability_and_ordering),
-          stream_id_(stream_id) {}
-
-    RouterEndpoint* creator_;
-    NodeId peer_;
-    ReliabilityAndOrdering reliability_and_ordering_;
-    StreamId stream_id_;
-  };
-
-  struct ReceivedIntroduction final {
-    NewStream new_stream;
-    Slice introduction;
-  };
-
-  class Stream final : public DatagramStream {
-   public:
-    Stream(NewStream introduction, TraceSink trace_sink);
-  };
-
-  using SendOp = Stream::SendOp;
-  using ReceiveOp = Stream::ReceiveOp;
-
-  explicit RouterEndpoint(Timer* timer, TraceSink trace_sink, NodeId node_id,
-                          bool allow_threading);
-  ~RouterEndpoint();
-  void Close(Callback<void> done);
-
-  void RegisterPeer(NodeId peer);
-
-  template <class F>
-  void ForEachPeer(F f) {
-    for (const auto& peer : connection_streams_) {
-      f(peer.first);
-    }
-  }
-
-  Router* router() { return &router_; }
-  NodeId node_id() const { return router_.node_id(); }
-
-  void RecvIntro(StatusOrCallback<ReceivedIntroduction> ready);
-  StatusOr<NewStream> SendIntro(NodeId peer,
-                                ReliabilityAndOrdering reliability_and_ordering,
-                                Slice introduction);
-
- private:
-  void MaybeContinueIncomingForks();
-
-  class ConnectionStream final : public DatagramStream {
-    friend class RouterEndpoint;
-
-   public:
-    ConnectionStream(RouterEndpoint* endpoint, NodeId peer);
-    ~ConnectionStream();
-
-    StatusOr<NewStream> Fork(ReliabilityAndOrdering reliability_and_ordering,
-                             Slice introduction);
-
-   private:
-    void BeginRead();
-
-    enum class ForkReadState {
-      Reading,
-      Waiting,
-      Stopped,
-    };
-
-    RouterEndpoint* const endpoint_;
-    uint64_t next_stream_id_;
-
-    ForkReadState fork_read_state_;
-    ManualConstructor<ReceiveOp> fork_read_;
-    InternalListNode<ConnectionStream> forking_ready_;
-    ManualConstructor<ForkFrame> fork_frame_;
-  };
-
-  Router router_;
-  std::unordered_map<NodeId, ConnectionStream> connection_streams_;
-  InternalList<ConnectionStream, &ConnectionStream::forking_ready_>
-      incoming_forks_;
-  StatusOrCallback<ReceivedIntroduction> recv_intro_ready_;
-};
-
-}  // namespace overnet
diff --git a/lib/overnet/router_endpoint_2node_test.cc b/lib/overnet/router_endpoint_2node_test.cc
deleted file mode 100644
index d15377b..0000000
--- a/lib/overnet/router_endpoint_2node_test.cc
+++ /dev/null
@@ -1,259 +0,0 @@
-// Copyright 2018 The Fuchsia 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 "gtest/gtest.h"
-#include "packet_link.h"
-#include "router_endpoint.h"
-#include "test_timer.h"
-#include "trace_cout.h"
-
-//////////////////////////////////////////////////////////////////////////////
-// Two node fling
-
-namespace overnet {
-namespace router_endpoint2node {
-
-static const bool kTraceEverything = false;
-
-class InProcessLinkImpl final
-    : public PacketLink,
-      public std::enable_shared_from_this<InProcessLinkImpl> {
- public:
-  InProcessLinkImpl(RouterEndpoint* src, RouterEndpoint* dest,
-                    TraceSink trace_sink, uint64_t link_id)
-      : PacketLink(src->router(), trace_sink, dest->node_id(), 256),
-        timer_(dest->router()->timer()),
-        trace_sink_(trace_sink),
-        from_(src->node_id()) {
-    src->RegisterPeer(dest->node_id());
-  }
-
-  ~InProcessLinkImpl() {
-    auto strong_partner = partner_.lock();
-    if (strong_partner != nullptr) {
-      strong_partner->partner_.reset();
-    }
-  }
-
-  void Partner(std::shared_ptr<InProcessLinkImpl> other) {
-    partner_ = other;
-    other->partner_ = shared_from_this();
-  }
-
-  void Emit(Slice packet) {
-    timer_->At(
-        timer_->Now() + TimeDelta::FromMilliseconds(3),
-        Callback<void>(ALLOCATED_CALLBACK, [partner = partner_, from = from_,
-                                            now = timer_->Now(), packet,
-                                            trace_sink = trace_sink_]() {
-          auto strong_partner = partner.lock();
-          OVERNET_TRACE(DEBUG, trace_sink)
-              << (strong_partner == nullptr ? "DROP" : "EMIT")
-              << " PACKET from " << from << " " << packet << "\n";
-          if (strong_partner) {
-            strong_partner->Process(now, packet);
-          }
-        }));
-  }
-
- private:
-  Timer* const timer_;
-  TraceSink trace_sink_;
-  std::weak_ptr<InProcessLinkImpl> partner_;
-  const NodeId from_;
-};
-
-class InProcessLink final : public Link {
- public:
-  InProcessLink(RouterEndpoint* src, RouterEndpoint* dest, TraceSink trace_sink,
-                uint64_t link_id)
-      : impl_(new InProcessLinkImpl(src, dest, trace_sink, link_id)) {}
-
-  std::shared_ptr<InProcessLinkImpl> get() { return impl_; }
-
-  void Close(Callback<void> quiesced) { impl_->Close(std::move(quiesced)); }
-  void Forward(Message message) { impl_->Forward(std::move(message)); }
-  LinkMetrics GetLinkMetrics() { return impl_->GetLinkMetrics(); }
-
- private:
-  std::shared_ptr<InProcessLinkImpl> impl_;
-};
-
-class TwoNodeFling : public ::testing::Test {
- public:
-  TwoNodeFling() {
-    auto link1 =
-        MakeLink<InProcessLink>(endpoint1_, endpoint2_, trace_sink(), 99599104);
-    auto link2 =
-        MakeLink<InProcessLink>(endpoint2_, endpoint1_, trace_sink(), 99594576);
-    link1->get()->Partner(link2->get());
-    endpoint1_->router()->RegisterLink(std::move(link1));
-    endpoint2_->router()->RegisterLink(std::move(link2));
-
-    while (!endpoint1_->router()->HasRouteTo(NodeId(2)) ||
-           !endpoint2_->router()->HasRouteTo(NodeId(1))) {
-      endpoint1_->router()->BlockUntilNoBackgroundUpdatesProcessing();
-      endpoint2_->router()->BlockUntilNoBackgroundUpdatesProcessing();
-      test_timer_.StepUntilNextEvent();
-    }
-  }
-
-  virtual ~TwoNodeFling() {
-    endpoint1_->Close([this]() {
-      endpoint2_->Close([this]() {
-        delete endpoint1_;
-        delete endpoint2_;
-      });
-    });
-    FlushTodo();
-  }
-
-  RouterEndpoint* endpoint1() { return endpoint1_; }
-  RouterEndpoint* endpoint2() { return endpoint2_; }
-
-  void FlushTodo(std::function<bool()> until = []() { return false; }) {
-    const TimeDelta initial_dt = TimeDelta::FromMilliseconds(1);
-    TimeDelta dt = initial_dt;
-    while (dt < TimeDelta::FromSeconds(30)) {
-      if (until())
-        return;
-      if (test_timer_.StepUntilNextEvent(dt)) {
-        dt = initial_dt;
-        continue;
-      }
-      dt = dt + dt;
-    }
-  }
-
-  TraceSink trace_sink() const { return trace_sink_; }
-
- private:
-  TestTimer test_timer_;
-  TraceSink trace_sink_ =
-      kTraceEverything ? TraceCout(&test_timer_) : TraceSink();
-  RouterEndpoint* endpoint1_ =
-      new RouterEndpoint(&test_timer_, trace_sink_, NodeId(1), true);
-  RouterEndpoint* endpoint2_ =
-      new RouterEndpoint(&test_timer_, trace_sink_, NodeId(2), true);
-  Optional<TimeStamp> end_time_;
-};
-
-TEST_F(TwoNodeFling, NoOp) {}
-
-struct OneMessageArgs {
-  TimeDelta timeout;
-  Slice intro;
-  Slice body;
-};
-
-std::ostream& operator<<(std::ostream& out, OneMessageArgs args) {
-  return out << "intro=" << args.intro << " body=" << args.body;
-}
-
-class TwoNodeFling_OneMessage
-    : public TwoNodeFling,
-      public ::testing::WithParamInterface<OneMessageArgs> {};
-
-TEST_P(TwoNodeFling_OneMessage, Works) {
-  bool got_pull_cb = false;
-
-  auto intro_status = this->endpoint1()->SendIntro(
-      NodeId(2), ReliabilityAndOrdering::ReliableOrdered, GetParam().intro);
-  ASSERT_TRUE(intro_status.is_ok()) << intro_status;
-  auto stream = MakeClosedPtr<RouterEndpoint::Stream>(
-      std::move(*intro_status.get()), trace_sink());
-  auto* op = new RouterEndpoint::SendOp(stream.get(), GetParam().body.length());
-  op->Push(GetParam().body);
-  op->Close(Status::Ok(), [op]() { delete op; });
-
-  this->endpoint2()->RecvIntro(
-      StatusOrCallback<RouterEndpoint::ReceivedIntroduction>(
-          ALLOCATED_CALLBACK,
-          [this, &got_pull_cb](
-              StatusOr<RouterEndpoint::ReceivedIntroduction>&& status) {
-            OVERNET_TRACE(INFO, trace_sink())
-                << "ep2: recv_intro status=" << status.AsStatus();
-            ASSERT_TRUE(status.is_ok()) << status.AsStatus();
-            auto intro = std::move(*status);
-            EXPECT_EQ(GetParam().intro, intro.introduction)
-                << intro.introduction.AsStdString();
-            auto stream = MakeClosedPtr<RouterEndpoint::Stream>(
-                std::move(intro.new_stream), trace_sink());
-            auto* op = new RouterEndpoint::ReceiveOp(stream.get());
-            op->PullAll(StatusOrCallback<std::vector<Slice>>(
-                ALLOCATED_CALLBACK,
-                [this, &got_pull_cb, stream{std::move(stream)},
-                 op](const StatusOr<std::vector<Slice>>& status) mutable {
-                  OVERNET_TRACE(INFO, trace_sink())
-                      << "ep2: pull_all status=" << status.AsStatus();
-                  EXPECT_TRUE(status.is_ok()) << status.AsStatus();
-                  auto pull_text = Slice::Join(status->begin(), status->end());
-                  EXPECT_EQ(GetParam().body, pull_text)
-                      << pull_text.AsStdString();
-                  delete op;
-                  got_pull_cb = true;
-                }));
-          }));
-
-  FlushTodo([&got_pull_cb]() { return got_pull_cb; });
-
-  EXPECT_TRUE(got_pull_cb);
-}
-
-INSTANTIATE_TEST_CASE_P(
-    TwoNodeFling_OneMessage_Instance, TwoNodeFling_OneMessage,
-    ::testing::Values(OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::FromStaticString("abc"),
-                                     Slice::FromStaticString("123")},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::FromStaticString("abc"),
-                                     Slice::RepeatedChar(3, 'a')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::FromStaticString("abc"),
-                                     Slice::RepeatedChar(128, 'a')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::FromStaticString("abc"),
-                                     Slice::RepeatedChar(240, 'a')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::FromStaticString("abc"),
-                                     Slice::RepeatedChar(256, 'a')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::FromStaticString("abc"),
-                                     Slice::RepeatedChar(512, 'a')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::RepeatedChar(1024, 'a'),
-                                     Slice::RepeatedChar(1024, 'b')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::RepeatedChar(2 * 1024, 'a'),
-                                     Slice::RepeatedChar(2 * 1024, 'b')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::RepeatedChar(4 * 1024, 'a'),
-                                     Slice::RepeatedChar(4 * 1024, 'b')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::RepeatedChar(8 * 1024, 'a'),
-                                     Slice::RepeatedChar(8 * 1024, 'b')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::RepeatedChar(16 * 1024, 'a'),
-                                     Slice::RepeatedChar(16 * 1024, 'b')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::RepeatedChar(32 * 1024, 'a'),
-                                     Slice::RepeatedChar(32 * 1024, 'b')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::RepeatedChar(64 * 1024, 'a'),
-                                     Slice::RepeatedChar(64 * 1024, 'b')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::RepeatedChar(128 * 1024, 'a'),
-                                     Slice::RepeatedChar(128 * 1024, 'b')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::RepeatedChar(256 * 1024, 'a'),
-                                     Slice::RepeatedChar(256 * 1024, 'b')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::RepeatedChar(512 * 1024, 'a'),
-                                     Slice::RepeatedChar(512 * 1024, 'b')},
-                      OneMessageArgs{TimeDelta::FromSeconds(60),
-                                     Slice::RepeatedChar(1024 * 1024, 'a'),
-                                     Slice::RepeatedChar(1024 * 1024, 'b')}));
-
-}  // namespace router_endpoint2node
-}  // namespace overnet
diff --git a/lib/overnet/routing/BUILD.gn b/lib/overnet/routing/BUILD.gn
new file mode 100644
index 0000000..521201d
--- /dev/null
+++ b/lib/overnet/routing/BUILD.gn
@@ -0,0 +1,69 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+###############################################################################
+# AGGREGATE LIBRARIES
+
+source_set("lib") {
+  public_deps = []
+}
+
+source_set("tests") {
+  testonly = true
+  deps = [
+    ":router_test",
+  ]
+}
+
+###############################################################################
+
+# router
+source_set("router") {
+  sources = [
+    "router.cc",
+    "router.h",
+  ]
+  deps = [
+    ":routing_table",
+    "//garnet/lib/overnet/environment:timer",
+    "//garnet/lib/overnet/environment:trace",
+    "//garnet/lib/overnet/protocol:routable_message",
+    "//garnet/lib/overnet/vocabulary:callback",
+    "//garnet/lib/overnet/vocabulary:closed_ptr",
+    "//garnet/lib/overnet/vocabulary:lazy_slice",
+    "//garnet/lib/overnet/vocabulary:slice",
+  ]
+}
+
+source_set("router_test") {
+  testonly = true
+  sources = [
+    "router_test.cc",
+  ]
+  deps = [
+    ":router",
+    "//garnet/lib/overnet/testing:test_timer",
+    "//garnet/lib/overnet/testing:trace_cout",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# routing_table
+source_set("routing_table") {
+  sources = [
+    "routing_table.cc",
+    "routing_table.h",
+  ]
+  deps = [
+    "//garnet/lib/overnet/environment:timer",
+    "//garnet/lib/overnet/environment:trace",
+    "//garnet/lib/overnet/labels:node_id",
+    "//garnet/lib/overnet/protocol:varint",  # TODO(ctiller): remove once we can take a FIDL dependency
+    "//garnet/lib/overnet/vocabulary:bandwidth",
+    "//garnet/lib/overnet/vocabulary:internal_list",
+    "//garnet/lib/overnet/vocabulary:optional",
+    "//garnet/lib/overnet/vocabulary:slice",
+  ]
+}
diff --git a/lib/overnet/router.cc b/lib/overnet/routing/router.cc
similarity index 61%
rename from lib/overnet/router.cc
rename to lib/overnet/routing/router.cc
index 5602a42..28be67c 100644
--- a/lib/overnet/router.cc
+++ b/lib/overnet/routing/router.cc
@@ -10,10 +10,20 @@
 static constexpr TimeDelta kPollLinkChangeTimeout =
     TimeDelta::FromMilliseconds(100);
 
+Router::Router(Timer* timer, NodeId node_id, bool allow_non_determinism)
+    : timer_(timer),
+      node_id_(node_id),
+      rng_(allow_non_determinism ? std::random_device()() : 0),
+      routing_table_(node_id, timer, allow_non_determinism),
+      own_metrics_(node_id, 1, true) {
+  UpdateRoutingTable({own_metrics_}, {}, false);
+}
+
 Router::~Router() { shutting_down_ = true; }
 
 void Router::Close(Callback<void> quiesced) {
-  OVERNET_TRACE(DEBUG, trace_sink_) << "Close";
+  ScopedModule<Router> scoped_module(this);
+  OVERNET_TRACE(INFO) << node_id_ << " Close";
   shutting_down_ = true;
   poll_link_changes_timeout_.Reset();
   flush_old_nodes_timeout_.Reset();
@@ -22,8 +32,8 @@
 }
 
 void Router::CloseLinks(Callback<void> quiesced) {
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "CloseLinks remaining=" << owned_links_.size();
+  OVERNET_TRACE(INFO) << node_id_
+                      << " CloseLinks remaining=" << owned_links_.size();
   if (owned_links_.empty()) {
     CloseStreams(std::move(quiesced));
     return;
@@ -33,22 +43,25 @@
   owned_links_.erase(it);
   p->Close(Callback<void>(ALLOCATED_CALLBACK,
                           [this, p, quiesced = std::move(quiesced)]() mutable {
+                            ScopedModule<Router> scoped_module(this);
                             delete p;
                             CloseLinks(std::move(quiesced));
                           }));
 }
 
 void Router::CloseStreams(Callback<void> quiesced) {
-  OVERNET_TRACE(DEBUG, trace_sink_)
-      << "CloseStreams remaining=" << streams_.size();
   if (streams_.empty()) {
-    OVERNET_TRACE(DEBUG, trace_sink_) << "Closed";
+    OVERNET_TRACE(INFO) << "Closed";
     return;
   }
   auto it = streams_.begin();
   auto id = it->first;
+  OVERNET_TRACE(INFO) << node_id_
+                      << " CloseStreams remaining=" << streams_.size()
+                      << " next=" << id.peer << "/" << id.stream_id;
   it->second.Close(Callback<void>(
       ALLOCATED_CALLBACK, [this, id, quiesced = std::move(quiesced)]() mutable {
+        ScopedModule<Router> scoped_module(this);
         auto it = streams_.find(id);
         if (it != streams_.end()) {
           assert(!it->second.has_handler());
@@ -60,7 +73,9 @@
 }
 
 void Router::Forward(Message message) {
-  OVERNET_TRACE(DEBUG, trace_sink_) << "Forward " << message.header;
+  ScopedModule<Router> scoped_module(this);
+  OVERNET_TRACE(DEBUG) << "Forward " << message.header
+                       << " shutting_down=" << shutting_down_;
   if (shutting_down_) {
     return;
   }
@@ -85,10 +100,13 @@
       const RoutableMessage::Destination& dst =
           message.header.destinations()[0];
       if (dst.dst() == node_id_) {
-        streams_[LocalStreamId{message.header.src(), dst.stream_id()}]
-            .HandleMessage(dst.seq(), message.received,
-                           message.make_payload(LazySliceArgs{
-                               0, std::numeric_limits<uint32_t>::max()}));
+        if (!stream_holder(message.header.src(), dst.stream_id())
+                 ->HandleMessage(dst.seq(), message.received,
+                                 message.make_payload(LazySliceArgs{
+                                     Border::None(),
+                                     std::numeric_limits<uint32_t>::max()}))) {
+          OnUnknownStream(message.header.src(), dst.stream_id());
+        }
       } else {
         link_holder(dst.dst())->Forward(std::move(message));
       }
@@ -113,8 +131,7 @@
           // Locally handled stream
           if (!handle_locally.has_value()) {
             handle_locally = std::make_pair(
-                dst, &streams_[LocalStreamId{message.header.src(),
-                                             dst.stream_id()}]);
+                dst, stream_holder(message.header.src(), dst.stream_id()));
           }
         } else {
           // Remote destination
@@ -131,7 +148,13 @@
           }
         }
       }
-      Slice payload = message.make_payload(LazySliceArgs{0, overall_mss});
+      const auto max_header_length = message.header.MaxHeaderLength();
+      if (overall_mss < max_header_length) {
+        return;
+      }
+      overall_mss -= max_header_length;
+      Slice payload =
+          message.make_payload(LazySliceArgs{Border::None(), overall_mss});
       // Forward any grouped messages now that we've examined all destinations
       for (auto& grp : group_forward) {
         grp.first->Forward(Message::SimpleForwarder(
@@ -144,8 +167,12 @@
             message.received));
       }
       if (handle_locally.has_value()) {
-        handle_locally->second->HandleMessage(
-            handle_locally->first.seq(), message.received, std::move(payload));
+        if (!handle_locally->second->HandleMessage(handle_locally->first.seq(),
+                                                   message.received,
+                                                   std::move(payload))) {
+          OnUnknownStream(message.header.src(),
+                          handle_locally->first.stream_id());
+        }
       }
     } break;
   }
@@ -154,12 +181,14 @@
 void Router::UpdateRoutingTable(std::vector<NodeMetrics> node_metrics,
                                 std::vector<LinkMetrics> link_metrics,
                                 bool flush_old_nodes) {
+  ScopedModule<Router> scoped_module(this);
   routing_table_.Update(std::move(node_metrics), std::move(link_metrics),
                         flush_old_nodes);
   MaybeStartPollingLinkChanges();
 }
 
 void Router::MaybeStartPollingLinkChanges() {
+  ScopedModule<Router> scoped_module(this);
   if (shutting_down_ || poll_link_changes_timeout_)
     return;
   poll_link_changes_timeout_.Reset(
@@ -172,18 +201,20 @@
                 // Clear routing information for now unreachable links.
                 for (auto& lnk : links_) {
                   if (selected_links.count(lnk.first) == 0) {
-                    lnk.second.SetLink(nullptr, 0);
+                    lnk.second.SetLink(nullptr, 0, false);
                   }
                 }
                 // Set routing information for other links.
                 for (const auto& sl : selected_links) {
-                  OVERNET_TRACE(INFO, trace_sink_)
+                  OVERNET_TRACE(INFO)
                       << "Select: " << sl.first << " " << sl.second.link_id
                       << " (route_mss=" << sl.second.route_mss << ")";
                   auto it = owned_links_.find(sl.second.link_id);
+                  auto* link =
+                      it == owned_links_.end() ? nullptr : it->second.get();
                   link_holder(sl.first)->SetLink(
-                      it == owned_links_.end() ? nullptr : it->second.get(),
-                      sl.second.route_mss);
+                      link, sl.second.route_mss,
+                      link ? link->GetLinkMetrics().to() == sl.first : false);
                 }
                 MaybeStartFlushingOldEntries();
               });
@@ -194,6 +225,7 @@
 }
 
 void Router::MaybeStartFlushingOldEntries() {
+  ScopedModule<Router> scoped_module(this);
   if (flush_old_nodes_timeout_)
     return;
   flush_old_nodes_timeout_.Reset(timer_,
@@ -208,15 +240,17 @@
 
 Status Router::RegisterStream(NodeId peer, StreamId stream_id,
                               StreamHandler* stream_handler) {
-  OVERNET_TRACE(DEBUG, trace_sink_) << "RegisterStream: " << peer << "/"
-                                    << stream_id << " at " << stream_handler;
-  return streams_[LocalStreamId{peer, stream_id}].SetHandler(stream_handler);
+  ScopedModule<Router> scoped_module(this);
+  OVERNET_TRACE(DEBUG) << "RegisterStream: " << peer << "/" << stream_id
+                       << " at " << stream_handler;
+  return stream_holder(peer, stream_id)->SetHandler(stream_handler);
 }
 
 Status Router::UnregisterStream(NodeId peer, StreamId stream_id,
                                 StreamHandler* stream_handler) {
-  OVERNET_TRACE(DEBUG, trace_sink_) << "UnregisterStream: " << peer << "/"
-                                    << stream_id << " at " << stream_handler;
+  ScopedModule<Router> scoped_module(this);
+  OVERNET_TRACE(DEBUG) << "UnregisterStream: " << peer << "/" << stream_id
+                       << " at " << stream_handler;
   auto it = streams_.find(LocalStreamId{peer, stream_id});
   if (it == streams_.end()) {
     return Status(StatusCode::FAILED_PRECONDITION, "Stream not registered");
@@ -226,18 +260,73 @@
   return status;
 }
 
-void Router::RegisterLink(LinkPtr<> link) {
-  const auto& metrics = link->GetLinkMetrics();
-  owned_links_.emplace(metrics.link_label(), std::move(link));
-  UpdateRoutingTable({NodeMetrics(metrics.to(), 0)}, {metrics}, false);
+Optional<NodeId> Router::SelectGossipPeer() {
+  ScopedModule<Router> scoped_module(this);
+  const uint64_t gossip_version = routing_table_.gossip_version();
+  std::vector<NodeId> eligible_nodes;
+  for (const auto& peer : links_) {
+    if (peer.second.has_direct_link() &&
+        peer.second.last_gossip_version() < gossip_version) {
+      eligible_nodes.push_back(peer.first);
+    }
+  }
+  if (eligible_nodes.empty()) {
+    return Nothing;
+  }
+  std::uniform_int_distribution<> dis(0, eligible_nodes.size() - 1);
+  return eligible_nodes[dis(rng_)];
 }
 
-void Router::StreamHolder::HandleMessage(SeqNum seq, TimeStamp received,
+Slice Router::WriteGossipUpdate(Border desired_border, NodeId target) {
+  ScopedModule<Router> scoped_module(this);
+  auto slice_and_version = routing_table_.Write(desired_border, target);
+  link_holder(target)->set_last_gossip_version(slice_and_version.second);
+  return slice_and_version.first;
+}
+
+Status Router::ApplyGossipUpdate(Slice update, NodeId from_peer) {
+  ScopedModule<Router> scoped_module(this);
+  auto status = routing_table_.Read(std::move(update));
+  if (status.is_ok()) {
+    MaybeStartPollingLinkChanges();
+  }
+  return status;
+}
+
+void Router::SetDescription(Slice slice) {
+  OVERNET_TRACE(INFO) << "SetDescription:" << slice;
+  if (slice == own_metrics_.description()) {
+    return;
+  }
+  own_metrics_.set_description(std::move(slice));
+  own_metrics_.IncrementVersion();
+  UpdateRoutingTable({own_metrics_}, {}, false);
+}
+
+void Router::RegisterLink(LinkPtr<> link) {
+  ScopedModule<Router> scoped_module(this);
+  const auto& metrics = link->GetLinkMetrics();
+  owned_links_.emplace(metrics.link_label(), std::move(link));
+  UpdateRoutingTable({NodeMetrics(metrics.to(), 0, true)}, {metrics}, false);
+}
+
+bool Router::StreamHolder::HandleMessage(SeqNum seq, TimeStamp received,
                                          Slice payload) {
   if (handler_ == nullptr) {
-    pending_.emplace_back(Pending{seq, received, std::move(payload)});
+    if (!buffered_ || buffered_->seq.Reconstruct(1) < seq.Reconstruct(1)) {
+      OVERNET_TRACE(DEBUG) << "Buffer message: peer=" << peer_
+                           << " stream=" << stream_ << " seq=" << seq
+                           << " message=" << payload;
+      buffered_.reset(new BufferedPacket{seq, received, std::move(payload)});
+    } else {
+      OVERNET_TRACE(DEBUG) << "Drop message: peer=" << peer_
+                           << " stream=" << stream_ << " seq=" << seq
+                           << " message=" << payload;
+    }
+    return false;
   } else {
     handler_->HandleMessage(seq, received, std::move(payload));
+    return true;
   }
 }
 
@@ -246,10 +335,9 @@
     return Status(StatusCode::FAILED_PRECONDITION, "Handler already set");
   }
   handler_ = handler;
-  std::vector<Pending> pending;
-  pending.swap(pending_);
-  for (auto& p : pending) {
-    handler_->HandleMessage(p.seq, p.received, std::move(p.payload));
+  if (buffered_) {
+    auto pkt = std::move(buffered_);
+    handler_->HandleMessage(pkt->seq, pkt->received, std::move(pkt->payload));
   }
   return Status::Ok();
 }
@@ -264,19 +352,23 @@
 
 void Router::LinkHolder::Forward(Message message) {
   if (link_ == nullptr) {
-    OVERNET_TRACE(DEBUG, trace_sink_) << "Queue: " << message.header;
+    OVERNET_TRACE(DEBUG) << "Queue: " << message.header;
     pending_.emplace_back(std::move(message));
   } else {
+    message.mss = std::min(message.mss, path_mss_);
     link_->Forward(std::move(message));
   }
 }
 
-void Router::LinkHolder::SetLink(Link* link, uint32_t path_mss) {
+void Router::LinkHolder::SetLink(Link* link, uint32_t path_mss,
+                                 bool is_direct) {
   link_ = link;
+  is_direct_ = is_direct;
+  path_mss_ = path_mss;
   std::vector<Message> pending;
   pending.swap(pending_);
   for (auto& p : pending) {
-    link_->Forward(std::move(p));
+    Forward(std::move(p));
   }
 }
 
diff --git a/lib/overnet/router.h b/lib/overnet/routing/router.h
similarity index 64%
rename from lib/overnet/router.h
rename to lib/overnet/routing/router.h
index d5f9181..c936c79 100644
--- a/lib/overnet/router.h
+++ b/lib/overnet/routing/router.h
@@ -4,17 +4,16 @@
 
 #pragma once
 
+#include <random>
 #include <unordered_map>
-#include "callback.h"
-#include "closed_ptr.h"
-#include "lazy_slice.h"
-#include "once_fn.h"
-#include "routable_message.h"
-#include "routing_table.h"
-#include "sink.h"
-#include "slice.h"
-#include "timer.h"
-#include "trace.h"
+#include "garnet/lib/overnet/environment/timer.h"
+#include "garnet/lib/overnet/environment/trace.h"
+#include "garnet/lib/overnet/protocol/routable_message.h"
+#include "garnet/lib/overnet/routing/routing_table.h"
+#include "garnet/lib/overnet/vocabulary/callback.h"
+#include "garnet/lib/overnet/vocabulary/closed_ptr.h"
+#include "garnet/lib/overnet/vocabulary/lazy_slice.h"
+#include "garnet/lib/overnet/vocabulary/slice.h"
 
 namespace overnet {
 namespace router_impl {
@@ -50,6 +49,7 @@
   RoutableMessage header;
   LazySlice make_payload;
   TimeStamp received;
+  uint32_t mss = std::numeric_limits<uint32_t>::max();
 
   static Message SimpleForwarder(RoutableMessage msg, Slice payload,
                                  TimeStamp received) {
@@ -72,31 +72,22 @@
   return MakeClosedPtr<T, Link>(std::forward<Args>(args)...);
 }
 
-class Router final {
+class Router {
  public:
+  static constexpr inline auto kModule = Module::ROUTER;
+
   class StreamHandler {
    public:
     virtual ~StreamHandler() {}
-    virtual void Close(Callback<void> quiesced) = 0;
+    virtual void RouterClose(Callback<void> quiesced) = 0;
     virtual void HandleMessage(SeqNum seq, TimeStamp received, Slice data) = 0;
   };
 
-  Router(Timer* timer, TraceSink trace_sink, NodeId node_id,
-         bool allow_threading)
-      : timer_(timer),
-        trace_sink_(trace_sink.Decorate([this](const std::string& msg) {
-          std::ostringstream out;
-          out << "Router[" << this << "] " << msg;
-          return out.str();
-        })),
-        node_id_(node_id),
-        routing_table_(node_id, timer, trace_sink_, allow_threading) {
-    UpdateRoutingTable({NodeMetrics(node_id, 0)}, {}, false);
-  }
+  Router(Timer* timer, NodeId node_id, bool allow_non_determinism);
 
-  ~Router();
+  virtual ~Router();
 
-  void Close(Callback<void> quiesced);
+  virtual void Close(Callback<void> quiesced);
 
   // Forward a message to either ourselves or a link
   void Forward(Message message);
@@ -110,6 +101,7 @@
 
   NodeId node_id() const { return node_id_; }
   Timer* timer() const { return timer_; }
+  auto* rng() { return &rng_; }
 
   void UpdateRoutingTable(std::vector<NodeMetrics> node_metrics,
                           std::vector<LinkMetrics> link_metrics) {
@@ -125,16 +117,25 @@
     return node_id == node_id_ || link_holder(node_id)->link() != nullptr;
   }
 
-  TraceSink trace_sink() const { return trace_sink_; }
+  Optional<NodeId> SelectGossipPeer();
+  Slice WriteGossipUpdate(Border desired_border, NodeId target);
+  Status ApplyGossipUpdate(Slice update, NodeId peer);
+
+  void SetDescription(Slice slice);
+
+  template <class F>
+  void ForEachNodeMetric(F mutator) {
+    routing_table_.ForEachNodeMetric(mutator);
+  }
 
  private:
   Timer* const timer_;
-  const TraceSink trace_sink_;
   const NodeId node_id_;
 
   void UpdateRoutingTable(std::vector<NodeMetrics> node_metrics,
                           std::vector<LinkMetrics> link_metrics,
                           bool flush_old_nodes);
+  virtual void OnUnknownStream(NodeId peer, StreamId stream_id) {}
 
   void MaybeStartPollingLinkChanges();
   void MaybeStartFlushingOldEntries();
@@ -144,45 +145,48 @@
 
   class StreamHolder {
    public:
-    void HandleMessage(SeqNum seq, TimeStamp received, Slice payload);
+    StreamHolder(NodeId peer, StreamId id) : peer_(peer), stream_(id) {}
     Status SetHandler(StreamHandler* handler);
+    [[nodiscard]] bool HandleMessage(SeqNum seq, TimeStamp received,
+                                     Slice payload);
     Status ClearHandler(StreamHandler* handler);
     void Close(Callback<void> quiesced) {
       if (handler_ != nullptr)
-        handler_->Close(std::move(quiesced));
+        handler_->RouterClose(std::move(quiesced));
     }
     bool has_handler() { return handler_ != nullptr; }
 
    private:
-    struct Pending {
+    const NodeId peer_;
+    const StreamId stream_;
+    StreamHandler* handler_ = nullptr;
+    // TODO(ctiller): globally cap the number of buffered packets within Router
+    struct BufferedPacket {
       SeqNum seq;
       TimeStamp received;
       Slice payload;
     };
-
-    StreamHandler* handler_ = nullptr;
-    std::vector<Pending> pending_;
+    std::unique_ptr<BufferedPacket> buffered_;
   };
 
   class LinkHolder {
    public:
-    LinkHolder(NodeId target, TraceSink trace_sink)
-        : trace_sink_(
-              trace_sink.Decorate([this, target](const std::string& msg) {
-                std::ostringstream out;
-                out << "Link[" << this << ";to=" << target << "] " << msg;
-                return out.str();
-              })) {}
+    LinkHolder(NodeId target) {}
     void Forward(Message message);
-    void SetLink(Link* link, uint32_t path_mss);
+    void SetLink(Link* link, uint32_t path_mss, bool is_direct);
     Link* link() { return link_; }
+    bool has_direct_link() const { return is_direct_; }
     uint32_t path_mss() { return path_mss_; }
 
+    uint64_t last_gossip_version() const { return last_gossip_version_; }
+    void set_last_gossip_version(uint64_t n) { last_gossip_version_ = n; }
+
    private:
-    const TraceSink trace_sink_;
     Link* link_ = nullptr;
+    bool is_direct_ = false;
     uint32_t path_mss_ = std::numeric_limits<uint32_t>::max();
     std::vector<Message> pending_;
+    uint64_t last_gossip_version_ = 0;
   };
 
   LinkHolder* link_holder(NodeId node_id) {
@@ -192,7 +196,19 @@
     return &links_
                 .emplace(std::piecewise_construct,
                          std::forward_as_tuple(node_id),
-                         std::forward_as_tuple(node_id, trace_sink_))
+                         std::forward_as_tuple(node_id))
+                .first->second;
+  }
+
+  StreamHolder* stream_holder(NodeId node_id, StreamId stream_id) {
+    auto it = streams_.find(LocalStreamId{node_id, stream_id});
+    if (it != streams_.end())
+      return &it->second;
+    return &streams_
+                .emplace(
+                    std::piecewise_construct,
+                    std::forward_as_tuple(LocalStreamId{node_id, stream_id}),
+                    std::forward_as_tuple(node_id, stream_id))
                 .first->second;
   }
 
@@ -203,10 +219,12 @@
 
   std::unordered_map<LocalStreamId, StreamHolder> streams_;
   std::unordered_map<NodeId, LinkHolder> links_;
+  std::mt19937 rng_;
 
   RoutingTable routing_table_;
   Optional<Timeout> poll_link_changes_timeout_;
   Optional<Timeout> flush_old_nodes_timeout_;
+  NodeMetrics own_metrics_;
 };
 
 }  // namespace overnet
diff --git a/lib/overnet/router_test.cc b/lib/overnet/routing/router_test.cc
similarity index 89%
rename from lib/overnet/router_test.cc
rename to lib/overnet/routing/router_test.cc
index c04cfaf..26374d8 100644
--- a/lib/overnet/router_test.cc
+++ b/lib/overnet/routing/router_test.cc
@@ -4,10 +4,10 @@
 
 #include "router.h"
 #include <memory>
+#include "garnet/lib/overnet/testing/test_timer.h"
+#include "garnet/lib/overnet/testing/trace_cout.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "test_timer.h"
-#include "trace_cout.h"
 
 using testing::_;
 using testing::Invoke;
@@ -26,7 +26,7 @@
 class MockStreamHandler : public Router::StreamHandler {
  public:
   MOCK_METHOD3(HandleMessage, void(SeqNum, TimeStamp, Slice));
-  void Close(Callback<void> quiesced) override {}
+  void RouterClose(Callback<void> quiesced) override {}
 };
 
 class MockLink {
@@ -38,8 +38,8 @@
      public:
       LinkInst(MockLink* link, NodeId src, NodeId peer)
           : link_(link),
-            fake_link_metrics_(src, peer, 1, reinterpret_cast<uint64_t>(this)) {
-      }
+            fake_link_metrics_(src, peer, 1, reinterpret_cast<uint64_t>(this),
+                               true) {}
 
       void Close(Callback<void> quiesced) override {}
 
@@ -59,13 +59,17 @@
 
 TEST(Router, NoOp) {
   TestTimer timer;
-  Router router(&timer, TraceCout(&timer), NodeId(1), true);
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
+  Router router(&timer, NodeId(1), true);
 }
 
 // We should be able to forward messages to ourselves.
 TEST(Router, ForwardToSelf) {
   TestTimer timer;
-  Router router(&timer, TraceCout(&timer), NodeId(1), true);
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
+  Router router(&timer, NodeId(1), true);
 
   StrictMock<MockStreamHandler> mock_stream_handler;
 
@@ -91,7 +95,9 @@
 // ready yet.
 TEST(Router, ForwardToSelfDelayed) {
   TestTimer timer;
-  Router router(&timer, TraceCout(&timer), NodeId(1), true);
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
+  Router router(&timer, NodeId(1), true);
 
   StrictMock<MockStreamHandler> mock_stream_handler;
 
@@ -115,7 +121,9 @@
 // We should be able to forward messages to others.
 TEST(Router, ForwardToLink) {
   TestTimer timer;
-  Router router(&timer, TraceCout(&timer), NodeId(1), true);
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
+  Router router(&timer, NodeId(1), true);
 
   StrictMock<MockLink> mock_link;
 
@@ -141,7 +149,9 @@
 // yet.
 TEST(Router, ForwardToLinkDelayed) {
   TestTimer timer;
-  Router router(&timer, TraceCout(&timer), NodeId(1), true);
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
+  Router router(&timer, NodeId(1), true);
 
   StrictMock<MockLink> mock_link;
 
@@ -166,7 +176,9 @@
 // We should be able to multicast messages to ourselves and links.
 TEST(Router, ForwardToSelfAndLink) {
   TestTimer timer;
-  Router router(&timer, TraceCout(&timer), NodeId(1), true);
+  TraceCout trace(&timer);
+  ScopedRenderer scoped_renderer(&trace);
+  Router router(&timer, NodeId(1), true);
 
   StrictMock<MockStreamHandler> mock_stream_handler;
   StrictMock<MockLink> mock_link;
diff --git a/lib/overnet/routing/routing_table.cc b/lib/overnet/routing/routing_table.cc
new file mode 100644
index 0000000..f1cca95
--- /dev/null
+++ b/lib/overnet/routing/routing_table.cc
@@ -0,0 +1,614 @@
+// Copyright 2018 The Fuchsia 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 "routing_table.h"
+#include <iostream>
+#include <unordered_set>
+#include "garnet/lib/overnet/protocol/varint.h"
+
+using overnet::routing_table_impl::FullLinkLabel;
+
+namespace overnet {
+
+namespace {
+
+template <class T>
+void MoveInto(std::vector<T>* from, std::vector<T>* to) {
+  if (to->empty()) {
+    *to = std::move(*from);
+  } else {
+    for (auto& val : *from) {
+      to->emplace_back(std::move(val));
+    }
+  }
+}
+
+}  // namespace
+
+RoutingTable::RoutingTable(NodeId root_node, Timer* timer, bool allow_threading)
+    : root_node_(root_node), timer_(timer), allow_threading_(allow_threading) {}
+
+RoutingTable::~RoutingTable() {
+  std::unique_lock<std::mutex> lock(mu_);
+  if (processing_changes_) {
+    std::thread pending_processing = std::move(*processing_changes_);
+    processing_changes_.Reset();
+    cv_.notify_all();
+    lock.unlock();
+    pending_processing.join();
+  }
+
+  for (auto& n : node_metrics_) {
+    RemoveOutgoingLinks(n.second);
+  }
+}
+
+void RoutingTable::Update(std::vector<NodeMetrics> node_metrics,
+                          std::vector<LinkMetrics> link_metrics,
+                          bool flush_old_nodes) {
+  if (node_metrics.empty() && link_metrics.empty() && !flush_old_nodes)
+    return;
+  std::unique_lock<std::mutex> lock(mu_);
+  last_update_ = timer_->Now();
+  const bool was_empty = change_log_.Empty() && !flush_requested_;
+  if (flush_old_nodes)
+    flush_requested_ = true;
+#ifndef NDEBUG
+  for (const auto& m : node_metrics) {
+    assert(m.version() != 0 || m.created_locally());
+  }
+  for (const auto& m : link_metrics) {
+    assert(m.version() != 0);
+  }
+#endif
+  MoveInto(&node_metrics, &change_log_.node_metrics);
+  MoveInto(&link_metrics, &change_log_.link_metrics);
+  if (!was_empty)
+    return;
+  if (processing_changes_)
+    return;
+  auto process_changes = [this, changes = std::move(change_log_),
+                          flush = flush_requested_, now = last_update_,
+                          renderer = ScopedRenderer::current(),
+                          severity = ScopedSeverity::current()]() mutable {
+    ScopedRenderer scoped_renderer(renderer);
+    ScopedSeverity scoped_severity(severity);
+    while (true) {
+      ApplyChanges(now, changes, flush);
+      SelectedLinks new_selected_links = BuildForwardingTable();
+
+      // Publish changes. If change-log has grown, restart update.
+      std::lock_guard<std::mutex> lock(mu_);
+      if (selected_links_ != new_selected_links) {
+        selected_links_.swap(new_selected_links);
+        selected_links_version_++;
+      }
+      if (!processing_changes_) {
+        // Indicates that the owning RoutingTable instance is in its destruction
+        // sequence.
+        cv_.notify_all();
+        return;
+      } else if (change_log_.Empty() && !flush_requested_) {
+        processing_changes_->detach();
+        processing_changes_.Reset();
+        cv_.notify_all();
+        return;
+      } else {
+        changes = std::move(change_log_);
+        change_log_.Clear();
+        flush = flush_requested_;
+        flush_requested_ = false;
+        now = last_update_;
+      }
+    }
+  };
+  if (allow_threading_) {
+    processing_changes_.Reset(std::move(process_changes));
+    cv_.notify_all();
+  }
+  flush_requested_ = false;
+  change_log_.Clear();
+  if (!allow_threading_) {
+    lock.unlock();
+    process_changes();
+  }
+}
+
+void RoutingTable::ApplyChanges(TimeStamp now, const Metrics& changes,
+                                bool flush) {
+  bool new_gossip_version = false;
+
+  // Update all metrics from changelogs.
+  for (const auto& m : changes.node_metrics) {
+    OVERNET_TRACE(DEBUG) << "APPLY: " << m;
+    if (m.node_id() == root_node_ && !m.created_locally()) {
+      OVERNET_TRACE(WARNING) << "Dropping node update received to self: " << m;
+      continue;
+    }
+    auto it = node_metrics_.find(m.node_id());
+    if (it == node_metrics_.end()) {
+      if (m.version() != METRIC_VERSION_TOMBSTONE) {
+        new_gossip_version = true;
+        node_metrics_.emplace(std::piecewise_construct,
+                              std::forward_as_tuple(m.node_id()),
+                              std::forward_as_tuple(now, m));
+      }
+    } else if (m.version() > it->second.metrics.version()) {
+      new_gossip_version = true;
+      it->second.metrics = m;
+      it->second.last_updated = now;
+    }
+  }
+  for (const auto& m : changes.link_metrics) {
+    OVERNET_TRACE(DEBUG) << "APPLY: " << m;
+    auto report_drop = [&m](const char* why) {
+      OVERNET_TRACE(INFO) << "Drop link info: from=" << m.from()
+                          << " to=" << m.to() << " label=" << m.link_label()
+                          << " version=" << m.version() << ": " << why;
+    };
+    if (m.from() == root_node_ && !m.created_locally()) {
+      OVERNET_TRACE(WARNING)
+          << "Dropping link update for link owned by self: " << m;
+      continue;
+    }
+    // Cannot add a link if the relevant nodes are unknown.
+    auto from_node = node_metrics_.find(m.from());
+    if (from_node == node_metrics_.end()) {
+      report_drop("from node does not exist in routing table");
+      continue;
+    }
+    auto to_node = node_metrics_.find(m.to());
+    if (to_node == node_metrics_.end()) {
+      report_drop("to node does not exist in routing table");
+      continue;
+    }
+
+    // Add the link.
+    const FullLinkLabel key = {m.from(), m.to(), m.link_label()};
+    auto it = link_metrics_.find(key);
+    if (it == link_metrics_.end() && m.version() != METRIC_VERSION_TOMBSTONE) {
+      new_gossip_version = true;
+      it = link_metrics_
+               .emplace(std::piecewise_construct, std::forward_as_tuple(key),
+                        std::forward_as_tuple(now, m, &to_node->second))
+               .first;
+      from_node->second.outgoing_links.PushBack(&it->second);
+    } else if (m.version() > it->second.metrics.version()) {
+      new_gossip_version = true;
+      it->second.metrics = m;
+      it->second.last_updated = now;
+    } else {
+      report_drop("old version");
+      continue;  // Skip keep-alive.
+    }
+
+    // Keep-alive the nodes.
+    from_node->second.last_updated = now;
+    to_node->second.last_updated = now;
+  }
+
+  // Remove anything old if we've been asked to.
+  if (flush) {
+    for (auto it = node_metrics_.begin(); it != node_metrics_.end();) {
+      if (it->first != root_node_ &&
+          it->second.last_updated >= now + EntryExpiry()) {
+        RemoveOutgoingLinks(it->second);
+        it = node_metrics_.erase(it);
+      } else {
+        ++it;
+      }
+    }
+  }
+
+  // Publish out to the application for propagation.
+  if (new_gossip_version) {
+    std::vector<NodeMetrics> publish_node_metrics;
+    std::vector<LinkMetrics> publish_link_metrics;
+    for (const auto& np : node_metrics_) {
+      publish_node_metrics.push_back(np.second.metrics);
+    }
+    for (const auto& lp : link_metrics_) {
+      publish_link_metrics.push_back(lp.second.metrics);
+    }
+    std::lock_guard<std::mutex> mutex(shared_table_mu_);
+    gossip_version_++;
+    shared_node_metrics_.swap(publish_node_metrics);
+    shared_link_metrics_.swap(publish_link_metrics);
+  }
+}
+
+void RoutingTable::RemoveOutgoingLinks(Node& node) {
+  while (Link* link = node.outgoing_links.PopFront()) {
+    link_metrics_.erase(FullLinkLabel{link->metrics.from(), link->metrics.to(),
+                                      link->metrics.link_label()});
+  }
+}
+
+RoutingTable::SelectedLinks RoutingTable::BuildForwardingTable() {
+  OVERNET_TRACE(DEBUG) << "Rebuilding forwarding table";
+  auto node_it = node_metrics_.find(root_node_);
+  if (node_it == node_metrics_.end()) {
+    OVERNET_TRACE(DEBUG) << "No root known";
+    return SelectedLinks();  // Root node as yet unknown.
+  }
+
+  ++path_finding_run_;
+  node_it->second.last_path_finding_run = path_finding_run_;
+  node_it->second.best_rtt = TimeDelta::Zero();
+  node_it->second.mss = std::numeric_limits<uint32_t>::max();
+  InternalList<Node, &Node::path_finding_node> todo;
+
+  auto enqueue = [&todo](Node* node) {
+    if (node->queued)
+      return;
+    node->queued = true;
+    todo.PushBack(node);
+  };
+
+  enqueue(&node_it->second);
+
+  while (!todo.Empty()) {
+    Node* src = todo.PopFront();
+    OVERNET_TRACE(DEBUG) << "VISIT: " << src->metrics;
+    src->queued = false;
+    for (auto link : src->outgoing_links) {
+      OVERNET_TRACE(DEBUG) << "VISIT_LINK: " << link->metrics;
+      if (link->metrics.version() == METRIC_VERSION_TOMBSTONE)
+        continue;
+      TimeDelta rtt =
+          src->best_rtt + src->metrics.forwarding_time() + link->metrics.rtt();
+      Node* dst = link->to_node;
+      // For now we order by RTT.
+      if (dst->last_path_finding_run != path_finding_run_ ||
+          dst->best_rtt > rtt) {
+        dst->last_path_finding_run = path_finding_run_;
+        dst->best_rtt = rtt;
+        dst->best_from = src;
+        dst->best_link = link;
+        dst->mss = std::min(src->mss, link->metrics.mss());
+        enqueue(dst);
+      }
+    }
+  }
+
+  SelectedLinks selected_links;
+
+  for (node_it = node_metrics_.begin(); node_it != node_metrics_.end();
+       ++node_it) {
+    if (node_it->second.last_path_finding_run != path_finding_run_) {
+      continue;  // Unreachable
+    }
+    if (node_it->first == root_node_) {
+      continue;
+    }
+    Node* n = &node_it->second;
+    while (n->best_from->metrics.node_id() != root_node_) {
+      n = n->best_from;
+    }
+    Link* link = n->best_link;
+    assert(link->metrics.from() == root_node_);
+    selected_links[node_it->first] =
+        SelectedLink{link->metrics.link_label(), n->mss};
+  }
+
+  return selected_links;
+}
+
+namespace {
+enum class Key : uint64_t {
+  NODE = 1,
+  LINK = 2,
+  VERSION = 3,
+
+  FORWARDING_TIME_US = 10,
+  DESCRIPTION = 11,
+
+  LABEL = 49,
+  BW_LINK = 50,
+  BW_USED = 51,
+  RTT = 52,
+  MSS = 53,
+};
+}
+
+std::pair<Slice, uint64_t> RoutingTable::Write(
+    Border desired_border, Optional<NodeId> exclude_node) const {
+  struct Segment {
+    Segment(Key key, NodeId id)
+        : key(key),
+          length(id.wire_length()),
+          write([id](uint8_t* p) { id.Write(p); }) {}
+    Segment(Key key, std::pair<NodeId, NodeId> p)
+        : key(key),
+          length(p.first.wire_length() + p.second.wire_length()),
+          write([p](uint8_t* out) { p.second.Write(p.first.Write(out)); }) {}
+    Segment(Key key, uint64_t x)
+        : key(key),
+          length(varint::WireSizeFor(x)),
+          write([x, length = length](uint8_t* p) {
+            varint::Write(x, length, p);
+          }) {}
+    Segment(Key key, Slice value)
+        : key(key),
+          length(value.length()),
+          write([value = std::move(value)](uint8_t* p) {
+            memcpy(p, value.begin(), value.length());
+          }) {}
+    Key key;
+    uint8_t key_length = varint::WireSizeFor(static_cast<uint64_t>(key));
+    uint32_t length;
+    uint8_t length_length = varint::WireSizeFor(length);
+    std::function<void(uint8_t*)> write;
+  };
+
+  std::vector<Segment> segments;
+
+  uint64_t version;
+
+  {
+    std::unordered_set<NodeId> version_zero_nodes;
+
+    std::lock_guard<std::mutex> mutex(shared_table_mu_);
+    version = gossip_version_;
+
+    for (const auto& m : shared_node_metrics_) {
+      if (m.version() == 0) {
+        version_zero_nodes.insert(m.node_id());
+        continue;
+      }
+      if (m.node_id() == exclude_node)
+        continue;
+      OVERNET_TRACE(DEBUG) << "WRITE: " << m;
+      segments.emplace_back(Key::NODE, m.node_id());
+      segments.emplace_back(Key::VERSION, m.version());
+      if (m.forwarding_time() != TimeDelta::PositiveInf()) {
+        segments.emplace_back(Key::FORWARDING_TIME_US,
+                              m.forwarding_time().as_us());
+      }
+      if (m.description().length() != 0) {
+        segments.emplace_back(Key::DESCRIPTION, m.description());
+      }
+    }
+
+    for (const auto& m : shared_link_metrics_) {
+      if (m.from() == exclude_node || version_zero_nodes.count(m.from()) > 0 ||
+          version_zero_nodes.count(m.to()) > 0)
+        continue;
+      OVERNET_TRACE(DEBUG) << "WRITE: " << m;
+      segments.emplace_back(Key::LINK, std::make_pair(m.from(), m.to()));
+      segments.emplace_back(Key::VERSION, m.version());
+      segments.emplace_back(Key::LABEL, m.link_label());
+      if (m.bw_link() != Bandwidth::Zero()) {
+        segments.emplace_back(Key::BW_LINK, m.bw_link().bits_per_second());
+      }
+      if (m.bw_used() != Bandwidth::Zero()) {
+        segments.emplace_back(Key::BW_USED, m.bw_used().bits_per_second());
+      }
+      if (m.rtt() != TimeDelta::PositiveInf()) {
+        segments.emplace_back(Key::RTT, m.rtt().as_us());
+      }
+      if (m.mss() != std::numeric_limits<uint32_t>::max()) {
+        segments.emplace_back(Key::MSS, m.mss());
+      }
+    }
+  }
+
+  size_t slice_length = 0;
+  for (const auto& seg : segments) {
+    slice_length += seg.key_length;
+    slice_length += seg.length_length;
+    slice_length += seg.length;
+  }
+
+  return std::make_pair(
+      Slice::WithInitializerAndBorders(
+          slice_length, desired_border,
+          [&](uint8_t* out) {
+            for (const auto& seg : segments) {
+              out = varint::Write(static_cast<uint64_t>(seg.key),
+                                  seg.key_length, out);
+              out = varint::Write(seg.length, seg.length_length, out);
+              seg.write(out);
+              out += seg.length;
+            }
+          }),
+      version);
+}
+
+Status RoutingTable::Parse(Slice update, std::vector<NodeMetrics>* node_metrics,
+                           std::vector<LinkMetrics>* link_metrics) const {
+  const uint8_t* p = update.begin();
+  const uint8_t* const end = update.end();
+  NodeMetrics* adding_node = nullptr;
+  LinkMetrics* adding_link = nullptr;
+  uint64_t key;
+  uint64_t length;
+  auto read_node = [&](const char* what) -> StatusOr<NodeId> {
+    uint64_t id;
+    if (!ParseLE64(&p, end, &id)) {
+      return StatusOr<NodeId>(StatusCode::FAILED_PRECONDITION, what);
+    }
+    return NodeId(id);
+  };
+  auto read_uint64 = [&](const char* what) -> StatusOr<uint64_t> {
+    uint64_t x;
+    if (!varint::Read(&p, end, &x)) {
+      return StatusOr<uint64_t>(StatusCode::FAILED_PRECONDITION, what);
+    }
+    return x;
+  };
+  auto read_slice = [&](const char* what) -> Slice {
+    auto out = update.FromPointer(p).ToOffset(length);
+    p += length;
+    return out;
+  };
+  auto read_bandwidth = [&](const char* what) -> StatusOr<Bandwidth> {
+    return read_uint64(what).Then([](uint64_t x) {
+      return StatusOr<Bandwidth>(Bandwidth::FromBitsPerSecond(x));
+    });
+  };
+  auto read_time_delta = [&](const char* what) -> StatusOr<TimeDelta> {
+    return read_uint64(what).Then([](uint64_t x) {
+      return StatusOr<TimeDelta>(TimeDelta::FromMicroseconds(x));
+    });
+  };
+  auto read_uint32 = [&](const char* what) -> StatusOr<uint32_t> {
+    return read_uint64(what).Then([](uint64_t x) {
+      if (x > std::numeric_limits<uint32_t>::max()) {
+        return StatusOr<uint32_t>(StatusCode::FAILED_PRECONDITION,
+                                  "Out of range");
+      }
+      return StatusOr<uint32_t>(static_cast<uint32_t>(x));
+    });
+  };
+  auto read_node_pair =
+      [&](const char* what) -> StatusOr<std::pair<NodeId, NodeId>> {
+    return read_node(what).Then([&](NodeId n) {
+      return read_node(what).Then(
+          [n](NodeId m) -> StatusOr<std::pair<NodeId, NodeId>> {
+            return std::make_pair(n, m);
+          });
+    });
+  };
+  auto with_link = [&adding_link](auto setter) {
+    return [&adding_link, setter](auto x) {
+      if (!adding_link)
+        return Status(StatusCode::FAILED_PRECONDITION,
+                      "Property only on links");
+      (adding_link->*setter)(x);
+      return Status::Ok();
+    };
+  };
+  auto with_node = [&adding_node](auto setter) {
+    return [&adding_node, setter](auto x) {
+      if (!adding_node)
+        return Status(StatusCode::FAILED_PRECONDITION,
+                      "Property only on nodes");
+      (adding_node->*setter)(x);
+      return Status::Ok();
+    };
+  };
+  while (p != end) {
+    if (!varint::Read(&p, end, &key) || !varint::Read(&p, end, &length)) {
+      return Status(StatusCode::FAILED_PRECONDITION,
+                    "Failed to read segment header");
+    }
+    if (uint64_t(end - p) < length) {
+      return Status(StatusCode::FAILED_PRECONDITION, "Short segment");
+    }
+    Status status = Status::Ok();
+    const uint8_t* const next = p + length;
+    switch (static_cast<Key>(key)) {
+      case Key::NODE:
+        status = read_node("Node::Id").Then([&](NodeId n) {
+          adding_link = nullptr;
+          node_metrics->emplace_back();
+          adding_node = &node_metrics->back();
+          adding_node->set_node_id(n);
+          return Status::Ok();
+        });
+        break;
+      case Key::LINK:
+        status = read_node_pair("Link::Id").Then([&](auto p) {
+          adding_node = nullptr;
+          link_metrics->emplace_back();
+          adding_link = &link_metrics->back();
+          adding_link->set_from_to(p.first, p.second);
+          return Status::Ok();
+        });
+        break;
+      case Key::VERSION:
+        status = read_uint64("Version").Then([&](uint64_t v) {
+          if (v == 0) {
+            return Status(StatusCode::INVALID_ARGUMENT,
+                          "Zero version number is not allowed");
+          }
+          if (adding_link)
+            adding_link->set_version(v);
+          if (adding_node)
+            adding_node->set_version(v);
+          return Status::Ok();
+        });
+        break;
+      case Key::FORWARDING_TIME_US:
+        status = read_time_delta("ForwardingTime")
+                     .Then(with_node(&NodeMetrics::set_forwarding_time));
+        break;
+      case Key::DESCRIPTION:
+        status =
+            with_node(&NodeMetrics::set_description)(read_slice("Description"));
+        break;
+      case Key::LABEL:
+        status =
+            read_uint64("Label").Then(with_link(&LinkMetrics::set_link_label));
+        break;
+      case Key::BW_LINK:
+        status =
+            read_bandwidth("BWLink").Then(with_link(&LinkMetrics::set_bw_link));
+        break;
+      case Key::BW_USED:
+        status =
+            read_bandwidth("BWUsed").Then(with_link(&LinkMetrics::set_bw_link));
+        break;
+      case Key::RTT:
+        status = read_time_delta("RTT").Then(with_link(&LinkMetrics::set_rtt));
+        break;
+      case Key::MSS:
+        status = read_uint32("MSS").Then(with_link(&LinkMetrics::set_mss));
+        break;
+      default:
+        // Unknown field: skip it.
+        OVERNET_TRACE(DEBUG) << "Skipping unknown routing table key " << key;
+        p = next;
+        break;
+    }
+    if (status.is_error()) {
+      return status;
+    }
+    if (p != next) {
+      return Status(StatusCode::FAILED_PRECONDITION,
+                    "Length mismatch reading segment");
+    }
+    p = next;
+  }
+  // Verify everythings ok
+  for (const auto& m : *node_metrics) {
+    if (m.version() == 0) {
+      return Status(StatusCode::INVALID_ARGUMENT,
+                    "Bad node metric: no version number");
+    }
+  }
+  for (const auto& m : *link_metrics) {
+    if (m.version() == 0) {
+      return Status(StatusCode::INVALID_ARGUMENT,
+                    "Bad link metric: no version number");
+    }
+  }
+  return Status::Ok();
+}
+
+Status RoutingTable::Read(Slice update) {
+  OVERNET_TRACE(DEBUG) << "READ: " << update;
+  std::vector<NodeMetrics> node_metrics;
+  std::vector<LinkMetrics> link_metrics;
+  return Parse(std::move(update), &node_metrics, &link_metrics).Then([&] {
+    Update(std::move(node_metrics), std::move(link_metrics), true);
+    return Status::Ok();
+  });
+}
+
+std::ostream& operator<<(std::ostream& out, const LinkMetrics& m) {
+  return out << "LINK{" << m.from() << "->" << m.to() << "/" << m.link_label()
+             << " @ " << m.version() << "; bw_link=" << m.bw_link()
+             << ", bw_used=" << m.bw_used() << ", rtt=" << m.rtt()
+             << ", mss=" << m.mss() << "}";
+}
+
+std::ostream& operator<<(std::ostream& out, const NodeMetrics& m) {
+  return out << "NODE{" << m.node_id() << " @ " << m.version()
+             << "; forwarding_time=" << m.forwarding_time()
+             << ", description=" << m.description() << "}";
+}
+
+}  // namespace overnet
diff --git a/lib/overnet/routing_table.h b/lib/overnet/routing/routing_table.h
similarity index 65%
rename from lib/overnet/routing_table.h
rename to lib/overnet/routing/routing_table.h
index 16662c2..cad33dc 100644
--- a/lib/overnet/routing_table.h
+++ b/lib/overnet/routing/routing_table.h
@@ -10,12 +10,13 @@
 #include <tuple>
 #include <unordered_map>
 #include <vector>
-#include "bandwidth.h"
-#include "internal_list.h"
-#include "node_id.h"
-#include "optional.h"
-#include "timer.h"
-#include "trace.h"
+#include "garnet/lib/overnet/environment/timer.h"
+#include "garnet/lib/overnet/environment/trace.h"
+#include "garnet/lib/overnet/labels/node_id.h"
+#include "garnet/lib/overnet/vocabulary/bandwidth.h"
+#include "garnet/lib/overnet/vocabulary/internal_list.h"
+#include "garnet/lib/overnet/vocabulary/optional.h"
+#include "garnet/lib/overnet/vocabulary/slice.h"
 
 #include <iostream>
 
@@ -47,19 +48,18 @@
 
 namespace overnet {
 
-enum class LinkMedia : uint8_t {
-  Unknown,
-  Wired,
-  Wireless,
-  Internet,
-};
-
 static constexpr uint64_t METRIC_VERSION_TOMBSTONE = ~uint64_t(0);
 
 class LinkMetrics {
  public:
-  LinkMetrics(NodeId from, NodeId to, uint64_t version, uint64_t link_label)
-      : from_(from), to_(to), version_(version), link_label_(link_label) {}
+  LinkMetrics() = default;
+  LinkMetrics(NodeId from, NodeId to, uint64_t version, uint64_t link_label,
+              bool created_locally)
+      : created_locally_(created_locally),
+        from_(from),
+        to_(to),
+        version_(version),
+        link_label_(link_label) {}
 
   NodeId from() const { return from_; }
   NodeId to() const { return to_; }
@@ -68,24 +68,29 @@
   Bandwidth bw_link() const { return bw_link_; }
   Bandwidth bw_used() const { return bw_used_; }
   TimeDelta rtt() const { return rtt_; }
-  LinkMedia media() const { return media_; }
   uint32_t mss() const { return mss_; }
+  bool created_locally() const { return created_locally_; }
 
+  void set_from_to(NodeId from, NodeId to) {
+    from_ = from;
+    to_ = to;
+  }
+  void set_link_label(uint64_t label) { link_label_ = label; }
+  void set_version(uint64_t version) { version_ = version; }
   void set_bw_link(Bandwidth x) { bw_link_ = x; }
   void set_bw_used(Bandwidth x) { bw_used_ = x; }
   void set_rtt(TimeDelta x) { rtt_ = x; }
-  void set_media(LinkMedia x) { media_ = x; }
   void set_mss(uint32_t x) { mss_ = x; }
 
  private:
-  NodeId from_;
-  NodeId to_;
-  uint64_t version_;
-  uint64_t link_label_;
+  bool created_locally_ = false;
+  NodeId from_{0};
+  NodeId to_{0};
+  uint64_t version_{0};
+  uint64_t link_label_{0};
   Bandwidth bw_link_{Bandwidth::Zero()};
   Bandwidth bw_used_{Bandwidth::Zero()};
   TimeDelta rtt_{TimeDelta::PositiveInf()};
-  LinkMedia media_{LinkMedia::Unknown};
   uint32_t mss_{std::numeric_limits<uint32_t>::max()};
 };
 
@@ -93,37 +98,46 @@
 
 class NodeMetrics {
  public:
-  NodeMetrics(NodeId node_id, uint64_t version)
-      : node_id_(node_id), version_(version) {}
+  NodeMetrics() = default;
+  NodeMetrics(NodeId node_id, uint64_t version, bool created_locally)
+      : created_locally_(created_locally),
+        node_id_(node_id),
+        version_(version) {}
 
   NodeId node_id() const { return node_id_; }
   uint64_t version() const { return version_; }
-  uint8_t battery_pct() const { return battery_pct_; }
   TimeDelta forwarding_time() const { return forwarding_time_; }
+  const Slice& description() const { return description_; }
 
-  void set_battery_pct(uint8_t battery_pct) { battery_pct_ = battery_pct; }
+  void set_node_id(NodeId node_id) { node_id_ = node_id; }
+  void set_version(uint64_t version) { version_ = version; }
+  void IncrementVersion() { version_++; }
   void set_forwarding_time(TimeDelta forwarding_time) {
     forwarding_time_ = forwarding_time;
   }
+  void set_description(Slice description) {
+    description_ = std::move(description);
+  }
+  bool created_locally() const { return created_locally_; }
+
+  auto value_parts() const { return std::tie(forwarding_time_, description_); }
 
  private:
-  NodeId node_id_;
-  uint64_t version_;
-  uint8_t battery_pct_ = 100;
+  bool created_locally_ = false;
+  NodeId node_id_{0};
+  uint64_t version_{0};
   TimeDelta forwarding_time_{TimeDelta::PositiveInf()};
+  Slice description_;
 };
 
-std::ostream& operator<<(std::ostream& out, const LinkMetrics& m);
+std::ostream& operator<<(std::ostream& out, const NodeMetrics& m);
 
 class RoutingTable {
  public:
-  RoutingTable(NodeId root_node, Timer* timer, TraceSink trace_sink,
-               bool allow_threading)
-      : root_node_(root_node),
-        timer_(timer),
-        trace_sink_(trace_sink),
-        allow_threading_(allow_threading) {}
+  RoutingTable(NodeId root_node, Timer* timer, bool allow_threading);
   ~RoutingTable();
+  RoutingTable(const RoutingTable&) = delete;
+  RoutingTable& operator=(const RoutingTable&) = delete;
 
   static constexpr TimeDelta EntryExpiry() { return TimeDelta::FromMinutes(5); }
 
@@ -161,10 +175,28 @@
     cv_.wait(lock, [this]() -> bool { return !processing_changes_; });
   }
 
+  uint64_t gossip_version() const {
+    std::lock_guard<std::mutex> lock(shared_table_mu_);
+    return gossip_version_;
+  }
+  std::pair<Slice, uint64_t> Write(Border desired_border,
+                                   Optional<NodeId> exclude_node) const;
+  Status Parse(Slice update, std::vector<NodeMetrics>* node_metrics,
+               std::vector<LinkMetrics>* link_metrics) const;
+  Status Read(Slice update);
+
+  template <class F>
+  void ForEachNodeMetric(F visitor) const {
+    std::vector<NodeMetrics> nodes_copy =
+        (std::lock_guard{shared_table_mu_}, shared_node_metrics_);
+    for (const auto& m : nodes_copy) {
+      visitor(m);
+    }
+  }
+
  private:
   const NodeId root_node_;
   Timer* const timer_;
-  const TraceSink trace_sink_;
 
   struct Metrics {
     std::vector<NodeMetrics> node_metrics;
@@ -222,6 +254,11 @@
   std::unordered_map<NodeId, Node> node_metrics_;
   std::unordered_map<routing_table_impl::FullLinkLabel, Link> link_metrics_;
 
+  mutable std::mutex shared_table_mu_;
+  uint64_t gossip_version_ = 0;
+  std::vector<NodeMetrics> shared_node_metrics_;
+  std::vector<LinkMetrics> shared_link_metrics_;
+
   uint64_t selected_links_version_ = 0;
   SelectedLinks selected_links_;
   uint64_t published_links_version_ = 0;
diff --git a/lib/overnet/routing_table.cc b/lib/overnet/routing_table.cc
deleted file mode 100644
index 1f10711..0000000
--- a/lib/overnet/routing_table.cc
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright 2018 The Fuchsia 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 "routing_table.h"
-#include <iostream>
-
-using overnet::routing_table_impl::FullLinkLabel;
-
-namespace overnet {
-
-namespace {
-
-template <class T>
-void MoveInto(std::vector<T>* from, std::vector<T>* to) {
-  if (to->empty()) {
-    *to = std::move(*from);
-  } else {
-    for (auto& val : *from) {
-      to->emplace_back(std::move(val));
-    }
-  }
-}
-
-}  // namespace
-
-RoutingTable::~RoutingTable() {
-  std::unique_lock<std::mutex> lock(mu_);
-  if (processing_changes_) {
-    std::thread pending_processing = std::move(*processing_changes_);
-    processing_changes_.Reset();
-    cv_.notify_all();
-    lock.unlock();
-    pending_processing.join();
-  }
-
-  for (auto& n : node_metrics_) {
-    RemoveOutgoingLinks(n.second);
-  }
-}
-
-void RoutingTable::Update(std::vector<NodeMetrics> node_metrics,
-                          std::vector<LinkMetrics> link_metrics,
-                          bool flush_old_nodes) {
-  if (node_metrics.empty() && link_metrics.empty() && !flush_old_nodes)
-    return;
-  std::unique_lock<std::mutex> lock(mu_);
-  last_update_ = timer_->Now();
-  const bool was_empty = change_log_.Empty() && !flush_requested_;
-  if (flush_old_nodes)
-    flush_requested_ = true;
-  MoveInto(&node_metrics, &change_log_.node_metrics);
-  MoveInto(&link_metrics, &change_log_.link_metrics);
-  if (!was_empty)
-    return;
-  if (processing_changes_)
-    return;
-  auto process_changes = [this, changes = std::move(change_log_),
-                          flush = flush_requested_,
-                          now = last_update_]() mutable {
-    while (true) {
-      ApplyChanges(now, changes, flush);
-      SelectedLinks new_selected_links = BuildForwardingTable();
-
-      // Publish changes. If change-log has grown, restart update.
-      std::lock_guard<std::mutex> lock(mu_);
-      if (selected_links_ != new_selected_links) {
-        selected_links_.swap(new_selected_links);
-        selected_links_version_++;
-      }
-      if (!processing_changes_) {
-        // Indicates that the owning RoutingTable instance is in its destruction
-        // sequence.
-        cv_.notify_all();
-        return;
-      } else if (change_log_.Empty() && !flush_requested_) {
-        processing_changes_->detach();
-        processing_changes_.Reset();
-        cv_.notify_all();
-        return;
-      } else {
-        changes = std::move(change_log_);
-        change_log_.Clear();
-        flush = flush_requested_;
-        flush_requested_ = false;
-        now = last_update_;
-      }
-    }
-  };
-  if (allow_threading_) {
-    processing_changes_.Reset(std::move(process_changes));
-    cv_.notify_all();
-  }
-  flush_requested_ = false;
-  change_log_.Clear();
-  if (!allow_threading_) {
-    lock.unlock();
-    process_changes();
-  }
-}
-
-void RoutingTable::ApplyChanges(TimeStamp now, const Metrics& changes,
-                                bool flush) {
-  // Update all metrics from changelogs.
-  for (const auto& m : changes.node_metrics) {
-    auto it = node_metrics_.find(m.node_id());
-    if (it == node_metrics_.end()) {
-      node_metrics_.emplace(std::piecewise_construct,
-                            std::forward_as_tuple(m.node_id()),
-                            std::forward_as_tuple(now, m));
-    } else if (m.version() > it->second.metrics.version()) {
-      it->second.metrics = m;
-      it->second.last_updated = now;
-    }
-  }
-  for (const auto& m : changes.link_metrics) {
-    auto report_drop = [this, &m](const char* why) {
-      OVERNET_TRACE(INFO, trace_sink_)
-          << "Drop link info: from=" << m.from() << " to=" << m.to()
-          << " label=" << m.link_label() << " version=" << m.version() << ": "
-          << why << std::endl;
-    };
-    // Cannot add a link if the relevant nodes are unknown.
-    auto from_node = node_metrics_.find(m.from());
-    if (from_node == node_metrics_.end()) {
-      report_drop("from node does not exist in routing table");
-      continue;
-    }
-    auto to_node = node_metrics_.find(m.to());
-    if (to_node == node_metrics_.end()) {
-      report_drop("to node does not exist in routing table");
-      continue;
-    }
-
-    // Keep-alive the nodes.
-    from_node->second.last_updated = now;
-    to_node->second.last_updated = now;
-
-    // Add the link.
-    const FullLinkLabel key = {m.from(), m.to(), m.link_label()};
-    auto it = link_metrics_.find(key);
-    if (it == link_metrics_.end()) {
-      it = link_metrics_
-               .emplace(std::piecewise_construct, std::forward_as_tuple(key),
-                        std::forward_as_tuple(now, m, &to_node->second))
-               .first;
-      from_node->second.outgoing_links.PushBack(&it->second);
-    } else if (m.version() > it->second.metrics.version()) {
-      it->second.metrics = m;
-      it->second.last_updated = now;
-    } else {
-      report_drop("old version");
-    }
-  }
-
-  // Remove anything old if we've been asked to.
-  if (flush) {
-    for (auto it = node_metrics_.begin(); it != node_metrics_.end();) {
-      if (it->first == root_node_)
-        continue;
-      if (it->second.last_updated >= now + EntryExpiry()) {
-        RemoveOutgoingLinks(it->second);
-        it = node_metrics_.erase(it);
-      } else {
-        ++it;
-      }
-    }
-  }
-}
-
-void RoutingTable::RemoveOutgoingLinks(Node& node) {
-  while (Link* link = node.outgoing_links.PopFront()) {
-    link_metrics_.erase(FullLinkLabel{link->metrics.from(), link->metrics.to(),
-                                      link->metrics.link_label()});
-  }
-}
-
-RoutingTable::SelectedLinks RoutingTable::BuildForwardingTable() {
-  auto node_it = node_metrics_.find(root_node_);
-  if (node_it == node_metrics_.end()) {
-    return SelectedLinks();  // Root node as yet unknown.
-  }
-
-  ++path_finding_run_;
-  node_it->second.last_path_finding_run = path_finding_run_;
-  node_it->second.best_rtt = TimeDelta::Zero();
-  node_it->second.mss = std::numeric_limits<uint32_t>::max();
-  InternalList<Node, &Node::path_finding_node> todo;
-
-  auto enqueue = [&todo](Node* node) {
-    if (node->queued)
-      return;
-    node->queued = true;
-    todo.PushBack(node);
-  };
-
-  enqueue(&node_it->second);
-
-  while (!todo.Empty()) {
-    Node* src = todo.PopFront();
-    src->queued = false;
-    for (auto link : src->outgoing_links) {
-      if (link->metrics.version() == METRIC_VERSION_TOMBSTONE)
-        continue;
-      TimeDelta rtt =
-          src->best_rtt + src->metrics.forwarding_time() + link->metrics.rtt();
-      Node* dst = link->to_node;
-      // For now we order by RTT.
-      if (dst->last_path_finding_run != path_finding_run_ ||
-          dst->best_rtt > rtt) {
-        dst->last_path_finding_run = path_finding_run_;
-        dst->best_rtt = rtt;
-        dst->best_from = src;
-        dst->best_link = link;
-        dst->mss = std::min(src->mss, link->metrics.mss());
-        enqueue(dst);
-      }
-    }
-  }
-
-  SelectedLinks selected_links;
-
-  for (node_it = node_metrics_.begin(); node_it != node_metrics_.end();
-       ++node_it) {
-    if (node_it->second.last_path_finding_run != path_finding_run_) {
-      continue;  // Unreachable
-    }
-    if (node_it->first == root_node_) {
-      continue;
-    }
-    Node* n = &node_it->second;
-    while (n->best_from->metrics.node_id() != root_node_) {
-      n = n->best_from;
-    }
-    Link* link = n->best_link;
-    assert(link->metrics.from() == root_node_);
-    selected_links[node_it->first] =
-        SelectedLink{link->metrics.link_label(), n->mss};
-  }
-
-  return selected_links;
-}
-
-}  // namespace overnet
diff --git a/lib/overnet/scripts/summarize_beg_end.py b/lib/overnet/scripts/summarize_beg_end.py
new file mode 100755
index 0000000..803a39f
--- /dev/null
+++ b/lib/overnet/scripts/summarize_beg_end.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+import sys
+
+begun = set()
+
+for line in open(sys.argv[1]):
+    m = re.search(
+        r"(BEG|END) ([^ ]+) ([^ ]+).*packet_protocol:([^ \n]+)", line)
+    if not m:
+        continue
+    tag = "%s:%s:%s" % (m.group(2), m.group(3), m.group(4))
+    if m.group(1) == "BEG":
+        assert tag not in begun
+        begun.add(tag)
+    else:
+        assert tag in begun
+        begun.remove(tag)
+
+print begun
diff --git a/lib/overnet/scripts/trace-linearizer-speed.py b/lib/overnet/scripts/trace-linearizer-speed.py
new file mode 100755
index 0000000..501a8d4
--- /dev/null
+++ b/lib/overnet/scripts/trace-linearizer-speed.py
@@ -0,0 +1,13 @@
+#!/usr/bin/python
+
+import re
+import sys
+
+for line in open(sys.argv[1]):
+  m = re.match(r'^D@([0-9.]+m?)s .* linearizer.*OFFSET:([0-9]+) .*' + sys.argv[2], line)
+  if m:
+    t_str = m.group(1)
+    t = float(t_str[:-1])/1000 if t_str[-1]=='m' else float(t_str)
+    b = int(m.group(2))
+    print t, b, b*8.0/1000/1000 / t
+
diff --git a/lib/overnet/sink.h b/lib/overnet/sink.h
deleted file mode 100644
index 78bca7e..0000000
--- a/lib/overnet/sink.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#pragma once
-
-#include <vector>
-#include "callback.h"
-#include "optional.h"
-#include "status.h"
-
-namespace overnet {
-
-// A typed data sink for some kind of object
-template <class T>
-class Sink {
- public:
-  virtual ~Sink() {}
-  virtual void Close(const Status& status, Callback<void> quiesced) = 0;
-  virtual void Push(T item) = 0;
-  void PushMany(T* items, size_t count);
-};
-
-template <class T>
-class Source {
- public:
-  virtual ~Source() {}
-  virtual void Close(const Status& status) = 0;
-  virtual void Pull(StatusOrCallback<Optional<T>> ready) = 0;
-  // Default implementation of a draining-pull.
-  // Derived types are encouraged but not required to replace this.
-  // Implies a Close() call.
-  virtual void PullAll(StatusOrCallback<std::vector<T>> ready);
-};
-
-template <class T>
-void Sink<T>::PushMany(T* items, size_t count) {
-  for (size_t i = 0; i < count; i++) {
-    Push(items[i]);
-  }
-}
-
-template <class T>
-void Source<T>::PullAll(StatusOrCallback<std::vector<T>> ready) {
-  class Puller {
-   public:
-    Puller(Source* source, StatusOrCallback<std::vector<T>> ready)
-        : source_(source), ready_(std::move(ready)) {
-      Next();
-    }
-
-   private:
-    void Next() {
-      source_->Pull(
-          StatusOrCallback<Optional<T>>([this](StatusOr<Optional<T>>&& status) {
-            if (status.is_error() || !status->has_value()) {
-              source_->Close(status.AsStatus());
-              ready_(std::move(so_far_));
-              delete this;
-              return;
-            }
-            so_far_.emplace_back(std::move(**status.get()));
-            Next();
-          }));
-    }
-
-    Source* const source_;
-    std::vector<T> so_far_;
-    StatusOrCallback<std::vector<T>> ready_;
-  };
-  new Puller(this, std::move(ready));
-}
-
-}  // namespace overnet
diff --git a/lib/overnet/sink_test.cc b/lib/overnet/sink_test.cc
deleted file mode 100644
index 58cd5d2..0000000
--- a/lib/overnet/sink_test.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2018 The Fuchsia 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 "sink.h"
-#include <functional>
-#include <memory>
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-using testing::_;
-using testing::InSequence;
-using testing::Invoke;
-using testing::InvokeArgument;
-using testing::Property;
-using testing::StrictMock;
-
-namespace overnet {
-namespace sink_test {
-
-class MockCallbacks {
- public:
-  MOCK_METHOD1(PullDone, void(const StatusOr<int>&));
-
-  StatusOrCallback<int> NewPull() {
-    return StatusOrCallback<int>(
-        ALLOCATED_CALLBACK,
-        [this](const StatusOr<int>& status) { this->PullDone(status); });
-  }
-};
-
-class MockSink : public Sink<int> {
- public:
-  MOCK_METHOD1(Push, void(int item));
-  MOCK_METHOD2(Closed, void(const Status&, std::function<void()> quiesced));
-  // Since gmock has a hard time with move-only types, we provide this override
-  // directly, and use Closed as the mock method (which takes a function that
-  // wraps done).
-  void Close(const Status& status, Callback<void> done) override {
-    auto done_ptr = std::make_shared<Callback<void>>(std::move(done));
-    this->Closed(status, [done_ptr]() { (*done_ptr)(); });
-  }
-};
-
-class MockSource : public Source<int> {
- public:
-  MOCK_METHOD1(Close, void(const Status&));
-  MOCK_METHOD1(Pulled,
-               void(std::function<void(const StatusOr<Optional<int>>&)> done));
-  // Since gmock has a hard time with move-only types, we provide this override
-  // directly, and use Pushed as the mock method (which takes a function that
-  // wraps done).
-  void Pull(StatusOrCallback<Optional<int>> done) override {
-    auto done_ptr =
-        std::make_shared<StatusOrCallback<Optional<int>>>(std::move(done));
-    this->Pulled([done_ptr](const StatusOr<Optional<int>>& status) {
-      (*done_ptr)(status);
-    });
-  }
-};
-
-TEST(Sink, PushManyHappy) {
-  StrictMock<MockSink> sink;
-
-  int to_push[] = {1, 2, 3};
-  EXPECT_CALL(sink, Push(1));
-  EXPECT_CALL(sink, Push(2));
-  EXPECT_CALL(sink, Push(3));
-  sink.PushMany(to_push, sizeof(to_push) / sizeof(*to_push));
-}
-
-TEST(Source, PullAllHappy) {
-  StrictMock<MockCallbacks> cb;
-  StrictMock<MockSource> source;
-
-  {
-    InSequence in_sequence;
-    EXPECT_CALL(source, Pulled(_)).WillOnce(InvokeArgument<0>(1));
-    EXPECT_CALL(source, Pulled(_)).WillOnce(InvokeArgument<0>(2));
-    EXPECT_CALL(source, Pulled(_)).WillOnce(InvokeArgument<0>(3));
-    EXPECT_CALL(source, Pulled(_)).WillOnce(InvokeArgument<0>(Optional<int>()));
-  }
-  EXPECT_CALL(source, Close(Property(&Status::is_ok, true)));
-  source.PullAll([](StatusOr<std::vector<int>> status) {
-    ASSERT_TRUE(status.is_ok());
-    EXPECT_EQ(*status, (std::vector<int>{1, 2, 3}));
-  });
-}
-
-TEST(Source, PullAllFail) {
-  StrictMock<MockCallbacks> cb;
-  StrictMock<MockSource> source;
-
-  {
-    InSequence in_sequence;
-    EXPECT_CALL(source, Pulled(_)).WillOnce(InvokeArgument<0>(1));
-    EXPECT_CALL(source, Pulled(_)).WillOnce(InvokeArgument<0>(2));
-    EXPECT_CALL(source, Pulled(_)).WillOnce(InvokeArgument<0>(3));
-    EXPECT_CALL(source, Pulled(_))
-        .WillOnce(InvokeArgument<0>(Status::Cancelled()));
-  }
-  EXPECT_CALL(source, Close(Property(&Status::code, StatusCode::CANCELLED)));
-  source.PullAll([](StatusOr<std::vector<int>> status) {
-    ASSERT_TRUE(status.is_ok());
-    EXPECT_EQ(*status, (std::vector<int>{1, 2, 3}));
-  });
-}
-
-}  // namespace sink_test
-}  // namespace overnet
diff --git a/lib/overnet/slice.cc b/lib/overnet/slice.cc
deleted file mode 100644
index 70cd007..0000000
--- a/lib/overnet/slice.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2018 The Fuchsia 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 "slice.h"
-#include <iomanip>
-#include <sstream>
-
-namespace overnet {
-
-std::ostream& operator<<(std::ostream& out, const Slice& slice) {
-  bool first = true;
-  std::ostringstream temp;
-  for (auto b : slice) {
-    if (!first)
-      temp << ' ';
-    temp << std::hex << std::setfill('0') << std::setw(2)
-         << static_cast<unsigned>(b);
-    first = false;
-  }
-  if (!first)
-    temp << ' ';
-  temp << '"';
-  for (auto b : slice) {
-    const auto c = static_cast<char>(b);
-    if (isprint(c)) {
-      temp << c;
-    } else {
-      temp << '.';
-    }
-  }
-  temp << '"';
-  return out << '[' << temp.str() << ']';
-}
-
-std::ostream& operator<<(std::ostream& out, const Chunk& chunk) {
-  return out << "@" << chunk.offset << chunk.slice;
-}
-
-}  // namespace overnet
diff --git a/lib/overnet/testing/BUILD.gn b/lib/overnet/testing/BUILD.gn
new file mode 100644
index 0000000..26d527e
--- /dev/null
+++ b/lib/overnet/testing/BUILD.gn
@@ -0,0 +1,63 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("tests") {
+  testonly = true
+  deps = [
+    ":test_timer_test",
+  ]
+}
+
+source_set("run_all_tests") {
+  testonly = true
+  sources = [
+    "run_all_tests.cc",
+  ]
+  deps = [
+    "//third_party/googletest:gtest",
+  ]
+}
+
+source_set("csv_writer") {
+  testonly = true
+  sources = [
+    "csv_writer.h",
+  ]
+}
+
+source_set("test_timer") {
+  testonly = true
+  sources = [
+    "test_timer.cc",
+    "test_timer.h",
+  ]
+  deps = [
+    "//garnet/lib/overnet/environment:timer",
+    "//garnet/lib/overnet/vocabulary:optional",
+  ]
+}
+
+source_set("test_timer_test") {
+  testonly = true
+  sources = [
+    "test_timer_test.cc",
+  ]
+  deps = [
+    ":test_timer",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+source_set("trace_cout") {
+  testonly = true
+  sources = [
+    "trace_cout.cc",
+    "trace_cout.h",
+  ]
+  deps = [
+    "//garnet/lib/overnet/environment:timer",
+    "//garnet/lib/overnet/environment:trace",
+  ]
+}
diff --git a/lib/overnet/csv_writer.h b/lib/overnet/testing/csv_writer.h
similarity index 100%
rename from lib/overnet/csv_writer.h
rename to lib/overnet/testing/csv_writer.h
diff --git a/lib/overnet/run_all_tests.cc b/lib/overnet/testing/run_all_tests.cc
similarity index 100%
rename from lib/overnet/run_all_tests.cc
rename to lib/overnet/testing/run_all_tests.cc
diff --git a/lib/overnet/test_timer.cc b/lib/overnet/testing/test_timer.cc
similarity index 65%
rename from lib/overnet/test_timer.cc
rename to lib/overnet/testing/test_timer.cc
index af8e813..f34e9e7 100644
--- a/lib/overnet/test_timer.cc
+++ b/lib/overnet/testing/test_timer.cc
@@ -3,9 +3,12 @@
 // found in the LICENSE file.
 
 #include "test_timer.h"
+#include <mutex>
 
 namespace overnet {
 
+static std::mutex mu;
+
 TestTimer::~TestTimer() {
   shutting_down_ = true;
   for (auto tmr : pending_timeouts_) {
@@ -15,26 +18,31 @@
 }
 
 TimeStamp TestTimer::Now() {
+  std::lock_guard<std::mutex> lock(mu);
   if (shutting_down_)
     return TimeStamp::AfterEpoch(TimeDelta::PositiveInf());
   return TimeStamp::AfterEpoch(TimeDelta::FromMicroseconds(now_));
 }
 
 bool TestTimer::Step(uint64_t microseconds) {
-  if (shutting_down_)
-    return false;
-  auto new_now = now_ + microseconds;
-  if (now_ > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
-    return false;
+  {
+    std::lock_guard<std::mutex> lock(mu);
+    if (shutting_down_)
+      return false;
+    auto new_now = now_ + microseconds;
+    if (now_ > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
+      return false;
+    }
+    if (TimeStamp::AfterEpoch(TimeDelta::FromMicroseconds(new_now)) <
+        TimeStamp::AfterEpoch(TimeDelta::FromMicroseconds(now_))) {
+      return false;
+    }
+    now_ = new_now;
   }
-  if (TimeStamp::AfterEpoch(TimeDelta::FromMicroseconds(new_now)) <
-      TimeStamp::AfterEpoch(TimeDelta::FromMicroseconds(now_))) {
-    return false;
-  }
-  now_ = new_now;
   bool ticked = false;
   while (!pending_timeouts_.empty() &&
-         pending_timeouts_.begin()->first <= now_) {
+         pending_timeouts_.begin()->first <=
+             (std::lock_guard<std::mutex>(mu), now_)) {
     ticked = true;
     auto it = pending_timeouts_.begin();
     auto* timeout = it->second;
@@ -47,7 +55,8 @@
 bool TestTimer::StepUntilNextEvent(Optional<TimeDelta> max_step) {
   if (pending_timeouts_.empty())
     return false;
-  int64_t step = pending_timeouts_.begin()->first - now_;
+  int64_t step = (std::lock_guard<std::mutex>(mu),
+                  pending_timeouts_.begin()->first - now_);
   if (max_step.has_value() && step > max_step->as_us()) {
     step = max_step->as_us();
   }
@@ -55,8 +64,12 @@
 }
 
 void TestTimer::InitTimeout(Timeout* timeout, TimeStamp when) {
-  if (shutting_down_ ||
-      when.after_epoch().as_us() <= static_cast<int64_t>(now_)) {
+  if (shutting_down_) {
+    FireTimeout(timeout, Status::Cancelled());
+    return;
+  }
+  if (when.after_epoch().as_us() <=
+      (std::lock_guard<std::mutex>(mu), static_cast<int64_t>(now_))) {
     FireTimeout(timeout, Status::Ok());
     return;
   }
diff --git a/lib/overnet/test_timer.h b/lib/overnet/testing/test_timer.h
similarity index 84%
rename from lib/overnet/test_timer.h
rename to lib/overnet/testing/test_timer.h
index 82991b7..43284b3 100644
--- a/lib/overnet/test_timer.h
+++ b/lib/overnet/testing/test_timer.h
@@ -5,13 +5,13 @@
 #pragma once
 
 #include <map>
-#include "optional.h"
-#include "timer.h"
+#include "garnet/lib/overnet/environment/timer.h"
+#include "garnet/lib/overnet/vocabulary/optional.h"
 
 namespace overnet {
 
 // A timer for testing purposes
-class TestTimer final : public Timer {
+class TestTimer : public Timer {
  public:
   TestTimer(uint64_t start = 0) : now_(start) {}
   ~TestTimer();
diff --git a/lib/overnet/test_timer_test.cc b/lib/overnet/testing/test_timer_test.cc
similarity index 100%
rename from lib/overnet/test_timer_test.cc
rename to lib/overnet/testing/test_timer_test.cc
diff --git a/lib/overnet/testing/trace_cout.cc b/lib/overnet/testing/trace_cout.cc
new file mode 100644
index 0000000..750edb4
--- /dev/null
+++ b/lib/overnet/testing/trace_cout.cc
@@ -0,0 +1,79 @@
+// Copyright 2018 The Fuchsia 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 "trace_cout.h"
+#include <iostream>
+
+namespace overnet {
+
+std::mutex TraceCout::mu_;
+
+void TraceCout::Render(TraceOutput output) {
+  std::lock_guard<std::mutex> lock(mu_);
+
+  char sev = '?';
+  switch (output.severity) {
+    case Severity::DEBUG:
+      sev = 'D';
+      break;
+    case Severity::INFO:
+      sev = 'I';
+      break;
+    case Severity::WARNING:
+      sev = 'W';
+      break;
+    case Severity::ERROR:
+      sev = 'E';
+      break;
+  }
+
+  auto padded = [](ssize_t len, auto fn) {
+    std::ostringstream out;
+    fn(out);
+    auto s = out.str();
+    if (len >= 0) {
+      while (s.length() < size_t(len)) {
+        s += ' ';
+      }
+    } else if (s.length() < size_t(-len)) {
+      s = std::string(-len - s.length(), ' ') + s;
+    }
+    return s;
+  };
+
+  const char* file = strrchr(output.file, '/');
+  if (file == nullptr) {
+    file = output.file;
+  } else {
+    file++;
+  }
+
+  auto pfx = padded(12, [=](auto& out) { out << sev << timer_->Now(); });
+
+  std::cout << pfx << padded(-40, [=](auto& out) { out << file << ":"; })
+            << padded(3, [=](auto& out) { out << output.line; }) << ": "
+            << output.message;
+  bool opened_tags = false;
+  auto maybe_open_tags = [&] {
+    if (!opened_tags) {
+      static const size_t kMessagePad = 120;
+      if (strlen(output.message) < kMessagePad) {
+        std::cout << std::string(kMessagePad - strlen(output.message), ' ');
+      }
+      std::cout << " // ";
+      opened_tags = true;
+    }
+  };
+  if (output.op.type() != OpType::INVALID) {
+    maybe_open_tags();
+    std::cout << " OP:" << output.op;
+  }
+  output.scopes.Visit([&](Module module, void* p) {
+    maybe_open_tags();
+    std::cout << ' ' << module << ":" << p;
+  });
+  std::cout << std::endl;
+}
+
+}  // namespace overnet
diff --git a/lib/overnet/testing/trace_cout.h b/lib/overnet/testing/trace_cout.h
new file mode 100644
index 0000000..1d8f48f
--- /dev/null
+++ b/lib/overnet/testing/trace_cout.h
@@ -0,0 +1,25 @@
+// Copyright 2018 The Fuchsia 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 <mutex>
+#include "garnet/lib/overnet/environment/timer.h"
+#include "garnet/lib/overnet/environment/trace.h"
+
+#pragma once
+
+namespace overnet {
+
+class TraceCout : public TraceRenderer {
+ public:
+  TraceCout(Timer* timer) : timer_(timer) {}
+
+  void Render(TraceOutput output) override;
+  void NoteParentChild(Op, Op) override {}
+
+ private:
+  Timer* const timer_;
+  static std::mutex mu_;
+};
+
+}  // namespace overnet
diff --git a/lib/overnet/trace.h b/lib/overnet/trace.h
deleted file mode 100644
index e665d35..0000000
--- a/lib/overnet/trace.h
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2018 The Fuchsia 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 <functional>
-#include <sstream>
-#include <string>
-
-#pragma once
-
-//#define OVERNET_DEBUG_TRACE_SINK_REFS
-
-namespace overnet {
-
-enum class Severity {
-  DEBUG,
-  INFO,
-  WARNING,
-  ERROR,
-};
-
-struct TraceOutput {
-  std::string message;
-  const char* file;
-  int line;
-  Severity severity;
-};
-
-class TraceSinkInterface {
- public:
-  void Ref() {
-#ifdef OVERNET_DEBUG_TRACE_SINK_REFS
-    std::ostringstream msg;
-    msg << "TSI:" << this << ":REF:" << refs_ << "->" << (refs_ + 1);
-    Trace(TraceOutput{msg.str(), __FILE__, __LINE__, Severity::DEBUG});
-#endif
-
-    ++refs_;
-  }
-  void Unref() {
-#ifdef OVERNET_DEBUG_TRACE_SINK_REFS
-    std::ostringstream msg;
-    msg << "TSI:" << this << ":UNREF:" << refs_ << "->" << (refs_ - 1);
-    Trace(TraceOutput{msg.str(), __FILE__, __LINE__, Severity::DEBUG});
-#endif
-
-    if (0 == --refs_)
-      Done();
-  }
-
-  virtual void Done() = 0;
-  virtual void Trace(TraceOutput output) = 0;
-
- private:
-  int refs_ = 0;
-};
-
-class TraceSink {
- public:
-  TraceSink() : severity_(Severity::DEBUG), impl_(nullptr) {}
-  TraceSink(Severity severity, TraceSinkInterface* impl)
-      : severity_(severity), impl_(impl) {
-    impl_->Ref();
-  }
-  TraceSink(const TraceSink& other)
-      : severity_(other.severity_), impl_(other.impl_) {
-    if (impl_ != nullptr)
-      impl_->Ref();
-  }
-  TraceSink(TraceSink&& other)
-      : severity_(other.severity_), impl_(other.impl_) {
-    other.impl_ = nullptr;
-  }
-  TraceSink& operator=(TraceSink other) {
-    std::swap(impl_, other.impl_);
-    std::swap(severity_, other.severity_);
-    return *this;
-  }
-  TraceSink& operator=(TraceSink&& other) {
-    std::swap(impl_, other.impl_);
-    std::swap(severity_, other.severity_);
-    return *this;
-  }
-  ~TraceSink() {
-    if (impl_ != nullptr) {
-      impl_->Unref();
-    }
-  }
-
-  Severity severity() const { return severity_; }
-  TraceSinkInterface* get() const { return impl_; }
-
-  template <class F>
-  TraceSink Decorate(F fn) const {
-    if (impl_ == nullptr) {
-      return TraceSink();
-    }
-    class Impl final : public TraceSinkInterface {
-     public:
-      Impl(TraceSink parent, F fn)
-          : fn_(std::move(fn)), parent_(std::move(parent)) {}
-
-      void Done() override { delete this; }
-      void Trace(TraceOutput output) override {
-        output.message = fn_(output.message);
-        parent_.get()->Trace(std::move(output));
-      }
-
-     private:
-      F fn_;
-      TraceSink parent_;
-    };
-    return TraceSink(severity_, new Impl(*this, std::move(fn)));
-  }
-
- private:
-  Severity severity_;
-  TraceSinkInterface* impl_;
-};
-
-class Trace : public std::ostringstream {
- public:
-  Trace(TraceSinkInterface* output, const char* file, int line,
-        Severity severity)
-      : output_(output), file_(file), line_(line), severity_(severity) {}
-  ~Trace() { output_->Trace(TraceOutput{str(), file_, line_, severity_}); }
-
- private:
-  TraceSinkInterface* const output_;
-  const char* const file_;
-  const int line_;
-  const Severity severity_;
-};
-
-}  // namespace overnet
-
-#define OVERNET_TRACE(trace_severity, sink)                         \
-  if ((sink).get() == nullptr)                                      \
-    ;                                                               \
-  else if ((sink).severity() > ::overnet::Severity::trace_severity) \
-    ;                                                               \
-  else                                                              \
-    ::overnet::Trace((sink).get(), __FILE__, __LINE__,              \
-                     ::overnet::Severity::trace_severity)
diff --git a/lib/overnet/trace_cout.h b/lib/overnet/trace_cout.h
deleted file mode 100644
index a843002..0000000
--- a/lib/overnet/trace_cout.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2018 The Fuchsia 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 "timer.h"
-#include "trace.h"
-
-#pragma once
-
-namespace overnet {
-
-inline TraceSink TraceCout(Timer* timer, Severity severity = Severity::DEBUG) {
-  class Impl final : public TraceSinkInterface {
-   public:
-    Impl(Timer* timer) : timer_(timer) {}
-
-    void Trace(TraceOutput output) {
-      char sev = '?';
-      switch (output.severity) {
-        case Severity::DEBUG:
-          sev = 'D';
-        case Severity::INFO:
-          sev = 'I';
-        case Severity::WARNING:
-          sev = 'W';
-        case Severity::ERROR:
-          sev = 'E';
-      }
-      std::cout << sev << timer_->Now() << " " << output.file << ":"
-                << output.line << ": " << output.message << std::endl;
-    }
-    void Done() { delete this; }
-
-   private:
-    Timer* const timer_;
-  };
-  return TraceSink(severity, new Impl(timer));
-}
-
-}  // namespace overnet
diff --git a/lib/overnet/trace_test.cc b/lib/overnet/trace_test.cc
deleted file mode 100644
index efb95d5..0000000
--- a/lib/overnet/trace_test.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2018 The Fuchsia 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 "trace.h"
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include "optional.h"
-
-using testing::Field;
-using testing::Mock;
-using testing::StrictMock;
-
-namespace overnet {
-namespace trace_test {
-
-class MockTraceSink : public TraceSinkInterface {
- public:
-  MOCK_METHOD0(Done, void());
-  MOCK_METHOD1(Trace, void(TraceOutput));
-};
-
-TEST(Trace, Simple) {
-  StrictMock<MockTraceSink> sink_impl;
-  TraceSink debug_sink(Severity::DEBUG, &sink_impl);
-  TraceSink error_sink(Severity::ERROR, &sink_impl);
-  TraceSink null_sink;
-
-  auto outputs = [&](Optional<const char*> message, auto fn) {
-    if (message) {
-      EXPECT_CALL(sink_impl, Trace(Field(&TraceOutput::message, *message)));
-    }
-    fn();
-    Mock::VerifyAndClearExpectations(&sink_impl);
-  };
-
-  outputs("Hello World", [&] {
-    OVERNET_TRACE(DEBUG, debug_sink) << "Hello "
-                                     << "World";
-  });
-  outputs("Hello World", [&] {
-    OVERNET_TRACE(ERROR, debug_sink) << "Hello "
-                                     << "World";
-  });
-  outputs("Hello World", [&] {
-    OVERNET_TRACE(ERROR, error_sink) << "Hello "
-                                     << "World";
-  });
-  outputs(Nothing, [&] {
-    OVERNET_TRACE(DEBUG, error_sink) << "Hello "
-                                     << "World";
-  });
-  outputs(Nothing, [&] {
-    OVERNET_TRACE(DEBUG, null_sink) << "Hello "
-                                    << "World";
-  });
-
-  EXPECT_CALL(sink_impl, Done());
-}
-
-}  // namespace trace_test
-}  // namespace overnet
diff --git a/lib/overnet/vocabulary/BUILD.gn b/lib/overnet/vocabulary/BUILD.gn
new file mode 100644
index 0000000..61cdf2e
--- /dev/null
+++ b/lib/overnet/vocabulary/BUILD.gn
@@ -0,0 +1,205 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+###############################################################################
+# AGGREGATE LIBRARIES
+
+source_set("lib") {
+  public_deps = [
+    ":optional",
+    ":slice",
+    ":status",
+    ":time",
+  ]
+}
+
+source_set("tests") {
+  testonly = true
+  deps = [
+    ":callback_test",
+    ":internal_list_test",
+    ":once_fn_test",
+    ":optional_test",
+    ":slice_test",
+    ":status_test",
+  ]
+}
+
+###############################################################################
+
+# bandwidth
+source_set("bandwidth") {
+  sources = [
+    "bandwidth.h",
+  ]
+  deps = [
+    ":time",
+  ]
+}
+
+# callback
+source_set("callback") {
+  sources = [
+    "callback.h",
+  ]
+  deps = [
+    ":status",
+  ]
+}
+
+source_set("callback_test") {
+  testonly = true
+  sources = [
+    "callback_test.cc",
+  ]
+  deps = [
+    ":callback",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# closed_ptr
+source_set("closed_ptr") {
+  sources = [
+    "closed_ptr.h",
+  ]
+}
+
+# internal_list
+source_set("internal_list") {
+  sources = [
+    "internal_list.h",
+  ]
+}
+
+source_set("internal_list_fuzzer_helpers") {
+  sources = [
+    "internal_list_fuzzer_helpers.h",
+  ]
+  deps = [
+    ":internal_list",
+  ]
+}
+
+source_set("internal_list_test") {
+  testonly = true
+  sources = [
+    "internal_list_test.cc",
+  ]
+  deps = [
+    ":internal_list",
+    ":internal_list_fuzzer_helpers",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# lazy_slice
+source_set("lazy_slice") {
+  sources = [
+    "lazy_slice.h",
+  ]
+  deps = [
+    ":once_fn",
+    ":slice",
+  ]
+}
+
+# manual_constructor
+source_set("manual_constructor") {
+  sources = [
+    "manual_constructor.h",
+  ]
+}
+
+# once_fn
+source_set("once_fn") {
+  sources = [
+    "once_fn.h",
+  ]
+}
+
+source_set("once_fn_test") {
+  testonly = true
+  sources = [
+    "once_fn_test.cc",
+  ]
+  deps = [
+    ":once_fn",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# optional
+source_set("optional") {
+  sources = [
+    "optional.h",
+  ]
+  deps = [
+    ":manual_constructor",
+  ]
+}
+
+source_set("optional_test") {
+  testonly = true
+  sources = [
+    "optional_test.cc",
+  ]
+  deps = [
+    ":optional",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# slice
+source_set("slice") {
+  sources = [
+    "slice.cc",
+    "slice.h",
+  ]
+  deps = [
+    ":optional",
+  ]
+}
+
+source_set("slice_test") {
+  testonly = true
+  sources = [
+    "slice_test.cc",
+  ]
+  deps = [
+    ":slice",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# status
+source_set("status") {
+  sources = [
+    "status.cc",
+    "status.h",
+  ]
+}
+
+source_set("status_test") {
+  testonly = true
+  sources = [
+    "status_test.cc",
+  ]
+  deps = [
+    ":status",
+    "//third_party/googletest:gmock",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+# time
+source_set("time") {
+  sources = [
+    "time.h",
+  ]
+}
diff --git a/lib/overnet/bandwidth.h b/lib/overnet/vocabulary/bandwidth.h
similarity index 97%
rename from lib/overnet/bandwidth.h
rename to lib/overnet/vocabulary/bandwidth.h
index 260ceb8..7ed7bca 100644
--- a/lib/overnet/bandwidth.h
+++ b/lib/overnet/vocabulary/bandwidth.h
@@ -6,7 +6,7 @@
 
 #include <stdint.h>
 #include <ostream>
-#include "timer.h"
+#include "garnet/lib/overnet/vocabulary/time.h"
 
 namespace overnet {
 
diff --git a/lib/overnet/callback.h b/lib/overnet/vocabulary/callback.h
similarity index 100%
rename from lib/overnet/callback.h
rename to lib/overnet/vocabulary/callback.h
diff --git a/lib/overnet/callback_test.cc b/lib/overnet/vocabulary/callback_test.cc
similarity index 100%
rename from lib/overnet/callback_test.cc
rename to lib/overnet/vocabulary/callback_test.cc
diff --git a/lib/overnet/closed_ptr.h b/lib/overnet/vocabulary/closed_ptr.h
similarity index 100%
rename from lib/overnet/closed_ptr.h
rename to lib/overnet/vocabulary/closed_ptr.h
diff --git a/lib/overnet/internal_list.h b/lib/overnet/vocabulary/internal_list.h
similarity index 78%
rename from lib/overnet/internal_list.h
rename to lib/overnet/vocabulary/internal_list.h
index 98f8525..a13d3ce 100644
--- a/lib/overnet/internal_list.h
+++ b/lib/overnet/vocabulary/internal_list.h
@@ -4,6 +4,8 @@
 
 #pragma once
 
+//#define OVERNET_UNIQUE_PTR_NODE
+
 namespace overnet {
 
 template <class T>
@@ -21,13 +23,24 @@
   InternalListNode() = default;
   InternalListNode(const InternalListNode&) = delete;
   InternalListNode& operator=(const InternalListNode&) = delete;
+#ifdef OVERNET_UNIQUE_PTR_NODE
+  ~InternalListNode() { assert(inner_->owner_ == nullptr); }
+#else
   ~InternalListNode() { assert(owner_ == nullptr); }
+#endif
 
  private:
-  T* next_ = nullptr;
-  T* prev_ = nullptr;
+#ifdef OVERNET_UNIQUE_PTR_NODE
+  struct Inner {
+#endif
+    T* next_ = nullptr;
+    T* prev_ = nullptr;
 #ifndef NDEBUG
-  void* owner_ = nullptr;
+    std::unique_ptr<void*> owner_;
+#endif
+#ifdef OVERNET_UNIQUE_PTR_NODE
+  };
+  std::unique_ptr<Inner> inner_ = std::make_unique<Inner>();
 #endif
 };
 
@@ -45,10 +58,17 @@
   T* Front() { return head_; }
   bool Empty() const { return head_ == nullptr; }
 
+  bool Contains(T* p) {
+    for (auto q : *this)
+      if (p == q)
+        return true;
+    return false;
+  }
+
   void PushBack(T* p) {
 #ifndef NDEBUG
     assert(Node(p)->owner_ == nullptr);
-    Node(p)->owner_ = this;
+    Node(p)->owner_ = std::make_unique<void*>(this);
 #endif
     assert(Node(p)->next_ == nullptr);
     assert(Node(p)->prev_ == nullptr);
@@ -67,7 +87,7 @@
   void PushFront(T* p) {
 #ifndef NDEBUG
     assert(Node(p)->owner_ == nullptr);
-    Node(p)->owner_ = this;
+    Node(p)->owner_ = std::make_unique<void*>(this);
 #endif
     assert(Node(p)->next_ == nullptr);
     assert(Node(p)->prev_ == nullptr);
@@ -85,8 +105,8 @@
 
   void Remove(T* p) {
 #ifndef NDEBUG
-    assert(Node(p)->owner_ == this);
-    Node(p)->owner_ = nullptr;
+    assert(*Node(p)->owner_ == this);
+    Node(p)->owner_.reset();
 #endif
     if (p == head_) {
       head_ = Node(p)->next_;
@@ -124,6 +144,7 @@
       return *this;
     }
     T* operator*() { return p_; }
+    T* operator->() { return p_; }
 
    private:
     T* p_;
@@ -135,7 +156,13 @@
   size_t Size() const { return size_; }
 
  private:
-  static InternalListNode<T>* Node(T* p) { return &(p->*NodePtr); }
+  static auto* Node(T* p) {
+#ifdef OVERNET_UNIQUE_PTR_NODE
+    return (p->*NodePtr).inner_.get();
+#else
+    return &(p->*NodePtr);
+#endif
+  }
 
   T* head_ = nullptr;
   T* tail_ = nullptr;
diff --git a/lib/overnet/internal_list_fuzzer.cc b/lib/overnet/vocabulary/internal_list_fuzzer.cc
similarity index 100%
rename from lib/overnet/internal_list_fuzzer.cc
rename to lib/overnet/vocabulary/internal_list_fuzzer.cc
diff --git a/lib/overnet/internal_list_fuzzer_corpus_to_code.py b/lib/overnet/vocabulary/internal_list_fuzzer_corpus_to_code.py
similarity index 100%
rename from lib/overnet/internal_list_fuzzer_corpus_to_code.py
rename to lib/overnet/vocabulary/internal_list_fuzzer_corpus_to_code.py
diff --git a/lib/overnet/internal_list_fuzzer_helpers.h b/lib/overnet/vocabulary/internal_list_fuzzer_helpers.h
similarity index 100%
rename from lib/overnet/internal_list_fuzzer_helpers.h
rename to lib/overnet/vocabulary/internal_list_fuzzer_helpers.h
diff --git a/lib/overnet/internal_list_test.cc b/lib/overnet/vocabulary/internal_list_test.cc
similarity index 100%
rename from lib/overnet/internal_list_test.cc
rename to lib/overnet/vocabulary/internal_list_test.cc
diff --git a/lib/overnet/lazy_slice.h b/lib/overnet/vocabulary/lazy_slice.h
similarity index 75%
rename from lib/overnet/lazy_slice.h
rename to lib/overnet/vocabulary/lazy_slice.h
index bc4ddc5..69e1c42 100644
--- a/lib/overnet/lazy_slice.h
+++ b/lib/overnet/vocabulary/lazy_slice.h
@@ -12,10 +12,9 @@
 class TimeStamp;
 
 struct LazySliceArgs {
-  size_t desired_prefix;
-  size_t max_length;
-  bool has_other_content = false;
-  TimeStamp* delay_until_time = nullptr;
+  const Border desired_border;
+  const size_t max_length;
+  const bool has_other_content = false;
 };
 
 using LazySlice = OnceFn<Slice(LazySliceArgs), 32 * sizeof(void*)>;
diff --git a/lib/overnet/vocabulary/manual_constructor.h b/lib/overnet/vocabulary/manual_constructor.h
new file mode 100644
index 0000000..b624216
--- /dev/null
+++ b/lib/overnet/vocabulary/manual_constructor.h
@@ -0,0 +1,88 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <type_traits>
+#include <utility>
+
+namespace overnet {
+
+//#define OVERNET_UNIQUE_PTR_MANUAL_CONSTRUCTOR
+
+template <typename Type>
+class ManualConstructor {
+ public:
+  ManualConstructor() {}
+  ~ManualConstructor() {}
+  ManualConstructor(const ManualConstructor&) = delete;
+  ManualConstructor& operator=(const ManualConstructor&) = delete;
+
+  Type* get() { return reinterpret_cast<Type*>(space()); }
+  const Type* get() const { return reinterpret_cast<const Type*>(space()); }
+
+  Type* operator->() { return get(); }
+  const Type* operator->() const { return get(); }
+
+  Type& operator*() { return *get(); }
+  const Type& operator*() const { return *get(); }
+
+  void Init() { new (Materialize()) Type; }
+
+  // Init() constructs the Type instance using the given arguments
+  // (which are forwarded to Type's constructor).
+  //
+  // Note that Init() with no arguments performs default-initialization,
+  // not zero-initialization (i.e it behaves the same as "new Type;", not
+  // "new Type();"), so it will leave non-class types uninitialized.
+  template <typename... Ts>
+  void Init(Ts&&... args) {
+    new (Materialize()) Type(std::forward<Ts>(args)...);
+  }
+
+  // Init() that is equivalent to copy and move construction.
+  // Enables usage like this:
+  //   ManualConstructor<std::vector<int>> v;
+  //   v.Init({1, 2, 3});
+  void Init(const Type& x) { new (Materialize()) Type(x); }
+  void Init(Type&& x) { new (Materialize()) Type(std::move(x)); }
+
+  void Destroy() {
+    get()->~Type();
+    Dematerialize();
+  }
+
+ private:
+#ifndef OVERNET_UNIQUE_PTR_MANUAL_CONSTRUCTOR
+  union {
+    Type space_;
+  };
+  Type* space() { return &space_; }
+  const Type* space() const { return &space_; }
+  Type* Materialize() { return &space_; }
+  void Dematerialize() {}
+#else
+  typedef std::aligned_storage_t<sizeof(Type), alignof(Type)> Storage;
+  std::unique_ptr<Storage> storage_;
+  Storage* space() {
+    assert(storage_ != nullptr);
+    return storage_.get();
+  }
+  const Storage* space() const {
+    assert(storage_ != nullptr);
+    return storage_.get();
+  }
+  Storage* Materialize() {
+    assert(storage_ == nullptr);
+    storage_.reset(new Storage);
+    return storage_.get();
+  }
+  void Dematerialize() {
+    assert(storage_ != nullptr);
+    storage_.reset();
+  }
+#endif
+};
+
+}  // namespace overnet
diff --git a/lib/overnet/once_fn.h b/lib/overnet/vocabulary/once_fn.h
similarity index 76%
rename from lib/overnet/once_fn.h
rename to lib/overnet/vocabulary/once_fn.h
index d9c06f1..735fc4a 100644
--- a/lib/overnet/once_fn.h
+++ b/lib/overnet/vocabulary/once_fn.h
@@ -87,6 +87,75 @@
 const VTable<void, Arg...> SmallFunctor<F, void, Arg...>::vtable = {Move, Call,
                                                                     NotCalled};
 
+template <class F, class A, class R, class... Arg>
+class SmallMustCall {
+ public:
+  typedef std::tuple<F, A> Rep;
+
+  static const VTable<R, Arg...> vtable;
+  static void InitEnv(void* env, F&& f, A&& a) {
+    new (env) Rep(std::forward<F>(f), std::forward<A>(a));
+  }
+
+ private:
+  static size_t Move(void* dst, void* src, size_t dst_size) {
+    assert(dst_size >= sizeof(Rep));
+    Rep* rep = static_cast<Rep*>(src);
+    new (dst) Rep(std::move(*rep));
+    rep->~Rep();
+    return sizeof(Rep);
+  }
+  static R Call(void* env, Arg&&... arg) {
+    Rep* rep = static_cast<Rep*>(env);
+    R r = std::get<0>(*rep)(std::forward<Arg>(arg)...);
+    rep->~Rep();
+    return r;
+  }
+  static void NotCalled(void* env) {
+    Rep* rep = static_cast<Rep*>(env);
+    std::apply(std::get<0>(*rep), std::get<1>(*rep)());
+    rep->~Rep();
+  }
+};
+
+template <class F, class A, class... Arg>
+class SmallMustCall<F, A, void, Arg...> {
+ public:
+  typedef std::tuple<F, A> Rep;
+
+  static const VTable<void, Arg...> vtable;
+  static void InitEnv(void* env, F&& f, A&& a) {
+    new (env) Rep(std::forward<F>(f), std::forward<A>(a));
+  }
+
+ private:
+  static size_t Move(void* dst, void* src, size_t dst_size) {
+    assert(dst_size >= sizeof(Rep));
+    Rep* rep = static_cast<Rep*>(src);
+    new (dst) Rep(std::move(*rep));
+    rep->~Rep();
+    return sizeof(Rep);
+  }
+  static void Call(void* env, Arg&&... arg) {
+    Rep* rep = static_cast<Rep*>(env);
+    std::get<0> (*rep)(std::forward<Arg>(arg)...);
+    rep->~Rep();
+  }
+  static void NotCalled(void* env) {
+    Rep* rep = static_cast<Rep*>(env);
+    std::apply(std::get<0>(*rep), std::get<1>(*rep)());
+    rep->~Rep();
+  }
+};
+
+template <class F, class A, class R, class... Arg>
+const VTable<R, Arg...> SmallMustCall<F, A, R, Arg...>::vtable = {Move, Call,
+                                                                  NotCalled};
+
+template <class F, class A, class... Arg>
+const VTable<void, Arg...> SmallMustCall<F, A, void, Arg...>::vtable = {
+    Move, Call, NotCalled};
+
 template <class R, class... Arg>
 struct NullVTable {
   static size_t Move(void*, void*, size_t) { return 0; }
@@ -101,6 +170,9 @@
 
 }  // namespace once_fn_detail
 
+// Marker to designate a function that *must* be called
+enum MustCall { MUST_CALL };
+
 // Function that is called at most once
 template <class R, class... Arg, size_t kStorage>
 class OnceFn<R(Arg...), kStorage> {
@@ -116,6 +188,20 @@
                                                         std::forward<F>(f));
   }
 
+  template <class F, class A,
+            typename = typename std::enable_if<sizeof(std::tuple<F, A>) <=
+                                               kStorage>::type>
+  OnceFn(MustCall, F&& f, A&& a) {
+    vtable_ = &once_fn_detail::SmallMustCall<F, A, R, Arg...>::vtable;
+    once_fn_detail::SmallMustCall<F, A, R, Arg...>::InitEnv(
+        &env_, std::forward<F>(f), std::forward<A>(a));
+  }
+
+  template <class F>
+  OnceFn(MustCall, F&& f)
+      : OnceFn(MUST_CALL, std::forward<F>(f),
+               []() { return std::tuple<Arg...>(); }) {}
+
   OnceFn(const OnceFn&) = delete;
   OnceFn& operator=(const OnceFn&) = delete;
 
diff --git a/lib/overnet/once_fn_test.cc b/lib/overnet/vocabulary/once_fn_test.cc
similarity index 89%
rename from lib/overnet/once_fn_test.cc
rename to lib/overnet/vocabulary/once_fn_test.cc
index 63e3b8d..ac9122c 100644
--- a/lib/overnet/once_fn_test.cc
+++ b/lib/overnet/vocabulary/once_fn_test.cc
@@ -31,6 +31,26 @@
   EXPECT_DEATH_IF_SUPPORTED(empty(), "");
 }
 
+TEST(OnceFn, MustCalledCalledPath) {
+  StrictMock<MockCallbackClass> cb;
+  OnceFn<void(int)> must_call(MUST_CALL, [&](int i) {
+    EXPECT_EQ(i, 1);
+    cb.Called();
+  });
+  EXPECT_CALL(cb, Called());
+  must_call(1);
+  EXPECT_DEATH_IF_SUPPORTED(must_call(2), "");
+}
+
+TEST(OnceFn, MustCalledNotCalledPath) {
+  StrictMock<MockCallbackClass> cb;
+  OnceFn<void(int)> must_call(MUST_CALL, [&](int i) {
+    EXPECT_EQ(i, 0);
+    cb.Called();
+  });
+  EXPECT_CALL(cb, Called());
+}
+
 TEST(OnceFn, NotCalledCleansUp) {
   StrictMock<MockCallbackClass> mock;
   int i = 42;
diff --git a/lib/overnet/optional.h b/lib/overnet/vocabulary/optional.h
similarity index 84%
rename from lib/overnet/optional.h
rename to lib/overnet/vocabulary/optional.h
index 71d9de2..56b2bf2 100644
--- a/lib/overnet/optional.h
+++ b/lib/overnet/vocabulary/optional.h
@@ -53,8 +53,9 @@
     if (!set_ && !other->set_)
       return;
     if (set_ && other->set_) {
-      using std::swap;
-      swap(*storage_.get(), *other->storage_.get());
+      auto tmp = std::move(*storage_.get());
+      *storage_.get() = std::move(*other->storage_.get());
+      *other->storage_.get() = std::move(tmp);
       return;
     }
     if (set_) {
@@ -118,7 +119,7 @@
   }
 
   template <typename F>
-  auto Map(F f) -> Optional<decltype(f(*get()))> const {
+  auto Map(F f) const -> Optional<decltype(f(*get()))> const {
     if (!set_)
       return Nothing;
     return f(*storage_.get());
@@ -153,6 +154,30 @@
                        : !b.has_value();
 }
 
+template <class T>
+bool operator==(const Optional<T>& a, const T& b) {
+  if (!a.has_value())
+    return false;
+  return a.value() == b;
+}
+
+template <class T>
+bool operator==(const T& a, const Optional<T>& b) {
+  if (!b.has_value())
+    return false;
+  return b.value() == a;
+}
+
+template <class T>
+bool operator!=(const Optional<T>& a, const T& b) {
+  return !operator==(a, b);
+}
+
+template <class T>
+bool operator!=(const T& a, const Optional<T>& b) {
+  return !operator==(a, b);
+}
+
 template <class T, class U>
 bool operator!=(const Optional<T>& a, const Optional<U>& b) {
   return !operator==(a, b);
diff --git a/lib/overnet/optional_test.cc b/lib/overnet/vocabulary/optional_test.cc
similarity index 100%
rename from lib/overnet/optional_test.cc
rename to lib/overnet/vocabulary/optional_test.cc
diff --git a/lib/overnet/vocabulary/slice.cc b/lib/overnet/vocabulary/slice.cc
new file mode 100644
index 0000000..bdbd270
--- /dev/null
+++ b/lib/overnet/vocabulary/slice.cc
@@ -0,0 +1,97 @@
+// Copyright 2018 The Fuchsia 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 "slice.h"
+#include <iomanip>
+#include <sstream>
+
+namespace overnet {
+
+namespace {
+std::vector<std::pair<uint8_t, size_t>> RLE(const uint8_t* begin,
+                                            const uint8_t* end) {
+  uint16_t cur = 256;
+  size_t count = 0;
+  std::vector<std::pair<uint8_t, size_t>> out;
+  auto flush = [&](uint16_t new_cur) {
+    if (cur >= 256) {
+      assert(count == 0);
+    } else {
+      if (count < 10) {
+        for (size_t i = 0; i < count; i++) {
+          out.push_back(std::make_pair(cur, 1));
+        }
+      } else {
+        out.push_back(std::make_pair(cur, count));
+      }
+    }
+    cur = new_cur;
+    count = 0;
+  };
+  for (const auto* p = begin; p != end; ++p) {
+    if (*p != cur) {
+      flush(*p);
+    }
+    count++;
+  }
+  flush(256);
+  return out;
+}
+}  // namespace
+
+std::ostream& operator<<(std::ostream& out, const Border& border) {
+  return out << "<" << border.prefix << "+++" << border.suffix << ">";
+}
+
+std::ostream& operator<<(std::ostream& out, const Slice& slice) {
+  bool first = true;
+  std::ostringstream temp;
+  auto rle = RLE(slice.begin(), slice.end());
+  for (auto b : rle) {
+    if (!first)
+      temp << ' ';
+    temp << std::hex << std::setfill('0') << std::setw(2)
+         << static_cast<unsigned>(b.first);
+    if (b.second != 1) {
+      assert(b.second != 0);
+      temp << '*' << std::dec << b.second;
+    }
+    first = false;
+  }
+  if (!first)
+    temp << ' ';
+  temp << '"';
+  for (auto b : rle) {
+    const auto c = static_cast<char>(b.first);
+    if (isprint(c)) {
+      temp << c;
+    } else {
+      temp << '.';
+    }
+    if (b.second != 1) {
+      assert(b.second != 0);
+      temp << '*' << std::dec << b.second;
+    }
+  }
+  temp << '"';
+  return out << '[' << temp.str() << ']';
+}
+
+std::ostream& operator<<(std::ostream& out, const std::vector<Slice>& slices) {
+  out << "[";
+  bool first = true;
+  for (const auto& s : slices) {
+    if (!first)
+      out << ", ";
+    first = false;
+    out << s;
+  }
+  return out << "]";
+}
+
+std::ostream& operator<<(std::ostream& out, const Chunk& chunk) {
+  return out << "@" << chunk.offset << chunk.slice;
+}
+
+}  // namespace overnet
diff --git a/lib/overnet/slice.h b/lib/overnet/vocabulary/slice.h
similarity index 64%
rename from lib/overnet/slice.h
rename to lib/overnet/vocabulary/slice.h
index cfdc7bd..0d8d48b 100644
--- a/lib/overnet/slice.h
+++ b/lib/overnet/vocabulary/slice.h
@@ -12,9 +12,31 @@
 #include <iosfwd>
 #include <string>
 #include <type_traits>
+#include <vector>
+#include "optional.h"
 
 namespace overnet {
 
+// Describes the extent of padding both before and after a slice.
+struct Border {
+  size_t prefix;
+  size_t suffix;
+
+  static Border Prefix(size_t size) { return Border{size, 0}; }
+  static Border Suffix(size_t size) { return Border{0, size}; }
+  static Border None() { return Border{0, 0}; }
+
+  Border WithAddedPrefix(size_t size) const {
+    return Border{prefix + size, suffix};
+  }
+
+  Border WithAddedSuffix(size_t size) const {
+    return Border{size, suffix + size};
+  }
+
+  size_t Total() const { return prefix + suffix; }
+};
+
 class Slice final {
  public:
   static constexpr size_t kSmallSliceMaxLength = 3 * sizeof(void*) - 1;
@@ -40,8 +62,8 @@
     void (*ref)(Data* data);
     void (*unref)(Data* data);
     void (*trim)(Data* data, size_t trim_left, size_t trim_right);
-    uint8_t* (*maybe_add_prefix)(const Data* data, size_t length,
-                                 Data* new_slice_data);
+    uint8_t* (*maybe_add_borders)(const Data* data, Border border,
+                                  Data* new_slice_data);
     const char* name;
   };
 
@@ -77,6 +99,25 @@
     std::swap(data_, other->data_);
   }
 
+  static Optional<Slice> JoinIfSameUnderlyingMemory(const Slice& a,
+                                                    const Slice& b) {
+    if (a.vtable_ != b.vtable_) {
+      return Nothing;
+    }
+
+    if (a.end() != b.begin()) {
+      return Nothing;
+    }
+
+    if (a.data_.general.control != b.data_.general.control) {
+      return Nothing;
+    }
+
+    Slice out(a.vtable_, a.data_.general.control, a.begin(), b.end());
+    out.vtable_->ref(&out.data_);
+    return out;
+  }
+
   const uint8_t* begin() const { return vtable_->begin(&data_); }
   const uint8_t* end() const { return vtable_->end(&data_); }
   size_t length() const { return vtable_->length(&data_); }
@@ -91,14 +132,19 @@
     return out;
   }
 
+  Slice ToOffset(size_t offset) const {
+    Slice out(*this);
+    out.TrimEnd(length() - offset);
+    return out;
+  }
+
   Slice FromPointer(const uint8_t* internal_pointer) const {
     assert(internal_pointer >= begin() && internal_pointer <= end());
     return FromOffset(internal_pointer - begin());
   }
 
   Slice TakeUntilOffset(size_t offset) {
-    Slice out(*this);
-    out.TrimEnd(length() - offset);
+    auto out = ToOffset(offset);
     TrimBegin(offset);
     return out;
   }
@@ -108,18 +154,25 @@
     return TakeUntilOffset(internal_pointer - begin());
   }
 
+  Slice TakeFromOffset(size_t offset) {
+    auto out = FromOffset(offset);
+    TrimEnd(length() - offset);
+    return out;
+  }
+
   Slice Cut(size_t from_offset, size_t to_offset) {
     Slice copy(*this);
     copy.Trim(from_offset, copy.length() - to_offset);
     return copy;
   }
 
-  static Slice Join(std::initializer_list<Slice> slices) {
-    return Join(slices.begin(), slices.end());
+  static Slice Join(std::initializer_list<Slice> slices,
+                    Border desired_border = Border::None()) {
+    return Join(slices.begin(), slices.end(), desired_border);
   }
 
   template <class IT>
-  static Slice Join(IT begin, IT end, size_t desired_prefix = 0) {
+  static Slice Join(IT begin, IT end, Border desired_border = Border::None()) {
     if (begin == end)
       return Slice();
     if (std::next(begin) == end)
@@ -130,8 +183,8 @@
       total_length += it->length();
     }
 
-    return Slice::WithInitializerAndPrefix(
-        total_length, desired_prefix, [begin, end](uint8_t* out) {
+    return Slice::WithInitializerAndBorders(
+        total_length, desired_border, [begin, end](uint8_t* out) {
           size_t offset = 0;
           for (auto it = begin; it != end; ++it) {
             memcpy(out + offset, it->begin(), it->length());
@@ -142,37 +195,52 @@
 
   template <class F>
   Slice WithPrefix(size_t length, F initializer) const {
+    return WithBorders(Border::Prefix(length), initializer);
+  }
+
+  template <class F>
+  Slice WithBorders(Border border, F initializer) const {
     Data new_slice_data;
     if (uint8_t* prefix =
-            vtable_->maybe_add_prefix(&data_, length, &new_slice_data)) {
+            vtable_->maybe_add_borders(&data_, border, &new_slice_data)) {
       initializer(prefix);
       return Slice{vtable_, new_slice_data};
     } else {
       size_t own_length = this->length();
       const uint8_t* begin = this->begin();
-      return WithInitializer(
-          own_length + length,
-          [length, own_length, initializer, begin](uint8_t* p) {
-            initializer(p);
-            memcpy(p + length, begin, own_length);
-          });
+      return WithInitializer(own_length + border.prefix + border.suffix,
+                             [add_prefix = border.prefix, own_length,
+                              initializer, begin](uint8_t* p) {
+                               memcpy(p + add_prefix, begin, own_length);
+                               initializer(p);
+                             });
     }
   }
 
+  template <class F>
+  Slice MutateUnique(F f) const {
+    return WithBorders(Border::None(), f);
+  }
+
   std::string AsStdString() const { return std::string(begin(), end()); }
 
+  bool StartsWith(const Slice& prefix) const;
+
   /////////////////////////////////////////////////////////////////////////////
   // Factory functions
 
   static Slice FromStaticString(const char* s) {
-    struct StaticString {
-      size_t length;
-      const uint8_t* bytes;
-    };
     auto bs = reinterpret_cast<const uint8_t*>(s);
     return Slice(&Static<>::const_vtable_, nullptr, bs, bs + strlen(s));
   }
 
+  template <class I>
+  static Slice ReferencingContainer(I begin, I end) {
+    return Slice(&Static<>::const_vtable_, nullptr,
+                 static_cast<const uint8_t*>(begin),
+                 static_cast<const uint8_t*>(end));
+  }
+
   static Slice FromCopiedBuffer(const void* data, size_t length) {
     return WithInitializer(
         length, [data, length](void* p) { memcpy(p, data, length); });
@@ -200,26 +268,28 @@
   }
 
   template <class F>
-  static Slice WithInitializerAndPrefix(size_t length, size_t prefix,
-                                        F&& initializer) {
+  static Slice WithInitializerAndBorders(size_t length, Border border,
+                                         F&& initializer) {
     if (length <= kSmallSliceMaxLength) {
-      // Ignore prefix request if this is small enough (we'll maybe allocate
-      // later, but that's ok - we didn't here).
+      // Ignore prefix/suffix request if this is small enough (we'll maybe
+      // allocate later, but that's ok - we didn't here).
       Slice s(&Static<>::small_vtable_);
       s.data_.small.length = length;
       std::forward<F>(initializer)(s.data_.small.bytes);
       return s;
     } else {
-      auto* block = BHNew(length + prefix);
-      std::forward<F>(initializer)(block->bytes + prefix);
-      return Slice(&Static<>::block_vtable_, block, block->bytes + prefix,
-                   block->bytes + prefix + length);
+      auto* block = BHNew(length + border.prefix + border.suffix);
+      std::forward<F>(initializer)(block->bytes + border.prefix);
+      return Slice(&Static<>::block_vtable_, block,
+                   block->bytes + border.prefix,
+                   block->bytes + border.prefix + length);
     }
   }
 
   template <class F>
   static Slice WithInitializer(size_t length, F&& initializer) {
-    return WithInitializerAndPrefix(length, 0, std::forward<F>(initializer));
+    return WithInitializerAndBorders(length, Border::None(),
+                                     std::forward<F>(initializer));
   }
 
   static Slice RepeatedChar(size_t count, char c) {
@@ -289,11 +359,13 @@
               data->small.length);
     }
   }
-  static uint8_t* SmallAddPrefix(const Data* data, size_t length,
-                                 Data* new_slice_data) {
-    if (data->small.length + length < kSmallSliceMaxLength) {
-      new_slice_data->small.length = data->small.length + length;
-      memcpy(new_slice_data->small.bytes + length, data->small.bytes,
+  static uint8_t* SmallAddBorders(const Data* data, Border border,
+                                  Data* new_slice_data) {
+    if (data->small.length + border.prefix + border.suffix <
+        kSmallSliceMaxLength) {
+      new_slice_data->small.length =
+          data->small.length + border.prefix + border.suffix;
+      memcpy(new_slice_data->small.bytes + border.prefix, data->small.bytes,
              data->small.length);
       return new_slice_data->small.bytes;
     }
@@ -302,10 +374,12 @@
 
   struct BlockHeader {
     int refs;
+    size_t block_length;
     uint8_t bytes[0];
   };
   static BlockHeader* BHNew(size_t length) {
     auto* out = static_cast<BlockHeader*>(malloc(sizeof(BlockHeader) + length));
+    out->block_length = length;
     out->refs = 1;
     return out;
   }
@@ -317,23 +391,26 @@
       free(data->general.control);
     }
   }
-  static uint8_t* BHAddPrefix(const Data* data, size_t length,
-                              Data* new_slice_data) {
+  static uint8_t* BHAddBorders(const Data* data, Border border,
+                               Data* new_slice_data) {
     auto* hdr = static_cast<BlockHeader*>(data->general.control);
     if (hdr->refs != 1)
       return nullptr;
     assert(data->general.begin - hdr->bytes >= 0);
-    if (static_cast<size_t>(data->general.begin - hdr->bytes) >= length) {
+    if (static_cast<size_t>(data->general.begin - hdr->bytes) >=
+            border.prefix &&
+        static_cast<size_t>(hdr->bytes + hdr->block_length -
+                            data->general.end) >= border.suffix) {
       hdr->refs++;
-      *new_slice_data =
-          Data(hdr, data->general.begin - length, data->general.end);
+      *new_slice_data = Data(hdr, data->general.begin - border.prefix,
+                             data->general.end + border.suffix);
       return const_cast<uint8_t*>(new_slice_data->general.begin);
     }
     return nullptr;
   }
 
-  static uint8_t* NoAddPrefix(const Data* data, size_t length,
-                              Data* new_slice_data) {
+  static uint8_t* NoAddBorders(const Data* data, Border border,
+                               Data* new_slice_data) {
     return nullptr;
   }
 
@@ -359,8 +436,8 @@
     NoOpRef,
     // trim
     SmallTrim,
-    // maybe_add_prefix
-    SmallAddPrefix,
+    // maybe_add_borders
+    SmallAddBorders,
     // name
     "small_vtable"};
 
@@ -378,8 +455,8 @@
     NoOpRef,
     // trim
     GeneralTrim,
-    // maybe_add_prefix
-    NoAddPrefix,
+    // maybe_add_borders
+    NoAddBorders,
     // name
     "small_vtable"};
 
@@ -397,8 +474,8 @@
     BHUnref,
     // trim
     GeneralTrim,
-    // maybe_add_prefix
-    BHAddPrefix,
+    // maybe_add_borders
+    BHAddBorders,
     // name
     "block_vtable"};
 
@@ -423,6 +500,26 @@
     offset += slice_offset;
     return out;
   }
+
+  Chunk TakeFromSliceOffset(size_t slice_offset) {
+    Chunk out{offset + slice_offset, end_of_message,
+              slice.TakeFromOffset(slice_offset)};
+    end_of_message = false;
+    return out;
+  }
+
+  static Optional<Chunk> JoinIfSameUnderlyingMemory(const Chunk& a,
+                                                    const Chunk& b) {
+    if (a.offset + a.slice.length() != b.offset) {
+      return Nothing;
+    }
+
+    return Slice::JoinIfSameUnderlyingMemory(a.slice, b.slice)
+        .Map([offset = a.offset,
+              end_of_message = b.end_of_message](Slice slice) {
+          return Chunk{offset, end_of_message, std::move(slice)};
+        });
+  }
 };
 
 inline bool operator==(const Slice& a, const Slice& b) {
@@ -431,11 +528,22 @@
   return 0 == memcmp(a.begin(), b.begin(), a.length());
 }
 
+inline bool operator!=(const Slice& a, const Slice& b) { return !(a == b); }
+
 inline bool operator==(const Chunk& a, const Chunk& b) {
   return a.offset == b.offset && a.slice == b.slice;
 }
 
+inline bool Slice::StartsWith(const Slice& prefix) const {
+  if (length() < prefix.length()) {
+    return false;
+  }
+  return ToOffset(prefix.length()) == prefix;
+}
+
+std::ostream& operator<<(std::ostream& out, const Border& border);
 std::ostream& operator<<(std::ostream& out, const Slice& slice);
+std::ostream& operator<<(std::ostream& out, const std::vector<Slice>& slices);
 std::ostream& operator<<(std::ostream& out, const Chunk& chunk);
 
 }  // namespace overnet
diff --git a/lib/overnet/slice_test.cc b/lib/overnet/vocabulary/slice_test.cc
similarity index 100%
rename from lib/overnet/slice_test.cc
rename to lib/overnet/vocabulary/slice_test.cc
diff --git a/lib/overnet/status.cc b/lib/overnet/vocabulary/status.cc
similarity index 100%
rename from lib/overnet/status.cc
rename to lib/overnet/vocabulary/status.cc
diff --git a/lib/overnet/status.h b/lib/overnet/vocabulary/status.h
similarity index 93%
rename from lib/overnet/status.h
rename to lib/overnet/vocabulary/status.h
index 74338a7..fd0b36a 100644
--- a/lib/overnet/status.h
+++ b/lib/overnet/vocabulary/status.h
@@ -155,7 +155,7 @@
 // the future to also capture additional data in the case of errors.
 // Contains an optimization whereby Status objects containing no reason string
 // are carried without allocation.
-class Status final {
+class [[nodiscard]] Status final {
  public:
   Status(StatusCode code) : code_(static_cast<uintptr_t>(code)) {}
   Status(StatusCode code, std::string reason)
@@ -169,7 +169,7 @@
       delete rep_;
   }
 
-  Status(Status&& other) : code_(other.code_) { other.code_ = 0; }
+  Status(Status && other) : code_(other.code_) { other.code_ = 0; }
   Status(const Status& other) : code_(other.code_) {
     if (!is_code_only()) {
       rep_->refs.fetch_add(1, std::memory_order_relaxed);
@@ -185,7 +185,7 @@
     return *this;
   }
 
-  void Swap(Status* other) { std::swap(code_, other->code_); }
+  void Swap(Status * other) { std::swap(code_, other->code_); }
 
   static const Status& Ok() { return Static<>::Ok; }
   static const Status& Cancelled() { return Static<>::Cancelled; }
@@ -206,16 +206,24 @@
     return is_code_only() ? status_impl::empty_string : rep_->reason;
   }
 
+  Status WithContext(const char* context) const {
+    if (is_ok())
+      return *this;
+    return Status(code(), std::string(context) + ": " + reason());
+  }
+
   template <class T>
-  StatusOr<typename std::remove_reference<T>::type> Or(T&& value) const;
+  StatusOr<typename std::remove_reference<T>::type> Or(T && value) const;
 
   template <class F>
-  auto Then(F fn) -> decltype(fn()) {
+  auto Then(F fn)->decltype(fn()) {
     if (is_error())
       return *this;
     return fn();
   }
 
+  void Ignore() {}
+
  private:
   bool is_code_only() const { return code_ < 256; }
 
@@ -250,14 +258,14 @@
 // Either a status or a T - allows carrying a separate object in the case of
 // success.
 template <class T>
-class StatusOr final {
+class [[nodiscard]] StatusOr final {
  public:
   using element_type = T;
 
   template <typename U, typename = typename std::enable_if<
                             !std::is_convertible<U, Status>::value &&
                             !std::is_convertible<U, StatusOr>::value>::type>
-  StatusOr(U&& value)
+  StatusOr(U && value)
       : code_(StatusCode::OK), storage_(std::forward<U>(value)) {}
   StatusOr(StatusCode code, const std::string& description)
       : code_(code),
@@ -276,7 +284,7 @@
       p->refs.fetch_add(1, std::memory_order_relaxed);
     }
   }
-  StatusOr(StatusOr&& other) : code_(other.code_) {
+  StatusOr(StatusOr && other) : code_(other.code_) {
     if (is_ok()) {
       new (&storage_) Storage(std::move(*other.unwrap()));
     } else {
@@ -304,6 +312,8 @@
     }
   }
 
+  void Ignore() {}
+
   StatusCode code() const { return code_; }
   bool is_ok() const { return code() == StatusCode::OK; }
   bool is_error() const { return !is_ok(); }
@@ -340,12 +350,19 @@
   }
 
   template <class F>
-  auto Then(F fn) const -> decltype(fn(*get())) {
+  auto Then(F fn) const->decltype(fn(*get())) {
     if (is_error())
       return AsStatus();
     return fn(*get());
   }
 
+  template <class F>
+  auto Then(F fn)->decltype(fn(std::move(*get()))) {
+    if (is_error())
+      return AsStatus();
+    return fn(std::move(*get()));
+  }
+
  private:
   const T* unwrap() const {
     assert(is_ok());
diff --git a/lib/overnet/status_test.cc b/lib/overnet/vocabulary/status_test.cc
similarity index 100%
rename from lib/overnet/status_test.cc
rename to lib/overnet/vocabulary/status_test.cc
diff --git a/lib/overnet/timer.h b/lib/overnet/vocabulary/time.h
similarity index 69%
rename from lib/overnet/timer.h
rename to lib/overnet/vocabulary/time.h
index 6ab3044..853113d 100644
--- a/lib/overnet/timer.h
+++ b/lib/overnet/vocabulary/time.h
@@ -4,15 +4,6 @@
 
 #pragma once
 
-#include <stdint.h>
-#include <limits>
-#include <ostream>
-#include <type_traits>
-#include "callback.h"
-#include "optional.h"
-
-#include <iostream>
-
 namespace overnet {
 
 // Number of microseconds per millisecond.
@@ -208,92 +199,4 @@
   return a.after_epoch() != b.after_epoch();
 }
 
-class Timeout;
-
-class Timer {
-  friend class Timeout;
-
- public:
-  virtual TimeStamp Now() = 0;
-
-  template <class F>
-  void At(TimeStamp t, F f);
-  void At(TimeStamp t, StatusCallback cb);
-
- protected:
-  template <class T>
-  T* TimeoutStorage(Timeout* timeout);
-
-  static void FireTimeout(Timeout* timeout, Status status);
-
- private:
-  virtual void InitTimeout(Timeout* timeout, TimeStamp when) = 0;
-  virtual void CancelTimeout(Timeout* timeout, Status status) = 0;
-};
-
-class Timeout {
-  friend class Timer;
-
- public:
-  static constexpr uint64_t kMaxTimerStorage = 5 * sizeof(void*);
-
-  Timeout(const Timeout&) = delete;
-  Timeout& operator=(const Timeout&) = delete;
-
-  // Initialize a timeout for timestamp when. cb will be called when the timeout
-  // expires (with status OK) or when the timeout is cancelled (with non-OK
-  // status).
-  Timeout(Timer* timer, TimeStamp when, StatusCallback cb)
-      : timer_(timer), cb_(std::move(cb)) {
-    assert(!cb_.empty());
-    timer_->InitTimeout(this, when);
-  }
-
-  ~Timeout() {
-#ifndef NDEBUG
-    assert(!destroyed_);
-    destroyed_ = true;
-#endif
-    if (!cb_.empty())
-      Cancel();
-    assert(cb_.empty());
-  }
-
-  void Cancel(Status status = Status::Cancelled()) {
-    timer_->CancelTimeout(this, std::move(status));
-  }
-
- private:
-#ifndef NDEBUG
-  bool destroyed_ = false;
-#endif
-  Timer* const timer_;
-  StatusCallback cb_;
-  typename std::aligned_storage<kMaxTimerStorage>::type storage_;
-};
-
-template <class T>
-T* Timer::TimeoutStorage(Timeout* timeout) {
-  static_assert(Timeout::kMaxTimerStorage >= sizeof(T),
-                "Must have enough storage in Timeout for Timer data");
-  return reinterpret_cast<T*>(&timeout->storage_);
-}
-
-inline void Timer::FireTimeout(Timeout* timeout, Status status) {
-  if (timeout->cb_.empty())
-    return;
-  auto cb = std::move(timeout->cb_);
-  cb(status);
-}
-
-template <class F>
-void Timer::At(TimeStamp t, F f) {
-  At(t, StatusCallback(ALLOCATED_CALLBACK,
-                       [f = std::move(f)](const Status& status) mutable {
-                         if (status.is_ok()) {
-                           f();
-                         }
-                       }));
-}
-
 }  // namespace overnet
diff --git a/packages/examples/all b/packages/examples/all
index df80edc..80670f6 100644
--- a/packages/examples/all
+++ b/packages/examples/all
@@ -13,6 +13,7 @@
         "garnet/packages/examples/mediaplayer",
         "garnet/packages/examples/netconnector",
         "garnet/packages/examples/netstack",
+        "garnet/packages/examples/overnet",
         "garnet/packages/examples/rust",
         "garnet/packages/examples/scenic",
         "garnet/packages/examples/tcp",
diff --git a/packages/examples/overnet b/packages/examples/overnet
new file mode 100644
index 0000000..5237215
--- /dev/null
+++ b/packages/examples/overnet
@@ -0,0 +1,8 @@
+{
+    "packages": [
+        "//garnet/examples/overnet/echo/client:overnet-echo-client",
+        "//garnet/examples/overnet/echo/server:overnet-echo-server",
+        "//garnet/examples/overnet/interface_passing/client:overnet-interface-passing-client",
+        "//garnet/examples/overnet/interface_passing/server:overnet-interface-passing-server"
+    ]
+}
diff --git a/packages/tests/overnet b/packages/tests/overnet
index 4de6c6f..e658e64 100644
--- a/packages/tests/overnet
+++ b/packages/tests/overnet
@@ -1,8 +1,13 @@
 {
     "packages": [
-        "//garnet/lib/overnet:overnet_tests"
+        "//garnet/lib/overnet:overnet_tests",
+        "//garnet/lib/overnet:overnet_fuzzers",
+        "//garnet/bin/overnet/overnetstack:overnetstack_tests"
     ],
     "host_tests": [
         "//garnet/lib/overnet:overnet_unittests"
+    ],
+    "labels": [
+        "//garnet/lib/overnet:host_overnet_fuzzers"
     ]
 }
diff --git a/public/fidl/fuchsia.overnet/overnet.fidl b/public/fidl/fuchsia.overnet/overnet.fidl
index 884c11c..34b0de0 100644
--- a/public/fidl/fuchsia.overnet/overnet.fidl
+++ b/public/fidl/fuchsia.overnet/overnet.fidl
@@ -4,11 +4,26 @@
 
 library fuchsia.overnet;
 
+using NodeId = uint64;
+
 [Discoverable]
 interface Overnet {
     1: ListPeers() -> (vector<Peer> peers);
+    // TODO(ctiller): remove this, and move to something that looks way more like an explicit registry.
+    2: RegisterService(string service_name, ServiceProvider provider);
+    3: ConnectToService(NodeId node, string service_name, handle<channel> chan);
+};
+
+interface ServiceProvider {
+    1: ConnectToService(string service_name, handle<channel> chan);
+};
+
+struct PeerDescription {
+    vector<string> services;
 };
 
 struct Peer {
-    uint64 id;
+    NodeId id;
+    bool is_self;
+    PeerDescription description;
 };