Архитектура переносимых коммутаторов PSA

PDF

Приведенный ниже включаемый файл (include) с переведенными на русский язык комментариями содержит определения для архитектуры PSA. Файл psa.p4 размещается в каталоге p4include пакета p4c (https://github.com/p4lang/p4c/blob/master/p4include/psa.p4).

Этот файл включается в программы P4, работающие с прототипом коммутатора psa_switch на основе модели BMV2.

/* Copyright 2013-present Barefoot Networks, 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.
*/

#ifndef __PSA_P4__
#define __PSA_P4__

#include<core.p4>

#ifndef _PORTABLE_SWITCH_ARCHITECTURE_P4_
#define _PORTABLE_SWITCH_ARCHITECTURE_P4_

/**
 *   Объявления P4-16 для архитектуры PSA
 */

/**
 * Эти типы нужно определить до включения файла архитектуры, а затем 
 * следует определить защиту макросов.
 */
#define PSA_ON_BMV2_CORE_TYPES
#ifdef PSA_ON_BMV2_CORE_TYPES
/* Показанные ниже размеры в битах относятся к платформе BMv2 psa_switch.
 * Размер этих типов не требуется сохранять в других реализациях PSA. 
 * Каждая реализация может указать свои размеры в форме bit<W> с нужным W.
 * Одной из причин приведенного здесь выбора является реализация PSA в
 * модели BMv2. Другая заключается в упрощении компиляции этого файла и 
 * примера программы PSA (P4), включающей файл.
 *
 * Размеры в битах для BMv2 psa_switch выбраны так, чтобы они совпадали с
 * соответствующими типами InHeader. Это упрощает реализацию P4Runtime для
 * BMv2 psa_switch. */

/* В определениях используется typedef, а не type, что просто задает другие
 * имена для типа bit<W> с показанным значением W. В отличие от определений
 * type значения, объявленные с typedef, можно свободно смешивать в 
 * выражениях со значениями типа bit<W>. Значения, объявленные с type,
 * нельзя свободно смешивать, пока они не приведены к соответствующему типу
 * typedef. Это может быть неудобно для арифметических операций, зато можно
 * пометить нужным типом все значения, объявленные с type, при генерации
 * API плоскости управления.
 *
 * Отметим, что размер typedef <name>Uint_t всегда совпадает с type <name>_t. */
typedef bit<32> PortIdUint_t;
typedef bit<32> MulticastGroupUint_t;
typedef bit<16> CloneSessionIdUint_t;
typedef bit<8>  ClassOfServiceUint_t;
typedef bit<16> PacketLengthUint_t;
typedef bit<16> EgressInstanceUint_t;
typedef bit<64> TimestampUint_t;

/* Отметим, что clone_spec в BMv2 simple_switch v1model имеет размер 32 бита, 
 * но используется так, что 16 битов содержат идентификатор сеанса клонирования
 * и другие 16 битов - численный идентификатор field_list. Лишь 16 битов
 * идентификатора сессии можно сравнивать с типов CloneSessionIdUint_t. См.
 * https://github.com/p4lang/behavioral-model/blob/master/targets/simple_switch/simple_switch.cpp 
 */

@p4runtime_translation("p4.org/psa/v1/PortId_t", 32)
type PortIdUint_t         PortId_t;
@p4runtime_translation("p4.org/psa/v1/MulticastGroup_t", 32)
type MulticastGroupUint_t MulticastGroup_t;
@p4runtime_translation("p4.org/psa/v1/CloneSessionId_t", 16)
type CloneSessionIdUint_t CloneSessionId_t;
@p4runtime_translation("p4.org/psa/v1/ClassOfService_t", 8)
type ClassOfServiceUint_t ClassOfService_t;
@p4runtime_translation("p4.org/psa/v1/PacketLength_t", 16)
type PacketLengthUint_t   PacketLength_t;
@p4runtime_translation("p4.org/psa/v1/EgressInstance_t", 16)
type EgressInstanceUint_t EgressInstance_t;
@p4runtime_translation("p4.org/psa/v1/Timestamp_t", 64)
type TimestampUint_t      Timestamp_t;
typedef error   ParserError_t;

const PortId_t PSA_PORT_RECIRCULATE = (PortId_t) 0xfffffffa;
const PortId_t PSA_PORT_CPU = (PortId_t) 0xfffffffd;

const CloneSessionId_t PSA_CLONE_SESSION_TO_CPU = (CloneSessionId_t) 0;

#endif  // PSA_ON_BMV2_CORE_TYPES

#ifndef PSA_ON_BMV2_CORE_TYPES
#error "Please define the following types for PSA and the PSA_EXAMPLE_CORE_TYPES macro"
// BEGIN:Type_defns
/* В определениях используется typedef, а не type, что просто задает другие
 * имена для типа bit<W> с показанным значением W. В отличие от определений
 * type значения, объявленные с typedef, можно свободно смешивать в 
 * выражениях со значениями типа bit<W>. Значения, объявленные с type,
 * нельзя свободно смешивать, пока они не приведены к соответствующему типу
 * typedef. Это может быть неудобно для арифметических операций, зато можно
 * пометить нужным типом все значения, объявленные с type, при генерации
 * API плоскости управления.
 *
 * Отметим, что размер typedef <name>Uint_t всегда совпадает с type <name>_t. */
typedef bit<unspecified> PortIdUint_t;
typedef bit<unspecified> MulticastGroupUint_t;
typedef bit<unspecified> CloneSessionIdUint_t;
typedef bit<unspecified> ClassOfServiceUint_t;
typedef bit<unspecified> PacketLengthUint_t;
typedef bit<unspecified> EgressInstanceUint_t;
typedef bit<unspecified> TimestampUint_t;

@p4runtime_translation("p4.org/psa/v1/PortId_t", 32)
type PortIdUint_t         PortId_t;
@p4runtime_translation("p4.org/psa/v1/MulticastGroup_t", 32)
type MulticastGroupUint_t MulticastGroup_t;
@p4runtime_translation("p4.org/psa/v1/CloneSessionId_t", 16)
type CloneSessionIdUint_t CloneSessionId_t;
@p4runtime_translation("p4.org/psa/v1/ClassOfService_t", 8)
type ClassOfServiceUint_t ClassOfService_t;
@p4runtime_translation("p4.org/psa/v1/PacketLength_t", 16)
type PacketLengthUint_t   PacketLength_t;
@p4runtime_translation("p4.org/psa/v1/EgressInstance_t", 16)
type EgressInstanceUint_t EgressInstance_t;
@p4runtime_translation("p4.org/psa/v1/Timestamp_t", 64)
type TimestampUint_t      Timestamp_t;
typedef error   ParserError_t;

const PortId_t PSA_PORT_RECIRCULATE = (PortId_t) unspecified;
const PortId_t PSA_PORT_CPU = (PortId_t) unspecified;

const CloneSessionId_t PSA_CLONE_SESSION_TO_CPU = (CloneSessiontId_t) unspecified;
// END:Type_defns
#endif  // #ifndef PSA_EXAMPLE_CORE_TYPES

// BEGIN:Type_defns2
/* Все типы с InHeader в имени предназначены для передачи лишь значений
 * соответствующих типов из заголовков пакета между устройством PSA и
 * управляющей им программой сервера P4Runtime.
 *
 * Предполагается, что размер будет не меньше, чем у любого устройства PSA
 * для данного типа. Таким образом, эти типы могут также быть полезны для
 * определения заголовков пакета, передаваемых между устройством PSA и
 * другими устройствами без прохождения через сервер P4Runtime (например,
 * для отправки пакетов контроллеру или системе сбора данных с 
 * использованием скорости передачи пакетов выше, чем может обработать 
 * сервер P4Runtime). При использовании для таких целей не предъявляется
 * требования к плоскости данных PSA по автоматическому численному
 * преобразованию этих типов, которое происходило бы при прохождении
 * заголовков через сервер P4Runtime. Все нужные преобразования следует
 * выполнять программисту P4 явно в кода программы.
 *
 * Все размеры должны быть кратны 8, поэтому любое подмножество этих
 * полей можно использовать в одном определении заголовка P4, даже в 
 * реализациях P4, ограничивающих заголовки полями с общим размером, 
 * кратным 8 битам. */

/* См. комментарий выше (PortIdUint_t). */
typedef bit<32> PortIdInHeaderUint_t;
typedef bit<32> MulticastGroupInHeaderUint_t;
typedef bit<16> CloneSessionIdInHeaderUint_t;
typedef bit<8>  ClassOfServiceInHeaderUint_t;
typedef bit<16> PacketLengthInHeaderUint_t;
typedef bit<16> EgressInstanceInHeaderUint_t;
typedef bit<64> TimestampInHeaderUint_t;

@p4runtime_translation("p4.org/psa/v1/PortIdInHeader_t", 32)
type  PortIdInHeaderUint_t         PortIdInHeader_t;
@p4runtime_translation("p4.org/psa/v1/MulticastGroupInHeader_t", 32)
type  MulticastGroupInHeaderUint_t MulticastGroupInHeader_t;
@p4runtime_translation("p4.org/psa/v1/CloneSessionIdInHeader_t", 16)
type  CloneSessionIdInHeaderUint_t CloneSessionIdInHeader_t;
@p4runtime_translation("p4.org/psa/v1/ClassOfServiceInHeader_t", 8)
type  ClassOfServiceInHeaderUint_t ClassOfServiceInHeader_t;
@p4runtime_translation("p4.org/psa/v1/PacketLengthInHeader_t", 16)
type  PacketLengthInHeaderUint_t   PacketLengthInHeader_t;
@p4runtime_translation("p4.org/psa/v1/EgressInstanceInHeader_t", 16)
type  EgressInstanceInHeaderUint_t EgressInstanceInHeader_t;
@p4runtime_translation("p4.org/psa/v1/TimestampInHeader_t", 64)
type  TimestampInHeaderUint_t      TimestampInHeader_t;
// END:Type_defns2

/* Функции _int_to_header были написаны для преобразования значений типа
 * <name>_t (значение внутри пути данных) в значения типа <name>InHeader_t
 * в заголовке, который будет передан в порт CPU.
 *
 * Функции _header_to_int  были написаны для преобразования значений в
 * обратном направлении, обычно при назначении из заголовка, полученного из
 * CPU, значению, которое будет использоваться в остальном коде программы.
 *
 * Причина этих трех преобразований заключается в том, что каждый из этих
 * типов объявлен через P4_16 type, поэтому без приведения типа значения 
 * можно присваивать лишь идентичному типу. Первое приведение меняет
 * исходный тип на bit<W1> с тем же размером W1. Второе приведение меняет 
 * размер путем добавления нулей в начале или отбрасывания старших битов.
 * Третье приведение меняет тип bit<W2> на окончательный тип с размером W2. */

PortId_t psa_PortId_header_to_int (in PortIdInHeader_t x) {
    return (PortId_t) (PortIdUint_t) (PortIdInHeaderUint_t) x;
}
MulticastGroup_t psa_MulticastGroup_header_to_int (in MulticastGroupInHeader_t x) {
    return (MulticastGroup_t) (MulticastGroupUint_t) (MulticastGroupInHeaderUint_t) x;
}
CloneSessionId_t psa_CloneSessionId_header_to_int (in CloneSessionIdInHeader_t x) {
    return (CloneSessionId_t) (CloneSessionIdUint_t) (CloneSessionIdInHeaderUint_t) x;
}
ClassOfService_t psa_ClassOfService_header_to_int (in ClassOfServiceInHeader_t x) {
    return (ClassOfService_t) (ClassOfServiceUint_t) (ClassOfServiceInHeaderUint_t) x;
}
PacketLength_t psa_PacketLength_header_to_int (in PacketLengthInHeader_t x) {
    return (PacketLength_t) (PacketLengthUint_t) (PacketLengthInHeaderUint_t) x;
}
EgressInstance_t psa_EgressInstance_header_to_int (in EgressInstanceInHeader_t x) {
    return (EgressInstance_t) (EgressInstanceUint_t) (EgressInstanceInHeaderUint_t) x;
}
Timestamp_t psa_Timestamp_header_to_int (in TimestampInHeader_t x) {
    return (Timestamp_t) (TimestampUint_t) (TimestampInHeaderUint_t) x;
}

PortIdInHeader_t psa_PortId_int_to_header (in PortId_t x) {
    return (PortIdInHeader_t) (PortIdInHeaderUint_t) (PortIdUint_t) x;
}
MulticastGroupInHeader_t psa_MulticastGroup_int_to_header (in MulticastGroup_t x) {
    return (MulticastGroupInHeader_t) (MulticastGroupInHeaderUint_t) (MulticastGroupUint_t) x;
}
CloneSessionIdInHeader_t psa_CloneSessionId_int_to_header (in CloneSessionId_t x) {
    return (CloneSessionIdInHeader_t) (CloneSessionIdInHeaderUint_t) (CloneSessionIdUint_t) x;
}
ClassOfServiceInHeader_t psa_ClassOfService_int_to_header (in ClassOfService_t x) {
    return (ClassOfServiceInHeader_t) (ClassOfServiceInHeaderUint_t) (ClassOfServiceUint_t) x;
}
PacketLengthInHeader_t psa_PacketLength_int_to_header (in PacketLength_t x) {
    return (PacketLengthInHeader_t) (PacketLengthInHeaderUint_t) (PacketLengthUint_t) x;
}
EgressInstanceInHeader_t psa_EgressInstance_int_to_header (in EgressInstance_t x) {
    return (EgressInstanceInHeader_t) (EgressInstanceInHeaderUint_t) (EgressInstanceUint_t) x;
}
TimestampInHeader_t psa_Timestamp_int_to_header (in Timestamp_t x) {
    return (TimestampInHeader_t) (TimestampInHeaderUint_t) (TimestampUint_t) x;
}

/// Диапазон поддерживаемых значений для свойств таблицы psa_idle_timeout
enum PSA_IdleTimeout_t {
  NO_TIMEOUT,
  NOTIFY_CONTROL
};

// BEGIN:Metadata_types
enum PSA_PacketPath_t {
    NORMAL,     /// Пакет, полученный ingress и не относящийся к перечисленным ниже.
    NORMAL_UNICAST,   /// Обычный индивидуальный пакет, принятый egress.
    NORMAL_MULTICAST, /// Обычный групповой пакет, принятый egress.
    CLONE_I2E,  /// Пакет, созданный клонированием в ingress и предназначенный
                /// для egress.
    CLONE_E2E,  /// Пакет, созданный клонированием в egress и предназначенный
                /// для egress.
    RESUBMIT,   /// Пакет, принятый в результате операции resubmit.
    RECIRCULATE /// Пакет, принятый в результате операции recirculate.
}

struct psa_ingress_parser_input_metadata_t {
  PortId_t                 ingress_port;
  PSA_PacketPath_t         packet_path;
}

struct psa_egress_parser_input_metadata_t {
  PortId_t                 egress_port;
  PSA_PacketPath_t         packet_path;
}

struct psa_ingress_input_metadata_t {
  // Все эти значения инициализируются архитектурой до начала выполнения
  // блока управления Ingress.
  PortId_t                 ingress_port;
  PSA_PacketPath_t         packet_path;
  Timestamp_t              ingress_timestamp;
  ParserError_t            parser_error;
}
// BEGIN:Metadata_ingress_output
struct psa_ingress_output_metadata_t {
  // В комментариях к полям приведены исходные значения в момент начала
  // выполнения блока управления Ingress.
  ClassOfService_t         class_of_service; // 0
  bool                     clone;            // false
  CloneSessionId_t         clone_session_id; // не определено
  bool                     drop;             // true
  bool                     resubmit;         // false
  MulticastGroup_t         multicast_group;  // 0
  PortId_t                 egress_port;      // не определено
}
// END:Metadata_ingress_output
struct psa_egress_input_metadata_t {
  ClassOfService_t	class_of_service;
  PortId_t            egress_port;
  PSA_PacketPath_t    packet_path;
  EgressInstance_t    instance;       /// Экземпляр приходит из PacketReplicationEngine
  Timestamp_t         egress_timestamp;
  ParserError_t       parser_error;
}

/// Эта структура является входным (in) параметром для выходного сборщика.
/// Она включает данные для выходного сборщика, позволяющие решить вопрос
/// о рециркуляции пакета.
struct psa_egress_deparser_input_metadata_t {
  PortId_t                 egress_port;
}
// BEGIN:Metadata_egress_output
struct psa_egress_output_metadata_t {
  // В комментариях к полям приведены исходные значения в момент начала
  // выполнения блока управления Egress.
  bool                     clone;         // false
  CloneSessionId_t         clone_session_id; // не определено
  bool                     drop;          // false
}
// END:Metadata_egress_output
// END:Metadata_types

/// При выполнении IngressDeparser функция psa_clone_i2e возвращает true
/// тогда и только тогда, когда создается клон обрабатываемого пакета для 
/// выхода. Какие-либо назначения параметра clone_i2e_meta в
/// IngressDeparser могут выполняться лишь внутри оператора if, который
/// позволяет выполнять такое назначение лишь в случае возврата функцией 
/// psa_clone_i2e(istd) значения true. psa_clone_i2e можно реализовать 
/// путем возврата istd.clone

@pure
extern bool psa_clone_i2e(in psa_ingress_output_metadata_t istd);

/// При выполнении IngressDeparser функция psa_resubmit возвращает true
/// тогда и только тогда, когда пакет представляется заново (resubmit).
/// Какие-либо назначения параметра resubmit_meta в  IngressDeparser
/// могут выполняться лишь внутри оператора if, который  позволяет
/// выполнять такое назначение лишь в случае возврата функцией 
/// psa_resubmit(istd) значения true. psa_resubmit можно реализовать 
/// путем возврата (!istd.drop && istd.resubmit)


@pure
extern bool psa_resubmit(in psa_ingress_output_metadata_t istd);

/// При выполнении IngressDeparser функция psa_normal возвращает true
/// тогда и только тогда, когда пакет передается на выход «обычным» 
/// путем как индивидуальный или групповой. Какие-либо назначения 
/// параметра resubmit_meta в IngressDeparser могут выполняться лишь 
/// внутри оператора if, который позволяет выполнять такое назначение 
/// лишь в случае возврата функцией psa_normal(istd) значения true. 
/// psa_normal можно реализовать путем возврата (!istd.drop && istd.resubmit)

@pure
extern bool psa_normal(in psa_ingress_output_metadata_t istd);

/// При выполнении EgressDeparser функция psa_clone_e2e возвращает true
/// тогда и только тогда, когда создается клон обрабатываемого пакета
/// для выхода. Какие-либо назначения параметра clone_e2e_meta в 
/// EgressDeparser могут выполняться лишь внутри оператора if, который
/// позволяет выполнять такое назначение лишь в случае возврата функцией
/// psa_clone_e2e(istd) значения true. psa_clone_e2e можно реализовать
/// путем возврата istd.clone

@pure
extern bool psa_clone_e2e(in psa_egress_output_metadata_t istd);

/// При выполнении EgressDeparser функция psa_recirculate возвращает true
/// тогда и только тогда, когда пакет передается в рециркуляцию. 
/// Какие-либо назначения параметра recirculate_meta в EgressDeparser
/// могут выполняться лишь внутри оператора if, который позволяет
/// выполнять такое назначение лишь в случае возврата функцией 
/// psa_recirculate(istd) значения true. psa_recirculate можно 
/// реализовать путем возврата (!istd.drop && (edstd.egress_port
/// == PSA_PORT_RECIRCULATE))

@pure
extern bool psa_recirculate(in psa_egress_output_metadata_t istd,
                            in psa_egress_deparser_input_metadata_t edstd);


extern void assert(in bool check);
extern void assume(in bool check);

// BEGIN:Match_kinds
match_kind {
    range,   /// Служит для представления интервалов min..max.
    selector /// Служит для динамического выбора действия с помощью 
             /// внешнего метода ActionSelector.
}
// END:Match_kinds

// BEGIN:Action_send_to_port
/// Меняет выходные метаданные ingress для отправки пакета на выходную
/// обработку с последующим выводом в egress_port (при выходной 
/// обработке пакет может быть отброшен).

/// Это действие не влияет на операции clone или resubmit.

@noWarnUnused
action send_to_port(inout psa_ingress_output_metadata_t meta,
                    in PortId_t egress_port)
{
    meta.drop = false;
    meta.multicast_group = (MulticastGroup_t) 0;
    meta.egress_port = egress_port;
}
// END:Action_send_to_port

// BEGIN:Action_multicast
/// Меняет выходные метаданные ingress для создания копий пакета, 
/// отправляемых на выходную обработку.

/// Это действие не влияет на операции clone или resubmit.

@noWarnUnused
action multicast(inout psa_ingress_output_metadata_t meta,
                 in MulticastGroup_t multicast_group)
{
    meta.drop = false;
    meta.multicast_group = multicast_group;
}
// END:Action_multicast

// BEGIN:Action_ingress_drop
/// Меняет выходные метаданные ingress для для обычной выходной
/// обработки.

/// Это действие не влияет на операцию clone, но предотвращает 
/// повторное представления пакета (resubmit).

@noWarnUnused
action ingress_drop(inout psa_ingress_output_metadata_t meta)
{
    meta.drop = true;
}
// END:Action_ingress_drop

// BEGIN:Action_egress_drop
/// Меняет выходные метаданные egress для передачи пакета из устройства.

/// Это действие не влияет на операцию clone.

@noWarnUnused
action egress_drop(inout psa_egress_output_metadata_t meta)
{
    meta.drop = true;
}
// END:Action_egress_drop

extern PacketReplicationEngine {
    PacketReplicationEngine();
    // Для этого объекта нет методов, вызываемых из программ P4. Метод
    // будет иметь экземпляр с именем, которое плоскость управления
    // может использовать для вызова объекта через API.
}

extern BufferingQueueingEngine {
    BufferingQueueingEngine();
    // Для этого объекта нет методов, вызываемых из программ P4. 
    // См. предыдущий комментарий. 
}

// BEGIN:Hash_algorithms
enum PSA_HashAlgorithm_t {
  IDENTITY,
  CRC32,
  CRC32_CUSTOM,
  CRC16,
  CRC16_CUSTOM,
  ONES_COMPLEMENT16,  /// 16-битовая контрольная сумма с дополнением до 1,
                      /// используемая в заголовках IPv4, TCP и UDP.
  TARGET_DEFAULT      /// Определяется реализацией целевой платформы.
}
// END:Hash_algorithms

// BEGIN:Hash_extern
extern Hash<O> {
  /// Constructor
  Hash(PSA_HashAlgorithm_t algo);

  /// Расчет хэш-значения для данных.
  /// @param data - данные для расчета хэш-значения.
  /// @return - хэш-значение.
  @pure
  O get_hash<D>(in D data);

  /// Расчет хэш-значения для данных с модулем max и добавлением base.
  /// @param base - минимальное возвращаемое значение.
  /// @param data - данные для расчета хэш-значения.
  /// @param max - хэш-значение делится на max.
  ///        Реализация может ограничивать поддерживаемое максимальное
  ///        значение (например, 32 или 256), а также может поддерживать
  ///        для него лишь степени 2. Разработчикам P4 следует выбирать
  ///        такие значения для обеспечения переносимости.
  /// @return (base + (h % max)), где h - хэш-значение.
  @pure
  O get_hash<T, D>(in T base, in D data, in T max);
}
// END:Hash_extern

// BEGIN:Checksum_extern
extern Checksum<W> {
  /// Constructor
  Checksum(PSA_HashAlgorithm_t hash);

  /// Сбрасывает внутреннее состояние и готовит модуль к расчету. Каждый
  /// экземпляр объекта Checksum автоматически инициализируется как при
  /// вызове clear(). Инициализация выполняется при каждом создании 
  /// экземпляра объекта, независимо от применения в анализаторе или 
  /// элементе управления. Все состояния, поддерживаемые объектом  
  /// Checksum независимы между пакетами.
  void clear();

  /// Добавление данных в контрольную сумму.
  void update<T>(in T data);

  /// Получение контрольной суммы для добавленных (и не удаленных) 
  /// с момента последней очистки данных.
  @noSideEffects
  W    get();
}
// END:Checksum_extern

// BEGIN:InternetChecksum_extern
// Контрольная сумма на основе алгоритма ONES_COMPLEMENT16, используемая в 
// IPv4, TCP и UDP. Поддерживается инкрементальное обновление методом 
// subtract (см. IETF RFC 1624).
extern InternetChecksum {
  /// Конструктор
  InternetChecksum();

  /// Сбрасывает внутреннее состояние и готовит модуль к расчету. Каждый
  /// экземпляр объекта InternetChecksum автоматически инициализируется как 
  /// при вызове clear(). Инициализация выполняется при каждом создании 
  /// запуске анализатора или элемента управления, где применяется объект. 
  /// Все состояния, поддерживаемые объектом независимы между пакетами.
  void clear();

  /// Добавляет в расчет контрольной суммы данные data, размер которых
  /// должен быть кратным 16 битам.
  void add<T>(in T data);

  /// Исключает из расчета контрольной суммы данные data, размер которых
  /// должен быть кратным 16 битам.
  void subtract<T>(in T data);

  /// Возвращает контрольную сумму для данных, добавленных (и не удаленных)
  /// после предшествующего вызова clear.
  @noSideEffects
  bit<16> get();

  /// Возвращает состояние расчета контрольной суммы для использования при
  /// последующем вызове метода set_state.
  @noSideEffects
  bit<16> get_state();

  /// Возвращает состояние экземпляра InternetChecksum к возвращенному при
  /// предшествующем вызове метода get_state. Состояние может возвращаться
  /// для одного или разных экземпляров InternetChecksum.
  void set_state(in bit<16> checksum_state);
}
// END:InternetChecksum_extern

// BEGIN:CounterType_defn
enum PSA_CounterType_t {
    PACKETS,
    BYTES,
    PACKETS_AND_BYTES
}
// END:CounterType_defn

// BEGIN:Counter_extern
/// Опосредованный счетчик с n_counters независимых значений, где каждое
/// значение имеет заданный плоскостью данных размер W.

extern Counter<W, S> {
  Counter(bit<32> n_counters, PSA_CounterType_t type);
  void count(in S index);

  /*
  /// API плоскости управления использует 64-битовые значения считчиков. 
  /// Это не указывает размеры счетчиков в плоскости управления. 
  /// Предполагается, что программы управления периодически считывают
  /// значения счетчиков плоскости данных и аккумулирует их в счетчиках
  /// большего размера, в которых максимальное значение достигается реже.
  /// 64-битовые счетчики позволяют работать при скорости порта 100 Гбит/с
  /// в течение 46 лет без переполнения.

  @ControlPlaneAPI
  {
    bit<64> read      (in S index);
    bit<64> sync_read (in S index);
    void set          (in S index, in bit<64> seed);
    void reset        (in S index);
    void start        (in S index);
    void stop         (in S index);
  }
  */
}
// END:Counter_extern

// BEGIN:DirectCounter_extern
extern DirectCounter<W> {
  DirectCounter(PSA_CounterType_t type);
  void count();

  /*
  @ControlPlaneAPI
  {
    W    read<W>      (in TableEntry key);
    W    sync_read<W> (in TableEntry key);
    void set          (in TableEntry key, in W seed);
    void reset        (in TableEntry key);
    void start        (in TableEntry key);
    void stop         (in TableEntry key);
  }
  */
}
// END:DirectCounter_extern

// BEGIN:MeterType_defn
enum PSA_MeterType_t {
    PACKETS,
    BYTES
}
// END:MeterType_defn

// BEGIN:MeterColor_defn
enum PSA_MeterColor_t { RED, GREEN, YELLOW }
// END:MeterColor_defn

// BEGIN:Meter_extern
// Индексируемый измеритель с n_meters независимых состояний.

extern Meter<S> {
  Meter(bit<32> n_meters, PSA_MeterType_t type);

  // Этот метод служит для «перекрашивания» трафика (см. RFC 2698). 
  // «Цвет» пакета перед вызовом метода указан параметром color.
  PSA_MeterColor_t execute(in S index, in PSA_MeterColor_t color);

  // Этот метод служит для «окрашивания» трафика вслепую (см. RFC 2698). 
  // Метод может быть реализован вызовом execute(index, MeterColor_t.GREEN).
  PSA_MeterColor_t execute(in S index);

  /*
  @ControlPlaneAPI
  {
    reset(in MeterColor_t color);
    setParams(in S index, in MeterConfig config);
    getParams(in S index, out MeterConfig config);
  }
  */
}
// END:Meter_extern

// BEGIN:DirectMeter_extern
extern DirectMeter {
  DirectMeter(PSA_MeterType_t type);
  // См. аналогичный метод для extern Meter.
  PSA_MeterColor_t execute(in PSA_MeterColor_t color);
  PSA_MeterColor_t execute();

  /*
  @ControlPlaneAPI
  {
    reset(in TableEntry entry, in MeterColor_t color);
    void setConfig(in TableEntry entry, in MeterConfig config);
    void getConfig(in TableEntry entry, out MeterConfig config);
  }
  */
}
// END:DirectMeter_extern

// BEGIN:Register_extern
extern Register<T, S> {
  /// Создание массива из <size> регистров с неопределенными значениями.
  Register(bit<32> size);
  ///  Создание массива из <size> регистров с заданными значениями.
  Register(bit<32> size, T initial_value);

  @noSideEffects
  T    read  (in S index);
  void write (in S index, in T value);

  /*
  @ControlPlaneAPI
  {
    T    read<T>      (in S index);
    void set          (in S index, in T seed);
    void reset        (in S index);
  }
  */
}
// END:Register_extern

// BEGIN:Random_extern
extern Random<T> {

  /// Возвращает случайное значение из диапазона [min, max].
  /// Реализациям разрешается поддерживать лишь диапазоны, где
  /// значение (max - min + 1) является степенью 2. Программистам P4
  /// следует ограничивать аргументы значениями, обеспечивающими
  /// переносимость программы.

  Random(T min, T max);
  T read();

  /*
  @ControlPlaneAPI
  {
    void reset();
    void setSeed(in T seed);
  }
  */
}
// END:Random_extern

// BEGIN:ActionProfile_extern
extern ActionProfile {
  /// Создаёт профиль действия записей size
  ActionProfile(bit<32> size);

  /*
  @ControlPlaneAPI
  {
     entry_handle add_member    (action_ref, action_data);
     void         delete_member (entry_handle);
     entry_handle modify_member (entry_handle, action_ref, action_data);
  }
  */
}
// END:ActionProfile_extern

// BEGIN:ActionSelector_extern
extern ActionSelector {
  /// Создает селектор действий с числом записей size.
  /// @param algo - алгоритм хэширования для выбора записи в группе;
  /// @param size - число записей в селекторе действий;
  /// @param outputWidth - размер ключа выбора.
  ActionSelector(PSA_HashAlgorithm_t algo, bit<32> size, bit<32> outputWidth);

  /*
  @ControlPlaneAPI
  {
     entry_handle add_member        (action_ref, action_data);
     void         delete_member     (entry_handle);
     entry_handle modify_member     (entry_handle, action_ref, action_data);
     group_handle create_group      ();
     void         delete_group      (group_handle);
     void         add_to_group      (group_handle, entry_handle);
     void         delete_from_group (group_handle, entry_handle);
  }
  */
}
// END:ActionSelector_extern

// BEGIN:Digest_extern
extern Digest<T> {
  Digest();                       /// Определяет поток сообщений (digest) для 
                                  /// плоскости управления.
  void pack(in T data);           /// Передает данные в поток сообщений.
  /*
  @ControlPlaneAPI
  {
  T data;                           /// Если T является списком, плоскость управления
                                    /// создает структуру (struct).
  int unpack(T& data);              /// Неупакованные данные находятся в T&, int 
                                    /// int возвращает код состояния.
  }
  */
}
// END:Digest_extern

// BEGIN:Programmable_blocks
parser IngressParser<H, M, RESUBM, RECIRCM>(
    packet_in buffer,
    out H parsed_hdr,
    inout M user_meta,
    in psa_ingress_parser_input_metadata_t istd,
    in RESUBM resubmit_meta,
    in RECIRCM recirculate_meta);

control Ingress<H, M>(
    inout H hdr, inout M user_meta,
    in    psa_ingress_input_metadata_t  istd,
    inout psa_ingress_output_metadata_t ostd);

control IngressDeparser<H, M, CI2EM, RESUBM, NM>(
    packet_out buffer,
    out CI2EM clone_i2e_meta,
    out RESUBM resubmit_meta,
    out NM normal_meta,
    inout H hdr,
    in M meta,
    in psa_ingress_output_metadata_t istd);

parser EgressParser<H, M, NM, CI2EM, CE2EM>(
    packet_in buffer,
    out H parsed_hdr,
    inout M user_meta,
    in psa_egress_parser_input_metadata_t istd,
    in NM normal_meta,
    in CI2EM clone_i2e_meta,
    in CE2EM clone_e2e_meta);

control Egress<H, M>(
    inout H hdr, inout M user_meta,
    in    psa_egress_input_metadata_t  istd,
    inout psa_egress_output_metadata_t ostd);

control EgressDeparser<H, M, CE2EM, RECIRCM>(
    packet_out buffer,
    out CE2EM clone_e2e_meta,
    out RECIRCM recirculate_meta,
    inout H hdr,
    in M meta,
    in psa_egress_output_metadata_t istd,
    in psa_egress_deparser_input_metadata_t edstd);

package IngressPipeline<IH, IM, NM, CI2EM, RESUBM, RECIRCM>(
    IngressParser<IH, IM, RESUBM, RECIRCM> ip,
    Ingress<IH, IM> ig,
    IngressDeparser<IH, IM, CI2EM, RESUBM, NM> id);

package EgressPipeline<EH, EM, NM, CI2EM, CE2EM, RECIRCM>(
    EgressParser<EH, EM, NM, CI2EM, CE2EM> ep,
    Egress<EH, EM> eg,
    EgressDeparser<EH, EM, CE2EM, RECIRCM> ed);

package PSA_Switch<IH, IM, EH, EM, NM, CI2EM, CE2EM, RESUBM, RECIRCM> (
    IngressPipeline<IH, IM, NM, CI2EM, RESUBM, RECIRCM> ingress,
    PacketReplicationEngine pre,
    EgressPipeline<EH, EM, NM, CI2EM, CE2EM, RECIRCM> egress,
    BufferingQueueingEngine bqe);

// END:Programmable_blocks

#endif  /* _PORTABLE_SWITCH_ARCHITECTURE_P4_ */

#endif   // __PSA_P4__

Николай Малых

nmalykh@protokols.ru

Рубрика: SDN, Сетевое программирование | Комментарии к записи Архитектура переносимых коммутаторов PSA отключены

Архитектура uBPF

PDF

Приведенный ниже включаемый файл (include) с переведенными на русский язык комментариями содержит определения пакета ubpf в одноименной модели. Файл ubpf.p4 размещается в каталоге p4include пакета p4c и доступен по ссылке.

/*
Copyright 2019 Orange

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.
*/

#ifndef _UBPF_MODEL_P4_
#define _UBPF_MODEL_P4_

#include <core.p4>

/*
 * Платформа uBPF в настоящее время позволяет пропускать (pass) или отбрасывать 
 * (drop) пакеты. По умолчанию пакеты пропускаются. Можно использовать 
 * внешнюю функцию mark_to_drop(), чтобы пометить пакет для отбрасывания. 
 * Функция mark_to_drop() меняет лишь состояние, скрытое от  пользовательской
 * программы P4 и вызывать ее следует лишь из элемента управления pipe.
 */
extern void mark_to_drop();

/*
 * Платформа uBPF в настоящее время позволяет пропускать (pass) или отбрасывать 
 * (drop) пакеты. По умолчанию пакеты пропускаются. Можно использовать 
 * внешнюю функцию mark_to_pass() чтобы пометить пакет для пропускания (это
 * отменяет предшествующее действие mark_to_drop()). Функция mark_to_drop()
 * меняет лишь состояние, скрытое от  пользовательской программы P4 
 * и вызывать ее следует лишь из элемента управления pipe.
 */
extern void mark_to_pass();


extern Register<T, S> {
  /***
   * Объект Register создается вызовом его конструктора. При вызове нужно
   * указать размер Register, определяющий максимальное число записей в
   * регистре. После создания объекта Register его можно применять в
   * действиях и блоках apply. При создании Register не инициализируется.
   */
  Register(bit<32> size);

  /***
   * Метод read() считывает состояние (T) из массива регистров по заданному
   * индексу S и возвращает значение в параметре result.
   *
   * @param index Индекс элемента массива регистров для считывания. Обычно
   *              из диапазона [0, size-1].
   * @return Возвращает результат типа T (в настоящее время только bit<W>).
   *         Если индекс находится в диапазоне, result получает значение 
   *         элемента массива регистров. При index >= size результат 
   *         становится неопределенным и его следует игнорировать.
   */
  T read  (in S index);

  void write (in S index, in T value);
}

/*
 * Функция возвращает метку текущего времени в наносекундах.
 */
extern bit<48> ubpf_time_get_ns();

enum HashAlgorithm {
    lookup3
}

/***
 * Рассчитывает хэш-функцию от значения, заданного параметром data. В силу 
 * ограничений uBPF максимальный размер data составляет bit<64>.
 *
 * Отметим, что типы всех параметров могут быть одинаковыми или разными.
 * Во втором случае их битовые размеры могут различаться.
 *
 * Результат всегда имеет размер bit<32>.
 *
 * @param D    Должен иметь тип tuple, состоящий из битовых полей (type bit<W>, int<W>) 
 *             или varbit. Максимальный размер D составляет 64 бита (ограничение uBPF).
 */
extern void hash<D>(out bit<32> result, in HashAlgorithm algo, in D data);

/***
 * Расчет контрольной суммы с помощью инкрементального обновления (RFC 1624).
 * Функция реализует расчет контрольной суммы для 16-битовых полей.
 */
extern bit<16> csum_replace2(in bit<16> csum,  // Текущая контрольная сумма.
                             in bit<16> old,   // Старое значение поля.
                             in bit<16> new);

/***
 * Расчет контрольной суммы с помощью инкрементального обновления (RFC 1624).
 * Функция реализует расчет контрольной суммы для 32-битовых полей.
 */
extern bit<16> csum_replace4(in bit<16> csum,
                             in bit<32> old,
                             in bit<32> new);

/*
 * Архитектура
 *
 * Параметр M должен иметь тип struct.
 *
 * Параметр H должен иметь тип struct, а каждый элемент структуры должен
 * быть заголовком, стеком или объединением заголовков (header_union).
 */

parser parse<H, M>(packet_in packet, out H headers, inout M meta);
control pipeline<H, M>(inout H headers, inout M meta);

/*
 * В теле сборщика (deparser) допускается лишь вызов метода packet_out.emit().
 */
@deparser
control deparser<H>(packet_out b, in H headers);

package ubpf<H, M>(parse<H, M> prs,
                pipeline<H, M> p,
                deparser<H> dprs);

#endif /* _UBPF_MODEL_P4_ */

Перевод на русский язык

Николай Малых

nmalykh@protokols.ru

Рубрика: SDN, Сетевое программирование | Комментарии к записи Архитектура uBPF отключены

Базовая библиотека P4 — core.p4

PDF

В спецификации языка P416 часть базовых функций была вынесена в отдельную библиотеку core.p4, содержащую базовые определения и функции языка и используемую в качестве включаемого файла (директива #include) программами P4. Ниже представлен вариант этого файла с переведенными на русский язык комментариями. Исходный файл доступен в каталоге p4includes пакета p4c или по ссылке.

/*
Copyright 2013-present Barefoot Networks, 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.
*/

/* Базовая библиотека P4-16, где объявлены некоторые встроенные элементы языка P4 
     для использования в программах P4 */

#ifndef _CORE_P4_
#define _CORE_P4_

/// Стандартные коды ошибок. Пользователи могут добавлять свои коды.
error {
    NoError,           /// Нет ошибок.
    PacketTooShort,    /// В пакете недостаточно битов для извлечения.
    NoMatch,           /// В выражении select не найдено соответствия.
    StackOutOfBounds,  /// Ссылка на недействительный элемент стека заголовков.
    HeaderTooShort,    /// Извлекается слишком много битов заголовка в поле varbit.
    ParserTimeout,     /// Превышено время выполнения кода синтаксического анализатора.
    ParserInvalidArgument  /// Операция анализатора была вызвана с неподдерживаемым
                           /// реализацией значением.
}

extern packet_in {
    /// Считывает заголовок пакета в заголовок фиксированного размера и перемещает указатель.
    /// Может приводить к ошибке PacketTooShort или StackOutOfBounds.
    /// Параметр @T должен иметь тип заголовка фиксированного размера.
    void extract<T>(out T hdr);
    /// Считывает биты из заголовка пакета в заголовок @variableSizeHeader с переменным размером
    /// и перемещает указатель.
    /// Параметр @T должен быть заголовком, содержащим в точности 1 поле 1.
    /// Может приводить к ошибке PacketTooShort, StackOutOfBounds или HeaderTooShort.
    void extract<T>(out T variableSizeHeader,
                    in bit<32> variableFieldSizeInBits);
    /// Считывает биты из пакета без перемещения указателя.
    /// Параметр @returns содержит прочитанные из пакета биты.
    /// T может быть произвольным типом фиксированного размера.
    T lookahead<T>();
    /// Перемещает указатель на заданное число битов.
    void advance(in bit<32> sizeInBits);
    /// Переменная @return содержит размер пакета в байтах. Может не поддерживаться архитектурой.
    bit<32> length();
}

extern packet_out {
    /// Записывает заголовок @hdr в выходной пакет, перемещая указатель.
    /// @T может иметь тип заголовка, стека или объединения заголовков, а также структурой с 
    /// такими полями.
    void emit<T>(in T hdr);
}

// В будущих версиях следует исключить приведенное ниже из файла, преобразовав в built-in

/// Проверяет предикат @check в синтаксическом анализаторе. При значении true не делает ничего,
/// в противном случае устанавливает ошибку анализатора в @toSignal и переходит в состояние reject.
extern void verify(in bool check, in error toSignal);

/// Встроенная пустая операция.
@noWarn("unused")
action NoAction() {}

/// Стандартные типы сопоставления для ключей таблиц.
/// Отдельные типы могут не поддерживаться архитектурой.
/// Архитектура может добавлять типы сопоставления.
match_kind {
    /// Точное совпадение битов
    exact,
    /// Троичное сопоставление с использованием маски.
    ternary,
    /// Наибольший совпадающий префикс.
    lpm
}

#endif  /* _CORE_P4_ */

Николай Малых

nmalykh@protokols.ru

Рубрика: SDN, Сетевое программирование | Комментарии к записи Базовая библиотека P4 — core.p4 отключены

Архитектура v1model

PDF

Приведенный ниже включаемый файл (include) с переведенными на русский язык комментариями содержит определения пакета V1Switch для архитектуры v1model и платформы BMV2. Файл v1model.p4 размещается в каталоге p4include пакета p4c (https://github.com/p4lang/p4c/blob/master/p4include/v1model.p4).

Этот файл включается в программы P4, работающие с прототипом коммутатора simple_switch на основе BMV2, включая тестовые примеры из пакета p4c (https://github.com/p4lang/p4c/tree/master/testdata).

/*
Copyright 2013-present Barefoot Networks, 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.
*/

/* Объявление P4-16 для модели коммутатора P4 v1.0 */

/* Примечание 1. Более подробное описание архитектуры v1model доступно
 * по приведенной ниже ссылке.
 *
 * https://github.com/p4lang/behavioral-model/blob/master/docs/simple_switch.md1 
 *
 * Примечание 2. В начале 2019 г. в рабочей группе P4 несколько раз
 * обсуждались способы вызова операций resubmit, recirculate и clone3
 * из соответствующих элементов управления (control), но значениями 
 * сохраняемых полей будут значения на момент завершения вызова
 * элемента управления. Так эти операции определены в P4_14. См.
 * https://github.com/p4lang/behavioral-model/blob/master/docs/simple_switch.md#restrictions-on-recirculate-resubmit-and-clone-operations1 
 *
 * Примечание 3. Имеются реализации P4_14, где вызов операции
 * generate_digest в field_list приводит к созданию сообщения для
 * плоскости управления, содержащего значения этих полей при завершении
 * работы элемента управления ingress, которые могут отличаться от
 * значений этих же полей в момент вызова из программы операции
 * generate_digest, если значения этих полей меняются при вызове 
 * элемента управления P4_14 ingress.
 *
 * Реализации P4_16 с моделью v1model всегда следует создавать 
 * сообщения digest, содержащие значения указанных полей на момент вызова
 * внешней функции digest. Программа P4_14 с описанным выше поведением,
 * скомпилированная с использованием p4c, может вести себя иначе.
 */

#ifndef _V1_MODEL_P4_
#define _V1_MODEL_P4_

#include "core.p4"

#ifndef V1MODEL_VERSION
#define V1MODEL_VERSION 20180101
#endif

match_kind {
    range,
    // Точное или шаблонное (соответствует любому) совпадение.
    optional,
    // Служит для реализации dynamic_action_selection.
    selector
}

const bit<32> __v1model_version = V1MODEL_VERSION;

#if V1MODEL_VERSION >= 20200408
typedef bit<9>  PortId_t;       // Не должно иметь постоянный размер?
#endif

@metadata @name("standard_metadata")
struct standard_metadata_t {
#if V1MODEL_VERSION >= 20200408
    PortId_t    ingress_port;
    PortId_t    egress_spec;
    PortId_t    egress_port;
#else
    bit<9>      ingress_port;
    bit<9>      egress_spec;
    bit<9>      egress_port;
#endif
    bit<32>     instance_type;
    bit<32>     packet_length;
    //
    // @alias служит для создания раздела field_alias в файле BMV2 JSON.
    // Псевдоним поля создает отображение имени метаданных в программе P4
    // на внутреннее имя метаданных в модели поведения (bmv2). Здесь это
    // служит для раскрытия всех метаданных, поддерживаемых simple_switch, 
    // для пользователя через standard_metadata_t.
    //
    // «Сглаживающие» поля из bmv2-ss для запроса метаданных.
    @alias("queueing_metadata.enq_timestamp")
    bit<32> enq_timestamp;
    @alias("queueing_metadata.enq_qdepth")
    bit<19> enq_qdepth;
    @alias("queueing_metadata.deq_timedelta")
    bit<32> deq_timedelta;
    /// Глубина очереди в момент извлечения пакета из нее.
    @alias("queueing_metadata.deq_qdepth")
    bit<19> deq_qdepth;

    // Внутренние метаданные
    @alias("intrinsic_metadata.ingress_global_timestamp")
    bit<48> ingress_global_timestamp;
    @alias("intrinsic_metadata.egress_global_timestamp")
    bit<48> egress_global_timestamp;
    /// Идентификатор multicast-группы (ключ для таблицы репликации mcast)
    @alias("intrinsic_metadata.mcast_grp")
    bit<16> mcast_grp;
    /// Идентификатор репликации для multicast
    @alias("intrinsic_metadata.egress_rid")
    bit<16> egress_rid;
    /// Указывает отрицательный результат verify_checksum().
    /// 1 при ошибке контрольной суммы, иначе 0.
    bit<1>  checksum_error;
    /// Ошибка при синтаксическом анализе.
    error parser_error;
    /// Задание приоритета для пакета.
    @alias("intrinsic_metadata.priority")
    bit<3> priority;
}

enum CounterType {
    packets,
    bytes,
    packets_and_bytes
}

enum MeterType {
    packets,
    bytes
}

extern counter
#if V1MODEL_VERSION >= 20200408
<I>
#endif
{
    /***
     * Объект counter (счетчик) создается вызовом конструктора. При 
     * этом создается массив состояний счетчика с набором состояний,
     * заданным параметром size. Индексы массива имеют значения  
     * [0, size-1].
     *
     * Нужно указать, будут ли учитываться только пакеты 
     * (CounterType.packets), только байты (CounterType.bytes) или
     * то и другое (CounterType.packets_and_bytes).
     *
     * Счетчики могут обновляться из программы P4, но для чтения 
     * доступны лишь плоскости управления. Если требуется чтение и 
     * запись из программы P4, следует использовать регистры.
     */
    counter(bit<32> size, CounterType type);
    /* FIXME - Аргумент size должен иметь тип int, но это нарушает
     * проверку типов
     */
 
    /***
     * count() вызывает для указанного индексом состояния считывание,
     * изменение и запись обратно в виде неделимого (atomic) блока
     * относительно обработки других пакетов. Обновление счетчиков 
     * пакетов, байтов или обоих зависит от CounterType экземпляра
     * счетчика при вызове для него конструктора.
     *
     * @param index Индекс обновляемого состояния счетчика в массиве.
     *              Обычно это значение из диапазона [0, size-1].
     *              Если index >= size, состояние счетчика не меняется.
     */
#if V1MODEL_VERSION >= 20200408
    void count(in I index);
#else
    void count(in bit<32> index);
#endif
}

extern direct_counter {
    /***
     * Объект direct_counter создается вызовом его конструктора. Нужно
     * указать подсчет только пакетов (CounterType.packets), только
     * байтов (CounterType.bytes) или обоих (CounterType.packets_and_bytes).
     * После создания объекта его можно связать с одной таблицей, добавляя
     * в ее определение указанное ниже свойство
     *     counters = <object_name>;
     *
     * Счетчики могут обновляться из программы P4, но для чтения 
     * доступны лишь плоскости управления. Если требуется чтение и 
     * запись из программы P4, следует использовать регистры.
     */
    direct_counter(CounterType type);
    /***
     * Метод count() реально не требуется в архитектуре v1model. 
     * Это связано с тем, что после привязки объекта direct_counter к
     * таблице, как описано в документации конструктора direct_counter,
     * при каждом обращении к таблице с возвратом подходящей записи
     * состояние счетчика, связанного с этой записью, считывается, 
     * изменяется и записывается обратно неделимым блоком относительно 
     * обработки других пакетов независимо от вызова метода count() из
     * тела данного действия.
     */
    void count();
}

#define V1MODEL_METER_COLOR_GREEN  0
#define V1MODEL_METER_COLOR_YELLOW 1
#define V1MODEL_METER_COLOR_RED    2

extern meter
#if V1MODEL_VERSION >= 20200408
<I>
#endif
{
    /***
     * Объект meter создается вызовом его конструктора. При этом 
     * создается массив состояний, число которых определяется параметром
     * size. Индексы массива лежат в диапазоне [0, size-1]. Например,
     * при наличии в системе 128 «потоков» с номерами от 0 до 127 и 
     * желании независимо измерять каждый из них, можно создать 
     * измеритель с size=128.
     *
     * Нужно указать для измерителя учет пакетов независимо от размера
     * (MeterType.packets) или числа байтов в пакетах (MeterType.bytes).
     */
    meter(bit<32> size, MeterType type);
    /* FIXME - Аргумент size должен иметь тип int, но это нарушает
     * проверку типов
     */

    /***
     * execute_meter() вызывает для указанного состояния считывание, 
     * изменение и запись обратно в виде неделимого блока относительно
     * обработки других пакетов и представление одним из цветов 
     * (green, yellow, red) в параметре out.
     *
     * @param index Индекс обновляемого состояния в массиве. Обычно из
     *              диапазона [0, size-1]. Если index >= size, состояние
     *              не обновляется.
     * @param result Тип T должен быть bit<W> с W >= 2. Если индекс
     *              находится в диапазоне, будет указано значение 0 для
     *              GREEN, 1 - для YELLOW и 2 - для RED (см. RFC 2697 и 
     *              RFC 2698). Если индекс выходит за пределы диапазона,
     *              результат становится неопределенным и его следует
     *              игнорировать вызывающему.
     */
#if V1MODEL_VERSION >= 20200408
    void execute_meter<T>(in I index, out T result);
#else
    void execute_meter<T>(in bit<32> index, out T result);
#endif
}

extern direct_meter<T> {
    /***
     * Объект direct_meter создается вызовом его конструктора. Нужно
     * указать учет пакетов независимо от размера (MeterType.packets)
     * или числа байтов в пакетах (MeterType.bytes). Созданный объект
     * можно связать с одной таблицей, добавляя в ее определение
     * приведенное ниже свойство
     *     meters = <object_name>;
     */
    direct_meter(MeterType type);
    /***
     * После привязки объекта direct_meter к таблице, как описано в 
     * документации конструктора direct_meter, при каждом нахождении
     * в таблице совпадающей записи значение связанного с этой записью
     * измерителя считывается, обновляется и записывается обратно в 
     * форме неделимого блока относительно обработки других пакетов
     * независимо от вызова метода read() из тела данного действия.
     *
     * Метод read() можно вызывать лишь внутри действия, выполняемого в
     * результате совпадения записи в таблице, с которой связан объект 
     * direct_meter. Результатом вызова read() будет численное 
     * представление цвета (green, yellow, red) в параметре out.
     *
     * @param result Тип T должен быть bit<W> с W >= 2. Указывается 
     *              значение 0 для GREEN, 1 - для YELLOW и 2 - для RED 
     *              (см. RFC 2697 и RFC 2698). 
     */
    void read(out T result);
}

#if V1MODEL_VERSION >= 20200408
extern register<T, I>
#else
extern register<T>
#endif
{
    /***
     * Объект register создается вызовом его конструктора. При этом
     * создается массив из size идентичных элементов типа T с индексами
     * из диапазона [0, size-1]. Например, вызов
     *     register<bit<32>>(512) my_reg;
     * выделит место для 512 значений типа bit<32>.
     */
    register(bit<32> size);
    /* FIXME - Аргумент size должен иметь тип int, но это нарушает
     * проверку типов
     */
    /***
     * Метод read() считывает из массива регистров указанное индексом 
     * состояние и возвращает его значение в параметре result.
     *
     * @param index Индекс элемента в массиве регистров для чтения.
     *              Обычно лежит в диапазоне [0, size-1].
     * @param result Поддерживаются лишь типы bit<W>. Когда индекс
     *              находится в диапазоне, result получает значение
     *              указанного элемента массива. Если index >= size, 
     *              результат будет неопределенным и его следует 
     *              игнорировать вызывающему.
     */
    @noSideEffects
#if V1MODEL_VERSION >= 20200408
    void read(out T result, in I index);
#else
    void read(out T result, in bit<32> index);
#endif
    /***
     * Метод write() записывает значение параметра value в массив 
     * регистров по указанному индексу.
     *
     * Если нужно выполнить read(), а затем write() для одного
     * элемента массива регистров и хочется сделать блок 
     * read-modify-write неделимым относительно обработки других пакетов
     * (параллельное выполнение в архитектуре v1model) требуется 
     * указать блок P4_16 с аннотацией @atomic (см. спецификацию P4_16).
     *
     * @param index Индекс элемента массива регистров для записи. Обычно
     *              из диапазона [0, size-1]. Если index >= size, состояние
     *              регистров не изменяется.
     * @param value Поддерживаются лишь типы bit<W>. Когда индекс
     *              находится в диапазоне, значение value записывается
     *              в указанный индексом элемент массива регистров.
     */
#if V1MODEL_VERSION >= 20200408
    void write(in I index, in T value);
#else
    void write(in bit<32> index, in T value);
#endif
}

// Служит атрибутом реализации таблицы
extern action_profile {
    action_profile(bit<32> size);
}

/***
 * Генерирует случайное значение из диапазона [lo..hi] и записывает
 * его в параметр result. Значение result становится неопределенным,
 * если lo > hi.
 *
 * @param T          Должно иметь тип bit<W>
 */
extern void random<T>(out T result, in T lo, in T hi);

/***
 * Вызов digest вызывает передачу сообщения, содержащего значения,
 * указанные в параметре data, программе плоскости управления. Это
 * похоже на отправку клона пакета плоскости управления, но может
 * быть более эффективным за счет того, что сообщения обычно меньше
 * пакета и можно собрать несколько таких сообщений в блок для 
 * одновременной отправки программе плоскости управления.
 *
 * Значениями полей, передаваемых в сообщении плоскости управления, 
 * будут соответствующие значения в момент вызова digest, даже если
 * эти поля позднее будут изменены элементом управления ingress 
 * (см. Примечание 3).
 *
 * Вызов digest поддерживается лишь из элемента управления ingress. 
 * После вызова метода нельзя отказаться от производимого им эффекта.
 *
 * Если тип T является именованной структурой, имя служит для генерации 
 * API плоскости управления.
 *
 * Реализация архитектуры v1model в BMv2 игнорирует значение параметра
 * receiver.
 */
extern void digest<T>(in bit<32> receiver, in T data);

enum HashAlgorithm {
    crc32,
    crc32_custom,
    crc16,
    crc16_custom,
    random,
    identity,
    csum16,
    xor16
}

@deprecated("Please use mark_to_drop(standard_metadata) instead.")
extern void mark_to_drop();

/***
 * mark_to_drop(standard_metadata) является примитивом действия, меняющим 
 * standard_metadata.egress_spec на зависимое от реализации специальное
 * значение, что в некоторых случаях ведет к отбрасывают пакета в конце
 * входной или выходной обработки. Устанавливается также значение 0 для
 * standard_metadata.mcast_grp. Любое из этих полей метаданных может быть
 * изменено кодом P4 после вызова mark_to_drop(), что может поменять для
 * пакета поведение на отличное от его отбрасывания (drop).
 *
 * В параграфе «Pseudocode for what happens at the end of ingress and 
 * egress processing» документа
 * https://github.com/p4lang/behavioral-model/blob/master/docs/simple_switch.md2 
 * описан относительный приоритет возможных действий применительно к пакету
 * по завершении входной и выходной обработки.
 */
@pure
extern void mark_to_drop(inout standard_metadata_t standard_metadata);

/***
 * Расчет хэш-функции для значения, указанного параметром data. Результат,
 * записываемый в параметр out с именем result, всегда будет находиться в
 * диапазоне [base, base+max-1], если max >= 1. При max=0 будет записано base.
 *
 * Отметим, что типы параметров могут быть одинаковыми или разными и битовые
 * размеры могут различаться.
 *
 * @param O   Должен иметь тип bit<W>
 * @param D   Должен иметь тип tuple, где все поля являются битовыми (bit<W> или int<W>) или varbit.
 * @param T   Должен иметь тип bit<W>
 * @param M   Должен иметь тип bit<W>
 */
@pure
extern void hash<O, T, D, M>(out O result, in HashAlgorithm algo, in T base, in D data, in M max);

extern action_selector {
    action_selector(HashAlgorithm algorithm, bit<32> size, bit<32> outputWidth);
}

enum CloneType {
    I2E,
    E2E
}

@deprecated("Please use verify_checksum/update_checksum instead.")
extern Checksum16 {
    Checksum16();
    bit<16> get<D>(in D data);
}

/***
 * Проверяет контрольную сумму представленных данных. При обнаружении
 * несоответствия поле standard_metadata checksum_error будет иметь 
 * в начале входной обработки значение 1.
 *
 * Вызов verify_checksum поддерживается лишь из элемента VerifyChecksum.
 *
 * @param T          Должен иметь тип tuple, все элементы которого имеют
 *                   тип bit<W>, int<W> или ,varbit<W>. Общий размер полей
 *                   должен быть кратным выходному размеру.
 * @param O          Тип контрольной суммы (bit<X>).
 * @param condition  Значение false, задает совпадение при любом результате.
 * @param data       Данные для проверки контрольной суммы.
 * @param checksum   Ожидаемая контрольная сумма (должно быть l-value).
 * @param algo       Алгоритм расчета контрольной суммы (могут поддерживаться
 *                   не все алгоритмы), должен быть известной при компиляции
 *                   константой.
 */
extern void verify_checksum<T, O>(in bool condition, in T data, in O checksum, HashAlgorithm algo);

/***
 * Рассчитывает контрольную сумму представленных данных и записывает ее в 
 * параметр checksum.
 *
 * Вызов update_checksum поддерживается лишь из элементов управления 
 * ComputeChecksum.
 *
 * @param T          Должен иметь тип tuple, все элементы которого имеют
 *                   тип bit<W>, int<W> или ,varbit<W>. Общий размер полей
 *                   должен быть кратным выходному размеру.
 * @param O          Тип контрольной суммы (bit<X>).
 * @param condition  При значении false параметр checksum не изменяется.
 * @param data       Данные для расчета контрольной суммы.
 * @param checksum   Контрольная сумма данных.
 * @param algo       Алгоритм расчета контрольной суммы (могут поддерживаться
 *                   не все алгоритмы), должен быть известной при компиляции
 *                   константой.
 */
@pure
extern void update_checksum<T, O>(in bool condition, in T data, inout O checksum, HashAlgorithm algo);

/***
 * verify_checksum_with_payload идентичен verify_checksum, но учитывает
 * при расчете контрольной суммы данные из пакета (все байты, которые не 
 * разбираются синтаксическим анализатором).
 *
 * Вызов verify_checksum_with_payload поддерживается лишь из элемента
 * управления VerifyChecksum.
 */
extern void verify_checksum_with_payload<T, O>(in bool condition, in T data, in O checksum, HashAlgorithm algo);

/**
 * update_checksum_with_payload идентичен update_checksum, но учитывает
 * при расчете контрольной суммы данные из пакета (все байты, которые не 
 * разбираются синтаксическим анализатором).
 *
 * Вызов update_checksum_with_payload поддерживается лишь из элемента
 * управления ComputeChecksum.
 */
@noSideEffects
extern void update_checksum_with_payload<T, O>(in bool condition, in T data, inout O checksum, HashAlgorithm algo);

/***
 * Вызов resubmit в процессе выполнения элемента ingress будет при 
 * некоторых документированных условиях приводить к повторному 
 * представлению пакета, т. е. пакет будет заново обработан анализатором
 * с тем же содержимым, которое было при предыдущей обработке. Различие
 * состоит лишь в значении поля standard_metadata instance_type и 
 * заданных пользователем полей метаданных, которые операция resubmit 
 * будет сохранять.
 *
 * Значения пользовательских метаданных, сохраняемых при повторном 
 * представлении пакетов, являются значениями в конце входной обработки,
 * а не значениями в момент вызова resubmit (см. Примечание 2).
 *
 * Вызов resubmit поддерживается лишь из элемента управления ingress. 
 * Отменить воздействие этого вызова невозможно. При неоднократном вызове
 * resubmit в одном процессе выполнения элемента ingress повторно 
 * представляется лишь один пакет и сохраняются данные лишь для последнего
 * вызова (см. Примечание 1).
 */
extern void resubmit<T>(in T data);

/***
 * Вызов recirculate в процессе выполнения элемента ingress будет при 
 * некоторых документированных условиях приводить к повторной обработке 
 * пакета, начиная с синтаксического анализа пакета, созданного сборщиком
 * (deparser). Рециркулирующие пакеты могут при входной обработке 
 * различаться с новыми пакетами значением поля standard_metadata
 * instance_type. Вызывающий может запросить сохранение некоторых 
 * полей пользовательских метаданных при рециркуляции пакета.
 *
 * Значения пользовательских метаданных, сохраняемых при рециркуляции 
 * пакетов, являются значениями в конце выходной обработки,
 * а не значениями в момент вызова recirculate (см. Примечание 2).
 *
 * Вызов recirculate поддерживается лишь из элемента управления egress. 
 * Отменить воздействие этого вызова невозможно. При неоднократном вызове
 * recirculate в одном процессе выполнения элемента egress рециркулирует 
 * лишь один пакет и сохраняются данные лишь для последнего
 * вызова (см. Примечание 1).
 */
extern void recirculate<T>(in T data);

/***
 * Операция clone почти идентична clone3 и отличается лишь тем, что
 * никогда не сохраняет поля пользовательских метаданных в клонах пакетов.
 * Она эквивалентна вызову clone3 с теми же параметрами type и session, но
 * с пустым параметром data.
 */
extern void clone(in CloneType type, in bit<32> session);

/***
 * Вызов clone3 при выполнении элемента управления ingress или egress
 * приводит к созданию клонов пакета (иногда называют отражением). 
 * Т. е. могут создаваться копии пакета, каждая из которых затем 
 * подвергается выходной обработке как независимый пакет. Для исходного
 * пакета продолжается обычная обработка независимо от клонов.
 *
 * Целочисленный параметр session содержит идентификатор сеанса клонирования
 * (иногда называется идентификатором сеанса отражения). Программы плоскости
 * управления должны настроить конфигурацию каждой сессии, которую планируется
 * использовать. Без этого клонирования происходить не будет. Обычно такая 
 * настройка включает задание плоскостью управления выходного порта, куда 
 * будет передаваться клон или списка пар (port, egress_rid), для которых 
 * следует создавать клоны подобно групповым пакетам.
 *
 * Клонированные пакеты можно различать по значениям поля 
 * standard_metadata instance_type.
 *
 * Вызывающий может запросить сохранение некоторых полей пользовательских 
 * метаданных исходного пакета при клонировании. Сохраняться будут значения
 * в конце входной или выходной обработки, а не в момент вызова clone3
 * (см. Примечание 2).
 *
 * При вызове clone3 во время входной обработки первым параметром должен быть
 * CloneType.I2E, для вызова при выходной обработке - CloneType.E2E.
 *
 * Отменить воздействие этого вызова невозможно. При неоднократном вызове
 * clone3 и/или clone в одном процессе входной или выходной обработки 
 * используется лишь последняя сессия клонирования и данные (см. Примечание 1).
 */
extern void clone3<T>(in CloneType type, in bit<32> session, in T data);

extern void truncate(in bit<32> length);

/***
 * Вызов assert с аргументом, имеющим значение true не дает эффекта, за 
 * исключением тех, которые может давать вычисление аргумента (см. ниже).
 * Если аргумент имеет значение false, поведение зависит от целевой платформы,
 * но цель заключается в записи или выводе информации об отказавшем операторе 
 * assert и возможно дополнительных данных об отказе.
 *
 * Например, на платформе simple_switch выполнение оператора assert с
 * аргументом false вызывает сообщение с именем файла и номером строки с
 * оператором assert, после чего процесс simple_switch завершается.
 *
 * Если была задана опция --ndebug в командной строке p4c при компиляции,
 * скомпилированная программа ведет себя как при отсутствии операторов 
 * assert в исходном коде.
 *
 * Настоятельно рекомендуется избегать выражений в качестве аргумента 
 * assert, поскольку это может приводить к побочным эффектам, например,
 * вызову внешнего метода или функции с побочным эффектом. Компилятор p4c 
 * позволяет это, не выдавая предупреждений. Это рекомендуется потому, что
 * программа будет вести себя так же, как при удалении операторов assert.
 */
extern void assert(in bool check);

/***
 * Для целей компиляции и запуска программ P4 на целевом устройстве
 * операторы assert и assume являются идентичными, включая использование
 * опции --ndebug компилятор p4c для их исключения. См. описание assert.
 *
 * Причиной использования assume как отдельной функции является допущение 
 * о ее специальной трактовке инструментами формальной проверки. Для 
 * некоторых таких инструментов цель состоит в попытке найти примеры 
 * пакетов и установленных записей таблиц, где условие оператора assert
 * имеет значение false.
 *
 * Предположим, что такой инструмент запускается для программы и примером 
 * служит пакет MPLS, где hdr.ethernet.etherType == 0x8847. При рассмотрении
 * примера видно, что условие assert имеет значение false. Однако планируется 
 * развертывание программы P4 в сети, где нет пакетов MPLS. Можно добавить
 * в программу P4 дополнительные условия для корректной обработки пакетов без
 * отказов assert, но проще сказать инструменту, что «такие экземпляры пакетов
 * не применимы и не нужно их показывать» путем добавления оператора
 *     assume(hdr.ethernet.etherType != 0x8847);
 * в подходящее место программы. Тогда формальный инструмент не будет показывать 
 * такие примеры, ограничиваясь лишь теми, которые дают условие true.
 *
 * Причина одинакового поведения операторов assume и assert при компиляции для
 * целевого устройства заключается в том, что значение false при оценке 
 * условия во время работы в сети может говорить об ошибочности допущения 
 * и необходимости его проверки.
 */
extern void assume(in bool check);

/*
 * Вывод заданного пользователем сообщения.
 * Например, log_msg("User defined message");
 * или log_msg("Value1 = {}, Value2 = {}",{value1, value2});
 */
extern void log_msg(string msg);
extern void log_msg<T>(string msg, in T data);

// Имя standard_metadata является зарезервированным.

/*
 * Архитектура.
 *
 * M должно иметь тип struct.
 *
 * H должно иметь тип struct, где каждый из элементов является заголовком,
 * стеком или объединением заголовков.
 */

parser Parser<H, M>(packet_in b,
                    out H parsedHdr,
                    inout M meta,
                    inout standard_metadata_t standard_metadata);

/*
 * В теле элемента управления VerifyChecksum допускаются лишь операторы 
 * блока, вызовы методов verify_checksum и verify_checksum_with_payload, 
 * а также операторы return.
 */
control VerifyChecksum<H, M>(inout H hdr,
                             inout M meta);
@pipeline
control Ingress<H, M>(inout H hdr,
                      inout M meta,
                      inout standard_metadata_t standard_metadata);
@pipeline
control Egress<H, M>(inout H hdr,
                     inout M meta,
                     inout standard_metadata_t standard_metadata);

/*
 * В теле элемента управления ComputeChecksum допускаются лишь операторы 
 * блока, вызовы методов update_checksum и update_checksum_with_payload, 
 * а также операторы return.
 */
control ComputeChecksum<H, M>(inout H hdr,
                              inout M meta);

/*
 * В теле элемента управления Deparser допускается лишь метод packet_out.emit().
 */
@deparser
control Deparser<H>(packet_out b, in H hdr);

package V1Switch<H, M>(Parser<H, M> p,
                       VerifyChecksum<H, M> vr,
                       Ingress<H, M> ig,
                       Egress<H, M> eg,
                       ComputeChecksum<H, M> ck,
                       Deparser<H> dep
                       );

#endif  /* _V1_MODEL_P4_ */

Николай Малых

nmalykh@protokols.ru

1Имеется перевод документа на русский язык.

2Параграф «Псевдокод для завершения входной и выходной обработки» в переводе.

Рубрика: SDN, Сетевое программирование | Комментарии к записи Архитектура v1model отключены

Параметры simple_switch

PDF

Синтаксис

simple_switch [опции] <путь к JSON-файлу конфигурации коммутатора>

Опции

-h [ —help ]

Выводит справочную информацию и на этом завершает работу.

-i [ —interface ] arg <port-num>@<interface-name>

Привязывает сетевой интерфейс <interface-name> в качестве порта с номером <port-num> при запуске программы. Опция может использоваться в команде многократно.

—pcap [=arg(=.)]

Задает создание файлов pcap для интерфейсов коммутатора. Необязательный аргумент позволяет указать каталог для сохранения файлов pcap, которые по умолчанию записываются в текущий каталог.

—use-files arg

Считывает пакеты из файлов или записывает в файлы (интерфейсу X соответствуют файлы X_in.pcap и X_out.pcap). Аргумент задает время ожидания (в секундах) перед началом обработки файлов с пакетами.

—packet-in arg

Включает прием пакетов на этом (nanomsg) сокете. В этом случае опция —interface будет игнорироваться.

—thrift-port arg

Порт TCP на котором будет работать сервер интерфейса управления Thrift.

—device-id arg

Идентификатор, указывающий устройство в сообщениях IPC. По умолчанию 0.

—nanolog arg

Сокет IPC для использования с журналами nanomsg pub/sub. По умолчанию журналы nanomsg не ведутся.

—log-console

Включает вывод журнальной информации на устройство stdout.

—log-file arg

Включает вывод журнала в указанный файл.

-L [ —log-level ] arg

Задает уровень детализации при выводе журнала работы и может принимать значение trace, debug, info, warn, error или off. По умолчанию используется уровень trace.

—log-flush

При использовании с опцией —log-file задает сброс журнала на диск после каждого сообщения.

—notifications-addr arg

Указывает адрес nanomsg для уведомлений (обучение, старения и т. п.), по умолчанию ipc:///tmp/bmv2-<device-id>-notifications.ipc

—restore-state arg

Задает восстановление состояния из файла.

—dump-packet-data arg

Задает число байтов для дампа принимаемых и передаваемых пакетов. Дамп выводится с уровнем info, поэтому следует убедиться, что при выводе этот уровень не исключается. По умолчанию установлено значение 0 и не выводится ничего.

-v [ —version ]

Выводит информацию о версии программы.

—json-version

Выводит максимальную поддерживаемую версию bmv2 JSON в формате <major>.<minor>. Должны поддерживаться все версии bmv2 JSON с совпадающим значением <major>.

—no-p4

Позволяет запустить коммутатор без файла конфигурации.

—max-port-count arg (=512)

Максимальное число интерфейсов, которые можно привязать к коммутатору. Это не верхняя граница номера порта, который может быть произвольным. В зависимости от платформы максимальное значение может не применяться.

Платформа имеет также свой анализатор команд и нужно отделять опции платформы от опций bmv2 двумя дефисами (—).

Опции платформы

—load-modules arg

Загружает указанные через запятую файлы .so как модули. Это полезно при использовании динамических библиотек с реализациями типа extern.

—enable-swap

Включает файл подкачки JSON в процессе работы.

—drop-port arg

Задает номер порта drop (по умолчанию 511).

Рубрика: SDN, Сетевое программирование | Комментарии к записи Параметры simple_switch отключены

simple_switch_CLI

Консольный интерфейс simple_switch_CLI

PDF

Версия 1.0

24.09.2020

Программы runtime_CLI и simple_switch_CLI служат для управления программными коммутаторами и маршрутизаторами из пакета BMv2, распространяемого в исходных кодах. Эти программы, по сути, представляют собой прототип плоскости управления (control-plane), полнофункциональной реализацией которой в SDN служит контроллер (например, P4Runtime).

Документ подготовлен на основе представленного разработчиками описания.

Большая часть описанных здесь команд работает в runtime_CLI и simple_switch_CLI, несколько команд применимы лишь в simple_switch_CLI и помечены как [simple_switch_CLI].

Управление программными реализациями прототипов устройств BMv2 выполняется через встроенный в эти реализации сервер Thrift. Интерфейс управления в настоящее время имеет достаточно ограниченную функциональность и позволяет выполнять лишь небольшой набор операций управления коммутатором:

  • просмотр состояния коммутатора и его портов;
  • добавление и удаление портов, без возможности управления их состоянием;
  • просмотр, добавление и удаление записей синтаксического анализатора (parser);
  • просмотр и изменение имеющихся таблиц сопоставления, без возможности добавления и удаления таблиц;
  • просмотр и ограниченное управление действиями в таблицах сопоставления;
  • просмотр и ограниченное управление клонированием пакетов для multicast-групп;
  • чтение, запись и сброс значений счетчиков и регистров, определенных в программе P4;
  • сохранение конфигурации в файл, загрузка новой конфигурации из файла, смена конфигурации.

По умолчанию сервер управления коммутатором доступен через порт 9090. При необходимости номер порта можно изменить с помощью опции —thrift-port в командной строке simple_switch (плоскость данных) и simple_switch_CLI (плоскость управления).

Команды управления портами

Набор команд управления портами в simple_switch достаточно скромен и позволяет лишь добавлять или удалять порты, связанные с физическими или логическими сетевыми интерфейсами аппаратной платформы. Включение (up) или отключение (down) порта из командного интерфейса не поддерживается.

show_ports

Команда без параметров, выводящая номера портов, имена связанных с ними интерфейсов и их состояние (up или down). Для портов коммутатора, связанных с логическими (виртуальными) интерфейсами аппаратной платформы команда всегда выводит состояние DOWN, хотя эти порты реально работают и способны принимать и передавать пакеты.

port_add

Добавляет в коммутатор порт, связанный с физическим или логическим интерфейсом платформы. Результат добавления зависит от используемого менеджера устройств. Команда принимает в качестве параметров имя интерфейса и номер порта. Дополнительно может указываться также путь к программе pcap, служащей для записи или «воспроизведение» сохраненных ранее пакетов.

port_add <iface_name> <port_num> [pcap_path]

Отметим, что для портов коммутатора simple_switch, связанных с логическими (виртуальными) интерфейсами платформы команда show_ports всегда показывает состояние DOWN, хотя эти порты реально работают и способны принимать и передавать пакеты.

port_remove

Удаляет порт из коммутатора. Результат удаления зависит от используемого менеджера устройств. Команда принимает в качестве параметра номер удаляемого порта.

port_remove <port_num>

Команды управления профилями действий

Концепция профилей действий (операций) в таблицах сопоставления использовалась в спецификации P414, но была исключена из P416. Тем не менее она сохранена в архитектуре V1Model, используемой simple_switch с программами P416. Ниже приведен фрагмент спецификации P414, связанный с профилями деййствий в таблицах сопоставления.

В некоторых экземплярах значения параметров действий (action) не специфичны для совпадающей записи и могут совместно использоваться несколькими записями. Это можно выразить в P4 с помощью профилей действий, которые представляют собой объявляемую структуру, задающую список возможных действий, а также могущую включать иные атрибуты.

Записи размещаются в процессе работы для указания одного действия (из числа указанных в профиле действий), которое будет выполняться при выборе данной записи, а также используемые значения параметров действия.

Вместо статической привязки одной записи из профиля действий к каждой записи таблицы сопоставления, можно связать несколько записей из профиля действий с одной записью таблицы сопоставления и позволить системе (т. е. логике плоскости данных) динамически связывать одну из записей профиля действий с каждым классом пакетов. Такое поведение включается атрибутом dynamic_action_selection. При задании этого атрибута записи профиля действий могут собираться в группы в процессе работы, а запись таблицы сопоставления можно связать с группой записей профиля действий. Для задания конкретного механизма плоскости данных, выбирающего определенную запись профиля действий в группе, нужно предоставить селектор действий. Этот селектор выбирает конкретную запись профиля действий для каждого пакета (псевдо)случайно или предсказуемо на основе полей заголовка и/или метаданных.

Ниже представлено определение профиля действий в формате BNF.

action_profile_declaration ::=
	action_profile action_profile_name {
		action_specification
		[ size : const_expr ; ]
		[ dynamic_action_selection : selector_name ; ]
	}
action_specification ::=
	actions { [ action_name ; ] + }
action_selector_declaration ::=
	action_selector selector_name {
		selection_key : field_list_calculation_name ;
}

Профили действий задаются и применяются в соответствии с приведенным ниже соглашением.

  • Атрибут size указывает число записей, требуемых для профиля действий. Если заданный размер не поддерживается, при обработке объявления будет выдан сигнал об ошибке. При опущенном атрибуте размера нет гарантии создания нужного числа записей в профиле действий в процессе работы.

Далее описаны поддерживаемые в simple_switch_CLI команды для работы с профилями действий в таблицах сопоставления программ P4.

act_prof_add_member_to_group

Добавляет элемент в группу в профиле действия (операции). Параметрами команды служат имя профиля действия, идентификатор элемента и идентификатор группы.

act_prof_add_member_to_group <action profile name> <member handle> <group handle>

act_prof_create_group

Добавляет группу в профиль действия (операции). Параметром команды служит имя профиля действия.

act_prof_create_group <action profile name>

act_prof_create_member

Добавляет элемент в профиль действия (операции). Параметрами команды служат имя профиля действия и имя действия, за которым могут следовать параметры действия.

act_prof_create_member <action profile name> <action_name> [action parameters]

act_prof_delete_group

Удаляет группу из профиля действия (операции). Параметрами команды служат имя профиля действия и идентификатор удаляемой группы.

act_prof_delete_group <action profile name> <group handle>

act_prof_delete_member

Удаляет элемент из профиля действия (операции). Параметрами команды служат имя профиля действия и идентификатор удаляемого элемента.

act_prof_delete_member <action profile name> <member handle>

act_prof_dump

Выводит список записей в профиле действия (операции). Параметром команды служит имя профиля действия.

act_prof_dump <action profile name>

act_prof_dump_group

Выводит информацию о группе из профиля действия (операции). Параметрами команды служат имя профиля действия и идентификатор группы.

act_prof_dump_group <action profile name> <group handle>

act_prof_dump_member

Выводит информацию об элементе из профиля действия (операции). Параметрами команды служат имя профиля действия и идентификатор элемента.

act_prof_dump_member <action profile name> <member handle>

act_prof_modify_member

Изменяет элемент в профиле действия (операции). Параметрами команды служат имя профиля действия, имя действия и идентификатор изменяемого элемента, за которым могут следовать параметры действия.

act_prof_modify_member <action profile name> <action_name> <member_handle> [action parameters]

act_prof_remove_member_from_group

Удаляет элемент из группы в профиле действия (операции). Параметрами команды служат имя профиля действия, идентификатор удаляемого элемента и идентификатор группы.

act_prof_remove_member_from_group <action profile name> <member handle> <group handle>

show_actions

Команда без параметров. Для каждого действия в загруженной программе P4 выводит имя и список параметров с указанием для каждого параметра имени и размера в битах.

Команды для работы с таблицами

table_add

Добавляет действие в указанную именем таблицу.

RuntimeCmd: help table_add Add entry to a match table: table_add <table name> <action name> <match fields> => <action parameters> [priority]

Можно увидеть список имен всех таблиц и действий с помощью команд show_tables и show_actions. Если не используется аннотация @name в программе P4, принятое по умолчанию имя зачастую является иерархическим и начинается с имени элемента управления, в котором определена таблица или действие.

Поля сопоставления должны указываться в том же порядке, в котором заданы поля ключей поиска в программе P4, но без имен. Вывод команды show_tables показывает имена, типы сопоставления (ternary, range, lpm, exact) и размер в битах для каждой таблицы после строки mk=. Поля с типом сопоставления optional представляются как ternary в файле BMv2 JSON, а в simple_switch_CLI всегда идентичны ternary.

Числовые значения можно задавать в десятичном или шестнадцатеричном формате (префикс 0x).

Для удобства представления некоторых числовых значений (адресов) применяются специальные форматы:

  • 32-битовые значения можно указывать в формате адресов IPv4 (например, 10.1.2.3);
  • 128-битовые значения можно задавать подобно адресам IPv6 в нотации IETF (например, FEDC:BA98:7654:3210:FEDC:BA98:7654:3210 или 1080::8:800:200C:417A) как описано в RFC 2732;
  • 48-битовые значения можно задавать подобно адресам Ethernet MAC с парами шестнадцатеричных цифр, разделенными двоеточиями (например, 00:12:34:56:78:9a).

Поля ключей поиска в таблице с типом сопоставления lpm должны иметь значение, за которым указан символ / и размер префикса (например, 0x0a010203/24). Размер префикса должен указываться десятичным числом. Может указываться префикс с размером 0 и такие записи будут соответствовать любому значению искомого поля.

Поля ключей поиска в таблице с типом сопоставления ternary задаются численным значением, за которым следует &&& и численная маска без пробелов между числами и &&&. Биты маски со значением 1 задают совпадение битов, а биты со значением маски 0 не рассматриваются при сопоставлении. Т. е. запись value&&&mask будет соответствовать ключу поиска k, если (k & mask) == (value & mask), где операция & выполняется для каждого бита, как в P4. Для соответствия любому значению при троичном поиске можно задать 0&&&0.

Поля ключей поиска в таблице с типом сопоставления optional в исходном коде P4 задаются так же, как поля с типом сопоставления ternary. Отметим, что CLI при работе не проверяет наличие в маске только нулей или только 1, что позволяет задавать произвольные ternary-маски, даже если это не следует разрешать. Было бы хорошо ограничить задание сопоставления типа optional, но это потребует внесений существенных изменений в код behavioral-model.

Поля ключей поиска в таблице с типом сопоставления range задаются минимальным значением, за которым без пробела следует -> и максимальное значение (без пробела). Для соответствия любому значению из диапазона можно указать 0->255 для 8-битового поля, 0->0xffffffff для 32-битового и т. п.

Если любое поле ключа поиска в таблице имеет тип сопоставления ternary, optional или range, в записи должно быть указано числовое значение приоритета. Для полей с другими типами сопоставления задание приоритета является ошибкой. Если ключу поиска соответствует несколько записей таблицы, из них выбирается запись с меньшим числовым значением приоритета и выполняется действие из этой записи.

Параметры действий должны указываться в том же порядке, как они заданы в программе P4, но без имен. Вывод команды show_actions показывает имена и размеры в битах для всех параметров каждого действия.

table_clear

Удаляет все записи в таблице сопоставления (прямого или опосредованного), указанной именем, оставляя лишь запись default.

table_clear <table name>

table_delete

Удаляет из указанной именем таблицы заданную идентификатором запись.

table_delete <table name> <entry handle>

table_dump

Выводит дамп указанной именем таблицы.

table_dump MyIngress.ipv4_lpm 
========== 
TABLE ENTRIES 
========== 
Dumping default entry 
Action entry: MyIngress.drop -  
==========

table_dump_entry, table_dump_entry_from_key

Выводит дамп записи из указанной по имени таблицы. Запись задается идентификатором или ключом поиска.

table_dump_entry <table name> <entry handle>
table_dump_entry_from_key <table name> <match fields> [priority]

table_dump_group, table_dump_member

Выводит информацию (дамп) о группе или элементе из указанной именем таблицы. Группа или элемент задаются идентификатором.

table_dump_group <table name> <group handle>
table_dump_member <table name> <member handle>

table_indirect_add

Добавляет запись в таблицу непрямого сопоставления, указанную именем. Параметрами команды являются поля сопоставления и идентификатор элемента таблицы, может также указываться значение приоритета.

table_indirect_add <table name> <match fields> => <member handle> [priority]

table_indirect_add_member_to_group

Заменена командой act_prof_add_member_to_group.

table_indirect_add_with_group

Добавляет запись в таблицу непрямого сопоставления, указанную именем. Параметрами команды являются поля сопоставления и идентификатор группы, может также указываться значение приоритета.

table_indirect_add <table name> <match fields> => <group handle> [priority]

table_indirect_create_group

Заменена командой act_prof_create_group.

table_indirect_create_member

Заменена командой act_prof_create_member.

table_indirect_delete

Удаляет запись из заданной именем таблицы непрямого сопоставления. Запись указывается идентификатором.

table_indirect_delete <table name> <entry handle>

table_indirect_delete_group

Заменена командой act_prof_delete_group.

table_indirect_delete_member

Заменена командой act_prof_delete_member.

table_indirect_modify_member

Заменена командой act_prof_modify_member.

table_indirect_remove_member_from_group

Заменена командой act_prof_remove_member_from_group.

table_indirect_reset_default, table_indirect_set_default

Сбрасывает или устанавливает принятое по умолчанию действие в таблице непрямого сопоставления указанной именем. Устанавливаемое действие задается идентификатором элемента.

table_indirect_reset_default <table name>
table_indirect_set_default <table name> <member handle>

table_indirect_set_default_with_group

Устанавливает используемую по умолчанию группу в заданной именем таблице непрямого сопоставления. Группа указывается идентификатором.

table_indirect_set_default <table name> <group handle>

table_info

Выводит информацию об указанной именем таблице сопоставления.

table_info <table name>

table_modify

Добавляет запись в указанную именем таблицу сопоставления. Запись задается именем добавляемого действия и идентификатором, а также может включать параметры действия.

table_modify <table name> <action name> <entry handle> [action parameters]

table_num_entries

Возвращает число записей в таблице прямого или опосредованного сопоставления, указанной именем.

table_num_entries <table name>

table_reset_default

RuntimeCmd: help table_reset_default Reset default entry for a match table: table_reset_default <table name>

Меняет действие, выполняемое в таблице при отсутствии соответствующей ключу поиска записи на исходное значение, заданное в программе P4. Если в программе такое действие не задано, используется «пустая операция» — no-op (иногда ее называют NoAction).

table_set_default

RuntimeCmd: help table_set_default Set default action for a match table: table_set_default <table name> <action name> <action parameters>

Назначает действие, выполняемое таблицей при отсутствии записи, соответствующей ключу поиска.

Задание параметров действия рассмотрено в описании команды table_add.

table_set_timeout

Устанавливает время ожидания (тайм-аут) для указанной идентификатором записи в заданной именем таблице. Время ожидания указывается в миллисекундах.

table_set_timeout <table_name> <entry handle> <timeout>

show_tables

Команда без параметров. Для каждой таблицы в загруженной программе P4 выводит имя, реализацию (часто None, но может выводиться профиль или селектор действия для таблиц, созданных с этими опциями) и список полей поиска в таблице (ключей) с указанием для каждого поля имени, типа соответствия (например, exact, lpm, ternary, range) и размера в битах. Поля с типом соответствия optional в коде P4 представляются в файле BMv2 JSON как ternary.

table_show_actions

Выводит список действий заданной именем таблицы в порядке их представления в программе P4. Например,

RuntimeCmd: table_show_actions MyIngress.ipv4_lpm 
MyIngress.drop                 [] 
MyIngress.ipv4_forward         [dstAddr(48),    port(9)] 
NoAction

Команды управления синтаксическим анализатором

pvs_add, pvs_clear, pvs_get, pvs_remove

Эти команды служат для управления экземпляром набора значений синтаксического анализатора P4. Команда pvs_add добавляет запись в набор значений, pvs_remove удаляет запись, pvs_clear удаляет все записи, а pvs_get служит для просмотра имеющихся записей.

Язык P4 позволяет передавать value_set в качестве параметра структурные типы, как показано ниже.

struct vsk_t { bit<16> f1; bit<7> f2; } value_set<vsk_t>(4) pvs; select (<X>) { pvs: accept; _: reject; }

При добавлении или удалении записи требуется указать целое число, соответствующее сжатой разрядности набора значений, иначе CLI будет сообщать об ошибке. Когда записи набора значений состоят из множества отдельных полей, как в приведенном выше примере, сжатая разрядность определяется как сумма разрядностей этих полей без учета заполнения. Когда параметр типа для набора значений является целым числом (со знаком или без него) или структурой с одним полем, сжатая разрядность просто совпадает с размером этого поля.

В будущем CLI сможет поддерживать более понятный интуитивно интерфейс и позволит предоставлять целочисленные значения для разных полей, составляющих запись набора значений. Однако в настоящее время размер приходится вычислять самостоятельно.

Отметим, что pvs_add не выдает предупреждений при попытке добавить уже имеющуюся запись, а pvs_remove не предупреждает о попытке удалить отсутствующее значение.

Реализация наборов значений в bmv2 при сопоставлении поддерживает лишь точное совпадение (exact).

show_pvs

Команда без параметров. Для каждого набора значений анализатора в загруженной программе P4 выводит имя и ожидаемый размер записей. При добавлении записей командой pvs_add требуется указывать целочисленное значение, не превышающее этот размер.

Команды для работы с групповыми пакетами

mc_dump, mc_mgrp_create, mc_mgrp_destroy, mc_node_associate, mc_node_create, mc_node_destroy, mc_node_dissociate

При завершении входной обработки в архитектуре v1model входной код P4 может устанавливать значения нескольких полей в standard_metadata, контролирующих отбрасывание пакетов, передаваемых индивидуально (unicast) в один выходной порт, или групповых пакетов, реплицированных в список (возможно пустой) выходных портов. Более подробное описание приведено в разделе «Псевдокод для завершения входной и выходной обработки» описания bmv2.

Буфер пакетов содержит машину репликации PRE1.

В v1model.p4 standard_metadata поле mcast_grp задает один из 65535 идентификаторов multicast-групп из диапазона [1, 65535]. Значение mcast_grp = 0 задает отсутствие групповой репликации пакетов. Коммутатор simple_switch может использовать большее число идентификаторов групп и указанный диапазон задан определением поля mcast_grp в standard_metadata (файл v1model.p4) с типом bit<16>.

Для реплицируемых пакетов PRE находит в таблице значение mcast_grp и по нему получает (возможно пустой) список пар (egress_port, egress_rid), для которых создаются копии пакета. Для каждой копии позднее выполняется выходная обработка с двумя полями standard_metadata, инициализированными значениями найденной в таблице пары.

Отметим, что таблица групповой репликации не является таблицей, определяемой в программе P4. Здесь термин «таблица» имеет более общий смысл. Таблица репликации похожа на обычную таблицу P4 в том смысле, что поле mcast_grp является единственным ключом поиска и для него всегда применяется точное совпадение. Однако имеется ряд перечисленных ниже различий.

  • Таблица не объявляется в программе и присутствует в simple_switch независимо от загруженной программы P4 и применения в программе этой таблицы.
  • Программы плоскости управления используют для чтения и записи в таблицу групп иные вызовы API, нежели для таблиц P4.
  • Результатом поиска в таблице является список с переменным числом пар, тогда как язык P4 не имеет простого способа представления списков переменного размера в процессе работы.

В управление списками групповой рассылки на деле включены два «уровня» объектов:

  • каждая multicast-группа является множеством узлов;
  • каждый создаваемый узел групповой рассылки имеет одно значение egress_rid и набор egress_port, в котором не может быть дубликатов.

Предположим, что нужно настроить simple_switch так, чтобы пакеты для группы с mcast_grp = 1113 рассылались в приведенный ниже список пар (egress_port, egress_rid):

  • (egress_port=0, egress_rid=5);
  • (egress_port=7, egress_rid=10);
  • (egress_port=3, egress_rid=5).

Первая и последняя запись имеют общее значение egress_rid = 5, поэтому они могут находится в одном multicast-узле, который мы хотим создать в этом примере. Делается это командой mc_node_create, как показано ниже.

RuntimeCmd: help mc_node_create Create multicast node: mc_node_create <rid> <space-separated port list> [ | <space-separated lag list> ]

Команда имеет два обязательных параметра, задающих идентификатор и список выходных портов, а также позволяет указать список агрегированных портов (LAG) для групповой рассылки. Создадим узел для рассылки в порты 0 и 3.

RuntimeCmd: mc_node_create 5 0 3 Creating node with rid 5 , port map 1001 and lag map node was created with handle 0

Отметим дескриптор 0 в выводе команды. При каждом создании multicast-узла программа simple_switch выбирает целочисленный идентификатор для его именования, который должен использоваться при указании данного узла в последующих командах. Значения обычно начинаются с 0 и увеличиваются по мере создания новых узлов, но лучше не полагаться на это и смотреть вывод команды.

Создадим другой узел для копирования пакетов с egress_rid = 10, в нашем примере включающий лишь порт 7

RuntimeCmd: mc_node_create 10 7 Creating node with rid 10 , port map 10000000 and lag map node was created with handle 1

Вывод показывает, что этому узлу был назначен дескриптор 1.

Далее создадим группу с идентификатором 1113 командой mc_mgrp_create.

RuntimeCmd: help mc_mgrp_create Create multicast group: mc_mgrp_create <group id>

Справка говорит, что команда имеет единственный параметр — идентификатор группы.

RuntimeCmd: mc_mgrp_create 1113 Creating multicast group 1113

Здесь не используются специальные дескрипторы. Группы всегда именуются по заданным для них идентификаторам (1113 в этом примере).

После этого можно посмотреть созданные группы с помощью команды mc_dump.

RuntimeCmd: mc_dump ========== MC ENTRIES ********** mgrp(1113) ========== LAGS ==========

Вывод показывает наличие группы 1113, с которой не связано никаких узлов. В последующих примерах будут показаны группы с multicast-узлами. В текущей конфигурации simple_switch, передача пакета для репликации с mcast_grp = 1113 будет создавать 0 копий пакета, что равносильно его отбрасыванию (drop).

Для добавления multicast-узла в группу служит команда mc_node_associate.

RuntimeCmd: help mc_node_associate Associate node to multicast group: mc_node_associate <group handle> <node handle>

Команда принимает два параметра, один из которых задает идентификатор группы, другой — узла.

RuntimeCmd: mc_node_associate 1113 0 Associating node 0 to multicast group 1113

После добавления узла группа выглядит уже иначе.

RuntimeCmd: mc_dump ========== MC ENTRIES ********** mgrp(1113)   -> (L1h=0, rid=5) -> (ports=[0, 3], lags=[]) ========== LAGS ==========

Видно, что группа 1113 имеет один узел. L1h является сокращением для записей L1 handle, которые можно увидеть в системном журнале simple_switch (это просто идентификатор узла). Показано значение rid = 5 (rid — сокращение для egress_rid), и список портов 0 и 3.

В текущей конфигурации simple_switch передача пакета для репликации с mcast_grp = 1113 будет создавать 2 копии пакета с парами:

  • (egress_port=0, egress_rid=5);
  • (egress_port=3, egress_rid=5).

Добавим в группу 1113 второй узел, упомянутый выше.

RuntimeCmd: mc_node_associate 1113 1 Associating node 1 to multicast group 1113

Группа после этого будет иметь вид

RuntimeCmd: mc_dump ========== MC ENTRIES ********** mgrp(1113)   -> (L1h=0, rid=5) -> (ports=[0, 3], lags=[])   -> (L1h=1, rid=10) -> (ports=[7], lags=[]) ========== LAGS ==========

В текущей конфигурации simple_switch передача пакета для репликации с mcast_grp = 1113 приведет к созданию трех копий с парами:

  • (egress_port=0, egress_rid=5);
  • (egress_port=3, egress_rid=5);
  • (egress_port=7, egress_rid=10).

Можно исключить узел из группы с помощью команды mc_node_dissociate.

RuntimeCmd: help mc_node_dissociate Dissociate node from multicast group: mc_node_associate <group handle> <node handle>

В качестве параметров команды нужно указать группу и дескриптор исключаемого узла.

RuntimeCmd: mc_node_dissociate 1113 0 Dissociating node 0 from multicast group 1113

Группа после исключения узла будет иметь вид

RuntimeCmd: mc_dump ========== MC ENTRIES ********** mgrp(1113)   -> (L1h=1, rid=10) -> (ports=[7], lags=[]) ========== LAGS ==========

В этой конфигурации simple_switch передача пакета для репликации с mcast_grp = 1113 будет создавать 1 копию с (egress_port=7, egress_rid=10).

При исключении узла 1 из группы 1113, она вернется в исходное состояние и групповые пакеты будут отбрасываться.

RuntimeCmd: mc_node_dissociate 1113 1 Dissociating node 1 from multicast group 1113 RuntimeCmd: mc_dump ========== MC ENTRIES ********** mgrp(1113) ========== LAGS ==========

Можно удалить группу с помощью команды mc_mgrp_destroy.

RuntimeCmd: help mc_mgrp_destroy Destroy multicast group: mc_mgrp_destroy <group id>

Единственным параметром команды является идентификатор группы.

RuntimeCmd: mc_mgrp_destroy 1113 Destroying multicast group 1113

После этого команда просмотра групп будет давать пустой вывод.

RuntimeCmd: mc_dump ========== MC ENTRIES ========== LAGS ==========

Для описанных операций есть ряд ограничений.

  • Для привязки узла к группе нужно сначала создать то и другое. Порядок создания не имеет значения.
  • Коммутатор simple_switch позволяет связать узел лишь с одной группой. Для привязки узла к другой группе его нужно сначала отсоединить от текущей группы.
  • При удалении группы рекомендуется сначала удалить привязки узлов к ней.
  • В одном узле значение egress_port не может повторяться. Если нужно отправить в порт несколько копий, следует создать дополнительные узлы. Использование разных egress_rid для этих узлов позволяет коду P4 различать копии и по разному обрабатывать их.

Команда mc_dump выводит информацию об узлах, привязанных к группам. Для просмотра узлов, не связанных с группой, в simple_switch_CLI нет команды.

Отметим, что в справке simple_switch_CLI и выводе команды mc_dump указываются группы lag и их списки, которые еще не документированы. Однако групповую репликацию можно использовать и без них.

mc_node_update

Обновляет групповой узел, принимая в качестве параметра идентификатор узла и список разделенных пробелами номеров портов. Дополнительно может указываться список разделенных пробелами LAG.

mc_node_update <node handle> <space-separated port list> [ | <space-separated lag list> ]

mc_set_lag_membership

Задает набор портов, включаемых в группу LAG. Параметрами служат индекс LAG и список разделенных пробелами номеров портов коммутатора.

mc_set_lag_membership <lag index> <space-separated port list>

Программы управления клонированием пакетов

mirroring_add, mirroring_add_mc, mirroring_delete, mirroring_get

[simple_switch_CLI]

При обработке пакетов могут происходить перечисленные ниже события:

  • программа с архитектурой P416 v1model вызывает внешнюю функцию clone или clone3 при входной или выходной обработке и на этом соответствующая обработка завершается;
  • программа P414 вызывает clone_ingress_pkt_to_egress при входной обработке, которая на этом завершается;
  • программа P414 вызывает clone_egress_pkt_to_egress при выходной обработке, которая на этом завершается.

В этих случаях не только «исходный» пакет продолжит прохождение через конвейер, но и пакеты, которые могут быть созданы PRE в составе simple_switch (копии или клоны) из текущего пакета, будут помещены в очередь в буфере пакетов и позднее для каждого из них будет выполнена выходная обработка. Каждый из клонов обрабатывается независимо от других и от исходного пакета. Дополнительную информацию можно найти поиском по слову «клон» в разделе «Псевдокод для завершения входной и выходной обработки» описания bmv2.

В simple_switch поддерживается до 32768 независимых сессий клонирования с номерами от 0 до 32767, которые называют также сессиями отражения (mirroring session). При клонировании пакета указывается желаемая сессия для вызова из программы P4 операции клонирования.

Целью команд отражения является указание портов, в которые передаются клонированные пакеты. Это настраивается независимо для каждой сессии клонирования по ее идентификатору.

По умолчанию при старте simple_switch сессии клонирования не настроены. Любая операция клонирования для ненастроенной сессии не будет создавать клонов, т. е поведение обработки пакетов пойдет обычным путем, как будто клонирование не запрашивалось.

RuntimeCmd: help mirroring_add Add mirroring session to unicast port: mirroring_add <mirror_id> <egress_port>

Например, по команде mirroring_add 5 7 в конфигурации сессии с идентификатором 5 будет задано создание операцией клонирования в сессии 5 одного клона, предназначенного для выходного порта 7.

RuntimeCmd: mirroring_add 5 7 RuntimeCmd: mirroring_get 5 MirroringSessionConfig(mgid=None, port=7)

Можно задать в конфигурации сессии клонирование пакетов в multicast-группу.

RuntimeCmd: help mirroring_add_mc Add mirroring session to multicast group: mirroring_add_mc <mirror_id> <mgrp>

Команда mirroring_add_mc 5 22 изменит конфигурацию сессии 5 так, что последующие операции клонирования для этой сессии будут выполнять множественное клонирование для multicast-группы 22. Команда mc_mgrp_create и другие команды работы с группами описаны выше.

RuntimeCmd: mirroring_add_mc 5 22 RuntimeCmd: mirroring_get 5 MirroringSessionConfig(mgid=22, port=None)

Можно сбросить состояние настроенной сессии клонирования по ее идентификатору.

RuntimeCmd: help mirroring_delete Delete mirroring session: mirroring_delete <mirror_id>

Команда mirroring_delete 5 возвращает сессию 5 в исходное (ненастроенное) состояние.

RuntimeCmd: mirroring_delete 5

С помощью команды mirroring_get можно увидеть состояние указанной идентификатором сессии клонирования. Например, для сброшенной предыдущей командой сессии 5 вывод будет иметь вид

RuntimeCmd: mirroring_get 5 Invalid mirroring operation (SESSION_NOT_FOUND)

Команды для работы с измерителями

meter_array_set_rates

Задает скорости для всего массива измерителей, принимая в качестве параметров имя, а также набор значений скоростей и пиков.

meter_array_set_rates <name> <rate_1>:<burst_1> <rate_2>:<burst_2> ...

meter_get_rates

Возвращает параметры измерителя по имени и индексу.

meter_get_rates <name> <index>

meter_set_rates

Синтаксис команды в CLI приведен ниже.

RuntimeCmd: help meter_set_rates Configure rates for a meter: meter_set_rates <name> <index> <rate_1>:<burst_1> <rate_2>:<burst_2> ...

Поведение измерителя соответствует RFC 2697 и RFC 2698.

Пользователь задает скорость для измерителя в байтах за микросекунду и burst_size в байтах. Для измерителей, учитывающих пакеты, скорость задается числом пакетов за микросекунду, burst_size — числом пакетов.

Команды для работы со счетчиками

counter_read, counter_reset, counter_write

Считывает, сбрасывает или задает значение указанного именем счетчика. При чтении и записи команда включает индекс, а при записи дополнительно указываются записываемые в счетчик значения числа пакетов и байтов.

Команды для работы с регистрами

register_read, register_write

Читает или записывает регистр, указанный именем и индексом. При записи также указывается вносимое в регистр значение.

register_reset

Сбрасывает в 0 все ячейки массива регистров, указанного именем.

register_reset <name>

Команды управления очередями

set_queue_depth

[simple_switch_CLI]

Задает размер всех или указанной выходной очереди, принимая в качестве параметров число пакетов в очереди и необязательный номер порта. По умолчанию устанавливается размер очередей на всех выходных портах.

set_queue_depth <nb_pkts> [<egress_port>]

set_queue_rate

[simple_switch_CLI]

Задает скорость (пакет/с) всех или указанной выходной очереди, принимая в качестве параметров скорость для очереди и необязательный номер порта. По умолчанию устанавливается скорость для очередей на всех выходных портах.

set_queue_rate <rate_pps> [<egress_port>]

Прочие команды

help

При запуске без параметра выводит список поддерживаемых интерфейсом CLI команд. При указании в качестве параметра имени той или иной команды CLI будет выведена краткая справка по работе с командой. Например,

RuntimeCmd: help help 
List available commands with "help" or detailed help with "help cmd". RuntimeCmd: help show_tables 
List tables defined in the P4 program: show_tables

switch_info

Выводит базовую информацию о коммутаторе.

switch_info 
device_id                : 0 
thrift_port              : 9090 
notifications_socket     : ipc:///tmp/bmv2-0-notifications.ipc 
elogger_socket           : None 
debugger_socket          : None

serialize_state

Преобразует все состояния коммутатора в последовательную форму и выводит информацию в файл, заданный параметром команды.

reset_state

Сбрасывает все состояния коммутатора (таблицы, регистры и т. п.), сохраняя конфигурацию P4. Команда не имеет параметров.

get_time_elapsed, get_time_since_epoch

[simple_switch_CLI]

Возвращает время, прошедшее с момента запуска коммутатора или с начала «эпохи» в микросекундах. Команда не имеет параметров.

set_crc16_parameters, set_crc32_parameters

Задает параметры вычисления контрольных сумм crc16 или crc32.

set_crc16_parameters <name> <polynomial> <initial remainder> <final xor value> <reflect data?> <reflect remainder?>
set_crc32_parameters <name> <polynomial> <initial remainder> <final xor value> <reflect data?> <reflect remainder?>

load_new_config_file

Загружает конфигурацию коммутатора из заданного параметром файла JSON. Для активизации этой конфигурации служит команда Ошибка: источник перекрёстной ссылки не найден.

swap_configs

Меняет конфигурацию, используя файл, загруженный ранее командой load_new_config_file.

write_config_to_file

Считывает конфигурацию из используемого в данный момент файла JSON и записывает ее в указанный пользователем файл на станции управления, где запущена программа simple_switch_CLI или runtime_CLI. Если файл задан без абсолютного пути, он будет сохранен в каталоге, из которого была запущена программа.

shell

Позволяет выполнить из консоли коммутатора команду операционной системы, в которой коммутатор работает.

shell <command>

Николай Малых

nmalykh@protokols.ru

1Packet Replication «Engine» — «машина» репликации пакетов.

Рубрика: SDN, Сетевое программирование | Комментарии к записи simple_switch_CLI отключены

Компилятор P4C (драйвер компиляции)

PDF

Синтаксис

p4c [-h] [-V] [-v] [-###] [-Xpreprocessor <arg>] [-Xp4c <arg>] 
    [-Xassembler <arg>] [-Xlinker <arg>] [-b TARGET] [-a ARCH] [-c] 
    [-D PREPROCESSOR_DEFINES] [-E] [-e] [-g] [-I SEARCH_PATH] [-o PATH] 
    [--p4runtime-file P4RUNTIME_FILE] 
    [--p4runtime-files P4RUNTIME_FILES] 
    [--p4runtime-format {binary,json,text}] [--help-pragmas] 
    [--help-targets] [--disable-annotations DISABLED_ANNOS] [-S] 
    [--std {p4-14,p4_14,p4-16,p4_16}] [--ndebug] 
    [source_file]

Позиционные аргументы source_file обязательны (хотя бы один) и указывают имена файлов P4 для компиляции.

Опции

Остальные аргументы команды являются необязательными (опции) и служат для управления компиляцией исходного кода P4. Многие опции доступны в краткой и длинной форме (с префиксом —)

-h, —help

Выводит справку о параметрах команды и завершает работу.

-V, —version

Выводит номер версии программы и завершает работу.

-v, —debug

Задает подробный вывод программы.

-###, —test-only

Выводит команды на консоль без из реального выполнения.

-Xpreprocessor <arg>

Задает передачу аргумента <arg> препроцессору компилятора.

-Xp4c <arg>

Задает аргумент, передаваемый компилятору.

-Xassembler <arg>

Задает передачу аргумента <arg> ассемблеру.

-Xlinker <arg>

Задает передачу аргумента <arg> компоновщику.

-b TARGET, —target TARGET

Задает целевое устройство (платформу).

-a ARCH, —arch ARCH

Задает целевую архитектуру.

-c, —compile

Задает выполнение лишь этапов предварительной обработки, компиляции и сборки.

-D PREPROCESSOR_DEFINES

Определяет макрос, используемый препроцессором.

-E

Задает выполнение только препроцессора.

-e

Задает пропуск препроцессора.

-g

Задает генерацию отладочной информации.

-I SEARCH_PATH

Задает каталог для поиска включаемых файлов.

-o PATH, —output PATH

Задает каталог для записи вывода.

—p4runtime-file P4RUNTIME_FILE

Задает вывод описания P4Runtime (API плоскости управления) в указанный файл. Опция устарела и взамен следует использовать —p4runtime-files.

—p4runtime-files P4RUNTIME_FILES

Задает вывод описания P4Runtime (P4Info, API плоскости управления) в указанные через запятые файлы. Формат файлов задают указанные расширения имен (суффиксы) .txt, .json, .bin.

—p4runtime-format {binary,json,text}

Задает выходной формат для описания P4Runtime API (по умолчанию .bin). Опция устарела и взамен следует использовать —p4runtime-files.

—help-pragmas, —pragma-help, —pragmas-help, —help-annotations, —annotation-help, —annotations-help

Выводит документацию поддерживаемых annotation и pragma, после чего завершает работу.

—help-targets, —target-help, —targets-help

Выводит связанные с целевой платформой опции команд.

—disable-annotations DISABLED_ANNOS, —disable-annotation DISABLED_ANNOS …

Задает список разделенных запятыми аннотаций, которые компилятору следует игнорировать.

-S

Задает выполнение лишь этапов предварительной обработки и компиляции.

—std {p4-14,p4_14,p4-16,p4_16}, -x {p4-14,p4_14,p4-16,p4_16}

Указывает вариант языка во входных файлах.

—ndebug

Задает компиляцию программы без отладочной информации.

Николай Малых

nmalykh@protokols.ru

Рубрика: SDN, Сетевое программирование | Комментарии к записи Компилятор P4C (драйвер компиляции) отключены

RFC 8877 Guidelines for Defining Packet Timestamps

Internet Engineering Task Force (IETF)                        T. Mizrahi
Request for Comments: 8877                                        Huawei
Category: Informational                                        J. Fabini
ISSN: 2070-1721                                                  TU Wien
                                                               A. Morton
                                                               AT&T Labs
                                                          September 2020

Guidelines for Defining Packet Timestamps

Рекомендации по определению временных меток в пакетах

PDF

Аннотация

Разные сетевые протоколы применяют метки времени с двоичным кодированием, встраиваемые в формат пакетов протокола и называемые временными метками пакета (packet timestamps). Документ содержит рекомендации по заданию формата меток в протоколах разных уровней, а также представляет 3 рекомендуемых формата. Ожидается, что новые протоколы, которым нужны метки времени будут в большинстве случаев применять один из этих форматов. Если протоколу не подходят эти форматы, в спецификации протокола следует указать формат временных меток пакетов в соответствии с этими рекомендациями.

Статус документа

Документ не относится к категории Internet Standards Track и публикуется лишь для информации.

Документ является результатом работы IETF1 и представляет согласованный взгляд сообщества IETF. Документ прошёл открытое обсуждение и был одобрен для публикации IESG2. Дополнительную информацию о стандартах Internet можно найти в разделе 2 в RFC 7841.

Информацию о текущем статусе документа, ошибках и способах обратной связи можно найти по ссылке https://www.rfc-editor.org/info/rfc8877.

Авторские права

Авторские права (Copyright (c) 2020) принадлежат IETF Trust и лицам, указанным в качестве авторов документа. Все права защищены.

К документу применимы права и ограничения, указанные в BCP 78 и IETF Trust Legal Provisions и относящиеся к документам IETF (http://trustee.ietf.org/license-info), на момент публикации данного документа. Прочтите упомянутые документы внимательно. Фрагменты программного кода, включённые в этот документ, распространяются в соответствии с упрощённой лицензией BSD, как указано в параграфе 4.e документа IETF Trust Legal Provisions, без каких-либо гарантий (как указано в Simplified BSD License).

1. Введение

1.1. Основы

Метки времени широко применяются в сетевых протоколах с разными целями — от журнальных записей и сообщений о событиях до сообщений при измерении задержки и синхронизации часов, а также как часть редко повторяющихся значений (nonce) в протоколах защиты.

Временные метки представляются в RFC в форме текста и меток в пакетах. Текстовые метки времени [RFC3339] представляются понятными человеку строками и широко используются в RFC, например, в информационных объектах и моделях данных [RFC5646], [RFC6991], [RFC7493]. Метки в пакетах представляются компактным двоичным полем фиксированного размера, не предназначенным для чтения человеком. Такие метки также очень распространены в RFC, например, для измерения задержки и синхронизации часов [RFC5905], [RFC4656], [RFC7323].

1.2. Область действия

Этот документ содержит рекомендации по определению формата меток времени в пакетах сетевых протоколов. Представлены 3 рекомендуемых формата меток. Предполагается, что новые сетевые протоколы, которым нужны метки времени, будут использовать один из рекомендуемых форматов. В некоторых случаях протокол может применять не один формат меток. Если рекомендуемые форматы не подходят для протокола, в спецификации протокола следует задавать формат временных меток в пакетах в соответствии с рекомендациями этого документа.

Выбор столь небольшого числа рекомендуемых форматов обусловлен тем, что протоколы обычно могут применять формат протокола сетевого времени (Network Time Protocol или NTP) [RFC5905] при протокола точного времени (Precision Time Protocol или PTP) [IEEE1588], что обеспечивает прямое взаимодействие с таймерами NTP или PTP. Кроме того, механизмы точных временных меток зачастую реализуются аппаратно и новые протоколами с теми же форматами временных меток легко разворачиваются в имеющейся аппаратной инфраструктуре с использованием временных меток.

1.3. Применение документа

Этот документ предназначен служить справочником для разработчиков сетевых протоколов. При определении протокола, применяющего временные метки пакетов, следует сначала рассмотреть рекомендованные форматы (раздел 4). При выборе одного из этих форматов, его следует указать, как это сделано в примерах параграфов 6.1 и 6.2. Если ни один из рекомендованных форматов не подходит, следует задать свой формат на основе шаблона из раздела 3.

2. Терминология

2.1. Уровни требований

Ключевые слова должно (MUST), недопустимо (MUST NOT), требуется (REQUIRED), нужно (SHALL), не следует (SHALL NOT), следует (SHOULD), не нужно (SHOULD NOT), рекомендуется (RECOMMENDED), не рекомендуется (NOT RECOMMENDED), возможно (MAY), необязательно (OPTIONAL) в данном документе интерпретируются в соответствии с BCP 14 [RFC2119] [RFC8174] тогда и только тогда, когда они выделены шрифтом, как показано здесь.

2.2. Сокращения

NTP

Network Time Protocol [RFC5905] — протокол сетевого времени.

PTP

Precision Time Protocol [IEEE1588] — протокол точного времени.

TAI

International Atomic Time — международное время по атомным часам.

UTC

Coordinated Universal Time — универсальное время с учётом координат.

2.3. Используемые термины

Timestamp — метка времени

Значение, представляющее момент (точку) во времени, соответствующий произошедшему или планируемому событию.

Timestamp error — ошибка временной метки

Разность между значениями метки времени и эталонных часов в момент, на который указывает метка времени.

Timestamp format — формат мекти времени

Спецификация метки времени, представленная набором атрибутов, однозначно задающих синтаксис и семантику метки.

Timestamp accuracy — точность взятия метки времени

Среднее значение для совокупности измерений ошибки временной метки (timestamp error).

Timestamp precision — точность метки времени

Вариация по ансамблю измерений ошибки временной метки (timestamp error).

Timestamp resolution — дискретность (разрешение) метки времени

Минимальная единица времени, применяемая для представления метки.

3. Шаблон спецификации временной метки пакета

Этот документ рекомендует применять форматы временных меток, заданные в разделе 4. Если эти метки не подходят для протокола, в спецификации меток следует чётко указать причины задания нового формата. Кроме того, рекомендуется создавать новые форматы меток на основе определенных в этом или ином документе.

Спецификация временной метки должна однозначно определять синтаксис и семантику метки. В этом разделе задан минимальный набор атрибутов, но следует помнить, что во многих случаях может потребоваться определение дополнительных атрибутов или аспектов в спецификации временных меток.

Этот раздел задаёт шаблон для определения временных меток пакетов. Спецификация формата временной метки должна включать по меньшей мере перечисленные ниже аспекты.

Синтаксис временной метки

Размер

Число битов (или октетов), применяемых для представления поля временной метки в пакете. Если метка имеет более одного поля, задаётся формат каждого из полей. По умолчанию предполагается сетевой порядов (big endian) и в ином случае порядок требуется указать явно.

Семантика временной метки

Единицы измерения

Единицы представления метки времени. Если метка имеет более одного поля, задаётся единица для каждого из полей. Если диапазон значений поля ограничен, задаются граничные значения.

Разрешение (дискретность)

Дискретность временной метки совпадает с единицей измерения поля. Если временная метка содержит несколько полей, дискретность определяется наименьшей единицей измерения времени среди полей.

Переход через максимум (Wraparound)

Продолжительность цикла значений меток времени. Здесь следует описать все аспекты, связанные с достижением максимального значения полей.

Эпоха

Начало отсчёта значений меток времени — момент, когда значение времени считается 0. Например, эпоха может быть основана на стандартной шкале времени, такой как UTC. Другим примером являются относительные временные метки, где эпоху определяет время, когда использующие метки времени устройство получает питание и на это время не влияют високосные секунды (см. ниже).

Високосные секунды

Здесь указывается влияние високосных секунд на метки времени. Если метки учитывают високосные секунды, они представляют время с начала эпохи за вычетом високосных секунд с начала эпохи.

Аспекты синхронизации

Предполагается, что спецификация сетевого протокола, применяющего временные метки пакетов, будет включать вопросы синхронизации при использовании временных меток. Хотя аспекты синхронизации не являются частью спецификации формата меток времени, они обеспечивают контекст, требуемый для использования меток в рамках протокола. В некоторых случаях временные метки применяются без синхронизации, например, это может быть число секунд с момента включения. В таких случаях раздел аспектов синхронизации будет указывать, что метки не соответствуют синхронизированным привязкам ко времени, и могут указывать, как это влияет на использование меток. Дополнительное рассмотрение вопросов синхронизации приведено в разделе 5.

4. Рекомендуемые форматы меток времени

Этот документ задаёт набор рекомендуемых форматов временных меток. Очевидно, что разные сетевые протоколы могут иметь свои требования и ограничения, поэтому они могут использовать разные форматы меток. Выбор формата для данного протокола может зависеть от разных факторов. Ниже приведены примеры факторов, влияющих на выбор.

Размер метки времени

Хотя в некоторых протоколах поля меток достаточно велики, в некоторых случаях могут быть ограничения на размер поля метки, влияющие на выбор формата.

Разрешение

Разрешение (дискретность) является другим фактором, влияющим на выбор формата меток времени. В этом контексте может быть важна расширяемость, т. е. возможность расширения формата метки для лучшего разрешения путём увеличения размера поля. Например, разрешение 32-битовых меток NTP может быть улучшено путём перехода на 64-битовый формат NTP простым способом.

Достижение максимума

Продолжительность интервала времени, в течение которого метки уникальны, также может влиять на выбор формата. Этот фактор вместе с разрешением меток определяет число битов, требуемых для записи метки.

Общий формат для нескольких протоколов

Если имеется несколько протоколов, использующих метки времени, которые часто применяются совместно в типовых системах, использование общего формата меток будет предпочтительным. Например, если определяемый сетевой протокол обычно будет применяться на ПК, формат меток времени на основе NTP может упростить интеграцию с таймером, синхронизируемым по протоколу NTP. Если же протокол предназначен для использования в основном на аппаратных платформах, предпочтительней будет формат на основе PTP, обеспечивающий более эффективную интеграцию с таймером, синхронизируемым по протоколу PTP.

4.1. Применение рекомендованного формата меток

В спецификации, использующей один из рекомендованных форматов, следует явно указать, что это рекомендованный формат и привести ссылку на соответствующий параграф этого документа.

4.2. Форматы временных меток NTP

4.2.1. Формат 64-битовых меток NTP

Формат 64-битовых временных меток NTP определён в [RFC5905] и применяется в нескольких сетевых протоколах, включая [RFC6374], [RFC4656], [RFC5357]. Поскольку такие временные метки используются в NTP, их следует выбирать для протоколов, которые обычно развёртываются вместе с NTP.

Представленный в этом параграфе формат соответствует шаблону, заданному в разделе 3.

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                            Seconds                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                            Fraction                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Рисунок 1. Формат 64-битовой метки NTP.


Seconds

Целое число секунд с начала эпохи, размером 32 бита. Единицей измерения является секунда.

Fraction

Дробная часть секунд, прошедших с начала эпохи, размером 32 бита. Единицей измерения является 2-32 сек (около 233 пикосекунд).

Эпоха

Началом эпохи служит полночь 1 января 1900 г. (1 January 1900 at 00:00) по часовому поясу UTC.

Примечание. Как указано в [RFC5905], строго говоря, UTC не было до 1 января 1972 г., но удобно предполагать, что это было всегда. Текущая эпоха предполагает, что временные метки задают число секунд с 00:00 UTC 1 января 1972 г. плюс 2272060800 (число секунд с 1 января 1900 г. до 1 января 1972 г.).

Високосные секунды

Этот формат предполагает учёт високосных секунд. Временная метка представляет число секунд с начала эпохи за вычетом числа високосных секунд. Таким образом, в процессе и, возможно, до и/или после появления високосной секунды значение метки времени может временно быть неоднозначным, как описано в разделе 5.

Разрешение

2-32 секунд.

Цикл метки

Этот формат достигает максимального значения каждые 232 секунд, что составляет около 136 лет. Следующий максимум наступит в 2036 г.

4.2.2. Формат 32-битовых меток NTP

Формат 32-битовых временных меток NTP определён в [RFC5905] и применяется в [METRICS] и [NSHMD]. Этот формат следует выбирать для протоколов, которые обычно развёртываются вместе с NTP. 32-битовый формат можно применять при ограниченном пространстве, не позволяющем разместить 64 бита, или в случаях, когда он подходит по требованиям к дискретности и продолжительности цикла.

Представленный в этом параграфе формат соответствует шаблону, заданному в разделе 3.

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Seconds              |           Fraction            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Рисунок 2. Формат 32-битовой метки NTP.


Seconds

Целое число секунд с начала эпохи, размером 16 битов. Единицей измерения является секунда.

Fraction

Дробная часть секунд, прошедших с начала эпохи, размером 16 битов. Единицей измерения является 2-16 секунд (около 15,3 мксек).

Эпоха

Началом эпохи служит полночь 1 января 1970 г. (1 January 1900 at 00:00) по часовому поясу UTC.

Примечание. Как указано в [RFC5905], строго говоря, UTC не было до 1 января 1972 г., но удобно предполагать, что это было всегда. Текущая эпоха предполагает, что временные метки задают число секунд с 00:00 UTC 1 января 1972 г. плюс 2272060800 (число секунд с 1 января 1970 г. до 1 января 1972 г.).

Високосные секунды

Этот формат предполагает учёт високосных секунд. Временная метка представляет число секунд с начала эпохи за вычетом числа високосных секунд. Таким образом, в процессе и, возможно, до и/или после появления високосной секунды значение метки времени может временно быть неоднозначным, как описано в разделе 5.

Разрешение

2-16 секунд.

Цикл метки

Этот формат достигает максимального значения каждые 216 секунд, что составляет около 18 часов.

4.3. Формат усечённых меток PTP

Протокол PTP [IEEE1588] использует 80-битовый формат меток времени. Усечённый формат использует 64-битовое поле, в которое помещается 64 младших бита 80-битовой метки PTP. Поскольку этот формат похож на применяемый в PTP, его следует выбирать для сетевых протоколов, которые обычно развёртываются в поддерживающих PTP устройствах.

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                            Seconds                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          Nanoseconds                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Рисунок 3. Формат усечённой метки PTP.


Усечённый формат PTP был определён в [IEEE1588v1] и применяется в нескольких протоколах, таких как [RFC6374], [RFC7456], [RFC8186], [ITU-T-Y.1731].

Seconds

Целое число секунд с начала эпохи, размером 32 бита. Единицей измерения является секунда.

Nanoseconds

Дробная часть числа секунд с начала эпохи размером 32 бита. Единицей измерения является наносекунда, поле может принимать значения от 0 до 109-1.

Эпоха

Эпоха PTP [IEEE1588] начинается с полуночи 1 января 1970 г. по часам TAI (1 January 1970 00:00:00 TAI).

Високосные секунды

На этот формат високосные секунды не влияют.

Разрешение

1 наносекунда.

Цикл метки

Этот формат достигает максимального значения каждые 232 секунд, что составляет около 136 лет. Следующий максимум наступит в 2106 г.

5. Аспекты синхронизации

В спецификацию, задающую новый формат метки времени или использующую один из рекомендованных форматов, следует включать раздел Synchronization Aspects (аспекты синхронизации). Отметим, что для рекомендованных этим документом форматах меток времени (раздел 4) этот раздел не включён, но предполагается что в спецификации сетевых протоколов, использующих эти форматы, следует включать раздел аспектов синхронизации. Примеры Synchronization Aspects даны в разделе 6.

В разделе Synchronization Aspects следует указать все допущения и требования, относящиеся к синхронизации. Например, можно указать, должны ли заполняющие метки времени узлы синхронизироваться между собой и измеряется ли метка относительно неких эталонных часов, таких как сервер NTP. Если предполагается синхронизация со стандартным временем, таким как UTC или TAI, это следует указать в разделе. Могут также включаться другие вопросы, такие как требуемая точность временных меток.

Другим аспектом, который следует включить в этот раздел, являются високосные секунды [RFC5905]. Шаблон спецификации временных меток (раздел 3) указывает, влияют ли високосные секунды на временную метку. Зачастую в раздел Synchronization Aspects следует включать дополнительные сведения о влиянии високосных секунд. В общем случае, високосная секунда — это корректировка в 1 секунду, время от времени применяемая к UTC для сохранения соответствия солнечному времени. Високосные секунды могут быть положительными и отрицательными, т. е. часы могут смещаться на 1 секунду вперёд или назад. Все високосные секунды до момента публикации этого документа смещали часы назад, хотя теоретически возможны високосные секунды со сдвигом вперёд. В документе рассматриваются лишь високосные секунды со сдвигом часов назад. В системах хронометража, учитывающих високосные секунды, эти секунды могут влиять на системное время одним из трёх способов.

  • Часы смещаются на одну секунду назад в конце високосной секунды.

  • Часы останавливаются в течение високосной секунды.

  • Часы замедляются в течение високосной секунды и смежных интервалов, пока не будет достигнуто новое точное значение времени. Этот интервал обычно называют leap smear, он может составлять от нескольких секунд до нескольких часов и охватывать время до, в процессе и после високосной секунды.

Способ обработки високосных секунд зависит от протокола синхронизации и поэтому в документе не задаётся. Однако, если формат временной метки определён относительно шкалы, на которую влияют високосные секунды, в раздел Synchronization Aspects следует включать описания влияния високосных секунд на использование метки времени.

6. Примеры использования меток времени

Временные метки пакетов применяются в разных сетевых протоколах. Типовыми приложениями являются измерение задержки, синхронизация часов и др. В таблице 1 представлен (неполный) список протоколов, использующих метки времени в пакетах, и форматы применяемых этими протоколами временных меток.

Таблица 1. Протоколы, применяющие временные метки пакетов.

Рекомендуемые форматы

Прочие

Протокол

NTP 64-бита

NTP 32-бита

PTP с отсечкой

NTP [RFC5905]

+

OWAMP [RFC4656]

+

TWAMP [RFC5357]

+

TWAMP [RFC8186]

+

+

TRILL [RFC7456]

+

MPLS [RFC6374]

+

TCP [RFC7323]

+

RTP [RFC3550]

+

+

IPFIX [RFC7011]

+

BinaryTime [RFC6019]

+

[METRICS]

+

+

[NSHMD]

+

+

Дале в разделе приведены два гипотетических примера спецификаций сетевых протоколов, применяющих один из рекомендованных форматов меток. Примеры включают текст, задающий сведения, относящиеся к формату меток.

6.1. Пример 1

Метка времени

Метки времени в этой спецификации используют 64-битовый формат NTP [RFC5905], как описано в параграфе 4.2.1 RFC 8877.

Аспекты синхронизации

Предполагается, что узлы, на которых работает этот протокол, синхронизированы с UTC на основе механизма, выходящего за рамки этого документа. В типовом развёртывании этот протокол будет работать на машинах, использующих для синхронизации протокол NTP [RFC5905]. Таким образом, метки можно получать от часов с синхронизацией NTP, что позволяет связать метки времени с часами сервера NTP. Поскольку на формат времени в NTP влияют високосные секунды, они будут влиять и на временные метки. Таким образом, значение метки в течение високосной секунды, а возможно до и/или после неё может быть временно неточным.

6.2. Пример 2

Метка времени

Метки времени в этой спецификации используют усечённый формат PTP [IEEE1588], как описано в параграфе 4.3 RFC 8877.

Аспекты синхронизации

Предполагается, что узлы, на которых работает этот протокол, синхронизированы между собой. Узлы также могут быть синхронизированы с глобальным эталоном времени. Отметим, что при использовании для синхронизации протокола PTP [IEEE1588] метки могут извлекаться из синхронизированных с PTP часов, что позволяет связать метки с базовыми часами PTP (grandmaster clock).

7. Поле управления для временной метки

В некоторых случаях желательно иметь поле управления, описывающее структуру, формат, содержимое и свойства метки времени. Управляющие сведения о формате метки времени могут передаваться в некоторых протоколах с использованием выделенного протокола плоскости управления или быть доступными в плоскости администрирования (management), например, с использованием модели YANG. Необязательное поле управления позволяет прикрепить к метке времени часть управляющей информации.

Примером поля управления для временной метки является поле Error Estimate, определённое в параграфе 4.1.2 [RFC4656] и применяемое в протоколах OWAMP [RFC4656] и TWAMP [RFC5357]. Root Dispersion и Root Delay в заголовке NTP [RFC5905] служат примерами полей, содержащих сведения о точности временной метки. Другим примером является поле Correction в заголовке PTP [IEEE1588], значение которого применяется для корректировки метки времени и может задаваться отправителем сообщения PTP и обновляться транзитными узлами (Transparent Clocks) с учётом задержки в пути.

Этот раздел задаёт рекомендации верхнего уровня для определения полей управления временными метками в сетевых протоколах, которые воспользоваться сведениями управления. Слово «требование» в данном контексте применяется неформально.

7.1. Рекомендации для полей управления

Поле управления для меток времени должно предоставлять адекватный набор свойств и удовлетворять ряду требований, чтобы быть приемлемым и применимым. Ниже приведены основные требования верхнего уровня.

  1. Расширяемый набор свойств. Протоколы и приложения зависят от различных характеристик меток времени. Поле управления для метки должно поддерживать переменное число элементов, которые описывают или задают количественно характеристики и параметры меток. Примеры таких элементов включают размер, кодирование, точность, високосные секунды, идентификаторы эталонных часов и т. п.

  2. Размер. Для эффективного использования полей управления важен компромисс между набором свойств и размером поля управления. Протоколы и приложения могут выбирать нужные элементы для своей работы из числа доступных.

  3. Состав. Приложения могут зависеть от конкретных элементов поля управления, присутствующих в сообщениях. Эти элементы могут быть обязательными, условно обязательными или опциональными в зависимости от контекста конкретного приложения. Спецификация поля управления должна поддерживать приложения в части передачи или согласования (a) набора элементов поля управления и (b) статуса каждого элемента (обязательный, условно обязательный, опциональный) путём определения подходящих структур данных и кодов отождествления.

  4. Категория. Элементы поля управления могут характеризовать статические (например, размер метки в байтах и семантику — 64-битовая метка NTP) или рабочие (runtime) параметры (например, оценка точности метки в момент выборки — 20 мксек от UTC) метки времени. В целях эффективности может иметь смысл разделение двух концепций — статические сведения обычно действительны в течение всей сессии и их достаточно передать один раз в момент организации, а динамические данные дополняют любой экземпляр метки и могут вызывать существенные издержки для протоколов с высоким уровнем трафика.

Предложения по полям управления для временных меток выходят за рамки этого документа и будут представлены отдельно.

8. Взаимодействие с IANA

Этот документ не требует действий IANA.

9. Вопросы безопасности

Сетевые протоколы, применяющие метки времени в пакетах, должны указывать соображения безопасности, связанные с использованием временных меток. В этом разделе рассмотрены некоторые базовые вопросы безопасности применения меток времени.

Любые метаданные, прикрепляемые к пакетам управления или данных, в частности, метки времени, могут упростить сетевую разведку — пассивный перехват пакетов с метками времени позволит злоумышленникам собрать сведения о производительности сети и уровне синхронизации между узлами.

В некоторых случаях метки могут быть подделаны или изменены злоумышленниками на пути передачи, что будет атакой на приложения, применяющие метки времени. Например, если метки использует протокол определения задержки, атакующий может изменить метки в пути, влияя на результат измерения. Механизмы защиты целостности, такие как коды проверки подлинности сообщений (Message Authentication Code или MAC) могут смягчить такие атаки. Спецификация механизмов защиты целостности выходит за рамки этого документа и такая защита обычно зависит от применяемого сетевого протокола, а не от формата меток времени.

Другой потенциальной угрозой с похожим эффектом могут быть атаки с задержкой (delay attack). Злоумышленник может намеренно задерживать все или некоторые сообщения в пути с такими же последствиями, которые указаны в предыдущем абзаце. Смягчение таких атак является сложной задачей — в отличие от атак с подделкой или изменением меток времени задержки невозможно предотвратить механизмами криптографической защиты целостности. В некоторых случаях атаки с задержкой можно смягчить путём передачи информации с временными метками по нескольким путям, что позволяет обнаружить злой умысел и противостоять атакующему, размещенному на одном из путей.

В многих случаях метки времени полагаются на базовый механизм синхронизации. Поэтому любые атаки, нарушающие синхронизацию, могут скомпрометировать также протоколы, применяющие метки времени. Атаки на протоколы согласования времени подробно рассмотрены в [RFC7384].

10. Литература

10.1. Нормативные документы

[RFC2119] Bradner, S., «Key words for use in RFCs to Indicate Requirement Levels», BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.

[RFC8174] Leiba, B., «Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words», BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.

10.2. Дополнительная литература

[IEEE1588] IEEE, «IEEE Standard for a Precision Clock Synchronization Protocol for Networked Measurement and Control Systems», DOI 10.1109/IEEESTD.2008.4579760, IEEE Std. 1588-2008, July 2008, <https://doi.org/10.1109/IEEESTD.2008.4579760>.

[IEEE1588v1] IEEE, «IEEE Standard for a Precision Clock Synchronization Protocol for Networked Measurement and Control Systems», DOI 10.1109/IEEESTD.2002.94144, IEEE Std. 1588-2002, October 2002, <https://doi.org/10.1109/IEEESTD.2002.94144>.

[ITU-T-Y.1731] ITU-T, «Operations, administration and maintenance (OAM) functions and mechanisms for Ethernet-based networks», ITU-T Recommendation G.8013/Y.1731, August 2015.

[METRICS] Morton, A., Bagnulo, M., Eardley, P., and K. D’Souza, «Initial Performance Metrics Registry Entries», Work in Progress4, Internet-Draft, draft-ietf-ippm-initial-registry-16, 9 March 2020, <https://tools.ietf.org/html/draft-ietf-ippm-initial-registry-16>.

[NSHMD] Guichard, J., Smith, M., Kumar, S., Majee, S., and T. Mizrahi, «Network Service Header (NSH) MD Type 1: Context Header Allocation (Data Center)», Work in Progress, Internet-Draft, draft-ietf-sfc-nsh-dc-allocation-02, 25 September 2018, <https://tools.ietf.org/html/draft-ietf-sfc-nsh-dc-allocation-02>.

[RFC3339] Klyne, G. and C. Newman, «Date and Time on the Internet: Timestamps», RFC 3339, DOI 10.17487/RFC3339, July 2002, <https://www.rfc-editor.org/info/rfc3339>.

[RFC3550] Schulzrinne, H., Casner, S., Frederick, R., and V. Jacobson, «RTP: A Transport Protocol for Real-Time Applications», STD 64, RFC 3550, DOI 10.17487/RFC3550, July 2003, <https://www.rfc-editor.org/info/rfc3550>.

[RFC4656] Shalunov, S., Teitelbaum, B., Karp, A., Boote, J., and M. Zekauskas, «A One-way Active Measurement Protocol (OWAMP)», RFC 4656, DOI 10.17487/RFC4656, September 2006, <https://www.rfc-editor.org/info/rfc4656>.

[RFC5357] Hedayat, K., Krzanowski, R., Morton, A., Yum, K., and J. Babiarz, «A Two-Way Active Measurement Protocol (TWAMP)», RFC 5357, DOI 10.17487/RFC5357, October 2008, <https://www.rfc-editor.org/info/rfc5357>.

[RFC5646] Phillips, A., Ed. and M. Davis, Ed., «Tags for Identifying Languages», BCP 47, RFC 5646, DOI 10.17487/RFC5646, September 2009, <https://www.rfc-editor.org/info/rfc5646>.

[RFC5905] Mills, D., Martin, J., Ed., Burbank, J., and W. Kasch, «Network Time Protocol Version 4: Protocol and Algorithms Specification», RFC 5905, DOI 10.17487/RFC5905, June 2010, <https://www.rfc-editor.org/info/rfc5905>.

[RFC6019] Housley, R., «BinaryTime: An Alternate Format for Representing Date and Time in ASN.1», RFC 6019, DOI 10.17487/RFC6019, September 2010, <https://www.rfc-editor.org/info/rfc6019>.

[RFC6374] Frost, D. and S. Bryant, «Packet Loss and Delay Measurement for MPLS Networks», RFC 6374, DOI 10.17487/RFC6374, September 2011, <https://www.rfc-editor.org/info/rfc6374>.

[RFC6991] Schoenwaelder, J., Ed., «Common YANG Data Types», RFC 6991, DOI 10.17487/RFC6991, July 2013, <https://www.rfc-editor.org/info/rfc6991>.

[RFC7011] Claise, B., Ed., Trammell, B., Ed., and P. Aitken, «Specification of the IP Flow Information Export (IPFIX) Protocol for the Exchange of Flow Information», STD 77, RFC 7011, DOI 10.17487/RFC7011, September 2013, <https://www.rfc-editor.org/info/rfc7011>.

[RFC7323] Borman, D., Braden, B., Jacobson, V., and R. Scheffenegger, Ed., «TCP Extensions for High Performance», RFC 7323, DOI 10.17487/RFC7323, September 2014, <https://www.rfc-editor.org/info/rfc7323>.

[RFC7384] Mizrahi, T., «Security Requirements of Time Protocols in Packet Switched Networks», RFC 7384, DOI 10.17487/RFC7384, October 2014, <https://www.rfc-editor.org/info/rfc7384>.

[RFC7456] Mizrahi, T., Senevirathne, T., Salam, S., Kumar, D., and D. Eastlake 3rd, «Loss and Delay Measurement in Transparent Interconnection of Lots of Links (TRILL)», RFC 7456, DOI 10.17487/RFC7456, March 2015, <https://www.rfc-editor.org/info/rfc7456>.

[RFC7493] Bray, T., Ed., «The I-JSON Message Format», RFC 7493, DOI 10.17487/RFC7493, March 2015, <https://www.rfc-editor.org/info/rfc7493>.

[RFC8186] Mirsky, G. and I. Meilik, «Support of the IEEE 1588 Timestamp Format in a Two-Way Active Measurement Protocol (TWAMP)», RFC 8186, DOI 10.17487/RFC8186, June 2017, <https://www.rfc-editor.org/info/rfc8186>.

Благодарности

Авторы благодарны Russ Housley, Yaakov Stein, Greg Mirsky, Warner Losh, Rodney Cummings, Miroslav Lichvar, Denis Reilly, Daniel Franke, Éric Vyncke, Ben Kaduk, Ian Swett, Francesca Palombini, Watson Ladd и другим участникам рабочей группы NTP за полезные комментарии. Авторы признательны Harlan Stenn и участникам Network Time Foundation за то, что они делились своими мыслями и идеями.

Адреса авторов

Tal Mizrahi

Huawei

8-2 Matam

Haifa 3190501

Israel

Email: tal.mizrahi.phd@gmail.com

Joachim Fabini

TU Wien

Gusshausstrasse 25/E389

1040 Vienna

Austria

Phone: +43 1 58801 38813

Email: Joachim.Fabini@tuwien.ac.at

URI: http://www.tc.tuwien.ac.at/about-us/staff/joachim-fabini/

Al Morton

AT&T Labs

200 Laurel Avenue South

Middletown, NJ 07748

United States of America

Phone: +1 732 420 1571

Email: acmorton@att.com


Перевод на русский язык

Николай Малых

nmalykh@protokols.ru

1Internet Engineering Task Force — комиссия по решению инженерных задач Internet.

2Internet Engineering Steering Group — комиссия по инженерным разработкам Internet.

4Опубликовано в RFC 8912. Прим. перев.

Рубрика: RFC, Измерения и тестирование | Оставить комментарий

Промежуточные представления компилятора P4C

Оригинал

PDF

Введение

В этом документе рассматривается устройство компилятора P416, описаны разные классы и структуры данных, применяемые для компиляции, а также сам процесс компиляции. Компилятор рассчитан на «традиционную» модель работы, преобразующую программу P4 во внутреннее представление, выполняющую несколько проходов с этим представлением, дающих в результате промежуточное представление (IR1), которое более или менее напрямую отображается в код для целевой платформы. По ходу дела набор классов IR, представленных в коде, может меняться, при этом некоторые конструкции будут присутствовать лишь в начальном представлении после синтаксического анализатора, а другие будут добавляться в процессе компиляции.

Новизна заключается в возможности всегда вернуться к более ранней версии IR (обратное прохождение) или использовать множество потоков при работе с IR, что позволяет реализовать разные стратегии при поиске окончательного результата. Поддержка ветвления и возврата является центральным моментом IR и преобразования. Задачей IR является представление программы в виде дерева или DAG2 с одним корневым узлом и остальными узлами, указывающими лишь своих потомков. Преобразования выполняются путем создания новых узлов IR, являющихся «мелкими» копиями имеющихся узлов, и последующего создания для них новых родительских узлов вплоть до корня IR. Внутреннее представления реализовано на C++ в форме одной иерархии наследования объектов с основным базовым узлом типа Node, используемым всеми субклассами. Этот базовый класс Node поддерживает функциональность, требуемую для преобразований — клонирование, проверку эквивалентности, диспетчеризацию субклассов.

Представление IR должно быть расширяемым, что позволяет добавлять новые классы в иерархию по мере добавления поддержки новых платформ. Исходное представление IR создаваемое синтаксическим анализатором, содержит конструкции, относящиеся лишь к исходному коду P4, и дле него будут выполняться преобразования frontend-компилятором для перевода в более простую и каноническую форму.

IR может быть деревом или DAG, но разрешение ссылок «вперед-назад» (back/up) в IR (циклах) сводит на нет большинство преимуществ неизменного IR. Если циклы (например, узлы-листья для имен напрямую ссылающиеся на именованный объект) разрешены, практически любое преобразование будет приводить к клонированию всего графа IR, поскольку изменение листа включает клонирование содержащего его объекта, а затем (рекурсивно) любого объекта, указывающего на этот объект.

Проходы Visitor и Transform

Компилятор организован в форме серии проходов Visitor и Transform. Базовые классы Visitor и Transform упрощают определение новых проходов и новому преобразованию требуется лишь указать нужные типы IR, а прочие можно игнорировать. Проход Visitor (постоянный) посещает каждый узел в дереве IR м собирает информацию о дереве, не меняя ее, а проход Transform посещает каждый узел и может менять его или заменять иным узлом.

    /* псевдокод базового преобразования */
    visit(node, context=ROOT, visited={}) {
        if (node in visited) {
                if (visited[node] == INVALID)
                        throw loop_detected
                if (VisitDagOnce)
                        return visited[node] }
        visited[node] = INVALID
        copy = shallow_clone(node)
        if (VisitDagOnce)
                forward_children(copy, visited)
        copy = preorder(copy, context) // переопределен в субклассе Transform
        visit_children(copy, {copy, context}, visited)
        copy = postorder(copy, context) // переопределен в субклассе Transform
        if (*copy == *node) {
                visited[node] = node
                return node
        } else {
                visited[node] = copy
                return copy }
    }
    forward_children(node, visited) {
        for (child in children(node))
                if (child in visited)
                        child = visited[child]
    }
    visit_children(node, context, visited) {
        for (child in children(node))
                child = visit(child, context, visited)
    }

В проходе Transform разрешено свободно изменять или полностью заменять узел в своих программах и будет автоматически перестраивать дерево должным образом. Подпрограммы преобразования не могут менять другие узлы в дереве, однако они могут обращаться к ним и ссылаться на них. Эти программы могут быть перезагружены, чтобы автоматически различать разные субклассы IR. Любому данному посетителю узла нужно лишь переопределить интересующие программы для нужных типов IR.

Имеется несколько субклассов Visitor, описывающих разные типы посетителей.

Субкласс

Описание

Inspector

Простой посетитель, собирающий информацию, но не меняющий узлы.

Modifier

Простой посетитель, которые не меняет структуру дерева, но может менять узлы.

Transform

Полное преобразование узла

PassManager

Комбинация нескольких типов, выполняемых последовательно.

Имеется также два интерфейса, которые могут реализовать субклассы Visitor для изменения режима посещения узлов.

Интерфейс

Описание

ControlFlowVisitor

Посещение узлов в порядке потока управления, расщепление (клонирование) посетителя по условию и слияние клонов в точке соединения.

Backtrack

Посетитель уведомляется, когда происходит возврат, и может сохранить свое состояние как точку возврата.

ControlFlowVisitor

Для поддержки анализа потока управления имеется специальный интерфейс ControlFlowVisitor,
который
понимают определенные субклассы
IR, инкапсулирующие поток управления, и применяют для управления порядком посещения узлов-потомков и состоянием посетителя, передаваемым его подпрограммам preorder/postorder для анализа потока управления.

Базовая идея заключается в том, что узлы потока управления в IR при посещении своих потомков создают клоны объектов ControlFlowVisitor для посещения различных потомков после ветвления и вызывают функцию flow_merge в точке соединения для слияния состояний посетителя. Базовый класс Visitor и интерфейс ControlFlowVisitor определяют две виртуальные функции — flow_clone и flow_merge решающие эти задачи. В базовом классе Visitor (используется всеми посетителями, не наследующими из интерфейса ControlFlowVisitor), где функции реализованы в виде no-ops, функция flow_clone просто возвращает тот же объект, а flow_merge не делает ничего. В интерфейсе ControlFlowVisitor функция flow_clone вызывает функцию clone (которая должна быть реализована классом leaf), а flow_merge является абстрактной виртуальной функцией, которая должна быть реализована в классе leaf.

В качестве примера использования в программах посетителя IR рассмотрим класс IR::If, имеющих 3 дочерних элемента для посещения — утверждение (predicate) и два следствия (consequent), причем значение утверждения определяет, какое из следствий выполняется. Дочерний посетитель для IR::If имеет вид

    visitor.visit(predicate);
    clone = visitor.flow_clone();
    visitor.visit(ifTrue);
    clone.visit(ifFalse);
    visitor.flow_merge(clone);

Таким образом оба следствия посещаются с состоянием посетителя, соответствующим потоку управления, сразу после оценки утверждения, а затем состояния потока управления после обоих посещений объединяются.

Для использования этой функциональности субклассу Visitor нужно лишь наследовать от интерфейса ControlFlowVisitor и реализовать функции clone и flow_merge. Другим классам Visitor не нужно делать ничего, но они могут реализовать clone по иным причинам.

Этот анализ потока управления работает лишь с потоком управления «ветвление-слияние» и не подходит для циклов (которые обычно требуют итерации процесса до фиксированной точки). Поскольку IR (в настоящее время) не поддерживает циклы, это не создает проблем. Язык P4 не разрешает включать циклы в таблицы «сопоставление-действие» внутри конвейера и это работает достаточно хорошо, но может быть недостаточно для синтаксических анализаторов, которые могут поддерживать циклы.

Поток в целом

Поток компиляции можно грубо разделить на три части — frontend, mid-end и backend. Интерфейсная часть (frontend) выполняет в основном не связанные с целевой платформой преобразования, предназначенные для очистки IR и приведения в каноническую форму. Средняя часть (middleend) выполняет зависящие от целевой платформы преобразования IR. Последняя часть (backend) принимает решения о выделении ресурсов. Поскольку этот этап может завершаться отказом по причине имеющихся ограничений, может потребоваться возврат к mid-end для проверки иной конфигурации. Возврата к начальному этапу (frontend) не предполагается. Разделение на этапы Front/Middle/Back достаточно произвольно и некоторые проходы могут перемещаться между этапами, если в этом будет смысл.

Frontend

Интерфейсная часть (frontend) анализирует исходный код и преобразует его в представление IR, напрямую соответствующее исходному коду. Затем выполняется серия проходов, таких как проверка типов (изменение узлов для указания выведенных типов), раскрытие констант, устранение неиспользуемого кода и т. п. Дерево первоначального IR соответствует исходному коду, а затем выполняются различные (в основном независимые от целевой платформы) преобразования. Сложная структура и сложные области действия в коде здесь преобразуются в «плоское» представление.

Mid-end

На этапе mid-end выполняются преобразования, в той или иной степени зависящие от целевой платформы, но не связанные с выделением конкретных ресурсов. В какой-то части этих преобразований представление IR преобразуется в фрому, соответствующую возможностям целевой платформы.

Управление проходами

Для управления возвратами и порядком проходов и преобразования служит объект PassManager, который инкапсулирует последовательность проходов и выполняет их по порядку. Проходы, поддерживающие интерфейс Backtrack, являются точками возврата.

Базовый объект PassManager может управлять динамически создаваемыми последовательностями проходов. Он отслеживает проходы, реализующие Backtrack, и при обнаружении отказа на последующем этапе возвращается к проходу Backtrack.

    interface Backtrack {        bool backtrack(trigger);     }

Метод backtrack вызывается при возникновении необходимости возврата и возвращает значение true, если в повторном проходе можно проверить применимый вариант. В противном случае (вариантов нет) возвращается false для поиска иной точки возврата.

Использование исключений

Исключения применимы при ошибках, о которых можно сообщить пользователю, проблемах с ограничениями, требующих возврата, и внутренних проблемах компилятора. Для этого создано множество классов исключений.

  • Backtrack::trigger служит для управления возвратом и может включать субклассы для добавления информации.

  • CompilerBug используется при внутренних ошибках компилятора. Взамен непосредственного вызова служит макрос BUG().

Классы IR

Чтобы упростить управление и расширений для классов IR, они определяются файлах .def, которые обрабатываются программой ir-generator, создающей код IR C++. По сути, файлы .def являются просто определениями классов C++ с удаленным шаблоном, который вставляет программа ir-generator, расщепляя также определение каждого класса на файлы .h и .cpp по мере необходимости.

Основная часть «нормального» IR определяется в каталоге ir, при этом некоторые frontend и backend используют свои расширения имен для IR, определяемых в их подкаталогах внутри ir. Программа ir-generator считывает все файлы .def в дереве и создает по одному файлу .h и .cpp, которые содержат весь код определения класса IR.

Все ссылки на объекты класса IR из объектов других классов IR должны быть указателями const, чтобы поддерживать инварианты, открытые лишь для записи (write-only). Когда это слишком обременительно, можно встраивать классы IR непосредственно в другие объекты IR, вместо использования ссылок. При таком встраивании объектов IR они становятся доступными для изменения посетителями Modifier
и
Transform при посещении содержащего объект узла, а сами объекты непосредственно посещаться не будут.

Программа ir-generator понимает оба эти механизма. Любое поле, объявленное в классе IR с другим классом IR в качестве типа, будет преобразовано в указатель const, если у него нет встроенного модификатора (в данном случае это будет встроенный напрямую субобъект).

Программа ir-generator понимает множество «стандартных» методов для классов IR — visit_children, operator==, dbprint, toString, apply. Эти методы можно объявлять без аргументов и типов возврата (т. е. просто указывать имя метода с блоком кода в фигурных скобках — {}), при этом будет синтезировано стандартное объявление. Если метод не объявлен в файле .def, создается стандартное определение (наоснове объявленных в классе полей). Таким способом большинство классов могут избежать включения этого шаблонного кода в файл .def.

IR::Node

Это основной абстрактный класс для всех узлов IR, содержащий лишь небольшое количество данных для информирования об ошибках и отладки. В общем случае эти данные никогда не сравниваются на предмет равенства (субклассам никогда не следует вызывать Node::operator==`), поскольку узлы (Node), различающиеся лишь этой информацией следует считать эквивалентными и не требовать для них клонирования или дерева IR.

IR::Vector<T>

Этот шаблонный класс содержит вектор указателей (const) на узлы конкретного субкласса IR::Node.

Перевод на русский язык

Николай Малых

nmalykh@protokols.ru

1Intermediate representation.

2Directed acyclic graph — ориентированный ациклический граф (орграф), в котором отсутствуют направленные циклы, но могут быть «параллельные» пути, выходящие из одного узла и разными путями приходящие в конечный узел.

Рубрика: SDN, Сетевое программирование | Комментарии к записи Промежуточные представления компилятора P4C отключены

RFC 8875 Working Group GitHub Administration

Internet Engineering Task Force (IETF)                         A. Cooper
Request for Comments: 8875                                         Cisco
Category: Informational                                       P. Hoffman
ISSN: 2070-1721                                                    ICANN
                                                             August 2020

Working Group GitHub Administration

Администрирование рабочих групп GitHub

PDF

Аннотация

Использование GitHub в рабочих группах (WG) IETF возрастает. Этот документ описывает использование и соглашения для рабочих групп, начинающих пользоваться GitHub. Документ не задает обязательных процессов и не требует изменения работы существующих и будущих групп, которые не пользуются GitHub.

Статус документа

Документ не содержит какой-либо спецификации (Internet Standards Track) и публикуется с информационными целями.

Документ является результатом работы IETF1 и представляет согласованный взгляд сообщества IETF. Документ прошел открытое обсуждение и был одобрен для публикации IESG2. Дополнительную информацию о стандартах Internet можно найти в разделе 2 в RFC 7841.

Информация о текущем статусе документа, найденных ошибках и способах обратной связи доступна по ссылке https://www.rfc-editor.org/info/rfc8875.

Авторские права

Copyright (c) 2020. Авторские права принадлежат IETF Trust и лицам, указанным в качестве авторов документа. Все права защищены.

К этому документу применимы права и ограничения, перечисленные в BCP 78 и IETF Trust Legal Provisions и относящиеся к документам IETF (http://trustee.ietf.org/license-info), на момент публикации данного документа. Прочтите упомянутые документы внимательно. Фрагменты программного кода, включённые в этот документ, распространяются в соответствии с упрощённой лицензией BSD, как указано в параграфе 4.e документа IETF Trust Legal Provisions, без каких-либо гарантий (как указано в Simplified BSD License).

1. Введение

Многие рабочие группы и участники процессов IETF используют GitHub разными способами в своей работе над документами IETF. Некоторые люди заинтересованы в использовании GitHub их рабочими группами, но не знают, с чего начать или каким соглашениям следовать. Некоторые рабочие группы используют или планируют применять другие репозитории кода, такие как GitLab и Bitbucket, свойства которых отличаются от GitHub.

В этом документе задан набор административных процессов и соглашений для рабочих групп IETF, которые следует применять при выборе GitHub для упрощения своей работы. Эти спецификации не предназначены для рабочих групп и отдельных лиц, которые уже применяют GitHub для работы IETF. Практика таких групп различается и некоторые из них не соответствуют предлагаемым здесь соглашениям (это нормально). Цель спецификации не состоит в формировании единообразной практики и основной задачей является помощь рабочим группам, начинающим использовать GitHub, описав проверенные подходы способы, которые можно перенять при желании.

2. Административный процесс и соглашения

В этом разделе описан административный процесс и соглашения для поддержки создания и управления организацией в GitHub для рабочих групп и хранилищ отдельных документов единообразным способом. Эти действия можно выполнить вручную через секретариат IETF или автоматизированным путем. Примеры автоматизированных решений доступны по ссылкам https://github.com/richsalz/ietf-gh-scripts и https://github.com/martinthomson/i-d-template.

В этом документе вопрос выбора ручного или автоматизированного процесса намеренно не рассматривается, поскольку эти детали будут рассмотрены в IETF Secretariat и Tools Team.

Большинство приведенных ниже соглашений взяты из [RFC8874].

2.1. Создание организации в GitHub

Этот документ указывает, что интерфейс IETF Datatracker позволяет руководителям направлений (area director — AD) или рабочих групп запрашивать создание в GitHub организации для конкретной рабочей группы. В идеале эта возможность должна присутствовать как часть пользовательского интерфейса (UI) создания рабочей группы и ее страницы.

При запросе руководителем направления или рабочей группы создания организации в GitHub инициируется описанный ниже процесс.

  1. Для рабочей группы создается организация GitHub.

  2. Имя организации имеет формат ietf-wg-<wgname>…

  3. Организация инициализируется путем указания IETF Secretariat и руководителей направления в качестве владельцев области данной группы. Если ответственный AD для рабочей группы работает в другом направлении (area), этот AD также будет владельцем.

  4. Организация инициализируется с указанием администраторов, в число которых входят руководитель и секретарь рабочей группы, если они есть.

После создания организации ее URL добавляется на страницу рабочей группы в Datatracker.

Этапы 3 и 4 подразумевают, что идентификаторы владельцев и администраторов известны GitHub. Запись идентификаторов GitHub в Datatracker (см. https://trac.tools.ietf.org/tools/ietfdb/ticket/2548) будет облегчать это. Лицо, запрашивающее создание организации о недоступности идентификаторов GitHub для любого из указанных в качестве владельцев и администраторов.

2.2. Перенос существующих организаций

Если рабочая группа уже имеет организацию, для нее будет полезно указание такого же персонала управления, как описано в параграфе 2.1. Т. е. будет полезно иметь возможность выполнить этапы 3 и 4 из параграфа 2.1, чтобы описанные ниже действия, такие как смена персонала, выполнялись так же, как для созданной заново организации.

2.3. Изменение персонала

При смене персонала для направления или рабочей группы эти изменения будут отражаться в организации GitHub. В Datatracker следует обеспечить возможность отслеживания смены персонала.

2.4. Закрытие рабочей группы

При закрытии рабочей группы команда с административным доступом удаляется, а список владельцев возвращается в секретариат и текущим AD (на момент закрытия). Сводные данные об организации и репозитории внутри организации обновляются для указания того, что работа завершена. Позже список владельцев может быть ограничен секретариатом или включать других лиц по выбору секретариата или IESG.

2.5. Создание репозитория документов

Имеется много вариантов и конфигураций, в которых может быть полезно использование автоматизации или административных соглашения для репозиториев в рамках организации WG:

  • создание нового репозитория для отдельного чернового документа (по решению руководителя WG);

  • создание нового репозитория для уже принятого рабочей группой проекта (draft);

  • перенос имеющегося репозиория документов в организацию WG;

  • создание нового репозитория с множеством черновых документов.

В качестве этапа процесса этот документ указывает наличие в интерфейсе Datatracker инструмента, позволяющего администратору организации ietf-wg-<wgname> запросить создание нового репозитория для одного документа внутри этой организации. Авторы этого документа будут указаны как участники работы, а репозиторий получит имя чернового документа. В идеале репозиторий должен быть настроен с шаблоном чернового документа, файлами CONTRIBUTING, LICENSE и README, а также с поддержкой непрерывной интеграции в стиле https://github.com/martinthomson/i-d-template. Выполнение этого этапа будет автоматически информировать IETF Secretariat о необходимости создать резерную копию репозитория в соответствии с параграфом 3.2.

2.6. Список связанных репозиториев

В IETF Datatracker следует позволять пользователя добавлять ссылки на репозитории (GitHub и др.) для рабочих групп, документов и страниц пользователей. В данный момент эта функция находится стадии разработки.

3. Рабочий процесс

В [RFC8874] рассмотрены разные варианты использования GitHub рабочими группами и множество решений для реализации этого. В этом разделе указаны базовые административные правила для рабочих групп и описана административная поддержка этих правил.

3.1. Участники работы

Каждый репозиторий, созданный в организации WG, должен как минимум включать в свой файл CONTRIBUTING шаблонный текст https://trustee.ietf.org/license-for-open-source-repositories.html из файла лицензии IETF для репозиториев с открытым кодом. Этот файл может включать и другую информацию (см., например, https://github.com/ietf/repo-files/tree/master/contributing-samples).

Будет полезно указывать в пользовательских данных Datatracker как минимум список учетные данные пользователей GitHub, чтобы упростить отслеживание их вклада в работу.

Некоторые рабочие группы предпочитают иметь в репозитории несколько черновых вариантов, особенно для документов, связанных перекрестными ссылками. В таких случаях в файле README этого репозитория следует четко указывать это, чтобы читатель знал о возможности одновременного изменения нескольких черновых документов.

3.2. Резервные копии и архивы содержимого GitHub

Списки рассылок рабочих групп IETF автоматически копируются секретариатом IETF и архивы доступны для всех. Все официальные взаимодействия в WG должны архивироваться.

Информация рабочей группы в GitHub также должна архивироваться с обеспечением публичного доступа к архивам. Этот документ задает использование для решения этих задач протокола Git [git-protocol].

Репозиторий каждой рабочей группы IETF в GitHub будет иметь одноименный репозиторий (зеркало) на сервере, поддерживаемом секретариатом IETF. Каждый час служба будет использовать команду git fetch для всех отслеживаемых репозиториев GitHub. Зеркало репозитория будет обеспечивать доступ для всех.

Отметим, что эта система не делает резервных копий и запросов на вытягивание (pull). Резервные копии следует делать и GitHub API позволяет это. Секретариату IETF следует делать резервные копии зеркала одновременно с созданием резерных копий репозиториев GitHub.

Шаги, описанные в параграфе 2.5, информируют IETF Secretariat о репозиториях, для которых следует создать резервные копии. Руководителям рабочих групп и направлений следует также иметь возможность запрашивать у секретариата IETF резервное копирование других репозиториев и связанных рабочих IETF.

4. Вопросы безопасности

Злоумышленник может изменить содержимое Internet-Drafts, особенно на финальных этапах работы и внести незаметные изменения в протоколы, которые могут быть в конечном итоге приняты.

Существует риск потери данных из-за их размещения в одном сервисе. Это учитывается и может быть смягчено мерами, описанными в параграфе 3.2.

5. Взаимодействие с IANA

Этот документ не требует действий IANA.

6. Литература

[git-protocol] Chacon, S. and B. Straub, «Git on the Server — The Protocols», in Pro Git, 2014, <https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols#The-Git-Protocol>.

[RFC8874] Thomson, M. and B. Stark, «Working Group GitHub Usage Guidance», RFC 8874, DOI 10.17487/RFC8874, August 2020, <https://www.rfc-editor.org/info/rfc8874>.

Адреса авторов

Alissa Cooper

Cisco

Email: alcoop@cisco.com

Paul Hoffman

ICANN

Email: paul.hoffman@icann.org

Перевод на русский язык

Николай Малых

nmalykh@protokols.ru

1Internet Engineering Task Force.

2Internet Engineering Steering Group.

Рубрика: RFC | Комментарии к записи RFC 8875 Working Group GitHub Administration отключены