// Copyright (c) 2016, Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

import "google/protobuf/any.proto";
import "google/rpc/status.proto";
import "p4/config/v1/p4info.proto";
import "p4/v1/p4data.proto";

// This package and its contents are a work-in-progress.

package p4.v1;

option go_package = "github.com/p4lang/p4runtime/go/p4/v1";
option cc_enable_arenas = true;

service P4Runtime {
  // Update one or more P4 entities on the target.
  rpc Write(WriteRequest) returns (WriteResponse) {}
  // Read one or more P4 entities from the target.
  rpc Read(ReadRequest) returns (stream ReadResponse) {}

  // Sets the P4 forwarding-pipeline config.
  rpc SetForwardingPipelineConfig(SetForwardingPipelineConfigRequest)
      returns (SetForwardingPipelineConfigResponse) {}
  // Gets the current P4 forwarding-pipeline config.
  rpc GetForwardingPipelineConfig(GetForwardingPipelineConfigRequest)
      returns (GetForwardingPipelineConfigResponse) {}

  // Represents the bidirectional stream between the controller and the
  // switch (initiated by the controller), and is managed for the following
  // purposes:
  // - connection initiation through client arbitration
  // - indicating switch session liveness: the session is live when switch
  //   sends a positive client arbitration update to the controller, and is
  //   considered dead when either the stream breaks or the switch sends a
  //   negative update for client arbitration
  // - the controller sending/receiving packets to/from the switch
  // - streaming of notifications from the switch
  rpc StreamChannel(stream StreamMessageRequest)
      returns (stream StreamMessageResponse) {}

  rpc Capabilities(CapabilitiesRequest) returns (CapabilitiesResponse) {}
}

//------------------------------------------------------------------------------
message WriteRequest {
  uint64 device_id = 1;
  // Deprecated in v1.4.0
  uint64 role_id = 2 [deprecated=true];
  // Added in v1.4.0
  string role = 6;
  Uint128 election_id = 3;
  // The write batch, comprising a list of Update operations. The P4Runtime
  // server may arbitrarily reorder messages within a batch to maximize
  // performance.
  repeated Update updates = 4;
  enum Atomicity {
    // Required. This is the default behavior. The batch is processed in a
    // non-atomic manner from a data plane point of view. Each operation within
    // the batch must be attempted even if one or more encounter errors.
    // Every data plane packet is guaranteed to be processed according to
    // table contents as they are between two individual operations of the
    // batch, but there could be several packets processed that see each of
    // these intermediate stages.
    CONTINUE_ON_ERROR = 0;
    // Optional. Operations within the batch are committed to data plane until
    // an error is encountered. At this point, the operations must be rolled
    // back such that both software and data plane state is consistent with the
    // state before the batch was attempted. The resulting behavior is
    // all-or-none, except the batch is not atomic from a data plane point of
    // view. Every data plane packet is guaranteed to be processed according to
    // table contents as they are between two individual operations of the
    // batch, but there could be several packets processed that see each of
    // these intermediate stages.
    ROLLBACK_ON_ERROR = 1;
    // Optional. Every data plane packet is guaranteed to be processed according
    // to table contents before the batch began, or after the batch completed
    // and the operations were programmed to the hardware.
    // The batch is therefore treated as a transaction.
    DATAPLANE_ATOMIC = 2;
  }
  Atomicity atomicity = 5;
}

message WriteResponse {}

message ReadRequest {
  uint64 device_id = 1;
  // When specified, only return table entries for the given role.
  // Added in 1.4.0.
  string role = 3;
  repeated Entity entities = 2;
}

message ReadResponse {
  repeated Entity entities = 1;
}

message Update {
  enum Type {
    UNSPECIFIED = 0;
    INSERT = 1;
    MODIFY = 2;
    DELETE = 3;
  }
  Type type = 1;
  Entity entity = 2;
}

message Entity {
  oneof entity {
    ExternEntry extern_entry = 1;
    TableEntry table_entry = 2;
    ActionProfileMember action_profile_member = 3;
    ActionProfileGroup action_profile_group = 4;
    MeterEntry meter_entry = 5;
    DirectMeterEntry direct_meter_entry = 6;
    CounterEntry counter_entry = 7;
    DirectCounterEntry direct_counter_entry = 8;
    PacketReplicationEngineEntry packet_replication_engine_entry = 9;
    ValueSetEntry value_set_entry = 10;
    RegisterEntry register_entry = 11;
    DigestEntry digest_entry = 12;
  }
}

message ExternEntry {
  // the extern_type_id is unique for a given architecture and must be in the
  // range [0x81, 0xfe].
  uint32 extern_type_id = 1;
  uint32 extern_id = 2;  // id of the instance
  .google.protobuf.Any entry = 3;
}

message TableEntry {
  uint32 table_id = 1;
  repeated FieldMatch match = 2;
  TableAction action = 3;
  // Should only be set if the match implies a TCAM lookup, i.e. at least one of
  // the match fields is Optional, Ternary or Range.  A higher number indicates
  // higher priority.  Only a highest priority entry that matches the packet
  // must be selected.  Multiple entries in the same table with the same
  // priority value are permitted.  See Section "TableEntry" in the
  // specification for details of the behavior.
  int32 priority = 4;
  // Metadata (cookie) opaque to the target. There is no requirement of where
  // this is stored, as long as it is returned with the rest of the entry in
  // a Read RPC. This is deprecated in favor of the more flexible metadata
  // field.
  uint64 controller_metadata = 5 [deprecated = true];
  // meter_config, counter_data and meter_counter_data are convenience fields
  // that enable the controller to configure the direct resources associated
  // with the table at the same time as a match-action entry is inserted or
  // modified.
  // Table write:
  //   - If the table does not contain a direct resource, then setting the
  //     corresponding direct resource field in any table write operation will
  //     return an error.
  //   - When inserting a new table entry, leaving these fields unset means that
  //     the direct resources of this table (if any) will assume default values.
  //     For counters, the default value is 0, and for meters, the default value
  //     is always green.
  //   - When updating a table entry, leaving meter_config unset will reset the
  //     meter (if any) to its default configuration, while leaving counter_data
  //     or meter_counter_data unset means that the counter (if any) will not be
  //     updated.
  // Table read:
  //   - If the table does not contain a direct resource, then the corresponding
  //     field will not be set in the read table entry.
  //   - If meter_config is unset in the request, or if the meter has a default
  //     configuration, meter_config will not be set in the response.
  //   - If counter_data or meter_counter_data is unset in the request, it will
  //     be unset in the response as well.
  MeterConfig meter_config = 6;
  CounterData counter_data = 7;
  // Per color counters for tables with a direct meter.
  // Added in v1.4.0
  MeterCounterData meter_counter_data = 12;
  // Set to true if the table entry is being used to update the non-const
  // default action of the table. If true, the "match" field must be empty and
  // the "action" field must be populated with a valid direct action.
  bool is_default_action = 8;
  // The TTL for the entry, in nanoseconds. A value of 0 means that the table
  // entry never "expires".
  int64 idle_timeout_ns = 9;
  message IdleTimeout {
    // Time elapsed - in nanoseconds - since the table entry was last "hit" as
    // part of a data plane table lookup.
    int64 elapsed_ns = 1;
  }
  // Table write: this field should be left unset.
  // Table read: if the table supports idle timeout and time_since_last_hit is
  // set in the request, this field will be set in the response.
  IdleTimeout time_since_last_hit = 10;
  // Arbitrary metadata from the controller that is opaque to the target.
  bytes metadata = 11;
  // True if and only if the entry cannot be deleted or modified,
  // i.e. any of the following:
  // + Any entry read from a table declared with `const entries`
  // + The default entry read from a table declared with `const
  //   default_action`
  // + Any entry declared with `entries` without the `const` qualifier
  //   before `entries`, where the individual entry has the `const`
  //   qualifier on it in the P4 source code.
  // Note: Older P4Runtime API servers before the `is_const` field was
  // added to this message will not return a value for `is_const` in
  // the first two cases above, but newer P4Runtime API servers will.
  // Added in 1.4.0.
  bool is_const = 13;
}

// field_match_type ::= exact | ternary | lpm | range | optional
message FieldMatch {
  uint32 field_id = 1;

  // Matches can be performed on arbitrarily-large inputs; the protobuf type
  // 'bytes' is used to model arbitrarily-large values.
  message Exact {
    bytes value = 1;
  }
  message Ternary {
    bytes value = 1;
    bytes mask = 2;
  }
  message LPM {
    bytes value = 1;
    int32 prefix_len = 2;  // in bits
  }
  // A Range is logically a set that contains all values numerically between
  // 'low' and 'high' inclusively.
  message Range {
    bytes low = 1;
    bytes high = 2;
  }
  // If the Optional match should be a wildcard, the FieldMatch must be omitted.
  // Otherwise, this behaves like an exact match.
  message Optional {
    bytes value = 1;
  }

  oneof field_match_type {
    Exact exact = 2;
    Ternary ternary = 3;
    LPM lpm = 4;
    Range range = 6;
    Optional optional = 7;
    // Architecture-specific match value; it corresponds to the other_match_type
    // in the P4Info MatchField message.
    .google.protobuf.Any other = 100;
  }
}

// table_actions ::= action_specification | action_profile_specification
message TableAction {
  oneof type {
    Action action = 1;
    uint32 action_profile_member_id = 2;
    uint32 action_profile_group_id = 3;
    ActionProfileActionSet action_profile_action_set = 4;
  }
}

message Action {
  uint32 action_id = 1;
  message Param {
    uint32 param_id = 2;
    bytes value = 3;
  }
  repeated Param params = 4;
}

message ActionProfileActionSet {
  repeated ActionProfileAction action_profile_actions = 1;
  // Added in v1.5.0.
  enum ActionSelectionMode {
    // Uses the mode specified by the action selector.
    DEFAULT_MODE_DETERMINED_BY_ACTION_SELECTOR = 0;
    // Uses the hash mode specified by the action selector or a target-defined
    // hash mode if there is none.
    HASH = 1;
    // Picks the action randomly.
    RANDOM = 2;
  }
  // Added in v1.5.0.
  enum SizeSemantics {
    // Uses the `selector_size_semantics` specified by the action selector.
    DEFAULT_SIZE_DETERMINED_BY_ACTION_SELECTOR = 0;
    // Uses the `sum_of_weights` `selector_size_semantics` for this group.
    SUM_OF_WEIGHTS = 1;
    // Uses the `sum_of_members` `selector_size_semantics` for this group.
    SUM_OF_MEMBERS = 2;
  }
  // Determines how the group selects a per-packet action.
  ActionSelectionMode action_selection_mode = 2;
  // Determines the resources used by the group.
  SizeSemantics size_semantics = 3;
}

message ActionProfileAction {
  Action action = 1;
  int32 weight = 2;
  oneof watch_kind {
    // Using int32 as ports is deprecated, use watch_port instead.
    int32 watch = 3 [deprecated = true];
    bytes watch_port = 4;
  }
}

//------------------------------------------------------------------------------
message ActionProfileMember {
  uint32 action_profile_id = 1;
  uint32 member_id = 2;
  Action action = 3;
}

//------------------------------------------------------------------------------
message ActionProfileGroup {
  uint32 action_profile_id = 1;
  uint32 group_id = 2;
  message Member {
    uint32 member_id = 1;
    int32 weight = 2;
    oneof watch_kind {
      // Using int32 as ports is deprecated, use watch_port instead.
      int32 watch = 3 [deprecated = true];
      bytes watch_port = 4;
    }
  }
  repeated Member members = 3;
  // Max number of weighted member entries in this group. It cannot be modified
  // after a group has been created. It must not exceed the static
  // max_group_size included in P4Info. If the max size is not known at group
  // creation-time, the client may leave this field unset (default value 0), in
  // which case the static max_group_size value will be used and the group will
  // be able to include up to max_group_size weighted member entries.
  int32 max_size = 4;
}

// An index as a protobuf message. In proto3, fields cannot be optional and
// there is no difference between an unset integer field and an integer field
// set to 0. This is inconvenient for reading from P4 array-like structures,
// such as indirect counters and meters. We need a way to do a wildcard read on
// these but we cannot use a default zero index value to do so, as zero is a
// valid index (first entry in the array). We therefore wrap the index in a
// message.
message Index {
  int64 index = 1;
}

//------------------------------------------------------------------------------
// For WriteRequest, Update.Type must be MODIFY.
// For ReadRequest, the scope is defined as follows:
// - All meter cells for all meters if meter_id = 0 (default).
// - All meter cells for given meter_id if index is unset (default).
message MeterEntry {
  uint32 meter_id = 1;
  Index index = 2;
  MeterConfig config = 3;
  // Added in v1.4.0
  MeterCounterData counter_data = 4;
}

//------------------------------------------------------------------------------
// For WriteRequest, Update.Type must be MODIFY. INSERT and DELETE on direct
// meters is not allowed and will return an error. The insertion/deletion
// should happen as part of INSERT/DELETE on the associated table-entry.
// For ReadRequest, the scope is defined as follows:
// - All meter cells for all tables if table_entry.table_id = 0.
// - All meter cells of a table if table_entry.table_id is present and
//   table_entry.match is empty.
message DirectMeterEntry {
  // The associated table entry. This field is required.
  // table_entry.action is ignored. Other fields specify the match.
  TableEntry table_entry = 1;
  MeterConfig config = 2;
  // Added in v1.4.0
  MeterCounterData counter_data = 3;
}

// Modeled to support both RFC 2698: A Two Rate Three Color Marker (trTCM) and
// RFC 2697: A Single Rate Three Color Marker (srTCM) based on the `type` of the
// corresponding MeterSpec.
//
// The trTCM meters a packet stream and marks its packets based on two rates,
// Peak Information Rate (PIR) and Committed Information Rate (CIR), and their
// associated burst sizes to be either green, yellow, or red.  A packet is
// marked red if it exceeds the PIR.  Otherwise it is marked either yellow or
// green depending on whether it exceeds or doesn't exceed the CIR. For this
// meter type, `eburst` must be unset.
//
// The srTCM meters a packet stream and marks its packets based on one rate,
// Committed Information Rate (CIR), and its associated burst size as well as an
// Excess Burst Size (EBS) to be either green, yellow, or red. Roughly, a packet
// is marked green if it doesn't exceed the CIR.  Otherwise it is marked either
// yellow or green depending on whether it exceeds or doesn't exceed the EBS.
// For this meter type, it must be the case that `cir == pir && cburst ==
// pburst`.
message MeterConfig {
  // Committed information rate (units per sec)
  int64 cir = 1;
  // Committed burst size
  int64 cburst = 2;
  // Peak information rate (units per sec)
  int64 pir = 3;
  // Peak burst size
  int64 pburst = 4;
  // Excess burst size (only used by srTCM).
  // Added in 1.4.0.
  int64 eburst = 5;
}

//------------------------------------------------------------------------------
// For WriteRequest, Update.Type must be MODIFY.
// For ReadRequest, the scope is defined as follows:
// - All counter cells for all counters if counter_id = 0 (default).
// - All counter cells for given counter_id if index is unset (default).
message CounterEntry {
  uint32 counter_id = 1;
  Index index = 2;
  CounterData data = 3;
}

//------------------------------------------------------------------------------
// For WriteRequest, Update.Type must be MODIFY. INSERT and DELETE on direct
// counters is not allowed and will return an error. The insertion/deletion
// should happen as part of INSERT/DELETE on the associated table-entry.
// For ReadRequest, the scope is defined as follows:
// - All counter cells for all tables if table_entry.table_id = 0.
// - All counter cells of a table if table_entry.table_id is present and
//   table_entry.match is empty.
message DirectCounterEntry {
  // The associated table entry. This field is required.
  // table_entry.action is ignored. Other fields specify the match.
  TableEntry table_entry = 1;
  CounterData data = 2;
}

message CounterData {
  int64 byte_count = 1;
  int64 packet_count = 2;
}

// Added in v1.4.0
message MeterCounterData {
  CounterData green = 1;
  CounterData yellow = 2;
  CounterData red = 3;
}

//------------------------------------------------------------------------------
// Only one instance of a Packet Replication Engine (PRE) is expected in the
// P4 pipeline. Hence, no instance id is needed to access the PRE.
message PacketReplicationEngineEntry {
  oneof type {
    MulticastGroupEntry multicast_group_entry = 1;
    CloneSessionEntry clone_session_entry = 2;
  }
}

// A backup replica used as a fallback when the primary replica port goes down.
// Added in v1.5.0.
message BackupReplica {
  bytes port = 1;
  uint32 instance = 2;
}

// Used for replicas created for cloning and multicasting actions.
message Replica {
  oneof port_kind {
    // Using uint32 as ports is deprecated, use port field instead.
    // Deprecated in v1.4.0
    uint32 egress_port = 1 [deprecated=true];
    // Added in v1.4.0
    bytes port = 3;
  }
  uint32 instance = 2;
  // List of backup replicas used as a fallback when the primary replica port
  // (and all higher-preference backup replica ports) are down.
  // Added in v1.5.0.
  repeated BackupReplica backup_replicas = 4;
}

// The (port, instance) pair must be unique for each replica in a given
// multicast group entry. A packet may be multicast by setting the
// multicast_group field of PSA ingress output metadata to multicast_group_id
// of a programmed multicast group entry. The port and instance fields of each
// replica's egress input metadata will be set to the respective values
// programmed in the multicast group entry.
message MulticastGroupEntry {
  uint32 multicast_group_id = 1;
  repeated Replica replicas = 2;
  // Arbitrary metadata from the controller that is opaque to the target.
  // Added in 1.4.0.
  bytes metadata = 3;
}

// A packet may be cloned by setting the clone_session_id field of PSA
// ingress/egress output metadata to session_id of a programmed clone session
// entry. Multiple clones may be created via a single clone session entry by
// using the replicas message. The clones may be distinguished in the egress
// using the instance field. The class_of_service field of the clone's egress
// input metadata will be set to the respective value programmed in the clone
// session entry. Note that in case of multiple clones, all clones, defined
// for a clone session, will get the same class of service. The
// packet_length_bytes field must be set to a non-zero value if the clone
// packet(s) should be truncated to the given value (in bytes). The packet
// length is also common to all clones in the clone session. If the
// packet_length_bytes field is 0, no truncation on the clone(s) will be
// performed.
message CloneSessionEntry {
  uint32 session_id = 1;
  repeated Replica replicas = 2;
  uint32 class_of_service = 3;
  int32 packet_length_bytes = 4;
}

// A member in a P4 value set. Each member defines a list of matches, which can
// have different match types.
message ValueSetMember {
  repeated FieldMatch match = 1;
}

//------------------------------------------------------------------------------
// For writing and reading matches in a parser value set. A state transition
// on an empty value set will never be taken. The number of matches must be at
// most the size of the value set as specified by the size argument of the
// value_set constructor in the P4 program.
//
// For Write Requests:
//   - MODIFY will write the given matches in the repeated field to the value
//     set.
//   - INSERT and DELETE are not allowed.
//
// For Read Requests:
//   - All matches for all value-set entries if value_set_id = 0
//   - All matches of the value-set if a valid value_set_id is specified
//   - The 'match' field must never be set in the ReadRequest
message ValueSetEntry {
  uint32 value_set_id = 1;
  repeated ValueSetMember members = 2;
}

//------------------------------------------------------------------------------
message RegisterEntry {
  uint32 register_id = 1;
  Index index = 2;
  P4Data data = 3;
}

//------------------------------------------------------------------------------
// Used to configure the digest extern only, not to stream digests or acks
message DigestEntry {
  uint32 digest_id = 1;
  // a DigestList message is streamed when the following conditions are met:
  //   - there is at least one digest ready
  //   - the oldest digest in the list has been waiting for at least
  //     max_timeout_ns nanoseconds or we have gathered max_list_size digests
  //     already
  message Config {
    int64 max_timeout_ns = 1;  // max timeout for outstanding digest data
    int32 max_list_size = 2;   // max size for a digest list
    int64 ack_timeout_ns = 3;  // timeout for DigestListAck message
  }
  Config config = 2;
}

//------------------------------------------------------------------------------
message StreamMessageRequest {
  oneof update {
    MasterArbitrationUpdate arbitration = 1;
    PacketOut packet = 2;
    DigestListAck digest_ack = 3;
    .google.protobuf.Any other = 4;
  }
}

// Packet sent from the controller to the switch.
message PacketOut {
  bytes payload = 1;
  // This will be based on P4 header annotated as
  // @controller_header("packet_out").
  // At most one P4 header can have this annotation.
  repeated PacketMetadata metadata = 2;
}

// Used by the controller to ack a DigestList message. To avoid flooding the
// controller, the switch must not generate digest notifications for the same
// data until a DigestListAck message with the same list_id is received or the
// ack timeout (ack_timeout_ns field in DigestEntry.Config) expires.
message DigestListAck {
  uint32 digest_id = 1;
  uint64 list_id = 2;
}

message StreamMessageResponse {
  oneof update {
    MasterArbitrationUpdate arbitration = 1;
    PacketIn packet = 2;
    DigestList digest = 3;
    IdleTimeoutNotification idle_timeout_notification = 4;
    .google.protobuf.Any other = 5;
    // Used by the server to asynchronously report errors which occur when
    // processing StreamMessageRequest messages.
    StreamError error = 6;
  }
}

// Packet sent from the switch to the controller.
message PacketIn {
  bytes payload = 1;
  // This will be based on P4 header annotated as
  // @controller_header("packet_in").
  // At most one P4 header can have this annotation.
  repeated PacketMetadata metadata = 2;
}

message DigestList {
  uint32 digest_id = 1;  // identifies the digest extern instance
  uint64 list_id = 2;  // identifies a list of entries, used by receiver to ack
  // List of entries: each call to the Digest<T>::pack() method corresponds to
  // one entry and we can have as little as one entry.
  repeated P4Data data = 3;
  // Timestamp at which the server generated the message (in nanoseconds since
  // Epoch)
  int64 timestamp = 4;
}

// Any metadata associated with Packet-IO (controller Packet-In or Packet-Out)
// needs to be modeled as P4 headers carrying special annotations
// @controller_header("packet_out") and @controller_header("packet_in")
// respectively. There can be at most one header each with these annotations.
// These special headers are captured in P4Info ControllerPacketMetadata.
message PacketMetadata {
  // This refers to Metadata.id coming from P4Info ControllerPacketMetadata.
  uint32 metadata_id = 1;
  bytes value = 2;
}

message MasterArbitrationUpdate {
  uint64 device_id = 1;
  // The role for which the primary client is being arbitrated. For use-cases
  // where multiple roles are not needed, the controller can leave this unset,
  // implying default role and full pipeline access.
  Role role = 2;
  // The stream RPC with the highest election_id is the primary. The 'primary'
  // controller instance populates this with its latest election_id. Switch
  // populates with the highest election ID it has received from all connected
  // controllers.
  Uint128 election_id = 3;
  // Switch populates this with OK for the client that is the primary, and
  // with an error status for all other connected clients (at every primary
  // client change). The controller does not populate this field.
  .google.rpc.Status status = 4;
}

message Role {
  // Uniquely identifies this role.
  // Deprecated in 1.4.0.
  uint64 id = 1 [deprecated=true];
  // Added in 1.4.0.
  string name = 3;
  // Describes the role configuration, i.e. what operations, P4 entities,
  // behaviors, etc. are in the scope of a given role. If config is not set
  // (default case), it implies all P4 objects and control behaviors are in
  // scope, i.e. full pipeline access. The format of this message is
  // out-of-scope of P4Runtime.
  .google.protobuf.Any config = 2;
}

message IdleTimeoutNotification {
  // The only fields that are required to be set in each TableEntry are the
  // "key" fields (table_id, match and priority) along with controller_metadata,
  // metadata and idle_timeout_ns.
  repeated TableEntry table_entry = 1;
  // Timestamp at which the server generated the message (in nanoseconds since
  // Epoch)
  int64 timestamp = 2;
}

// Used by the server to asynchronously report errors which occur when
// processing StreamMessageRequest messages.
message StreamError {
  // gRPC canonical error code (see
  // https://developers.google.com/maps-booking/reference/grpc-api/status_codes)
  int32 canonical_code = 1;
  // Optional. An explanation of the error.
  string message = 2;
  // Optional. Target and architecture specific space to which this error
  // belongs.
  // We encourage using triplet: <target>-<arch>-<vendor>,
  // e.g."targetX-psa-vendor1" or "targetY-psa-vendor2".
  string space = 3;
  // Optional. Numeric code drawn from target-specific error space above.
  int32 code = 4;
  // Used by the server to convey additional information about the error. One of
  // the fields must be set (so that the client can identify which type of
  // stream message triggered the error), but that field may be set to its
  // default value.
  oneof details {
    PacketOutError packet_out = 5;
    DigestListAckError digest_list_ack = 6;
    StreamOtherError other = 7;
  }
}

message PacketOutError {
  // Optional. The packet out message that caused the error.
  PacketOut packet_out = 1;
}

message DigestListAckError {
  // Optional. The digest list acknowledgement message that caused the error.
  DigestListAck digest_list_ack = 1;
}

message StreamOtherError {
  // Optional. The architecture-specific stream message that caused the error.
  google.protobuf.Any other = 1;
}

message Uint128 {
  // Highest 64 bits of a 128 bit number.
  uint64 high = 1;
  // Lowest 64 bits of a 128 bit number.
  uint64 low = 2;
}

//------------------------------------------------------------------------------
message SetForwardingPipelineConfigRequest {
  enum Action {
    UNSPECIFIED = 0;
    // Verify that the target can realize the given config. Do not modify the
    // forwarding state in the target. Returns error if config is not provided
    // of if the provided config cannot be realized.
    VERIFY = 1;
    // Save the config if the target can realize it. Do not modify the
    // forwarding state in the target. Any subsequent read/write requests must
    // refer to fields in the new config. Returns error if config is not
    // provided of if the provided config cannot be realized.
    VERIFY_AND_SAVE = 2;
    // Verify, save and realize the given config. Clear the forwarding state
    // in the target. Returns error if config is not provided of if the
    // provided config cannot be realized.
    VERIFY_AND_COMMIT = 3;
    // Realize the last saved, but not yet committed, config. Update the
    // forwarding state in the target by replaying the write requests since the
    // last config was saved. Config should not be provided for this action
    // type. Returns an error if no saved config is found or if a config is
    // provided with this message.
    COMMIT = 4;
    // Verify, save and realize the given config, while preserving the
    // forwarding state in the target. This is an advanced use case to enable
    // changes to the P4 forwarding pipeline configuration with minimal traffic
    // loss. P4Runtime does not impose any constraints on the duration of the
    // traffic loss. The support for this option is not expected to be uniform
    // across all P4Runtime targets. A target that does not support this option
    // may return an UNIMPLEMENTED error. For targets that support this option,
    // an INVALID_ARGUMENT error is returned if no config is provided, or if
    // the existing forwarding state cannot be preserved for the given config
    // by the target.
    RECONCILE_AND_COMMIT = 5;
  }
  uint64 device_id = 1;
  // Deprecated in 1.4.0.
  uint64 role_id = 2 [deprecated=true];
  // Added in 1.4.0.
  string role = 6;
  Uint128 election_id = 3;
  Action action = 4;
  ForwardingPipelineConfig config = 5;
}

message SetForwardingPipelineConfigResponse {}

message ForwardingPipelineConfig {
  config.v1.P4Info p4info = 1;
  // Target-specific P4 configuration.
  bytes p4_device_config = 2;
  // Metadata (cookie) opaque to the target. A control plane may use this field
  // to uniquely identify this config. There are no restrictions on how such
  // value is computed, or where this is stored on the target, as long as it is
  // returned with a GetForwardingPipelineConfig RPC. When reading the cookie,
  // we need to distinguish those cases where a cookie is NOT present (e.g. not
  // set in the SetForwardingPipelineConfigRequest, therefore we wrap the actual
  // uint64 value in a protobuf message.
  message Cookie {
    uint64 cookie = 1;
  }
  Cookie cookie = 3;
}

message GetForwardingPipelineConfigRequest {
  // Specifies the fields to populate in the response.
  enum ResponseType {
    // Default behaviour. Returns a ForwardingPipelineConfig with all fields set
    // as stored by the target.
    ALL = 0;
    // Reply by setting only the cookie field, omitting all other fields.
    COOKIE_ONLY = 1;
    // Reply by setting the p4info and cookie fields.
    P4INFO_AND_COOKIE = 2;
    // Reply by setting the p4_device_config and cookie fields.
    DEVICE_CONFIG_AND_COOKIE = 3;
  }
  uint64 device_id = 1;
  ResponseType response_type = 2;
}

message GetForwardingPipelineConfigResponse {
  ForwardingPipelineConfig config = 1;
}

// Error message used to report a single P4-entity error for a Write RPC.
message Error {
  // gRPC canonical error code (see
  // https://developers.google.com/maps-booking/reference/grpc-api/status_codes)
  int32 canonical_code = 1;
  // Detailed error message.
  string message = 2;
  // Target and architecture specific space to which this error belongs.
  // We encourage using triplet: <target>-<arch>-<vendor>,
  // e.g."targetX-psa-vendor1" or "targetY-psa-vendor2".
  string space = 3;
  // Numeric code drawn from target-specific error space above.
  int32 code = 4;
  // Optional: Allows reporting back additional target-specific details on the
  // error.
  .google.protobuf.Any details = 5;
}

//------------------------------------------------------------------------------
// Reserved controller-specified SDN port numbers for reference.
enum SdnPort {
  SDN_PORT_UNKNOWN = 0;
  // SDN ports are numbered starting form 1.
  SDN_PORT_MIN = 1;
  // 0xfffffeff: The maximum value of an SDN port (physical or logical).
  SDN_PORT_MAX = -257;
  // Reserved SDN port numbers (0xffffff00 - 0xffffffff)
  // 0xfffffffa: Recirculate the packet back to ingress
  SDN_PORT_RECIRCULATE = -6;
  // 0xfffffffd: Send to CPU
  SDN_PORT_CPU = -3;
}

//------------------------------------------------------------------------------
message CapabilitiesRequest {
  // Optional: Response should include the device-specific capabilities of the
  // device identified by `device_id` in addition to server-wide capabilities.
  // Added in v1.5.0
  uint64 device_id = 1;
}

message CapabilitiesResponse {
  // The full semantic version string (e.g. "1.1.0-rc.1") corresponding to the
  // version of the P4Runtime API currently implemented by the server.
  string p4runtime_api_version = 1;

  // Used for experimental features before proposing them for standardization.
  // Added in 1.5.0.
  .google.protobuf.Any experimental = 999;
}
