コード例 #1
0
int
main(int argc, char* argv[])
{
  // Parse command line arguments.
  if (argc == 2) {
    if (std::string(argv[1]) == "once")
      once = true;
  }

  // TODO: Use sigaction.
  signal(SIGINT, on_signal);
  signal(SIGKILL, on_signal);
  signal(SIGHUP, on_signal);

  // Build a server socket that will accept network connections.
  Ipv4_socket_address addr(Ipv4_address::any(), 5000);
  Ipv4_stream_socket server(addr);

  set_option(server.fd(), reuse_address(true));

  // TODO: Handle exceptions.

  // Configure the dataplane. Ports must be added before
  // applications are loaded.
  for (int i = 0; i < 2; i++) {
    dp.add_port(&ports[i]);
    port_thread[i].assign(i, port_work);
  }

  dp.add_virtual_ports();
  dp.load_application("apps/wire.app");
  dp.up();

  ss.add_read(server.fd());


  // FIXME: Factor the accept/ingress code into something a little more 
  // reusable.

  // Accept connections from the server socket.
  auto accept = [&](Ipv4_stream_socket& server)
  {
    std::cout << "[flowpath] accept\n";
    
    // Accept the connection.
    Ipv4_socket_address addr;
    Ipv4_stream_socket client = server.accept(addr);
    if (!client)
      return; // TODO: Log the error.

    // If we already have two endpoints, just return, which will cause 
    // the socket to be closed.
    if (nports == 2) {
      std::cout << "[flowpath] reject connection " << addr.port() << '\n';
      return;
    }
    std::cout << "[flowpath] accept connection " << addr.port() << '\n';

    // Update the poll set.
    ss.add_read(client.fd());

    // Bind the socket to a port.
    // TODO: Emit a port status change to the application. Does
    // that happen implicitly, or do we have to cause the dataplane
    // to do it.
    Port_tcp* port = nullptr;
    if (nports == 0)
      port = &ports[0];
    if (nports == 1)
      port = &ports[1];
    port->attach(std::move(client));
    port_thread[nports].run();
    ++nports;

    // Once we have two connections, start the timer.
    if (nports == 2)
      start = now();

    // Notify the application of the port change.
    Application* app = dp.get_application();
    app->port_changed(*port);
  };

  // Main loop.
  running = true;
  while (running) {
    // Wait for 100 milliseconds. Note that this can fail with
    // EINTR, which really isn't an error.
    //
    // NOTE: It seems the common practice is to re-poll when EINTR
    // occurs since like you said, it's not really an error. Most
    // impls just stick it in a do-while(errno != EINTR);
    int n = select(ss, 10ms);
    if (n <= 0)
      continue;

    // Is a connection available?
    if (ss.can_read(server.fd()))
      accept(server);
  }

  // Take the dataplane down.
  port_thread[0].halt();
  port_thread[1].halt();
  dp.down();
  dp.unload_application();

  // Write out stats.
  double s = duration.count();
  double Mb = double(nbytes * 8) / (1 << 20);
  double Mbps = Mb / s;
  long Pps = npackets / s;

  // FIXME: Make this pretty.
  // std::cout.imbue(std::locale(""));
  std::cout.precision(6);
  std::cout << "processed " << npackets << " packets in " 
            << s << " seconds (" << Pps << " Pps)\n";
  std::cout << "processed " << nbytes << " bytes in " 
            << s << " seconds (" << Mbps << " Mbps)\n";

  return 0;
}
コード例 #2
0
ファイル: timer.hpp プロジェクト: thehexia/filterflow
 // Get current elapsed time.
 double elapsed() const
 {
   Fp_seconds secs = Clock::now() - start;
   return secs.count();
 }
コード例 #3
0
int
main()
{
  // TODO: Use sigaction.
  signal(SIGINT, on_signal);
  signal(SIGKILL, on_signal);
  signal(SIGHUP, on_signal);

  // Build a server socket that will accept network connections.
  Ipv4_socket_address addr(Ipv4_address::any(), 5000);
  Ipv4_stream_socket server(addr);
  set_option(server.fd(), reuse_address(true));
  set_option(server.fd(), nonblocking(true));

  // TODO: Handle exceptions.

  // Pre-create all standard ports.
  Port_eth_tcp port1(1);
  Port_eth_tcp port2(2);

  // Reporting stastics init.
  Port::Statistics p1_stats = {0,0,0,0};
  Port::Statistics p2_stats = {0,0,0,0};

  // Egress queue.
  Queue<Context> egress_queue;

  // Configure the dataplane. Ports must be added before
  // applications are loaded.
  fp::Dataplane dp = "dp1";
  dp.add_port(&port1);
  dp.add_port(&port2);
  dp.add_virtual_ports();
  dp.load_application("apps/wire.app");
  dp.up();

  // Set up the initial polling state.
  Epoll_set eps(3);
  // Current number of ports.
  int nports = 0; 
  // Add the server socket to the select set.
  eps.add(server.fd());

  // FIXME: Factor the accept/ingress code into something
  // a little more reusable.

  // Accept connections from the server socket.
  auto accept = [&](Ipv4_stream_socket& server)
  {
    // Accept the connection.
    Ipv4_socket_address addr;
    Ipv4_stream_socket client = server.accept(addr);
    if (!client)
      return; // TODO: Log the error.

    // If we already have two endpoints, just return, which
    // will cause the socket to be closed.
    if (nports == 2) {
      std::cout << "[flowpath] reject connection " << addr.port() << '\n';
      return;
    }
    std::cout << "[flowpath] accept connection " << addr.port() << '\n';

    // Update the poll set.
    eps.add(client.fd());
    // Set non-blocking.
    set_option(client.fd(), nonblocking(true));

    // Bind the socket to a port.
    // TODO: Emit a port status change to the application. Does
    // that happen implicitly, or do we have to cause the dataplane
    // to do it.
    Port_tcp* port = nullptr;
    if (nports == 0)
      port = &port1;
    if (nports == 1)
      port = &port2;
    port->attach(std::move(client));
    ++nports;

    // Notify the application of the port change.
    Application* app = dp.get_application();
    app->port_changed(*port);
  };

  // Handle input from the client socket.
  //
  // TODO: This defines the basic ingress pipeline. How
  // do we refactor this to make it reasonably composable.
  auto ingress = [&](Port_eth_tcp& port)
  {
    //std::cout << "[wire] ingress on: " << port.id() << '\n';
    // Ingress the packet.
    Byte buf[2048];
    Context cxt(buf, &dp);
    bool ok = port.recv(cxt);

    // Handle error or closure.
    if (!ok) {
      // Detach the socket.
      Ipv4_stream_socket client = port.detach();

      // Notify the application of the port change.
      Application* app = dp.get_application();
      app->port_changed(port);

      // Update the poll set.
      eps.del(client.fd());
      --nports;
      return;
    }

    // Otherwise, process the application.
    //
    // TODO: This really just runs one step of the pipeline. This needs
    // to be a loop that continues processing until there are no further
    // table redirections.
    Application* app = dp.get_application();
    app->process(cxt);

    // Assuming there's an output send to it.
    if (cxt.output_port())
      egress_queue.enqueue(cxt);
  };

  // Select a port to handle input.
  auto input = [&](int fd)
  {
    if (fd == port1.fd())
      ingress(port1);
    else
      ingress(port2);
  };


  // Apply egress processing on a port.
  auto egress = [&](Port_eth_tcp& port)
  {
    while (egress_queue.size()) {
      Context cxt = egress_queue.dequeue();
      port.send(cxt);
    }
  };

  // Select a port to handle output.
  auto output = [&](int fd)
  {
    if (fd == port1.fd())
      egress(port1);
    else
      egress(port2);
  };

  // Report statistics.
  auto report = [&]()
  {
    auto p1_curr = port1.stats();
    auto p2_curr = port2.stats();
    // Clears the screen.
    system("clear");
    std::cout << "Receive Rate  (Pkt/s): " << (p2_curr.packets_rx -
      p2_stats.packets_rx) << '\n';
    std::cout << "Receive Rate   (Gb/s): " <<  ((p2_curr.bytes_rx -
      p2_stats.bytes_rx) * 8.0 / (1 << 30)) << '\n';
    std::cout << "Transmit Rate (Pkt/s): " << (p1_curr.packets_tx -
      p1_stats.packets_tx) << "\n";
    std::cout << "Transmit Rate  (Gb/s): " <<  ((p1_curr.bytes_tx -
      p1_stats.bytes_tx) * 8.0 / (1 << 30)) << "\n\n";
    p1_stats = p1_curr;
    p2_stats = p2_curr;
  };

  // Main lookup.
  running = true;

  // Init timer for reporting.
  Time last = now();
  Time curr;
  while (running) {
    // Wait for 100 milliseconds. Note that this can fail with
    // EINTR, which really isn't an error.
    //
    // NOTE: It seems the common practice is to re-poll when EINTR
    // occurs since like you said, it's not really an error. Most
    // impls just stick it in a do-while(errno != EINTR);
    epoll(eps, 100);

    if (eps.can_read(server.fd()))
      accept(server);
    if (eps.can_read(port2.fd()))
      input(port2.fd());
    if (eps.can_write(port1.fd()))
      output(port1.fd());

    curr = now();
    Fp_seconds dur = curr - last;
    double duration = dur.count();
    if (duration >= 1.0) {
      report();
      last = curr;
    }
  }

  eps.clear();

  // Take the dataplane down.
  dp.down();
  dp.unload_application();

  return 0;
}
コード例 #4
0
ファイル: fetch.cpp プロジェクト: flowgrammable/freeflow
// Fetch works like expect, except that it connects to the specified 
// source instead of accepting a connection. 
//
// TODO: This could legitimately be part of the expect framework, but
// using options to distinguish between passive and active connections.
int
fetch(int argc, char* argv[])
{
  // FIXME: We should be using our own usage function, not the general
  // application's.
  if (argc < 5) {
    std::cerr << "error: too few arguments to 'expect'\n";
    return usage(std::cerr);
  }

  std::string path = argv[2];
  std::string host = argv[3];
  std::string port = argv[4];


  int iterations = 1;
  if (argc > 5)
    iterations = std::stoi(argv[5]);

  // Convert the host name to an address.
  Ipv4_address addr;
  try {
    addr = host;
  } 
  catch (std::runtime_error& err) {
    std::cerr << "error: " << err.what() << '\n';
    return 1;
  }

  // Convert the port number.
  std::uint16_t portnum;
  std::stringstream ss(port);
  ss >> portnum;
  if (ss.fail() && !ss.eof()) {
    std::cerr << "error: invalid port '" << port << "'\n";
    return 1;
  }

  // Build and connect the socket.
  Ipv4_stream_socket sock;
  if (!sock.connect({addr, portnum})) {
    std::cerr << "error: could not connect to host\n";
    return -1;
  }

  // Open an offline stream capture.
  cap::Stream cap(cap::offline(argv[2]));
  if (cap.link_type() != cap::ethernet_link) {
    std::cerr << "error: input is not ethernet\n";
    return 1;
  }

  // Iterate over each packet and and send each packet to the
  // connected host.
  std::uint64_t n = 0;
  std::uint64_t b = 0;

  // Records the start time.
  Time start;
  bool started = false;

  cap::Packet p;
  while (cap.get(p)) {
    // FIXME: This is broken. We know exactly how many bytes are expected 
    // in a packet. We should receive exactly that many.
    char buf[4096];
    assert(p.captured_size() < 4096);
    for (int i = 0; i < iterations; i++) {
      int k = sock.recv(buf, p.captured_size() + 4);
      // int k = sock.recv(buf, p.captured_size());
      if (k <= 0) {
        if (k < 0) {
          std::cerr << "error: " << std::strerror(errno) << '\n';
          return 1;
        }
        return 0;
      }
      // assert(k == p.captured_size() + 4);

      // Start the timer on receipt of the first packet.
      if (!started) {
        start = now();
        started = true;
      }

      // TODO: Verify that the content captured actually matches the content
      // sent. That seems like a good idea.

      ++n;
      b += p.captured_size();
    }
  }
  Time stop = now();
  sock.close();
  // Make some measurements.
  Fp_seconds dur = stop - start;
  double s = dur.count();
  double Mb = double(b * 8) / (1 << 20);
  double Mbps = Mb / s;
  std::uint64_t Pps = n / s;

  // FIXME: Make this pretty.
  // std::cout.imbue(std::locale(""));
  std::cout << "received " << n << " packets in " 
            << s << " seconds (" << Pps << " Pps)\n";
  std::cout << "received " << b << " bytes in " 
            << s << " seconds (" << Mbps << " Mbps)\n";

  return 0;
}