const ControlFlowNode *
MatchTableAbstract::apply_action(Packet *pkt)
{
  entry_handle_t handle;
  bool hit;

  ReadLock lock = lock_read();

  const ActionEntry &action_entry = lookup(*pkt, &hit, &handle);

  // we're holding the lock for this...
  if(hit) {
    ELOGGER->table_hit(*pkt, *this, handle);
    BMLOG_DEBUG_PKT(*pkt, "Table '{}': hit with handle {}",
		    get_name(), handle);
  }
  else {
    ELOGGER->table_miss(*pkt, *this);
    BMLOG_DEBUG_PKT(*pkt, "Table '{}': miss", get_name());
  }

  BMLOG_DEBUG_PKT(*pkt, "Action entry is {}", action_entry);

  action_entry.action_fn(pkt);

  const ControlFlowNode *next_node = action_entry.next_node;

  return next_node;
}
Beispiel #2
0
void
Deparser::deparse(Packet *pkt) const {
  PHV *phv = pkt->get_phv();
  BMELOG(deparser_start, *pkt, *this);
  // TODO(antonin)
  // this is temporary while we experiment with the debugger
  DEBUGGER_NOTIFY_CTR(
      Debugger::PacketId::make(pkt->get_packet_id(), pkt->get_copy_id()),
      DBG_CTR_DEPARSER | get_id());
  BMLOG_DEBUG_PKT(*pkt, "Deparser '{}': start", get_name());
  update_checksums(pkt);
  char *data = pkt->prepend(get_headers_size(*phv));
  int bytes_parsed = 0;
  // invalidating headers, and resetting header stacks is done in the Packet
  // destructor, when the PHV is released
  for (auto it = headers.begin(); it != headers.end(); ++it) {
    Header &header = phv->get_header(*it);
    if (header.is_valid()) {
      BMELOG(deparser_emit, *pkt, *it);
      BMLOG_DEBUG_PKT(*pkt, "Deparsing header '{}'", header.get_name());
      header.deparse(data + bytes_parsed);
      bytes_parsed += header.get_nbytes_packet();
      // header.mark_invalid();
    }
  }
  // phv->reset_header_stacks();
  BMELOG(deparser_done, *pkt, *this);
  DEBUGGER_NOTIFY_CTR(
      Debugger::PacketId::make(pkt->get_packet_id(), pkt->get_copy_id()),
      DBG_CTR_EXIT(DBG_CTR_DEPARSER) | get_id());
  BMLOG_DEBUG_PKT(*pkt, "Deparser '{}': end", get_name());
}
void
Pipeline::apply(Packet *pkt) {
  ELOGGER->pipeline_start(*pkt, *this);
  BMLOG_DEBUG_PKT(*pkt, "Pipeline '{}': start", get_name());
  const ControlFlowNode *node = first_node;
  while (node) {
    node = (*node)(pkt);
  }
  ELOGGER->pipeline_done(*pkt, *this);
  BMLOG_DEBUG_PKT(*pkt, "Pipeline '{}': end", get_name());
}
void
ParserOpSet<ParserLookAhead>::operator()(Packet *pkt, const char *data,
                                         size_t *bytes_parsed) const {
  (void) bytes_parsed;
  static thread_local ByteContainer bc;
  PHV *phv = pkt->get_phv();
  Field &f_dst = phv->get_field(dst.header, dst.offset);
  int f_bits = f_dst.get_nbits();
  /* I expect the first case to be the most common one. In the first case, we
     extract the packet bytes to the field bytes and sync the bignum value. The
     second case requires extracting the packet bytes to the ByteContainer, then
     importing the bytes into the field's bignum value, then finally exporting
     the bignum value to the field's byte array. I could alternatively write a
     more general extract function which would account for a potential size
     difference between source and destination. */
  // TODO(antonin)
  if (src.bitwidth == f_bits) {
    data += src.byte_offset;
    f_dst.extract(data, src.bit_offset);
  } else {
    bc.clear();
    src.peek(data, &bc);
    f_dst.set(bc);
  }
  BMLOG_DEBUG_PKT(
    *pkt,
    "Parser set: setting field ({}, {}) from lookahead ({}, {}), "
    "new value is {}",
    dst.header, dst.offset, src.bit_offset, src.bitwidth, f_dst);
}
void
ParserOpSet<Data>::operator()(Packet *pkt, const char *data,
                              size_t *bytes_parsed) const {
  (void) bytes_parsed; (void) data;
  PHV *phv = pkt->get_phv();
  Field &f_dst = phv->get_field(dst.header, dst.offset);
  f_dst.set(src);
  BMLOG_DEBUG_PKT(*pkt, "Parser set: setting field ({}, {}) to {}",
                  dst.header, dst.offset, f_dst);
}
void SimpleSwitch::transmit_thread() {
  while (1) {
    std::unique_ptr<Packet> packet;
    output_buffer.pop_back(&packet);
    ELOGGER->packet_out(*packet);
    BMLOG_DEBUG_PKT(*packet, "Transmitting packet of size {} out of port {}",
                    packet->get_data_size(), packet->get_egress_port());
    transmit_fn(packet->get_egress_port(),
                packet->data(), packet->get_data_size());
  }
}
void
ParserOpSet<ArithExpression>::operator()(Packet *pkt, const char *data,
                                         size_t *bytes_parsed) const {
  (void) bytes_parsed; (void) data;
  PHV *phv = pkt->get_phv();
  Field &f_dst = phv->get_field(dst.header, dst.offset);
  src.eval(*phv, &f_dst);
  BMLOG_DEBUG_PKT(
    *pkt,
    "Parser set: setting field ({}, {}) from expression, new value is {}",
    dst.header, dst.offset, f_dst);
}
bool
Checksum::verify(const Packet &pkt) const {
  if (!is_checksum_condition_met(pkt)) {
    BMLOG_TRACE_PKT(
        pkt, "Skipping checksum '{}' verification because condition not met",
        get_name());
    return true;
  } else {
    bool valid = verify_(pkt);
    BMLOG_DEBUG_PKT(pkt, "Verifying checksum '{}': {}", get_name(), valid);
    return valid;
  }
}
void
Checksum::update(Packet *pkt) const {
  // is it a good idea to put this in the implementation; should the deparser be
  // aware that nothing was updated?
  if (!is_checksum_condition_met(*pkt)) {
    BMLOG_TRACE_PKT(
        *pkt, "Skipping checksum '{}' update because condition not met",
        get_name());
  } else {
    BMLOG_DEBUG_PKT(*pkt, "Updating checksum '{}'", get_name());
    update_(pkt);
  }
}
Beispiel #10
0
void
Parser::parse(Packet *pkt) const {
  BMELOG(parser_start, *pkt, *this);
  // TODO(antonin)
  // this is temporary while we experiment with the debugger
  DEBUGGER_NOTIFY_CTR(
      Debugger::PacketId::make(pkt->get_packet_id(), pkt->get_copy_id()),
      DBG_CTR_PARSER | get_id());
  BMLOG_DEBUG_PKT(*pkt, "Parser '{}': start", get_name());
  const char *data = pkt->data();
  if (!init_state) return;
  const ParseState *next_state = init_state;
  size_t bytes_parsed = 0;
  while (next_state) {
    next_state = (*next_state)(pkt, data, &bytes_parsed);
    BMLOG_TRACE("Bytes parsed: {}", bytes_parsed);
  }
  pkt->remove(bytes_parsed);
  BMELOG(parser_done, *pkt, *this);
  DEBUGGER_NOTIFY_CTR(
      Debugger::PacketId::make(pkt->get_packet_id(), pkt->get_copy_id()),
      DBG_CTR_EXIT(DBG_CTR_PARSER) | get_id());
  BMLOG_DEBUG_PKT(*pkt, "Parser '{}': end", get_name());
}
Beispiel #11
0
const ParseState *
ParseState::find_next_state(Packet *pkt, const char *data,
                            size_t *bytes_parsed) const {
  // execute parser ops
  PHV *phv = pkt->get_phv();

  register_sync.lock_registers();

  for (auto &parser_op : parser_ops)
    (*parser_op)(pkt, data + *bytes_parsed, bytes_parsed);

  register_sync.unlock_registers();

  if (!has_switch) {
    BMLOG_DEBUG_PKT(
      *pkt,
      "Parser state '{}' has no switch, going to default next state",
      get_name());
    return default_next_state;
  }

  // build key
  static thread_local ByteContainer key;
  key.clear();
  key_builder(*phv, data + *bytes_parsed, &key);

  BMLOG_DEBUG_PKT(*pkt, "Parser state '{}': key is {}",
                  get_name(), key.to_hex());

  // try the matches in order
  const ParseState *next_state = NULL;
  for (const auto &switch_case : parser_switch)
    if (switch_case.match(key, &next_state)) return next_state;

  return default_next_state;
}
void SimpleSwitch::pipeline_thread() {
  Pipeline *ingress_mau = this->get_pipeline("ingress");
  Pipeline *egress_mau = this->get_pipeline("egress");
  Parser *parser = this->get_parser("parser");
  Deparser *deparser = this->get_deparser("deparser");
  PHV *phv;

  while (1) {
    std::unique_ptr<Packet> packet;
    input_buffer.pop_back(&packet);
    phv = packet->get_phv();

    int ingress_port = packet->get_ingress_port();
    BMLOG_DEBUG_PKT(*packet, "Processing packet received on port {}",
                    ingress_port);

    phv->get_field("standard_metadata.ingress_port").set(ingress_port);
    ingress_port = phv->get_field("standard_metadata.ingress_port").get_int();
    std::cout << ingress_port << std::endl;

    parser->parse(packet.get());
    ingress_mau->apply(packet.get());

    int egress_port = phv->get_field("standard_metadata.egress_port").get_int();
    BMLOG_DEBUG_PKT(*packet, "Egress port is {}", egress_port);

    int learn_id = phv->get_field("intrinsic_metadata.learn_id").get_int();
    BMLOG_DEBUG_PKT(*packet, "Learn id is {}", learn_id);

    unsigned int mgid = phv->get_field("intrinsic_metadata.mgid").get_uint();
    BMLOG_DEBUG_PKT(*packet, "Mgid is {}", mgid);

    if (learn_id > 0) {
      get_learn_engine()->learn(learn_id, *packet.get());
      phv->get_field("intrinsic_metadata.learn_id").set(0);
    }

    if (egress_port == 511 && mgid == 0) {
      BMLOG_DEBUG_PKT(*packet, "Dropping packet");
      continue;
    }

    if (mgid != 0) {
      assert(mgid == 1);
      phv->get_field("intrinsic_metadata.mgid").set(0);
      packet_id_t copy_id = 1;
      const auto pre_out = pre->replicate({mgid});
      for (const auto &out : pre_out) {
        egress_port = out.egress_port;
        if (ingress_port == egress_port) continue;  // pruning
        BMLOG_DEBUG_PKT(*packet, "Replicating packet on port {}", egress_port);
        std::unique_ptr<Packet> packet_copy = packet->clone_with_phv_ptr();
        packet_copy->set_egress_port(egress_port);
        egress_mau->apply(packet_copy.get());
        deparser->deparse(packet_copy.get());
        output_buffer.push_front(std::move(packet_copy));
      }
    } else {
      packet->set_egress_port(egress_port);
      egress_mau->apply(packet.get());
      deparser->deparse(packet.get());
      output_buffer.push_front(std::move(packet));
    }
  }
}
void
SimpleSwitch::egress_thread(size_t worker_id) {
  PHV *phv;

  while (1) {
    std::unique_ptr<Packet> packet;
    size_t port;
    egress_buffers.pop_back(worker_id, &port, &packet);

    Deparser *deparser = this->get_deparser("deparser");
    Pipeline *egress_mau = this->get_pipeline("egress");

    phv = packet->get_phv();

    if (with_queueing_metadata) {
      auto enq_timestamp =
          phv->get_field("queueing_metadata.enq_timestamp").get<ts_res::rep>();
      phv->get_field("queueing_metadata.deq_timedelta").set(
          get_ts().count() - enq_timestamp);
      phv->get_field("queueing_metadata.deq_qdepth").set(
          egress_buffers.size(port));
    }

    phv->get_field("standard_metadata.egress_port").set(port);

    Field &f_egress_spec = phv->get_field("standard_metadata.egress_spec");
    f_egress_spec.set(0);

    phv->get_field("standard_metadata.packet_length").set(
        packet->get_register(PACKET_LENGTH_REG_IDX));

    egress_mau->apply(packet.get());

    Field &f_clone_spec = phv->get_field("standard_metadata.clone_spec");
    unsigned int clone_spec = f_clone_spec.get_uint();

    // EGRESS CLONING
    if (clone_spec) {
      BMLOG_DEBUG_PKT(*packet, "Cloning packet at egress");
      int egress_port = get_mirroring_mapping(clone_spec & 0xFFFF);
      if (egress_port >= 0) {
        f_clone_spec.set(0);
        p4object_id_t field_list_id = clone_spec >> 16;
        std::unique_ptr<Packet> packet_copy =
            packet->clone_with_phv_reset_metadata_ptr();
        PHV *phv_copy = packet_copy->get_phv();
        FieldList *field_list = this->get_field_list(field_list_id);
        for (const auto &p : *field_list) {
          phv_copy->get_field(p.header, p.offset)
            .set(phv->get_field(p.header, p.offset));
        }
        phv_copy->get_field("standard_metadata.instance_type")
            .set(PKT_INSTANCE_TYPE_EGRESS_CLONE);
        enqueue(egress_port, std::move(packet_copy));
      }
    }

    // TODO(antonin): should not be done like this in egress pipeline
    int egress_spec = f_egress_spec.get_int();
    if (egress_spec == 511) {  // drop packet
      BMLOG_DEBUG_PKT(*packet, "Dropping packet at the end of egress");
      continue;
    }

    deparser->deparse(packet.get());

    // RECIRCULATE
    if (phv->has_field("intrinsic_metadata.recirculate_flag")) {
      Field &f_recirc = phv->get_field("intrinsic_metadata.recirculate_flag");
      if (f_recirc.get_int()) {
        BMLOG_DEBUG_PKT(*packet, "Recirculating packet");
        p4object_id_t field_list_id = f_recirc.get_int();
        f_recirc.set(0);
        FieldList *field_list = this->get_field_list(field_list_id);
        // TODO(antonin): just like for resubmit, there is no need for a copy
        // here, but it is more convenient for this first prototype
        std::unique_ptr<Packet> packet_copy = packet->clone_no_phv_ptr();
        PHV *phv_copy = packet_copy->get_phv();
        phv_copy->reset_metadata();
        for (const auto &p : *field_list) {
          phv_copy->get_field(p.header, p.offset)
              .set(phv->get_field(p.header, p.offset));
        }
        phv_copy->get_field("standard_metadata.instance_type")
            .set(PKT_INSTANCE_TYPE_RECIRC);
        size_t packet_size = packet_copy->get_data_size();
        packet_copy->set_register(PACKET_LENGTH_REG_IDX, packet_size);
        phv_copy->get_field("standard_metadata.packet_length").set(packet_size);
        input_buffer.push_front(std::move(packet_copy));
        continue;
      }
    }

    output_buffer.push_front(std::move(packet));
  }
void
SimpleSwitch::ingress_thread() {
  PHV *phv;

  while (1) {
    std::unique_ptr<Packet> packet;
    input_buffer.pop_back(&packet);

    // TODO(antonin): only update these if swapping actually happened?
    Parser *parser = this->get_parser("parser");
    Pipeline *ingress_mau = this->get_pipeline("ingress");

    phv = packet->get_phv();

    int ingress_port = packet->get_ingress_port();
    (void) ingress_port;
    BMLOG_DEBUG_PKT(*packet, "Processing packet received on port {}",
                    ingress_port);

    /* This looks like it comes out of the blue. However this is needed for
       ingress cloning. The parser updates the buffer state (pops the parsed
       headers) to make the deparser's job easier (the same buffer is
       re-used). But for ingress cloning, the original packet is needed. This
       kind of looks hacky though. Maybe a better solution would be to have the
       parser leave the buffer unchanged, and move the pop logic to the
       deparser. TODO? */
    const Packet::buffer_state_t packet_in_state = packet->save_buffer_state();
    parser->parse(packet.get());

    ingress_mau->apply(packet.get());

    packet->reset_exit();

    Field &f_egress_spec = phv->get_field("standard_metadata.egress_spec");
    int egress_spec = f_egress_spec.get_int();

    Field &f_clone_spec = phv->get_field("standard_metadata.clone_spec");
    unsigned int clone_spec = f_clone_spec.get_uint();

    int learn_id = 0;
    unsigned int mgid = 0u;

    if (phv->has_field("intrinsic_metadata.lf_field_list")) {
      Field &f_learn_id = phv->get_field("intrinsic_metadata.lf_field_list");
      learn_id = f_learn_id.get_int();
    }

    // detect mcast support, if this is true we assume that other fields needed
    // for mcast are also defined
    if (phv->has_field("intrinsic_metadata.mcast_grp")) {
      Field &f_mgid = phv->get_field("intrinsic_metadata.mcast_grp");
      mgid = f_mgid.get_uint();
    }

    int egress_port;

    // INGRESS CLONING
    if (clone_spec) {
      BMLOG_DEBUG_PKT(*packet, "Cloning packet at ingress");
      egress_port = get_mirroring_mapping(clone_spec & 0xFFFF);
      f_clone_spec.set(0);
      if (egress_port >= 0) {
        const Packet::buffer_state_t packet_out_state =
            packet->save_buffer_state();
        packet->restore_buffer_state(packet_in_state);
        p4object_id_t field_list_id = clone_spec >> 16;
        auto packet_copy = copy_ingress_pkt(
            packet, PKT_INSTANCE_TYPE_INGRESS_CLONE, field_list_id);
        // we need to parse again
        // the alternative would be to pay the (huge) price of PHV copy for
        // every ingress packet
        parser->parse(packet_copy.get());
        enqueue(egress_port, std::move(packet_copy));
        packet->restore_buffer_state(packet_out_state);
      }
    }

    // LEARNING
    if (learn_id > 0) {
      get_learn_engine()->learn(learn_id, *packet.get());
    }

    // RESUBMIT
    if (phv->has_field("intrinsic_metadata.resubmit_flag")) {
      Field &f_resubmit = phv->get_field("intrinsic_metadata.resubmit_flag");
      if (f_resubmit.get_int()) {
        BMLOG_DEBUG_PKT(*packet, "Resubmitting packet");
        // get the packet ready for being parsed again at the beginning of
        // ingress
        packet->restore_buffer_state(packet_in_state);
        p4object_id_t field_list_id = f_resubmit.get_int();
        f_resubmit.set(0);
        // TODO(antonin): a copy is not needed here, but I don't yet have an
        // optimized way of doing this
        auto packet_copy = copy_ingress_pkt(
            packet, PKT_INSTANCE_TYPE_RESUBMIT, field_list_id);
        input_buffer.push_front(std::move(packet_copy));
        continue;
      }
    }

    Field &f_instance_type = phv->get_field("standard_metadata.instance_type");

    // MULTICAST
    int instance_type = f_instance_type.get_int();
    if (mgid != 0) {
      BMLOG_DEBUG_PKT(*packet, "Multicast requested for packet");
      Field &f_rid = phv->get_field("intrinsic_metadata.egress_rid");
      const auto pre_out = pre->replicate({mgid});
      auto packet_size = packet->get_register(PACKET_LENGTH_REG_IDX);
      for (const auto &out : pre_out) {
        egress_port = out.egress_port;
        // if (ingress_port == egress_port) continue; // pruning
        BMLOG_DEBUG_PKT(*packet, "Replicating packet on port {}", egress_port);
        f_rid.set(out.rid);
        f_instance_type.set(PKT_INSTANCE_TYPE_REPLICATION);
        std::unique_ptr<Packet> packet_copy = packet->clone_with_phv_ptr();
        packet_copy->set_register(PACKET_LENGTH_REG_IDX, packet_size);
        enqueue(egress_port, std::move(packet_copy));
      }
      f_instance_type.set(instance_type);

      // when doing multicast, we discard the original packet
      continue;
    }

    egress_port = egress_spec;
    BMLOG_DEBUG_PKT(*packet, "Egress port is {}", egress_port);

    if (egress_port == 511) {  // drop packet
      BMLOG_DEBUG_PKT(*packet, "Dropping packet at the end of ingress");
      continue;
    }

    enqueue(egress_port, std::move(packet));
  }
}