Message ID | 1407173625-11764-1-git-send-email-bill.fischofer@linaro.org |
---|---|
State | New |
Headers | show |
How long are strings in that document? Maybe to fit them to 80 characters to better read in terminal? Maxim. On 08/04/2014 09:33 PM, Bill Fischofer wrote: > Switch to use of @verbatim/@endverbatim to avoid formatting issues with different levels of doxygen. > > Signed-off-by: Bill Fischofer <bill.fischofer@linaro.org> > --- > classification_design.dox | 879 +++++++++++++++++++++++++++++++++++++++++ > images/classification_flow.png | Bin 0 -> 35193 bytes > 2 files changed, 879 insertions(+) > create mode 100644 classification_design.dox > create mode 100644 images/classification_flow.png > > diff --git a/classification_design.dox b/classification_design.dox > new file mode 100644 > index 0000000..0cb7134 > --- /dev/null > +++ b/classification_design.dox > @@ -0,0 +1,879 @@ > +/* Copyright (c) 2014, Linaro Limited > + * All rights reserved > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +/*! > +@page classification_design ODP Design - Classification API > +For the implementation of the ODP classification API please see @ref odp_classify.h > + > +@tableofcontents > + > +@section introduction Introduction > +This document defines the Classification APIs supported by ODP v1.0. > +Classification is logically composed of two stages: Parsing and Rule Matching. > +Parsing takes a raw packet and validates its structure and identifies fields of interest in the various headers that comprise the layers of the packet. > +Rule Matching, in turn, takes the result of parsing and sorts packets into Classes of Service (CoS) based on application-defined rule sets. > +@subsection use_of_terms Use of Terms > +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc21199). > +@subsection purpose Purpose > +ODP is a framework for software-based packet forwarding/filtering applications, and the purpose of the Packet Classifier API is to enable applications to program the platform hardware or software implementation to assist in prioritization, classification and scheduling of each packet, so that the software application can run faster, scale better and adhere to QoS requirements. > +The following API abstraction are not modelled after any existing product implementation, but is instead defined in terms of what a typical data-plane application may require from such a platform, without sacrificing simplicity and avoiding ambiguity. Certain terms that are being used within the context of existing products in relation to packet parsing and classification, such as “access lists” are avoided such that not to suggest any relationship between the abstraction used within this API and any particular manner in which they may be implemented in hardware. > +These are the key ODP objects that the parser needs to employ, that are presently defined in ODP: > +@subsubsection odp_pktio odp_pktio > +odp_pktio specifies an individual packet I/O channel instance. In other words, it would translate to a physical interface or a logical port, or in the case of channelized protocols (e.g., [Interlaken](https://www.google.com/url?q=https%3A%2F%2Fwww.cortina-systems.com%2Fimages%2Fdocuments%2F400023_Interlaken_Technology_White_Paper.pdf&sa=D&sntz=1&usg=AFQjCNEBdJTBmA1XaNGY3pmumQTfgSi1oA)) it would map to a logical channel on that interface. > +Since the classifier API deals exclusively with ingress, this object represents the source of packets into the classifier. In order to support any non-trivial use case, the classifier API needs to be able to assign multiple odp_queue instances for any single odp_pktio object, and may also assign any odp_queue instance to more than one odp_pktio object. > +@subsubsection odp_queue odp_queue > +odp_queue specifies a logical queue for packets, and in the case of ingress, this would represent a stream of packets which share several attributes, that are delivered to the ODP application for processing. The per-queue attributes currently defined are: queue type, sync (ordering); priority; and schedule group (set of processor cores). > +@subsubsection odp_buffer_pool odp_buffer_pool > +odp_buffer_pool specifies a collection of buffers of same size and alignment, as well as a set of policies such as flow control and processor affinity. The classifier API refers to such pools that are designated for storing ingress packets. > +@section functional_description Functional Description > +Following is the functionality that is required of the classifier API, and its underlying implementation. The details and order of the following paragraph is informative, and is only intended to help convey the functional scope of a classifier and provide context for the API. In reality, implementations may execute many of these steps concurrently, or in different order while maintaining the evident dependencies: > + > +-# Apply a set of \e classification \e rules to the header of an incoming packet, identify the header fields, e.g., \e ethertype, IP version, IP protocol, transport layer port numbers, IP DiffServ, VLAN id, 802.1p priority. > + > +-# Store these fields as packet meta data for application use, and for the remainder of parser operations. The \e odp_pktio is also stored as one of the meta data fields for subsequent use. > + > +-# Compute an \e odp_cos (Class of Service) value from a subset of supported fields from 1) above. > + > + > +-# Based on the \e odp_cos from 3) above, select the \e odp_queue through which the packet is delivered to the application. > + > +-# Validate the packet data integrity (checksums, FCS) and correctness (e.g., length fields) and store the validation result, along with optional error layer and type indicator, in packet meta data. Optionally, if a packet fails validation, override the \e odp_cos selection in step 3 to a class of service designated for errored packets. > + > +-# Since the selected \e odp_queue may require preservation of packet order, i.e., SYNC_ATOMIC or SYNC_ORDERED, optionally select the packet header fields from which the parser calculates a \e odp_flow_signature, which may be a unique flow identifier or a hash, such that the packets which are assigned the same \e odp_flow_signature are scheduled in the same order they are received. > + > +-# Based on the \e odp_cos from 3) above, select the \e odp_buffer_pool that should be used to acquire a buffer to store the packet data and meta data. > + > +-# Allocate a buffer from \e odp_buffer_pool selected in 6) above and logically store the packet data and meta data to the allocated buffer, or in accordance with class-of-service drop policy and subject to pool buffer availability, optionally discard the packet. > + > +-# Enqueue the buffer into the \e odp_queue selected in 4) above. > + > +The above is an abstract description of the classifier functionality, and may be applied to a variety of applications in many different ways. > +The ultimate meaning of how this functionality applies to an application also depends on other ODP modules, so the above may not complete a full depiction. > +For instance, the exact meaning of \e priority, which is a per-queue attribute is influenced by the ODP scheduler semantics, and the system behavior under stress depends on the ODP buffer pool module behavior. > + > +For the sole purpose of illustrating the above abstract functionality, here is an example of a Layer-2 (IEEE 802.1D) bridge application: > +Such a forwarding application that also adheres to IEEE 802.1p/q priority, which has 8 traffic priority levels, might create 8 \e odp_buffer_pool instances, one for each PCP priority level, and 8 \e odp_queue instances one per priority level. > +Incoming packets will be inspected for a VLAN header; the PCP field will be extracted, and used to select both the pool and the queue. > +Because each queue will be assigned a priority value, the packets with highest PCP values will be scheduled before any packet with a lower PCP value. > +Also, in a case of congestion, buffer pools for lower priority packets will be depleted earlier than the pools containing packets of the high priority, and hence the lower priority packets will be dropped (assuming that is the only flow control method that is supported in the platform) while higher priority packets will continue to be received into buffers and processed. > +@subsection flow_diagram Classification Processing Flow Diagram > +@image html classification_flow.png "Figure 1: Classification Flow Diagram" > +@image latex classification_flow.eps "Figure 1: Classification Flow Diagram" > + > +@section api_elements API Elements > +While the above description refers to the abstracted packet classifier, the following is the description of the API designed to program the packet classifier, and is intended to add clarity to the functions provided further below. > +@subsection cos_creation Class of Service Creation and Binding > +To program the classifier, a class-of-service instance must be created, which will contain the packet filtering resources that it may require. > +All subsequent calls refer to one or more of these resources. > +Each class of service instance must be associated with a single queue or queue group, which will be the destination of all packets matching that particular filter. > +The queue assignment is implemented as a separate function call such that the queue may be modified at any time, without tearing down the filters that define the class of service. In other words, it is possible to change the destination queue for a class of service defined by its filters quickly and dynamically. > +Optionally, on platforms that support multiple packet buffer pools, each class of service may be assigned a different pool such that when buffers are exhausted for one class of service, other classes are not negatively impacted and continue to be processed. > + > +@subsection default_packet_handling Default packet handling > +There SHOULD be one \b odp_cos assigned to each port with the \c odp_cos_pktio_set() function, which will function as the default class-of-service for all packets received from an ingress port, that do not match any of the filters defined subsequently. At minimum this default class-of-service MUST have a queue and a buffer pool assigned to it on platforms that support multiple packet buffer pools. Multiple odp_pktio instances (i.e., multiple ports) MAY each have their own default odp_cos, or MAY share a odp_cos with other ports, based on application requirements. > + > +@subsection packet_classification Packet Classification > +For each odp_pktio port, the API allows the assignment of a class-of-service to a packet using one of three methods: > + > +-# The packet may be assigned a specific class-of-service based on its Layer-2 (802.1P/902.1Q VLAN tag) priority field. Since the standard field defines 8 discrete priority levels, the API allows to assign an odp_cos to each of these priority levels with the \c odp_cos_with_l2_priority() function. > + > +-# Similarly, a class-of-service may be assigned using the Layer-3 (IP DiffServ) header field. The application supplies an array of \e odp_cos values that covers the entire range of the standard protocol header field, where array elements do not need to contain unique values. There is also a need to specify if Layer-3 priority takes precedence over Layer-2 priority in a packet with both headers present. > + > +-# Additionally, the application may also program a number of \e pattern \e matching \e rules that assign a class-of-service for packets with header fields matching specified values. The field-matching rules take precedence over the previously described priority-based assignment of a class-of-service. Using these matching rules the application should be able for example to identify all packets containing VoIP traffic based on the protocol being UDP, and a specific destination or source port numbers, and appropriately assign these packets an class-of-service that maps to a higher priority queue, assuring voice packets a lower and bound latency. > + > +@subsection scaling_and_flow Scaling and Flow Discrimination > +In addition to classifying packets and routing them to those queues with the appropriate priority, and optionally limiting their memory consumption by designating certain classes of packets to specific buffer pools, the classifier API also facilitates the scaling of data-plane application on multi-core systems by creating a mechanism to define which packet headers need to be combined to result in a value representing a specific packet flow. The classifier generates a signature, which can be a checksum or hash of arbitrary strength that covers those packet header fields that are identified by the application as identifying flows. > + > +The \e flow \e signatures that result from hashing are then stored with the packet meta data (along with its class-of-service and its ingress \e odp_pktio port), and subsequently may be utilized by the implementation of a scheduler queue to maintain the order of packets with the same flow signature, while allowing packets with different signatures to be processed concurrently and independently on different processing cores. > + > +@subsection packet_meta_data Packet meta data Elements > +Here are the specific information elements that SHOULD be stored within the packet meta data structure: > +- Protocol fields that are decoded and extracted by the parsing phase > +- Flow-signature calculated from a prescribed collection of protocol fields > +- The class-of-service identifier that is selected for the packet > +- The ingress port identifier > +- The result of packet validation, including an indication of the type of error detected, if any > + > +The ODP packet API module SHALL provide accessors for retrieving the above meta data fields from the container buffer in an implementation-independent manner. > + > +@section api_definitions API Definitions > +@subsection data_types Data Types > +The following data types are referenced in the API descriptions described below. > +The names are part of the ODP API and MUST be present in any conforming implementation, however the type values shown here are illustrative and implementations SHOULD either use these or substitute their own type values that are appropriate to the underlying platform. > + > +@verbatim > +/** > + * 'odp_pktio_t' value to indicate any port > + */ > +#define ODP_PKTIO_ANY ((odp_pktio_t)~0) > + > + > +/** > + * 'odp_pktio_t' value to indicate an error > + */ > +#define ODP_PKTIO_INVALID ((odp_pktio_t)0) > + > + > +/** > + * Class of service instance type > + */ > +typedef uint32_t odp_cos_t; > + > + > +/** > + * flow signature type, only used for packet meta data field. > + */ > +typedef uint32_t odp_flowsig_t; > + > + > +/** > + * This value is returned from odp_cos_create() on failure, > + * May also be used as a “sink” class of service that > + * results in packets being discarded. > + */ > +#define ODP_COS_INVALID ((odp_cos_t)~0) > +@endverbatim > + > +@subsection cos_routines Class of Service Routines > +Conforming ODP implementations MUST provide the following Classification APIs: > +@subsubsection cos_create odp_cos_create > +@verbatim > +/** > + * Create a class-of-service > + * > + * @param name is a string intended for debugging purposes. > + * > + * @return Class of service instance identifier, > + * or ODP_COS_INVALID on error. > + */ > + > +odp_cos_t odp_cos_create(const char *name); > +@endverbatim > + > +This routine is used to create a class of service that can be the target of classifier rules. > +The number of such classes supported is implementation-defined. > +Attempts to create more than are supported by the implementation will result in an \c ODP_COS_INVALID return and errno being set to \c ODP_IMPLEMENTATION_LIMIT. > + > +@subsubsection cos_destroy odp_cos_destroy > +@verbatim > +/** > + * Discard a class-of-service along with all its associated resources > + * > + * @param cos_id class-of-service instance. > + * > + * @return 0 on success, -1 on error. > + */ > + > +int odp_cos_destroy(odp_cos_t cos_id); > +@endverbatim > + > +This routine is the bracketing routine for odp_cos_create(). > +It is used to destroy an existing CoS. > +It is the caller’s responsibility to ensure that no active pattern matching rules refer to the CoS prior to calling this routine. > +Results are unpredictable if this restriction is not met. > +@subsubsection cos_set_queue odp_cos_set_queue > +@verbatim > +/** > + * Assign a queue for a class-of-service > + * > + * @param cos_id class-of-service instance. > + * > + * @param queue_id is the identifier of a queue where all packets > + * of this specific class of service will be enqueued. > + * > + * @return 0 on success, negative error code on failure. > + */ > + > +int odp_cos_set_queue(odp_cos_t cos_id, odp_queue_t queue_id); > +@endverbatim > + > +This routine associates a target queue with a CoS such that all packets assigned to this CoS will be enqueued to the specified queue_id at the end of classification processing. > +@subsubsection cos_set_queue_group odp_cos_set_queue_group > +@verbatim > +/** > + * Assign a homogenous queue-group to a class-of-service. > + * > + * @param cos_id identifier of class-of-service instance > + * @param queue_group_id identifier of the queue group to receive packets > + * associated with this class of service. > + * > + * @return 0 on success, negative error code on failure. > + */ > + > +int odp_cos_set_queue_group(odp_cos_t cos_id, odp_queue_group_t queue_group_id); > +@endverbatim > + > +This routine associates a target queue group with a CoS such that all packets assigned to this CoS will be distributed to the specified queue_group_id at the end of classification processing. > +@subsubsection cos_set_pool odp_cos_set_pool > +@verbatim > +/** > + * Assign packet buffer pool for specific class-of-service > + * > + * @param cos_id class-of-service instance. > + * @param pool_id is a buffer pool identifier where all packet buffers > + * will be sourced to store packet that belong to this > + * class of service. > + * > + * @return 0 on success negative error code on failure. > + * > + * > + */ > + > +int odp_cos_set_pool(odp_cos_t cos_id, odp_buffer_pool_t pool_id); > +@endverbatim > + > +This OPTIONAL routine associates a target buffer pool with a CoS such that all packets assigned to this CoS will be stored in packet buffers allocated from the designated pool_id. > + > + > +@subsection cos_drop_policy Class of Service Drop Policy Routines > +These routines control how drop policies are to be observed for a given class of service. > +@subsubsection drop_data_types Data types > +~~~~~{.c} > +enum odp_cos_drop_e { > + ODP_COS_DROP_POOL, /**< Follow buffer pool drop policy */ > + ODP_COS_DROP_NEVER, /**< Never drop, ignoring buffer pool policy */ > +}; > +typedef enum odp_drop_e odp_drop_t; > +~~~~~ > + > +@subsubsection cos_set_drop odp_cos_set_drop > +@verbatim > +/** > + * Assign packet drop policy for specific class-of-service > + * > + * @param cos_id class-of-service instance. > + * @param drop_policy is the desired packet drop policy for this class. > + * > + * @return 0 on success negative error code on failure. > + */ > + > +int odp_cos_set_drop(odp_cos_t cos_id, odp_drop_t drop_policy); > +@endverbatim > + > +This routine sets the drop policy for a class of service. > +It is an OPTIONAL routine. > +If an implementation does not provide this function it MUST supply a definition of it that simply returns ODP_FUNCTION_NOT_AVAILABLE. > +@subsubsection pktio_set_default_cos odp_pktio_set_default_cos > +@verbatim > +/** > + * Setup per-port default class-of-service > + * > + * @param pktio_in ingress port identifier. > + * @param default_cos class-of-service set to all packets arriving > + * at the 'pktio_in' ingress port, unless overridden by subsequent > + * header-based filters. > + * > + * @return 0 on success negative error code on failure. > + * > + * > + * @note This may replace the default queue per pktio. > + */ > + > +int odp_pktio_set_default_cos(odp_pktio_t pktio_in, odp_cos_t default_cos); > +@endverbatim > + > +This routine specifies a default class of service for a given pktio instance. > +Incoming packets on the specified pktio are assigned to this class of service if no other pattern matching rule obtains. > +@subsubsection pktio_set_error_cos odp_pktio_set_error_cos > +@verbatim > +/** > + * Setup per-port error class-of-service > + * > + * @param pktio_in ingress port identifier. > + * @param error_cos class-of-service set to all packets arriving > + * at the 'pktio_in' ingress port that contain an error. > + * > + * @return 0 on success negative error code on failure. > + */ > + > +int odp_pktio_set_error_cos(odp_pktio_t pktio_in, odp_cos_t error_cos); > +@endverbatim > + > +This OPTIONAL function assigns a class-of-service used to handle packets containing various types of errors. The specific errors types include L2 FCS and optionally L3/L4 checksum errors, malformed headers, etc., depending on platform capabilities. > +The specified error_cos MAY simply discard these packets or deliver them via a queue to the application for further processing. > +@subsubsection pktio_set_skip odp_pktio_set_skip > +@verbatim > +/** > + * Setup per-port header offset > + * > + * @param pktio_in ingress port identifier. > + * @param offset is the number of bytes the classifier must skip. > + * > + * @return Success or ODP_FUNCTION_NOT_AVAILABLE > + */ > + > +int odp_pktio_set_skip(odp_pktio_t pktio_in, size_t offset); > +@endverbatim > + > +This OPTIONAL function applies to ports that carry an additional headers preceding the standard Ethernet header. Such headers are typically vendor-specific and thus the classifier is not required to parse such headers, but the size of a custom header is critical for the classifier to be able to parse standard protocol headers that normally follow. > +@subsubsection cos_set_headroom odp_cos_set_headroom > +@verbatim > +/** > + * Specify per-port buffer headroom > + * > + * @param pktio_in ingress port identifier. > + * @param headroom number of bytes of space preceding packet data to reserve > + * for use as headroom. Must not exceed the implementation > + * defined ODP_PACKET_MAX_HEADROOM. > + * > + * @return Success or ODP_PARAMETER_ERROR, > + * or ODP_FUNCTION_NOT_AVAILABLE > + */ > + > +int odp_cos_set_headroom(odp_cos_t cos_id, size_t req_room); > +@endverbatim > + > +This OPTIONAL routine specifies the number of bytes of headroom that should be reserved for each packet assigned to this class of service. > +Each implementation defines an ODP_PACKET_MAX_HEADROOM limit that sets an upper bound on the size of the headroom that can be reserved for a packet. > +@subsubsection cos_with_l2_priority odp_cos_with_l2_priority > +@verbatim > +/** > + * Request to override per-port class of service > + * based on Layer-2 priority field if present. > + * > + * @param pktio_in ingress port identifier. > + * @param num_qos is the number of QoS levels, typically 8. > + * @param qos_table are the values of the Layer-2 QoS header field. > + * @param cos_table is the class-of-service assigned to each of the > + * allowed Layer-2 QOS levels. > + * @return 0 on success negative error code on failure. > + */ > + > +int odp_cos_with_l2_priority(odp_pktio_t pktio_in, > + size_t num_qos, > + uint8_t qos_table[], /**< 'num_qos' elements */ > + odp_cos_t cos_table[]); /**< 'num_qos' elements */ > +@endverbatim > + > +This routine is used to assign classes of service based on the layer 2 (L2) priority associated with input packets received on the specified pktio_in. > +For each of the values in qos_table[], the corresponding value in cos_table[] will be assigned. > +@subsubsection cos_with_l3_dscp odp_cos_with_l3_dscp > +@verbatim > +/** > + * > + * @param pktio_in ingress port identifier. > + * @param num_qos is the number of allowed Layer-3 QoS levels. > + * @param qos_table are the values of the Layer-3 QoS header field. > + * @param cos_table is the class-of-service assigned to each of the > + * allowed Layer-3 QOS levels. > + * @param l3_preference when true, Layer-3 QoS overrides L2 QoS when present. > + * > + * @return 0 on success negative error code on failure. > + */ > + > +int odp_cos_with_l3_qos(odp_pktio_t pktio_in, > + size_t num_qos, > + uint8_t qos_table[], /**< 'num_qos' elements */ > + odp_cos_t cos_table[], /**< 'num_qos' elements */ > + odp_bool_t l3_preference); > +@endverbatim > + > +This OPTIONAL routine is used to assign classes of service based on the layer 3 (L3) Differentiated Services (DS) designation. > +This is the DSCP field of an IPv4 header or the first six bits of the Traffic Class of an IPv6 header. > +For each of the values in qos_table[], the corresponding value in cos_table[] will be assigned. > +The l3_preference flag is use to control whether the CoS assigned by this routine takes precedence over the CoS assigned by odp_cos_with_l2_priority() in the event that both apply to the same packet. > + > +@subsection pmrs Pattern Matching Rules > +While the above routines permit class of service assignments to be made based on static criteria, the real power of classification is the ability to identify flows based on the variable contents of packet headers. > +To do this ODP provides support for defining pattern matching rules (PMRs) that operate based on values contained in specified header fields. > + > +Associated with PMRs are enums that are used to specify standard packet header fields: > +@subsubsection cos_hdr_flow_fields odp_cos_hdr_flow_fields_e > +@verbatim > +/** > + * Packet header field enumeration > + * for fields that may be used to calculate > + * the flow signature, if present in a packet. > + */ > + > +enum odp_cos_hdr_flow_fields_e { > + ODP_COS_FHDR_IN_PKTIO, /**< Ingress port number */ > + ODP_COS_FHDR_L2_SAP, /**< Ethernet Source MAC address */ > + ODP_COS_FHDR_L2_DAP, /**< Ethernet Destination MAC address */ > + ODP_COS_FHDR_L2_VID, /**< Ethernet VLAN ID */ > + ODP_COS_FHDR_L3_FLOW /**< IPv6 flow_id */ > + ODP_COS_FHDR_L3_SAP, /**< IP source address */ > + ODP_COS_FHDR_L3_DAP, /**< IP destination address */ > + ODP_COS_FHDR_L4_PROTO, /**< IP protocol (e.g. TCP/UDP/ICMP) */ > + ODP_COS_FHDR_L4_SAP, /**< Transport source port */ > + ODP_COS_FHDR_L4_DAP, /**< Transport destination port */ > + ODP_COS_FHDR_IPSEC_SPI, /**< IPsec session identifier */ > + ODP_COS_FHDR_LD_VNI, /**< NVGRE/VXLAN network identifier */ > + ODP_COS_FHDR_USER /**< Application-specific header field(s) */ > +}; > +@endverbatim > + > +Conforming ODP implementations SHOULD implement efficient flow set management routines such as these: > + > +~~~~~{.c} > +/** > + * Set of header fields that take part in flow signature hash calculation: > + * bit positions per 'odp_cos_hdr_flow_fields_e' enumeration. > + * > +typedef uint16_t odp_cos_flow_set_t; > + > + > +/** > + * Set a member of the flow signature fields data set > + * > +static inline odp_cos_flow_set_t > +odp_cos_flow_set( odp_cos_flow_set_t set, > + enum odp_cos_hdr_flow_fields_e field) > +{ > + return set | (1U << field); > +} > + > + > +/** > + * Test a member of the flow signature fields data set > + * > +static inline bool > +odp_cos_flow_is_set( odp_cos_flow_set_t set, > + enum odp_cos_hdr_flow_fields_e field) > +{ > + return (set & (1U << field)) != 0; > +} > +~~~~~ > + > +These routines are intended to be used in support of the following flow signature APIs: > + > +@subsubsection cos_class_flow_sig odp_cos_class_flow_signature > +@verbatim > +/** > + * Set up set of headers used to calculate a flow signature > + * based on class-of-service. > + * > + * @param cos_id class of service instance identifier > + * @param req_data_set requested data-set for flow signature calculation > + * > + * @return data-set that was successfully applied. All-zeros data set > + * indicates a failure to assign any of the requested fields, or other > + * error. > + */ > + > +odp_cos_flow_set_t > +odp_cos_class_flow_signature(odp_cos_t cos_id, > + odp_cos_flow_set_t req_data_set); > +@endverbatim > + > +This OPTIONAL routine associates a fow set with a class of service for flow signature calculation. > + > +@subsubsection cos_port_flow_sig odp_cos_port_flow_signature > +@verbatim > +/** > + * Set up set of headers used to calculate a flow signature > + * based on ingress port. > + * > + * @param pktio_in ingress port identifier. > + * @param req_data_set requested data-set for flow signature calculation > + * > + * @return data-set that was successfully applied. An all-zeros data-set > + * indicates a failure to assign any of the requested fields, or other > + * error. > + */ > + > +odp_cos_flow_set_t > +odp_cos_port_flow_signature(odp_pktio_t pktio_in, > + odp_cos_flow_set_t req_data_set); > +@endverbatim > + > +@subsection pmr_routines Pattern Matching Rules Routines > +The following data structures SHOULD be implemented to support the definition of pattern matching routines by conforming ODP implementations: > + > +~~~~~{.c} > +/** > + * PMR - Packet Matching Rule > + * Up to 32 bit of ternary matching of one of the available header fields > + * > + > + > +#define ODP_PMR_INVAL ((odp_pmr_t)NULL) > +typedef struct odp_pmr_s *odp_pmr_t; > +~~~~~ > + > +@subsecion terms Terms > +Terms are the elements of a PMR and are identified by the following enum: > + > +@verbatim > +enum odp_pmr_term_e { > + ODP_PMR_ETHTYPE_0, /**< Initial (outer) Ethertype only (*val=uint16_t)*/ > + ODP_PMR_ETHTYPE_X, /**< Ethertype of most inner VLAN tag (*val=uint16_t)*/ > + ODP_PMR_VLAN_ID_0, /**< First VLAN ID (outer) (*val=uint16_t) */ > + ODP_PMR_VLAN_ID_X, /**< Last VLAN ID (inner) (*val=uint16_t) */ > + ODP_PMR_DMAC, /**< destination MAC address (*val=uint64_t) */ > + ODP_PMR_IPPROTO, /**< IP Protocol or IPv6 Next Header (*val=uint8_t) */ > + ODP_PMR_UDP_DPORT, /**< Destination UDP port, implies IPPROTO=17 */ > + ODP_PMR_TCP_DPORT, /**< Destination TCP port implies IPPROTO=6 */ > + ODP_PMR_UDP_SPORT, /**< Source UDP Port (*val=uint16_t) */ > + ODP_PMR_TCP_SPORT, /**< Source TCP port (*val=uint16_t) */ > + ODP_PMR_SIP_ADDR, /**< Source IP address (uint32_t) */ > + ODP_PMR_DIP_ADDR, /**< Destination IP address (uint32_t) */ > + ODP_PMR_SIP6_ADDR, /**< Source IP address (uint8_t[16]) */ > + ODP_PMR_DIP6_ADDR, /**< Destination IP address (uint8_t[16]) */ > + ODP_PMR_IPSEC_SPI, /**< IPsec session identifier(*val=uint32_t) */ > + ODP_PMR_LD_VNI, /**< NVGRE/VXLAN network identifier (*val=uint32_t) */ > + > + > + /** Inner header may repeat above values with this offset */ > + ODP_PMR_INNER_HDR_OFF=32 > +}; > +@endverbatim > + > +@subsubsection tunnel_considerations Tunnel Considerations > +Note that PMRs may be extended to support tunnels and tenants (NVGRE, VXLAN) via the ODP_PMR_INNER_HDR_OFF enum. > +This enum is intended to be used as an “adder” to a PMR to indicate that the term refers to an inner header. > +For example, the term ODP_PMR_DMAC would refer to the destination MAC address of the packet if the packet is not a tunnel, or of the outer header (the tunnel) if the packet is a tunnel. > +To refer to the inner (tenant) destination MAC, the term would be specified as ODP_PMR_INNER_HDR_OFF+ODP_PMR_DMAC. > + > +@subsection pmr_apis PMR APIs > +The following APIs are provided to enable an ODP application to specify PMRs as a series of individual or cascaded terms: > +@subsubsection pmr_create_match odp_pmr_create_match > +@verbatim > +/** > + * Create a packet match rule with mask and value > + * > + * @param term is one value of the enumerated values supported > + * @param val is the value to match against the packet header > + * in native byte order. > + * @param mask is the mask to indicate which bits of the header > + * should be matched ('1') and which should be ignored ('0') > + * @param val_sz size of the ‘val’ and ‘mask’ arguments, > + * that must match the value size requirement of the > + * specific ‘term’. > + * > + * @return a handle of the matching rule or ODP_PMR_INVAL on error > + */ > + > +odp_pmr_t odp_pmr_create_match(enum odp_pmr_term_e term, > + const void *val, const void *mask, size_t val_sz); > +@endverbatim > + > +This routine creates a PMR that matches a single value to a term. > + > +@subsubsection pmr_create_range odp_pmr_create_range > +@verbatim > +/** > + * Create a packet match rule with value range > + * > + * @param term is one value of the enumerated values supported > + * @param val1 is the lower bound of the header field range. > + * @param val2 is the upper bound of the header field range. > + * @param val_sz size of the ‘val1’ and ‘val2’ arguments, > + * that must match the value size requirement of the > + * specific ‘term’. > + * > + * @return a handle of the matching rule or ODP_PMR_INVAL on error > + * @note: Range is inclusive [val1..val2]. > + */ > + > +odp_pmr_t odp_pmr_create_range(enum odp_pmr_term_e term, > + const void *val1, const void *val2, size_t val_sz); > +@endverbatim > + > +This routine creates a PMR that matches an inclusive range of values to a term. > + > +@subsubsection pmr_destroy odp_pmr_destroy > +@verbatim > +/** > + * Invalidate a packet match rule and vacate its resources > + * > + * @param pmr_id the identifier of the PMR to be destroyed > + * > + * @return Success or ODP_PMR_INVALID if the specified pmr_id not found. > + */ > + > +int odp_pmr_destroy(odp_omr_t pmr_id); > +@endverbatim > + > +This routine destroys a previously created PMR. > +If the PMR is currently associated with an active class of service it is unpredictable at which point the match defined by the PMR is deactivated in terms of packet flow. > +However, implementations MUST ensure that a PMR is either matched or not matched in its entirety such that dynamic changes to PMRs do not result in partial matches. > + > +@subsubsection pktio_pmr_cos odp_pktio_pmr_cos > +@verbatim > +/** > + * Apply a PMR to a pktio to assign a CoS. > + * > + * @param pmr_id the id of the PMR to be activated > + * @param src_pktio the pktio to which this PMR is to be applied > + * @param dst_cos the CoS to be assigned by this PMR > + * > + * @return Success or ODP_PARAMETER_ERROR > + */ > + > +int odp_pktio_pmr_cos(odp_pmr_t pmr_id, odp_pktio_t src_pktio, odp_cos_t dst_cos); > +@endverbatim > + > +This routine links a pktio to a corresponding class of service via a specified PMR. > +Any packet received on the specified src_pktio that matches the specified pmr_id will be assigned to the specified dst_cos. > +If multiple PMRs match the implementation MAY define an inherent precedence or it MAY be unpredictable as to which PMR will determine the assigned CoS. > +For this reason applications SHOULD NOT be written to use conflicting or ambiguous PMR definitions. > + > +@subsubsection cos_pmr_cos odp_cos_pmr_cos > +@verbatim > +/** > + * Cascade a PMR to refine packets from one CoS to another. > + * > + * @param pmr_id the id of the PMR to be activated > + * @param src_cos the id of the CoS to be filtered > + * @param dst_cos the id of the CoS to be assigned to packets filtered > + * from src_cos that match pmr_id. > + * > + * @return Success or ODP_PARAMETER_ERROR if an input is in error > + * or ODP_IMPLEMENTATION_LIMIT if cascade depth is exceeded > + */ > + > +int odp_cos_pmr_cos(odp_pmr_t pmr_id, odp_cos_t src_cos, odp_cos_t dst_cos); > +@endverbatim > + > +This routine is used to cascade PMRs by passing packets assigned to the src_cos through another PMR. > +Those matching are reassigned to the specified dst_cos. > +Note that this process can be repeated to an implementation-defined maximum supported cascade depth. > +When cascades are defined, the actual class of service assigned to a packet is the result of the longest chain of PMRs that can be matched against the packet. > + > +For example, suppose the following sequence of PMRs is in effect: > + > +@verbatim > +odp_pktio_pmr_cos(pmr_idA, pktio_id, cos_idA); > +odp_cos_pmr_cos(pmr_idB, cos_idA, cos_idB); > +odp_cos_pmr_cos(pmr_idC, cos_idB, cos_idC); > +odp_cos_pmr_cos(pmr_idD, cos_idC, cos_idD); > +@endverbatim > + > +If a packet arrives on pktio_id that matches pmr_idA it is assigned to cos_idA. > +But since it is now on cos_idA it is further filtered by pmr_idB and if it matches is reassigned to cos_idB. > +This process continues until no further more specific match is found to determine the final CoS that the packet receives. > + > +Note that given this rule set a packet that matched pmr_idA and pmr_idC it would be assigned to cos_idA because the rule that can assign packets to pmr_idC is only applicable to packets that are assigned to cos_idB, not cos_idA. > + > +Using cascaded PMRs it is possible to build quite sophisticated filters (up to the implementation limits supported by a given platform). > +For example, one could add additional rules to the above set: > + > +@verbatim > +odp_cos_pmr_cos(pmr_idAC, cos_idA, cos_idC); > +odp_cos_pmr_cos(pmr_idAD, cos_idA, cos_idD); > +@endverbatim > + > +To cover cases where some packets on cos_idA should be further sorted to cos_idB while others should be sorted directly to cos_idC or cos_idD. > +Again it is the application’s responsibility to ensure that the cascades remain unambiguous and that loops be avoided (e.g., having rules that bounce packets between cos_idA and cos_idB endlessly). > + > +@subsection pmr_stats PMR Statistics > +Conforming ODP implementations SHOULD maintain statistics regarding PMRs and provide the following routines for retrieving them: > + > +@subsubsection pmr_match_count odp_pmr_match_count > +@verbatim > +/** > + * Retrieve packet matcher statistics > + * > + * @param pmr_id the id of the PMR from which to retrieve the count > + * > + * @return The current number of matches for a given matcher instance. > + */ > + > +signed long odp_pmr_match_count(odp_pmr_t pmr_id); > +@endverbatim > + > +@subsubsection pmr_terms_cap odp_pmr_terms_cap > +@verbatim > +/** > + * Inquire about matching terms supported by the classifier > + * > + * @return A mask one bit per enumerated term, one for each of op_pmr_term_e > + */ > + > +unsigned long long odp_pmr_terms_cap(void); > +@endverbatim > + > +@subsubsection pmr_terms_avail odp_pmr_terms_avail > +@verbatim > +/** > + * Return the number of packet matching terms available for use > + * > + * @return A number of packet matcher resources available for use. > + */ > + > +unsigned odp_pmr_terms_avail(void); > +@endverbatim > + > +@subsection pmr_composite_rules Pattern Matching Composite Routines > +As a shorthand, applications MAY express pattern matching rules using a table rather than constructing them term-by-term. > +ODP implementations MUST support both methods of rule specification but MAY have implementation-specific restrictions on the complexity of table-based rules they support. > +Note that some implementations MAY be able to implement tables directly while others MAY choose to implement tables by internally generating the equivalent set of term generating calls. > + > +@subsubsection pmr_table_structure PMR Table Structure > +@verbatim > +/** > + * Following structure is used to define composite packet matching rules > + * in the form of an array of individual match or range rules. > + * The underlying platform may not support all or any specific combination > + * of value match or range rules, and the application should take care > + * of inspecting the return value when installing such rules, and perform > + * appropriate fallback action. > + */ > + > +typedef struct odp_pmr_match_t { > + enum odp_pmr_match_type_e { > + ODP_PMR_MASK, /**< Match a masked set of bits */ > + ODP_PMR_RANGE, /**< Match an integer range */ > + } match_type; > + union { > + struct { > + enum odp_pmr_term_e term; > + const void *val; > + const void *mask; > + unsigned int val_sz; > + } mask; /**< Match a masked set of bits */ > + struct { > + enum odp_pmr_term_e term; > + const void *val1; > + const void *val2; > + unsigned int val_sz; > + } range; /**< Match an integer range */ > + }; > +} odp_pmr_match_t; > + > + > +/** An opaque handle to a composite packet match rule-set */ > +typedef struct odp_pmr_set_s *odp_pmr_set_t; > +@endverbatim; > + > +The above structure is used with the following APIs to implement table-based PMRs: > + > +@subsubsection pmr_match_set_create odp_pmr_match_set_create > +@verbatim > +/** > + * Create a composite packet match rule > + * > + * @param num_terms is the number of terms in the match rule. > + * @param terms is an array of num_terms entries, one entry per > + * term desired. > + * @param dst_cos is the class-of-service to be assigned to packets > + * that match the compound rule-set, or a subset thereof, > + * if partly applied. > + * @param pmr_set_id is the returned handle to the composite rule set. > + * > + * @return The return value may be a negative number indicating a general > + * error, or a positive number indicating the number of ‘terms’ elements that > + * have been successfully mapped to the underlying platform classification engine, > + * and may be in the range from 1 to ‘num_terms’. > + */ > + > +int odp_pmr_match_set_create(int num_terms, odp_pmr_match_t *terms, > + odp_pmr_set_t *pmr_set_id); > +@endverbatim > + > +This routine is used to create a PMR match set. > + It is the equivalent to a cascade of PMRs except that there are no “intermediate” classes of service defined. > +Instead, the entire match set either matches or does not match as a single entity. > + > +@subsubsection pmr_match_set_destroy odp_pmr_match_set_destroy > +@verbatim > +/** > + * Function to delete a composite packet match rule set > + * > + * All of the resources pertaining to the match set associated with the > + * class-of-service will be released, but the class-of-service will > + * remain intact. > + * > + * @param pmr_set_id a composite rule-set handle returned when created. > + * > + * @note Depending on the implementation details, destroying a rule-set > + * may not guarantee the availability of hardware resources to create the > + * same or essentially similar rule-set. > + */ > + > +int odp_pmr_match_set_destroy(odp_pmr_set_t pmr_set_id); > +@endverbatim > + > +This routine destroys a PMR match set previously created by odp_pmr_match_set_create(). > + > +@subsubsection pktio_pmr_match_set_cos odp_pktio_pmr_match_set_cos > +@verbatim > +/** > + * Apply a PMR Match Set to a pktio to assign a CoS. > + * > + * @param pmr_set_id the id of the PMR match set to be activated > + * @param src_pktio the pktio to which this PMR match set is to be applied > + * @param dst_cos the CoS to be assigned by this PMR match set > + * > + * @return Success or ODP_PARAMETER_ERROR > + */ > + > +int odp_pktio_pmr_match_set_cos(odp_pmr_t pmr_id, odp_pktio_t src_pktio, > + odp_cos_t dst_cos); > +@endverbatim > + > +This routine is the same as odp_pktio_pmr_cos() except that it operates on PMR match sets rather than individual PMRs. > + > +@section items_pending Items pending resolution > +- Revise ‘odp_packet_io.h’ API with respect of default input queue per ‘pktio’ instance. > +- Revise ‘odp_queue.h’ API to support an arbitrary priority range, typically 8 priority levels with numeric priority values are platform-specific. > +- Add specific packet meta data fields to go into packet buffer which contain all meta data fields parsed and generated by the classifier, for later application use. > + > +@section implementation_notes Implementation Notes > +The following sections are not part of the specification, but shed light into the intent of the specification in several areas, describing some specific implementation approaches of these aspects. > + > +@subsection supporting_multi_pools Supporting multiple buffer pools > +The support of multiple buffer pools for containing packet buffers is optional, and may not be supported by some platforms. > +The importance of this feature stems from the need of protecting a networking application in the event of a congestion, or an attempted denial of service attack. > +Separating different classes of service to dedicated buffer pools allows the system to limit the memory resources that may be consumed by a particular type of traffic, thereby reserving buffer resources for other classes of traffic. > + > +In a software implementation, a packet would already be stored in memory when the classifier is invoked, and so it seems the classifier is unable to insert itself into the process of selecting a buffer pool. > +For obvious reasons the copying of a packet into a new buffer allocated from a different pool by the classifier is not a desirable solution. > + > +The recommended solution is to implement buffer pools in the form of buffer counters, while the actual buffers all belong to a single free list when not used to store a packet. > +In such an implementation, the classifier will be able to associate a packet already occupying a buffer to a different pool than the default by incrementing the buffer counter of the newly selected pool, and decrementing the counter representing the default pool. > +If however the selected pool counter has already reached a certain limit, the classifier would be able to e.g discard the packet instead of incrementing the destination pool counter, and thereby enforce the desirable semantics of distinct buffer pools per class of service. > + > +Other possible action that may be taken in response to running out of buffers or coming too low on buffers include back-pressure and random-early-detect with a discard probability inversely proportional to the number of free buffers in a pool. > +A related implementation topic is the ability to begin dropping some packets before a buffer pool is entirely exhausted. > +This is typically referred to as <em>Random Early Detect</em> (or “RED”). > +This is deemed to be a feature of the buffer pool implementation on a given platform, where in addition to a hard limit on the number of buffers that can be allocated to a pool, there can also be an option discard packets with a probability the increases as the number of outstanding buffers approaches that hard limit. > + > +@subsection resolving_gaps Resolving gaps between the API and hardware capabilities > +On platforms that support hardware packet accelerators, it is possible that the packet parsing and classification functionality is sufficient to address only a portion of the functionality specified within this document. > +This gap may be potentially bridged by augmenting the hardware classification capabilities with a software logic implemented as part of the platform. > +In that case, the platform will have to curve out a fraction of the processing resources and dedicate those to the software classification logic, which would be invoked for packets that the hardware platform was unable to classify completely. > +At the time of this writing, it is believed however that the performance penalty that will be incurred as a result of software augmentation is unjustified for most application, i.e. > +it is preferred to lose the precision of packet prioritization while maintaining full hardware packet processing speed. > + > +@subsection loopback_case The case for loopback ports, and some of their uses > +In some applications, it may be desirable to be able to run a single packet through the classifier more than once. > +For example, an encrypted IPsec packet is received from a physical port. > +The encrypted packet is assigned a class of service based on its outer unencrypted header fields. > +Later, processing the packet entails decrypting the payload of the packet, authenticating it, and removing the original outer headers, which reveals a new set of protocol headers which need to be used to re-classify the packet, and assign it a new priority and buffer pool. > +An elegant solution for this use case would be to take advantage of “loopback” logical ports that may be implemented in certain platforms, by transmitting decapsulated packet into a loop-back port. > +The same packet then is received from a loop-back port and is examined by the classifier in accordance to the rules assigned to the loopback odp_pktio logical port instance. > +Similar mechanism may be applied to tunnel termination processing, fragment reassembly et al. > + > +@section related_topics Related Topics > +The following section discusses aspects of the ODP API that are not integral to the classifier, which only applies to ingress preprocessing. > +This section covers miscellaneous aspects of the API that need to be addressed, and are related to packet buffer processing and egress post-processing. > +Additional packet buffer manipulation APIs > +The need for these following calls are made evident by the need to encapsulate, i.e., remove some headers and add other, thereby changing the size of the headers of a packet during processing. > + > +@subsection initial_headroom Configuring initial packet buffer headroom > +The following function is provided to configure the pktio receive mechanism to (optionally)reserve some headroom between start of the first buffer to the first byte of the first packet data byte, which subsequently could be used to increase the header size “in-place”, without allocating additional gather list elements. > +If the request is granted, at least <req_bytes> bytes will be reserved in the front of the packet data: > +@verbatim > +int odp_pktio_set_headroom(odp_pktio_t port_id, unsigned req_bytes); > +@endverbatim > +The return value should be negative if the request can not be satisfied, or positive otherwise indicating the actual minimum headroom reserved. > +Note that the implementation may reserve more than the requested amount of headroom, and hence on platforms that are unable to support per-port (or per CoS) headroom configuration, a system-wide headroom configuration may be set to the largest of all such requests, and thus satisfy the requirement. > +In addition to the above per-port headroom configuration call, there should be an optional, per-CoS call that allows the reservation of different amounts of packet buffer headroom for packets that match certain criteria: for example, the following call allows the application to request that only packets that are expected to be encapsulated in a tunnel, be augmented with a large headroom amount, while packets that are received from a tunnel, and are IP fragments, be assigned a different headroom requirement (see definition for odp_cos_set_headroom() above. > +Egress packet scheduling, prioritization and ordering > + > + > +Open Issues > +* Parallel matching rules relative precedence. > +* Specify application-defined header field declaration APIs. > +* Review RFC 4301 for match requirements for IPsec SA, consider the use of L4 port ranges instead of or in addition to value & mask matching criteria. > +* Consider the type of packet checks should route a packet through the error CoS: L2 is a safe choice, but L3/L4 checksum or other exceptions deserve consideration. > +Usage Examples > +Following is a simple sample configuration using the API elements described above. > +TBD. > + > +*/ > diff --git a/images/classification_flow.png b/images/classification_flow.png > new file mode 100644 > index 0000000000000000000000000000000000000000..2d94ff64ec772aa97f3687181c121fb91d671889 > GIT binary patch > literal 35193 > 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 > Ru|ekbQbt9(O42mw{{YqNUv2;Z > > literal 0 > HcmV?d00001 >
Here is $5 to get a modern monitor - the teletype you have is very noisy anyway :) I think the long lines are actually a benefit - with a sentence per line git then shows a better change log, given this text is never part of a source file I think it is ok. On 4 August 2014 13:42, Maxim Uvarov <maxim.uvarov@linaro.org> wrote: > How long are strings in that document? Maybe to fit them to 80 characters > to better read in terminal? > > Maxim. > > On 08/04/2014 09:33 PM, Bill Fischofer wrote: > >> Switch to use of @verbatim/@endverbatim to avoid formatting issues with >> different levels of doxygen. >> >> Signed-off-by: Bill Fischofer <bill.fischofer@linaro.org> >> --- >> classification_design.dox | 879 ++++++++++++++++++++++++++++++ >> +++++++++++ >> images/classification_flow.png | Bin 0 -> 35193 bytes >> 2 files changed, 879 insertions(+) >> create mode 100644 classification_design.dox >> create mode 100644 images/classification_flow.png >> >> diff --git a/classification_design.dox b/classification_design.dox >> new file mode 100644 >> index 0000000..0cb7134 >> --- /dev/null >> +++ b/classification_design.dox >> @@ -0,0 +1,879 @@ >> +/* Copyright (c) 2014, Linaro Limited >> + * All rights reserved >> + * >> + * SPDX-License-Identifier: BSD-3-Clause >> + */ >> + >> +/*! >> +@page classification_design ODP Design - Classification API >> +For the implementation of the ODP classification API please see @ref >> odp_classify.h >> + >> +@tableofcontents >> + >> +@section introduction Introduction >> +This document defines the Classification APIs supported by ODP v1.0. >> +Classification is logically composed of two stages: Parsing and Rule >> Matching. >> +Parsing takes a raw packet and validates its structure and identifies >> fields of interest in the various headers that comprise the layers of the >> packet. >> +Rule Matching, in turn, takes the result of parsing and sorts packets >> into Classes of Service (CoS) based on application-defined rule sets. >> +@subsection use_of_terms Use of Terms >> +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", >> "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this >> document are to be interpreted as described in [RFC 2119]( >> https://tools.ietf.org/html/rfc21199). >> +@subsection purpose Purpose >> +ODP is a framework for software-based packet forwarding/filtering >> applications, and the purpose of the Packet Classifier API is to enable >> applications to program the platform hardware or software implementation to >> assist in prioritization, classification and scheduling of each packet, so >> that the software application can run faster, scale better and adhere to >> QoS requirements. >> +The following API abstraction are not modelled after any existing >> product implementation, but is instead defined in terms of what a typical >> data-plane application may require from such a platform, without >> sacrificing simplicity and avoiding ambiguity. Certain terms that are being >> used within the context of existing products in relation to packet parsing >> and classification, such as “access lists” are avoided such that not to >> suggest any relationship between the abstraction used within this API and >> any particular manner in which they may be implemented in hardware. >> +These are the key ODP objects that the parser needs to employ, that are >> presently defined in ODP: >> +@subsubsection odp_pktio odp_pktio >> +odp_pktio specifies an individual packet I/O channel instance. In other >> words, it would translate to a physical interface or a logical port, or in >> the case of channelized protocols (e.g., [Interlaken](https://www. >> google.com/url?q=https%3A%2F%2Fwww.cortina-systems.com% >> 2Fimages%2Fdocuments%2F400023_Interlaken_Technology_White_ >> Paper.pdf&sa=D&sntz=1&usg=AFQjCNEBdJTBmA1XaNGY3pmumQTfgSi1oA)) it would >> map to a logical channel on that interface. >> +Since the classifier API deals exclusively with ingress, this object >> represents the source of packets into the classifier. In order to support >> any non-trivial use case, the classifier API needs to be able to assign >> multiple odp_queue instances for any single odp_pktio object, and may also >> assign any odp_queue instance to more than one odp_pktio object. >> +@subsubsection odp_queue odp_queue >> +odp_queue specifies a logical queue for packets, and in the case of >> ingress, this would represent a stream of packets which share several >> attributes, that are delivered to the ODP application for processing. The >> per-queue attributes currently defined are: queue type, sync (ordering); >> priority; and schedule group (set of processor cores). >> +@subsubsection odp_buffer_pool odp_buffer_pool >> +odp_buffer_pool specifies a collection of buffers of same size and >> alignment, as well as a set of policies such as flow control and processor >> affinity. The classifier API refers to such pools that are designated for >> storing ingress packets. >> +@section functional_description Functional Description >> +Following is the functionality that is required of the classifier API, >> and its underlying implementation. The details and order of the following >> paragraph is informative, and is only intended to help convey the >> functional scope of a classifier and provide context for the API. In >> reality, implementations may execute many of these steps concurrently, or >> in different order while maintaining the evident dependencies: >> + >> +-# Apply a set of \e classification \e rules to the header of an >> incoming packet, identify the header fields, e.g., \e ethertype, IP >> version, IP protocol, transport layer port numbers, IP DiffServ, VLAN id, >> 802.1p priority. >> + >> +-# Store these fields as packet meta data for application use, and for >> the remainder of parser operations. The \e odp_pktio is also stored as one >> of the meta data fields for subsequent use. >> + >> +-# Compute an \e odp_cos (Class of Service) value from a subset of >> supported fields from 1) above. >> + >> + >> +-# Based on the \e odp_cos from 3) above, select the \e odp_queue >> through which the packet is delivered to the application. >> + >> +-# Validate the packet data integrity (checksums, FCS) and correctness >> (e.g., length fields) and store the validation result, along with optional >> error layer and type indicator, in packet meta data. Optionally, if a >> packet fails validation, override the \e odp_cos selection in step 3 to a >> class of service designated for errored packets. >> + >> +-# Since the selected \e odp_queue may require preservation of packet >> order, i.e., SYNC_ATOMIC or SYNC_ORDERED, optionally select the packet >> header fields from which the parser calculates a \e odp_flow_signature, >> which may be a unique flow identifier or a hash, such that the packets >> which are assigned the same \e odp_flow_signature are scheduled in the same >> order they are received. >> + >> +-# Based on the \e odp_cos from 3) above, select the \e odp_buffer_pool >> that should be used to acquire a buffer to store the packet data and meta >> data. >> + >> +-# Allocate a buffer from \e odp_buffer_pool selected in 6) above and >> logically store the packet data and meta data to the allocated buffer, or >> in accordance with class-of-service drop policy and subject to pool buffer >> availability, optionally discard the packet. >> + >> +-# Enqueue the buffer into the \e odp_queue selected in 4) above. >> + >> +The above is an abstract description of the classifier functionality, >> and may be applied to a variety of applications in many different ways. >> +The ultimate meaning of how this functionality applies to an application >> also depends on other ODP modules, so the above may not complete a full >> depiction. >> +For instance, the exact meaning of \e priority, which is a per-queue >> attribute is influenced by the ODP scheduler semantics, and the system >> behavior under stress depends on the ODP buffer pool module behavior. >> + >> +For the sole purpose of illustrating the above abstract functionality, >> here is an example of a Layer-2 (IEEE 802.1D) bridge application: >> +Such a forwarding application that also adheres to IEEE 802.1p/q >> priority, which has 8 traffic priority levels, might create 8 \e >> odp_buffer_pool instances, one for each PCP priority level, and 8 \e >> odp_queue instances one per priority level. >> +Incoming packets will be inspected for a VLAN header; the PCP field will >> be extracted, and used to select both the pool and the queue. >> +Because each queue will be assigned a priority value, the packets with >> highest PCP values will be scheduled before any packet with a lower PCP >> value. >> +Also, in a case of congestion, buffer pools for lower priority packets >> will be depleted earlier than the pools containing packets of the high >> priority, and hence the lower priority packets will be dropped (assuming >> that is the only flow control method that is supported in the platform) >> while higher priority packets will continue to be received into buffers and >> processed. >> +@subsection flow_diagram Classification Processing Flow Diagram >> +@image html classification_flow.png "Figure 1: Classification Flow >> Diagram" >> +@image latex classification_flow.eps "Figure 1: Classification Flow >> Diagram" >> + >> +@section api_elements API Elements >> +While the above description refers to the abstracted packet classifier, >> the following is the description of the API designed to program the packet >> classifier, and is intended to add clarity to the functions provided >> further below. >> +@subsection cos_creation Class of Service Creation and Binding >> +To program the classifier, a class-of-service instance must be created, >> which will contain the packet filtering resources that it may require. >> +All subsequent calls refer to one or more of these resources. >> +Each class of service instance must be associated with a single queue or >> queue group, which will be the destination of all packets matching that >> particular filter. >> +The queue assignment is implemented as a separate function call such >> that the queue may be modified at any time, without tearing down the >> filters that define the class of service. In other words, it is possible to >> change the destination queue for a class of service defined by its filters >> quickly and dynamically. >> +Optionally, on platforms that support multiple packet buffer pools, each >> class of service may be assigned a different pool such that when buffers >> are exhausted for one class of service, other classes are not negatively >> impacted and continue to be processed. >> + >> +@subsection default_packet_handling Default packet handling >> +There SHOULD be one \b odp_cos assigned to each port with the \c >> odp_cos_pktio_set() function, which will function as the default >> class-of-service for all packets received from an ingress port, that do not >> match any of the filters defined subsequently. At minimum this default >> class-of-service MUST have a queue and a buffer pool assigned to it on >> platforms that support multiple packet buffer pools. Multiple odp_pktio >> instances (i.e., multiple ports) MAY each have their own default odp_cos, >> or MAY share a odp_cos with other ports, based on application requirements. >> + >> +@subsection packet_classification Packet Classification >> +For each odp_pktio port, the API allows the assignment of a >> class-of-service to a packet using one of three methods: >> + >> +-# The packet may be assigned a specific class-of-service based on its >> Layer-2 (802.1P/902.1Q VLAN tag) priority field. Since the standard field >> defines 8 discrete priority levels, the API allows to assign an odp_cos to >> each of these priority levels with the \c odp_cos_with_l2_priority() >> function. >> + >> +-# Similarly, a class-of-service may be assigned using the Layer-3 (IP >> DiffServ) header field. The application supplies an array of \e odp_cos >> values that covers the entire range of the standard protocol header field, >> where array elements do not need to contain unique values. There is also a >> need to specify if Layer-3 priority takes precedence over Layer-2 priority >> in a packet with both headers present. >> + >> +-# Additionally, the application may also program a number of \e pattern >> \e matching \e rules that assign a class-of-service for packets with header >> fields matching specified values. The field-matching rules take precedence >> over the previously described priority-based assignment of a >> class-of-service. Using these matching rules the application should be able >> for example to identify all packets containing VoIP traffic based on the >> protocol being UDP, and a specific destination or source port numbers, and >> appropriately assign these packets an class-of-service that maps to a >> higher priority queue, assuring voice packets a lower and bound latency. >> + >> +@subsection scaling_and_flow Scaling and Flow Discrimination >> +In addition to classifying packets and routing them to those queues with >> the appropriate priority, and optionally limiting their memory consumption >> by designating certain classes of packets to specific buffer pools, the >> classifier API also facilitates the scaling of data-plane application on >> multi-core systems by creating a mechanism to define which packet headers >> need to be combined to result in a value representing a specific packet >> flow. The classifier generates a signature, which can be a checksum or hash >> of arbitrary strength that covers those packet header fields that are >> identified by the application as identifying flows. >> + >> +The \e flow \e signatures that result from hashing are then stored with >> the packet meta data (along with its class-of-service and its ingress \e >> odp_pktio port), and subsequently may be utilized by the implementation of >> a scheduler queue to maintain the order of packets with the same flow >> signature, while allowing packets with different signatures to be processed >> concurrently and independently on different processing cores. >> + >> +@subsection packet_meta_data Packet meta data Elements >> +Here are the specific information elements that SHOULD be stored within >> the packet meta data structure: >> +- Protocol fields that are decoded and extracted by the parsing phase >> +- Flow-signature calculated from a prescribed collection of protocol >> fields >> +- The class-of-service identifier that is selected for the packet >> +- The ingress port identifier >> +- The result of packet validation, including an indication of the type >> of error detected, if any >> + >> +The ODP packet API module SHALL provide accessors for retrieving the >> above meta data fields from the container buffer in an >> implementation-independent manner. >> + >> +@section api_definitions API Definitions >> +@subsection data_types Data Types >> +The following data types are referenced in the API descriptions >> described below. >> +The names are part of the ODP API and MUST be present in any conforming >> implementation, however the type values shown here are illustrative and >> implementations SHOULD either use these or substitute their own type values >> that are appropriate to the underlying platform. >> + >> +@verbatim >> +/** >> + * 'odp_pktio_t' value to indicate any port >> + */ >> +#define ODP_PKTIO_ANY ((odp_pktio_t)~0) >> + >> + >> +/** >> + * 'odp_pktio_t' value to indicate an error >> + */ >> +#define ODP_PKTIO_INVALID ((odp_pktio_t)0) >> + >> + >> +/** >> + * Class of service instance type >> + */ >> +typedef uint32_t odp_cos_t; >> + >> + >> +/** >> + * flow signature type, only used for packet meta data field. >> + */ >> +typedef uint32_t odp_flowsig_t; >> + >> + >> +/** >> + * This value is returned from odp_cos_create() on failure, >> + * May also be used as a “sink” class of service that >> + * results in packets being discarded. >> + */ >> +#define ODP_COS_INVALID ((odp_cos_t)~0) >> +@endverbatim >> + >> +@subsection cos_routines Class of Service Routines >> +Conforming ODP implementations MUST provide the following Classification >> APIs: >> +@subsubsection cos_create odp_cos_create >> +@verbatim >> +/** >> + * Create a class-of-service >> + * >> + * @param name is a string intended for debugging purposes. >> + * >> + * @return Class of service instance identifier, >> + * or ODP_COS_INVALID on error. >> + */ >> + >> +odp_cos_t odp_cos_create(const char *name); >> +@endverbatim >> + >> +This routine is used to create a class of service that can be the target >> of classifier rules. >> +The number of such classes supported is implementation-defined. >> +Attempts to create more than are supported by the implementation will >> result in an \c ODP_COS_INVALID return and errno being set to \c >> ODP_IMPLEMENTATION_LIMIT. >> + >> +@subsubsection cos_destroy odp_cos_destroy >> +@verbatim >> +/** >> + * Discard a class-of-service along with all its associated resources >> + * >> + * @param cos_id class-of-service instance. >> + * >> + * @return 0 on success, -1 on error. >> + */ >> + >> +int odp_cos_destroy(odp_cos_t cos_id); >> +@endverbatim >> + >> +This routine is the bracketing routine for odp_cos_create(). >> +It is used to destroy an existing CoS. >> +It is the caller’s responsibility to ensure that no active pattern >> matching rules refer to the CoS prior to calling this routine. >> +Results are unpredictable if this restriction is not met. >> +@subsubsection cos_set_queue odp_cos_set_queue >> +@verbatim >> +/** >> + * Assign a queue for a class-of-service >> + * >> + * @param cos_id class-of-service instance. >> + * >> + * @param queue_id is the identifier of a queue where all >> packets >> + * of this specific class of service will be enqueued. >> + * >> + * @return 0 on success, negative error code on failure. >> + */ >> + >> +int odp_cos_set_queue(odp_cos_t cos_id, odp_queue_t queue_id); >> +@endverbatim >> + >> +This routine associates a target queue with a CoS such that all packets >> assigned to this CoS will be enqueued to the specified queue_id at the end >> of classification processing. >> +@subsubsection cos_set_queue_group odp_cos_set_queue_group >> +@verbatim >> +/** >> + * Assign a homogenous queue-group to a class-of-service. >> + * >> + * @param cos_id identifier of class-of-service instance >> + * @param queue_group_id identifier of the queue group to receive packets >> + * associated with this class of service. >> + * >> + * @return 0 on success, negative error code on failure. >> + */ >> + >> +int odp_cos_set_queue_group(odp_cos_t cos_id, odp_queue_group_t >> queue_group_id); >> +@endverbatim >> + >> +This routine associates a target queue group with a CoS such that all >> packets assigned to this CoS will be distributed to the specified >> queue_group_id at the end of classification processing. >> +@subsubsection cos_set_pool odp_cos_set_pool >> +@verbatim >> +/** >> + * Assign packet buffer pool for specific class-of-service >> + * >> + * @param cos_id class-of-service instance. >> + * @param pool_id is a buffer pool identifier where all packet buffers >> + * will be sourced to store packet that belong to this >> + * class of service. >> + * >> + * @return 0 on success negative error code on failure. >> + * >> + * >> + */ >> + >> +int odp_cos_set_pool(odp_cos_t cos_id, odp_buffer_pool_t pool_id); >> +@endverbatim >> + >> +This OPTIONAL routine associates a target buffer pool with a CoS such >> that all packets assigned to this CoS will be stored in packet buffers >> allocated from the designated pool_id. >> + >> + >> +@subsection cos_drop_policy Class of Service Drop Policy Routines >> +These routines control how drop policies are to be observed for a given >> class of service. >> +@subsubsection drop_data_types Data types >> +~~~~~{.c} >> +enum odp_cos_drop_e { >> + ODP_COS_DROP_POOL, /**< Follow buffer pool drop policy */ >> + ODP_COS_DROP_NEVER, /**< Never drop, ignoring buffer pool >> policy */ >> +}; >> +typedef enum odp_drop_e odp_drop_t; >> +~~~~~ >> + >> +@subsubsection cos_set_drop odp_cos_set_drop >> +@verbatim >> +/** >> + * Assign packet drop policy for specific class-of-service >> + * >> + * @param cos_id class-of-service instance. >> + * @param drop_policy is the desired packet drop policy for this class. >> + * >> + * @return 0 on success negative error code on failure. >> + */ >> + >> +int odp_cos_set_drop(odp_cos_t cos_id, odp_drop_t drop_policy); >> +@endverbatim >> + >> +This routine sets the drop policy for a class of service. >> +It is an OPTIONAL routine. >> +If an implementation does not provide this function it MUST supply a >> definition of it that simply returns ODP_FUNCTION_NOT_AVAILABLE. >> +@subsubsection pktio_set_default_cos odp_pktio_set_default_cos >> +@verbatim >> +/** >> + * Setup per-port default class-of-service >> + * >> + * @param pktio_in ingress port identifier. >> + * @param default_cos class-of-service set to all packets arriving >> + * at the 'pktio_in' ingress port, unless overridden by >> subsequent >> + * header-based filters. >> + * >> + * @return 0 on success negative error code on failure. >> + * >> + * >> + * @note This may replace the default queue per pktio. >> + */ >> + >> +int odp_pktio_set_default_cos(odp_pktio_t pktio_in, odp_cos_t >> default_cos); >> +@endverbatim >> + >> +This routine specifies a default class of service for a given pktio >> instance. >> +Incoming packets on the specified pktio are assigned to this class of >> service if no other pattern matching rule obtains. >> +@subsubsection pktio_set_error_cos odp_pktio_set_error_cos >> +@verbatim >> +/** >> + * Setup per-port error class-of-service >> + * >> + * @param pktio_in ingress port identifier. >> + * @param error_cos class-of-service set to all packets arriving >> + * at the 'pktio_in' ingress port that contain an error. >> + * >> + * @return 0 on success negative error code on failure. >> + */ >> + >> +int odp_pktio_set_error_cos(odp_pktio_t pktio_in, odp_cos_t error_cos); >> +@endverbatim >> + >> +This OPTIONAL function assigns a class-of-service used to handle packets >> containing various types of errors. The specific errors types include L2 >> FCS and optionally L3/L4 checksum errors, malformed headers, etc., >> depending on platform capabilities. >> +The specified error_cos MAY simply discard these packets or deliver them >> via a queue to the application for further processing. >> +@subsubsection pktio_set_skip odp_pktio_set_skip >> +@verbatim >> +/** >> + * Setup per-port header offset >> + * >> + * @param pktio_in ingress port identifier. >> + * @param offset is the number of bytes the classifier must skip. >> + * >> + * @return Success or ODP_FUNCTION_NOT_AVAILABLE >> + */ >> + >> +int odp_pktio_set_skip(odp_pktio_t pktio_in, size_t offset); >> +@endverbatim >> + >> +This OPTIONAL function applies to ports that carry an additional headers >> preceding the standard Ethernet header. Such headers are typically >> vendor-specific and thus the classifier is not required to parse such >> headers, but the size of a custom header is critical for the classifier to >> be able to parse standard protocol headers that normally follow. >> +@subsubsection cos_set_headroom odp_cos_set_headroom >> +@verbatim >> +/** >> + * Specify per-port buffer headroom >> + * >> + * @param pktio_in ingress port identifier. >> + * @param headroom number of bytes of space preceding packet data to >> reserve >> + * for use as headroom. Must not exceed the >> implementation >> + * defined ODP_PACKET_MAX_HEADROOM. >> + * >> + * @return Success or ODP_PARAMETER_ERROR, >> + * or ODP_FUNCTION_NOT_AVAILABLE >> + */ >> + >> +int odp_cos_set_headroom(odp_cos_t cos_id, size_t req_room); >> +@endverbatim >> + >> +This OPTIONAL routine specifies the number of bytes of headroom that >> should be reserved for each packet assigned to this class of service. >> +Each implementation defines an ODP_PACKET_MAX_HEADROOM limit that sets >> an upper bound on the size of the headroom that can be reserved for a >> packet. >> +@subsubsection cos_with_l2_priority odp_cos_with_l2_priority >> +@verbatim >> +/** >> + * Request to override per-port class of service >> + * based on Layer-2 priority field if present. >> + * >> + * @param pktio_in ingress port identifier. >> + * @param num_qos is the number of QoS levels, typically 8. >> + * @param qos_table are the values of the Layer-2 QoS header field. >> + * @param cos_table is the class-of-service assigned to each of the >> + * allowed Layer-2 QOS levels. >> + * @return 0 on success negative error code on failure. >> + */ >> + >> +int odp_cos_with_l2_priority(odp_pktio_t pktio_in, >> + size_t num_qos, >> + uint8_t qos_table[], /**< >> 'num_qos' elements */ >> + odp_cos_t cos_table[]); /**< >> 'num_qos' elements */ >> +@endverbatim >> + >> +This routine is used to assign classes of service based on the layer 2 >> (L2) priority associated with input packets received on the specified >> pktio_in. >> +For each of the values in qos_table[], the corresponding value in >> cos_table[] will be assigned. >> +@subsubsection cos_with_l3_dscp odp_cos_with_l3_dscp >> +@verbatim >> +/** >> + * >> + * @param pktio_in ingress port identifier. >> + * @param num_qos is the number of allowed Layer-3 QoS levels. >> + * @param qos_table are the values of the Layer-3 QoS header field. >> + * @param cos_table is the class-of-service assigned to each of the >> + * allowed Layer-3 QOS levels. >> + * @param l3_preference when true, Layer-3 QoS overrides L2 QoS when >> present. >> + * >> + * @return 0 on success negative error code on failure. >> + */ >> + >> +int odp_cos_with_l3_qos(odp_pktio_t pktio_in, >> + size_t num_qos, >> + uint8_t qos_table[], /**< 'num_qos' >> elements */ >> + odp_cos_t cos_table[], /**< 'num_qos' >> elements */ >> + odp_bool_t l3_preference); >> +@endverbatim >> + >> +This OPTIONAL routine is used to assign classes of service based on the >> layer 3 (L3) Differentiated Services (DS) designation. >> +This is the DSCP field of an IPv4 header or the first six bits of the >> Traffic Class of an IPv6 header. >> +For each of the values in qos_table[], the corresponding value in >> cos_table[] will be assigned. >> +The l3_preference flag is use to control whether the CoS assigned by >> this routine takes precedence over the CoS assigned by >> odp_cos_with_l2_priority() in the event that both apply to the same packet. >> + >> +@subsection pmrs Pattern Matching Rules >> +While the above routines permit class of service assignments to be made >> based on static criteria, the real power of classification is the ability >> to identify flows based on the variable contents of packet headers. >> +To do this ODP provides support for defining pattern matching rules >> (PMRs) that operate based on values contained in specified header fields. >> + >> +Associated with PMRs are enums that are used to specify standard packet >> header fields: >> +@subsubsection cos_hdr_flow_fields odp_cos_hdr_flow_fields_e >> +@verbatim >> +/** >> + * Packet header field enumeration >> + * for fields that may be used to calculate >> + * the flow signature, if present in a packet. >> + */ >> + >> +enum odp_cos_hdr_flow_fields_e { >> + ODP_COS_FHDR_IN_PKTIO, /**< Ingress port number */ >> + ODP_COS_FHDR_L2_SAP, /**< Ethernet Source MAC address */ >> + ODP_COS_FHDR_L2_DAP, /**< Ethernet Destination MAC >> address */ >> + ODP_COS_FHDR_L2_VID, /**< Ethernet VLAN ID */ >> + ODP_COS_FHDR_L3_FLOW /**< IPv6 flow_id */ >> + ODP_COS_FHDR_L3_SAP, /**< IP source address */ >> + ODP_COS_FHDR_L3_DAP, /**< IP destination address */ >> + ODP_COS_FHDR_L4_PROTO, /**< IP protocol (e.g. >> TCP/UDP/ICMP) */ >> + ODP_COS_FHDR_L4_SAP, /**< Transport source port */ >> + ODP_COS_FHDR_L4_DAP, /**< Transport destination port */ >> + ODP_COS_FHDR_IPSEC_SPI, /**< IPsec session identifier */ >> + ODP_COS_FHDR_LD_VNI, /**< NVGRE/VXLAN network >> identifier */ >> + ODP_COS_FHDR_USER /**< Application-specific header >> field(s) */ >> +}; >> +@endverbatim >> + >> +Conforming ODP implementations SHOULD implement efficient flow set >> management routines such as these: >> + >> +~~~~~{.c} >> +/** >> + * Set of header fields that take part in flow signature hash >> calculation: >> + * bit positions per 'odp_cos_hdr_flow_fields_e' enumeration. >> + * >> +typedef uint16_t odp_cos_flow_set_t; >> + >> + >> +/** >> + * Set a member of the flow signature fields data set >> + * >> +static inline odp_cos_flow_set_t >> +odp_cos_flow_set( odp_cos_flow_set_t set, >> + enum odp_cos_hdr_flow_fields_e field) >> +{ >> + return set | (1U << field); >> +} >> + >> + >> +/** >> + * Test a member of the flow signature fields data set >> + * >> +static inline bool >> +odp_cos_flow_is_set( odp_cos_flow_set_t set, >> + enum odp_cos_hdr_flow_fields_e field) >> +{ >> + return (set & (1U << field)) != 0; >> +} >> +~~~~~ >> + >> +These routines are intended to be used in support of the following flow >> signature APIs: >> + >> +@subsubsection cos_class_flow_sig odp_cos_class_flow_signature >> +@verbatim >> +/** >> + * Set up set of headers used to calculate a flow signature >> + * based on class-of-service. >> + * >> + * @param cos_id class of service instance identifier >> + * @param req_data_set requested data-set for flow signature calculation >> + * >> + * @return data-set that was successfully applied. All-zeros data set >> + * indicates a failure to assign any of the requested fields, or other >> + * error. >> + */ >> + >> +odp_cos_flow_set_t >> +odp_cos_class_flow_signature(odp_cos_t cos_id, >> + odp_cos_flow_set_t req_data_set); >> +@endverbatim >> + >> +This OPTIONAL routine associates a fow set with a class of service for >> flow signature calculation. >> + >> +@subsubsection cos_port_flow_sig odp_cos_port_flow_signature >> +@verbatim >> +/** >> + * Set up set of headers used to calculate a flow signature >> + * based on ingress port. >> + * >> + * @param pktio_in ingress port identifier. >> + * @param req_data_set requested data-set for flow signature calculation >> + * >> + * @return data-set that was successfully applied. An all-zeros data-set >> + * indicates a failure to assign any of the requested fields, or other >> + * error. >> + */ >> + >> +odp_cos_flow_set_t >> +odp_cos_port_flow_signature(odp_pktio_t pktio_in, >> + odp_cos_flow_set_t req_data_set); >> +@endverbatim >> + >> +@subsection pmr_routines Pattern Matching Rules Routines >> +The following data structures SHOULD be implemented to support the >> definition of pattern matching routines by conforming ODP implementations: >> + >> +~~~~~{.c} >> +/** >> + * PMR - Packet Matching Rule >> + * Up to 32 bit of ternary matching of one of the available header fields >> + * >> + >> + >> +#define ODP_PMR_INVAL ((odp_pmr_t)NULL) >> +typedef struct odp_pmr_s *odp_pmr_t; >> +~~~~~ >> + >> +@subsecion terms Terms >> +Terms are the elements of a PMR and are identified by the following enum: >> + >> +@verbatim >> +enum odp_pmr_term_e { >> + ODP_PMR_ETHTYPE_0, /**< Initial (outer) Ethertype only >> (*val=uint16_t)*/ >> + ODP_PMR_ETHTYPE_X, /**< Ethertype of most inner VLAN tag >> (*val=uint16_t)*/ >> + ODP_PMR_VLAN_ID_0, /**< First VLAN ID (outer) (*val=uint16_t) >> */ >> + ODP_PMR_VLAN_ID_X, /**< Last VLAN ID (inner) (*val=uint16_t) >> */ >> + ODP_PMR_DMAC, /**< destination MAC address >> (*val=uint64_t) */ >> + ODP_PMR_IPPROTO, /**< IP Protocol or IPv6 Next Header >> (*val=uint8_t) */ >> + ODP_PMR_UDP_DPORT, /**< Destination UDP port, implies >> IPPROTO=17 */ >> + ODP_PMR_TCP_DPORT, /**< Destination TCP port implies >> IPPROTO=6 */ >> + ODP_PMR_UDP_SPORT, /**< Source UDP Port (*val=uint16_t) */ >> + ODP_PMR_TCP_SPORT, /**< Source TCP port (*val=uint16_t) */ >> + ODP_PMR_SIP_ADDR, /**< Source IP address (uint32_t) */ >> + ODP_PMR_DIP_ADDR, /**< Destination IP address (uint32_t) */ >> + ODP_PMR_SIP6_ADDR, /**< Source IP address (uint8_t[16]) */ >> + ODP_PMR_DIP6_ADDR, /**< Destination IP address (uint8_t[16]) >> */ >> + ODP_PMR_IPSEC_SPI, /**< IPsec session >> identifier(*val=uint32_t) */ >> + ODP_PMR_LD_VNI, /**< NVGRE/VXLAN network identifier >> (*val=uint32_t) */ >> + >> + >> + /** Inner header may repeat above values with this offset */ >> + ODP_PMR_INNER_HDR_OFF=32 >> +}; >> +@endverbatim >> + >> +@subsubsection tunnel_considerations Tunnel Considerations >> +Note that PMRs may be extended to support tunnels and tenants (NVGRE, >> VXLAN) via the ODP_PMR_INNER_HDR_OFF enum. >> +This enum is intended to be used as an “adder” to a PMR to indicate that >> the term refers to an inner header. >> +For example, the term ODP_PMR_DMAC would refer to the destination MAC >> address of the packet if the packet is not a tunnel, or of the outer header >> (the tunnel) if the packet is a tunnel. >> +To refer to the inner (tenant) destination MAC, the term would be >> specified as ODP_PMR_INNER_HDR_OFF+ODP_PMR_DMAC. >> + >> +@subsection pmr_apis PMR APIs >> +The following APIs are provided to enable an ODP application to specify >> PMRs as a series of individual or cascaded terms: >> +@subsubsection pmr_create_match odp_pmr_create_match >> +@verbatim >> +/** >> + * Create a packet match rule with mask and value >> + * >> + * @param term is one value of the enumerated values supported >> + * @param val is the value to match against the packet header >> + * in native byte order. >> + * @param mask is the mask to indicate which bits of the header >> + * should be matched ('1') and which should be ignored >> ('0') >> + * @param val_sz size of the ‘val’ and ‘mask’ arguments, >> + * that must match the value size requirement of the >> + * specific ‘term’. >> + * >> + * @return a handle of the matching rule or ODP_PMR_INVAL on error >> + */ >> + >> +odp_pmr_t odp_pmr_create_match(enum odp_pmr_term_e term, >> + const void *val, const void *mask, size_t >> val_sz); >> +@endverbatim >> + >> +This routine creates a PMR that matches a single value to a term. >> + >> +@subsubsection pmr_create_range odp_pmr_create_range >> +@verbatim >> +/** >> + * Create a packet match rule with value range >> + * >> + * @param term is one value of the enumerated values supported >> + * @param val1 is the lower bound of the header field range. >> + * @param val2 is the upper bound of the header field range. >> + * @param val_sz size of the ‘val1’ and ‘val2’ arguments, >> + * that must match the value size requirement of the >> + * specific ‘term’. >> + * >> + * @return a handle of the matching rule or ODP_PMR_INVAL on error >> + * @note: Range is inclusive [val1..val2]. >> + */ >> + >> +odp_pmr_t odp_pmr_create_range(enum odp_pmr_term_e term, >> + const void *val1, const void >> *val2, size_t val_sz); >> +@endverbatim >> + >> +This routine creates a PMR that matches an inclusive range of values to >> a term. >> + >> +@subsubsection pmr_destroy odp_pmr_destroy >> +@verbatim >> +/** >> + * Invalidate a packet match rule and vacate its resources >> + * >> + * @param pmr_id the identifier of the PMR to be destroyed >> + * >> + * @return Success or ODP_PMR_INVALID if the specified pmr_id not found. >> + */ >> + >> +int odp_pmr_destroy(odp_omr_t pmr_id); >> +@endverbatim >> + >> +This routine destroys a previously created PMR. >> +If the PMR is currently associated with an active class of service it is >> unpredictable at which point the match defined by the PMR is deactivated in >> terms of packet flow. >> +However, implementations MUST ensure that a PMR is either matched or not >> matched in its entirety such that dynamic changes to PMRs do not result in >> partial matches. >> + >> +@subsubsection pktio_pmr_cos odp_pktio_pmr_cos >> +@verbatim >> +/** >> + * Apply a PMR to a pktio to assign a CoS. >> + * >> + * @param pmr_id the id of the PMR to be activated >> + * @param src_pktio the pktio to which this PMR is to be applied >> + * @param dst_cos the CoS to be assigned by this PMR >> + * >> + * @return Success or ODP_PARAMETER_ERROR >> + */ >> + >> +int odp_pktio_pmr_cos(odp_pmr_t pmr_id, odp_pktio_t src_pktio, odp_cos_t >> dst_cos); >> +@endverbatim >> + >> +This routine links a pktio to a corresponding class of service via a >> specified PMR. >> +Any packet received on the specified src_pktio that matches the >> specified pmr_id will be assigned to the specified dst_cos. >> +If multiple PMRs match the implementation MAY define an inherent >> precedence or it MAY be unpredictable as to which PMR will determine the >> assigned CoS. >> +For this reason applications SHOULD NOT be written to use conflicting or >> ambiguous PMR definitions. >> + >> +@subsubsection cos_pmr_cos odp_cos_pmr_cos >> +@verbatim >> +/** >> + * Cascade a PMR to refine packets from one CoS to another. >> + * >> + * @param pmr_id the id of the PMR to be activated >> + * @param src_cos the id of the CoS to be filtered >> + * @param dst_cos the id of the CoS to be assigned to packets filtered >> + * from src_cos that match pmr_id. >> + * >> + * @return Success or ODP_PARAMETER_ERROR if an input is in error >> + * or ODP_IMPLEMENTATION_LIMIT if cascade depth is >> exceeded >> + */ >> + >> +int odp_cos_pmr_cos(odp_pmr_t pmr_id, odp_cos_t src_cos, odp_cos_t >> dst_cos); >> +@endverbatim >> + >> +This routine is used to cascade PMRs by passing packets assigned to the >> src_cos through another PMR. >> +Those matching are reassigned to the specified dst_cos. >> +Note that this process can be repeated to an implementation-defined >> maximum supported cascade depth. >> +When cascades are defined, the actual class of service assigned to a >> packet is the result of the longest chain of PMRs that can be matched >> against the packet. >> + >> +For example, suppose the following sequence of PMRs is in effect: >> + >> +@verbatim >> +odp_pktio_pmr_cos(pmr_idA, pktio_id, cos_idA); >> +odp_cos_pmr_cos(pmr_idB, cos_idA, cos_idB); >> +odp_cos_pmr_cos(pmr_idC, cos_idB, cos_idC); >> +odp_cos_pmr_cos(pmr_idD, cos_idC, cos_idD); >> +@endverbatim >> + >> +If a packet arrives on pktio_id that matches pmr_idA it is assigned to >> cos_idA. >> +But since it is now on cos_idA it is further filtered by pmr_idB and if >> it matches is reassigned to cos_idB. >> +This process continues until no further more specific match is found to >> determine the final CoS that the packet receives. >> + >> +Note that given this rule set a packet that matched pmr_idA and pmr_idC >> it would be assigned to cos_idA because the rule that can assign packets to >> pmr_idC is only applicable to packets that are assigned to cos_idB, not >> cos_idA. >> + >> +Using cascaded PMRs it is possible to build quite sophisticated filters >> (up to the implementation limits supported by a given platform). >> +For example, one could add additional rules to the above set: >> + >> +@verbatim >> +odp_cos_pmr_cos(pmr_idAC, cos_idA, cos_idC); >> +odp_cos_pmr_cos(pmr_idAD, cos_idA, cos_idD); >> +@endverbatim >> + >> +To cover cases where some packets on cos_idA should be further sorted to >> cos_idB while others should be sorted directly to cos_idC or cos_idD. >> +Again it is the application’s responsibility to ensure that the cascades >> remain unambiguous and that loops be avoided (e.g., having rules that >> bounce packets between cos_idA and cos_idB endlessly). >> + >> +@subsection pmr_stats PMR Statistics >> +Conforming ODP implementations SHOULD maintain statistics regarding PMRs >> and provide the following routines for retrieving them: >> + >> +@subsubsection pmr_match_count odp_pmr_match_count >> +@verbatim >> +/** >> + * Retrieve packet matcher statistics >> + * >> + * @param pmr_id the id of the PMR from which to retrieve the count >> + * >> + * @return The current number of matches for a given matcher instance. >> + */ >> + >> +signed long odp_pmr_match_count(odp_pmr_t pmr_id); >> +@endverbatim >> + >> +@subsubsection pmr_terms_cap odp_pmr_terms_cap >> +@verbatim >> +/** >> + * Inquire about matching terms supported by the classifier >> + * >> + * @return A mask one bit per enumerated term, one for each of >> op_pmr_term_e >> + */ >> + >> +unsigned long long odp_pmr_terms_cap(void); >> +@endverbatim >> + >> +@subsubsection pmr_terms_avail odp_pmr_terms_avail >> +@verbatim >> +/** >> + * Return the number of packet matching terms available for use >> + * >> + * @return A number of packet matcher resources available for use. >> + */ >> + >> +unsigned odp_pmr_terms_avail(void); >> +@endverbatim >> + >> +@subsection pmr_composite_rules Pattern Matching Composite Routines >> +As a shorthand, applications MAY express pattern matching rules using a >> table rather than constructing them term-by-term. >> +ODP implementations MUST support both methods of rule specification but >> MAY have implementation-specific restrictions on the complexity of >> table-based rules they support. >> +Note that some implementations MAY be able to implement tables directly >> while others MAY choose to implement tables by internally generating the >> equivalent set of term generating calls. >> + >> +@subsubsection pmr_table_structure PMR Table Structure >> +@verbatim >> +/** >> + * Following structure is used to define composite packet matching rules >> + * in the form of an array of individual match or range rules. >> + * The underlying platform may not support all or any specific >> combination >> + * of value match or range rules, and the application should take care >> + * of inspecting the return value when installing such rules, and perform >> + * appropriate fallback action. >> + */ >> + >> +typedef struct odp_pmr_match_t { >> + enum odp_pmr_match_type_e { >> + ODP_PMR_MASK, /**< Match a masked set of >> bits */ >> + ODP_PMR_RANGE, /**< Match an integer range >> */ >> + } match_type; >> + union { >> + struct { >> + enum odp_pmr_term_e term; >> + const void *val; >> + const void *mask; >> + unsigned int val_sz; >> + } mask; /**< Match a masked set of bits */ >> + struct { >> + enum odp_pmr_term_e term; >> + const void *val1; >> + const void *val2; >> + unsigned int val_sz; >> + } range; /**< Match an integer range */ >> + }; >> +} odp_pmr_match_t; >> + >> + >> +/** An opaque handle to a composite packet match rule-set */ >> +typedef struct odp_pmr_set_s *odp_pmr_set_t; >> +@endverbatim; >> + >> +The above structure is used with the following APIs to implement >> table-based PMRs: >> + >> +@subsubsection pmr_match_set_create odp_pmr_match_set_create >> +@verbatim >> +/** >> + * Create a composite packet match rule >> + * >> + * @param num_terms is the number of terms in the match rule. >> + * @param terms is an array of num_terms entries, one entry per >> + * term desired. >> + * @param dst_cos is the class-of-service to be assigned to packets >> + * that match the compound rule-set, or a subset >> thereof, >> + * if partly applied. >> + * @param pmr_set_id is the returned handle to the composite rule set. >> + * >> + * @return The return value may be a negative number indicating a general >> + * error, or a positive number indicating the number of ‘terms’ elements >> that >> + * have been successfully mapped to the underlying platform >> classification engine, >> + * and may be in the range from 1 to ‘num_terms’. >> + */ >> + >> +int odp_pmr_match_set_create(int num_terms, odp_pmr_match_t *terms, >> + odp_pmr_set_t *pmr_set_id); >> +@endverbatim >> + >> +This routine is used to create a PMR match set. >> + It is the equivalent to a cascade of PMRs except that there are no >> “intermediate” classes of service defined. >> +Instead, the entire match set either matches or does not match as a >> single entity. >> + >> +@subsubsection pmr_match_set_destroy odp_pmr_match_set_destroy >> +@verbatim >> +/** >> + * Function to delete a composite packet match rule set >> + * >> + * All of the resources pertaining to the match set associated with the >> + * class-of-service will be released, but the class-of-service will >> + * remain intact. >> + * >> + * @param pmr_set_id a composite rule-set handle returned when created. >> + * >> + * @note Depending on the implementation details, destroying a rule-set >> + * may not guarantee the availability of hardware resources to create the >> + * same or essentially similar rule-set. >> + */ >> + >> +int odp_pmr_match_set_destroy(odp_pmr_set_t pmr_set_id); >> +@endverbatim >> + >> +This routine destroys a PMR match set previously created by >> odp_pmr_match_set_create(). >> + >> +@subsubsection pktio_pmr_match_set_cos odp_pktio_pmr_match_set_cos >> +@verbatim >> +/** >> + * Apply a PMR Match Set to a pktio to assign a CoS. >> + * >> + * @param pmr_set_id the id of the PMR match set to be activated >> + * @param src_pktio the pktio to which this PMR match set is to be >> applied >> + * @param dst_cos the CoS to be assigned by this PMR match set >> + * >> + * @return Success or ODP_PARAMETER_ERROR >> + */ >> + >> +int odp_pktio_pmr_match_set_cos(odp_pmr_t pmr_id, odp_pktio_t src_pktio, >> + odp_cos_t dst_cos); >> +@endverbatim >> + >> +This routine is the same as odp_pktio_pmr_cos() except that it operates >> on PMR match sets rather than individual PMRs. >> + >> +@section items_pending Items pending resolution >> +- Revise ‘odp_packet_io.h’ API with respect of default input queue per >> ‘pktio’ instance. >> +- Revise ‘odp_queue.h’ API to support an arbitrary priority range, >> typically 8 priority levels with numeric priority values are >> platform-specific. >> +- Add specific packet meta data fields to go into packet buffer which >> contain all meta data fields parsed and generated by the classifier, for >> later application use. >> + >> +@section implementation_notes Implementation Notes >> +The following sections are not part of the specification, but shed light >> into the intent of the specification in several areas, describing some >> specific implementation approaches of these aspects. >> + >> +@subsection supporting_multi_pools Supporting multiple buffer pools >> +The support of multiple buffer pools for containing packet buffers is >> optional, and may not be supported by some platforms. >> +The importance of this feature stems from the need of protecting a >> networking application in the event of a congestion, or an attempted denial >> of service attack. >> +Separating different classes of service to dedicated buffer pools allows >> the system to limit the memory resources that may be consumed by a >> particular type of traffic, thereby reserving buffer resources for other >> classes of traffic. >> + >> +In a software implementation, a packet would already be stored in memory >> when the classifier is invoked, and so it seems the classifier is unable to >> insert itself into the process of selecting a buffer pool. >> +For obvious reasons the copying of a packet into a new buffer allocated >> from a different pool by the classifier is not a desirable solution. >> + >> +The recommended solution is to implement buffer pools in the form of >> buffer counters, while the actual buffers all belong to a single free list >> when not used to store a packet. >> +In such an implementation, the classifier will be able to associate a >> packet already occupying a buffer to a different pool than the default by >> incrementing the buffer counter of the newly selected pool, and >> decrementing the counter representing the default pool. >> +If however the selected pool counter has already reached a certain >> limit, the classifier would be able to e.g discard the packet instead of >> incrementing the destination pool counter, and thereby enforce the >> desirable semantics of distinct buffer pools per class of service. >> + >> +Other possible action that may be taken in response to running out of >> buffers or coming too low on buffers include back-pressure and >> random-early-detect with a discard probability inversely proportional to >> the number of free buffers in a pool. >> +A related implementation topic is the ability to begin dropping some >> packets before a buffer pool is entirely exhausted. >> +This is typically referred to as <em>Random Early Detect</em> (or “RED”). >> +This is deemed to be a feature of the buffer pool implementation on a >> given platform, where in addition to a hard limit on the number of buffers >> that can be allocated to a pool, there can also be an option discard >> packets with a probability the increases as the number of outstanding >> buffers approaches that hard limit. >> + >> +@subsection resolving_gaps Resolving gaps between the API and hardware >> capabilities >> +On platforms that support hardware packet accelerators, it is possible >> that the packet parsing and classification functionality is sufficient to >> address only a portion of the functionality specified within this document. >> +This gap may be potentially bridged by augmenting the hardware >> classification capabilities with a software logic implemented as part of >> the platform. >> +In that case, the platform will have to curve out a fraction of the >> processing resources and dedicate those to the software classification >> logic, which would be invoked for packets that the hardware platform was >> unable to classify completely. >> +At the time of this writing, it is believed however that the >> performance penalty that will be incurred as a result of software >> augmentation is unjustified for most application, i.e. >> +it is preferred to lose the precision of packet prioritization while >> maintaining full hardware packet processing speed. >> + >> +@subsection loopback_case The case for loopback ports, and some of their >> uses >> +In some applications, it may be desirable to be able to run a single >> packet through the classifier more than once. >> +For example, an encrypted IPsec packet is received from a physical port. >> +The encrypted packet is assigned a class of service based on its outer >> unencrypted header fields. >> +Later, processing the packet entails decrypting the payload of the >> packet, authenticating it, and removing the original outer headers, which >> reveals a new set of protocol headers which need to be used to re-classify >> the packet, and assign it a new priority and buffer pool. >> +An elegant solution for this use case would be to take advantage of >> “loopback” logical ports that may be implemented in certain platforms, by >> transmitting decapsulated packet into a loop-back port. >> +The same packet then is received from a loop-back port and is examined >> by the classifier in accordance to the rules assigned to the loopback >> odp_pktio logical port instance. >> +Similar mechanism may be applied to tunnel termination processing, >> fragment reassembly et al. >> + >> +@section related_topics Related Topics >> +The following section discusses aspects of the ODP API that are not >> integral to the classifier, which only applies to ingress preprocessing. >> +This section covers miscellaneous aspects of the API that need to be >> addressed, and are related to packet buffer processing and egress >> post-processing. >> +Additional packet buffer manipulation APIs >> +The need for these following calls are made evident by the need to >> encapsulate, i.e., remove some headers and add other, thereby changing the >> size of the headers of a packet during processing. >> + >> +@subsection initial_headroom Configuring initial packet buffer headroom >> +The following function is provided to configure the pktio receive >> mechanism to (optionally)reserve some headroom between start of the first >> buffer to the first byte of the first packet data byte, which subsequently >> could be used to increase the header size “in-place”, without allocating >> additional gather list elements. >> +If the request is granted, at least <req_bytes> bytes will be reserved >> in the front of the packet data: >> +@verbatim >> +int odp_pktio_set_headroom(odp_pktio_t port_id, unsigned req_bytes); >> +@endverbatim >> +The return value should be negative if the request can not be satisfied, >> or positive otherwise indicating the actual minimum headroom reserved. >> +Note that the implementation may reserve more than the requested amount >> of headroom, and hence on platforms that are unable to support per-port (or >> per CoS) headroom configuration, a system-wide headroom configuration may >> be set to the largest of all such requests, and thus satisfy the >> requirement. >> +In addition to the above per-port headroom configuration call, there >> should be an optional, per-CoS call that allows the reservation of >> different amounts of packet buffer headroom for packets that match certain >> criteria: for example, the following call allows the application to request >> that only packets that are expected to be encapsulated in a tunnel, be >> augmented with a large headroom amount, while packets that are received >> from a tunnel, and are IP fragments, be assigned a different headroom >> requirement (see definition for odp_cos_set_headroom() above. >> +Egress packet scheduling, prioritization and ordering >> + >> + >> +Open Issues >> +* Parallel matching rules relative precedence. >> +* Specify application-defined header field declaration APIs. >> +* Review RFC 4301 for match requirements for IPsec SA, consider the use >> of L4 port ranges instead of or in addition to value & mask matching >> criteria. >> +* Consider the type of packet checks should route a packet through the >> error CoS: L2 is a safe choice, but L3/L4 checksum or other exceptions >> deserve consideration. >> +Usage Examples >> +Following is a simple sample configuration using the API elements >> described above. >> +TBD. >> + >> +*/ >> diff --git a/images/classification_flow.png b/images/classification_flow. >> png >> new file mode 100644 >> index 0000000000000000000000000000000000000000.. >> 2d94ff64ec772aa97f3687181c121fb91d671889 >> GIT binary patch >> literal 35193 >> zcma&OWmJ@5*EWozA_z!INewLmA}K97Gy;NxLy9zr4Bbli2uMkfgtYWf2HgxXG*ZGa >> zgv5Xh^<Ja*{XFlx*7xK4p=-f)_KtJ!<Jc!rdOB)kB#a~k1O#O2U}b#*f~#oYpYqKc >> zz;8-b5z+(%!365cj}5(Nwz6)gJ)Q8WJU*jTQ|5XLQ`Rcd010>Gev54CSnf<#e!~?l >> zm-e2&T9oTLGmW9>vu+K6dxhiAG~PT34<_c`$TF;brp~1?qA$kT`NC;0x&deSBQ`#o >> z?;h#t&Z3;7u;bB35%-}1x%I7pp;Nix<AH%kr&~3)<*k#ra@Qb?X*Ll7!FirsRY{05 >> z$v`E=EbB8zi=<*RV3`D9rXLWuz!}?Nhs%Qg%dL48&7FPM6U^?Xp$hXe=F9h&Cp><- >> za%)M>@fPMxQxu;W@tu$g))tlEp6t^I^Xb!(r1qnR%kG8f>Qf09tgiHmyDLLp8%I^1 >> z;Uj!oP6nS3UK(G`S~n(#G^o}qWKZ=YF6;W+u_2V3nXOuOxABb>Ku>N)%AamB-80H< >> z7Ppx}mFbylO%US0P%#L7!!-?GK7M)=ig_ZxcdWZ$ZeVbbol1Og1>Y4+lj(rjB(GSk >> z9jv?q6EtnBpAh7P^a|j=5vb=sQN*?!w3|G=IzLlVl(mGKoV*y~n7xJn$Qh|Zh3?;J >> zYQLv~tCLuCX+?`P(&9g`QIax_S2_QNsf7#AxJ53gW#hDYeT^^vu)bkA*r=yNw94;n >> z356Pw!!Byp&1yjbN*2^acGfaTsjEQmuLLEP<)x=T4@HLKwGV%BAa$K&dM_s@7Ud4T >> z#>g5%jRakB)j#5IRWxxA&1)a~q|a-wW%0z~m4<-Cs!Vy!re;0G5FP|w?M6f$zL_o3 >> zk>+Um)xSQ#Ix<v<J4=oc#L7Q<<y1twB_<}edqbY?#^l%Szr9Z$JW<rYDq{Y)w2H<? >> z<&oFE@tI{e^R7hp8FA1>T*J{lWcxHUD`+y*{d{t8I4p-1>aD~(?i|@4``lfrrd$5x >> ztaNE{zY!z7ioU!+kf~6kFSqC9x7@9s6!XDtcj_cgICLbd$0D(G?{bnF*YFF@6?&zi >> z&`15T^3V958goEw&>qV6#4!-(%{zkG#N77jDgZ&OOoex;j&P=nVuw?fc|8q_m@c^M >> z?8L7GskL#vkBh!2*`@W>tVu6_FD#)7C3;=wg0(sZ>WB*xRc8?0`28+KPu`;M?%&zG >> zU}lkjCid2~aQY1>MM_`qckLeQn0aLc?M5k!e;MjEoiSWuv0CQr>^5s){;cg`R(H^9 >> z=<=2vqt?}EE@U|$Q7bo#88_)GmIf9A0ywdaYFeb_Gxf4jOc9T)Z;Q;PFT`pt8X8vY >> z%{|mynFck6Yd+{tVmXg(*_6up4o0r3Cv;)>WqNL6OQ0tN1c^7euhlQc2sK{~w`?`7 >> zNsWRX(Frt_vcXtrYmCzPQHXO03$!y2Q)Jd}3}4siF5VsY*<EZD^V~ZNQ#ZdC1#YnU >> z^g0iR`>`NK%>{T1Ws$L)5nR8#NPk2-_H8{BOl@eU&_&$w*!1ujF9)|fpKQLqQmkm0 >> zpWiG23tU3I3csaVu;}>1B6>;v@(^wNK?~k5nm&5WEc~Y@s1QR$5L_F17)5hkq1Uds >> zynH*k+mB5cZKC|y+tE3a1|sQjy(IIoDhT4XHLZ*wbA<i$YF9#~X}5I0=Lz`Zw&dk- >> z`~>0^r0uqT;zHu_jZ4HICj*!p;Vp(L0*zsqQurBbm^lpynPP9Vf1NIZ1*_RE+??qp >> zYf92}Fm&nAP?c&R=V1cq*Nh5qjH~yuAq*DDk5t#6a;I6@c)=PLV}xTjrYyTX?qRLW >> zt<Lj!f|xIK)mYsJYUaX#qkWk{i6;0|RNr=CRM+Z%kcfE}Wy|!z>Y#A;SmJVXZA7!| >> z<Hjh8=1YEi-Pgkd>UR1-qg@rRyH7vR6VDsyN^_i%N1YYh$_mz4iiVumNmTu2w{d#T >> z`YdzkIDI&KqU4ezr|h8__u)-IuVCZ^5qd_S^puY!xMc?mqZFMX^F-e+#bgTi?@~?O >> zNEw@6*P21idO5x+Sa}<T%yDM|>>R{QfN-0#d>8mhW_|Sj{S-l@^JY&*Mg~!|UfKTV >> z2;q}fXDIv)D=yF4>owiK_W4>yrv@K=%)c8|uX+XUwnbB*bL3XE7PqcS72dRwdRTRy >> z^Jlf__|TJ#<aBfy;_?`9e{e#D+gGb`#WT5aOqEarPlmDeGG41{mvPSAhAAjde?NFf >> z@n5rK%8*?D*7}Mai%;Ht?&<mhzUdEA*ITtneX5^r*{JiNGxj_E?z3DcAW)VfIQ%}P >> z(CEN3@cJFcC!R7+H@qX0S#6pO?ci@a_N_;CbF25U>MjEJMkJJABj!nnJ$u38>yDo~ >> z&W4=qC0d+>(;G!E3g<TWKl0BnSw@g;ik=Vr`l_0od*Akm3t4wFwGHs=6lj9P+ay<> >> zEbKzeVwui4zt$6p^H<@hQaFIa!++JIQ6&=ZWSUZyUlX^fw7;)3&5i_|Iv|h42f$|n >> zJz-FQsLB=m8VG`4Mjfg~U&VL(yD0oF{z`!YjsFZB6agE)Gs#OnVD!JOdh@RXL*c*v >> zZH%83KXxP(-xc5Z{~7E5&o2O#3<d*-@;|fTsL)D$B!7P<;MDx@8t~)(cUm?i(CKe4 >> ze5b$D!s3B3f|F|){=Zg7J0!``XroC&fj;;++}ov>{I4^;WRjM=XV2y)<{X<@zea=Z >> zN7ds-hb6_MLH{KB^{ZV~z%=DMBG#6`J+HH;x8*(<<TfipSg?gmE_;r^6`9fYnz@ts >> z@5+L`izTkkUjE_fhF)wn(VfzuzW_JF$q#uMs~j{@s($RhNh4W_or5}vc|0|OkGV(I >> zoLS$DkFs9t-aUR;{U0d=gM=#!3x9nTW|ca!sHjSnZuiOw^8P_d^wxS!B}Wwo2n0Cx >> z+&@~S<>dk9!kbj96$-`!2&)l7_DiGJbJwDQ=m~#XpR5%j0q15mzOV<W3hn+wXAj<K >> zvW=LN1;!Uf;-zLr|AUYT<1uU=i*?puHXe2+Gvi*AaQ=QEDD5<oOOACf%Jx*{9dUGY >> zG$#)<Q=aCmzx@vSFxf4$nrWF1eM<dC0P)!NdVObqRy#drcBW-uxd}G6@U;KhScU!V >> zF2;EJkG(l&#%eyCvqzwDgYM+7;-l*iJeAy(aXZ&uM0nnsL)(b6PY3;4ANKM4)lf0h >> zY}X61WU(%D9a}lmyjaiKsza|jI65wp2M+zbQ>pQ4lSR)??c?#3ig_lQZl&c@71O&m >> zWmLdHR*+f{Os{!l&b+JUX0I*SqTGzzf9oqxxBo9}|3z)d%4~z+Y4GWzpynf?9dFl} >> z*nVj_QKvs<?w4M`a^5TBYI8%{nIij!`qy8~pR68K{z_S-?{{txFyH2{3A6vL**I8g >> z0IxjTlke~~w^!in@(~_rZKpt&P;M+4?ACB=&7Jlbv@fy5+i%HjZdf0Qcq-{D<Nhe* >> zw$t70yG{RRMa=ov!_8y^5#dHGw!yuW1JGW25VO^?%f!TCdeBU7E7Rpv0J2-nE2F+D >> z(Uc5kCJ0-gxOteGLgGBA%Ua}i=Ue@b|9PU``W^#_85YluWWP}>IS$-TE97#g`b5Vr >> zu&x~#tK*g-J&ey)0iOEty2FNhWowk`^VR1kBi0doLWj~UxAgHwO$s9?%&o6KlAE9& >> zs$0(JL)Avq&3R1p$3Kidgmjah>#f&AoJD)6I-1hE+LBG%e3fxxgAHMCr37mpbyc>W >> z96cM3vD`Ti1M7-nJ+Ro*<e}v1x<7?HOLZO|mz~bWty_XgU}N(2*JAQy)uKl*YPmw@ >> zJF~KHs*U2n-BrnqAlW8fYE#KPs}}-o5M11Jy>`_eBd8V5Tlu+?CPuUaIR(T`&el9c >> zZYhz6jRst1Y0EUR&-S-L`VR_2(4RRb=vEx|woQ$LGQ~r{LNVamDji<N@(N1#VSEWv >> zajghtDPc6dF#79MmYHLl^&{u2Ff%#Wki{^;O<?Z@37>b`t`}$%fU>%DtZms;;MRvv >> zzD{Xp0<MfKtBd?^lYG~2cGSV(Zo4huzm0<6>P+z~kx)jdIA#hoy(+89;@eSf0$BX$ >> z6#F;O`IqT~Gr$$_{W(jJ!DcGkJRP}&Va{!i=XT9s_r|&u(xe=?wusHGHONa7V(!NA >> z1bvs^J*#B7x2v1uR#+KlZ6y1LK7fV|$<S~GrlV9;O-N{ese)5e3mjneI-(<yYUpsz >> z%i$|je}K8%;18-+uB|XFTqI&%_<37;=g(W7yEEMiIdzXVSbg_VODDq_uU-B?5?uU3 >> z<N!|>I~xox${U*s38}3GLGmTzK(hC`OeN&$qPG>j4C)mZFUlWIo_NUNcopCxF7BA8 >> zmEfR^a2;!+8%V3D!@oY~va9?8i1|Q=^qOB6>iz33w=|iTPVr8%D96W>elu@(3UPq_ >> z?a>;9fgJy#3koo5l&x3Pn%e~ukF8UTcxb-KdNPs8`c!?Xz}#2O%AguF;<v|fjjr6K >> z13fa|9YY_s-e`I6oEB~Lx}R|8L*~WhMqTNPtyA3M_GKk%?Fs4x8~S){B~Ztv7&CTR >> zboryr_R<TI)TfEsXwzlA9y=d9n<xisO9z&{8>X5;Oo&!cMqC*2_5ezMk|fk9NmC`2 >> z&{4RMsvuXr2Y;ZYyzG8ZX+|D~2t9qG#!4NH<D-Kx`W8F4)YBrPQIi-Tu)TIU*K7U= >> z8iy-uGsqe9A!7Ae%}b;_AK%02WGQuT_(4C?))DT(G0k&n{>()y?mnB?=C;|3%jmGn >> zvksx~oUu#C;ZMEcX41CE)bC%$u>D)B-!FCxbRLPxr1s6~cvEmv7?=<L)4319U1mWY >> zKRG1QhGX+e*pNgZB-dMrZe3_~hf~FCx+s&@-EhCM6ZFc`aeuRm{EAsZ%lZ-HT9l{k >> zz$CvjUA^k{GMRBH)rCJHU2sfLjV|#%CAu}Nc}hK4W)Ek&5abtIVZPv{?LUWu(anD# >> zdV6jEWi;QZP3(Lvu>zcKZbYm{MVqB?w<0iiHaM~qIRy#PAqoMrR)M`_e~SA>52a|r >> z`<a|NiyZeq&bmLp4Xt;4(Qa?rfLeRx74}8iGBK?V1j!LnhGR^eJn5p`ed@W!A;QcN >> zU=&=5I?K*n;&>+W`>y-tv7H?mF!#2|KE<Zbu>i>!GGKA`<&&gkN7pvl%2OrXCaByj >> zH_gQ)%GN`7S{xF|wMI<id{v(V>Ctk7kIZRORAIKe*;hH9KH*a}E7eB5KVhb=T*Rjz >> zA2_w)n#r%EjRj$5#^$@y`FWYsD_;b}>(=AK^EMdE^gfJ0YF8^Bz?Ca8-aeD39LSnZ >> zxMmp!=j^S07c?&?)?*_jh`P*PmUAF=ZmJCkA6pk<+L#g`$~ax+cc9v39NSLH@z<|K >> zEY2G<4Ng|<3uAAcQyxV^d2dvHQirP%Ni}fvG~{t0{hYTVt|4SX>z%5hCKca=$v}`i >> zL6h;4kVg)aw-(G{f|a$(wcpKF<C0JiGen~@LMGDXQ70GD!@qt%(SG@fB`84A;?%zT >> zQ_Tzl@s5O#N!sP?n^~yuf_1}=y6iV3WAqC%XqM)!K41%LX&8Pn`yge+>}@t=-rOI1 >> zI`k1`V+(qR6mr!d;jPVRBE2lRQCVr=W`Ktl@I}T6$08Q*wBX~x8}0nGIPU!8Y}Cd_ >> zoPE253aB+x!Arz-N-g}El-4bQrwJ(0#SbR1im)69)Tp~=<E-BMi)iRM3)!dYy&bnm >> zXkCEBs_GK2$swAhptB|{5^D3N_b9-3UW)gg7Cgc_bdV{5c5y5GISF<{rI=3C6VrwY >> zm7INcFTtZW4zn;JTYmIO?~Vk&fg#+HG$H<*gF9o+cA*uJD0SIonJ(tFb?D$eF*!Al >> z1*~VACV4-S-LlPpo^<%gPb)}5R0*!s84V?d&Q47vI`hUt2G-<24TSk-sSP^2eEgvu >> z4HuJ&_Xp3mtt2Gc46-GLU&fEHl@4`7I14e-YX(JO3xv!??w^$5wzM115IGreZ8%2q >> zlfk%!ZO$?{dDcmYs=#-!kaMdN+>qoeo%lJfw0zS&15O7*im^2-s-a2)*`=07nnX7+ >> zLDex<w<yu~sIm4^sU1->#rk3nS95pE3}Hi8JNKUnDYoYNR)077J{tgGd0j_6yWsSJ >> zr?v2?q(0ZpFlctXG$5>bLmJxBWf8Z_*zQ5z06~jLscwTSG4ypR*eeKbi8Jl)DU~(c >> ztOH4wL5{oEl_RZ5u2VyhpsoyvSziFTtw`0%vzrn}^pxWbUEnjJ<bblm@WS?qKVnn% >> zRUd2z#Wf_{-(vNr)!fA!s-0ULeoxlWwSM5-rkc{UR?Fo;_6s7pTDT;gn`^rzg}*E* >> zpapw?U8g>)U!Nc-UtGH~_R~dzz0Vk~rX3KD<UnMLW2H^>HoL|(6M?*R`=#R5u57h{ >> zglmXx3<dfbaj5S%EGXiGbiK*riX)d@2>;CXl)db1hF1T|LMpf+$E!wax=LiWVr%)e >> zSNrw2^G(9J*Uyb#&L%QP1^SgE1c~2BC?(4ktU%*3BD+(b6T`?$sL)MXWL?pkVv`$! >> z0iIuL7K>Dwuy0Dgm5!>1+L}(6@Ae%1fiOpIU)%bc)iG|{Dtg4{`Ask5%{}X0TJ*VC >> zU8WPYsl<*{#V)dG%SZF6kc-T@g4X&-)4lRm5HiWuApwhxVE&EJ-@EfdX!6@yc!k}W >> z2hWbfX~qEJxYlgUz(u<4h#OhXAvnW5*IT&|6W+Zcrad(8=%#|m-a@;g99@1myeP^F >> zL?wAzA@e?~Am+<DGUhEG_?Eru+mEzA5?pc?QE3FpZibBcY;m}rHkyzvlDPPM-{NpU >> z&2>*wp##drb#uY2R8!ooJeBG%s-ZwI_=5tC7K@9*BP(87S!ELjsEhHKF1@%PN9rFs >> z8iy!p2N?aJv0XmIwmviSBh?Gah-@<(CcgsR*+co>S;6%T>4kMF^ilS}9fMuD`D2hD >> zJsUFQb?V*1E(fNpI}vu=PIO^dZsa`09YA(%a@MSM>Trx#7KcN9jd}_ASZlpme>dT* >> zby0ppE-JB`ZT2#j3$^uOKN5O>8w3fEbVi8&l4=npXYn``KGg~kz5R0Q1K&qw>*Diy >> z?L!OMBj1ct>+zu8D@ATSw9~Co&;V|Pts-FNA3yvy-iYpU@cHJUb3UI6PLNv~OOkAL >> z@gfv7Ql(dxGW-ZmRcM%vYtF{DDI;2bxs+uvwdF6;Rys>FplW}2iVxAmK>HPkR||}v >> zmlPP82qql9EUC%JV7&Y#ZOn$Ou8ltItzALR;8V0@y;KEpc0rAhN7lL44NNRnbe1#o >> zLG$z;t*im>9qGJ*9NIm~zL}#c5H;oz(7+^Kw1_nkgt;*LKXJBLnRUJ)p)AFQJfK07 >> z$h&z-82+eFs9SwH%!R}<h<VTUU?X+1-T4Q$K{TCN)2bCi1CTBKD<Fv0t&5@nT>Hm~ >> zO--#VOLSF=8H0q#_o_5pac}J!@`!;%OxR=Om(80AKfr%xK2_&-^%V8*=t^#8fAA(4 >> zEhb$Es2Z(lMOY?d&33+Xb7?g*X6;+5T$Oc8ms_TApLXwNI!)}6B%}r!9DvM4VKdrh >> z!+(H~S76yi;0F7&!wt=ZaZybpYvXPU+g-quGG@m&WW624{S>6HUWXM;zls>yn$!OF >> z3|ML`EA$T~b9`zTIS4|-l}!hWuXfY4IcE69H+SpX@Tl9dfDu&fnD;f^>lvD8#s)D} >> zQ%eEiIb#ylxCyFF&#ihi9YnXqY>d8QcSN$n5!MvNnH<+8G7WbsYZ?3u->pGygolBU >> zd7CN-#%EB%kOd?7>}#^-8B?;sp#`j8#r5|l<5nvJH8%F6Cr+g1hoc!4LhAN(L{n2t >> z5qCYtOMi*1*xkd!2;tz$XN3$Q7coEAe_I_2h!^Nrlqvfz#<v?0n7UQu936PR7^+_> >> zUKnc>8rzlXV#aKr;>a_n)ln<262738c3@Tpxts3Oo?ro^dajvka8<aqjs2F=#$5{Z >> zIVGC0>M(MO+>V4g=9TK3LGt>^8k^yQuy?HiL-P#^h5qZ&)^!Y7t7Kzi6?UIh&V@Xm >> zNQQ$W9{7CaJV?T40>o5kCJMBurvkj256RUx-}wv&Nl2xo`Y^a<@WqZd1JaU(aj$65 >> zTwF?TG>P&ke?00-DT|BWq2L|kaAa1yRKQ2AwZ!1hVAs6A!MUg)tm3-S{>x_J%z*8m >> z>5l8#5<m3i_A2&UY-i8Zgo~opJd~ztf16&Fi^@zGRl5RW{+GXylftW`p+2Jpv%z$x >> zaLr(~_A#+$_uP&arku|zue`_5|99qc&kVp^)kK784dDImIsGv^8Ovj|Br9!&?NQoA >> z30J#ZX0EXz?Kea{i|t@8*K6iif6$^yLeP$Twt}$iZFi$&j4xCRN@+j7YM-zJE!H+% >> z1-j+_KOLXJTz+WGc^|gjiATi9-+C_TQ28VJaHAp^P|7<ouTFE`XGo>s^O9QP8D$S- >> zB=~Q#^AR-9LaptSHvXE<XR!t%r26l86yReN_FH(Q>SSi}H+^%MXUU!TdwwyV*SGqA >> zRF;1~NH?_j-^>lxMNZ3sM7hq~Vd1DY(|}cw3P|brjMjXz81K@+UDzQyPb3x?U8r?f >> zAOClFX);-<MC(qGOtp-zAqA7IEkP3g(YFKo(a=oXlkUv}l+cBack7}PLa>EpO$l`k >> zgk#ut*$iY2XS>qn(&b$236m3>EwC-~=B~Ed^t<062Wm|NoiA4oZ^CL`<U`&zR_>+= >> zJG$8Ic-;ZzbFn1rKZ!B)C?|Ob^IkkFP4&Bb3S?4w!Kt}L2?}r}vjIKkl8Q##7}rU4 >> z%BgEEvdX@E3`M(bhfFp0kz8NgxdVE|kCx_ek%v~%P#ecReTwyRQ5e;MsaKm0YX~^v >> zjD9!WNpr5IQ$jXG)Y<A<0}GH)kB^VH;Fc&^WqDoi#8o#Q0k8({-3{iz2fRlDb4S2X >> zF;FJeW#5h@yZsT{7|U)7bYfCC0*5;fjZEpH0eP<`vOCXtMjpPYnu>86i=*up%n<>Q >> zn3~3IO7tfRbf0vE<Zrm|Sjx7_qcJxsAQF9WgZ0tslL>=*F_vd7x;Vq3cSN^Oq<5s0 >> zeIL=An3|q;;eB+hi~rkBTPi~UEg*7qa4@^s*OEQU0y86lxg4bOcqFl5A30fsf+GU+ >> zaKuWyO|CK4Z&-SwDD2=vxn*i@{p`s8&SV7{7CV`fm77@5R7Q^64y*K5<4|ti<xqba >> zA%U`QFW^Afk^D&(bAbvTw1dl`oz^D)t~1~Dcf^%_HPb7tvqKRwn<N2Cdk0^&n{sI# >> z9c4CluiMDP%>u{@7&rzB>K}t+(+{qMPRhF7zCBl4^uuR8NX&`o@yP|~&-B;waImZT >> z*Sem9g$m50oj(cC&!haZ1;`S03X6cuWt3=d({D$S^MV8h->;(ksjUMJ{>6&0!0o|? >> zZr_CIIx--_R?YljC1JZw3X)~!UFi||T*I!$d>+!p(zSh&Fu!gtdH!>WOzrLiXA5f! >> zrN&MQG=d?6`i-no>wp!~c)-K*ya5s-U~$K+POFg2g0Jp8BrcD*=tjAFhGW;=oyCw@ >> zPobn37B)A~yvp8~L7ZAunx~^hR;w5zwcr~FaRnE7=63?3c)gq`OPeopr$*MNpmYAr >> zdU&cZ7h?nesHkU+0u7LOX{ylQ$?YzSj)=`*S@e+!yiO2V=lUt=IsnS9izTj_-0W85 >> zCJ6p>YJ@Z9<=B5VEW&sbz(fE9HWz3W2A|9P#iL-w5CK;Ehu*@-6Yq0_{(`oV&|(lb >> zfJpzRwF;bYhA5bc7C>zQ5Ln@qK;qs%96OOkH80@5W2(TFzR)jMyWRN*{2rF$fna4R >> ze9ONb@zdZj(SPm#0m+nTQV<@OEvEnF<Mis<KY%)b|D2yQdOl=3pPr2j6d*`Q{BMk> >> zoJcPN(!?7uv%v)ae@Jt&V74lp5Ww5W2^1DJE=hW=+$~P|ZYaPx0FZmb6m8D_95i1< >> z|KBA7+g`0Ahmot>KK$D!7#wlWTG2w}YH1NK+h`W=e||{>oFIMx3iM$mesy@%{C^gn >> z>k1qPfM(;t?MP^_90SuccZ&i(lK*>7ob3%20FT1=!*3=xh@esZKke^V>5~5UfDS7Y >> zA8=>*xl*cJQMY<21cFGr##yjgQd|M%SE7wPd=-85eMj-v4>pu&strd%rtb>h*{%X2 >> zh#+|-3SxfW=g9$(W0wVACy6He{^ye#xq9cPgnV8$`}+@>SL8Et<c~i~&{F<K42d^Y >> z)8j|$0?e?Xh**(#Z=KY$K>^tZ>B4gWnF0tb*y^{wGTd0RxF5c8?Nfbyz598g^K&BD >> znxpCYeQL(V6-tJG!1hg1HD%knPy{K1Xt8DH#d=qgJS905z(w9)XR`|PJz(xX2Pjz9 >> z4-;-2a$lzV@7MkV>lMlx*@Y%(Hj&!J!)am-#vy`!N1w<q0j4@y0e|dDYgLj}VKjuC >> zDRixyBp!UviaAE`g3yC)kvs8p5~!~Yw{8w@B8&9Ya`95g%U8j<L|=Iyt!VaHV4O=R >> zFaF;!DhH&XR+-p61ab`^C~w*P3}^xfH6Q+QNQm6ok8a16BJ}|OY%L-DronV?zHS%L >> zYxIW#EEb{JQLPXChcSR!|DMAmo=tOc7nf)06Cl{QxS&#upOW1JuTp&@!Y|}(1E;Q# >> zb_n8TwB#$?Eqfc7Qs>qrvJdyFhIHi*oPhB6vSN7nh20M24dB$2rQp%n$ir)%q$e5M >> z3d@poAIN7B2OgJ0yYqD6Ei`Wu9`xW@685LDzZR6>*0fHf5C4ip0dD=zlKv_kkcRxV >> zCt(j#z)jupBqtOWpr2@}08rm6Ik!r+YL0KdBmFC){SEpDWgT5?gm{fPeDJM*i&i8f >> zpVzIMLT#qwmio8Iwe-TI#{hp0BS$x@5oYQ<;Viah<7VdI{yW25!7m;*q%dBGt%c8d >> zID`5A#eGBs7WWG;LthGy&IR4#CBcV+|NT`IUSsjl_Z0&A7_>Y)L+oR~j{bLum>WV# >> z;cmPtA$S)~KoHw`m_q<1{fl@1<%#}{g{Q3kGcmAN0MGsJ7HS57eCdBj3k5txE<Nxa >> zXcb(Yc0v7~3-2HP4fpk#)+KSTrSv~2{y*z^?nLe+)T#FkAF&t(%}tx%bjA#!YFOOY >> zk9Te@<^Zw4V^o#1apon!6hy5BRik)M0k{xH_u}CXfSK{XyofVB^%!cb(4It`Jh}wR >> z9LR?m0$itMkB@m90B$PmQmuaFKV~kYg3uK<1VB0|#>>g(onGI8=hQ=Fi>X!jR~hA` >> z_N|vrh;Z%xm`&G+MIlQUZImcbA+Ur&E%^I=TaU%eyu{O^L8I@ReOuZ#zjdE|Q)5Z5 >> zn;XhHqrJ@3MscaYFWI#Lo)JjI^J!sbT%hxgt|Y}J`hZK@;noe*1nLx@qrO!^3^5gf >> zhkudb@$4i1GM);@q@ou#2S8jCVK%mlkDqu)+_hZ}uUU*Nky)N_E5I*}E%*sDX`aar >> zQUEffL??QO6HS7J4MV`G#>V-=jQb!VEMJb)wqCKiVXoO3_IaMIsiH3)uQd<aKQX>d >> z>_{ABOd1q4f8!GAIv}@7>Dp%PBN1X62~I`(MlgG|1P#H3Jg9f2cEijw83H3JMn5PW >> z&b$z6ky#8gn*|D0Mt^X~;UUQ{PKc!sWoy5De+p&F3K4_OpB@+-MW^;QOU3`X38)TW >> z61+9whU|H}o*1k0^+j*T+tnqKjy_Xdp<McES#KP8KhUlVR13mN4>k{|N{yT_B1Yq; >> zLXDwrK`IERK9$sPGe=D4q}M$f)vM1?+`?tUK|mFTY{g*oOJa-@iaR6+;|Jt?){|$4 >> zdoGn2R}A|ufJXVPhUtQ}a8qADFv48S7ip)Kzp9RkUJ0(Ihmk9E>)n6}x(N(_VcOpZ >> zie<hZACA#x?o0Z?UQ_zia*fXqH+T0m3$Qa|r8H^y<^0(|^KC+w_jLmdB{drzujUb} >> z)H{xyPFHAOg;>d>j_<)$*3r&a>|8;R7*DK^fQ<Ik_b2D&zf;B4s`w|yTE40xwXXcV >> z*mA9T^T4Te^JG3Sq8AIlil>t4Ao)f)ZrYWYgVxS~=C?-Kc0Oe*ROk#X<#wgZm}`D3 >> zy*W2|u7#PIx-ORT5ClN{TtR=!5VFmu-KjF>Kd}P^Q{}guayX_Wu8a5gcl@{lSmc%9 >> z_`Ea_<n1Jx&y)LzL6hZZZ6?9@{Z0-)iYY#HKg;7Yfqy<egzUdI&0~_S3}_XC3Epm! >> zexz5{@}+%a4)?KT38<6NsszoqiifpMIbyc`4tz*PGQq}=e1ruo1Rj%gv4;%abh5TM >> z9bD&}t3&uBC=dT$u}C(a4CI6OPrRQM;Irew4}E;NjJB;tiovOpalXj%>CO)o1W$RR >> zL%-ZH0SFKedP1S$x@(QJ2r3kAHUly~oe4&Hp%0E`pybT_-^%Hs=d7NvS3pL0`9#qS >> zRY)A|=Nx%h*7HtIqDo*0uEBJ#4BJCNh`-z4&ljndDTzfwnfCMJ-mb<Caf>_Ouc!Sw >> zNyEb7_EGyLrUhqe$+=@&EBwS&u~7(Cu{-WZbzvmgPslPzQ1JWX3jle?ez{Mu%|E9S >> zqrLrh;vy0nU|zqgJ^e~GE<|Aep)lsiKibAPJdIe?9=QAnX`aG|;k=)f;j`DX_w$?6 >> z&0bWOh=%uR<V(5|YXult3gcok^+!ZloFVUM;Z&DEMosc0r`H(XZ00GjZ`jgemgE1e >> zGa`NhBURM0A--dZX251-7Cs5m40X&y<4Gb+-Sg4uS(ox@ZvK;KXv*{Y+El&;H^YRv >> zWU{V7ikjIL@$g?{_^nT+MT09}!zxr_NT#b|1h?OUJ6|;~KT({b{Xtde$)leh+IkI< >> zY5ZaxMqkgM&*{&lAxoVgI9z{zvGDLC@0Lopvi4%-<=D9nq^6^>RWP7IX2EEDb_H6F >> z5D)wLvRPbPkVdLtH_G>NKCBSevR|@}II?R}S?sS5F^pD0#I$Viw`4O|-SK@Hc?Gx+ >> zzAB{K?R@j0Q3;Da49QTgW{OxyRiE1Yvw*zQWw{^TOr7Abb*Yqx;;uH|&?!5@i83Tg >> zv?;0}RQAvYEm|!&Gr(lBvq**Uw+ww6*tL)sfJN_&y?rLDGGaBRC|6WOSYg`^_{*4w >> zUwM^cAAX-A6~;AvYmo~T{L9k&2$SRUj(v(bmli)cGic`_-MuY-1$cAMJ3(FFdQ##2 >> zmL&EYcs{>rRB}4<-7Q!=*3#r<rfXWPqQiSVA!5dtGzAsUuy*0}+rAp%^mthV5oIZh >> z<_4R{O#jPw(`z?9ZDO7;S6kvE47^zbSyX(>Mqk6utH|jkoBb~mNXXw?9e>}o`t$DS >> zNT=6(%ZhaHVrdwx{ze4&i_c6onf+BpYA;32Fs6^9rk6vB6c{fLq32mkO9RBoN^qXI >> zJTq>k0q)BFX2GSlq5iFoNrlv_fXIt2YD<qjqk)*89hnsSu!Hw-jiJ&uJWU-c@Hgb7 >> zMK|-^z~9iiw+dPYevyxo3Y&!f+l$htD#CwxnZ3<N%JoXgw~*xk80Ep=;EF$!5cBVv >> zmK_9+m0vsJ)umThH?P(!&iUKG;@HFR;nd%4>5shC7q}1E#STyugcSp#x45qgS7MwB >> z22vF3$U_aANPDl$SiJ@YD-+p=9Rv^Vj0Gv~cR%2@>3$95ZV_2O6t?ys9sD3)&G<EY >> z!@mMPdmL1ow|v=cmDZ|u_4G?BF;R(UE#G@Z^LlT{(t{Zp^-Nyv{8&J2fWmLueBmH} >> zP)1X~7&&4btH7)wLOPnSFCaLNPM&ZydquhF9ZmGt+ZOBO$x-+BUkqWQ`2rg?`g8fJ >> za;|x5%FD~MSB06Otoq0~l$q?KBlKpvKA8=Z3di7=ikIn6is)}9Xb<OSB4ULH@xsh5 >> zf|~hkOtyWN(*(l>Q<x?*V;47{sk$nvN;fa=G{4G0%B`7g=MXhOU4V#4ftI847x~yD >> zm#8Q7+QlU$kA7mQ7~kLvPN?pUubnS2teh13&aZrFNJB;T?&L2d_nK5<?Bli2Re(lF >> zs$2s!pZDQd>%c2j$?@E+0kOAWvxQ%2D%Mkcj>4a$7K<T&>eAJB5>c6sOq2&cYoE;5 >> zJ#1uQsSr0z8TW2{pS7w5Q!8MRhf;vH0eY^|Y)qJ`_(Y+J-r-up{6YXwn05Ff>&<uR >> z)#+a|uV~O`z9xBTP7*Ew_LSV$Q@*}7U<LGLzft$doAC;;lt^>DqszUk^&4?#%lUUS >> zooeDMnB2?@Q%z!S&3*Xp5n_3gORoD(=C6-)yh;O}fc<_rcX2nzTa5*qmKt2?5m3jY >> zruaICGQtLvY%s#UD{k}|k$z`t%<TBtR>@rbqfzBo)gs^a26to9SMleM>s5AV9s0zZ >> zYuH2B)QgFJCm0r~-VAmXMoAz#7E3$tm75(mdW88=L}W4fjIL=Xyo!9j3InVfMy}V0 >> zcNw`fT5cUNp|&M=_zEHL;Rh&*X064_G6@aBCKQGQlbD*;h@IPr3SAyYNv*xAK^L?1 >> zQV;@r-H6)*$jB6n=f6AzYBQfK@9H*#SuHDE1JWy%+%SC%E-1<^kt6{{Dc_=Ra|h*S >> z#u{p}SyL8fJO$+VtTT7D=awTK%K5l2DMkjs`FV|f)!uSLL|S%YxKr|9KWy;J>3LW2 >> z0+X5bCFyl)ca}dEFlK___iXI8tXLub0)mojFqa(VSj{Pqk^vLuZ%scU^_C}3RfRWi >> z;azMkhVP$5391Xaj|Jj4cpD<>sFZPr?s2!MeFJ<IzkJ7#ta}gkcZ^c{WwDyjlu2Gf >> z;w_Ay!QM<#>)_Rjfc}9sb6(zkLG>QHC&VBKHEXA^tCsh<X0p&j(0urtvfmmx>D+^M >> z)gup!NNuFGo8t4m@hU_R_Hg)S@42cTCs$iNl`_J6CUQYE^u1`>8!x3twR&%;202|f >> zwSCYfhN4OXG6_XAcqav2&3&#W{8fS8<yu;Ra!#%Hj0Pn-#z5*d@0@)BO4jucX@bi8 >> zFdbi-eH!3C>z!K!ya+z4%!}L0&ZEz5*E&S#@2e}2YMT~)%qREBZyM_4Yc=ljPFI1s >> z%;CO=E{LeBzV-h=x#3yBmxA`?Sbbi53rL^(DDmEpm&WIDW*j(CAua2tV>u0M!J-d` >> zPF|Ge7-${)byI5_sovtTWH%V0Xr8@?$Tozg^-|=i0ILQ|NK>LmSS_nMF&a&&hg;Ms >> zAY`Yii16po-{Chznr6`+VXhnYq-dhk2<}HFx2Ogw3RH?$cv%3uhm&uYjH(JNv~9Tg >> zMxehM6)N91dJi_YtMG6Ln>Pk7w?E<Kw|v5j{5r~yQ>#@!wl61_bokYDQ{Tel5$xJ{ >> z-<m75ifx&lyYCkl{5ewRkqz^{WTm5fu?B7GOu$%|iRR0DAKx`%FGPB^ilsqGY$+IU >> zG!fn>g75qK#`7@ysywP_kbdW9&E?U@x0T3M?`iZ$wx|)y6<@XWfI3MvV<nh-#`kIg >> z)a>-`w+bHmnzWk$01hBBXv~OH{Hv%!T=K>36iy^66T|$3>-(Du6s>LR;@RbjeFmYT >> zrGv;{E#$Vy036+4425m<-5;q+{-+D88r#VRQ|!ayDCvUe9W`z8wd?h_K5Q4t8OSbA >> z5=9?9N~7a5)gGveLUQ)e;T5X)vD<KSYlYg9{qdAh2AI{kqTy5YuC6v)IgxiNWxCUb >> z$ZN({t?)EvK!0W6ig7&%LcFOw{PH!Db^5!*MzX;Sd(F4UuSN?x3YZ$&#!KTL<m%_D >> zw2-*P0M5XPJNmC>QRS*Fcw#)G+BKq2%9UXYRuv4@$<KdF4r6?A1;oEhgicOQ$3lU- >> zn*vaZqwQDvy7-L|(h!yRZvHeYbD{CVWuTKMeNs}_@~BjBr0#<?O#!#_rtAfL{bwlO >> z_X$g<<VV%T7Nx0=m{p$Qf}g+oavSe5_X4rVRzZpz4}NaC9R-T~9I9B&Z27K?yXiiN >> zX^N`>4ZD`2WL+S~63;c4e*jU#&*YkUG0z(|WcYAS8K)JUkFYyL$i$m)iWizluhDq* >> z^rb9(E1%<^fw-rm*04_-yQ~r#v5gEa+>ie`>pdV220C9nAcUC(XdNK#%$EPYx481O >> z1Qd|owBt6149KSt?9`pD5?s4x{6gLhvsXaxAt1H5<1tK8&=yWA3@p1RCyFJn{HfAA >> z23}|GSbJz+$xdQX2F9$XJWB%GC9*^U7D(e>sFd6w&dPqV<=J;id%#G|37pp|4>FT4 >> zx{vj~^NzLbdl$HYPC!Ii+Rt;<mx`BC?M7-cp92Q=BR>h=+0VzIbpaQVe-D!YIO{~A >> zzFHohZkg!(hdm$2wG-5j7#|mf<xkvUHQ@sVz)>QC+8B}}2FsE}@|L8qf<`QD;oWWW >> z=6#dI61ZgX<rCEsCkCru`a8hdgEg|;w7o7q*qQF$Oi%}wzMEKgQ`i1F)8JxEmZR${ >> zJq8M@h?tfgHw@M8YQ7K)S>dpQ2e<kWI`RAaDU~~bN+$Xyms>T66i=&hivOz5Xf;T= >> zvjY+qyuJFOlHW{Q+WE`UWB>0@j$;9B%Zuh|`t*^LZ_X~sV25f4xGS+Ym97FeKb%5V >> z&S*-h^u+WQ@iCLB!k*zvE!++FLEURd*{C`X>313}T1N=a<vQVUk(MoXYwv&+r98+1 >> z-mHw?BmhAl0kT75EN@^B(V{og5;pFi!jI&->6L+z`+1Y$M#}Pou38ZZ7f6z&mzSA# >> zq`EM@yopHF)S<mgIK8YaW2Txk;6Mo#o~PMJB^?v`&7ZZ!e=Av5LFkno?%rMco~!Rq >> zV^sK>BQsAq|J;Rx6T4uRket@3Z0X<MQo*A($zI(v{+Nba2XLab1;vqrG)huLW#>Z{ >> zSaUOOzy6NMr_C0XhA_cGHe~<X<$@HkUb#U%X~V|p1lh@u<g1zNuRft<m14hN{PKZF >> z_dRq24g{VxU+DUdChyyNU8I7$a|}b7s=gtu08&K+)Vf8@<yF1%xRRffP{_reTgw0+ >> zvxukHUp8oc1ayOs4she)hZc0vs$nPXH$26@w`?jS)+3c~r0nMwJzXT<k_S2KTTudi >> zF_wk)w~zP=?ihLfn>85{c?hjWQeGMM_8y=_LvE=v7;2dQ>N{NaeeKR*HivY7W8rw! >> z?_Cr%5Yu)#iIazaR$bztZpq%8Q?HS3iS2$7Ga;z{r;gi(I?XByIxn(9ZMcx?lP+n$ >> z+5X4bWQjIt$!0Qm)$zbSC&IH+VDj>C?dt`yKlHS9vAe@Qv!t7If~e^y051Vtoq62l >> zpW+|n(q}v0)X1mD)NJFic)+qY&7<6SVS96h22I5f^VaW|eDPvidD}(RF(YIAA2(u5 >> zkfC$HIiwbSR#wp#Xg+TlabA24YrS%GY_QXsk=?V=J0J=L!1k}Xu>ntMcR;S4NB|Li >> z(0+)ob!`9np5daQ;-Y@hzk9zb#OhokC9iKE0C|q^Xv@@e`WLgb4xOf8av!S)Xm5jV >> za#_zb`IEOXO}6`GmoqanLm-sAvsC)Fdn9GnlI-vF;lQ13S8>R?kjWc5kEJPWO#2Fe >> z2%|0sY=<`w5w0IQ1see0^I7p-I7=9=$bVDxvbmJCXIih?+X<+*Jyr`W8zq<1)W3)1 >> zQSZ4c?EU*2z#nb`uWMB2@913uz-=-m1}NkmD@-z#7j76<!tS5QOYXJ;FPg~KvLa*= >> z+hKZDsL2Ut(WD|3(pg|82llz;Q-B=Y&j~1b_c%|v9(Z4Kq0Y+c9EOWcjyk0L7JL$a >> z4F@FAZ*g0;=cbj;l$$x?Sp&FH1Va#RKz<t@dkYT&p0J*tCvCfYzYhgy0z(-_|1)=< >> zpf$o9A;{U`Y`@Weh2$P1ODW<^g9<7LPf5}r*NVGCCfdJ;0wwOpUC%CS(e#-0wA;*n >> zRX_>wHvOiW@XkeAb<Xz7WznkMF*VhTY}~-PnGXsJ1hu&vPV!q-k9Qp(SP}3h%md}0 >> z+h-pNRp3O&1GCX?s!p6=F6_s8%UA{O+*Y#ZMTJCgq)R>Tr-%nfd@gK5UOo;&o)|3V >> ztq4_;$$CynKZew%{0?V|1hW<$({O0acUous9-o|Sqf|Oi$k*<6Wo2>uPDJRGgYDad >> zZra=GWeZRD=KO(sR?o+%K87njMZOR?woZE*tkd4&xpbH=F1t`n6AAsKcq<`qXIWU= >> z+v@WrUoBdBQdhFN6~I&bM7RyOSqyjzM)`rrZpd%4^sSTb%Rq3jK%;%{EY}VJP-<!W >> zX0gLjLxRrW+(4qYEC;RSv?BItKw0!}4Fhf<nhxD6rd1|t6i)v7D=6!f8-~@L=d1}# >> zlj(Fasg1W&{!0!487<}8Na&_5(8O2WBPR-6)WZHqOZ0f<zB0l(bugPw@J+vsiAQA| >> z&(<y}cjO_y=COqpC*=ZQyQj9=k`gjTOuskW>XGG7E<0)8&2Sk_&I<P}7m$@23VqH2 >> z9{qd{0?Ke}hd~oDJATg4@!3~uM`>2JdryKc07@5W3qta_g7d+ys&MH-=gtHvT$-MO >> zjfiyBPccQfnzh=&$z>hA7W)-#iMi$5a{E1cwFba6=#`-hMY{k2q(MNjnV~%X>DQkI >> z@zlJ)<x0Bs)3b=|GmlB@wrMEjPoTL+s`JCMa}OqxJ{h>U*1rutj=jBQpYS?08`LUP >> ziKn*Aiox<196Wyv1QDW+ftwCfqoTc_006t4e5IME;3bRs5ic!Avs*j790g4|opW^U >> z<I4tW8C$Q?RpDvDWC?|^m_z()3A#Y%SAQDM#%w<_kjscCKfV>bN*f&xR=ysL>}yiI >> zLaE09z=XOb==}9D*@EHulh#v;5E~lwtY=+VHp=dq3Lbu(6$Q0=gU<2qmyUp!TgI|~ >> z(&RUO-!S(<)l6i~7*^<|1LaeSroPnZ&6*@C+*`aT*^t&0=>5>m(Z-G@+L;-Wb36op >> zJ=pooWxX<yvGo_Z=zjgL{d??Jk=yFS*hfB8HN-@l?NE@c>HXUBOa~tmBSBaoPzC)G >> zKrk0P%Wp+aW(`#aPQGyDUB!lKc)UW9pgpkdp3HBrwN5gYBux6@O4<Qa#tn3u=d9&O >> zpYTTvmG1V?hQGs8rV0!yh#TupF{<U=BKMG7U3f3pAh24uq1DZMSrzcH;`fc_wVE4r >> zvO1}$^RjLF))tiLY1yz)1K_oc<6beX1C@(QW?>-kNMlp8<`K_fhE@MOVl0WAzYj~J >> zkyCdv|J3zO<i@FNP04%6pHkoC2gaAB!y83RudKsb<q8f2&1V^-{e4a-fk2%AoCY4Q >> zdp+)ns{p4GskPhQG8io|5m2a6T#%DO{mczf%VxrCk2(TgG4y8fRanIcNz@G=63UQX >> zD)fHW=K#n3GO6eaj$Hg=_A{E<g`^_-%<^Z;rLpakjx1!xPujBt=2UcrxO%vj=ZA~i >> zQzYgC<?i)hvqJ%P7f6p)!->SN!sLQq)1CU&(}TT&N7>kS69XXmW@c<YW95+z?{0u{ >> z->4wI=W#NMJ}SKvl3o8gwb;0|K{RB1_$yGccMt7_xds#Y5xwK&Q8dg;m@tXmkn};h >> zM?o>qcLlNvai<#@c^BbTfE}X=+%{2P#W|2fY{<2eJ*{V&hmxIQCb#33nPiiCl|b6; >> zLY_;x%<w~MO1&l!B<TS&9qqNq!+oF(XIRW9+Sd--@uT$peI4K-k$8*Bwj#B$Pn9!? >> zO2dw!{H8AU(Ibh&W}wm$m^t%tNkf!T{gxn}_ENYy6$ehRTq<UnP*n-O5$NOn;&Gu9 >> zWnWDBYetD9eXF*)1S3i`V6*!;H2pc}b=xn>2<{=NA_%LMws2pD9*<i=rUo@S<Ll?3 >> zWJzOlyPStj9z{6497gTuGp3C(AK9nxviZ)}s{nL))_(Z4n8W$V!z<BH0a2tyg||A{ >> zLzCa|D{2cY0*oF+0RcMI8#~A7It^W{5m3YI!5|hUHhtZ7a)qu=Bk=8e8dybrTKU$5 >> zkw<LH7b#U#iTmu9;KcrT!5nB_@S`!LDW(JhtH6C(3fZ~pG<)<V3ffs$I<JDd)AJm( >> zeiM+Vjt?P7Rt0#C@VQc;g9{=0y3sA3YCW32!AdJ%I>SVlg)LxcFh(Gx5_Rt!=F1fW >> z&FYViDz<;}aVQBBkiK(8*<w!_vB_pUZ7N)(N8;nob=4!##!>$!sjid;2hzqwk`?RQ >> z3;+WacHipcTrrwCHMb6(eNDX%9%y%nE+K`f7Bdp<EkCyaimfcQMn?Qf1dm&Gvt6rR >> zjfUR$0YS*gi7h3P!7+~uO6WZxJNf>PBsr1JG6?0iyPPhb0+4qfIV;r6$f?eqWw9=w >> zO~YE1D`(}j@l@oMzWc}kJcg-u6ND_uc^?Hpra(c&Mv@K7Ej55yd`k$^Imff}k<dOS >> z0@zUSJ8;nj{-?jqfF~HQ|9vEd>HPJvSN^h(O8-bsAkbc>MbBs{G2A?yc9^<z7Z(3h >> z0d63V-v(Z)9S=mSy1p_}pyLPI_a+~pn(V9c?A<{UIdwzND;}ePXQ1-}0t|#YJ}QX8 >> zlDlGSRz@zbCXCIQ?6CD5ui6TCp<`Wq^0k=`6(&G6>x26ap7Mli;N9(m>3usohf%VT >> z)oWnnPR*w~XpPol>ze05JI|d>;KN49ovKJxW#1G7)+uiT)@diZ55uv7D|=+Zcjd+3 >> zXlaVgC0!@mXjt&jozknj5-o#fe1Y}Z)+r(r-XcIEPMTi-Mh)wGEl@#s$o)tqdnwQv >> zbJHzD56=bi1YtasFwDVB{KDYDoVmN#NNn_X2Rby>s>^&&En(G6?Y_(Lk&knRb$?xZ >> zVOGTa#rF?tb<t4FYLCsEXqx?NBKF64dX}6}^{t1W(7AHGuLIx8UYzjV_D9{-0FT;L >> ztKJ1rjRK|XYH$1J*$Q3&&~qVpnET$=F{$Ay*|@tYfQC_ja@sAC#rRW=uJI-SvyZNb >> zrB8&b%rs5UtI)v6BM(2*Zi={1Mc(h$<z3lRvXTsZ^I@ehy@(_dnttO3*~T<WNOOW! >> z-KX048dJ^wRkAwEhx^y;nH-PPdg)%=-6{40o^^-4ON>wY>Djs`@3+(Wo3f?8e)3dG >> zhnk3=0ATsIev4u90-&?E@``lVJu4^7auymE%Q0ie=oZf}c}3Up(ZeC{rbm`}^8q^& >> z*w#JsR@)y*hCIF|1<w6X6L7L7K?tJSPfeT8awh86UA&XZP5ECzhxy>Dm3*O!yO=Xv >> zS-m!@3CoOs=w~Ff*Ob(*4?OA66^6>qAJo67JNPt{)Aw{6;{2UVJF1>R*2cY3t^4yp >> zc~RgvUs3pR`1UPB*ZQ0)kHUkJdE3myRIBxVcqG*Eo_tCc|M!A`FWg4;LH%4v=VybT >> ziCFt_qMDUxS>^&yd|8me0s-+I^o*6syLUHq$-YD@4RcM+yYtsR&Kc8<+QHBTttV16 >> zblZJR`Td??>=@jksob=zPS_R~l=>Sf%s5hRtp2V==A3Frs`5s<lkngXuI%;rDHRJ9 >> z;f#){;^W=vM*Aq5sN2Nit#^*CrX6U}8T4pb;wwxN6~2jVNeUl;KYO5yEy1NeNPRYb >> zinxt!0{%q9rcndcvac?+V5g)XH#g+F@^Re72Li*^%|Q3cAAzZx2%73qby_syctU5Q >> z%eiT$zA{lF1;&(AQD6Sh;MCyx=lSBn;_cbq#`veqrR3P=oh!!58l*E)b+^XEd>T+o >> zE;g0cFVqz3BB3djeIpQ8N=U@Yq@y^@rQh(pXo$-_#}4gviojV0$f@h{QpN#yud;9a >> z73BqyF^eBpo-G^Y^Ubnxgup`--ua5}{Q}-XR5^i$d;?Y=zt4S>FzN$=QIdH9gq<TB >> zvTnwDbu@LvT~^B%Ad<bF=>k0CL{ea?J%}?e59P7jXvM)uxILMJ-BP6+o^EuQNB|UL >> zrbgiIAh(Gjs-nS2(d^ihPf@l{0Lj(iG~ct8bUjPcYRFk+6nmutY<yC}++=0qVZ}4F >> zbDrD#q~dj~U1N;QcbqA9xBk>F=!rcDa-7@OmBB6dnW?oXN(*4cA*L_o3#5=f9__PX >> za}KvR37vE7<X5<F{zK&~LGuBI_grJHm11j^Thqm_ahtBYC^5f)v0%qv#%QMyBpz!K >> zrgdcB*pyWE740%uPib0KDMWqgTmzoo(4iR*pXyF>&T#&jQ%|(LllF@WO%*cT(uTNQ >> z?%m*liT>)ZjM(9ddg>>OXwwfxa6X357Fkb16G~;lY1p-S4;6$Q9hpFqx?bN8mt7(W >> zLk?D$3+9K;##V=X{Pj+IQ_0W!lO4StyfpWaVpn30yrgn)Zc!fR0z%E%4yrk3hbh}c >> zfTxdm|6qK)(+((7SrCvDHf0oQ<zfF6A|jQ$&Nx><s3r$IH_ET8E@M;{_3maz7A%&# >> zJB%Rxp7e7oQ=r2lnqO)rrD;1&;l_4-qsL6W6>41$XwgSv#DQTiHdy#4-cG{9n^&pd >> zE&<PRvIRK5FAyFbZyW>LcaD7DvgzwkgsT=trR=C1)5H3-sk5Af|Aa+DrTJox9K~0M >> z%SEsazPd^4`Y954rx@^XbX-jYsFaaU4tmkepDkm20hO*o5F)PW)<5p(y0XrX6nk8y >> z*pF@NOEND8qu3Tx!Kn>0rnn}nRYq0#IuG(G$`h2f?dz;D0<g}0tUZ1el29hOci>@j >> zc<JCvKU{7Vv8gNYs?Xto3bvm;+v`&Y{B44y@BLJ742!|X1Nmng28!u^EVAiHYVJ%c >> z<6q%o_#{A2ybFH?%4)r<YUv+MvmiEZwWae>C1UWApBOcD6WHTO&}IkCU+lc{8!a#v >> zP{3%zO+$W?2!?4N0!8{$#%7$yr-wl^v22lt$QY<})0_X()mO(w8Fb+yDhLwNNQ*QG >> z($XE$of3j{iF8X!Bhnz<-JODflCpFyO1h-L(y@0|zwf*Eclm31-*@WF%$b>Up7V^z >> zQkm|uNgHmR2t7=>KyH@*cf0mhn{K`xaMAijxnc+=WgDJG!gMp@YuL5G`@1F2${p9$ >> zlT%t3z=%2doke&GZL&kJu&GN_EZFj1&HX5yIUB~+w!vW)g{_HPnsaw~(l7O7)oCEO >> zY4^&}USZ?w%ndEt*Q3nALnb6-55Mr`2aD#b5%pIS(?T}BH=`;%{I)(Z>-DCMinZ4P >> zjW_}on{CFN;|XJ6&e@*F83Tk@4fX=V*G~ni?U!<k=yLxqT|gpNFznh56a7J_aQ0rg >> z-DXyJ{V9aEwf>2w<N15z=2FUh&b@GWmrR=jx9wB>81J?szUpoVKk(6>Cb#nX^3@kh >> zSLazAtpxdIznVRChwS1bH}ett1~(;uQ{YAQUKzD~bM^AX1%JzydVf`G=TU9KGbFSU >> z{|UqpV6eP#56cEC$q4DP>=z{Y;~bkP<xaZL9K8A(Xhv}`-T%x8Ebn5w*SPoB6Gsn4 >> zIjr@%;anN%yx1x3>bvpxGNm6o5r{zdD~2{_<=L+|Aap4?4&p^wUz2)V_A7uR!M9e6 >> z<rGubP$`fvy^^cn>8++w`&n8ib>7O>KERMG2KBKgx-i~x=D<>^1FA=Mm}d5#$Rd01 >> z1o(ba%Ve9xe&y#(Xl#tXiR++xhN@AuOg((A;^}ltn!4+%-DL1~vDInzj{@xkQjba% >> zt%CAcdv+g6#`^YQgI`X4Bqy#~ew9Y~1kJ$!Tqch$E%=oQa%GOm5fMggZ938OG130c >> z?h6Bo_$-F{$L#Ma+e@Uu&xG(Zg+%hs&tSWV?Ppbm9kMH}eI?L>gLMU$d?<WE_pPg1 >> zlyC83igAa#&KhAaawM1?qs=kkO@S2arHlw|rvgBDA3=w9m~m6lu-)z*L76LZrl@KQ >> z7V^3;GsYSs;05c7v3+!+uBt=kOdM}Y`@=i>og?82W#r`S#h3BA{D$t?gj1?As#!>9 >> z3e&?V0I}oLYLOo&-o$H$IX88{@Wr#fpOg>&EF8*;cWvKQ%@9;Vf5CU3)-NoD-d%XK >> z=t?s|R+(-%zEjfSNzrAtK^dQsLcI{B9kv9|5(E8-W_&_%ogm+8#vCdwB~2@>-=)Qh >> zuz0bFvCxf6wi7qy&p(9W@WZV72Lw@DFEy1;#A5j<yZwH3tBStpxh!poaA<ut5b+FH >> zk?@CEsmb_!sj2pk!VIRSXAf|<%@j2RH$(;3(TtpU&V*+Wm()l?8o;~LoukZH?~fy7 >> zueOD<$Vjlbu3~=&7~S;<C#RkUqv{B@UyoNoodmn}nekxIpHDvsm!0Kb9M$?yAwFOD >> zkDRDxs2JbyAdO1nU-`NsCVze4KR-D+QRNH)yK|Pb+`vXk=-aP>@4kBLw3ljPp*eZM >> z+`ZGL7fK(&HcuQ|(`s+0yUi-?c6JaV1DD0hgY%+*-(g@%)G%$1!QCthY+8!(Dkaio >> zIkiMJQ5OSN53)P5B|2S;5^dzDx#68TyJy~MdaGYuhb~;df!5%9>NhbB9#sbv`d~@_ >> z3?Rw{<}%KyallfvgSCI`VP2N~)~x*8eTd(gzz_@!k(k{F_Wb>7lp~Wlpqf8bL>L&e >> z8a<0L@sm>KJb#}i*)Ry3##229Tg1~9*QLUh)XWl!A)69}n)TF7m&w;leuv8k3$9{| >> zRy03WqFViDJ`gPKAo3FG_7Pv1t@*oIgUyjZ3RSX<yFM{yCG2tDsTOa}Kgdyh{G-9b >> zUx=-_)_>`wu)~=92BNk=>N@&<kg-;iJx7WPZ;sp8vi$Vs647GiU5zjob+u1rrh7z& >> zsJ;h8&fef3=FhZLPxI(E2T87f!#DL@?%RG7#G0RyJpm`Mse7mYg)5RoT21%X>nd%S >> zA1+q)5Z{~mIHl6sgQFk!c`F?eb-CAv8h>aOu>;JW6{lX-W{W@qD}FZH^iEvP+ZL;? >> zFpEs7Si#F+S`}bx2aBy%-e82L@?DiOBfL~9zb>M=-4K)Vj&yU#4`y6Y=Y$*|%fbul >> zWHm&D{bZBBj?U|w=e5e&|B(O)T>OYeoJ!u9mdDgCueyZzmh<WSGEIMD<Kx!|2VRxW >> zq_~e@Hu?SL)hP0Jum>jdWKy3p;~@KpNQvi!J0njXi#zoNClw7uGnnD*oeEAN6Aes> >> zn^OKESX99%Q7AaR$L8h6m6B&FjP!4Ow&%=zP}&NnDtw)rDt_!M25{e~QATrTej`SL >> zTIE%RV~YHfH3%bKv(aWA0%rnBzgZ<r{#*XvK_^u}keA#c*qZU5?3S#5l@NcleZmZD >> zdj!K``EK!oHWTR?$X$u%bK?)nd(SgrFPLzj@IGy(1pP#bKc5;_MDCsVG0*q%s>`JO >> z5#R$(x|&(koj>2GBjv5sUbe}vC~>MW0ym0(oO6Lv=oGZ$s}3#XvDPO=@}TF=yTvL< >> zfdD(US)N04bH*Jjo_-K@B3&m`lSwGGy1o85G7wZc%M&<So{TEN!@Xn*xJ&4a9_<&A >> zmJk3Bc{lHva3m^Z2rVv@2)KS(RG&T|7{u8QK_+5}AL~N<XdvsA_K_sCYf6&>d_b6? >> zOes;f{f48z2U%y~7}=aSAmY&_2f-A5&@4d2AfAcDFMRr<#*q-KZu9tq8ZsT$v!Ynf >> zp(Ki#Cy>XW!YFDA#Hl!zoS;)ad`0ry1PR^&J})iCqp3Yid+K0Zu*UT%+<W4NiuBal >> z6eZ{@f`$AE9#4iKnUp$RmCBq+5VDG~Z5fiB;)SLpGBL%kf#aJq=%Zks3h1TKHJ{x_ >> zSVUj-Tr{0>H36JxUSFJ29mo-^Bk%j!qtjK2qk){JCbS}Ho!z)%{`MbZRwuAX^T>Lp >> z-7heZUl2!rQa3xH^sjb=(eCK}>1OX0`UZ^H3;yPr8cCx>kx)u7a97&9Od5$uXTkRR >> z(byW#CqVYMfV$_kLyW^Os}f`g@2~xJkE=Q2H3z8xCL9Z*kWs?>ZlCCgeGy_GOs0<v >> z%qK;hVq5bH9n4Z1<7Rer8(pzLa0Tj*N%@&L@%}oInj7PjjeDRi_@ptQ+?XPjWUW;( >> zkK<8)Kc<d@o4p2cSALV$GQXRo@`Waz#s)-pk@jBot9gh!&H3W^Y&zhTDjrAMny0+( >> zyomH!oH3e*bp09fStybpL54kCj!RD-+Djw1PWC}>>1E<u1Z`ekiP1WF&~y|l@F)uN >> zH#Xb|W<2P=mD{_;4!r5V>vSG4eHqpb&*i^95WOr4I1}rFRbP622-xa#3K?8JvEFp* >> zWNhC%Siy>OGDgUrU+l~@d7M5veC7aX+_EwJt6K6b#)ZAsx+m&{Y%m@o9jAH*%3ItR >> z$o2l->m+XBQMbIei9ZsSAzi#I->TcIMF?kBuJk{2`!5>a7HzGZA~p&;1wu=CDa*$Z >> zebp=b$8!!}E?k%5nz}Zs=io&F!@+yE*VGL%{a4T*YyM7)SfSHMOx8Q@q2Z8@-3Jv^ >> zNQL!D-D%tUnM^#B?^=hv@R0(4rDHbXgCh&&@<ro1$@8-5y<E9hW)~9yenN!fei*yp >> zpu>LE-`%|?1|M#|nO1c@Zx;SBy4Gd#nk&%M<oQ;XNIqx^iip0YV9q?!43zllQQc#7 >> zH;qU54VX_bc0C;DA|<ux!m|A?GW{QJn}x&e^KJ%JyNSF{t8JZSuCgeV%NMLz+^OPm >> zymCVG4JNr0^WH1S^0z=ZyL9OJf%W85ufMeSs<onR=19B8cnIr(gI#0ZX;F5=2j^8x >> zqX<Ru6v=}2qK_Hf0sDK;$NX;7f7DkU_$YJk(ht!F%d2v@qn`k3DVEGu<NH>;KnqB| >> z9_)BC^S!FzRdmPD&;X>NY%ja=g)Wm~t+Y$2j5kk_(G$*D=&a)!Y{k}gzW$BMa28&; >> z>$hGI2B7uu#L{#@_dY*}fd54IU_Lrn@Vf%jnBjsk*8QFBrr;w!#H5qL(=iYqR>}NI >> z?hw9WoPxKbaN^lt)>P<{eDF9(9v!{i=aMNBwJeZ%Gt2~OfslGR7a^}of$8Q|`I#iA >> zf|22aS1RSk6=Ld_*vYC8J<%30<IebB92p}5e_lCVekUKOhjv{JCG!se9RN&xBqe2c >> z6!>q>F>k2>mc8I~MuL;B4wvoW5kNXappJb84d3c0R)sNWmx*@7=8kmEc_bdcUe}Iu >> zb_QHpU|T~r;GNcQpG3hJ1;*i%bu)8;ZMtodlo#y9FH}FtHy5d8XZRn<wzTj(+Fq=- >> zo#!TwH=Z6TG|nNbWi2X3QV6MO8Q{Sp_7DomFqV;py0X2>0?+XT7KlSfTW>&xt`Rdi >> z9!z_fNi*On6J`m6Yu~{MS8W|O(K!O}2iXL@eerklu_cK++1mJ=ZBsWe4)|ofiFZ`a >> z;4$(sxw?IRy#ZZOkpk-%?`!i;aDD+LJ`Q?xWM)&k{T0*IE0o_}HA`HzPCn)4I@DwJ >> z(KgaqKzWqYy=p<+sJ)O+2l_3OCM1ZGkU)X{hhg^1B5~C=`4pR{j3S{Q)pXN95Oa<} >> zjieLM=O9XiDC7s@nzCX1d@;GMKy|-hga+9co(FIq0Fb_XcY?bhs9kjPuZU;ko@w?R >> zz*`exRq>Q1`3)XferqOyuIjTXO<Fz6?I$VyP2t9+ckfQLvtpx)JfmI4JSjI=pr10i >> zmL1-goY8m!nN-Yoc2(n0YgFRNoMZrl+-6_OdSu<bD2s*_6E^8MoC&(3>0>vm!RbiC >> zZ=;Lzj0lq7Hu$hQAAXOJ*)lHK0^R!rN}W8KgleM|{MfI#XhjZ<?;D*3hWET6oof%o >> z{-vX$B_gxu$C02N;mv|O-vRTEY~ilrXelc+x2B;LBCc={tJy2;42P#GTX>HyoT@mW >> z71n`ll}#dn`_wY=kjti~tol8o_>(!LQ9y;y$@F2fbKbcZPOgeb67G(2oXR_M!vq%# >> z#YHX*U|#J`!VJvfi)U|lDnBV|-e6q<WCA2Gc!t`^92;Rh9_%|}rH(XMv2F*3a+3I< >> z&TG;<LN427JWG8@{Af$eai!W}KdNbdT#@qjsp`#uNw-Y~k^%Cr=*m#QIX@zfSPWL} >> zWpBkI-8SSt(RMmkr0sApYBSGGyV4X6Ceq!4$0CCE+<ZJ62t{DB4^?1#v+8#&_N}}t >> zW061pq~K9Ou(58*!xJrkx@{=8+ffsVxp13s`_Ug!`0%P{0sS`+g)vU;y;~zi5Z!j^ >> zjSSe@MgRh}hoURU_|9zag~!VbY#{37UiU>T@XOrtFE5Ic#2ZgwfwEmWXL-Wu!{O4C >> zt8Qoa@T=uSByL-v@UGQ~q&D@8-|#f$^&y`{19}Re#aAD|0efP)*!j1oH^_)!?+GMt >> zQf>~F`_iF5a2)>0_Pw3%7%v{*eua#YL4<YR)`}g+np`UC0tZgl&()e&Q*sRY1!9N2 >> zLb>wY@20cb@nA%8+ZL4lC?6x_W^)Q#M%BaNU%5DGZ^>HSN*TYAdyVZSln`CO>5Vrr >> z^6&j>`AqAly;wy)(|PF6DGftXgjQc5Sv3cJyp?B}IDERc>_BYFA8!29iBwk%53jj| >> zedTU%<;5>Q+nQ-0tW=@-F@>ZkTuP<y)9iTdEgKfEaWh&iN*S3_+dHmTCqT$vPn!MS >> z1N*?Oknbb<T~^^C`<dj>7o<M22}TaNvxdg|Q0byxU7Y?jbiWmtRn{J^V8@f|3Q4nd >> zk+?|s^Cl5mh+7s2$Rpr@AXWqUU6-Ab#0&USF`DcMp7Krm#@V&Wr9W+&8y0>&Y`LQ( >> zrp_|#j=kT={`NMdbQAIUXQG72@&j?Nx%EPyl&`jrV0<KJpV;oAB~#etX5rhAcs+Ky >> zl<}iz{<?F^^9DqKBy~AUnNC_lFNR&{To3%=qiVYYHXb*>w|;q;9Bu_9%pNu`Uxl0K >> z>K+8_f9ALdIO(Q3a=Km2KBq=Z!EQf<QB$+yOmjXX_c)is5`~Bzm6%^27u}>G#Hudi >> z5#6_k8*|knj+1UXhy>+s+tvVRMLP)0wX=^5Uz5SBy})JZ9#_523ji#fz2C=L>bu{5 >> zgRl2Q90!c1W<OuFW4OMl?Ud<iTDDwe`Bm*Y=8^GE*NCa|*qS5mEhoyi>{?3{Z?gC- >> zJu-j_JS7cFn%G9CJWLL8WKa<_ZvWku9ky~fZAUe-b50<O;77E?#T;%yj~8-hZ{bcR >> za9nENXtC3%dfd*_6T|Eya<StN{tgZ|xrnuVYB5&AuKshe)tdSuV-mYgN<g`Bi$uu~ >> z!8oKlMl11DkC*j3*3@w?;S>a20~CC8cg!Q~)?9?2)Tp8*%L*eFW7Jkw<h<;T^8XYx >> zs<eK`dx8J`zS;{`<-YVlc~!Q6EQ|L&pR8D!gSBRjwCpKd{R50wH-gIjJ>#X<>*SzV >> zeR8V}8M?w0{<A@gGwW{GuY<|1Z3289;%3Fp&^50IRDIh9cRYk!7PH@Nr2ohlHCuoA >> z%Omr616GTmcAkSY8eX^ZS3cPLR1^&;PciZx^aiHbdOaKPQf?gr7C&)Y&~w6SUrPlu >> zq<@ce4>Pi45EAk}++I}f=SzLrqqN5M^)BwmU_9g5r%esKiMGHgs}|HnXSQej2CsD9 >> zW(qOZ=QKd&(|MuG@nNO>#nG$53hzIh$nd$o6}R0ydXJMAvbj`8WWX^QDHrExSwOLi >> z<R$aOvH1A%ozkVr?E(LFQF7A?wyUtI5>r$Ysgv|IEjJ!?-K}yZV!`R|6+g$CU$!NK >> zF^VbKM<@Tki<C?oU;e_+lTWdnkJ_r2&eu-w_CG2ZYRV&wZsp_Gz?8k}B962ihUcZ) >> z_nYi^gnB5o{)SWt#g?t?Do!zc*{rq^A`ORI0aX#YkSzS$JE(iAq4lipfk!2_dTLCs >> z<7PSrx3Ndb5ZZBM%3(>I{Dd3WDbwIEc*QHRw^|!aH%XYC&8MtH7=45SxI?e#4hYjb >> z@l>t$QgL*#cqage%^Y(nkJ4Euqi#AnHWXmz0IcC1?%G*wzVSu1OdQeQZFN+DPU|N_ >> z<rXfl${*>Y`(niN>9+&EYPUCd`(QQwLl)sHyst(y@*LZg9vrJ^KFB-G*MB8ych2Zo >> z+isQx=!5gC4VO*o1x~Hrh2;`?athds&`#^)r+akUj-GUPs?JX4C$b+OWb?sPua7w{ >> z*8Y%oy5FoG&@DtkFO@9KoMncyynwzIgKtl_to9bR(!S{@)UJ|gc_+E%3G|GXNa >> zux`VhpgbIgJd80h>o)E_X8l#72Q!P%Jh6#<raKqeYq-vf*xyF?reGYu0Ok)rrqQ1O >> zNU~k~wDhQ2F*?A#H>KD9xx!?L3u~7MOaPKG+x-jd#N18CM0}3sU2)^H9QjUZA@!UL > >
Each sentence starts on a new line per the convention we agreed to earlier, but this is input to doxygen converted from the Google docs and is not otherwise line-wrapped. So it's whatever the Google output is. If we have an automated tool to further modify the lines we can use that but otherwise I don't see the value in doing a lot more manual editing on these. Bill On Mon, Aug 4, 2014 at 12:42 PM, Maxim Uvarov <maxim.uvarov@linaro.org> wrote: > How long are strings in that document? Maybe to fit them to 80 characters > to better read in terminal? > > Maxim. > > On 08/04/2014 09:33 PM, Bill Fischofer wrote: > >> Switch to use of @verbatim/@endverbatim to avoid formatting issues with >> different levels of doxygen. >> >> Signed-off-by: Bill Fischofer <bill.fischofer@linaro.org> >> --- >> classification_design.dox | 879 ++++++++++++++++++++++++++++++ >> +++++++++++ >> images/classification_flow.png | Bin 0 -> 35193 bytes >> 2 files changed, 879 insertions(+) >> create mode 100644 classification_design.dox >> create mode 100644 images/classification_flow.png >> >> diff --git a/classification_design.dox b/classification_design.dox >> new file mode 100644 >> index 0000000..0cb7134 >> --- /dev/null >> +++ b/classification_design.dox >> @@ -0,0 +1,879 @@ >> +/* Copyright (c) 2014, Linaro Limited >> + * All rights reserved >> + * >> + * SPDX-License-Identifier: BSD-3-Clause >> + */ >> + >> +/*! >> +@page classification_design ODP Design - Classification API >> +For the implementation of the ODP classification API please see @ref >> odp_classify.h >> + >> +@tableofcontents >> + >> +@section introduction Introduction >> +This document defines the Classification APIs supported by ODP v1.0. >> +Classification is logically composed of two stages: Parsing and Rule >> Matching. >> +Parsing takes a raw packet and validates its structure and identifies >> fields of interest in the various headers that comprise the layers of the >> packet. >> +Rule Matching, in turn, takes the result of parsing and sorts packets >> into Classes of Service (CoS) based on application-defined rule sets. >> +@subsection use_of_terms Use of Terms >> +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", >> "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this >> document are to be interpreted as described in [RFC 2119]( >> https://tools.ietf.org/html/rfc21199). >> +@subsection purpose Purpose >> +ODP is a framework for software-based packet forwarding/filtering >> applications, and the purpose of the Packet Classifier API is to enable >> applications to program the platform hardware or software implementation to >> assist in prioritization, classification and scheduling of each packet, so >> that the software application can run faster, scale better and adhere to >> QoS requirements. >> +The following API abstraction are not modelled after any existing >> product implementation, but is instead defined in terms of what a typical >> data-plane application may require from such a platform, without >> sacrificing simplicity and avoiding ambiguity. Certain terms that are being >> used within the context of existing products in relation to packet parsing >> and classification, such as “access lists” are avoided such that not to >> suggest any relationship between the abstraction used within this API and >> any particular manner in which they may be implemented in hardware. >> +These are the key ODP objects that the parser needs to employ, that are >> presently defined in ODP: >> +@subsubsection odp_pktio odp_pktio >> +odp_pktio specifies an individual packet I/O channel instance. In other >> words, it would translate to a physical interface or a logical port, or in >> the case of channelized protocols (e.g., [Interlaken](https://www. >> google.com/url?q=https%3A%2F%2Fwww.cortina-systems.com% >> 2Fimages%2Fdocuments%2F400023_Interlaken_Technology_White_ >> Paper.pdf&sa=D&sntz=1&usg=AFQjCNEBdJTBmA1XaNGY3pmumQTfgSi1oA)) it would >> map to a logical channel on that interface. >> +Since the classifier API deals exclusively with ingress, this object >> represents the source of packets into the classifier. In order to support >> any non-trivial use case, the classifier API needs to be able to assign >> multiple odp_queue instances for any single odp_pktio object, and may also >> assign any odp_queue instance to more than one odp_pktio object. >> +@subsubsection odp_queue odp_queue >> +odp_queue specifies a logical queue for packets, and in the case of >> ingress, this would represent a stream of packets which share several >> attributes, that are delivered to the ODP application for processing. The >> per-queue attributes currently defined are: queue type, sync (ordering); >> priority; and schedule group (set of processor cores). >> +@subsubsection odp_buffer_pool odp_buffer_pool >> +odp_buffer_pool specifies a collection of buffers of same size and >> alignment, as well as a set of policies such as flow control and processor >> affinity. The classifier API refers to such pools that are designated for >> storing ingress packets. >> +@section functional_description Functional Description >> +Following is the functionality that is required of the classifier API, >> and its underlying implementation. The details and order of the following >> paragraph is informative, and is only intended to help convey the >> functional scope of a classifier and provide context for the API. In >> reality, implementations may execute many of these steps concurrently, or >> in different order while maintaining the evident dependencies: >> + >> +-# Apply a set of \e classification \e rules to the header of an >> incoming packet, identify the header fields, e.g., \e ethertype, IP >> version, IP protocol, transport layer port numbers, IP DiffServ, VLAN id, >> 802.1p priority. >> + >> +-# Store these fields as packet meta data for application use, and for >> the remainder of parser operations. The \e odp_pktio is also stored as one >> of the meta data fields for subsequent use. >> + >> +-# Compute an \e odp_cos (Class of Service) value from a subset of >> supported fields from 1) above. >> + >> + >> +-# Based on the \e odp_cos from 3) above, select the \e odp_queue >> through which the packet is delivered to the application. >> + >> +-# Validate the packet data integrity (checksums, FCS) and correctness >> (e.g., length fields) and store the validation result, along with optional >> error layer and type indicator, in packet meta data. Optionally, if a >> packet fails validation, override the \e odp_cos selection in step 3 to a >> class of service designated for errored packets. >> + >> +-# Since the selected \e odp_queue may require preservation of packet >> order, i.e., SYNC_ATOMIC or SYNC_ORDERED, optionally select the packet >> header fields from which the parser calculates a \e odp_flow_signature, >> which may be a unique flow identifier or a hash, such that the packets >> which are assigned the same \e odp_flow_signature are scheduled in the same >> order they are received. >> + >> +-# Based on the \e odp_cos from 3) above, select the \e odp_buffer_pool >> that should be used to acquire a buffer to store the packet data and meta >> data. >> + >> +-# Allocate a buffer from \e odp_buffer_pool selected in 6) above and >> logically store the packet data and meta data to the allocated buffer, or >> in accordance with class-of-service drop policy and subject to pool buffer >> availability, optionally discard the packet. >> + >> +-# Enqueue the buffer into the \e odp_queue selected in 4) above. >> + >> +The above is an abstract description of the classifier functionality, >> and may be applied to a variety of applications in many different ways. >> +The ultimate meaning of how this functionality applies to an application >> also depends on other ODP modules, so the above may not complete a full >> depiction. >> +For instance, the exact meaning of \e priority, which is a per-queue >> attribute is influenced by the ODP scheduler semantics, and the system >> behavior under stress depends on the ODP buffer pool module behavior. >> + >> +For the sole purpose of illustrating the above abstract functionality, >> here is an example of a Layer-2 (IEEE 802.1D) bridge application: >> +Such a forwarding application that also adheres to IEEE 802.1p/q >> priority, which has 8 traffic priority levels, might create 8 \e >> odp_buffer_pool instances, one for each PCP priority level, and 8 \e >> odp_queue instances one per priority level. >> +Incoming packets will be inspected for a VLAN header; the PCP field will >> be extracted, and used to select both the pool and the queue. >> +Because each queue will be assigned a priority value, the packets with >> highest PCP values will be scheduled before any packet with a lower PCP >> value. >> +Also, in a case of congestion, buffer pools for lower priority packets >> will be depleted earlier than the pools containing packets of the high >> priority, and hence the lower priority packets will be dropped (assuming >> that is the only flow control method that is supported in the platform) >> while higher priority packets will continue to be received into buffers and >> processed. >> +@subsection flow_diagram Classification Processing Flow Diagram >> +@image html classification_flow.png "Figure 1: Classification Flow >> Diagram" >> +@image latex classification_flow.eps "Figure 1: Classification Flow >> Diagram" >> + >> +@section api_elements API Elements >> +While the above description refers to the abstracted packet classifier, >> the following is the description of the API designed to program the packet >> classifier, and is intended to add clarity to the functions provided >> further below. >> +@subsection cos_creation Class of Service Creation and Binding >> +To program the classifier, a class-of-service instance must be created, >> which will contain the packet filtering resources that it may require. >> +All subsequent calls refer to one or more of these resources. >> +Each class of service instance must be associated with a single queue or >> queue group, which will be the destination of all packets matching that >> particular filter. >> +The queue assignment is implemented as a separate function call such >> that the queue may be modified at any time, without tearing down the >> filters that define the class of service. In other words, it is possible to >> change the destination queue for a class of service defined by its filters >> quickly and dynamically. >> +Optionally, on platforms that support multiple packet buffer pools, each >> class of service may be assigned a different pool such that when buffers >> are exhausted for one class of service, other classes are not negatively >> impacted and continue to be processed. >> + >> +@subsection default_packet_handling Default packet handling >> +There SHOULD be one \b odp_cos assigned to each port with the \c >> odp_cos_pktio_set() function, which will function as the default >> class-of-service for all packets received from an ingress port, that do not >> match any of the filters defined subsequently. At minimum this default >> class-of-service MUST have a queue and a buffer pool assigned to it on >> platforms that support multiple packet buffer pools. Multiple odp_pktio >> instances (i.e., multiple ports) MAY each have their own default odp_cos, >> or MAY share a odp_cos with other ports, based on application requirements. >> + >> +@subsection packet_classification Packet Classification >> +For each odp_pktio port, the API allows the assignment of a >> class-of-service to a packet using one of three methods: >> + >> +-# The packet may be assigned a specific class-of-service based on its >> Layer-2 (802.1P/902.1Q VLAN tag) priority field. Since the standard field >> defines 8 discrete priority levels, the API allows to assign an odp_cos to >> each of these priority levels with the \c odp_cos_with_l2_priority() >> function. >> + >> +-# Similarly, a class-of-service may be assigned using the Layer-3 (IP >> DiffServ) header field. The application supplies an array of \e odp_cos >> values that covers the entire range of the standard protocol header field, >> where array elements do not need to contain unique values. There is also a >> need to specify if Layer-3 priority takes precedence over Layer-2 priority >> in a packet with both headers present. >> + >> +-# Additionally, the application may also program a number of \e pattern >> \e matching \e rules that assign a class-of-service for packets with header >> fields matching specified values. The field-matching rules take precedence >> over the previously described priority-based assignment of a >> class-of-service. Using these matching rules the application should be able >> for example to identify all packets containing VoIP traffic based on the >> protocol being UDP, and a specific destination or source port numbers, and >> appropriately assign these packets an class-of-service that maps to a >> higher priority queue, assuring voice packets a lower and bound latency. >> + >> +@subsection scaling_and_flow Scaling and Flow Discrimination >> +In addition to classifying packets and routing them to those queues with >> the appropriate priority, and optionally limiting their memory consumption >> by designating certain classes of packets to specific buffer pools, the >> classifier API also facilitates the scaling of data-plane application on >> multi-core systems by creating a mechanism to define which packet headers >> need to be combined to result in a value representing a specific packet >> flow. The classifier generates a signature, which can be a checksum or hash >> of arbitrary strength that covers those packet header fields that are >> identified by the application as identifying flows. >> + >> +The \e flow \e signatures that result from hashing are then stored with >> the packet meta data (along with its class-of-service and its ingress \e >> odp_pktio port), and subsequently may be utilized by the implementation of >> a scheduler queue to maintain the order of packets with the same flow >> signature, while allowing packets with different signatures to be processed >> concurrently and independently on different processing cores. >> + >> +@subsection packet_meta_data Packet meta data Elements >> +Here are the specific information elements that SHOULD be stored within >> the packet meta data structure: >> +- Protocol fields that are decoded and extracted by the parsing phase >> +- Flow-signature calculated from a prescribed collection of protocol >> fields >> +- The class-of-service identifier that is selected for the packet >> +- The ingress port identifier >> +- The result of packet validation, including an indication of the type >> of error detected, if any >> + >> +The ODP packet API module SHALL provide accessors for retrieving the >> above meta data fields from the container buffer in an >> implementation-independent manner. >> + >> +@section api_definitions API Definitions >> +@subsection data_types Data Types >> +The following data types are referenced in the API descriptions >> described below. >> +The names are part of the ODP API and MUST be present in any conforming >> implementation, however the type values shown here are illustrative and >> implementations SHOULD either use these or substitute their own type values >> that are appropriate to the underlying platform. >> + >> +@verbatim >> +/** >> + * 'odp_pktio_t' value to indicate any port >> + */ >> +#define ODP_PKTIO_ANY ((odp_pktio_t)~0) >> + >> + >> +/** >> + * 'odp_pktio_t' value to indicate an error >> + */ >> +#define ODP_PKTIO_INVALID ((odp_pktio_t)0) >> + >> + >> +/** >> + * Class of service instance type >> + */ >> +typedef uint32_t odp_cos_t; >> + >> + >> +/** >> + * flow signature type, only used for packet meta data field. >> + */ >> +typedef uint32_t odp_flowsig_t; >> + >> + >> +/** >> + * This value is returned from odp_cos_create() on failure, >> + * May also be used as a “sink” class of service that >> + * results in packets being discarded. >> + */ >> +#define ODP_COS_INVALID ((odp_cos_t)~0) >> +@endverbatim >> + >> +@subsection cos_routines Class of Service Routines >> +Conforming ODP implementations MUST provide the following Classification >> APIs: >> +@subsubsection cos_create odp_cos_create >> +@verbatim >> +/** >> + * Create a class-of-service >> + * >> + * @param name is a string intended for debugging purposes. >> + * >> + * @return Class of service instance identifier, >> + * or ODP_COS_INVALID on error. >> + */ >> + >> +odp_cos_t odp_cos_create(const char *name); >> +@endverbatim >> + >> +This routine is used to create a class of service that can be the target >> of classifier rules. >> +The number of such classes supported is implementation-defined. >> +Attempts to create more than are supported by the implementation will >> result in an \c ODP_COS_INVALID return and errno being set to \c >> ODP_IMPLEMENTATION_LIMIT. >> + >> +@subsubsection cos_destroy odp_cos_destroy >> +@verbatim >> +/** >> + * Discard a class-of-service along with all its associated resources >> + * >> + * @param cos_id class-of-service instance. >> + * >> + * @return 0 on success, -1 on error. >> + */ >> + >> +int odp_cos_destroy(odp_cos_t cos_id); >> +@endverbatim >> + >> +This routine is the bracketing routine for odp_cos_create(). >> +It is used to destroy an existing CoS. >> +It is the caller’s responsibility to ensure that no active pattern >> matching rules refer to the CoS prior to calling this routine. >> +Results are unpredictable if this restriction is not met. >> +@subsubsection cos_set_queue odp_cos_set_queue >> +@verbatim >> +/** >> + * Assign a queue for a class-of-service >> + * >> + * @param cos_id class-of-service instance. >> + * >> + * @param queue_id is the identifier of a queue where all >> packets >> + * of this specific class of service will be enqueued. >> + * >> + * @return 0 on success, negative error code on failure. >> + */ >> + >> +int odp_cos_set_queue(odp_cos_t cos_id, odp_queue_t queue_id); >> +@endverbatim >> + >> +This routine associates a target queue with a CoS such that all packets >> assigned to this CoS will be enqueued to the specified queue_id at the end >> of classification processing. >> +@subsubsection cos_set_queue_group odp_cos_set_queue_group >> +@verbatim >> +/** >> + * Assign a homogenous queue-group to a class-of-service. >> + * >> + * @param cos_id identifier of class-of-service instance >> + * @param queue_group_id identifier of the queue group to receive packets >> + * associated with this class of service. >> + * >> + * @return 0 on success, negative error code on failure. >> + */ >> + >> +int odp_cos_set_queue_group(odp_cos_t cos_id, odp_queue_group_t >> queue_group_id); >> +@endverbatim >> + >> +This routine associates a target queue group with a CoS such that all >> packets assigned to this CoS will be distributed to the specified >> queue_group_id at the end of classification processing. >> +@subsubsection cos_set_pool odp_cos_set_pool >> +@verbatim >> +/** >> + * Assign packet buffer pool for specific class-of-service >> + * >> + * @param cos_id class-of-service instance. >> + * @param pool_id is a buffer pool identifier where all packet buffers >> + * will be sourced to store packet that belong to this >> + * class of service. >> + * >> + * @return 0 on success negative error code on failure. >> + * >> + * >> + */ >> + >> +int odp_cos_set_pool(odp_cos_t cos_id, odp_buffer_pool_t pool_id); >> +@endverbatim >> + >> +This OPTIONAL routine associates a target buffer pool with a CoS such >> that all packets assigned to this CoS will be stored in packet buffers >> allocated from the designated pool_id. >> + >> + >> +@subsection cos_drop_policy Class of Service Drop Policy Routines >> +These routines control how drop policies are to be observed for a given >> class of service. >> +@subsubsection drop_data_types Data types >> +~~~~~{.c} >> +enum odp_cos_drop_e { >> + ODP_COS_DROP_POOL, /**< Follow buffer pool drop policy */ >> + ODP_COS_DROP_NEVER, /**< Never drop, ignoring buffer pool >> policy */ >> +}; >> +typedef enum odp_drop_e odp_drop_t; >> +~~~~~ >> + >> +@subsubsection cos_set_drop odp_cos_set_drop >> +@verbatim >> +/** >> + * Assign packet drop policy for specific class-of-service >> + * >> + * @param cos_id class-of-service instance. >> + * @param drop_policy is the desired packet drop policy for this class. >> + * >> + * @return 0 on success negative error code on failure. >> + */ >> + >> +int odp_cos_set_drop(odp_cos_t cos_id, odp_drop_t drop_policy); >> +@endverbatim >> + >> +This routine sets the drop policy for a class of service. >> +It is an OPTIONAL routine. >> +If an implementation does not provide this function it MUST supply a >> definition of it that simply returns ODP_FUNCTION_NOT_AVAILABLE. >> +@subsubsection pktio_set_default_cos odp_pktio_set_default_cos >> +@verbatim >> +/** >> + * Setup per-port default class-of-service >> + * >> + * @param pktio_in ingress port identifier. >> + * @param default_cos class-of-service set to all packets arriving >> + * at the 'pktio_in' ingress port, unless overridden by >> subsequent >> + * header-based filters. >> + * >> + * @return 0 on success negative error code on failure. >> + * >> + * >> + * @note This may replace the default queue per pktio. >> + */ >> + >> +int odp_pktio_set_default_cos(odp_pktio_t pktio_in, odp_cos_t >> default_cos); >> +@endverbatim >> + >> +This routine specifies a default class of service for a given pktio >> instance. >> +Incoming packets on the specified pktio are assigned to this class of >> service if no other pattern matching rule obtains. >> +@subsubsection pktio_set_error_cos odp_pktio_set_error_cos >> +@verbatim >> +/** >> + * Setup per-port error class-of-service >> + * >> + * @param pktio_in ingress port identifier. >> + * @param error_cos class-of-service set to all packets arriving >> + * at the 'pktio_in' ingress port that contain an error. >> + * >> + * @return 0 on success negative error code on failure. >> + */ >> + >> +int odp_pktio_set_error_cos(odp_pktio_t pktio_in, odp_cos_t error_cos); >> +@endverbatim >> + >> +This OPTIONAL function assigns a class-of-service used to handle packets >> containing various types of errors. The specific errors types include L2 >> FCS and optionally L3/L4 checksum errors, malformed headers, etc., >> depending on platform capabilities. >> +The specified error_cos MAY simply discard these packets or deliver them >> via a queue to the application for further processing. >> +@subsubsection pktio_set_skip odp_pktio_set_skip >> +@verbatim >> +/** >> + * Setup per-port header offset >> + * >> + * @param pktio_in ingress port identifier. >> + * @param offset is the number of bytes the classifier must skip. >> + * >> + * @return Success or ODP_FUNCTION_NOT_AVAILABLE >> + */ >> + >> +int odp_pktio_set_skip(odp_pktio_t pktio_in, size_t offset); >> +@endverbatim >> + >> +This OPTIONAL function applies to ports that carry an additional headers >> preceding the standard Ethernet header. Such headers are typically >> vendor-specific and thus the classifier is not required to parse such >> headers, but the size of a custom header is critical for the classifier to >> be able to parse standard protocol headers that normally follow. >> +@subsubsection cos_set_headroom odp_cos_set_headroom >> +@verbatim >> +/** >> + * Specify per-port buffer headroom >> + * >> + * @param pktio_in ingress port identifier. >> + * @param headroom number of bytes of space preceding packet data to >> reserve >> + * for use as headroom. Must not exceed the >> implementation >> + * defined ODP_PACKET_MAX_HEADROOM. >> + * >> + * @return Success or ODP_PARAMETER_ERROR, >> + * or ODP_FUNCTION_NOT_AVAILABLE >> + */ >> + >> +int odp_cos_set_headroom(odp_cos_t cos_id, size_t req_room); >> +@endverbatim >> + >> +This OPTIONAL routine specifies the number of bytes of headroom that >> should be reserved for each packet assigned to this class of service. >> +Each implementation defines an ODP_PACKET_MAX_HEADROOM limit that sets >> an upper bound on the size of the headroom that can be reserved for a >> packet. >> +@subsubsection cos_with_l2_priority odp_cos_with_l2_priority >> +@verbatim >> +/** >> + * Request to override per-port class of service >> + * based on Layer-2 priority field if present. >> + * >> + * @param pktio_in ingress port identifier. >> + * @param num_qos is the number of QoS levels, typically 8. >> + * @param qos_table are the values of the Layer-2 QoS header field. >> + * @param cos_table is the class-of-service assigned to each of the >> + * allowed Layer-2 QOS levels. >> + * @return 0 on success negative error code on failure. >> + */ >> + >> +int odp_cos_with_l2_priority(odp_pktio_t pktio_in, >> + size_t num_qos, >> + uint8_t qos_table[], /**< >> 'num_qos' elements */ >> + odp_cos_t cos_table[]); /**< >> 'num_qos' elements */ >> +@endverbatim >> + >> +This routine is used to assign classes of service based on the layer 2 >> (L2) priority associated with input packets received on the specified >> pktio_in. >> +For each of the values in qos_table[], the corresponding value in >> cos_table[] will be assigned. >> +@subsubsection cos_with_l3_dscp odp_cos_with_l3_dscp >> +@verbatim >> +/** >> + * >> + * @param pktio_in ingress port identifier. >> + * @param num_qos is the number of allowed Layer-3 QoS levels. >> + * @param qos_table are the values of the Layer-3 QoS header field. >> + * @param cos_table is the class-of-service assigned to each of the >> + * allowed Layer-3 QOS levels. >> + * @param l3_preference when true, Layer-3 QoS overrides L2 QoS when >> present. >> + * >> + * @return 0 on success negative error code on failure. >> + */ >> + >> +int odp_cos_with_l3_qos(odp_pktio_t pktio_in, >> + size_t num_qos, >> + uint8_t qos_table[], /**< 'num_qos' >> elements */ >> + odp_cos_t cos_table[], /**< 'num_qos' >> elements */ >> + odp_bool_t l3_preference); >> +@endverbatim >> + >> +This OPTIONAL routine is used to assign classes of service based on the >> layer 3 (L3) Differentiated Services (DS) designation. >> +This is the DSCP field of an IPv4 header or the first six bits of the >> Traffic Class of an IPv6 header. >> +For each of the values in qos_table[], the corresponding value in >> cos_table[] will be assigned. >> +The l3_preference flag is use to control whether the CoS assigned by >> this routine takes precedence over the CoS assigned by >> odp_cos_with_l2_priority() in the event that both apply to the same packet. >> + >> +@subsection pmrs Pattern Matching Rules >> +While the above routines permit class of service assignments to be made >> based on static criteria, the real power of classification is the ability >> to identify flows based on the variable contents of packet headers. >> +To do this ODP provides support for defining pattern matching rules >> (PMRs) that operate based on values contained in specified header fields. >> + >> +Associated with PMRs are enums that are used to specify standard packet >> header fields: >> +@subsubsection cos_hdr_flow_fields odp_cos_hdr_flow_fields_e >> +@verbatim >> +/** >> + * Packet header field enumeration >> + * for fields that may be used to calculate >> + * the flow signature, if present in a packet. >> + */ >> + >> +enum odp_cos_hdr_flow_fields_e { >> + ODP_COS_FHDR_IN_PKTIO, /**< Ingress port number */ >> + ODP_COS_FHDR_L2_SAP, /**< Ethernet Source MAC address */ >> + ODP_COS_FHDR_L2_DAP, /**< Ethernet Destination MAC >> address */ >> + ODP_COS_FHDR_L2_VID, /**< Ethernet VLAN ID */ >> + ODP_COS_FHDR_L3_FLOW /**< IPv6 flow_id */ >> + ODP_COS_FHDR_L3_SAP, /**< IP source address */ >> + ODP_COS_FHDR_L3_DAP, /**< IP destination address */ >> + ODP_COS_FHDR_L4_PROTO, /**< IP protocol (e.g. >> TCP/UDP/ICMP) */ >> + ODP_COS_FHDR_L4_SAP, /**< Transport source port */ >> + ODP_COS_FHDR_L4_DAP, /**< Transport destination port */ >> + ODP_COS_FHDR_IPSEC_SPI, /**< IPsec session identifier */ >> + ODP_COS_FHDR_LD_VNI, /**< NVGRE/VXLAN network >> identifier */ >> + ODP_COS_FHDR_USER /**< Application-specific header >> field(s) */ >> +}; >> +@endverbatim >> + >> +Conforming ODP implementations SHOULD implement efficient flow set >> management routines such as these: >> + >> +~~~~~{.c} >> +/** >> + * Set of header fields that take part in flow signature hash >> calculation: >> + * bit positions per 'odp_cos_hdr_flow_fields_e' enumeration. >> + * >> +typedef uint16_t odp_cos_flow_set_t; >> + >> + >> +/** >> + * Set a member of the flow signature fields data set >> + * >> +static inline odp_cos_flow_set_t >> +odp_cos_flow_set( odp_cos_flow_set_t set, >> + enum odp_cos_hdr_flow_fields_e field) >> +{ >> + return set | (1U << field); >> +} >> + >> + >> +/** >> + * Test a member of the flow signature fields data set >> + * >> +static inline bool >> +odp_cos_flow_is_set( odp_cos_flow_set_t set, >> + enum odp_cos_hdr_flow_fields_e field) >> +{ >> + return (set & (1U << field)) != 0; >> +} >> +~~~~~ >> + >> +These routines are intended to be used in support of the following flow >> signature APIs: >> + >> +@subsubsection cos_class_flow_sig odp_cos_class_flow_signature >> +@verbatim >> +/** >> + * Set up set of headers used to calculate a flow signature >> + * based on class-of-service. >> + * >> + * @param cos_id class of service instance identifier >> + * @param req_data_set requested data-set for flow signature calculation >> + * >> + * @return data-set that was successfully applied. All-zeros data set >> + * indicates a failure to assign any of the requested fields, or other >> + * error. >> + */ >> + >> +odp_cos_flow_set_t >> +odp_cos_class_flow_signature(odp_cos_t cos_id, >> + odp_cos_flow_set_t req_data_set); >> +@endverbatim >> + >> +This OPTIONAL routine associates a fow set with a class of service for >> flow signature calculation. >> + >> +@subsubsection cos_port_flow_sig odp_cos_port_flow_signature >> +@verbatim >> +/** >> + * Set up set of headers used to calculate a flow signature >> + * based on ingress port. >> + * >> + * @param pktio_in ingress port identifier. >> + * @param req_data_set requested data-set for flow signature calculation >> + * >> + * @return data-set that was successfully applied. An all-zeros data-set >> + * indicates a failure to assign any of the requested fields, or other >> + * error. >> + */ >> + >> +odp_cos_flow_set_t >> +odp_cos_port_flow_signature(odp_pktio_t pktio_in, >> + odp_cos_flow_set_t req_data_set); >> +@endverbatim >> + >> +@subsection pmr_routines Pattern Matching Rules Routines >> +The following data structures SHOULD be implemented to support the >> definition of pattern matching routines by conforming ODP implementations: >> + >> +~~~~~{.c} >> +/** >> + * PMR - Packet Matching Rule >> + * Up to 32 bit of ternary matching of one of the available header fields >> + * >> + >> + >> +#define ODP_PMR_INVAL ((odp_pmr_t)NULL) >> +typedef struct odp_pmr_s *odp_pmr_t; >> +~~~~~ >> + >> +@subsecion terms Terms >> +Terms are the elements of a PMR and are identified by the following enum: >> + >> +@verbatim >> +enum odp_pmr_term_e { >> + ODP_PMR_ETHTYPE_0, /**< Initial (outer) Ethertype only >> (*val=uint16_t)*/ >> + ODP_PMR_ETHTYPE_X, /**< Ethertype of most inner VLAN tag >> (*val=uint16_t)*/ >> + ODP_PMR_VLAN_ID_0, /**< First VLAN ID (outer) (*val=uint16_t) >> */ >> + ODP_PMR_VLAN_ID_X, /**< Last VLAN ID (inner) (*val=uint16_t) >> */ >> + ODP_PMR_DMAC, /**< destination MAC address >> (*val=uint64_t) */ >> + ODP_PMR_IPPROTO, /**< IP Protocol or IPv6 Next Header >> (*val=uint8_t) */ >> + ODP_PMR_UDP_DPORT, /**< Destination UDP port, implies >> IPPROTO=17 */ >> + ODP_PMR_TCP_DPORT, /**< Destination TCP port implies >> IPPROTO=6 */ >> + ODP_PMR_UDP_SPORT, /**< Source UDP Port (*val=uint16_t) */ >> + ODP_PMR_TCP_SPORT, /**< Source TCP port (*val=uint16_t) */ >> + ODP_PMR_SIP_ADDR, /**< Source IP address (uint32_t) */ >> + ODP_PMR_DIP_ADDR, /**< Destination IP address (uint32_t) */ >> + ODP_PMR_SIP6_ADDR, /**< Source IP address (uint8_t[16]) */ >> + ODP_PMR_DIP6_ADDR, /**< Destination IP address (uint8_t[16]) >> */ >> + ODP_PMR_IPSEC_SPI, /**< IPsec session >> identifier(*val=uint32_t) */ >> + ODP_PMR_LD_VNI, /**< NVGRE/VXLAN network identifier >> (*val=uint32_t) */ >> + >> + >> + /** Inner header may repeat above values with this offset */ >> + ODP_PMR_INNER_HDR_OFF=32 >> +}; >> +@endverbatim >> + >> +@subsubsection tunnel_considerations Tunnel Considerations >> +Note that PMRs may be extended to support tunnels and tenants (NVGRE, >> VXLAN) via the ODP_PMR_INNER_HDR_OFF enum. >> +This enum is intended to be used as an “adder” to a PMR to indicate that >> the term refers to an inner header. >> +For example, the term ODP_PMR_DMAC would refer to the destination MAC >> address of the packet if the packet is not a tunnel, or of the outer header >> (the tunnel) if the packet is a tunnel. >> +To refer to the inner (tenant) destination MAC, the term would be >> specified as ODP_PMR_INNER_HDR_OFF+ODP_PMR_DMAC. >> + >> +@subsection pmr_apis PMR APIs >> +The following APIs are provided to enable an ODP application to specify >> PMRs as a series of individual or cascaded terms: >> +@subsubsection pmr_create_match odp_pmr_create_match >> +@verbatim >> +/** >> + * Create a packet match rule with mask and value >> + * >> + * @param term is one value of the enumerated values supported >> + * @param val is the value to match against the packet header >> + * in native byte order. >> + * @param mask is the mask to indicate which bits of the header >> + * should be matched ('1') and which should be ignored >> ('0') >> + * @param val_sz size of the ‘val’ and ‘mask’ arguments, >> + * that must match the value size requirement of the >> + * specific ‘term’. >> + * >> + * @return a handle of the matching rule or ODP_PMR_INVAL on error >> + */ >> + >> +odp_pmr_t odp_pmr_create_match(enum odp_pmr_term_e term, >> + const void *val, const void *mask, size_t >> val_sz); >> +@endverbatim >> + >> +This routine creates a PMR that matches a single value to a term. >> + >> +@subsubsection pmr_create_range odp_pmr_create_range >> +@verbatim >> +/** >> + * Create a packet match rule with value range >> + * >> + * @param term is one value of the enumerated values supported >> + * @param val1 is the lower bound of the header field range. >> + * @param val2 is the upper bound of the header field range. >> + * @param val_sz size of the ‘val1’ and ‘val2’ arguments, >> + * that must match the value size requirement of the >> + * specific ‘term’. >> + * >> + * @return a handle of the matching rule or ODP_PMR_INVAL on error >> + * @note: Range is inclusive [val1..val2]. >> + */ >> + >> +odp_pmr_t odp_pmr_create_range(enum odp_pmr_term_e term, >> + const void *val1, const void >> *val2, size_t val_sz); >> +@endverbatim >> + >> +This routine creates a PMR that matches an inclusive range of values to >> a term. >> + >> +@subsubsection pmr_destroy odp_pmr_destroy >> +@verbatim >> +/** >> + * Invalidate a packet match rule and vacate its resources >> + * >> + * @param pmr_id the identifier of the PMR to be destroyed >> + * >> + * @return Success or ODP_PMR_INVALID if the specified pmr_id not found. >> + */ >> + >> +int odp_pmr_destroy(odp_omr_t pmr_id); >> +@endverbatim >> + >> +This routine destroys a previously created PMR. >> +If the PMR is currently associated with an active class of service it is >> unpredictable at which point the match defined by the PMR is deactivated in >> terms of packet flow. >> +However, implementations MUST ensure that a PMR is either matched or not >> matched in its entirety such that dynamic changes to PMRs do not result in >> partial matches. >> + >> +@subsubsection pktio_pmr_cos odp_pktio_pmr_cos >> +@verbatim >> +/** >> + * Apply a PMR to a pktio to assign a CoS. >> + * >> + * @param pmr_id the id of the PMR to be activated >> + * @param src_pktio the pktio to which this PMR is to be applied >> + * @param dst_cos the CoS to be assigned by this PMR >> + * >> + * @return Success or ODP_PARAMETER_ERROR >> + */ >> + >> +int odp_pktio_pmr_cos(odp_pmr_t pmr_id, odp_pktio_t src_pktio, odp_cos_t >> dst_cos); >> +@endverbatim >> + >> +This routine links a pktio to a corresponding class of service via a >> specified PMR. >> +Any packet received on the specified src_pktio that matches the >> specified pmr_id will be assigned to the specified dst_cos. >> +If multiple PMRs match the implementation MAY define an inherent >> precedence or it MAY be unpredictable as to which PMR will determine the >> assigned CoS. >> +For this reason applications SHOULD NOT be written to use conflicting or >> ambiguous PMR definitions. >> + >> +@subsubsection cos_pmr_cos odp_cos_pmr_cos >> +@verbatim >> +/** >> + * Cascade a PMR to refine packets from one CoS to another. >> + * >> + * @param pmr_id the id of the PMR to be activated >> + * @param src_cos the id of the CoS to be filtered >> + * @param dst_cos the id of the CoS to be assigned to packets filtered >> + * from src_cos that match pmr_id. >> + * >> + * @return Success or ODP_PARAMETER_ERROR if an input is in error >> + * or ODP_IMPLEMENTATION_LIMIT if cascade depth is >> exceeded >> + */ >> + >> +int odp_cos_pmr_cos(odp_pmr_t pmr_id, odp_cos_t src_cos, odp_cos_t >> dst_cos); >> +@endverbatim >> + >> +This routine is used to cascade PMRs by passing packets assigned to the >> src_cos through another PMR. >> +Those matching are reassigned to the specified dst_cos. >> +Note that this process can be repeated to an implementation-defined >> maximum supported cascade depth. >> +When cascades are defined, the actual class of service assigned to a >> packet is the result of the longest chain of PMRs that can be matched >> against the packet. >> + >> +For example, suppose the following sequence of PMRs is in effect: >> + >> +@verbatim >> +odp_pktio_pmr_cos(pmr_idA, pktio_id, cos_idA); >> +odp_cos_pmr_cos(pmr_idB, cos_idA, cos_idB); >> +odp_cos_pmr_cos(pmr_idC, cos_idB, cos_idC); >> +odp_cos_pmr_cos(pmr_idD, cos_idC, cos_idD); >> +@endverbatim >> + >> +If a packet arrives on pktio_id that matches pmr_idA it is assigned to >> cos_idA. >> +But since it is now on cos_idA it is further filtered by pmr_idB and if >> it matches is reassigned to cos_idB. >> +This process continues until no further more specific match is found to >> determine the final CoS that the packet receives. >> + >> +Note that given this rule set a packet that matched pmr_idA and pmr_idC >> it would be assigned to cos_idA because the rule that can assign packets to >> pmr_idC is only applicable to packets that are assigned to cos_idB, not >> cos_idA. >> + >> +Using cascaded PMRs it is possible to build quite sophisticated filters >> (up to the implementation limits supported by a given platform). >> +For example, one could add additional rules to the above set: >> + >> +@verbatim >> +odp_cos_pmr_cos(pmr_idAC, cos_idA, cos_idC); >> +odp_cos_pmr_cos(pmr_idAD, cos_idA, cos_idD); >> +@endverbatim >> + >> +To cover cases where some packets on cos_idA should be further sorted to >> cos_idB while others should be sorted directly to cos_idC or cos_idD. >> +Again it is the application’s responsibility to ensure that the cascades >> remain unambiguous and that loops be avoided (e.g., having rules that >> bounce packets between cos_idA and cos_idB endlessly). >> + >> +@subsection pmr_stats PMR Statistics >> +Conforming ODP implementations SHOULD maintain statistics regarding PMRs >> and provide the following routines for retrieving them: >> + >> +@subsubsection pmr_match_count odp_pmr_match_count >> +@verbatim >> +/** >> + * Retrieve packet matcher statistics >> + * >> + * @param pmr_id the id of the PMR from which to retrieve the count >> + * >> + * @return The current number of matches for a given matcher instance. >> + */ >> + >> +signed long odp_pmr_match_count(odp_pmr_t pmr_id); >> +@endverbatim >> + >> +@subsubsection pmr_terms_cap odp_pmr_terms_cap >> +@verbatim >> +/** >> + * Inquire about matching terms supported by the classifier >> + * >> + * @return A mask one bit per enumerated term, one for each of >> op_pmr_term_e >> + */ >> + >> +unsigned long long odp_pmr_terms_cap(void); >> +@endverbatim >> + >> +@subsubsection pmr_terms_avail odp_pmr_terms_avail >> +@verbatim >> +/** >> + * Return the number of packet matching terms available for use >> + * >> + * @return A number of packet matcher resources available for use. >> + */ >> + >> +unsigned odp_pmr_terms_avail(void); >> +@endverbatim >> + >> +@subsection pmr_composite_rules Pattern Matching Composite Routines >> +As a shorthand, applications MAY express pattern matching rules using a >> table rather than constructing them term-by-term. >> +ODP implementations MUST support both methods of rule specification but >> MAY have implementation-specific restrictions on the complexity of >> table-based rules they support. >> +Note that some implementations MAY be able to implement tables directly >> while others MAY choose to implement tables by internally generating the >> equivalent set of term generating calls. >> + >> +@subsubsection pmr_table_structure PMR Table Structure >> +@verbatim >> +/** >> + * Following structure is used to define composite packet matching rules >> + * in the form of an array of individual match or range rules. >> + * The underlying platform may not support all or any specific >> combination >> + * of value match or range rules, and the application should take care >> + * of inspecting the return value when installing such rules, and perform >> + * appropriate fallback action. >> + */ >> + >> +typedef struct odp_pmr_match_t { >> + enum odp_pmr_match_type_e { >> + ODP_PMR_MASK, /**< Match a masked set of >> bits */ >> + ODP_PMR_RANGE, /**< Match an integer range >> */ >> + } match_type; >> + union { >> + struct { >> + enum odp_pmr_term_e term; >> + const void *val; >> + const void *mask; >> + unsigned int val_sz; >> + } mask; /**< Match a masked set of bits */ >> + struct { >> + enum odp_pmr_term_e term; >> + const void *val1; >> + const void *val2; >> + unsigned int val_sz; >> + } range; /**< Match an integer range */ >> + }; >> +} odp_pmr_match_t; >> + >> + >> +/** An opaque handle to a composite packet match rule-set */ >> +typedef struct odp_pmr_set_s *odp_pmr_set_t; >> +@endverbatim; >> + >> +The above structure is used with the following APIs to implement >> table-based PMRs: >> + >> +@subsubsection pmr_match_set_create odp_pmr_match_set_create >> +@verbatim >> +/** >> + * Create a composite packet match rule >> + * >> + * @param num_terms is the number of terms in the match rule. >> + * @param terms is an array of num_terms entries, one entry per >> + * term desired. >> + * @param dst_cos is the class-of-service to be assigned to packets >> + * that match the compound rule-set, or a subset >> thereof, >> + * if partly applied. >> + * @param pmr_set_id is the returned handle to the composite rule set. >> + * >> + * @return The return value may be a negative number indicating a general >> + * error, or a positive number indicating the number of ‘terms’ elements >> that >> + * have been successfully mapped to the underlying platform >> classification engine, >> + * and may be in the range from 1 to ‘num_terms’. >> + */ >> + >> +int odp_pmr_match_set_create(int num_terms, odp_pmr_match_t *terms, >> + odp_pmr_set_t *pmr_set_id); >> +@endverbatim >> + >> +This routine is used to create a PMR match set. >> + It is the equivalent to a cascade of PMRs except that there are no >> “intermediate” classes of service defined. >> +Instead, the entire match set either matches or does not match as a >> single entity. >> + >> +@subsubsection pmr_match_set_destroy odp_pmr_match_set_destroy >> +@verbatim >> +/** >> + * Function to delete a composite packet match rule set >> + * >> + * All of the resources pertaining to the match set associated with the >> + * class-of-service will be released, but the class-of-service will >> + * remain intact. >> + * >> + * @param pmr_set_id a composite rule-set handle returned when created. >> + * >> + * @note Depending on the implementation details, destroying a rule-set >> + * may not guarantee the availability of hardware resources to create the >> + * same or essentially similar rule-set. >> + */ >> + >> +int odp_pmr_match_set_destroy(odp_pmr_set_t pmr_set_id); >> +@endverbatim >> + >> +This routine destroys a PMR match set previously created by >> odp_pmr_match_set_create(). >> + >> +@subsubsection pktio_pmr_match_set_cos odp_pktio_pmr_match_set_cos >> +@verbatim >> +/** >> + * Apply a PMR Match Set to a pktio to assign a CoS. >> + * >> + * @param pmr_set_id the id of the PMR match set to be activated >> + * @param src_pktio the pktio to which this PMR match set is to be >> applied >> + * @param dst_cos the CoS to be assigned by this PMR match set >> + * >> + * @return Success or ODP_PARAMETER_ERROR >> + */ >> + >> +int odp_pktio_pmr_match_set_cos(odp_pmr_t pmr_id, odp_pktio_t src_pktio, >> + odp_cos_t dst_cos); >> +@endverbatim >> + >> +This routine is the same as odp_pktio_pmr_cos() except that it operates >> on PMR match sets rather than individual PMRs. >> + >> +@section items_pending Items pending resolution >> +- Revise ‘odp_packet_io.h’ API with respect of default input queue per >> ‘pktio’ instance. >> +- Revise ‘odp_queue.h’ API to support an arbitrary priority range, >> typically 8 priority levels with numeric priority values are >> platform-specific. >> +- Add specific packet meta data fields to go into packet buffer which >> contain all meta data fields parsed and generated by the classifier, for >> later application use. >> + >> +@section implementation_notes Implementation Notes >> +The following sections are not part of the specification, but shed light >> into the intent of the specification in several areas, describing some >> specific implementation approaches of these aspects. >> + >> +@subsection supporting_multi_pools Supporting multiple buffer pools >> +The support of multiple buffer pools for containing packet buffers is >> optional, and may not be supported by some platforms. >> +The importance of this feature stems from the need of protecting a >> networking application in the event of a congestion, or an attempted denial >> of service attack. >> +Separating different classes of service to dedicated buffer pools allows >> the system to limit the memory resources that may be consumed by a >> particular type of traffic, thereby reserving buffer resources for other >> classes of traffic. >> + >> +In a software implementation, a packet would already be stored in memory >> when the classifier is invoked, and so it seems the classifier is unable to >> insert itself into the process of selecting a buffer pool. >> +For obvious reasons the copying of a packet into a new buffer allocated >> from a different pool by the classifier is not a desirable solution. >> + >> +The recommended solution is to implement buffer pools in the form of >> buffer counters, while the actual buffers all belong to a single free list >> when not used to store a packet. >> +In such an implementation, the classifier will be able to associate a >> packet already occupying a buffer to a different pool than the default by >> incrementing the buffer counter of the newly selected pool, and >> decrementing the counter representing the default pool. >> +If however the selected pool counter has already reached a certain >> limit, the classifier would be able to e.g discard the packet instead of >> incrementing the destination pool counter, and thereby enforce the >> desirable semantics of distinct buffer pools per class of service. >> + >> +Other possible action that may be taken in response to running out of >> buffers or coming too low on buffers include back-pressure and >> random-early-detect with a discard probability inversely proportional to >> the number of free buffers in a pool. >> +A related implementation topic is the ability to begin dropping some >> packets before a buffer pool is entirely exhausted. >> +This is typically referred to as <em>Random Early Detect</em> (or “RED”). >> +This is deemed to be a feature of the buffer pool implementation on a >> given platform, where in addition to a hard limit on the number of buffers >> that can be allocated to a pool, there can also be an option discard >> packets with a probability the increases as the number of outstanding >> buffers approaches that hard limit. >> + >> +@subsection resolving_gaps Resolving gaps between the API and hardware >> capabilities >> +On platforms that support hardware packet accelerators, it is possible >> that the packet parsing and classification functionality is sufficient to >> address only a portion of the functionality specified within this document. >> +This gap may be potentially bridged by augmenting the hardware >> classification capabilities with a software logic implemented as part of >> the platform. >> +In that case, the platform will have to curve out a fraction of the >> processing resources and dedicate those to the software classification >> logic, which would be invoked for packets that the hardware platform was >> unable to classify completely. >> +At the time of this writing, it is believed however that the >> performance penalty that will be incurred as a result of software >> augmentation is unjustified for most application, i.e. >> +it is preferred to lose the precision of packet prioritization while >> maintaining full hardware packet processing speed. >> + >> +@subsection loopback_case The case for loopback ports, and some of their >> uses >> +In some applications, it may be desirable to be able to run a single >> packet through the classifier more than once. >> +For example, an encrypted IPsec packet is received from a physical port. >> +The encrypted packet is assigned a class of service based on its outer >> unencrypted header fields. >> +Later, processing the packet entails decrypting the payload of the >> packet, authenticating it, and removing the original outer headers, which >> reveals a new set of protocol headers which need to be used to re-classify >> the packet, and assign it a new priority and buffer pool. >> +An elegant solution for this use case would be to take advantage of >> “loopback” logical ports that may be implemented in certain platforms, by >> transmitting decapsulated packet into a loop-back port. >> +The same packet then is received from a loop-back port and is examined >> by the classifier in accordance to the rules assigned to the loopback >> odp_pktio logical port instance. >> +Similar mechanism may be applied to tunnel termination processing, >> fragment reassembly et al. >> + >> +@section related_topics Related Topics >> +The following section discusses aspects of the ODP API that are not >> integral to the classifier, which only applies to ingress preprocessing. >> +This section covers miscellaneous aspects of the API that need to be >> addressed, and are related to packet buffer processing and egress >> post-processing. >> +Additional packet buffer manipulation APIs >> +The need for these following calls are made evident by the need to >> encapsulate, i.e., remove some headers and add other, thereby changing the >> size of the headers of a packet during processing. >> + >> +@subsection initial_headroom Configuring initial packet buffer headroom >> +The following function is provided to configure the pktio receive >> mechanism to (optionally)reserve some headroom between start of the first >> buffer to the first byte of the first packet data byte, which subsequently >> could be used to increase the header size “in-place”, without allocating >> additional gather list elements. >> +If the request is granted, at least <req_bytes> bytes will be reserved >> in the front of the packet data: >> +@verbatim >> +int odp_pktio_set_headroom(odp_pktio_t port_id, unsigned req_bytes); >> +@endverbatim >> +The return value should be negative if the request can not be satisfied, >> or positive otherwise indicating the actual minimum headroom reserved. >> +Note that the implementation may reserve more than the requested amount >> of headroom, and hence on platforms that are unable to support per-port (or >> per CoS) headroom configuration, a system-wide headroom configuration may >> be set to the largest of all such requests, and thus satisfy the >> requirement. >> +In addition to the above per-port headroom configuration call, there >> should be an optional, per-CoS call that allows the reservation of >> different amounts of packet buffer headroom for packets that match certain >> criteria: for example, the following call allows the application to request >> that only packets that are expected to be encapsulated in a tunnel, be >> augmented with a large headroom amount, while packets that are received >> from a tunnel, and are IP fragments, be assigned a different headroom >> requirement (see definition for odp_cos_set_headroom() above. >> +Egress packet scheduling, prioritization and ordering >> + >> + >> +Open Issues >> +* Parallel matching rules relative precedence. >> +* Specify application-defined header field declaration APIs. >> +* Review RFC 4301 for match requirements for IPsec SA, consider the use >> of L4 port ranges instead of or in addition to value & mask matching >> criteria. >> +* Consider the type of packet checks should route a packet through the >> error CoS: L2 is a safe choice, but L3/L4 checksum or other exceptions >> deserve consideration. >> +Usage Examples >> +Following is a simple sample configuration using the API elements >> described above. >> +TBD. >> + >> +*/ >> diff --git a/images/classification_flow.png b/images/classification_flow. >> png >> new file mode 100644 >> index 0000000000000000000000000000000000000000.. >> 2d94ff64ec772aa97f3687181c121fb91d671889 >> GIT binary patch >> literal 35193 >> zcma&OWmJ@5*EWozA_z!INewLmA}K97Gy;NxLy9zr4Bbli2uMkfgtYWf2HgxXG*ZGa >> zgv5Xh^<Ja*{XFlx*7xK4p=-f)_KtJ!<Jc!rdOB)kB#a~k1O#O2U}b#*f~#oYpYqKc >> zz;8-b5z+(%!365cj}5(Nwz6)gJ)Q8WJU*jTQ|5XLQ`Rcd010>Gev54CSnf<#e!~?l >> zm-e2&T9oTLGmW9>vu+K6dxhiAG~PT34<_c`$TF;brp~1?qA$kT`NC;0x&deSBQ`#o >> z?;h#t&Z3;7u;bB35%-}1x%I7pp;Nix<AH%kr&~3)<*k#ra@Qb?X*Ll7!FirsRY{05 >> z$v`E=EbB8zi=<*RV3`D9rXLWuz!}?Nhs%Qg%dL48&7FPM6U^?Xp$hXe=F9h&Cp><- >> za%)M>@fPMxQxu;W@tu$g))tlEp6t^I^Xb!(r1qnR%kG8f>Qf09tgiHmyDLLp8%I^1 >> z;Uj!oP6nS3UK(G`S~n(#G^o}qWKZ=YF6;W+u_2V3nXOuOxABb>Ku>N)%AamB-80H< >> z7Ppx}mFbylO%US0P%#L7!!-?GK7M)=ig_ZxcdWZ$ZeVbbol1Og1>Y4+lj(rjB(GSk >> z9jv?q6EtnBpAh7P^a|j=5vb=sQN*?!w3|G=IzLlVl(mGKoV*y~n7xJn$Qh|Zh3?;J >> zYQLv~tCLuCX+?`P(&9g`QIax_S2_QNsf7#AxJ53gW#hDYeT^^vu)bkA*r=yNw94;n >> z356Pw!!Byp&1yjbN*2^acGfaTsjEQmuLLEP<)x=T4@HLKwGV%BAa$K&dM_s@7Ud4T >> z#>g5%jRakB)j#5IRWxxA&1)a~q|a-wW%0z~m4<-Cs!Vy!re;0G5FP|w?M6f$zL_o3 >> zk>+Um)xSQ#Ix<v<J4=oc#L7Q<<y1twB_<}edqbY?#^l%Szr9Z$JW<rYDq{Y)w2H<? >> z<&oFE@tI{e^R7hp8FA1>T*J{lWcxHUD`+y*{d{t8I4p-1>aD~(?i|@4``lfrrd$5x >> ztaNE{zY!z7ioU!+kf~6kFSqC9x7@9s6!XDtcj_cgICLbd$0D(G?{bnF*YFF@6?&zi >> z&`15T^3V958goEw&>qV6#4!-(%{zkG#N77jDgZ&OOoex;j&P=nVuw?fc|8q_m@c^M >> z?8L7GskL#vkBh!2*`@W>tVu6_FD#)7C3;=wg0(sZ>WB*xRc8?0`28+KPu`;M?%&zG >> zU}lkjCid2~aQY1>MM_`qckLeQn0aLc?M5k!e;MjEoiSWuv0CQr>^5s){;cg`R(H^9 >> z=<=2vqt?}EE@U|$Q7bo#88_)GmIf9A0ywdaYFeb_Gxf4jOc9T)Z;Q;PFT`pt8X8vY >> z%{|mynFck6Yd+{tVmXg(*_6up4o0r3Cv;)>WqNL6OQ0tN1c^7euhlQc2sK{~w`?`7 >> zNsWRX(Frt_vcXtrYmCzPQHXO03$!y2Q)Jd}3}4siF5VsY*<EZD^V~ZNQ#ZdC1#YnU >> z^g0iR`>`NK%>{T1Ws$L)5nR8#NPk2-_H8{BOl@eU&_&$w*!1ujF9)|fpKQLqQmkm0 >> zpWiG23tU3I3csaVu;}>1B6>;v@(^wNK?~k5nm&5WEc~Y@s1QR$5L_F17)5hkq1Uds >> zynH*k+mB5cZKC|y+tE3a1|sQjy(IIoDhT4XHLZ*wbA<i$YF9#~X}5I0=Lz`Zw&dk- >> z`~>0^r0uqT;zHu_jZ4HICj*!p;Vp(L0*zsqQurBbm^lpynPP9Vf1NIZ1*_RE+??qp >> zYf92}Fm&nAP?c&R=V1cq*Nh5qjH~yuAq*DDk5t#6a;I6@c)=PLV}xTjrYyTX?qRLW >> zt<Lj!f|xIK)mYsJYUaX#qkWk{i6;0|RNr=CRM+Z%kcfE}Wy|!z>Y#A;SmJVXZA7!| >> z<Hjh8=1YEi-Pgkd>UR1-qg@rRyH7vR6VDsyN^_i%N1YYh$_mz4iiVumNmTu2w{d#T >> z`YdzkIDI&KqU4ezr|h8__u)-IuVCZ^5qd_S^puY!xMc?mqZFMX^F-e+#bgTi?@~?O >> zNEw@6*P21idO5x+Sa}<T%yDM|>>R{QfN-0#d>8mhW_|Sj{S-l@^JY&*Mg~!|UfKTV >> z2;q}fXDIv)D=yF4>owiK_W4>yrv@K=%)c8|uX+XUwnbB*bL3XE7PqcS72dRwdRTRy >> z^Jlf__|TJ#<aBfy;_?`9e{e#D+gGb`#WT5aOqEarPlmDeGG41{mvPSAhAAjde?NFf >> z@n5rK%8*?D*7}Mai%;Ht?&<mhzUdEA*ITtneX5^r*{JiNGxj_E?z3DcAW)VfIQ%}P >> z(CEN3@cJFcC!R7+H@qX0S#6pO?ci@a_N_;CbF25U>MjEJMkJJABj!nnJ$u38>yDo~ >> z&W4=qC0d+>(;G!E3g<TWKl0BnSw@g;ik=Vr`l_0od*Akm3t4wFwGHs=6lj9P+ay<> >> zEbKzeVwui4zt$6p^H<@hQaFIa!++JIQ6&=ZWSUZyUlX^fw7;)3&5i_|Iv|h42f$|n >> zJz-FQsLB=m8VG`4Mjfg~U&VL(yD0oF{z`!YjsFZB6agE)Gs#OnVD!JOdh@RXL*c*v >> zZH%83KXxP(-xc5Z{~7E5&o2O#3<d*-@;|fTsL)D$B!7P<;MDx@8t~)(cUm?i(CKe4 >> ze5b$D!s3B3f|F|){=Zg7J0!``XroC&fj;;++}ov>{I4^;WRjM=XV2y)<{X<@zea=Z >> zN7ds-hb6_MLH{KB^{ZV~z%=DMBG#6`J+HH;x8*(<<TfipSg?gmE_;r^6`9fYnz@ts >> z@5+L`izTkkUjE_fhF)wn(VfzuzW_JF$q#uMs~j{@s($RhNh4W_or5}vc|0|OkGV(I >> zoLS$DkFs9t-aUR;{U0d=gM=#!3x9nTW|ca!sHjSnZuiOw^8P_d^wxS!B}Wwo2n0Cx >> z+&@~S<>dk9!kbj96$-`!2&)l7_DiGJbJwDQ=m~#XpR5%j0q15mzOV<W3hn+wXAj<K >> zvW=LN1;!Uf;-zLr|AUYT<1uU=i*?puHXe2+Gvi*AaQ=QEDD5<oOOACf%Jx*{9dUGY >> zG$#)<Q=aCmzx@vSFxf4$nrWF1eM<dC0P)!NdVObqRy#drcBW-uxd}G6@U;KhScU!V >> zF2;EJkG(l&#%eyCvqzwDgYM+7;-l*iJeAy(aXZ&uM0nnsL)(b6PY3;4ANKM4)lf0h >> zY}X61WU(%D9a}lmyjaiKsza|jI65wp2M+zbQ>pQ4lSR)??c?#3ig_lQZl&c@71O&m >> zWmLdHR*+f{Os{!l&b+JUX0I*SqTGzzf9oqxxBo9}|3z)d%4~z+Y4GWzpynf?9dFl} >> z*nVj_QKvs<?w4M`a^5TBYI8%{nIij!`qy8~pR68K{z_S-?{{txFyH2{3A6vL**I8g >> z0IxjTlke~~w^!in@(~_rZKpt&P;M+4?ACB=&7Jlbv@fy5+i%HjZdf0Qcq-{D<Nhe* >> zw$t70yG{RRMa=ov!_8y^5#dHGw!yuW1JGW25VO^?%f!TCdeBU7E7Rpv0J2-nE2F+D >> z(Uc5kCJ0-gxOteGLgGBA%Ua}i=Ue@b|9PU``W^#_85YluWWP}>IS$-TE97#g`b5Vr >> zu&x~#tK*g-J&ey)0iOEty2FNhWowk`^VR1kBi0doLWj~UxAgHwO$s9?%&o6KlAE9& >> zs$0(JL)Avq&3R1p$3Kidgmjah>#f&AoJD)6I-1hE+LBG%e3fxxgAHMCr37mpbyc>W >> z96cM3vD`Ti1M7-nJ+Ro*<e}v1x<7?HOLZO|mz~bWty_XgU}N(2*JAQy)uKl*YPmw@ >> zJF~KHs*U2n-BrnqAlW8fYE#KPs}}-o5M11Jy>`_eBd8V5Tlu+?CPuUaIR(T`&el9c >> zZYhz6jRst1Y0EUR&-S-L`VR_2(4RRb=vEx|woQ$LGQ~r{LNVamDji<N@(N1#VSEWv >> zajghtDPc6dF#79MmYHLl^&{u2Ff%#Wki{^;O<?Z@37>b`t`}$%fU>%DtZms;;MRvv >> zzD{Xp0<MfKtBd?^lYG~2cGSV(Zo4huzm0<6>P+z~kx)jdIA#hoy(+89;@eSf0$BX$ >> z6#F;O`IqT~Gr$$_{W(jJ!DcGkJRP}&Va{!i=XT9s_r|&u(xe=?wusHGHONa7V(!NA >> z1bvs^J*#B7x2v1uR#+KlZ6y1LK7fV|$<S~GrlV9;O-N{ese)5e3mjneI-(<yYUpsz >> z%i$|je}K8%;18-+uB|XFTqI&%_<37;=g(W7yEEMiIdzXVSbg_VODDq_uU-B?5?uU3 >> z<N!|>I~xox${U*s38}3GLGmTzK(hC`OeN&$qPG>j4C)mZFUlWIo_NUNcopCxF7BA8 >> zmEfR^a2;!+8%V3D!@oY~va9?8i1|Q=^qOB6>iz33w=|iTPVr8%D96W>elu@(3UPq_ >> z?a>;9fgJy#3koo5l&x3Pn%e~ukF8UTcxb-KdNPs8`c!?Xz}#2O%AguF;<v|fjjr6K >> z13fa|9YY_s-e`I6oEB~Lx}R|8L*~WhMqTNPtyA3M_GKk%?Fs4x8~S){B~Ztv7&CTR >> zboryr_R<TI)TfEsXwzlA9y=d9n<xisO9z&{8>X5;Oo&!cMqC*2_5ezMk|fk9NmC`2 >> z&{4RMsvuXr2Y;ZYyzG8ZX+|D~2t9qG#!4NH<D-Kx`W8F4)YBrPQIi-Tu)TIU*K7U= >> z8iy-uGsqe9A!7Ae%}b;_AK%02WGQuT_(4C?))DT(G0k&n{>()y?mnB?=C;|3%jmGn >> zvksx~oUu#C;ZMEcX41CE)bC%$u>D)B-!FCxbRLPxr1s6~cvEmv7?=<L)4319U1mWY >> zKRG1QhGX+e*pNgZB-dMrZe3_~hf~FCx+s&@-EhCM6ZFc`aeuRm{EAsZ%lZ-HT9l{k >> zz$CvjUA^k{GMRBH)rCJHU2sfLjV|#%CAu}Nc}hK4W)Ek&5abtIVZPv{?LUWu(anD# >> zdV6jEWi;QZP3(Lvu>zcKZbYm{MVqB?w<0iiHaM~qIRy#PAqoMrR)M`_e~SA>52a|r >> z`<a|NiyZeq&bmLp4Xt;4(Qa?rfLeRx74}8iGBK?V1j!LnhGR^eJn5p`ed@W!A;QcN >> zU=&=5I?K*n;&>+W`>y-tv7H?mF!#2|KE<Zbu>i>!GGKA`<&&gkN7pvl%2OrXCaByj >> zH_gQ)%GN`7S{xF|wMI<id{v(V>Ctk7kIZRORAIKe*;hH9KH*a}E7eB5KVhb=T*Rjz >> zA2_w)n#r%EjRj$5#^$@y`FWYsD_;b}>(=AK^EMdE^gfJ0YF8^Bz?Ca8-aeD39LSnZ >> zxMmp!=j^S07c?&?)?*_jh`P*PmUAF=ZmJCkA6pk<+L#g`$~ax+cc9v39NSLH@z<|K >> zEY2G<4Ng|<3uAAcQyxV^d2dvHQirP%Ni}fvG~{t0{hYTVt|4SX>z%5hCKca=$v}`i >> zL6h;4kVg)aw-(G{f|a$(wcpKF<C0JiGen~@LMGDXQ70GD!@qt%(SG@fB`84A;?%zT >> zQ_Tzl@s5O#N!sP?n^~yuf_1}=y6iV3WAqC%XqM)!K41%LX&8Pn`yge+>}@t=-rOI1 >> zI`k1`V+(qR6mr!d;jPVRBE2lRQCVr=W`Ktl@I}T6$08Q*wBX~x8}0nGIPU!8Y}Cd_ >> zoPE253aB+x!Arz-N-g}El-4bQrwJ(0#SbR1im)69)Tp~=<E-BMi)iRM3)!dYy&bnm >> zXkCEBs_GK2$swAhptB|{5^D3N_b9-3UW)gg7Cgc_bdV{5c5y5GISF<{rI=3C6VrwY >> zm7INcFTtZW4zn;JTYmIO?~Vk&fg#+HG$H<*gF9o+cA*uJD0SIonJ(tFb?D$eF*!Al >> z1*~VACV4-S-LlPpo^<%gPb)}5R0*!s84V?d&Q47vI`hUt2G-<24TSk-sSP^2eEgvu >> z4HuJ&_Xp3mtt2Gc46-GLU&fEHl@4`7I14e-YX(JO3xv!??w^$5wzM115IGreZ8%2q >> zlfk%!ZO$?{dDcmYs=#-!kaMdN+>qoeo%lJfw0zS&15O7*im^2-s-a2)*`=07nnX7+ >> zLDex<w<yu~sIm4^sU1->#rk3nS95pE3}Hi8JNKUnDYoYNR)077J{tgGd0j_6yWsSJ >> zr?v2?q(0ZpFlctXG$5>bLmJxBWf8Z_*zQ5z06~jLscwTSG4ypR*eeKbi8Jl)DU~(c >> ztOH4wL5{oEl_RZ5u2VyhpsoyvSziFTtw`0%vzrn}^pxWbUEnjJ<bblm@WS?qKVnn% >> zRUd2z#Wf_{-(vNr)!fA!s-0ULeoxlWwSM5-rkc{UR?Fo;_6s7pTDT;gn`^rzg}*E* >> zpapw?U8g>)U!Nc-UtGH~_R~dzz0Vk~rX3KD<UnMLW2H^>HoL|(6M?*R`=#R5u57h{ >> zglmXx3<dfbaj5S%EGXiGbiK*riX)d@2>;CXl)db1hF1T|LMpf+$E!wax=LiWVr%)e >> zSNrw2^G(9J*Uyb#&L%QP1^SgE1c~2BC?(4ktU%*3BD+(b6T`?$sL)MXWL?pkVv`$! >> z0iIuL7K>Dwuy0Dgm5!>1+L}(6@Ae%1fiOpIU)%bc)iG|{Dtg4{`Ask5%{}X0TJ*VC >> zU8WPYsl<*{#V)dG%SZF6kc-T@g4X&-)4lRm5HiWuApwhxVE&EJ-@EfdX!6@yc!k}W >> z2hWbfX~qEJxYlgUz(u<4h#OhXAvnW5*IT&|6W+Zcrad(8=%#|m-a@;g99@1myeP^F >> zL?wAzA@e?~Am+<DGUhEG_?Eru+mEzA5?pc?QE3FpZibBcY;m}rHkyzvlDPPM-{NpU >> z&2>*wp##drb#uY2R8!ooJeBG%s-ZwI_=5tC7K@9*BP(87S!ELjsEhHKF1@%PN9rFs >> z8iy!p2N?aJv0XmIwmviSBh?Gah-@<(CcgsR*+co>S;6%T>4kMF^ilS}9fMuD`D2hD >> zJsUFQb?V*1E(fNpI}vu=PIO^dZsa`09YA(%a@MSM>Trx#7KcN9jd}_ASZlpme>dT* >> zby0ppE-JB`ZT2#j3$^uOKN5O>8w3fEbVi8&l4=npXYn``KGg~kz5R0Q1K&qw>*Diy >> z?L!OMBj1ct>+zu8D@ATSw9~Co&;V|Pts-FNA3yvy-iYpU@cHJUb3UI6PLNv~OOkAL >> z@gfv7Ql(dxGW-ZmRcM%vYtF{DDI;2bxs+uvwdF6;Rys>FplW}2iVxAmK>HPkR||}v >> zmlPP82qql9EUC%JV7&Y#ZOn$Ou8ltItzALR;8V0@y;KEpc0rAhN7lL44NNRnbe1#o >> zLG$z;t*im>9qGJ*9NIm~zL}#c5H;oz(7+^Kw1_nkgt;*LKXJBLnRUJ)p)AFQJfK07 >> z$h&z-82+eFs9SwH%!R}<h<VTUU?X+1-T4Q$K{TCN)2bCi1CTBKD<Fv0t&5@nT>Hm~ >> zO--#VOLSF=8H0q#_o_5pac}J!@`!;%OxR=Om(80AKfr%xK2_&-^%V8*=t^#8fAA(4 >> zEhb$Es2Z(lMOY?d&33+Xb7?g*X6;+5T$Oc8ms_TApLXwNI!)}6B%}r!9DvM4VKdrh >> z!+(H~S76yi;0F7&!wt=ZaZybpYvXPU+g-quGG@m&WW624{S>6HUWXM;zls>yn$!OF >> z3|ML`EA$T~b9`zTIS4|-l}!hWuXfY4IcE69H+SpX@Tl9dfDu&fnD;f^>lvD8#s)D} >> zQ%eEiIb#ylxCyFF&#ihi9YnXqY>d8QcSN$n5!MvNnH<+8G7WbsYZ?3u->pGygolBU >> zd7CN-#%EB%kOd?7>}#^-8B?;sp#`j8#r5|l<5nvJH8%F6Cr+g1hoc!4LhAN(L{n2t >> z5qCYtOMi*1*xkd!2;tz$XN3$Q7coEAe_I_2h!^Nrlqvfz#<v?0n7UQu936PR7^+_> >> zUKnc>8rzlXV#aKr;>a_n)ln<262738c3@Tpxts3Oo?ro^dajvka8<aqjs2F=#$5{Z >> zIVGC0>M(MO+>V4g=9TK3LGt>^8k^yQuy?HiL-P#^h5qZ&)^!Y7t7Kzi6?UIh&V@Xm >> zNQQ$W9{7CaJV?T40>o5kCJMBurvkj256RUx-}wv&Nl2xo`Y^a<@WqZd1JaU(aj$65 >> zTwF?TG>P&ke?00-DT|BWq2L|kaAa1yRKQ2AwZ!1hVAs6A!MUg)tm3-S{>x_J%z*8m >> z>5l8#5<m3i_A2&UY-i8Zgo~opJd~ztf16&Fi^@zGRl5RW{+GXylftW`p+2Jpv%z$x >> zaLr(~_A#+$_uP&arku|zue`_5|99qc&kVp^)kK784dDImIsGv^8Ovj|Br9!&?NQoA >> z30J#ZX0EXz?Kea{i|t@8*K6iif6$^yLeP$Twt}$iZFi$&j4xCRN@+j7YM-zJE!H+% >> z1-j+_KOLXJTz+WGc^|gjiATi9-+C_TQ28VJaHAp^P|7<ouTFE`XGo>s^O9QP8D$S- >> zB=~Q#^AR-9LaptSHvXE<XR!t%r26l86yReN_FH(Q>SSi}H+^%MXUU!TdwwyV*SGqA >> zRF;1~NH?_j-^>lxMNZ3sM7hq~Vd1DY(|}cw3P|brjMjXz81K@+UDzQyPb3x?U8r?f >> zAOClFX);-<MC(qGOtp-zAqA7IEkP3g(YFKo(a=oXlkUv}l+cBack7}PLa>EpO$l`k >> zgk#ut*$iY2XS>qn(&b$236m3>EwC-~=B~Ed^t<062Wm|NoiA4oZ^CL`<U`&zR_>+= >> zJG$8Ic-;ZzbFn1rKZ!B)C?|Ob^IkkFP4&Bb3S?4w!Kt}L2?}r}vjIKkl8Q##7}rU4 >> z%BgEEvdX@E3`M(bhfFp0kz8NgxdVE|kCx_ek%v~%P#ecReTwyRQ5e;MsaKm0YX~^v >> zjD9!WNpr5IQ$jXG)Y<A<0}GH)kB^VH;Fc&^WqDoi#8o#Q0k8({-3{iz2fRlDb4S2X >> zF;FJeW#5h@yZsT{7|U)7bYfCC0*5;fjZEpH0eP<`vOCXtMjpPYnu>86i=*up%n<>Q >> zn3~3IO7tfRbf0vE<Zrm|Sjx7_qcJxsAQF9WgZ0tslL>=*F_vd7x;Vq3cSN^Oq<5s0 >> zeIL=An3|q;;eB+hi~rkBTPi~UEg*7qa4@^s*OEQU0y86lxg4bOcqFl5A30fsf+GU+ >> zaKuWyO|CK4Z&-SwDD2=vxn*i@{p`s8&SV7{7CV`fm77@5R7Q^64y*K5<4|ti<xqba >> zA%U`QFW^Afk^D&(bAbvTw1dl`oz^D)t~1~Dcf^%_HPb7tvqKRwn<N2Cdk0^&n{sI# >> z9c4CluiMDP%>u{@7&rzB>K}t+(+{qMPRhF7zCBl4^uuR8NX&`o@yP|~&-B;waImZT >> z*Sem9g$m50oj(cC&!haZ1;`S03X6cuWt3=d({D$S^MV8h->;(ksjUMJ{>6&0!0o|? >> zZr_CIIx--_R?YljC1JZw3X)~!UFi||T*I!$d>+!p(zSh&Fu!gtdH!>WOzrLiXA5f! >> zrN&MQG=d?6`i-no>wp!~c)-K*ya5s-U~$K+POFg2g0Jp8BrcD*=tjAFhGW;=oyCw@ >> zPobn37B)A~yvp8~L7ZAunx~^hR;w5zwcr~FaRnE7=63?3c)gq`OPeopr$*MNpmYAr >> zdU&cZ7h?nesHkU+0u7LOX{ylQ$?YzSj)=`*S@e+!yiO2V=lUt=IsnS9izTj_-0W85 >> zCJ6p>YJ@Z9<=B5VEW&sbz(fE9HWz3W2A|9P#iL-w5CK;Ehu*@-6Yq0_{(`oV&|(lb >> zfJpzRwF;bYhA5bc7C>zQ5Ln@qK;qs%96OOkH80@5W2(TFzR)jMyWRN*{2rF$fna4R >> ze9ONb@zdZj(SPm#0m+nTQV<@OEvEnF<Mis<KY%)b|D2yQdOl=3pPr2j6d*`Q{BMk> >> zoJcPN(!?7uv%v)ae@Jt&V74lp5Ww5W2^1DJE=hW=+$~P|ZYaPx0FZmb6m8D_95i1< >> z|KBA7+g`0Ahmot>KK$D!7#wlWTG2w}YH1NK+h`W=e||{>oFIMx3iM$mesy@%{C^gn >> z>k1qPfM(;t?MP^_90SuccZ&i(lK*>7ob3%20FT1=!*3=xh@esZKke^V>5~5UfDS7Y >> zA8=>*xl*cJQMY<21cFGr##yjgQd|M%SE7wPd=-85eMj-v4>pu&strd%rtb>h*{%X2 >> zh#+|-3SxfW=g9$(W0wVACy6He{^ye#xq9cPgnV8$`}+@>SL8Et<c~i~&{F<K42d^Y >> z)8j|$0?e?Xh**(#Z=KY$K>^tZ>B4gWnF0tb*y^{wGTd0RxF5c8?Nfbyz598g^K&BD >> znxpCYeQL(V6-tJG!1hg1HD%knPy{K1Xt8DH#d=qgJS905z(w9)XR`|PJz(xX2Pjz9 >> z4-;-2a$lzV@7MkV>lMlx*@Y%(Hj&!J!)am-#vy`!N1w<q0j4@y0e|dDYgLj}VKjuC >> zDRixyBp!UviaAE`g3yC)kvs8p5~!~Yw{8w@B8&9Ya`95g%U8j<L|=Iyt!VaHV4O=R >> zFaF;!DhH&XR+-p61ab`^C~w*P3}^xfH6Q+QNQm6ok8a16BJ}|OY%L-DronV?zHS%L >> zYxIW#EEb{JQLPXChcSR!|DMAmo=tOc7nf)06Cl{QxS&#upOW1JuTp&@!Y|}(1E;Q# >> zb_n8TwB#$?Eqfc7Qs>qrvJdyFhIHi*oPhB6vSN7nh20M24dB$2rQp%n$ir)%q$e5M >> z3d@poAIN7B2OgJ0yYqD6Ei`Wu9`xW@685LDzZR6>*0fHf5C4ip0dD=zlKv_kkcRxV >> zCt(j#z)jupBqtOWpr2@}08rm6Ik!r+YL0KdBmFC){SEpDWgT5?gm{fPeDJM*i&i8f >> zpVzIMLT#qwmio8Iwe-TI#{hp0BS$x@5oYQ<;Viah<7VdI{yW25!7m;*q%dBGt%c8d >> zID`5A#eGBs7WWG;LthGy&IR4#CBcV+|NT`IUSsjl_Z0&A7_>Y)L+oR~j{bLum>WV# >> z;cmPtA$S)~KoHw`m_q<1{fl@1<%#}{g{Q3kGcmAN0MGsJ7HS57eCdBj3k5txE<Nxa >> zXcb(Yc0v7~3-2HP4fpk#)+KSTrSv~2{y*z^?nLe+)T#FkAF&t(%}tx%bjA#!YFOOY >> zk9Te@<^Zw4V^o#1apon!6hy5BRik)M0k{xH_u}CXfSK{XyofVB^%!cb(4It`Jh}wR >> z9LR?m0$itMkB@m90B$PmQmuaFKV~kYg3uK<1VB0|#>>g(onGI8=hQ=Fi>X!jR~hA` >> z_N|vrh;Z%xm`&G+MIlQUZImcbA+Ur&E%^I=TaU%eyu{O^L8I@ReOuZ#zjdE|Q)5Z5 >> zn;XhHqrJ@3MscaYFWI#Lo)JjI^J!sbT%hxgt|Y}J`hZK@;noe*1nLx@qrO!^3^5gf >> zhkudb@$4i1GM);@q@ou#2S8jCVK%mlkDqu)+_hZ}uUU*Nky)N_E5I*}E%*sDX`aar >> zQUEffL??QO6HS7J4MV`G#>V-=jQb!VEMJb)wqCKiVXoO3_IaMIsiH3)uQd<aKQX>d >> z>_{ABOd1q4f8!GAIv}@7>Dp%PBN1X62~I`(MlgG|1P#H3Jg9f2cEijw83H3JMn5PW >> z&b$z6ky#8gn*|D0Mt^X~;UUQ{PKc!sWoy5De+p&F3K4_OpB@+-MW^;QOU3`X38)TW >> z61+9whU|H}o*1k0^+j*T+tnqKjy_Xdp<McES#KP8KhUlVR13mN4>k{|N{yT_B1Yq; >> zLXDwrK`IERK9$sPGe=D4q}M$f)vM1?+`?tUK|mFTY{g*oOJa-@iaR6+;|Jt?){|$4 >> zdoGn2R}A|ufJXVPhUtQ}a8qADFv48S7ip)Kzp9RkUJ0(Ihmk9E>)n6}x(N(_VcOpZ >> zie<hZACA#x?o0Z?UQ_zia*fXqH+T0m3$Qa|r8H^y<^0(|^KC+w_jLmdB{drzujUb} >> z)H{xyPFHAOg;>d>j_<)$*3r&a>|8;R7*DK^fQ<Ik_b2D&zf;B4s`w|yTE40xwXXcV >> z*mA9T^T4Te^JG3Sq8AIlil>t4Ao)f)ZrYWYgVxS~=C?-Kc0Oe*ROk#X<#wgZm}`D3 >> zy*W2|u7#PIx-ORT5ClN{TtR=!5VFmu-KjF>Kd}P^Q{}guayX_Wu8a5gcl@{lSmc%9 >> z_`Ea_<n1Jx&y)LzL6hZZZ6?9@{Z0-)iYY#HKg;7Yfqy<egzUdI&0~_S3}_XC3Epm! >> zexz5{@}+%a4)?KT38<6NsszoqiifpMIbyc`4tz*PGQq}=e1ruo1Rj%gv4;%abh5TM >> z9bD&}t3&uBC=dT$u}C(a4CI6OPrRQM;Irew4}E;NjJB;tiovOpalXj%>CO)o1W$RR >> zL%-ZH0SFKedP1S$x@(QJ2r3kAHUly~oe4&Hp%0E`pybT_-^%Hs=d7NvS3pL0`9#qS >> zRY)A|=Nx%h*7HtIqDo*0uEBJ#4BJCNh`-z4&ljndDTzfwnfCMJ-mb<Caf>_Ouc!Sw >> zNyEb7_EGyLrUhqe$+=@&EBwS&u~7(Cu{-WZbzvmgPslPzQ1JWX3jle?ez{Mu%|E9S >> zqrLrh;vy0nU|zqgJ^e~GE<|Aep)lsiKibAPJdIe?9=QAnX`aG|;k=)f;j`DX_w$?6 >> z&0bWOh=%uR<V(5|YXult3gcok^+!ZloFVUM;Z&DEMosc0r`H(XZ00GjZ`jgemgE1e >> zGa`NhBURM0A--dZX251-7Cs5m40X&y<4Gb+-Sg4uS(ox@ZvK;KXv*{Y+El&;H^YRv >> zWU{V7ikjIL@$g?{_^nT+MT09}!zxr_NT#b|1h?OUJ6|;~KT({b{Xtde$)leh+IkI< >> zY5ZaxMqkgM&*{&lAxoVgI9z{zvGDLC@0Lopvi4%-<=D9nq^6^>RWP7IX2EEDb_H6F >> z5D)wLvRPbPkVdLtH_G>NKCBSevR|@}II?R}S?sS5F^pD0#I$Viw`4O|-SK@Hc?Gx+ >> zzAB{K?R@j0Q3;Da49QTgW{OxyRiE1Yvw*zQWw{^TOr7Abb*Yqx;;uH|&?!5@i83Tg >> zv?;0}RQAvYEm|!&Gr(lBvq**Uw+ww6*tL)sfJN_&y?rLDGGaBRC|6WOSYg`^_{*4w >> zUwM^cAAX-A6~;AvYmo~T{L9k&2$SRUj(v(bmli)cGic`_-MuY-1$cAMJ3(FFdQ##2 >> zmL&EYcs{>rRB}4<-7Q!=*3#r<rfXWPqQiSVA!5dtGzAsUuy*0}+rAp%^mthV5oIZh >> z<_4R{O#jPw(`z?9ZDO7;S6kvE47^zbSyX(>Mqk6utH|jkoBb~mNXXw?9e>}o`t$DS >> zNT=6(%ZhaHVrdwx{ze4&i_c6onf+BpYA;32Fs6^9rk6vB6c{fLq32mkO9RBoN^qXI >> zJTq>k0q)BFX2GSlq5iFoNrlv_fXIt2YD<qjqk)*89hnsSu!Hw-jiJ&uJWU-c@Hgb7 >> zMK|-^z~9iiw+dPYevyxo3Y&!f+l$htD#CwxnZ3<N%JoXgw~*xk80Ep=;EF$!5cBVv >> zmK_9+m0vsJ)umThH?P(!&iUKG;@HFR;nd%4>5shC7q}1E#STyugcSp#x45qgS7MwB >> z22vF3$U_aANPDl$SiJ@YD-+p=9Rv^Vj0Gv~cR%2@>3$95ZV_2O6t?ys9sD3)&G<EY >> z!@mMPdmL1ow|v=cmDZ|u_4G?BF;R(UE#G@Z^LlT{(t{Zp^-Nyv{8&J2fWmLueBmH} >> zP)1X~7&&4btH7)wLOPnSFCaLNPM&ZydquhF9ZmGt+ZOBO$x-+BUkqWQ`2rg?`g8fJ >> za;|x5%FD~MSB06Otoq0~l$q?KBlKpvKA8=Z3di7=ikIn6is)}9Xb<OSB4ULH@xsh5 >> zf|~hkOtyWN(*(l>Q<x?*V;47{sk$nvN;fa=G{4G0%B`7g=MXhOU4V#4ftI847x~yD >> zm#8Q7+QlU$kA7mQ7~kLvPN?pUubnS2teh13&aZrFNJB;T?&L2d_nK5<?Bli2Re(lF >> zs$2s!pZDQd>%c2j$?@E+0kOAWvxQ%2D%Mkcj>4a$7K<T&>eAJB5>c6sOq2&cYoE;5 >> zJ#1uQsSr0z8TW2{pS7w5Q!8MRhf;vH0eY^|Y)qJ`_(Y+J-r-up{6YXwn05Ff>&<uR >> z)#+a|uV~O`z9xBTP7*Ew_LSV$Q@*}7U<LGLzft$doAC;;lt^>DqszUk^&4?#%lUUS >> zooeDMnB2?@Q%z!S&3*Xp5n_3gORoD(=C6-)yh;O}fc<_rcX2nzTa5*qmKt2?5m3jY >> zruaICGQtLvY%s#UD{k}|k$z`t%<TBtR>@rbqfzBo)gs^a26to9SMleM>s5AV9s0zZ >> zYuH2B)QgFJCm0r~-VAmXMoAz#7E3$tm75(mdW88=L}W4fjIL=Xyo!9j3InVfMy}V0 >> zcNw`fT5cUNp|&M=_zEHL;Rh&*X064_G6@aBCKQGQlbD*;h@IPr3SAyYNv*xAK^L?1 >> zQV;@r-H6)*$jB6n=f6AzYBQfK@9H*#SuHDE1JWy%+%SC%E-1<^kt6{{Dc_=Ra|h*S >> z#u{p}SyL8fJO$+VtTT7D=awTK%K5l2DMkjs`FV|f)!uSLL|S%YxKr|9KWy;J>3LW2 >> z0+X5bCFyl)ca}dEFlK___iXI8tXLub0)mojFqa(VSj{Pqk^vLuZ%scU^_C}3RfRWi >> z;azMkhVP$5391Xaj|Jj4cpD<>sFZPr?s2!MeFJ<IzkJ7#ta}gkcZ^c{WwDyjlu2Gf >> z;w_Ay!QM<#>)_Rjfc}9sb6(zkLG>QHC&VBKHEXA^tCsh<X0p&j(0urtvfmmx>D+^M >> z)gup!NNuFGo8t4m@hU_R_Hg)S@42cTCs$iNl`_J6CUQYE^u1`>8!x3twR&%;202|f >> zwSCYfhN4OXG6_XAcqav2&3&#W{8fS8<yu;Ra!#%Hj0Pn-#z5*d@0@)BO4jucX@bi8 >> zFdbi-eH!3C>z!K!ya+z4%!}L0&ZEz5*E&S#@2e}2YMT~)%qREBZyM_4Yc=ljPFI1s >> z%;CO=E{LeBzV-h=x#3yBmxA`?Sbbi53rL^(DDmEpm&WIDW*j(CAua2tV>u0M!J-d` >> zPF|Ge7-${)byI5_sovtTWH%V0Xr8@?$Tozg^-|=i0ILQ|NK>LmSS_nMF&a&&hg;Ms >> zAY`Yii16po-{Chznr6`+VXhnYq-dhk2<}HFx2Ogw3RH?$cv%3uhm&uYjH(JNv~9Tg >> zMxehM6)N91dJi_YtMG6Ln>Pk7w?E<Kw|v5j{5r~yQ>#@!wl61_bokYDQ{Tel5$xJ{ >> z-<m75ifx&lyYCkl{5ewRkqz^{WTm5fu?B7GOu$%|iRR0DAKx`%FGPB^ilsqGY$+IU >> zG!fn>g75qK#`7@ysywP_kbdW9&E?U@x0T3M?`iZ$wx|)y6<@XWfI3MvV<nh-#`kIg >> z)a>-`w+bHmnzWk$01hBBXv~OH{Hv%!T=K>36iy^66T|$3>-(Du6s>LR;@RbjeFmYT >> zrGv;{E#$Vy036+4425m<-5;q+{-+D88r#VRQ|!ayDCvUe9W`z8wd?h_K5Q4t8OSbA >> z5=9?9N~7a5)gGveLUQ)e;T5X)vD<KSYlYg9{qdAh2AI{kqTy5YuC6v)IgxiNWxCUb >> z$ZN({t?)EvK!0W6ig7&%LcFOw{PH!Db^5!*MzX;Sd(F4UuSN?x3YZ$&#!KTL<m%_D >> zw2-*P0M5XPJNmC>QRS*Fcw#)G+BKq2%9UXYRuv4@$<KdF4r6?A1;oEhgicOQ$3lU- >> zn*vaZqwQDvy7-L|(h!yRZvHeYbD{CVWuTKMeNs}_@~BjBr0#<?O#!#_rtAfL{bwlO >> z_X$g<<VV%T7Nx0=m{p$Qf}g+oavSe5_X4rVRzZpz4}NaC9R-T~9I9B&Z27K?yXiiN >> zX^N`>4ZD`2WL+S~63;c4e*jU#&*YkUG0z(|WcYAS8K)JUkFYyL$i$m)iWizluhDq* >> z^rb9(E1%<^fw-rm*04_-yQ~r#v5gEa+>ie`>pdV220C9nAcUC(XdNK#%$EPYx481O >> z1Qd|owBt6149KSt?9`pD5?s4x{6gLhvsXaxAt1H5<1tK8&=yWA3@p1RCyFJn{HfAA >> z23}|GSbJz+$xdQX2F9$XJWB%GC9*^U7D(e>sFd6w&dPqV<=J;id%#G|37pp|4>FT4 >> zx{vj~^NzLbdl$HYPC!Ii+Rt;<mx`BC?M7-cp92Q=BR>h=+0VzIbpaQVe-D!YIO{~A >> zzFHohZkg!(hdm$2wG-5j7#|mf<xkvUHQ@sVz)>QC+8B}}2FsE}@|L8qf<`QD;oWWW >> z=6#dI61ZgX<rCEsCkCru`a8hdgEg|;w7o7q*qQF$Oi%}wzMEKgQ`i1F)8JxEmZR${ >> zJq8M@h?tfgHw@M8YQ7K)S>dpQ2e<kWI`RAaDU~~bN+$Xyms>T66i=&hivOz5Xf;T= >> zvjY+qyuJFOlHW{Q+WE`UWB>0@j$;9B%Zuh|`t*^LZ_X~sV25f4xGS+Ym97FeKb%5V >> z&S*-h^u+WQ@iCLB!k*zvE!++FLEURd*{C`X>313}T1N=a<vQVUk(MoXYwv&+r98+1 >> z-mHw?BmhAl0kT75EN@^B(V{og5;pFi!jI&->6L+z`+1Y$M#}Pou38ZZ7f6z&mzSA# >> zq`EM@yopHF)S<mgIK8YaW2Txk;6Mo#o~PMJB^?v`&7ZZ!e=Av5LFkno?%rMco~!Rq >> zV^sK>BQsAq|J;Rx6T4uRket@3Z0X<MQo*A($zI(v{+Nba2XLab1;vqrG)huLW#>Z{ >> zSaUOOzy6NMr_C0XhA_cGHe~<X<$@HkUb#U%X~V|p1lh@u<g1zNuRft<m14hN{PKZF >> z_dRq24g{VxU+DUdChyyNU8I7$a|}b7s=gtu08&K+)Vf8@<yF1%xRRffP{_reTgw0+ >> zvxukHUp8oc1ayOs4she)hZc0vs$nPXH$26@w`?jS)+3c~r0nMwJzXT<k_S2KTTudi >> zF_wk)w~zP=?ihLfn>85{c?hjWQeGMM_8y=_LvE=v7;2dQ>N{NaeeKR*HivY7W8rw! >> z?_Cr%5Yu)#iIazaR$bztZpq%8Q?HS3iS2$7Ga;z{r;gi(I?XByIxn(9ZMcx?lP+n$ >> z+5X4bWQjIt$!0Qm)$zbSC&IH+VDj>C?dt`yKlHS9vAe@Qv!t7If~e^y051Vtoq62l >> zpW+|n(q}v0)X1mD)NJFic)+qY&7<6SVS96h22I5f^VaW|eDPvidD}(RF(YIAA2(u5 >> zkfC$HIiwbSR#wp#Xg+TlabA24YrS%GY_QXsk=?V=J0J=L!1k}Xu>ntMcR;S4NB|Li >> z(0+)ob!`9np5daQ;-Y@hzk9zb#OhokC9iKE0C|q^Xv@@e`WLgb4xOf8av!S)Xm5jV >> za#_zb`IEOXO}6`GmoqanLm-sAvsC)Fdn9GnlI-vF;lQ13S8>R?kjWc5kEJPWO#2Fe >> z2%|0sY=<`w5w0IQ1see0^I7p-I7=9=$bVDxvbmJCXIih?+X<+*Jyr`W8zq<1)W3)1 >> zQSZ4c?EU*2z#nb`uWMB2@913uz-=-m1}NkmD@-z#7j76<!tS5QOYXJ;FPg~KvLa*= >> z+hKZDsL2Ut(WD|3(pg|82llz;Q-B=Y&j~1b_c%|v9(Z4Kq0Y+c9EOWcjyk0L7JL$a >> z4F@FAZ*g0;=cbj;l$$x?Sp&FH1Va#RKz<t@dkYT&p0J*tCvCfYzYhgy0z(-_|1)=< >> zpf$o9A;{U`Y`@Weh2$P1ODW<^g9<7LPf5}r*NVGCCfdJ;0wwOpUC%CS(e#-0wA;*n >> zRX_>wHvOiW@XkeAb<Xz7WznkMF*VhTY}~-PnGXsJ1hu&vPV!q-k9Qp(SP}3h%md}0 >> z+h-pNRp3O&1GCX?s!p6=F6_s8%UA{O+*Y#ZMTJCgq)R>Tr-%nfd@gK5UOo;&o)|3V >> ztq4_;$$CynKZew%{0?V|1hW<$({O0acUous9-o|Sqf|Oi$k*<6Wo2>uPDJRGgYDad >> zZra=GWeZRD=KO(sR?o+%K87njMZOR?woZE*tkd4&xpbH=F1t`n6AAsKcq<`qXIWU= >> z+v@WrUoBdBQdhFN6~I&bM7RyOSqyjzM)`rrZpd%4^sSTb%Rq3jK%;%{EY}VJP-<!W >> zX0gLjLxRrW+(4qYEC;RSv?BItKw0!}4Fhf<nhxD6rd1|t6i)v7D=6!f8-~@L=d1}# >> zlj(Fasg1W&{!0!487<}8Na&_5(8O2WBPR-6)WZHqOZ0f<zB0l(bugPw@J+vsiAQA| >> z&(<y}cjO_y=COqpC*=ZQyQj9=k`gjTOuskW>XGG7E<0)8&2Sk_&I<P}7m$@23VqH2 >> z9{qd{0?Ke}hd~oDJATg4@!3~uM`>2JdryKc07@5W3qta_g7d+ys&MH-=gtHvT$-MO >> zjfiyBPccQfnzh=&$z>hA7W)-#iMi$5a{E1cwFba6=#`-hMY{k2q(MNjnV~%X>DQkI >> z@zlJ)<x0Bs)3b=|GmlB@wrMEjPoTL+s`JCMa}OqxJ{h>U*1rutj=jBQpYS?08`LUP >> ziKn*Aiox<196Wyv1QDW+ftwCfqoTc_006t4e5IME;3bRs5ic!Avs*j790g4|opW^U >> z<I4tW8C$Q?RpDvDWC?|^m_z()3A#Y%SAQDM#%w<_kjscCKfV>bN*f&xR=ysL>}yiI >> zLaE09z=XOb==}9D*@EHulh#v;5E~lwtY=+VHp=dq3Lbu(6$Q0=gU<2qmyUp!TgI|~ >> z(&RUO-!S(<)l6i~7*^<|1LaeSroPnZ&6*@C+*`aT*^t&0=>5>m(Z-G@+L;-Wb36op >> zJ=pooWxX<yvGo_Z=zjgL{d??Jk=yFS*hfB8HN-@l?NE@c>HXUBOa~tmBSBaoPzC)G >> zKrk0P%Wp+aW(`#aPQGyDUB!lKc)UW9pgpkdp3HBrwN5gYBux6@O4<Qa#tn3u=d9&O >> zpYTTvmG1V?hQGs8rV0!yh#TupF{<U=BKMG7U3f3pAh24uq1DZMSrzcH;`fc_wVE4r >> zvO1}$^RjLF))tiLY1yz)1K_oc<6beX1C@(QW?>-kNMlp8<`K_fhE@MOVl0WAzYj~J >> zkyCdv|J3zO<i@FNP04%6pHkoC2gaAB!y83RudKsb<q8f2&1V^-{e4a-fk2%AoCY4Q >> zdp+)ns{p4GskPhQG8io|5m2a6T#%DO{mczf%VxrCk2(TgG4y8fRanIcNz@G=63UQX >> zD)fHW=K#n3GO6eaj$Hg=_A{E<g`^_-%<^Z;rLpakjx1!xPujBt=2UcrxO%vj=ZA~i >> zQzYgC<?i)hvqJ%P7f6p)!->SN!sLQq)1CU&(}TT&N7>kS69XXmW@c<YW95+z?{0u{ >> z->4wI=W#NMJ}SKvl3o8gwb;0|K{RB1_$yGccMt7_xds#Y5xwK&Q8dg;m@tXmkn};h >> zM?o>qcLlNvai<#@c^BbTfE}X=+%{2P#W|2fY{<2eJ*{V&hmxIQCb#33nPiiCl|b6; >> zLY_;x%<w~MO1&l!B<TS&9qqNq!+oF(XIRW9+Sd--@uT$peI4K-k$8*Bwj#B$Pn9!? >> zO2dw!{H8AU(Ibh&W}wm$m^t%tNkf!T{gxn}_ENYy6$ehRTq<UnP*n-O5$NOn;&Gu9 >> zWnWDBYetD9eXF*)1S3i`V6*!;H2pc}b=xn>2<{=NA_%LMws2pD9*<i=rUo@S<Ll?3 >> zWJzOlyPStj9z{6497gTuGp3C(AK9nxviZ)}s{nL))_(Z4n8W$V!z<BH0a2tyg||A{ >> zLzCa|D{2cY0*oF+0RcMI8#~A7It^W{5m3YI!5|hUHhtZ7a)qu=Bk=8e8dybrTKU$5 >> zkw<LH7b#U#iTmu9;KcrT!5nB_@S`!LDW(JhtH6C(3fZ~pG<)<V3ffs$I<JDd)AJm( >> zeiM+Vjt?P7Rt0#C@VQc;g9{=0y3sA3YCW32!AdJ%I>SVlg)LxcFh(Gx5_Rt!=F1fW >> z&FYViDz<;}aVQBBkiK(8*<w!_vB_pUZ7N)(N8;nob=4!##!>$!sjid;2hzqwk`?RQ >> z3;+WacHipcTrrwCHMb6(eNDX%9%y%nE+K`f7Bdp<EkCyaimfcQMn?Qf1dm&Gvt6rR >> zjfUR$0YS*gi7h3P!7+~uO6WZxJNf>PBsr1JG6?0iyPPhb0+4qfIV;r6$f?eqWw9=w >> zO~YE1D`(}j@l@oMzWc}kJcg-u6ND_uc^?Hpra(c&Mv@K7Ej55yd`k$^Imff}k<dOS >> z0@zUSJ8;nj{-?jqfF~HQ|9vEd>HPJvSN^h(O8-bsAkbc>MbBs{G2A?yc9^<z7Z(3h >> z0d63V-v(Z)9S=mSy1p_}pyLPI_a+~pn(V9c?A<{UIdwzND;}ePXQ1-}0t|#YJ}QX8 >> zlDlGSRz@zbCXCIQ?6CD5ui6TCp<`Wq^0k=`6(&G6>x26ap7Mli;N9(m>3usohf%VT >> z)oWnnPR*w~XpPol>ze05JI|d>;KN49ovKJxW#1G7)+uiT)@diZ55uv7D|=+Zcjd+3 >> zXlaVgC0!@mXjt&jozknj5-o#fe1Y}Z)+r(r-XcIEPMTi-Mh)wGEl@#s$o)tqdnwQv >> zbJHzD56=bi1YtasFwDVB{KDYDoVmN#NNn_X2Rby>s>^&&En(G6?Y_(Lk&knRb$?xZ >> zVOGTa#rF?tb<t4FYLCsEXqx?NBKF64dX}6}^{t1W(7AHGuLIx8UYzjV_D9{-0FT;L >> ztKJ1rjRK|XYH$1J*$Q3&&~qVpnET$=F{$Ay*|@tYfQC_ja@sAC#rRW=uJI-SvyZNb >> zrB8&b%rs5UtI)v6BM(2*Zi={1Mc(h$<z3lRvXTsZ^I@ehy@(_dnttO3*~T<WNOOW! >> z-KX048dJ^wRkAwEhx^y;nH-PPdg)%=-6{40o^^-4ON>wY>Djs`@3+(Wo3f?8e)3dG >> zhnk3=0ATsIev4u90-&?E@``lVJu4^7auymE%Q0ie=oZf}c}3Up(ZeC{rbm`}^8q^& >> z*w#JsR@)y*hCIF|1<w6X6L7L7K?tJSPfeT8awh86UA&XZP5ECzhxy>Dm3*O!yO=Xv >> zS-m!@3CoOs=w~Ff*Ob(*4?OA66^6>qAJo67JNPt{)Aw{6;{2UVJF1>R*2cY3t^4yp >> zc~RgvUs3pR`1UPB*ZQ0)kHUkJdE3myRIBxVcqG*Eo_tCc|M!A`FWg4;LH%4v=VybT >> ziCFt_qMDUxS>^&yd|8me0s-+I^o*6syLUHq$-YD@4RcM+yYtsR&Kc8<+QHBTttV16 >> zblZJR`Td??>=@jksob=zPS_R~l=>Sf%s5hRtp2V==A3Frs`5s<lkngXuI%;rDHRJ9 >> z;f#){;^W=vM*Aq5sN2Nit#^*CrX6U}8T4pb;wwxN6~2jVNeUl;KYO5yEy1NeNPRYb >> zinxt!0{%q9rcndcvac?+V5g)XH#g+F@^Re72Li*^%|Q3cAAzZx2%73qby_syctU5Q >> z%eiT$zA{lF1;&(AQD6Sh;MCyx=lSBn;_cbq#`veqrR3P=oh!!58l*E)b+^XEd>T+o >> zE;g0cFVqz3BB3djeIpQ8N=U@Yq@y^@rQh(pXo$-_#}4gviojV0$f@h{QpN#yud;9a >> z73BqyF^eBpo-G^Y^Ubnxgup`--ua5}{Q}-XR5^i$d;?Y=zt4S>FzN$=QIdH9gq<TB >> zvTnwDbu@LvT~^B%Ad<bF=>k0CL{ea?J%}?e59P7jXvM)uxILMJ-BP6+o^EuQNB|UL >> zrbgiIAh(Gjs-nS2(d^ihPf@l{0Lj(iG~ct8bUjPcYRFk+6nmutY<yC}++=0qVZ}4F >> zbDrD#q~dj~U1N;QcbqA9xBk>F=!rcDa-7@OmBB6dnW?oXN(*4cA*L_o3#5=f9__PX >> za}KvR37vE7<X5<F{zK&~LGuBI_grJHm11j^Thqm_ahtBYC^5f)v0%qv#%QMyBpz!K >> zrgdcB*pyWE740%uPib0KDMWqgTmzoo(4iR*pXyF>&T#&jQ%|(LllF@WO%*cT(uTNQ >> z?%m*liT>)ZjM(9ddg>>OXwwfxa6X357Fkb16G~;lY1p-S4;6$Q9hpFqx?bN8mt7(W >> zLk?D$3+9K;##V=X{Pj+IQ_0W!lO4StyfpWaVpn30yrgn)Zc!fR0z%E%4yrk3hbh}c >> zfTxdm|6qK)(+((7SrCvDHf0oQ<zfF6A|jQ$&Nx><s3r$IH_ET8E@M;{_3maz7A%&# >> zJB%Rxp7e7oQ=r2lnqO)rrD;1&;l_4-qsL6W6>41$XwgSv#DQTiHdy#4-cG{9n^&pd >> zE&<PRvIRK5FAyFbZyW>LcaD7DvgzwkgsT=trR=C1)5H3-sk5Af|Aa+DrTJox9K~0M >> z%SEsazPd^4`Y954rx@^XbX-jYsFaaU4tmkepDkm20hO*o5F)PW)<5p(y0XrX6nk8y >> z*pF@NOEND8qu3Tx!Kn>0rnn}nRYq0#IuG(G$`h2f?dz;D0<g}0tUZ1el29hOci>@j >> zc<JCvKU{7Vv8gNYs?Xto3bvm;+v`&Y{B44y@BLJ742!|X1Nmng28!u^EVAiHYVJ%c >> z<6q%o_#{A2ybFH?%4)r<YUv+MvmiEZwWae>C1UWApBOcD6WHTO&}IkCU+lc{8!a#v >> zP{3%zO+$W?2!?4N0!8{$#%7$yr-wl^v22lt$QY<})0_X()mO(w8Fb+yDhLwNNQ*QG >> z($XE$of3j{iF8X!Bhnz<-JODflCpFyO1h-L(y@0|zwf*Eclm31-*@WF%$b>Up7V^z >> zQkm|uNgHmR2t7=>KyH@*cf0mhn{K`xaMAijxnc+=WgDJG!gMp@YuL5G`@1F2${p9$ >> zlT%t3z=%2doke&GZL&kJu&GN_EZFj1&HX5yIUB~+w!vW)g{_HPnsaw~(l7O7)oCEO >> zY4^&}USZ?w%ndEt*Q3nALnb6-55Mr`2aD#b5%pIS(?T}BH=`;%{I)(Z>-DCMinZ4P >> zjW_}on{CFN;|XJ6&e@*F83Tk@4fX=V*G~ni?U!<k=yLxqT|gpNFznh56a7J_aQ0rg >> z-DXyJ{V9aEwf>2w<N15z=2FUh&b@GWmrR=jx9wB>81J?szUpoVKk(6>Cb#nX^3@kh >> zSLazAtpxdIznVRChwS1bH}ett1~(;uQ{YAQUKzD~bM^AX1%JzydVf`G=TU9KGbFSU >> z{|UqpV6eP#56cEC$q4DP>=z{Y;~bkP<xaZL9K8A(Xhv}`-T%x8Ebn5w*SPoB6Gsn4 >> zIjr@%;anN%yx1x3>bvpxGNm6o5r{zdD~2{_<=L+|Aap4?4&p^wUz2)V_A7uR!M9e6 >> z<rGubP$`fvy^^cn>8++w`&n8ib>7O>KERMG2KBKgx-i~x=D<>^1FA=Mm}d5#$Rd01 >> z1o(ba%Ve9xe&y#(Xl#tXiR++xhN@AuOg((A;^}ltn!4+%-DL1~vDInzj{@xkQjba% >> zt%CAcdv+g6#`^YQgI`X4Bqy#~ew9Y~1kJ$!Tqch$E%=oQa%GOm5fMggZ938OG130c >> z?h6Bo_$-F{$L#Ma+e@Uu&xG(Zg+%hs&tSWV?Ppbm9kMH}eI?L>gLMU$d?<WE_pPg1 >> zlyC83igAa#&KhAaawM1?qs=kkO@S2arHlw|rvgBDA3=w9m~m6lu-)z*L76LZrl@KQ >> z7V^3;GsYSs;05c7v3+!+uBt=kOdM}Y`@=i>og?82W#r`S#h3BA{D$t?gj1?As#!>9 >> z3e&?V0I}oLYLOo&-o$H$IX88{@Wr#fpOg>&EF8*;cWvKQ%@9;Vf5CU3)-NoD-d%XK >> z=t?s|R+(-%zEjfSNzrAtK^dQsLcI{B9kv9|5(E8-W_&_%ogm+8#vCdwB~2@>-=)Qh >> zuz0bFvCxf6wi7qy&p(9W@WZV72Lw@DFEy1;#A5j<yZwH3tBStpxh!poaA<ut5b+FH >> zk?@CEsmb_!sj2pk!VIRSXAf|<%@j2RH$(;3(TtpU&V*+Wm()l?8o;~LoukZH?~fy7 >> zueOD<$Vjlbu3~=&7~S;<C#RkUqv{B@UyoNoodmn}nekxIpHDvsm!0Kb9M$?yAwFOD >> zkDRDxs2JbyAdO1nU-`NsCVze4KR-D+QRNH)yK|Pb+`vXk=-aP>@4kBLw3ljPp*eZM >> z+`ZGL7fK(&HcuQ|(`s+0yUi-?c6JaV1DD0hgY%+*-(g@%)G%$1!QCthY+8!(Dkaio >> zIkiMJQ5OSN53)P5B|2S;5^dzDx#68TyJy~MdaGYuhb~;df!5%9>NhbB9#sbv`d~@_ >> z3?Rw{<}%KyallfvgSCI`VP2N~)~x*8eTd(gzz_@!k(k{F_Wb>7lp~Wlpqf8bL>L&e >> z8a<0L@sm>KJb#}i*)Ry3##229Tg1~9*QLUh)XWl!A)69}n)TF7m&w;leuv8k3$9{| >> zRy03WqFViDJ`gPKAo3FG_7Pv1t@*oIgUyjZ3RSX<yFM{yCG2tDsTOa}Kgdyh{G-9b >> zUx=-_)_>`wu)~=92BNk=>N@&<kg-;iJx7WPZ;sp8vi$Vs647GiU5zjob+u1rrh7z& >> zsJ;h8&fef3=FhZLPxI(E2T87f!#DL@?%RG7#G0RyJpm`Mse7mYg)5RoT21%X>nd%S >> zA1+q)5Z{~mIHl6sgQFk!c`F?eb-CAv8h>aOu>;JW6{lX-W{W@qD}FZH^iEvP+ZL;? >> zFpEs7Si#F+S`}bx2aBy%-e82L@?DiOBfL~9zb>M=-4K)Vj&yU#4`y6Y=Y$*|%fbul >> zWHm&D{bZBBj?U|w=e5e&|B(O)T>OYeoJ!u9mdDgCueyZzmh<WSGEIMD<Kx!|2VRxW >> zq_~e@Hu?SL)hP0Jum>jdWKy3p;~@KpNQvi!J0njXi#zoNClw7uGnnD*oeEAN6Aes> >> zn^OKESX99%Q7AaR$L8h6m6B&FjP!4Ow&%=zP}&NnDtw)rDt_!M25{e~QATrTej`SL >> zTIE%RV~YHfH3%bKv(aWA0%rnBzgZ<r{#*XvK_^u}keA#c*qZU5?3S#5l@NcleZmZD >> zdj!K``EK!oHWTR?$X$u%bK?)nd(SgrFPLzj@IGy(1pP#bKc5;_MDCsVG0*q%s>`JO >> z5#R$(x|&(koj>2GBjv5sUbe}vC~>MW0ym0(oO6Lv=oGZ$s}3#XvDPO=@}TF=yTvL< >> zfdD(US)N04bH*Jjo_-K@B3&m`lSwGGy1o85G7wZc%M&<So{TEN!@Xn*xJ&4a9_<&A >> zmJk3Bc{lHva3m^Z2rVv@2)KS(RG&T|7{u8QK_+5}AL~N<XdvsA_K_sCYf6&>d_b6? >> zOes;f{f48z2U%y~7}=aSAmY&_2f-A5&@4d2AfAcDFMRr<#*q-KZu9tq8ZsT$v!Ynf >> zp(Ki#Cy>XW!YFDA#Hl!zoS;)ad`0ry1PR^&J})iCqp3Yid+K0Zu*UT%+<W4NiuBal >> z6eZ{@f`$AE9#4iKnUp$RmCBq+5VDG~Z5fiB;)SLpGBL%kf#aJq=%Zks3h1TKHJ{x_ >> zSVUj-Tr{0>H36JxUSFJ29mo-^Bk%j!qtjK2qk){JCbS}Ho!z)%{`MbZRwuAX^T>Lp >> z-7heZUl2!rQa3xH^sjb=(eCK}>1OX0`UZ^H3;yPr8cCx>kx)u7a97&9Od5$uXTkRR >> z(byW#CqVYMfV$_kLyW^Os}f`g@2~xJkE=Q2H3z8xCL9Z*kWs?>ZlCCgeGy_GOs0<v >> z%qK;hVq5bH9n4Z1<7Rer8(pzLa0Tj*N%@&L@%}oInj7PjjeDRi_@ptQ+?XPjWUW;( >> zkK<8)Kc<d@o4p2cSALV$GQXRo@`Waz#s)-pk@jBot9gh!&H3W^Y&zhTDjrAMny0+( >> zyomH!oH3e*bp09fStybpL54kCj!RD-+Djw1PWC}>>1E<u1Z`ekiP1WF&~y|l@F)uN >> zH#Xb|W<2P=mD{_;4!r5V>vSG4eHqpb&*i^95WOr4I1}rFRbP622-xa#3K?8JvEFp* >> zWNhC%Siy>OGDgUrU+l~@d7M5veC7aX+_EwJt6K6b#)ZAsx+m&{Y%m@o9jAH*%3ItR >> z$o2l->m+XBQMbIei9ZsSAzi#I->TcIMF?kBuJk{2`!5>a7HzGZA~p&;1wu=CDa*$Z >> zebp=b$8!!}E?k%5nz}Zs=io&F!@+yE*VGL%{a4T*YyM7)SfSHMOx8Q@q2Z8@-3Jv^ >> zNQL!D-D%tUnM^#B?^=hv@R0(4rDHbXgCh&&@<ro1$@8-5y<E9hW)~9yenN!fei*yp >> zpu>LE-`%|?1|M#|nO1c@Zx;SBy4Gd#nk&%M<oQ;XNIqx^iip0YV9q?!43zllQQc#7 >> zH;qU54VX_bc0C;DA|<ux!m|A?GW{QJn}x&e^KJ%JyNSF{t8JZSuCgeV%NMLz+^OPm >> zymCVG4JNr0^WH1S^0z=ZyL9OJf%W85ufMeSs<onR=19B8cnIr(gI#0ZX;F5=2j^8x >> zqX<Ru6v=}2qK_Hf0sDK;$NX;7f7DkU_$YJk(ht!F%d2v@qn`k3DVEGu<NH>;KnqB| >> z9_)BC^S!FzRdmPD&;X>NY%ja=g)Wm~t+Y$2j5kk_(G$*D=&a)!Y{k}gzW$BMa28&; >> z>$hGI2B7uu#L{#@_dY*}fd54IU_Lrn@Vf%jnBjsk*8QFBrr;w!#H5qL(=iYqR>}NI >> z?hw9WoPxKbaN^lt)>P<{eDF9(9v!{i=aMNBwJeZ%Gt2~OfslGR7a^}of$8Q|`I#iA >> zf|22aS1RSk6=Ld_*vYC8J<%30<IebB92p}5e_lCVekUKOhjv{JCG!se9RN&xBqe2c >> z6!>q>F>k2>mc8I~MuL;B4wvoW5kNXappJb84d3c0R)sNWmx*@7=8kmEc_bdcUe}Iu >> zb_QHpU|T~r;GNcQpG3hJ1;*i%bu)8;ZMtodlo#y9FH}FtHy5d8XZRn<wzTj(+Fq=- >> zo#!TwH=Z6TG|nNbWi2X3QV6MO8Q{Sp_7DomFqV;py0X2>0?+XT7KlSfTW>&xt`Rdi >> z9!z_fNi*On6J`m6Yu~{MS8W|O(K!O}2iXL@eerklu_cK++1mJ=ZBsWe4)|ofiFZ`a >> z;4$(sxw?IRy#ZZOkpk-%?`!i;aDD+LJ`Q?xWM)&k{T0*IE0o_}HA`HzPCn)4I@DwJ >> z(KgaqKzWqYy=p<+sJ)O+2l_3OCM1ZGkU)X{hhg^1B5~C=`4pR{j3S{Q)pXN95Oa<} >> zjieLM=O9XiDC7s@nzCX1d@;GMKy|-hga+9co(FIq0Fb_XcY?bhs9kjPuZU;ko@w?R >> zz*`exRq>Q1`3)XferqOyuIjTXO<Fz6?I$VyP2t9+ckfQLvtpx)JfmI4JSjI=pr10i >> zmL1-goY8m!nN-Yoc2(n0YgFRNoMZrl+-6_OdSu<bD2s*_6E^8MoC&(3>0>vm!RbiC >> zZ=;Lzj0lq7Hu$hQAAXOJ*)lHK0^R!rN}W8KgleM|{MfI#XhjZ<?;D*3hWET6oof%o >> z{-vX$B_gxu$C02N;mv|O-vRTEY~ilrXelc+x2B;LBCc={tJy2;42P#GTX>HyoT@mW >> z71n`ll}#dn`_wY=kjti~tol8o_>(!LQ9y;y$@F2fbKbcZPOgeb67G(2oXR_M!vq%# >> z#YHX*U|#J`!VJvfi)U|lDnBV|-e6q<WCA2Gc!t`^92;Rh9_%|}rH(XMv2F*3a+3I< >> z&TG;<LN427JWG8@{Af$eai!W}KdNbdT#@qjsp`#uNw-Y~k^%Cr=*m#QIX@zfSPWL} >> zWpBkI-8SSt(RMmkr0sApYBSGGyV4X6Ceq!4$0CCE+<ZJ62t{DB4^?1#v+8#&_N}}t >> zW061pq~K9Ou(58*!xJrkx@{=8+ffsVxp13s`_Ug!`0%P{0sS`+g)vU;y;~zi5Z!j^ >> zjSSe@MgRh}hoURU_|9zag~!VbY#{37UiU>T@XOrtFE5Ic#2ZgwfwEmWXL-Wu!{O4C >> zt8Qoa@T=uSByL-v@UGQ~q&D@8-|#f$^&y`{19}Re#aAD|0efP)*!j1oH^_)!?+GMt >> zQf>~F`_iF5a2)>0_Pw3%7%v{*eua#YL4<YR)`}g+np`UC0tZgl&()e&Q*sRY1!9N2 >> zLb>wY@20cb@nA%8+ZL4lC?6x_W^)Q#M%BaNU%5DGZ^>HSN*TYAdyVZSln`CO>5Vrr >> z^6&j>`AqAly;wy)(|PF6DGftXgjQc5Sv3cJyp?B}IDERc>_BYFA8!29iBwk%53jj| >> zedTU%<;5>Q+nQ-0tW=@-F@>ZkTuP<y)9iTdEgKfEaWh&iN*S3_+dHmTCqT$vPn!MS >> z1N*?Oknbb<T~^^C`<dj>7o<M22}TaNvxdg|Q0byxU7Y?jbiWmtRn{J^V8@f|3Q4nd >> zk+?|s^Cl5mh+7s2$Rpr@AXWqUU6-Ab#0&USF`DcMp7Krm#@V&Wr9W+&8y0>&Y`LQ( >> zrp_|#j=kT={`NMdbQAIUXQG72@&j?Nx%EPyl&`jrV0<KJpV;oAB~#etX5rhAcs+Ky >> zl<}iz{<?F^^9DqKBy~AUnNC_lFNR&{To3%=qiVYYHXb*>w|;q;9Bu_9%pNu`Uxl0K >> z>K+8_f9ALdIO(Q3a=Km2KBq=Z!EQf<QB$+yOmjXX_c)is5`~Bzm6%^27u}>G#Hudi >> z5#6_k8*|knj+1UXhy>+s+tvVRMLP)0wX=^5Uz5SBy})JZ9#_523ji#fz2C=L>bu{5 >> zgRl2Q90!c1W<OuFW4OMl?Ud<iTDDwe`Bm*Y=8^GE*NCa|*qS5mEhoyi>{?3{Z?gC- >> zJu-j_JS7cFn%G9CJWLL8WKa<_ZvWku9ky~fZAUe-b50<O;77E?#T;%yj~8-hZ{bcR >> za9nENXtC3%dfd*_6T|Eya<StN{tgZ|xrnuVYB5&AuKshe)tdSuV-mYgN<g`Bi$uu~ >> z!8oKlMl11DkC*j3*3@w?;S>a20~CC8cg!Q~)?9?2)Tp8*%L*eFW7Jkw<h<;T^8XYx >> zs<eK`dx8J`zS;{`<-YVlc~!Q6EQ|L&pR8D!gSBRjwCpKd{R50wH-gIjJ>#X<>*SzV >> zeR8V}8M?w0{<A@gGwW{GuY<|1Z3289;%3Fp&^50IRDIh9cRYk!7PH@Nr2ohlHCuoA >> z%Omr616GTmcAkSY8eX^ZS3cPLR1^&;PciZx^aiHbdOaKPQf?gr7C&)Y&~w6SUrPlu >> zq<@ce4>Pi45EAk}++I}f=SzLrqqN5M^)BwmU_9g5r%esKiMGHgs}|HnXSQej2CsD9 >> zW(qOZ=QKd&(|MuG@nNO>#nG$53hzIh$nd$o6}R0ydXJMAvbj`8WWX^QDHrExSwOLi >> z<R$aOvH1A%ozkVr?E(LFQF7A?wyUtI5>r$Ysgv|IEjJ!?-K}yZV!`R|6+g$CU$!NK >> zF^VbKM<@Tki<C?oU;e_+lTWdnkJ_r2&eu-w_CG2ZYRV&wZsp_Gz?8k}B962ihUcZ) >> z_nYi^gnB5o{)SWt#g?t?Do!zc*{rq^A`ORI0aX#YkSzS$JE(iAq4lipfk!2_dTLCs >> z<7PSrx3Ndb5ZZBM%3(>I{Dd3WDbwIEc*QHRw^|!aH%XYC&8MtH7=45SxI?e#4hYjb >> z@l>t$QgL*#cqage%^Y(nkJ4Euqi#AnHWXmz0IcC1?%G*wzVSu1OdQeQZFN+DPU|N_ >> z<rXfl${*>Y`(niN>9+&EYPUCd`(QQwLl)sHyst(y@*LZg9vrJ^KFB-G*MB8ych2Zo >> z+isQx=!5gC4VO*o1x~Hrh2;`?athds&`#^)r+akUj-GUPs?JX4C$b+OWb?sPua7w{ >> z*8Y%oy5FoG&@DtkFO@9KoMncyynwzIgKtl_to9bR(!S{@)UJ|gc_+E%3G|GXNa >> zux`VhpgbIgJd80h>o)E_X8l#72Q!P%Jh6#<raKqeYq-vf*xyF?reGYu0Ok)rrqQ1O >> zNU~k~wDhQ2F*?A#H>KD9xx!?L3u~7MOaPKG+x-jd#N18CM0}3sU2)^H9QjUZA@!UL > >
I ran with the very latest doxygen 1.8.7 and it is ok again, with doxygen 1.8.6 the V2 of this doc also fails so that looks like a bug that has been fixed in doxygen - I checked the release notes for a reason and nothing jumped out at me. If the @verbaitam blocks were going to stay I would say they should be @code blocks since the sections do describe code. However as soon as there is an implementation of this API doxygen will be able to take the API definitions from the code and we will be deleting them from here, so I think it is ok. Tested-By: Mike Holmes <mike.holmes@linaro.org> On 4 August 2014 14:00, Bill Fischofer <bill.fischofer@linaro.org> wrote: > Each sentence starts on a new line per the convention we agreed to > earlier, but this is input to doxygen converted from the Google docs and is > not otherwise line-wrapped. So it's whatever the Google output is. If we > have an automated tool to further modify the lines we can use that but > otherwise I don't see the value in doing a lot more manual editing on these. > > Bill > > > On Mon, Aug 4, 2014 at 12:42 PM, Maxim Uvarov <maxim.uvarov@linaro.org> > wrote: > >> How long are strings in that document? Maybe to fit them to 80 characters >> to better read in terminal? >> >> Maxim. >> >> On 08/04/2014 09:33 PM, Bill Fischofer wrote: >> >>> Switch to use of @verbatim/@endverbatim to avoid formatting issues with >>> different levels of doxygen. >>> >>> Signed-off-by: Bill Fischofer <bill.fischofer@linaro.org> >>> --- >>> classification_design.dox | 879 ++++++++++++++++++++++++++++++ >>> +++++++++++ >>> images/classification_flow.png | Bin 0 -> 35193 bytes >>> 2 files changed, 879 insertions(+) >>> create mode 100644 classification_design.dox >>> create mode 100644 images/classification_flow.png >>> >>> diff --git a/classification_design.dox b/classification_design.dox >>> new file mode 100644 >>> index 0000000..0cb7134 >>> --- /dev/null >>> +++ b/classification_design.dox >>> @@ -0,0 +1,879 @@ >>> +/* Copyright (c) 2014, Linaro Limited >>> + * All rights reserved >>> + * >>> + * SPDX-License-Identifier: BSD-3-Clause >>> + */ >>> + >>> +/*! >>> +@page classification_design ODP Design - Classification API >>> +For the implementation of the ODP classification API please see @ref >>> odp_classify.h >>> + >>> +@tableofcontents >>> + >>> +@section introduction Introduction >>> +This document defines the Classification APIs supported by ODP v1.0. >>> +Classification is logically composed of two stages: Parsing and Rule >>> Matching. >>> +Parsing takes a raw packet and validates its structure and identifies >>> fields of interest in the various headers that comprise the layers of the >>> packet. >>> +Rule Matching, in turn, takes the result of parsing and sorts packets >>> into Classes of Service (CoS) based on application-defined rule sets. >>> +@subsection use_of_terms Use of Terms >>> +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", >>> "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this >>> document are to be interpreted as described in [RFC 2119]( >>> https://tools.ietf.org/html/rfc21199). >>> +@subsection purpose Purpose >>> +ODP is a framework for software-based packet forwarding/filtering >>> applications, and the purpose of the Packet Classifier API is to enable >>> applications to program the platform hardware or software implementation to >>> assist in prioritization, classification and scheduling of each packet, so >>> that the software application can run faster, scale better and adhere to >>> QoS requirements. >>> +The following API abstraction are not modelled after any existing >>> product implementation, but is instead defined in terms of what a typical >>> data-plane application may require from such a platform, without >>> sacrificing simplicity and avoiding ambiguity. Certain terms that are being >>> used within the context of existing products in relation to packet parsing >>> and classification, such as “access lists” are avoided such that not to >>> suggest any relationship between the abstraction used within this API and >>> any particular manner in which they may be implemented in hardware. >>> +These are the key ODP objects that the parser needs to employ, that are >>> presently defined in ODP: >>> +@subsubsection odp_pktio odp_pktio >>> +odp_pktio specifies an individual packet I/O channel instance. In other >>> words, it would translate to a physical interface or a logical port, or in >>> the case of channelized protocols (e.g., [Interlaken](https://www. >>> google.com/url?q=https%3A%2F%2Fwww.cortina-systems.com% >>> 2Fimages%2Fdocuments%2F400023_Interlaken_Technology_White_ >>> Paper.pdf&sa=D&sntz=1&usg=AFQjCNEBdJTBmA1XaNGY3pmumQTfgSi1oA)) it would >>> map to a logical channel on that interface. >>> +Since the classifier API deals exclusively with ingress, this object >>> represents the source of packets into the classifier. In order to support >>> any non-trivial use case, the classifier API needs to be able to assign >>> multiple odp_queue instances for any single odp_pktio object, and may also >>> assign any odp_queue instance to more than one odp_pktio object. >>> +@subsubsection odp_queue odp_queue >>> +odp_queue specifies a logical queue for packets, and in the case of >>> ingress, this would represent a stream of packets which share several >>> attributes, that are delivered to the ODP application for processing. The >>> per-queue attributes currently defined are: queue type, sync (ordering); >>> priority; and schedule group (set of processor cores). >>> +@subsubsection odp_buffer_pool odp_buffer_pool >>> +odp_buffer_pool specifies a collection of buffers of same size and >>> alignment, as well as a set of policies such as flow control and processor >>> affinity. The classifier API refers to such pools that are designated for >>> storing ingress packets. >>> +@section functional_description Functional Description >>> +Following is the functionality that is required of the classifier API, >>> and its underlying implementation. The details and order of the following >>> paragraph is informative, and is only intended to help convey the >>> functional scope of a classifier and provide context for the API. In >>> reality, implementations may execute many of these steps concurrently, or >>> in different order while maintaining the evident dependencies: >>> + >>> +-# Apply a set of \e classification \e rules to the header of an >>> incoming packet, identify the header fields, e.g., \e ethertype, IP >>> version, IP protocol, transport layer port numbers, IP DiffServ, VLAN id, >>> 802.1p priority. >>> + >>> +-# Store these fields as packet meta data for application use, and for >>> the remainder of parser operations. The \e odp_pktio is also stored as one >>> of the meta data fields for subsequent use. >>> + >>> +-# Compute an \e odp_cos (Class of Service) value from a subset of >>> supported fields from 1) above. >>> + >>> + >>> +-# Based on the \e odp_cos from 3) above, select the \e odp_queue >>> through which the packet is delivered to the application. >>> + >>> +-# Validate the packet data integrity (checksums, FCS) and correctness >>> (e.g., length fields) and store the validation result, along with optional >>> error layer and type indicator, in packet meta data. Optionally, if a >>> packet fails validation, override the \e odp_cos selection in step 3 to a >>> class of service designated for errored packets. >>> + >>> +-# Since the selected \e odp_queue may require preservation of packet >>> order, i.e., SYNC_ATOMIC or SYNC_ORDERED, optionally select the packet >>> header fields from which the parser calculates a \e odp_flow_signature, >>> which may be a unique flow identifier or a hash, such that the packets >>> which are assigned the same \e odp_flow_signature are scheduled in the same >>> order they are received. >>> + >>> +-# Based on the \e odp_cos from 3) above, select the \e odp_buffer_pool >>> that should be used to acquire a buffer to store the packet data and meta >>> data. >>> + >>> +-# Allocate a buffer from \e odp_buffer_pool selected in 6) above and >>> logically store the packet data and meta data to the allocated buffer, or >>> in accordance with class-of-service drop policy and subject to pool buffer >>> availability, optionally discard the packet. >>> + >>> +-# Enqueue the buffer into the \e odp_queue selected in 4) above. >>> + >>> +The above is an abstract description of the classifier functionality, >>> and may be applied to a variety of applications in many different ways. >>> +The ultimate meaning of how this functionality applies to an >>> application also depends on other ODP modules, so the above may not >>> complete a full depiction. >>> +For instance, the exact meaning of \e priority, which is a per-queue >>> attribute is influenced by the ODP scheduler semantics, and the system >>> behavior under stress depends on the ODP buffer pool module behavior. >>> + >>> +For the sole purpose of illustrating the above abstract functionality, >>> here is an example of a Layer-2 (IEEE 802.1D) bridge application: >>> +Such a forwarding application that also adheres to IEEE 802.1p/q >>> priority, which has 8 traffic priority levels, might create 8 \e >>> odp_buffer_pool instances, one for each PCP priority level, and 8 \e >>> odp_queue instances one per priority level. >>> +Incoming packets will be inspected for a VLAN header; the PCP field >>> will be extracted, and used to select both the pool and the queue. >>> +Because each queue will be assigned a priority value, the packets with >>> highest PCP values will be scheduled before any packet with a lower PCP >>> value. >>> +Also, in a case of congestion, buffer pools for lower priority packets >>> will be depleted earlier than the pools containing packets of the high >>> priority, and hence the lower priority packets will be dropped (assuming >>> that is the only flow control method that is supported in the platform) >>> while higher priority packets will continue to be received into buffers and >>> processed. >>> +@subsection flow_diagram Classification Processing Flow Diagram >>> +@image html classification_flow.png "Figure 1: Classification Flow >>> Diagram" >>> +@image latex classification_flow.eps "Figure 1: Classification Flow >>> Diagram" >>> + >>> +@section api_elements API Elements >>> +While the above description refers to the abstracted packet classifier, >>> the following is the description of the API designed to program the packet >>> classifier, and is intended to add clarity to the functions provided >>> further below. >>> +@subsection cos_creation Class of Service Creation and Binding >>> +To program the classifier, a class-of-service instance must be created, >>> which will contain the packet filtering resources that it may require. >>> +All subsequent calls refer to one or more of these resources. >>> +Each class of service instance must be associated with a single queue >>> or queue group, which will be the destination of all packets matching that >>> particular filter. >>> +The queue assignment is implemented as a separate function call such >>> that the queue may be modified at any time, without tearing down the >>> filters that define the class of service. In other words, it is possible to >>> change the destination queue for a class of service defined by its filters >>> quickly and dynamically. >>> +Optionally, on platforms that support multiple packet buffer pools, >>> each class of service may be assigned a different pool such that when >>> buffers are exhausted for one class of service, other classes are not >>> negatively impacted and continue to be processed. >>> + >>> +@subsection default_packet_handling Default packet handling >>> +There SHOULD be one \b odp_cos assigned to each port with the \c >>> odp_cos_pktio_set() function, which will function as the default >>> class-of-service for all packets received from an ingress port, that do not >>> match any of the filters defined subsequently. At minimum this default >>> class-of-service MUST have a queue and a buffer pool assigned to it on >>> platforms that support multiple packet buffer pools. Multiple odp_pktio >>> instances (i.e., multiple ports) MAY each have their own default odp_cos, >>> or MAY share a odp_cos with other ports, based on application requirements. >>> + >>> +@subsection packet_classification Packet Classification >>> +For each odp_pktio port, the API allows the assignment of a >>> class-of-service to a packet using one of three methods: >>> + >>> +-# The packet may be assigned a specific class-of-service based on its >>> Layer-2 (802.1P/902.1Q VLAN tag) priority field. Since the standard field >>> defines 8 discrete priority levels, the API allows to assign an odp_cos to >>> each of these priority levels with the \c odp_cos_with_l2_priority() >>> function. >>> + >>> +-# Similarly, a class-of-service may be assigned using the Layer-3 (IP >>> DiffServ) header field. The application supplies an array of \e odp_cos >>> values that covers the entire range of the standard protocol header field, >>> where array elements do not need to contain unique values. There is also a >>> need to specify if Layer-3 priority takes precedence over Layer-2 priority >>> in a packet with both headers present. >>> + >>> +-# Additionally, the application may also program a number of \e >>> pattern \e matching \e rules that assign a class-of-service for packets >>> with header fields matching specified values. The field-matching rules take >>> precedence over the previously described priority-based assignment of a >>> class-of-service. Using these matching rules the application should be able >>> for example to identify all packets containing VoIP traffic based on the >>> protocol being UDP, and a specific destination or source port numbers, and >>> appropriately assign these packets an class-of-service that maps to a >>> higher priority queue, assuring voice packets a lower and bound latency. >>> + >>> +@subsection scaling_and_flow Scaling and Flow Discrimination >>> +In addition to classifying packets and routing them to those queues >>> with the appropriate priority, and optionally limiting their memory >>> consumption by designating certain classes of packets to specific buffer >>> pools, the classifier API also facilitates the scaling of data-plane >>> application on multi-core systems by creating a mechanism to define which >>> packet headers need to be combined to result in a value representing a >>> specific packet flow. The classifier generates a signature, which can be a >>> checksum or hash of arbitrary strength that covers those packet header >>> fields that are identified by the application as identifying flows. >>> + >>> +The \e flow \e signatures that result from hashing are then stored with >>> the packet meta data (along with its class-of-service and its ingress \e >>> odp_pktio port), and subsequently may be utilized by the implementation of >>> a scheduler queue to maintain the order of packets with the same flow >>> signature, while allowing packets with different signatures to be processed >>> concurrently and independently on different processing cores. >>> + >>> +@subsection packet_meta_data Packet meta data Elements >>> +Here are the specific information elements that SHOULD be stored within >>> the packet meta data structure: >>> +- Protocol fields that are decoded and extracted by the parsing phase >>> +- Flow-signature calculated from a prescribed collection of protocol >>> fields >>> +- The class-of-service identifier that is selected for the packet >>> +- The ingress port identifier >>> +- The result of packet validation, including an indication of the type >>> of error detected, if any >>> + >>> +The ODP packet API module SHALL provide accessors for retrieving the >>> above meta data fields from the container buffer in an >>> implementation-independent manner. >>> + >>> +@section api_definitions API Definitions >>> +@subsection data_types Data Types >>> +The following data types are referenced in the API descriptions >>> described below. >>> +The names are part of the ODP API and MUST be present in any conforming >>> implementation, however the type values shown here are illustrative and >>> implementations SHOULD either use these or substitute their own type values >>> that are appropriate to the underlying platform. >>> + >>> +@verbatim >>> +/** >>> + * 'odp_pktio_t' value to indicate any port >>> + */ >>> +#define ODP_PKTIO_ANY ((odp_pktio_t)~0) >>> + >>> + >>> +/** >>> + * 'odp_pktio_t' value to indicate an error >>> + */ >>> +#define ODP_PKTIO_INVALID ((odp_pktio_t)0) >>> + >>> + >>> +/** >>> + * Class of service instance type >>> + */ >>> +typedef uint32_t odp_cos_t; >>> + >>> + >>> +/** >>> + * flow signature type, only used for packet meta data field. >>> + */ >>> +typedef uint32_t odp_flowsig_t; >>> + >>> + >>> +/** >>> + * This value is returned from odp_cos_create() on failure, >>> + * May also be used as a “sink” class of service that >>> + * results in packets being discarded. >>> + */ >>> +#define ODP_COS_INVALID ((odp_cos_t)~0) >>> +@endverbatim >>> + >>> +@subsection cos_routines Class of Service Routines >>> +Conforming ODP implementations MUST provide the following >>> Classification APIs: >>> +@subsubsection cos_create odp_cos_create >>> +@verbatim >>> +/** >>> + * Create a class-of-service >>> + * >>> + * @param name is a string intended for debugging purposes. >>> + * >>> + * @return Class of service instance identifier, >>> + * or ODP_COS_INVALID on error. >>> + */ >>> + >>> +odp_cos_t odp_cos_create(const char *name); >>> +@endverbatim >>> + >>> +This routine is used to create a class of service that can be the >>> target of classifier rules. >>> +The number of such classes supported is implementation-defined. >>> +Attempts to create more than are supported by the implementation will >>> result in an \c ODP_COS_INVALID return and errno being set to \c >>> ODP_IMPLEMENTATION_LIMIT. >>> + >>> +@subsubsection cos_destroy odp_cos_destroy >>> +@verbatim >>> +/** >>> + * Discard a class-of-service along with all its associated resources >>> + * >>> + * @param cos_id class-of-service instance. >>> + * >>> + * @return 0 on success, -1 on error. >>> + */ >>> + >>> +int odp_cos_destroy(odp_cos_t cos_id); >>> +@endverbatim >>> + >>> +This routine is the bracketing routine for odp_cos_create(). >>> +It is used to destroy an existing CoS. >>> +It is the caller’s responsibility to ensure that no active pattern >>> matching rules refer to the CoS prior to calling this routine. >>> +Results are unpredictable if this restriction is not met. >>> +@subsubsection cos_set_queue odp_cos_set_queue >>> +@verbatim >>> +/** >>> + * Assign a queue for a class-of-service >>> + * >>> + * @param cos_id class-of-service instance. >>> + * >>> + * @param queue_id is the identifier of a queue where all >>> packets >>> + * of this specific class of service will be enqueued. >>> + * >>> + * @return 0 on success, negative error code on failure. >>> + */ >>> + >>> +int odp_cos_set_queue(odp_cos_t cos_id, odp_queue_t queue_id); >>> +@endverbatim >>> + >>> +This routine associates a target queue with a CoS such that all packets >>> assigned to this CoS will be enqueued to the specified queue_id at the end >>> of classification processing. >>> +@subsubsection cos_set_queue_group odp_cos_set_queue_group >>> +@verbatim >>> +/** >>> + * Assign a homogenous queue-group to a class-of-service. >>> + * >>> + * @param cos_id identifier of class-of-service instance >>> + * @param queue_group_id identifier of the queue group to receive >>> packets >>> + * associated with this class of service. >>> + * >>> + * @return 0 on success, negative error code on failure. >>> + */ >>> + >>> +int odp_cos_set_queue_group(odp_cos_t cos_id, odp_queue_group_t >>> queue_group_id); >>> +@endverbatim >>> + >>> +This routine associates a target queue group with a CoS such that all >>> packets assigned to this CoS will be distributed to the specified >>> queue_group_id at the end of classification processing. >>> +@subsubsection cos_set_pool odp_cos_set_pool >>> +@verbatim >>> +/** >>> + * Assign packet buffer pool for specific class-of-service >>> + * >>> + * @param cos_id class-of-service instance. >>> + * @param pool_id is a buffer pool identifier where all packet buffers >>> + * will be sourced to store packet that belong to this >>> + * class of service. >>> + * >>> + * @return 0 on success negative error code on failure. >>> + * >>> + * >>> + */ >>> + >>> +int odp_cos_set_pool(odp_cos_t cos_id, odp_buffer_pool_t pool_id); >>> +@endverbatim >>> + >>> +This OPTIONAL routine associates a target buffer pool with a CoS such >>> that all packets assigned to this CoS will be stored in packet buffers >>> allocated from the designated pool_id. >>> + >>> + >>> +@subsection cos_drop_policy Class of Service Drop Policy Routines >>> +These routines control how drop policies are to be observed for a given >>> class of service. >>> +@subsubsection drop_data_types Data types >>> +~~~~~{.c} >>> +enum odp_cos_drop_e { >>> + ODP_COS_DROP_POOL, /**< Follow buffer pool drop policy */ >>> + ODP_COS_DROP_NEVER, /**< Never drop, ignoring buffer pool >>> policy */ >>> +}; >>> +typedef enum odp_drop_e odp_drop_t; >>> +~~~~~ >>> + >>> +@subsubsection cos_set_drop odp_cos_set_drop >>> +@verbatim >>> +/** >>> + * Assign packet drop policy for specific class-of-service >>> + * >>> + * @param cos_id class-of-service instance. >>> + * @param drop_policy is the desired packet drop policy for this >>> class. >>> + * >>> + * @return 0 on success negative error code on failure. >>> + */ >>> + >>> +int odp_cos_set_drop(odp_cos_t cos_id, odp_drop_t drop_policy); >>> +@endverbatim >>> + >>> +This routine sets the drop policy for a class of service. >>> +It is an OPTIONAL routine. >>> +If an implementation does not provide this function it MUST supply a >>> definition of it that simply returns ODP_FUNCTION_NOT_AVAILABLE. >>> +@subsubsection pktio_set_default_cos odp_pktio_set_default_cos >>> +@verbatim >>> +/** >>> + * Setup per-port default class-of-service >>> + * >>> + * @param pktio_in ingress port identifier. >>> + * @param default_cos class-of-service set to all packets arriving >>> + * at the 'pktio_in' ingress port, unless overridden by >>> subsequent >>> + * header-based filters. >>> + * >>> + * @return 0 on success negative error code on failure. >>> + * >>> + * >>> + * @note This may replace the default queue per pktio. >>> + */ >>> + >>> +int odp_pktio_set_default_cos(odp_pktio_t pktio_in, odp_cos_t >>> default_cos); >>> +@endverbatim >>> + >>> +This routine specifies a default class of service for a given pktio >>> instance. >>> +Incoming packets on the specified pktio are assigned to this class of >>> service if no other pattern matching rule obtains. >>> +@subsubsection pktio_set_error_cos odp_pktio_set_error_cos >>> +@verbatim >>> +/** >>> + * Setup per-port error class-of-service >>> + * >>> + * @param pktio_in ingress port identifier. >>> + * @param error_cos class-of-service set to all packets arriving >>> + * at the 'pktio_in' ingress port that contain an error. >>> + * >>> + * @return 0 on success negative error code on failure. >>> + */ >>> + >>> +int odp_pktio_set_error_cos(odp_pktio_t pktio_in, odp_cos_t error_cos); >>> +@endverbatim >>> + >>> +This OPTIONAL function assigns a class-of-service used to handle >>> packets containing various types of errors. The specific errors types >>> include L2 FCS and optionally L3/L4 checksum errors, malformed headers, >>> etc., depending on platform capabilities. >>> +The specified error_cos MAY simply discard these packets or deliver >>> them via a queue to the application for further processing. >>> +@subsubsection pktio_set_skip odp_pktio_set_skip >>> +@verbatim >>> +/** >>> + * Setup per-port header offset >>> + * >>> + * @param pktio_in ingress port identifier. >>> + * @param offset is the number of bytes the classifier must skip. >>> + * >>> + * @return Success or ODP_FUNCTION_NOT_AVAILABLE >>> + */ >>> + >>> +int odp_pktio_set_skip(odp_pktio_t pktio_in, size_t offset); >>> +@endverbatim >>> + >>> +This OPTIONAL function applies to ports that carry an additional >>> headers preceding the standard Ethernet header. Such headers are typically >>> vendor-specific and thus the classifier is not required to parse such >>> headers, but the size of a custom header is critical for the classifier to >>> be able to parse standard protocol headers that normally follow. >>> +@subsubsection cos_set_headroom odp_cos_set_headroom >>> +@verbatim >>> +/** >>> + * Specify per-port buffer headroom >>> + * >>> + * @param pktio_in ingress port identifier. >>> + * @param headroom number of bytes of space preceding packet data to >>> reserve >>> + * for use as headroom. Must not exceed the >>> implementation >>> + * defined ODP_PACKET_MAX_HEADROOM. >>> + * >>> + * @return Success or ODP_PARAMETER_ERROR, >>> + * or ODP_FUNCTION_NOT_AVAILABLE >>> + */ >>> + >>> +int odp_cos_set_headroom(odp_cos_t cos_id, size_t req_room); >>> +@endverbatim >>> + >>> +This OPTIONAL routine specifies the number of bytes of headroom that >>> should be reserved for each packet assigned to this class of service. >>> +Each implementation defines an ODP_PACKET_MAX_HEADROOM limit that sets >>> an upper bound on the size of the headroom that can be reserved for a >>> packet. >>> +@subsubsection cos_with_l2_priority odp_cos_with_l2_priority >>> +@verbatim >>> +/** >>> + * Request to override per-port class of service >>> + * based on Layer-2 priority field if present. >>> + * >>> + * @param pktio_in ingress port identifier. >>> + * @param num_qos is the number of QoS levels, typically 8. >>> + * @param qos_table are the values of the Layer-2 QoS header field. >>> + * @param cos_table is the class-of-service assigned to each of the >>> + * allowed Layer-2 QOS levels. >>> + * @return 0 on success negative error code on failure. >>> + */ >>> + >>> +int odp_cos_with_l2_priority(odp_pktio_t pktio_in, >>> + size_t num_qos, >>> + uint8_t qos_table[], /**< >>> 'num_qos' elements */ >>> + odp_cos_t cos_table[]); /**< >>> 'num_qos' elements */ >>> +@endverbatim >>> + >>> +This routine is used to assign classes of service based on the layer 2 >>> (L2) priority associated with input packets received on the specified >>> pktio_in. >>> +For each of the values in qos_table[], the corresponding value in >>> cos_table[] will be assigned. >>> +@subsubsection cos_with_l3_dscp odp_cos_with_l3_dscp >>> +@verbatim >>> +/** >>> + * >>> + * @param pktio_in ingress port identifier. >>> + * @param num_qos is the number of allowed Layer-3 QoS levels. >>> + * @param qos_table are the values of the Layer-3 QoS header field. >>> + * @param cos_table is the class-of-service assigned to each of the >>> + * allowed Layer-3 QOS levels. >>> + * @param l3_preference when true, Layer-3 QoS overrides L2 QoS when >>> present. >>> + * >>> + * @return 0 on success negative error code on failure. >>> + */ >>> + >>> +int odp_cos_with_l3_qos(odp_pktio_t pktio_in, >>> + size_t num_qos, >>> + uint8_t qos_table[], /**< 'num_qos' >>> elements */ >>> + odp_cos_t cos_table[], /**< 'num_qos' >>> elements */ >>> + odp_bool_t l3_preference); >>> +@endverbatim >>> + >>> +This OPTIONAL routine is used to assign classes of service based on the >>> layer 3 (L3) Differentiated Services (DS) designation. >>> +This is the DSCP field of an IPv4 header or the first six bits of the >>> Traffic Class of an IPv6 header. >>> +For each of the values in qos_table[], the corresponding value in >>> cos_table[] will be assigned. >>> +The l3_preference flag is use to control whether the CoS assigned by >>> this routine takes precedence over the CoS assigned by >>> odp_cos_with_l2_priority() in the event that both apply to the same packet. >>> + >>> +@subsection pmrs Pattern Matching Rules >>> +While the above routines permit class of service assignments to be made >>> based on static criteria, the real power of classification is the ability >>> to identify flows based on the variable contents of packet headers. >>> +To do this ODP provides support for defining pattern matching rules >>> (PMRs) that operate based on values contained in specified header fields. >>> + >>> +Associated with PMRs are enums that are used to specify standard packet >>> header fields: >>> +@subsubsection cos_hdr_flow_fields odp_cos_hdr_flow_fields_e >>> +@verbatim >>> +/** >>> + * Packet header field enumeration >>> + * for fields that may be used to calculate >>> + * the flow signature, if present in a packet. >>> + */ >>> + >>> +enum odp_cos_hdr_flow_fields_e { >>> + ODP_COS_FHDR_IN_PKTIO, /**< Ingress port number */ >>> + ODP_COS_FHDR_L2_SAP, /**< Ethernet Source MAC address >>> */ >>> + ODP_COS_FHDR_L2_DAP, /**< Ethernet Destination MAC >>> address */ >>> + ODP_COS_FHDR_L2_VID, /**< Ethernet VLAN ID */ >>> + ODP_COS_FHDR_L3_FLOW /**< IPv6 flow_id */ >>> + ODP_COS_FHDR_L3_SAP, /**< IP source address */ >>> + ODP_COS_FHDR_L3_DAP, /**< IP destination address */ >>> + ODP_COS_FHDR_L4_PROTO, /**< IP protocol (e.g. >>> TCP/UDP/ICMP) */ >>> + ODP_COS_FHDR_L4_SAP, /**< Transport source port */ >>> + ODP_COS_FHDR_L4_DAP, /**< Transport destination port */ >>> + ODP_COS_FHDR_IPSEC_SPI, /**< IPsec session identifier */ >>> + ODP_COS_FHDR_LD_VNI, /**< NVGRE/VXLAN network >>> identifier */ >>> + ODP_COS_FHDR_USER /**< Application-specific header >>> field(s) */ >>> +}; >>> +@endverbatim >>> + >>> +Conforming ODP implementations SHOULD implement efficient flow set >>> management routines such as these: >>> + >>> +~~~~~{.c} >>> +/** >>> + * Set of header fields that take part in flow signature hash >>> calculation: >>> + * bit positions per 'odp_cos_hdr_flow_fields_e' enumeration. >>> + * >>> +typedef uint16_t odp_cos_flow_set_t; >>> + >>> + >>> +/** >>> + * Set a member of the flow signature fields data set >>> + * >>> +static inline odp_cos_flow_set_t >>> +odp_cos_flow_set( odp_cos_flow_set_t set, >>> + enum odp_cos_hdr_flow_fields_e field) >>> +{ >>> + return set | (1U << field); >>> +} >>> + >>> + >>> +/** >>> + * Test a member of the flow signature fields data set >>> + * >>> +static inline bool >>> +odp_cos_flow_is_set( odp_cos_flow_set_t set, >>> + enum odp_cos_hdr_flow_fields_e field) >>> +{ >>> + return (set & (1U << field)) != 0; >>> +} >>> +~~~~~ >>> + >>> +These routines are intended to be used in support of the following flow >>> signature APIs: >>> + >>> +@subsubsection cos_class_flow_sig odp_cos_class_flow_signature >>> +@verbatim >>> +/** >>> + * Set up set of headers used to calculate a flow signature >>> + * based on class-of-service. >>> + * >>> + * @param cos_id class of service instance identifier >>> + * @param req_data_set requested data-set for flow signature calculation >>> + * >>> + * @return data-set that was successfully applied. All-zeros data set >>> + * indicates a failure to assign any of the requested fields, or other >>> + * error. >>> + */ >>> + >>> +odp_cos_flow_set_t >>> +odp_cos_class_flow_signature(odp_cos_t cos_id, >>> + odp_cos_flow_set_t req_data_set); >>> +@endverbatim >>> + >>> +This OPTIONAL routine associates a fow set with a class of service for >>> flow signature calculation. >>> + >>> +@subsubsection cos_port_flow_sig odp_cos_port_flow_signature >>> +@verbatim >>> +/** >>> + * Set up set of headers used to calculate a flow signature >>> + * based on ingress port. >>> + * >>> + * @param pktio_in ingress port identifier. >>> + * @param req_data_set requested data-set for flow signature calculation >>> + * >>> + * @return data-set that was successfully applied. An all-zeros data-set >>> + * indicates a failure to assign any of the requested fields, or other >>> + * error. >>> + */ >>> + >>> +odp_cos_flow_set_t >>> +odp_cos_port_flow_signature(odp_pktio_t pktio_in, >>> + odp_cos_flow_set_t req_data_set); >>> +@endverbatim >>> + >>> +@subsection pmr_routines Pattern Matching Rules Routines >>> +The following data structures SHOULD be implemented to support the >>> definition of pattern matching routines by conforming ODP implementations: >>> + >>> +~~~~~{.c} >>> +/** >>> + * PMR - Packet Matching Rule >>> + * Up to 32 bit of ternary matching of one of the available header >>> fields >>> + * >>> + >>> + >>> +#define ODP_PMR_INVAL ((odp_pmr_t)NULL) >>> +typedef struct odp_pmr_s *odp_pmr_t; >>> +~~~~~ >>> + >>> +@subsecion terms Terms >>> +Terms are the elements of a PMR and are identified by the following >>> enum: >>> + >>> +@verbatim >>> +enum odp_pmr_term_e { >>> + ODP_PMR_ETHTYPE_0, /**< Initial (outer) Ethertype only >>> (*val=uint16_t)*/ >>> + ODP_PMR_ETHTYPE_X, /**< Ethertype of most inner VLAN tag >>> (*val=uint16_t)*/ >>> + ODP_PMR_VLAN_ID_0, /**< First VLAN ID (outer) >>> (*val=uint16_t) */ >>> + ODP_PMR_VLAN_ID_X, /**< Last VLAN ID (inner) (*val=uint16_t) >>> */ >>> + ODP_PMR_DMAC, /**< destination MAC address >>> (*val=uint64_t) */ >>> + ODP_PMR_IPPROTO, /**< IP Protocol or IPv6 Next Header >>> (*val=uint8_t) */ >>> + ODP_PMR_UDP_DPORT, /**< Destination UDP port, implies >>> IPPROTO=17 */ >>> + ODP_PMR_TCP_DPORT, /**< Destination TCP port implies >>> IPPROTO=6 */ >>> + ODP_PMR_UDP_SPORT, /**< Source UDP Port (*val=uint16_t) */ >>> + ODP_PMR_TCP_SPORT, /**< Source TCP port (*val=uint16_t) */ >>> + ODP_PMR_SIP_ADDR, /**< Source IP address (uint32_t) */ >>> + ODP_PMR_DIP_ADDR, /**< Destination IP address (uint32_t) */ >>> + ODP_PMR_SIP6_ADDR, /**< Source IP address (uint8_t[16]) */ >>> + ODP_PMR_DIP6_ADDR, /**< Destination IP address (uint8_t[16]) >>> */ >>> + ODP_PMR_IPSEC_SPI, /**< IPsec session >>> identifier(*val=uint32_t) */ >>> + ODP_PMR_LD_VNI, /**< NVGRE/VXLAN network identifier >>> (*val=uint32_t) */ >>> + >>> + >>> + /** Inner header may repeat above values with this offset */ >>> + ODP_PMR_INNER_HDR_OFF=32 >>> +}; >>> +@endverbatim >>> + >>> +@subsubsection tunnel_considerations Tunnel Considerations >>> +Note that PMRs may be extended to support tunnels and tenants (NVGRE, >>> VXLAN) via the ODP_PMR_INNER_HDR_OFF enum. >>> +This enum is intended to be used as an “adder” to a PMR to indicate >>> that the term refers to an inner header. >>> +For example, the term ODP_PMR_DMAC would refer to the destination MAC >>> address of the packet if the packet is not a tunnel, or of the outer header >>> (the tunnel) if the packet is a tunnel. >>> +To refer to the inner (tenant) destination MAC, the term would be >>> specified as ODP_PMR_INNER_HDR_OFF+ODP_PMR_DMAC. >>> + >>> +@subsection pmr_apis PMR APIs >>> +The following APIs are provided to enable an ODP application to specify >>> PMRs as a series of individual or cascaded terms: >>> +@subsubsection pmr_create_match odp_pmr_create_match >>> +@verbatim >>> +/** >>> + * Create a packet match rule with mask and value >>> + * >>> + * @param term is one value of the enumerated values supported >>> + * @param val is the value to match against the packet header >>> + * in native byte order. >>> + * @param mask is the mask to indicate which bits of the header >>> + * should be matched ('1') and which should be ignored >>> ('0') >>> + * @param val_sz size of the ‘val’ and ‘mask’ arguments, >>> + * that must match the value size requirement of the >>> + * specific ‘term’. >>> + * >>> + * @return a handle of the matching rule or ODP_PMR_INVAL on error >>> + */ >>> + >>> +odp_pmr_t odp_pmr_create_match(enum odp_pmr_term_e term, >>> + const void *val, const void *mask, >>> size_t val_sz); >>> +@endverbatim >>> + >>> +This routine creates a PMR that matches a single value to a term. >>> + >>> +@subsubsection pmr_create_range odp_pmr_create_range >>> +@verbatim >>> +/** >>> + * Create a packet match rule with value range >>> + * >>> + * @param term is one value of the enumerated values supported >>> + * @param val1 is the lower bound of the header field range. >>> + * @param val2 is the upper bound of the header field range. >>> + * @param val_sz size of the ‘val1’ and ‘val2’ arguments, >>> + * that must match the value size requirement of the >>> + * specific ‘term’. >>> + * >>> + * @return a handle of the matching rule or ODP_PMR_INVAL on error >>> + * @note: Range is inclusive [val1..val2]. >>> + */ >>> + >>> +odp_pmr_t odp_pmr_create_range(enum odp_pmr_term_e term, >>> + const void *val1, const void >>> *val2, size_t val_sz); >>> +@endverbatim >>> + >>> +This routine creates a PMR that matches an inclusive range of values to >>> a term. >>> + >>> +@subsubsection pmr_destroy odp_pmr_destroy >>> +@verbatim >>> +/** >>> + * Invalidate a packet match rule and vacate its resources >>> + * >>> + * @param pmr_id the identifier of the PMR to be destroyed >>> + * >>> + * @return Success or ODP_PMR_INVALID if the specified pmr_id not found. >>> + */ >>> + >>> +int odp_pmr_destroy(odp_omr_t pmr_id); >>> +@endverbatim >>> + >>> +This routine destroys a previously created PMR. >>> +If the PMR is currently associated with an active class of service it >>> is unpredictable at which point the match defined by the PMR is deactivated >>> in terms of packet flow. >>> +However, implementations MUST ensure that a PMR is either matched or >>> not matched in its entirety such that dynamic changes to PMRs do not result >>> in partial matches. >>> + >>> +@subsubsection pktio_pmr_cos odp_pktio_pmr_cos >>> +@verbatim >>> +/** >>> + * Apply a PMR to a pktio to assign a CoS. >>> + * >>> + * @param pmr_id the id of the PMR to be activated >>> + * @param src_pktio the pktio to which this PMR is to be applied >>> + * @param dst_cos the CoS to be assigned by this PMR >>> + * >>> + * @return Success or ODP_PARAMETER_ERROR >>> + */ >>> + >>> +int odp_pktio_pmr_cos(odp_pmr_t pmr_id, odp_pktio_t src_pktio, >>> odp_cos_t dst_cos); >>> +@endverbatim >>> + >>> +This routine links a pktio to a corresponding class of service via a >>> specified PMR. >>> +Any packet received on the specified src_pktio that matches the >>> specified pmr_id will be assigned to the specified dst_cos. >>> +If multiple PMRs match the implementation MAY define an inherent >>> precedence or it MAY be unpredictable as to which PMR will determine the >>> assigned CoS. >>> +For this reason applications SHOULD NOT be written to use conflicting >>> or ambiguous PMR definitions. >>> + >>> +@subsubsection cos_pmr_cos odp_cos_pmr_cos >>> +@verbatim >>> +/** >>> + * Cascade a PMR to refine packets from one CoS to another. >>> + * >>> + * @param pmr_id the id of the PMR to be activated >>> + * @param src_cos the id of the CoS to be filtered >>> + * @param dst_cos the id of the CoS to be assigned to packets >>> filtered >>> + * from src_cos that match pmr_id. >>> + * >>> + * @return Success or ODP_PARAMETER_ERROR if an input is in error >>> + * or ODP_IMPLEMENTATION_LIMIT if cascade depth is >>> exceeded >>> + */ >>> + >>> +int odp_cos_pmr_cos(odp_pmr_t pmr_id, odp_cos_t src_cos, odp_cos_t >>> dst_cos); >>> +@endverbatim >>> + >>> +This routine is used to cascade PMRs by passing packets assigned to the >>> src_cos through another PMR. >>> +Those matching are reassigned to the specified dst_cos. >>> +Note that this process can be repeated to an implementation-defined >>> maximum supported cascade depth. >>> +When cascades are defined, the actual class of service assigned to a >>> packet is the result of the longest chain of PMRs that can be matched >>> against the packet. >>> + >>> +For example, suppose the following sequence of PMRs is in effect: >>> + >>> +@verbatim >>> +odp_pktio_pmr_cos(pmr_idA, pktio_id, cos_idA); >>> +odp_cos_pmr_cos(pmr_idB, cos_idA, cos_idB); >>> +odp_cos_pmr_cos(pmr_idC, cos_idB, cos_idC); >>> +odp_cos_pmr_cos(pmr_idD, cos_idC, cos_idD); >>> +@endverbatim >>> + >>> +If a packet arrives on pktio_id that matches pmr_idA it is assigned to >>> cos_idA. >>> +But since it is now on cos_idA it is further filtered by pmr_idB and if >>> it matches is reassigned to cos_idB. >>> +This process continues until no further more specific match is found to >>> determine the final CoS that the packet receives. >>> + >>> +Note that given this rule set a packet that matched pmr_idA and pmr_idC >>> it would be assigned to cos_idA because the rule that can assign packets to >>> pmr_idC is only applicable to packets that are assigned to cos_idB, not >>> cos_idA. >>> + >>> +Using cascaded PMRs it is possible to build quite sophisticated filters >>> (up to the implementation limits supported by a given platform). >>> +For example, one could add additional rules to the above set: >>> + >>> +@verbatim >>> +odp_cos_pmr_cos(pmr_idAC, cos_idA, cos_idC); >>> +odp_cos_pmr_cos(pmr_idAD, cos_idA, cos_idD); >>> +@endverbatim >>> + >>> +To cover cases where some packets on cos_idA should be further sorted >>> to cos_idB while others should be sorted directly to cos_idC or cos_idD. >>> +Again it is the application’s responsibility to ensure that the >>> cascades remain unambiguous and that loops be avoided (e.g., having rules >>> that bounce packets between cos_idA and cos_idB endlessly). >>> + >>> +@subsection pmr_stats PMR Statistics >>> +Conforming ODP implementations SHOULD maintain statistics regarding >>> PMRs and provide the following routines for retrieving them: >>> + >>> +@subsubsection pmr_match_count odp_pmr_match_count >>> +@verbatim >>> +/** >>> + * Retrieve packet matcher statistics >>> + * >>> + * @param pmr_id the id of the PMR from which to retrieve the count >>> + * >>> + * @return The current number of matches for a given matcher instance. >>> + */ >>> + >>> +signed long odp_pmr_match_count(odp_pmr_t pmr_id); >>> +@endverbatim >>> + >>> +@subsubsection pmr_terms_cap odp_pmr_terms_cap >>> +@verbatim >>> +/** >>> + * Inquire about matching terms supported by the classifier >>> + * >>> + * @return A mask one bit per enumerated term, one for each of >>> op_pmr_term_e >>> + */ >>> + >>> +unsigned long long odp_pmr_terms_cap(void); >>> +@endverbatim >>> + >>> +@subsubsection pmr_terms_avail odp_pmr_terms_avail >>> +@verbatim >>> +/** >>> + * Return the number of packet matching terms available for use >>> + * >>> + * @return A number of packet matcher resources available for use. >>> + */ >>> + >>> +unsigned odp_pmr_terms_avail(void); >>> +@endverbatim >>> + >>> +@subsection pmr_composite_rules Pattern Matching Composite Routines >>> +As a shorthand, applications MAY express pattern matching rules using a >>> table rather than constructing them term-by-term. >>> +ODP implementations MUST support both methods of rule specification but >>> MAY have implementation-specific restrictions on the complexity of >>> table-based rules they support. >>> +Note that some implementations MAY be able to implement tables directly >>> while others MAY choose to implement tables by internally generating the >>> equivalent set of term generating calls. >>> + >>> +@subsubsection pmr_table_structure PMR Table Structure >>> +@verbatim >>> +/** >>> + * Following structure is used to define composite packet matching rules >>> + * in the form of an array of individual match or range rules. >>> + * The underlying platform may not support all or any specific >>> combination >>> + * of value match or range rules, and the application should take care >>> + * of inspecting the return value when installing such rules, and >>> perform >>> + * appropriate fallback action. >>> + */ >>> + >>> +typedef struct odp_pmr_match_t { >>> + enum odp_pmr_match_type_e { >>> + ODP_PMR_MASK, /**< Match a masked set of >>> bits */ >>> + ODP_PMR_RANGE, /**< Match an integer range >>> */ >>> + } match_type; >>> + union { >>> + struct { >>> + enum odp_pmr_term_e term; >>> + const void *val; >>> + const void *mask; >>> + unsigned int val_sz; >>> + } mask; /**< Match a masked set of bits */ >>> + struct { >>> + enum odp_pmr_term_e term; >>> + const void *val1; >>> + const void *val2; >>> + unsigned int val_sz; >>> + } range; /**< Match an integer range */ >>> + }; >>> +} odp_pmr_match_t; >>> + >>> + >>> +/** An opaque handle to a composite packet match rule-set */ >>> +typedef struct odp_pmr_set_s *odp_pmr_set_t; >>> +@endverbatim; >>> + >>> +The above structure is used with the following APIs to implement >>> table-based PMRs: >>> + >>> +@subsubsection pmr_match_set_create odp_pmr_match_set_create >>> +@verbatim >>> +/** >>> + * Create a composite packet match rule >>> + * >>> + * @param num_terms is the number of terms in the match rule. >>> + * @param terms is an array of num_terms entries, one entry per >>> + * term desired. >>> + * @param dst_cos is the class-of-service to be assigned to packets >>> + * that match the compound rule-set, or a subset >>> thereof, >>> + * if partly applied. >>> + * @param pmr_set_id is the returned handle to the composite rule set. >>> + * >>> + * @return The return value may be a negative number indicating a >>> general >>> + * error, or a positive number indicating the number of ‘terms’ >>> elements that >>> + * have been successfully mapped to the underlying platform >>> classification engine, >>> + * and may be in the range from 1 to ‘num_terms’. >>> + */ >>> + >>> +int odp_pmr_match_set_create(int num_terms, odp_pmr_match_t *terms, >>> + odp_pmr_set_t *pmr_set_id); >>> +@endverbatim >>> + >>> +This routine is used to create a PMR match set. >>> + It is the equivalent to a cascade of PMRs except that there are no >>> “intermediate” classes of service defined. >>> +Instead, the entire match set either matches or does not match as a >>> single entity. >>> + >>> +@subsubsection pmr_match_set_destroy odp_pmr_match_set_destroy >>> +@verbatim >>> +/** >>> + * Function to delete a composite packet match rule set >>> + * >>> + * All of the resources pertaining to the match set associated with the >>> + * class-of-service will be released, but the class-of-service will >>> + * remain intact. >>> + * >>> + * @param pmr_set_id a composite rule-set handle returned when created. >>> + * >>> + * @note Depending on the implementation details, destroying a rule-set >>> + * may not guarantee the availability of hardware resources to create >>> the >>> + * same or essentially similar rule-set. >>> + */ >>> + >>> +int odp_pmr_match_set_destroy(odp_pmr_set_t pmr_set_id); >>> +@endverbatim >>> + >>> +This routine destroys a PMR match set previously created by >>> odp_pmr_match_set_create(). >>> + >>> +@subsubsection pktio_pmr_match_set_cos odp_pktio_pmr_match_set_cos >>> +@verbatim >>> +/** >>> + * Apply a PMR Match Set to a pktio to assign a CoS. >>> + * >>> + * @param pmr_set_id the id of the PMR match set to be activated >>> + * @param src_pktio the pktio to which this PMR match set is to be >>> applied >>> + * @param dst_cos the CoS to be assigned by this PMR match set >>> + * >>> + * @return Success or ODP_PARAMETER_ERROR >>> + */ >>> + >>> +int odp_pktio_pmr_match_set_cos(odp_pmr_t pmr_id, odp_pktio_t >>> src_pktio, >>> + odp_cos_t dst_cos); >>> +@endverbatim >>> + >>> +This routine is the same as odp_pktio_pmr_cos() except that it operates >>> on PMR match sets rather than individual PMRs. >>> + >>> +@section items_pending Items pending resolution >>> +- Revise ‘odp_packet_io.h’ API with respect of default input queue per >>> ‘pktio’ instance. >>> +- Revise ‘odp_queue.h’ API to support an arbitrary priority range, >>> typically 8 priority levels with numeric priority values are >>> platform-specific. >>> +- Add specific packet meta data fields to go into packet buffer which >>> contain all meta data fields parsed and generated by the classifier, for >>> later application use. >>> + >>> +@section implementation_notes Implementation Notes >>> +The following sections are not part of the specification, but shed >>> light into the intent of the specification in several areas, describing >>> some specific implementation approaches of these aspects. >>> + >>> +@subsection supporting_multi_pools Supporting multiple buffer pools >>> +The support of multiple buffer pools for containing packet buffers is >>> optional, and may not be supported by some platforms. >>> +The importance of this feature stems from the need of protecting a >>> networking application in the event of a congestion, or an attempted denial >>> of service attack. >>> +Separating different classes of service to dedicated buffer pools >>> allows the system to limit the memory resources that may be consumed by a >>> particular type of traffic, thereby reserving buffer resources for other >>> classes of traffic. >>> + >>> +In a software implementation, a packet would already be stored in >>> memory when the classifier is invoked, and so it seems the classifier is >>> unable to insert itself into the process of selecting a buffer pool. >>> +For obvious reasons the copying of a packet into a new buffer allocated >>> from a different pool by the classifier is not a desirable solution. >>> + >>> +The recommended solution is to implement buffer pools in the form of >>> buffer counters, while the actual buffers all belong to a single free list >>> when not used to store a packet. >>> +In such an implementation, the classifier will be able to associate a >>> packet already occupying a buffer to a different pool than the default by >>> incrementing the buffer counter of the newly selected pool, and >>> decrementing the counter representing the default pool. >>> +If however the selected pool counter has already reached a certain >>> limit, the classifier would be able to e.g discard the packet instead of >>> incrementing the destination pool counter, and thereby enforce the >>> desirable semantics of distinct buffer pools per class of service. >>> + >>> +Other possible action that may be taken in response to running out of >>> buffers or coming too low on buffers include back-pressure and >>> random-early-detect with a discard probability inversely proportional to >>> the number of free buffers in a pool. >>> +A related implementation topic is the ability to begin dropping some >>> packets before a buffer pool is entirely exhausted. >>> +This is typically referred to as <em>Random Early Detect</em> (or >>> “RED”). >>> +This is deemed to be a feature of the buffer pool implementation on a >>> given platform, where in addition to a hard limit on the number of buffers >>> that can be allocated to a pool, there can also be an option discard >>> packets with a probability the increases as the number of outstanding >>> buffers approaches that hard limit. >>> + >>> +@subsection resolving_gaps Resolving gaps between the API and hardware >>> capabilities >>> +On platforms that support hardware packet accelerators, it is possible >>> that the packet parsing and classification functionality is sufficient to >>> address only a portion of the functionality specified within this document. >>> +This gap may be potentially bridged by augmenting the hardware >>> classification capabilities with a software logic implemented as part of >>> the platform. >>> +In that case, the platform will have to curve out a fraction of the >>> processing resources and dedicate those to the software classification >>> logic, which would be invoked for packets that the hardware platform was >>> unable to classify completely. >>> +At the time of this writing, it is believed however that the >>> performance penalty that will be incurred as a result of software >>> augmentation is unjustified for most application, i.e. >>> +it is preferred to lose the precision of packet prioritization while >>> maintaining full hardware packet processing speed. >>> + >>> +@subsection loopback_case The case for loopback ports, and some of >>> their uses >>> +In some applications, it may be desirable to be able to run a single >>> packet through the classifier more than once. >>> +For example, an encrypted IPsec packet is received from a physical port. >>> +The encrypted packet is assigned a class of service based on its outer >>> unencrypted header fields. >>> +Later, processing the packet entails decrypting the payload of the >>> packet, authenticating it, and removing the original outer headers, which >>> reveals a new set of protocol headers which need to be used to re-classify >>> the packet, and assign it a new priority and buffer pool. >>> +An elegant solution for this use case would be to take advantage of >>> “loopback” logical ports that may be implemented in certain platforms, by >>> transmitting decapsulated packet into a loop-back port. >>> +The same packet then is received from a loop-back port and is examined >>> by the classifier in accordance to the rules assigned to the loopback >>> odp_pktio logical port instance. >>> +Similar mechanism may be applied to tunnel termination processing, >>> fragment reassembly et al. >>> + >>> +@section related_topics Related Topics >>> +The following section discusses aspects of the ODP API that are not >>> integral to the classifier, which only applies to ingress preprocessing. >>> +This section covers miscellaneous aspects of the API that need to be >>> addressed, and are related to packet buffer processing and egress >>> post-processing. >>> +Additional packet buffer manipulation APIs >>> +The need for these following calls are made evident by the need to >>> encapsulate, i.e., remove some headers and add other, thereby changing the >>> size of the headers of a packet during processing. >>> + >>> +@subsection initial_headroom Configuring initial packet buffer headroom >>> +The following function is provided to configure the pktio receive >>> mechanism to (optionally)reserve some headroom between start of the first >>> buffer to the first byte of the first packet data byte, which subsequently >>> could be used to increase the header size “in-place”, without allocating >>> additional gather list elements. >>> +If the request is granted, at least <req_bytes> bytes will be reserved >>> in the front of the packet data: >>> +@verbatim >>> +int odp_pktio_set_headroom(odp_pktio_t port_id, unsigned req_bytes); >>> +@endverbatim >>> +The return value should be negative if the request can not be >>> satisfied, or positive otherwise indicating the actual minimum headroom >>> reserved. >>> +Note that the implementation may reserve more than the requested amount >>> of headroom, and hence on platforms that are unable to support per-port (or >>> per CoS) headroom configuration, a system-wide headroom configuration may >>> be set to the largest of all such requests, and thus satisfy the >>> requirement. >>> +In addition to the above per-port headroom configuration call, there >>> should be an optional, per-CoS call that allows the reservation of >>> different amounts of packet buffer headroom for packets that match certain >>> criteria: for example, the following call allows the application to request >>> that only packets that are expected to be encapsulated in a tunnel, be >>> augmented with a large headroom amount, while packets that are received >>> from a tunnel, and are IP fragments, be assigned a different headroom >>> requirement (see definition for odp_cos_set_headroom() above. >>> +Egress packet scheduling, prioritization and ordering >>> + >>> + >>> +Open Issues >>> +* Parallel matching rules relative precedence. >>> +* Specify application-defined header field declaration APIs. >>> +* Review RFC 4301 for match requirements for IPsec SA, consider the use >>> of L4 port ranges instead of or in addition to value & mask matching >>> criteria. >>> +* Consider the type of packet checks should route a packet through the >>> error CoS: L2 is a safe choice, but L3/L4 checksum or other exceptions >>> deserve consideration. >>> +Usage Examples >>> +Following is a simple sample configuration using the API elements >>> described above. >>> +TBD. >>> + >>> +*/ >>> diff --git a/images/classification_flow.png >>> b/images/classification_flow.png >>> new file mode 100644 >>> index 0000000000000000000000000000000000000000.. >>> 2d94ff64ec772aa97f3687181c121fb91d671889 >>> GIT binary patch >>> literal 35193 >>> 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 >>> >> > > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > http://lists.linaro.org/mailman/listinfo/lng-odp > >
I originally tried to make these @code blocks and that's when I ran into the formatting issue since @code doesn't like the embedded comments (at least in 1.8.4). @verbatim seems to solve this issue. I'm happy to use whatever works if someone has a better suggestion. I agree we ultimately want this to be sourced from the implementations but the design doc is intended to guide implementers so we have a bit of chicken-and-egg issue here. Perhaps we can do a 2nd pass on these once the implementations are complete? Bill On Mon, Aug 4, 2014 at 1:15 PM, Mike Holmes <mike.holmes@linaro.org> wrote: > I ran with the very latest doxygen 1.8.7 and it is ok again, with doxygen > 1.8.6 the V2 of this doc also fails so that looks like a bug that has been > fixed in doxygen - I checked the release notes for a reason and nothing > jumped out at me. > > If the @verbaitam blocks were going to stay I would say they should be > @code blocks since the sections do describe code. However as soon as there > is an implementation of this API doxygen will be able to take the API > definitions from the code and we will be deleting them from here, so I > think it is ok. > > Tested-By: Mike Holmes <mike.holmes@linaro.org> > > > > On 4 August 2014 14:00, Bill Fischofer <bill.fischofer@linaro.org> wrote: > >> Each sentence starts on a new line per the convention we agreed to >> earlier, but this is input to doxygen converted from the Google docs and is >> not otherwise line-wrapped. So it's whatever the Google output is. If we >> have an automated tool to further modify the lines we can use that but >> otherwise I don't see the value in doing a lot more manual editing on these. >> >> Bill >> >> >> On Mon, Aug 4, 2014 at 12:42 PM, Maxim Uvarov <maxim.uvarov@linaro.org> >> wrote: >> >>> How long are strings in that document? Maybe to fit them to 80 >>> characters to better read in terminal? >>> >>> Maxim. >>> >>> On 08/04/2014 09:33 PM, Bill Fischofer wrote: >>> >>>> Switch to use of @verbatim/@endverbatim to avoid formatting issues with >>>> different levels of doxygen. >>>> >>>> Signed-off-by: Bill Fischofer <bill.fischofer@linaro.org> >>>> --- >>>> classification_design.dox | 879 ++++++++++++++++++++++++++++++ >>>> +++++++++++ >>>> images/classification_flow.png | Bin 0 -> 35193 bytes >>>> 2 files changed, 879 insertions(+) >>>> create mode 100644 classification_design.dox >>>> create mode 100644 images/classification_flow.png >>>> >>>> diff --git a/classification_design.dox b/classification_design.dox >>>> new file mode 100644 >>>> index 0000000..0cb7134 >>>> --- /dev/null >>>> +++ b/classification_design.dox >>>> @@ -0,0 +1,879 @@ >>>> +/* Copyright (c) 2014, Linaro Limited >>>> + * All rights reserved >>>> + * >>>> + * SPDX-License-Identifier: BSD-3-Clause >>>> + */ >>>> + >>>> +/*! >>>> +@page classification_design ODP Design - Classification API >>>> +For the implementation of the ODP classification API please see @ref >>>> odp_classify.h >>>> + >>>> +@tableofcontents >>>> + >>>> +@section introduction Introduction >>>> +This document defines the Classification APIs supported by ODP v1.0. >>>> +Classification is logically composed of two stages: Parsing and Rule >>>> Matching. >>>> +Parsing takes a raw packet and validates its structure and identifies >>>> fields of interest in the various headers that comprise the layers of the >>>> packet. >>>> +Rule Matching, in turn, takes the result of parsing and sorts packets >>>> into Classes of Service (CoS) based on application-defined rule sets. >>>> +@subsection use_of_terms Use of Terms >>>> +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", >>>> "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this >>>> document are to be interpreted as described in [RFC 2119]( >>>> https://tools.ietf.org/html/rfc21199). >>>> +@subsection purpose Purpose >>>> +ODP is a framework for software-based packet forwarding/filtering >>>> applications, and the purpose of the Packet Classifier API is to enable >>>> applications to program the platform hardware or software implementation to >>>> assist in prioritization, classification and scheduling of each packet, so >>>> that the software application can run faster, scale better and adhere to >>>> QoS requirements. >>>> +The following API abstraction are not modelled after any existing >>>> product implementation, but is instead defined in terms of what a typical >>>> data-plane application may require from such a platform, without >>>> sacrificing simplicity and avoiding ambiguity. Certain terms that are being >>>> used within the context of existing products in relation to packet parsing >>>> and classification, such as “access lists” are avoided such that not to >>>> suggest any relationship between the abstraction used within this API and >>>> any particular manner in which they may be implemented in hardware. >>>> +These are the key ODP objects that the parser needs to employ, that >>>> are presently defined in ODP: >>>> +@subsubsection odp_pktio odp_pktio >>>> +odp_pktio specifies an individual packet I/O channel instance. In >>>> other words, it would translate to a physical interface or a logical port, >>>> or in the case of channelized protocols (e.g., [Interlaken]( >>>> https://www.google.com/url?q=https%3A%2F%2Fwww.cortina-systems.com% >>>> 2Fimages%2Fdocuments%2F400023_Interlaken_Technology_White_ >>>> Paper.pdf&sa=D&sntz=1&usg=AFQjCNEBdJTBmA1XaNGY3pmumQTfgSi1oA)) it >>>> would map to a logical channel on that interface. >>>> +Since the classifier API deals exclusively with ingress, this object >>>> represents the source of packets into the classifier. In order to support >>>> any non-trivial use case, the classifier API needs to be able to assign >>>> multiple odp_queue instances for any single odp_pktio object, and may also >>>> assign any odp_queue instance to more than one odp_pktio object. >>>> +@subsubsection odp_queue odp_queue >>>> +odp_queue specifies a logical queue for packets, and in the case of >>>> ingress, this would represent a stream of packets which share several >>>> attributes, that are delivered to the ODP application for processing. The >>>> per-queue attributes currently defined are: queue type, sync (ordering); >>>> priority; and schedule group (set of processor cores). >>>> +@subsubsection odp_buffer_pool odp_buffer_pool >>>> +odp_buffer_pool specifies a collection of buffers of same size and >>>> alignment, as well as a set of policies such as flow control and processor >>>> affinity. The classifier API refers to such pools that are designated for >>>> storing ingress packets. >>>> +@section functional_description Functional Description >>>> +Following is the functionality that is required of the classifier API, >>>> and its underlying implementation. The details and order of the following >>>> paragraph is informative, and is only intended to help convey the >>>> functional scope of a classifier and provide context for the API. In >>>> reality, implementations may execute many of these steps concurrently, or >>>> in different order while maintaining the evident dependencies: >>>> + >>>> +-# Apply a set of \e classification \e rules to the header of an >>>> incoming packet, identify the header fields, e.g., \e ethertype, IP >>>> version, IP protocol, transport layer port numbers, IP DiffServ, VLAN id, >>>> 802.1p priority. >>>> + >>>> +-# Store these fields as packet meta data for application use, and for >>>> the remainder of parser operations. The \e odp_pktio is also stored as one >>>> of the meta data fields for subsequent use. >>>> + >>>> +-# Compute an \e odp_cos (Class of Service) value from a subset of >>>> supported fields from 1) above. >>>> + >>>> + >>>> +-# Based on the \e odp_cos from 3) above, select the \e odp_queue >>>> through which the packet is delivered to the application. >>>> + >>>> +-# Validate the packet data integrity (checksums, FCS) and >>>> correctness (e.g., length fields) and store the validation result, along >>>> with optional error layer and type indicator, in packet meta data. >>>> Optionally, if a packet fails validation, override the \e odp_cos selection >>>> in step 3 to a class of service designated for errored packets. >>>> + >>>> +-# Since the selected \e odp_queue may require preservation of packet >>>> order, i.e., SYNC_ATOMIC or SYNC_ORDERED, optionally select the packet >>>> header fields from which the parser calculates a \e odp_flow_signature, >>>> which may be a unique flow identifier or a hash, such that the packets >>>> which are assigned the same \e odp_flow_signature are scheduled in the same >>>> order they are received. >>>> + >>>> +-# Based on the \e odp_cos from 3) above, select the \e >>>> odp_buffer_pool that should be used to acquire a buffer to store the packet >>>> data and meta data. >>>> + >>>> +-# Allocate a buffer from \e odp_buffer_pool selected in 6) above and >>>> logically store the packet data and meta data to the allocated buffer, or >>>> in accordance with class-of-service drop policy and subject to pool buffer >>>> availability, optionally discard the packet. >>>> + >>>> +-# Enqueue the buffer into the \e odp_queue selected in 4) above. >>>> + >>>> +The above is an abstract description of the classifier functionality, >>>> and may be applied to a variety of applications in many different ways. >>>> +The ultimate meaning of how this functionality applies to an >>>> application also depends on other ODP modules, so the above may not >>>> complete a full depiction. >>>> +For instance, the exact meaning of \e priority, which is a per-queue >>>> attribute is influenced by the ODP scheduler semantics, and the system >>>> behavior under stress depends on the ODP buffer pool module behavior. >>>> + >>>> +For the sole purpose of illustrating the above abstract functionality, >>>> here is an example of a Layer-2 (IEEE 802.1D) bridge application: >>>> +Such a forwarding application that also adheres to IEEE 802.1p/q >>>> priority, which has 8 traffic priority levels, might create 8 \e >>>> odp_buffer_pool instances, one for each PCP priority level, and 8 \e >>>> odp_queue instances one per priority level. >>>> +Incoming packets will be inspected for a VLAN header; the PCP field >>>> will be extracted, and used to select both the pool and the queue. >>>> +Because each queue will be assigned a priority value, the packets with >>>> highest PCP values will be scheduled before any packet with a lower PCP >>>> value. >>>> +Also, in a case of congestion, buffer pools for lower priority packets >>>> will be depleted earlier than the pools containing packets of the high >>>> priority, and hence the lower priority packets will be dropped (assuming >>>> that is the only flow control method that is supported in the platform) >>>> while higher priority packets will continue to be received into buffers and >>>> processed. >>>> +@subsection flow_diagram Classification Processing Flow Diagram >>>> +@image html classification_flow.png "Figure 1: Classification Flow >>>> Diagram" >>>> +@image latex classification_flow.eps "Figure 1: Classification Flow >>>> Diagram" >>>> + >>>> +@section api_elements API Elements >>>> +While the above description refers to the abstracted packet >>>> classifier, the following is the description of the API designed to program >>>> the packet classifier, and is intended to add clarity to the functions >>>> provided further below. >>>> +@subsection cos_creation Class of Service Creation and Binding >>>> +To program the classifier, a class-of-service instance must be >>>> created, which will contain the packet filtering resources that it may >>>> require. >>>> +All subsequent calls refer to one or more of these resources. >>>> +Each class of service instance must be associated with a single queue >>>> or queue group, which will be the destination of all packets matching that >>>> particular filter. >>>> +The queue assignment is implemented as a separate function call such >>>> that the queue may be modified at any time, without tearing down the >>>> filters that define the class of service. In other words, it is possible to >>>> change the destination queue for a class of service defined by its filters >>>> quickly and dynamically. >>>> +Optionally, on platforms that support multiple packet buffer pools, >>>> each class of service may be assigned a different pool such that when >>>> buffers are exhausted for one class of service, other classes are not >>>> negatively impacted and continue to be processed. >>>> + >>>> +@subsection default_packet_handling Default packet handling >>>> +There SHOULD be one \b odp_cos assigned to each port with the \c >>>> odp_cos_pktio_set() function, which will function as the default >>>> class-of-service for all packets received from an ingress port, that do not >>>> match any of the filters defined subsequently. At minimum this default >>>> class-of-service MUST have a queue and a buffer pool assigned to it on >>>> platforms that support multiple packet buffer pools. Multiple odp_pktio >>>> instances (i.e., multiple ports) MAY each have their own default odp_cos, >>>> or MAY share a odp_cos with other ports, based on application requirements. >>>> + >>>> +@subsection packet_classification Packet Classification >>>> +For each odp_pktio port, the API allows the assignment of a >>>> class-of-service to a packet using one of three methods: >>>> + >>>> +-# The packet may be assigned a specific class-of-service based on its >>>> Layer-2 (802.1P/902.1Q VLAN tag) priority field. Since the standard field >>>> defines 8 discrete priority levels, the API allows to assign an odp_cos to >>>> each of these priority levels with the \c odp_cos_with_l2_priority() >>>> function. >>>> + >>>> +-# Similarly, a class-of-service may be assigned using the Layer-3 (IP >>>> DiffServ) header field. The application supplies an array of \e odp_cos >>>> values that covers the entire range of the standard protocol header field, >>>> where array elements do not need to contain unique values. There is also a >>>> need to specify if Layer-3 priority takes precedence over Layer-2 priority >>>> in a packet with both headers present. >>>> + >>>> +-# Additionally, the application may also program a number of \e >>>> pattern \e matching \e rules that assign a class-of-service for packets >>>> with header fields matching specified values. The field-matching rules take >>>> precedence over the previously described priority-based assignment of a >>>> class-of-service. Using these matching rules the application should be able >>>> for example to identify all packets containing VoIP traffic based on the >>>> protocol being UDP, and a specific destination or source port numbers, and >>>> appropriately assign these packets an class-of-service that maps to a >>>> higher priority queue, assuring voice packets a lower and bound latency. >>>> + >>>> +@subsection scaling_and_flow Scaling and Flow Discrimination >>>> +In addition to classifying packets and routing them to those queues >>>> with the appropriate priority, and optionally limiting their memory >>>> consumption by designating certain classes of packets to specific buffer >>>> pools, the classifier API also facilitates the scaling of data-plane >>>> application on multi-core systems by creating a mechanism to define which >>>> packet headers need to be combined to result in a value representing a >>>> specific packet flow. The classifier generates a signature, which can be a >>>> checksum or hash of arbitrary strength that covers those packet header >>>> fields that are identified by the application as identifying flows. >>>> + >>>> +The \e flow \e signatures that result from hashing are then stored >>>> with the packet meta data (along with its class-of-service and its ingress >>>> \e odp_pktio port), and subsequently may be utilized by the implementation >>>> of a scheduler queue to maintain the order of packets with the same flow >>>> signature, while allowing packets with different signatures to be processed >>>> concurrently and independently on different processing cores. >>>> + >>>> +@subsection packet_meta_data Packet meta data Elements >>>> +Here are the specific information elements that SHOULD be stored >>>> within the packet meta data structure: >>>> +- Protocol fields that are decoded and extracted by the parsing phase >>>> +- Flow-signature calculated from a prescribed collection of protocol >>>> fields >>>> +- The class-of-service identifier that is selected for the packet >>>> +- The ingress port identifier >>>> +- The result of packet validation, including an indication of the type >>>> of error detected, if any >>>> + >>>> +The ODP packet API module SHALL provide accessors for retrieving the >>>> above meta data fields from the container buffer in an >>>> implementation-independent manner. >>>> + >>>> +@section api_definitions API Definitions >>>> +@subsection data_types Data Types >>>> +The following data types are referenced in the API descriptions >>>> described below. >>>> +The names are part of the ODP API and MUST be present in any >>>> conforming implementation, however the type values shown here are >>>> illustrative and implementations SHOULD either use these or substitute >>>> their own type values that are appropriate to the underlying platform. >>>> + >>>> +@verbatim >>>> +/** >>>> + * 'odp_pktio_t' value to indicate any port >>>> + */ >>>> +#define ODP_PKTIO_ANY ((odp_pktio_t)~0) >>>> + >>>> + >>>> +/** >>>> + * 'odp_pktio_t' value to indicate an error >>>> + */ >>>> +#define ODP_PKTIO_INVALID ((odp_pktio_t)0) >>>> + >>>> + >>>> +/** >>>> + * Class of service instance type >>>> + */ >>>> +typedef uint32_t odp_cos_t; >>>> + >>>> + >>>> +/** >>>> + * flow signature type, only used for packet meta data field. >>>> + */ >>>> +typedef uint32_t odp_flowsig_t; >>>> + >>>> + >>>> +/** >>>> + * This value is returned from odp_cos_create() on failure, >>>> + * May also be used as a “sink” class of service that >>>> + * results in packets being discarded. >>>> + */ >>>> +#define ODP_COS_INVALID ((odp_cos_t)~0) >>>> +@endverbatim >>>> + >>>> +@subsection cos_routines Class of Service Routines >>>> +Conforming ODP implementations MUST provide the following >>>> Classification APIs: >>>> +@subsubsection cos_create odp_cos_create >>>> +@verbatim >>>> +/** >>>> + * Create a class-of-service >>>> + * >>>> + * @param name is a string intended for debugging purposes. >>>> + * >>>> + * @return Class of service instance identifier, >>>> + * or ODP_COS_INVALID on error. >>>> + */ >>>> + >>>> +odp_cos_t odp_cos_create(const char *name); >>>> +@endverbatim >>>> + >>>> +This routine is used to create a class of service that can be the >>>> target of classifier rules. >>>> +The number of such classes supported is implementation-defined. >>>> +Attempts to create more than are supported by the implementation will >>>> result in an \c ODP_COS_INVALID return and errno being set to \c >>>> ODP_IMPLEMENTATION_LIMIT. >>>> + >>>> +@subsubsection cos_destroy odp_cos_destroy >>>> +@verbatim >>>> +/** >>>> + * Discard a class-of-service along with all its associated resources >>>> + * >>>> + * @param cos_id class-of-service instance. >>>> + * >>>> + * @return 0 on success, -1 on error. >>>> + */ >>>> + >>>> +int odp_cos_destroy(odp_cos_t cos_id); >>>> +@endverbatim >>>> + >>>> +This routine is the bracketing routine for odp_cos_create(). >>>> +It is used to destroy an existing CoS. >>>> +It is the caller’s responsibility to ensure that no active pattern >>>> matching rules refer to the CoS prior to calling this routine. >>>> +Results are unpredictable if this restriction is not met. >>>> +@subsubsection cos_set_queue odp_cos_set_queue >>>> +@verbatim >>>> +/** >>>> + * Assign a queue for a class-of-service >>>> + * >>>> + * @param cos_id class-of-service instance. >>>> + * >>>> + * @param queue_id is the identifier of a queue where all >>>> packets >>>> + * of this specific class of service will be enqueued. >>>> + * >>>> + * @return 0 on success, negative error code on failure. >>>> + */ >>>> + >>>> +int odp_cos_set_queue(odp_cos_t cos_id, odp_queue_t queue_id); >>>> +@endverbatim >>>> + >>>> +This routine associates a target queue with a CoS such that all >>>> packets assigned to this CoS will be enqueued to the specified queue_id at >>>> the end of classification processing. >>>> +@subsubsection cos_set_queue_group odp_cos_set_queue_group >>>> +@verbatim >>>> +/** >>>> + * Assign a homogenous queue-group to a class-of-service. >>>> + * >>>> + * @param cos_id identifier of class-of-service instance >>>> + * @param queue_group_id identifier of the queue group to receive >>>> packets >>>> + * associated with this class of service. >>>> + * >>>> + * @return 0 on success, negative error code on failure. >>>> + */ >>>> + >>>> +int odp_cos_set_queue_group(odp_cos_t cos_id, odp_queue_group_t >>>> queue_group_id); >>>> +@endverbatim >>>> + >>>> +This routine associates a target queue group with a CoS such that all >>>> packets assigned to this CoS will be distributed to the specified >>>> queue_group_id at the end of classification processing. >>>> +@subsubsection cos_set_pool odp_cos_set_pool >>>> +@verbatim >>>> +/** >>>> + * Assign packet buffer pool for specific class-of-service >>>> + * >>>> + * @param cos_id class-of-service instance. >>>> + * @param pool_id is a buffer pool identifier where all packet buffers >>>> + * will be sourced to store packet that belong to this >>>> + * class of service. >>>> + * >>>> + * @return 0 on success negative error code on failure. >>>> + * >>>> + * >>>> + */ >>>> + >>>> +int odp_cos_set_pool(odp_cos_t cos_id, odp_buffer_pool_t pool_id); >>>> +@endverbatim >>>> + >>>> +This OPTIONAL routine associates a target buffer pool with a CoS such >>>> that all packets assigned to this CoS will be stored in packet buffers >>>> allocated from the designated pool_id. >>>> + >>>> + >>>> +@subsection cos_drop_policy Class of Service Drop Policy Routines >>>> +These routines control how drop policies are to be observed for a >>>> given class of service. >>>> +@subsubsection drop_data_types Data types >>>> +~~~~~{.c} >>>> +enum odp_cos_drop_e { >>>> + ODP_COS_DROP_POOL, /**< Follow buffer pool drop policy >>>> */ >>>> + ODP_COS_DROP_NEVER, /**< Never drop, ignoring buffer >>>> pool policy */ >>>> +}; >>>> +typedef enum odp_drop_e odp_drop_t; >>>> +~~~~~ >>>> + >>>> +@subsubsection cos_set_drop odp_cos_set_drop >>>> +@verbatim >>>> +/** >>>> + * Assign packet drop policy for specific class-of-service >>>> + * >>>> + * @param cos_id class-of-service instance. >>>> + * @param drop_policy is the desired packet drop policy for this >>>> class. >>>> + * >>>> + * @return 0 on success negative error code on failure. >>>> + */ >>>> + >>>> +int odp_cos_set_drop(odp_cos_t cos_id, odp_drop_t drop_policy); >>>> +@endverbatim >>>> + >>>> +This routine sets the drop policy for a class of service. >>>> +It is an OPTIONAL routine. >>>> +If an implementation does not provide this function it MUST supply a >>>> definition of it that simply returns ODP_FUNCTION_NOT_AVAILABLE. >>>> +@subsubsection pktio_set_default_cos odp_pktio_set_default_cos >>>> +@verbatim >>>> +/** >>>> + * Setup per-port default class-of-service >>>> + * >>>> + * @param pktio_in ingress port identifier. >>>> + * @param default_cos class-of-service set to all packets arriving >>>> + * at the 'pktio_in' ingress port, unless overridden by >>>> subsequent >>>> + * header-based filters. >>>> + * >>>> + * @return 0 on success negative error code on failure. >>>> + * >>>> + * >>>> + * @note This may replace the default queue per pktio. >>>> + */ >>>> + >>>> +int odp_pktio_set_default_cos(odp_pktio_t pktio_in, odp_cos_t >>>> default_cos); >>>> +@endverbatim >>>> + >>>> +This routine specifies a default class of service for a given pktio >>>> instance. >>>> +Incoming packets on the specified pktio are assigned to this class of >>>> service if no other pattern matching rule obtains. >>>> +@subsubsection pktio_set_error_cos odp_pktio_set_error_cos >>>> +@verbatim >>>> +/** >>>> + * Setup per-port error class-of-service >>>> + * >>>> + * @param pktio_in ingress port identifier. >>>> + * @param error_cos class-of-service set to all packets arriving >>>> + * at the 'pktio_in' ingress port that contain an error. >>>> + * >>>> + * @return 0 on success negative error code on failure. >>>> + */ >>>> + >>>> +int odp_pktio_set_error_cos(odp_pktio_t pktio_in, odp_cos_t >>>> error_cos); >>>> +@endverbatim >>>> + >>>> +This OPTIONAL function assigns a class-of-service used to handle >>>> packets containing various types of errors. The specific errors types >>>> include L2 FCS and optionally L3/L4 checksum errors, malformed headers, >>>> etc., depending on platform capabilities. >>>> +The specified error_cos MAY simply discard these packets or deliver >>>> them via a queue to the application for further processing. >>>> +@subsubsection pktio_set_skip odp_pktio_set_skip >>>> +@verbatim >>>> +/** >>>> + * Setup per-port header offset >>>> + * >>>> + * @param pktio_in ingress port identifier. >>>> + * @param offset is the number of bytes the classifier must skip. >>>> + * >>>> + * @return Success or ODP_FUNCTION_NOT_AVAILABLE >>>> + */ >>>> + >>>> +int odp_pktio_set_skip(odp_pktio_t pktio_in, size_t offset); >>>> +@endverbatim >>>> + >>>> +This OPTIONAL function applies to ports that carry an additional >>>> headers preceding the standard Ethernet header. Such headers are typically >>>> vendor-specific and thus the classifier is not required to parse such >>>> headers, but the size of a custom header is critical for the classifier to >>>> be able to parse standard protocol headers that normally follow. >>>> +@subsubsection cos_set_headroom odp_cos_set_headroom >>>> +@verbatim >>>> +/** >>>> + * Specify per-port buffer headroom >>>> + * >>>> + * @param pktio_in ingress port identifier. >>>> + * @param headroom number of bytes of space preceding packet data >>>> to reserve >>>> + * for use as headroom. Must not exceed the >>>> implementation >>>> + * defined ODP_PACKET_MAX_HEADROOM. >>>> + * >>>> + * @return Success or ODP_PARAMETER_ERROR, >>>> + * or ODP_FUNCTION_NOT_AVAILABLE >>>> + */ >>>> + >>>> +int odp_cos_set_headroom(odp_cos_t cos_id, size_t req_room); >>>> +@endverbatim >>>> + >>>> +This OPTIONAL routine specifies the number of bytes of headroom that >>>> should be reserved for each packet assigned to this class of service. >>>> +Each implementation defines an ODP_PACKET_MAX_HEADROOM limit that sets >>>> an upper bound on the size of the headroom that can be reserved for a >>>> packet. >>>> +@subsubsection cos_with_l2_priority odp_cos_with_l2_priority >>>> +@verbatim >>>> +/** >>>> + * Request to override per-port class of service >>>> + * based on Layer-2 priority field if present. >>>> + * >>>> + * @param pktio_in ingress port identifier. >>>> + * @param num_qos is the number of QoS levels, typically 8. >>>> + * @param qos_table are the values of the Layer-2 QoS header field. >>>> + * @param cos_table is the class-of-service assigned to each of the >>>> + * allowed Layer-2 QOS levels. >>>> + * @return 0 on success negative error code on failure. >>>> + */ >>>> + >>>> +int odp_cos_with_l2_priority(odp_pktio_t pktio_in, >>>> + size_t num_qos, >>>> + uint8_t qos_table[], /**< >>>> 'num_qos' elements */ >>>> + odp_cos_t cos_table[]); /**< >>>> 'num_qos' elements */ >>>> +@endverbatim >>>> + >>>> +This routine is used to assign classes of service based on the layer 2 >>>> (L2) priority associated with input packets received on the specified >>>> pktio_in. >>>> +For each of the values in qos_table[], the corresponding value in >>>> cos_table[] will be assigned. >>>> +@subsubsection cos_with_l3_dscp odp_cos_with_l3_dscp >>>> +@verbatim >>>> +/** >>>> + * >>>> + * @param pktio_in ingress port identifier. >>>> + * @param num_qos is the number of allowed Layer-3 QoS levels. >>>> + * @param qos_table are the values of the Layer-3 QoS header field. >>>> + * @param cos_table is the class-of-service assigned to each of the >>>> + * allowed Layer-3 QOS levels. >>>> + * @param l3_preference when true, Layer-3 QoS overrides L2 QoS when >>>> present. >>>> + * >>>> + * @return 0 on success negative error code on failure. >>>> + */ >>>> + >>>> +int odp_cos_with_l3_qos(odp_pktio_t pktio_in, >>>> + size_t num_qos, >>>> + uint8_t qos_table[], /**< 'num_qos' >>>> elements */ >>>> + odp_cos_t cos_table[], /**< 'num_qos' >>>> elements */ >>>> + odp_bool_t l3_preference); >>>> +@endverbatim >>>> + >>>> +This OPTIONAL routine is used to assign classes of service based on >>>> the layer 3 (L3) Differentiated Services (DS) designation. >>>> +This is the DSCP field of an IPv4 header or the first six bits of the >>>> Traffic Class of an IPv6 header. >>>> +For each of the values in qos_table[], the corresponding value in >>>> cos_table[] will be assigned. >>>> +The l3_preference flag is use to control whether the CoS assigned by >>>> this routine takes precedence over the CoS assigned by >>>> odp_cos_with_l2_priority() in the event that both apply to the same packet. >>>> + >>>> +@subsection pmrs Pattern Matching Rules >>>> +While the above routines permit class of service assignments to be >>>> made based on static criteria, the real power of classification is the >>>> ability to identify flows based on the variable contents of packet headers. >>>> +To do this ODP provides support for defining pattern matching rules >>>> (PMRs) that operate based on values contained in specified header fields. >>>> + >>>> +Associated with PMRs are enums that are used to specify standard >>>> packet header fields: >>>> +@subsubsection cos_hdr_flow_fields odp_cos_hdr_flow_fields_e >>>> +@verbatim >>>> +/** >>>> + * Packet header field enumeration >>>> + * for fields that may be used to calculate >>>> + * the flow signature, if present in a packet. >>>> + */ >>>> + >>>> +enum odp_cos_hdr_flow_fields_e { >>>> + ODP_COS_FHDR_IN_PKTIO, /**< Ingress port number */ >>>> + ODP_COS_FHDR_L2_SAP, /**< Ethernet Source MAC address >>>> */ >>>> + ODP_COS_FHDR_L2_DAP, /**< Ethernet Destination MAC >>>> address */ >>>> + ODP_COS_FHDR_L2_VID, /**< Ethernet VLAN ID */ >>>> + ODP_COS_FHDR_L3_FLOW /**< IPv6 flow_id */ >>>> + ODP_COS_FHDR_L3_SAP, /**< IP source address */ >>>> + ODP_COS_FHDR_L3_DAP, /**< IP destination address */ >>>> + ODP_COS_FHDR_L4_PROTO, /**< IP protocol (e.g. >>>> TCP/UDP/ICMP) */ >>>> + ODP_COS_FHDR_L4_SAP, /**< Transport source port */ >>>> + ODP_COS_FHDR_L4_DAP, /**< Transport destination port >>>> */ >>>> + ODP_COS_FHDR_IPSEC_SPI, /**< IPsec session identifier */ >>>> + ODP_COS_FHDR_LD_VNI, /**< NVGRE/VXLAN network >>>> identifier */ >>>> + ODP_COS_FHDR_USER /**< Application-specific header >>>> field(s) */ >>>> +}; >>>> +@endverbatim >>>> + >>>> +Conforming ODP implementations SHOULD implement efficient flow set >>>> management routines such as these: >>>> + >>>> +~~~~~{.c} >>>> +/** >>>> + * Set of header fields that take part in flow signature hash >>>> calculation: >>>> + * bit positions per 'odp_cos_hdr_flow_fields_e' enumeration. >>>> + * >>>> +typedef uint16_t odp_cos_flow_set_t; >>>> + >>>> + >>>> +/** >>>> + * Set a member of the flow signature fields data set >>>> + * >>>> +static inline odp_cos_flow_set_t >>>> +odp_cos_flow_set( odp_cos_flow_set_t set, >>>> + enum odp_cos_hdr_flow_fields_e field) >>>> +{ >>>> + return set | (1U << field); >>>> +} >>>> + >>>> + >>>> +/** >>>> + * Test a member of the flow signature fields data set >>>> + * >>>> +static inline bool >>>> +odp_cos_flow_is_set( odp_cos_flow_set_t set, >>>> + enum odp_cos_hdr_flow_fields_e field) >>>> +{ >>>> + return (set & (1U << field)) != 0; >>>> +} >>>> +~~~~~ >>>> + >>>> +These routines are intended to be used in support of the following >>>> flow signature APIs: >>>> + >>>> +@subsubsection cos_class_flow_sig odp_cos_class_flow_signature >>>> +@verbatim >>>> +/** >>>> + * Set up set of headers used to calculate a flow signature >>>> + * based on class-of-service. >>>> + * >>>> + * @param cos_id class of service instance identifier >>>> + * @param req_data_set requested data-set for flow signature >>>> calculation >>>> + * >>>> + * @return data-set that was successfully applied. All-zeros data set >>>> + * indicates a failure to assign any of the requested fields, or other >>>> + * error. >>>> + */ >>>> + >>>> +odp_cos_flow_set_t >>>> +odp_cos_class_flow_signature(odp_cos_t cos_id, >>>> + odp_cos_flow_set_t req_data_set); >>>> +@endverbatim >>>> + >>>> +This OPTIONAL routine associates a fow set with a class of service for >>>> flow signature calculation. >>>> + >>>> +@subsubsection cos_port_flow_sig odp_cos_port_flow_signature >>>> +@verbatim >>>> +/** >>>> + * Set up set of headers used to calculate a flow signature >>>> + * based on ingress port. >>>> + * >>>> + * @param pktio_in ingress port identifier. >>>> + * @param req_data_set requested data-set for flow signature >>>> calculation >>>> + * >>>> + * @return data-set that was successfully applied. An all-zeros >>>> data-set >>>> + * indicates a failure to assign any of the requested fields, or other >>>> + * error. >>>> + */ >>>> + >>>> +odp_cos_flow_set_t >>>> +odp_cos_port_flow_signature(odp_pktio_t pktio_in, >>>> + odp_cos_flow_set_t req_data_set); >>>> +@endverbatim >>>> + >>>> +@subsection pmr_routines Pattern Matching Rules Routines >>>> +The following data structures SHOULD be implemented to support the >>>> definition of pattern matching routines by conforming ODP implementations: >>>> + >>>> +~~~~~{.c} >>>> +/** >>>> + * PMR - Packet Matching Rule >>>> + * Up to 32 bit of ternary matching of one of the available header >>>> fields >>>> + * >>>> + >>>> + >>>> +#define ODP_PMR_INVAL ((odp_pmr_t)NULL) >>>> +typedef struct odp_pmr_s *odp_pmr_t; >>>> +~~~~~ >>>> + >>>> +@subsecion terms Terms >>>> +Terms are the elements of a PMR and are identified by the following >>>> enum: >>>> + >>>> +@verbatim >>>> +enum odp_pmr_term_e { >>>> + ODP_PMR_ETHTYPE_0, /**< Initial (outer) Ethertype only >>>> (*val=uint16_t)*/ >>>> + ODP_PMR_ETHTYPE_X, /**< Ethertype of most inner VLAN tag >>>> (*val=uint16_t)*/ >>>> + ODP_PMR_VLAN_ID_0, /**< First VLAN ID (outer) >>>> (*val=uint16_t) */ >>>> + ODP_PMR_VLAN_ID_X, /**< Last VLAN ID (inner) >>>> (*val=uint16_t) */ >>>> + ODP_PMR_DMAC, /**< destination MAC address >>>> (*val=uint64_t) */ >>>> + ODP_PMR_IPPROTO, /**< IP Protocol or IPv6 Next Header >>>> (*val=uint8_t) */ >>>> + ODP_PMR_UDP_DPORT, /**< Destination UDP port, implies >>>> IPPROTO=17 */ >>>> + ODP_PMR_TCP_DPORT, /**< Destination TCP port implies >>>> IPPROTO=6 */ >>>> + ODP_PMR_UDP_SPORT, /**< Source UDP Port (*val=uint16_t) */ >>>> + ODP_PMR_TCP_SPORT, /**< Source TCP port (*val=uint16_t) */ >>>> + ODP_PMR_SIP_ADDR, /**< Source IP address (uint32_t) */ >>>> + ODP_PMR_DIP_ADDR, /**< Destination IP address (uint32_t) */ >>>> + ODP_PMR_SIP6_ADDR, /**< Source IP address (uint8_t[16]) */ >>>> + ODP_PMR_DIP6_ADDR, /**< Destination IP address >>>> (uint8_t[16]) */ >>>> + ODP_PMR_IPSEC_SPI, /**< IPsec session >>>> identifier(*val=uint32_t) */ >>>> + ODP_PMR_LD_VNI, /**< NVGRE/VXLAN network identifier >>>> (*val=uint32_t) */ >>>> + >>>> + >>>> + /** Inner header may repeat above values with this offset */ >>>> + ODP_PMR_INNER_HDR_OFF=32 >>>> +}; >>>> +@endverbatim >>>> + >>>> +@subsubsection tunnel_considerations Tunnel Considerations >>>> +Note that PMRs may be extended to support tunnels and tenants (NVGRE, >>>> VXLAN) via the ODP_PMR_INNER_HDR_OFF enum. >>>> +This enum is intended to be used as an “adder” to a PMR to indicate >>>> that the term refers to an inner header. >>>> +For example, the term ODP_PMR_DMAC would refer to the destination MAC >>>> address of the packet if the packet is not a tunnel, or of the outer header >>>> (the tunnel) if the packet is a tunnel. >>>> +To refer to the inner (tenant) destination MAC, the term would be >>>> specified as ODP_PMR_INNER_HDR_OFF+ODP_PMR_DMAC. >>>> + >>>> +@subsection pmr_apis PMR APIs >>>> +The following APIs are provided to enable an ODP application to >>>> specify PMRs as a series of individual or cascaded terms: >>>> +@subsubsection pmr_create_match odp_pmr_create_match >>>> +@verbatim >>>> +/** >>>> + * Create a packet match rule with mask and value >>>> + * >>>> + * @param term is one value of the enumerated values supported >>>> + * @param val is the value to match against the packet header >>>> + * in native byte order. >>>> + * @param mask is the mask to indicate which bits of the header >>>> + * should be matched ('1') and which should be >>>> ignored ('0') >>>> + * @param val_sz size of the ‘val’ and ‘mask’ arguments, >>>> + * that must match the value size requirement of the >>>> + * specific ‘term’. >>>> + * >>>> + * @return a handle of the matching rule or ODP_PMR_INVAL on error >>>> + */ >>>> + >>>> +odp_pmr_t odp_pmr_create_match(enum odp_pmr_term_e term, >>>> + const void *val, const void *mask, >>>> size_t val_sz); >>>> +@endverbatim >>>> + >>>> +This routine creates a PMR that matches a single value to a term. >>>> + >>>> +@subsubsection pmr_create_range odp_pmr_create_range >>>> +@verbatim >>>> +/** >>>> + * Create a packet match rule with value range >>>> + * >>>> + * @param term is one value of the enumerated values supported >>>> + * @param val1 is the lower bound of the header field range. >>>> + * @param val2 is the upper bound of the header field range. >>>> + * @param val_sz size of the ‘val1’ and ‘val2’ arguments, >>>> + * that must match the value size requirement of the >>>> + * specific ‘term’. >>>> + * >>>> + * @return a handle of the matching rule or ODP_PMR_INVAL on error >>>> + * @note: Range is inclusive [val1..val2]. >>>> + */ >>>> + >>>> +odp_pmr_t odp_pmr_create_range(enum odp_pmr_term_e term, >>>> + const void *val1, const void >>>> *val2, size_t val_sz); >>>> +@endverbatim >>>> + >>>> +This routine creates a PMR that matches an inclusive range of values >>>> to a term. >>>> + >>>> +@subsubsection pmr_destroy odp_pmr_destroy >>>> +@verbatim >>>> +/** >>>> + * Invalidate a packet match rule and vacate its resources >>>> + * >>>> + * @param pmr_id the identifier of the PMR to be destroyed >>>> + * >>>> + * @return Success or ODP_PMR_INVALID if the specified pmr_id not >>>> found. >>>> + */ >>>> + >>>> +int odp_pmr_destroy(odp_omr_t pmr_id); >>>> +@endverbatim >>>> + >>>> +This routine destroys a previously created PMR. >>>> +If the PMR is currently associated with an active class of service it >>>> is unpredictable at which point the match defined by the PMR is deactivated >>>> in terms of packet flow. >>>> +However, implementations MUST ensure that a PMR is either matched or >>>> not matched in its entirety such that dynamic changes to PMRs do not result >>>> in partial matches. >>>> + >>>> +@subsubsection pktio_pmr_cos odp_pktio_pmr_cos >>>> +@verbatim >>>> +/** >>>> + * Apply a PMR to a pktio to assign a CoS. >>>> + * >>>> + * @param pmr_id the id of the PMR to be activated >>>> + * @param src_pktio the pktio to which this PMR is to be applied >>>> + * @param dst_cos the CoS to be assigned by this PMR >>>> + * >>>> + * @return Success or ODP_PARAMETER_ERROR >>>> + */ >>>> + >>>> +int odp_pktio_pmr_cos(odp_pmr_t pmr_id, odp_pktio_t src_pktio, >>>> odp_cos_t dst_cos); >>>> +@endverbatim >>>> + >>>> +This routine links a pktio to a corresponding class of service via a >>>> specified PMR. >>>> +Any packet received on the specified src_pktio that matches the >>>> specified pmr_id will be assigned to the specified dst_cos. >>>> +If multiple PMRs match the implementation MAY define an inherent >>>> precedence or it MAY be unpredictable as to which PMR will determine the >>>> assigned CoS. >>>> +For this reason applications SHOULD NOT be written to use conflicting >>>> or ambiguous PMR definitions. >>>> + >>>> +@subsubsection cos_pmr_cos odp_cos_pmr_cos >>>> +@verbatim >>>> +/** >>>> + * Cascade a PMR to refine packets from one CoS to another. >>>> + * >>>> + * @param pmr_id the id of the PMR to be activated >>>> + * @param src_cos the id of the CoS to be filtered >>>> + * @param dst_cos the id of the CoS to be assigned to packets >>>> filtered >>>> + * from src_cos that match pmr_id. >>>> + * >>>> + * @return Success or ODP_PARAMETER_ERROR if an input is in error >>>> + * or ODP_IMPLEMENTATION_LIMIT if cascade depth is >>>> exceeded >>>> + */ >>>> + >>>> +int odp_cos_pmr_cos(odp_pmr_t pmr_id, odp_cos_t src_cos, odp_cos_t >>>> dst_cos); >>>> +@endverbatim >>>> + >>>> +This routine is used to cascade PMRs by passing packets assigned to >>>> the src_cos through another PMR. >>>> +Those matching are reassigned to the specified dst_cos. >>>> +Note that this process can be repeated to an implementation-defined >>>> maximum supported cascade depth. >>>> +When cascades are defined, the actual class of service assigned to a >>>> packet is the result of the longest chain of PMRs that can be matched >>>> against the packet. >>>> + >>>> +For example, suppose the following sequence of PMRs is in effect: >>>> + >>>> +@verbatim >>>> +odp_pktio_pmr_cos(pmr_idA, pktio_id, cos_idA); >>>> +odp_cos_pmr_cos(pmr_idB, cos_idA, cos_idB); >>>> +odp_cos_pmr_cos(pmr_idC, cos_idB, cos_idC); >>>> +odp_cos_pmr_cos(pmr_idD, cos_idC, cos_idD); >>>> +@endverbatim >>>> + >>>> +If a packet arrives on pktio_id that matches pmr_idA it is assigned to >>>> cos_idA. >>>> +But since it is now on cos_idA it is further filtered by pmr_idB and >>>> if it matches is reassigned to cos_idB. >>>> +This process continues until no further more specific match is found >>>> to determine the final CoS that the packet receives. >>>> + >>>> +Note that given this rule set a packet that matched pmr_idA and >>>> pmr_idC it would be assigned to cos_idA because the rule that can assign >>>> packets to pmr_idC is only applicable to packets that are assigned to >>>> cos_idB, not cos_idA. >>>> + >>>> +Using cascaded PMRs it is possible to build quite sophisticated >>>> filters (up to the implementation limits supported by a given platform). >>>> +For example, one could add additional rules to the above set: >>>> + >>>> +@verbatim >>>> +odp_cos_pmr_cos(pmr_idAC, cos_idA, cos_idC); >>>> +odp_cos_pmr_cos(pmr_idAD, cos_idA, cos_idD); >>>> +@endverbatim >>>> + >>>> +To cover cases where some packets on cos_idA should be further sorted >>>> to cos_idB while others should be sorted directly to cos_idC or cos_idD. >>>> +Again it is the application’s responsibility to ensure that the >>>> cascades remain unambiguous and that loops be avoided (e.g., having rules >>>> that bounce packets between cos_idA and cos_idB endlessly). >>>> + >>>> +@subsection pmr_stats PMR Statistics >>>> +Conforming ODP implementations SHOULD maintain statistics regarding >>>> PMRs and provide the following routines for retrieving them: >>>> + >>>> +@subsubsection pmr_match_count odp_pmr_match_count >>>> +@verbatim >>>> +/** >>>> + * Retrieve packet matcher statistics >>>> + * >>>> + * @param pmr_id the id of the PMR from which to retrieve the count >>>> + * >>>> + * @return The current number of matches for a given matcher instance. >>>> + */ >>>> + >>>> +signed long odp_pmr_match_count(odp_pmr_t pmr_id); >>>> +@endverbatim >>>> + >>>> +@subsubsection pmr_terms_cap odp_pmr_terms_cap >>>> +@verbatim >>>> +/** >>>> + * Inquire about matching terms supported by the classifier >>>> + * >>>> + * @return A mask one bit per enumerated term, one for each of >>>> op_pmr_term_e >>>> + */ >>>> + >>>> +unsigned long long odp_pmr_terms_cap(void); >>>> +@endverbatim >>>> + >>>> +@subsubsection pmr_terms_avail odp_pmr_terms_avail >>>> +@verbatim >>>> +/** >>>> + * Return the number of packet matching terms available for use >>>> + * >>>> + * @return A number of packet matcher resources available for use. >>>> + */ >>>> + >>>> +unsigned odp_pmr_terms_avail(void); >>>> +@endverbatim >>>> + >>>> +@subsection pmr_composite_rules Pattern Matching Composite Routines >>>> +As a shorthand, applications MAY express pattern matching rules using >>>> a table rather than constructing them term-by-term. >>>> +ODP implementations MUST support both methods of rule specification >>>> but MAY have implementation-specific restrictions on the complexity of >>>> table-based rules they support. >>>> +Note that some implementations MAY be able to implement tables >>>> directly while others MAY choose to implement tables by internally >>>> generating the equivalent set of term generating calls. >>>> + >>>> +@subsubsection pmr_table_structure PMR Table Structure >>>> +@verbatim >>>> +/** >>>> + * Following structure is used to define composite packet matching >>>> rules >>>> + * in the form of an array of individual match or range rules. >>>> + * The underlying platform may not support all or any specific >>>> combination >>>> + * of value match or range rules, and the application should take care >>>> + * of inspecting the return value when installing such rules, and >>>> perform >>>> + * appropriate fallback action. >>>> + */ >>>> + >>>> +typedef struct odp_pmr_match_t { >>>> + enum odp_pmr_match_type_e { >>>> + ODP_PMR_MASK, /**< Match a masked set of >>>> bits */ >>>> + ODP_PMR_RANGE, /**< Match an integer >>>> range */ >>>> + } match_type; >>>> + union { >>>> + struct { >>>> + enum odp_pmr_term_e term; >>>> + const void *val; >>>> + const void *mask; >>>> + unsigned int val_sz; >>>> + } mask; /**< Match a masked set of bits */ >>>> + struct { >>>> + enum odp_pmr_term_e term; >>>> + const void *val1; >>>> + const void *val2; >>>> + unsigned int val_sz; >>>> + } range; /**< Match an integer range */ >>>> + }; >>>> +} odp_pmr_match_t; >>>> + >>>> + >>>> +/** An opaque handle to a composite packet match rule-set */ >>>> +typedef struct odp_pmr_set_s *odp_pmr_set_t; >>>> +@endverbatim; >>>> + >>>> +The above structure is used with the following APIs to implement >>>> table-based PMRs: >>>> + >>>> +@subsubsection pmr_match_set_create odp_pmr_match_set_create >>>> +@verbatim >>>> +/** >>>> + * Create a composite packet match rule >>>> + * >>>> + * @param num_terms is the number of terms in the match rule. >>>> + * @param terms is an array of num_terms entries, one entry per >>>> + * term desired. >>>> + * @param dst_cos is the class-of-service to be assigned to packets >>>> + * that match the compound rule-set, or a subset >>>> thereof, >>>> + * if partly applied. >>>> + * @param pmr_set_id is the returned handle to the composite rule set. >>>> + * >>>> + * @return The return value may be a negative number indicating a >>>> general >>>> + * error, or a positive number indicating the number of ‘terms’ >>>> elements that >>>> + * have been successfully mapped to the underlying platform >>>> classification engine, >>>> + * and may be in the range from 1 to ‘num_terms’. >>>> + */ >>>> + >>>> +int odp_pmr_match_set_create(int num_terms, odp_pmr_match_t *terms, >>>> + odp_pmr_set_t *pmr_set_id); >>>> +@endverbatim >>>> + >>>> +This routine is used to create a PMR match set. >>>> + It is the equivalent to a cascade of PMRs except that there are no >>>> “intermediate” classes of service defined. >>>> +Instead, the entire match set either matches or does not match as a >>>> single entity. >>>> + >>>> +@subsubsection pmr_match_set_destroy odp_pmr_match_set_destroy >>>> +@verbatim >>>> +/** >>>> + * Function to delete a composite packet match rule set >>>> + * >>>> + * All of the resources pertaining to the match set associated with the >>>> + * class-of-service will be released, but the class-of-service will >>>> + * remain intact. >>>> + * >>>> + * @param pmr_set_id a composite rule-set handle returned when created. >>>> + * >>>> + * @note Depending on the implementation details, destroying a rule-set >>>> + * may not guarantee the availability of hardware resources to create >>>> the >>>> + * same or essentially similar rule-set. >>>> + */ >>>> + >>>> +int odp_pmr_match_set_destroy(odp_pmr_set_t pmr_set_id); >>>> +@endverbatim >>>> + >>>> +This routine destroys a PMR match set previously created by >>>> odp_pmr_match_set_create(). >>>> + >>>> +@subsubsection pktio_pmr_match_set_cos odp_pktio_pmr_match_set_cos >>>> +@verbatim >>>> +/** >>>> + * Apply a PMR Match Set to a pktio to assign a CoS. >>>> + * >>>> + * @param pmr_set_id the id of the PMR match set to be activated >>>> + * @param src_pktio the pktio to which this PMR match set is to be >>>> applied >>>> + * @param dst_cos the CoS to be assigned by this PMR match set >>>> + * >>>> + * @return Success or ODP_PARAMETER_ERROR >>>> + */ >>>> + >>>> +int odp_pktio_pmr_match_set_cos(odp_pmr_t pmr_id, odp_pktio_t >>>> src_pktio, >>>> + odp_cos_t dst_cos); >>>> +@endverbatim >>>> + >>>> +This routine is the same as odp_pktio_pmr_cos() except that it >>>> operates on PMR match sets rather than individual PMRs. >>>> + >>>> +@section items_pending Items pending resolution >>>> +- Revise ‘odp_packet_io.h’ API with respect of default input queue per >>>> ‘pktio’ instance. >>>> +- Revise ‘odp_queue.h’ API to support an arbitrary priority range, >>>> typically 8 priority levels with numeric priority values are >>>> platform-specific. >>>> +- Add specific packet meta data fields to go into packet buffer which >>>> contain all meta data fields parsed and generated by the classifier, for >>>> later application use. >>>> + >>>> +@section implementation_notes Implementation Notes >>>> +The following sections are not part of the specification, but shed >>>> light into the intent of the specification in several areas, describing >>>> some specific implementation approaches of these aspects. >>>> + >>>> +@subsection supporting_multi_pools Supporting multiple buffer pools >>>> +The support of multiple buffer pools for containing packet buffers is >>>> optional, and may not be supported by some platforms. >>>> +The importance of this feature stems from the need of protecting a >>>> networking application in the event of a congestion, or an attempted denial >>>> of service attack. >>>> +Separating different classes of service to dedicated buffer pools >>>> allows the system to limit the memory resources that may be consumed by a >>>> particular type of traffic, thereby reserving buffer resources for other >>>> classes of traffic. >>>> + >>>> +In a software implementation, a packet would already be stored in >>>> memory when the classifier is invoked, and so it seems the classifier is >>>> unable to insert itself into the process of selecting a buffer pool. >>>> +For obvious reasons the copying of a packet into a new buffer >>>> allocated from a different pool by the classifier is not a desirable >>>> solution. >>>> + >>>> +The recommended solution is to implement buffer pools in the form of >>>> buffer counters, while the actual buffers all belong to a single free list >>>> when not used to store a packet. >>>> +In such an implementation, the classifier will be able to associate a >>>> packet already occupying a buffer to a different pool than the default by >>>> incrementing the buffer counter of the newly selected pool, and >>>> decrementing the counter representing the default pool. >>>> +If however the selected pool counter has already reached a certain >>>> limit, the classifier would be able to e.g discard the packet instead of >>>> incrementing the destination pool counter, and thereby enforce the >>>> desirable semantics of distinct buffer pools per class of service. >>>> + >>>> +Other possible action that may be taken in response to running out of >>>> buffers or coming too low on buffers include back-pressure and >>>> random-early-detect with a discard probability inversely proportional to >>>> the number of free buffers in a pool. >>>> +A related implementation topic is the ability to begin dropping some >>>> packets before a buffer pool is entirely exhausted. >>>> +This is typically referred to as <em>Random Early Detect</em> (or >>>> “RED”). >>>> +This is deemed to be a feature of the buffer pool implementation on a >>>> given platform, where in addition to a hard limit on the number of buffers >>>> that can be allocated to a pool, there can also be an option discard >>>> packets with a probability the increases as the number of outstanding >>>> buffers approaches that hard limit. >>>> + >>>> +@subsection resolving_gaps Resolving gaps between the API and hardware >>>> capabilities >>>> +On platforms that support hardware packet accelerators, it is possible >>>> that the packet parsing and classification functionality is sufficient to >>>> address only a portion of the functionality specified within this document. >>>> +This gap may be potentially bridged by augmenting the hardware >>>> classification capabilities with a software logic implemented as part of >>>> the platform. >>>> +In that case, the platform will have to curve out a fraction of the >>>> processing resources and dedicate those to the software classification >>>> logic, which would be invoked for packets that the hardware platform was >>>> unable to classify completely. >>>> +At the time of this writing, it is believed however that the >>>> performance penalty that will be incurred as a result of software >>>> augmentation is unjustified for most application, i.e. >>>> +it is preferred to lose the precision of packet prioritization while >>>> maintaining full hardware packet processing speed. >>>> + >>>> +@subsection loopback_case The case for loopback ports, and some of >>>> their uses >>>> +In some applications, it may be desirable to be able to run a single >>>> packet through the classifier more than once. >>>> +For example, an encrypted IPsec packet is received from a physical >>>> port. >>>> +The encrypted packet is assigned a class of service based on its outer >>>> unencrypted header fields. >>>> +Later, processing the packet entails decrypting the payload of the >>>> packet, authenticating it, and removing the original outer headers, which >>>> reveals a new set of protocol headers which need to be used to re-classify >>>> the packet, and assign it a new priority and buffer pool. >>>> +An elegant solution for this use case would be to take advantage of >>>> “loopback” logical ports that may be implemented in certain platforms, by >>>> transmitting decapsulated packet into a loop-back port. >>>> +The same packet then is received from a loop-back port and is examined >>>> by the classifier in accordance to the rules assigned to the loopback >>>> odp_pktio logical port instance. >>>> +Similar mechanism may be applied to tunnel termination processing, >>>> fragment reassembly et al. >>>> + >>>> +@section related_topics Related Topics >>>> +The following section discusses aspects of the ODP API that are not >>>> integral to the classifier, which only applies to ingress preprocessing. >>>> +This section covers miscellaneous aspects of the API that need to be >>>> addressed, and are related to packet buffer processing and egress >>>> post-processing. >>>> +Additional packet buffer manipulation APIs >>>> +The need for these following calls are made evident by the need to >>>> encapsulate, i.e., remove some headers and add other, thereby changing the >>>> size of the headers of a packet during processing. >>>> + >>>> +@subsection initial_headroom Configuring initial packet buffer headroom >>>> +The following function is provided to configure the pktio receive >>>> mechanism to (optionally)reserve some headroom between start of the first >>>> buffer to the first byte of the first packet data byte, which subsequently >>>> could be used to increase the header size “in-place”, without allocating >>>> additional gather list elements. >>>> +If the request is granted, at least <req_bytes> bytes will be reserved >>>> in the front of the packet data: >>>> +@verbatim >>>> +int odp_pktio_set_headroom(odp_pktio_t port_id, unsigned req_bytes); >>>> +@endverbatim >>>> +The return value should be negative if the request can not be >>>> satisfied, or positive otherwise indicating the actual minimum headroom >>>> reserved. >>>> +Note that the implementation may reserve more than the requested >>>> amount of headroom, and hence on platforms that are unable to support >>>> per-port (or per CoS) headroom configuration, a system-wide headroom >>>> configuration may be set to the largest of all such requests, and thus >>>> satisfy the requirement. >>>> +In addition to the above per-port headroom configuration call, there >>>> should be an optional, per-CoS call that allows the reservation of >>>> different amounts of packet buffer headroom for packets that match certain >>>> criteria: for example, the following call allows the application to request >>>> that only packets that are expected to be encapsulated in a tunnel, be >>>> augmented with a large headroom amount, while packets that are received >>>> from a tunnel, and are IP fragments, be assigned a different headroom >>>> requirement (see definition for odp_cos_set_headroom() above. >>>> +Egress packet scheduling, prioritization and ordering >>>> + >>>> + >>>> +Open Issues >>>> +* Parallel matching rules relative precedence. >>>> +* Specify application-defined header field declaration APIs. >>>> +* Review RFC 4301 for match requirements for IPsec SA, consider the >>>> use of L4 port ranges instead of or in addition to value & mask matching >>>> criteria. >>>> +* Consider the type of packet checks should route a packet through the >>>> error CoS: L2 is a safe choice, but L3/L4 checksum or other exceptions >>>> deserve consideration. >>>> +Usage Examples >>>> +Following is a simple sample configuration using the API elements >>>> described above. >>>> +TBD. >>>> + >>>> +*/ >>>> diff --git a/images/classification_flow.png >>>> b/images/classification_flow.png >>>> new file mode 100644 >>>> index 0000000000000000000000000000000000000000.. >>>> 2d94ff64ec772aa97f3687181c121fb91d671889 >>>> GIT binary patch >>>> literal 35193 >>>> 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 >>>> >>> >> >> _______________________________________________ >> lng-odp mailing list >> lng-odp@lists.linaro.org >> http://lists.linaro.org/mailman/listinfo/lng-odp >> >> > > > -- > *Mike Holmes* > Linaro Technical Manager / Lead > LNG - ODP >
I reviewed the text after the testing and created a simple rule to find sentences that are not on their own line, as a proof of of one of the submission conditions we came up with to make the most of git being able to show sentence differences in the future. That test is here 'grep -E ".+\. " classification_design.dox' And it finds the following sentences that are followed by a space rather than a newline, obviously it is not a perfect tool, but in an interactive script it would be very easy to catch most cases. Unfortunately the cut and paste of the output does not highlight in RED the hits on the sentences that need a new line as bash does, so I manually highlighted the first few below We should run this on all submitted docs I think, it is easy to do upfront - 12 ish cases for this doc ? And it pays off down the road in my opinion, developing a tiny script may even make it automatic. The following API abstraction are not modelled after any existing product implementation, but is instead defined in terms of what a typical data-plane application may require from such a platform, without sacrificing simplicity and avoiding ambiguity. Certain terms that are being used within the context of existing products in relation to packet parsing and classification, such as “access lists” are avoided such that not to suggest any relationship between the abstraction used within this API and any particular manner in which they may be implemented in hardware. odp_pktio specifies an individual packet I/O channel instance. In other words, it would translate to a physical interface or a logical port, or in the case of channelized protocols (e.g., [Interlaken]( https://www.google.com/url?q=https%3A%2F%2Fwww.cortina-systems.com%2Fimages%2Fdocuments%2F400023_Interlaken_Technology_White_Paper.pdf&sa=D&sntz=1&usg=AFQjCNEBdJTBmA1XaNGY3pmumQTfgSi1oA)) it would map to a logical channel on that interface. Since the classifier API deals exclusively with ingress, this object represents the source of packets into the classifier. In order to support any non-trivial use case, the classifier API needs to be able to assign multiple odp_queue instances for any single odp_pktio object, and may also assign any odp_queue instance to more than one odp_pktio object. odp_queue specifies a logical queue for packets, and in the case of ingress, this would represent a stream of packets which share several attributes, that are delivered to the ODP application for processing. The per-queue attributes currently defined are: queue type, sync (ordering); priority; and schedule group (set of processor cores). odp_buffer_pool specifies a collection of buffers of same size and alignment, as well as a set of policies such as flow control and processor affinity. The classifier API refers to such pools that are designated for storing ingress packets. Following is the functionality that is required of the classifier API, and its underlying implementation. The details and order of the following paragraph is informative, and is only intended to help convey the functional scope of a classifier and provide context for the API. In reality, implementations may execute many of these steps concurrently, or in different order while maintaining the evident dependencies: -# Store these fields as packet meta data for application use, and for the remainder of parser operations. The \e odp_pktio is also stored as one of the meta data fields for subsequent use. -# Validate the packet data integrity (checksums, FCS) and correctness (e.g., length fields) and store the validation result, along with optional error layer and type indicator, in packet meta data. Optionally, if a packet fails validation, override the \e odp_cos selection in step 3 to a class of service designated for errored packets. The queue assignment is implemented as a separate function call such that the queue may be modified at any time, without tearing down the filters that define the class of service. In other words, it is possible to change the destination queue for a class of service defined by its filters quickly and dynamically. There SHOULD be one \b odp_cos assigned to each port with the \c odp_cos_pktio_set() function, which will function as the default class-of-service for all packets received from an ingress port, that do not match any of the filters defined subsequently. At minimum this default class-of-service MUST have a queue and a buffer pool assigned to it on platforms that support multiple packet buffer pools. Multiple odp_pktio instances (i.e., multiple ports) MAY each have their own default odp_cos, or MAY share a odp_cos with other ports, based on application requirements. -# The packet may be assigned a specific class-of-service based on its Layer-2 (802.1P/902.1Q VLAN tag) priority field. Since the standard field defines 8 discrete priority levels, the API allows to assign an odp_cos to each of these priority levels with the \c odp_cos_with_l2_priority() function. -# Similarly, a class-of-service may be assigned using the Layer-3 (IP DiffServ) header field. The application supplies an array of \e odp_cos values that covers the entire range of the standard protocol header field, where array elements do not need to contain unique values. There is also a need to specify if Layer-3 priority takes precedence over Layer-2 priority in a packet with both headers present. -# Additionally, the application may also program a number of \e pattern \e matching \e rules that assign a class-of-service for packets with header fields matching specified values. The field-matching rules take precedence over the previously described priority-based assignment of a class-of-service. Using these matching rules the application should be able for example to identify all packets containing VoIP traffic based on the protocol being UDP, and a specific destination or source port numbers, and appropriately assign these packets an class-of-service that maps to a higher priority queue, assuring voice packets a lower and bound latency. In addition to classifying packets and routing them to those queues with the appropriate priority, and optionally limiting their memory consumption by designating certain classes of packets to specific buffer pools, the classifier API also facilitates the scaling of data-plane application on multi-core systems by creating a mechanism to define which packet headers need to be combined to result in a value representing a specific packet flow. The classifier generates a signature, which can be a checksum or hash of arbitrary strength that covers those packet header fields that are identified by the application as identifying flows. This OPTIONAL function assigns a class-of-service used to handle packets containing various types of errors. The specific errors types include L2 FCS and optionally L3/L4 checksum errors, malformed headers, etc., depending on platform capabilities. This OPTIONAL function applies to ports that carry an additional headers preceding the standard Ethernet header. Such headers are typically vendor-specific and thus the classifier is not required to parse such headers, but the size of a custom header is critical for the classifier to be able to parse standard protocol headers that normally follow. * for use as headroom. Must not exceed the implementation ODP_COS_FHDR_L4_PROTO, /**< IP protocol (e.g. TCP/UDP/ICMP) */ * @return data-set that was successfully applied. All-zeros data set * @return data-set that was successfully applied. An all-zeros data-set On 4 August 2014 14:22, Bill Fischofer <bill.fischofer@linaro.org> wrote: > I originally tried to make these @code blocks and that's when I ran into > the formatting issue since @code doesn't like the embedded comments (at > least in 1.8.4). @verbatim seems to solve this issue. I'm happy to use > whatever works if someone has a better suggestion. > > I agree we ultimately want this to be sourced from the implementations but > the design doc is intended to guide implementers so we have a bit of > chicken-and-egg issue here. Perhaps we can do a 2nd pass on these once the > implementations are complete? > > Bill > > > On Mon, Aug 4, 2014 at 1:15 PM, Mike Holmes <mike.holmes@linaro.org> > wrote: > >> I ran with the very latest doxygen 1.8.7 and it is ok again, with doxygen >> 1.8.6 the V2 of this doc also fails so that looks like a bug that has been >> fixed in doxygen - I checked the release notes for a reason and nothing >> jumped out at me. >> >> If the @verbaitam blocks were going to stay I would say they should be >> @code blocks since the sections do describe code. However as soon as there >> is an implementation of this API doxygen will be able to take the API >> definitions from the code and we will be deleting them from here, so I >> think it is ok. >> >> Tested-By: Mike Holmes <mike.holmes@linaro.org> >> >> >> >> On 4 August 2014 14:00, Bill Fischofer <bill.fischofer@linaro.org> wrote: >> >>> Each sentence starts on a new line per the convention we agreed to >>> earlier, but this is input to doxygen converted from the Google docs and is >>> not otherwise line-wrapped. So it's whatever the Google output is. If we >>> have an automated tool to further modify the lines we can use that but >>> otherwise I don't see the value in doing a lot more manual editing on these. >>> >>> Bill >>> >>> >>> On Mon, Aug 4, 2014 at 12:42 PM, Maxim Uvarov <maxim.uvarov@linaro.org> >>> wrote: >>> >>>> How long are strings in that document? Maybe to fit them to 80 >>>> characters to better read in terminal? >>>> >>>> Maxim. >>>> >>>> On 08/04/2014 09:33 PM, Bill Fischofer wrote: >>>> >>>>> Switch to use of @verbatim/@endverbatim to avoid formatting issues >>>>> with different levels of doxygen. >>>>> >>>>> Signed-off-by: Bill Fischofer <bill.fischofer@linaro.org> >>>>> --- >>>>> classification_design.dox | 879 ++++++++++++++++++++++++++++++ >>>>> +++++++++++ >>>>> images/classification_flow.png | Bin 0 -> 35193 bytes >>>>> 2 files changed, 879 insertions(+) >>>>> create mode 100644 classification_design.dox >>>>> create mode 100644 images/classification_flow.png >>>>> >>>>> diff --git a/classification_design.dox b/classification_design.dox >>>>> new file mode 100644 >>>>> index 0000000..0cb7134 >>>>> --- /dev/null >>>>> +++ b/classification_design.dox >>>>> @@ -0,0 +1,879 @@ >>>>> +/* Copyright (c) 2014, Linaro Limited >>>>> + * All rights reserved >>>>> + * >>>>> + * SPDX-License-Identifier: BSD-3-Clause >>>>> + */ >>>>> + >>>>> +/*! >>>>> +@page classification_design ODP Design - Classification API >>>>> +For the implementation of the ODP classification API please see @ref >>>>> odp_classify.h >>>>> + >>>>> +@tableofcontents >>>>> + >>>>> +@section introduction Introduction >>>>> +This document defines the Classification APIs supported by ODP v1.0. >>>>> +Classification is logically composed of two stages: Parsing and Rule >>>>> Matching. >>>>> +Parsing takes a raw packet and validates its structure and identifies >>>>> fields of interest in the various headers that comprise the layers of the >>>>> packet. >>>>> +Rule Matching, in turn, takes the result of parsing and sorts packets >>>>> into Classes of Service (CoS) based on application-defined rule sets. >>>>> +@subsection use_of_terms Use of Terms >>>>> +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", >>>>> "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this >>>>> document are to be interpreted as described in [RFC 2119]( >>>>> https://tools.ietf.org/html/rfc21199). >>>>> +@subsection purpose Purpose >>>>> +ODP is a framework for software-based packet forwarding/filtering >>>>> applications, and the purpose of the Packet Classifier API is to enable >>>>> applications to program the platform hardware or software implementation to >>>>> assist in prioritization, classification and scheduling of each packet, so >>>>> that the software application can run faster, scale better and adhere to >>>>> QoS requirements. >>>>> +The following API abstraction are not modelled after any existing >>>>> product implementation, but is instead defined in terms of what a typical >>>>> data-plane application may require from such a platform, without >>>>> sacrificing simplicity and avoiding ambiguity. Certain terms that are being >>>>> used within the context of existing products in relation to packet parsing >>>>> and classification, such as “access lists” are avoided such that not to >>>>> suggest any relationship between the abstraction used within this API and >>>>> any particular manner in which they may be implemented in hardware. >>>>> +These are the key ODP objects that the parser needs to employ, that >>>>> are presently defined in ODP: >>>>> +@subsubsection odp_pktio odp_pktio >>>>> +odp_pktio specifies an individual packet I/O channel instance. In >>>>> other words, it would translate to a physical interface or a logical port, >>>>> or in the case of channelized protocols (e.g., [Interlaken]( >>>>> https://www.google.com/url?q=https%3A%2F%2Fwww.cortina-systems.com% >>>>> 2Fimages%2Fdocuments%2F400023_Interlaken_Technology_White_ >>>>> Paper.pdf&sa=D&sntz=1&usg=AFQjCNEBdJTBmA1XaNGY3pmumQTfgSi1oA)) it >>>>> would map to a logical channel on that interface. >>>>> +Since the classifier API deals exclusively with ingress, this object >>>>> represents the source of packets into the classifier. In order to support >>>>> any non-trivial use case, the classifier API needs to be able to assign >>>>> multiple odp_queue instances for any single odp_pktio object, and may also >>>>> assign any odp_queue instance to more than one odp_pktio object. >>>>> +@subsubsection odp_queue odp_queue >>>>> +odp_queue specifies a logical queue for packets, and in the case of >>>>> ingress, this would represent a stream of packets which share several >>>>> attributes, that are delivered to the ODP application for processing. The >>>>> per-queue attributes currently defined are: queue type, sync (ordering); >>>>> priority; and schedule group (set of processor cores). >>>>> +@subsubsection odp_buffer_pool odp_buffer_pool >>>>> +odp_buffer_pool specifies a collection of buffers of same size and >>>>> alignment, as well as a set of policies such as flow control and processor >>>>> affinity. The classifier API refers to such pools that are designated for >>>>> storing ingress packets. >>>>> +@section functional_description Functional Description >>>>> +Following is the functionality that is required of the classifier >>>>> API, and its underlying implementation. The details and order of the >>>>> following paragraph is informative, and is only intended to help convey the >>>>> functional scope of a classifier and provide context for the API. In >>>>> reality, implementations may execute many of these steps concurrently, or >>>>> in different order while maintaining the evident dependencies: >>>>> + >>>>> +-# Apply a set of \e classification \e rules to the header of an >>>>> incoming packet, identify the header fields, e.g., \e ethertype, IP >>>>> version, IP protocol, transport layer port numbers, IP DiffServ, VLAN id, >>>>> 802.1p priority. >>>>> + >>>>> +-# Store these fields as packet meta data for application use, and >>>>> for the remainder of parser operations. The \e odp_pktio is also stored as >>>>> one of the meta data fields for subsequent use. >>>>> + >>>>> +-# Compute an \e odp_cos (Class of Service) value from a subset of >>>>> supported fields from 1) above. >>>>> + >>>>> + >>>>> +-# Based on the \e odp_cos from 3) above, select the \e odp_queue >>>>> through which the packet is delivered to the application. >>>>> + >>>>> +-# Validate the packet data integrity (checksums, FCS) and >>>>> correctness (e.g., length fields) and store the validation result, along >>>>> with optional error layer and type indicator, in packet meta data. >>>>> Optionally, if a packet fails validation, override the \e odp_cos selection >>>>> in step 3 to a class of service designated for errored packets. >>>>> + >>>>> +-# Since the selected \e odp_queue may require preservation of packet >>>>> order, i.e., SYNC_ATOMIC or SYNC_ORDERED, optionally select the packet >>>>> header fields from which the parser calculates a \e odp_flow_signature, >>>>> which may be a unique flow identifier or a hash, such that the packets >>>>> which are assigned the same \e odp_flow_signature are scheduled in the same >>>>> order they are received. >>>>> + >>>>> +-# Based on the \e odp_cos from 3) above, select the \e >>>>> odp_buffer_pool that should be used to acquire a buffer to store the packet >>>>> data and meta data. >>>>> + >>>>> +-# Allocate a buffer from \e odp_buffer_pool selected in 6) above and >>>>> logically store the packet data and meta data to the allocated buffer, or >>>>> in accordance with class-of-service drop policy and subject to pool buffer >>>>> availability, optionally discard the packet. >>>>> + >>>>> +-# Enqueue the buffer into the \e odp_queue selected in 4) above. >>>>> + >>>>> +The above is an abstract description of the classifier functionality, >>>>> and may be applied to a variety of applications in many different ways. >>>>> +The ultimate meaning of how this functionality applies to an >>>>> application also depends on other ODP modules, so the above may not >>>>> complete a full depiction. >>>>> +For instance, the exact meaning of \e priority, which is a per-queue >>>>> attribute is influenced by the ODP scheduler semantics, and the system >>>>> behavior under stress depends on the ODP buffer pool module behavior. >>>>> + >>>>> +For the sole purpose of illustrating the above abstract >>>>> functionality, here is an example of a Layer-2 (IEEE 802.1D) bridge >>>>> application: >>>>> +Such a forwarding application that also adheres to IEEE 802.1p/q >>>>> priority, which has 8 traffic priority levels, might create 8 \e >>>>> odp_buffer_pool instances, one for each PCP priority level, and 8 \e >>>>> odp_queue instances one per priority level. >>>>> +Incoming packets will be inspected for a VLAN header; the PCP field >>>>> will be extracted, and used to select both the pool and the queue. >>>>> +Because each queue will be assigned a priority value, the packets >>>>> with highest PCP values will be scheduled before any packet with a lower >>>>> PCP value. >>>>> +Also, in a case of congestion, buffer pools for lower priority >>>>> packets will be depleted earlier than the pools containing packets of the >>>>> high priority, and hence the lower priority packets will be dropped >>>>> (assuming that is the only flow control method that is supported in the >>>>> platform) while higher priority packets will continue to be received into >>>>> buffers and processed. >>>>> +@subsection flow_diagram Classification Processing Flow Diagram >>>>> +@image html classification_flow.png "Figure 1: Classification Flow >>>>> Diagram" >>>>> +@image latex classification_flow.eps "Figure 1: Classification Flow >>>>> Diagram" >>>>> + >>>>> +@section api_elements API Elements >>>>> +While the above description refers to the abstracted packet >>>>> classifier, the following is the description of the API designed to program >>>>> the packet classifier, and is intended to add clarity to the functions >>>>> provided further below. >>>>> +@subsection cos_creation Class of Service Creation and Binding >>>>> +To program the classifier, a class-of-service instance must be >>>>> created, which will contain the packet filtering resources that it may >>>>> require. >>>>> +All subsequent calls refer to one or more of these resources. >>>>> +Each class of service instance must be associated with a single queue >>>>> or queue group, which will be the destination of all packets matching that >>>>> particular filter. >>>>> +The queue assignment is implemented as a separate function call such >>>>> that the queue may be modified at any time, without tearing down the >>>>> filters that define the class of service. In other words, it is possible to >>>>> change the destination queue for a class of service defined by its filters >>>>> quickly and dynamically. >>>>> +Optionally, on platforms that support multiple packet buffer pools, >>>>> each class of service may be assigned a different pool such that when >>>>> buffers are exhausted for one class of service, other classes are not >>>>> negatively impacted and continue to be processed. >>>>> + >>>>> +@subsection default_packet_handling Default packet handling >>>>> +There SHOULD be one \b odp_cos assigned to each port with the \c >>>>> odp_cos_pktio_set() function, which will function as the default >>>>> class-of-service for all packets received from an ingress port, that do not >>>>> match any of the filters defined subsequently. At minimum this default >>>>> class-of-service MUST have a queue and a buffer pool assigned to it on >>>>> platforms that support multiple packet buffer pools. Multiple odp_pktio >>>>> instances (i.e., multiple ports) MAY each have their own default odp_cos, >>>>> or MAY share a odp_cos with other ports, based on application requirements. >>>>> + >>>>> +@subsection packet_classification Packet Classification >>>>> +For each odp_pktio port, the API allows the assignment of a >>>>> class-of-service to a packet using one of three methods: >>>>> + >>>>> +-# The packet may be assigned a specific class-of-service based on >>>>> its Layer-2 (802.1P/902.1Q VLAN tag) priority field. Since the standard >>>>> field defines 8 discrete priority levels, the API allows to assign an >>>>> odp_cos to each of these priority levels with the \c >>>>> odp_cos_with_l2_priority() function. >>>>> + >>>>> +-# Similarly, a class-of-service may be assigned using the Layer-3 >>>>> (IP DiffServ) header field. The application supplies an array of \e odp_cos >>>>> values that covers the entire range of the standard protocol header field, >>>>> where array elements do not need to contain unique values. There is also a >>>>> need to specify if Layer-3 priority takes precedence over Layer-2 priority >>>>> in a packet with both headers present. >>>>> + >>>>> +-# Additionally, the application may also program a number of \e >>>>> pattern \e matching \e rules that assign a class-of-service for packets >>>>> with header fields matching specified values. The field-matching rules take >>>>> precedence over the previously described priority-based assignment of a >>>>> class-of-service. Using these matching rules the application should be able >>>>> for example to identify all packets containing VoIP traffic based on the >>>>> protocol being UDP, and a specific destination or source port numbers, and >>>>> appropriately assign these packets an class-of-service that maps to a >>>>> higher priority queue, assuring voice packets a lower and bound latency. >>>>> + >>>>> +@subsection scaling_and_flow Scaling and Flow Discrimination >>>>> +In addition to classifying packets and routing them to those queues >>>>> with the appropriate priority, and optionally limiting their memory >>>>> consumption by designating certain classes of packets to specific buffer >>>>> pools, the classifier API also facilitates the scaling of data-plane >>>>> application on multi-core systems by creating a mechanism to define which >>>>> packet headers need to be combined to result in a value representing a >>>>> specific packet flow. The classifier generates a signature, which can be a >>>>> checksum or hash of arbitrary strength that covers those packet header >>>>> fields that are identified by the application as identifying flows. >>>>> + >>>>> +The \e flow \e signatures that result from hashing are then stored >>>>> with the packet meta data (along with its class-of-service and its ingress >>>>> \e odp_pktio port), and subsequently may be utilized by the implementation >>>>> of a scheduler queue to maintain the order of packets with the same flow >>>>> signature, while allowing packets with different signatures to be processed >>>>> concurrently and independently on different processing cores. >>>>> + >>>>> +@subsection packet_meta_data Packet meta data Elements >>>>> +Here are the specific information elements that SHOULD be stored >>>>> within the packet meta data structure: >>>>> +- Protocol fields that are decoded and extracted by the parsing phase >>>>> +- Flow-signature calculated from a prescribed collection of protocol >>>>> fields >>>>> +- The class-of-service identifier that is selected for the packet >>>>> +- The ingress port identifier >>>>> +- The result of packet validation, including an indication of the >>>>> type of error detected, if any >>>>> + >>>>> +The ODP packet API module SHALL provide accessors for retrieving the >>>>> above meta data fields from the container buffer in an >>>>> implementation-independent manner. >>>>> + >>>>> +@section api_definitions API Definitions >>>>> +@subsection data_types Data Types >>>>> +The following data types are referenced in the API descriptions >>>>> described below. >>>>> +The names are part of the ODP API and MUST be present in any >>>>> conforming implementation, however the type values shown here are >>>>> illustrative and implementations SHOULD either use these or substitute >>>>> their own type values that are appropriate to the underlying platform. >>>>> + >>>>> +@verbatim >>>>> +/** >>>>> + * 'odp_pktio_t' value to indicate any port >>>>> + */ >>>>> +#define ODP_PKTIO_ANY ((odp_pktio_t)~0) >>>>> + >>>>> + >>>>> +/** >>>>> + * 'odp_pktio_t' value to indicate an error >>>>> + */ >>>>> +#define ODP_PKTIO_INVALID ((odp_pktio_t)0) >>>>> + >>>>> + >>>>> +/** >>>>> + * Class of service instance type >>>>> + */ >>>>> +typedef uint32_t odp_cos_t; >>>>> + >>>>> + >>>>> +/** >>>>> + * flow signature type, only used for packet meta data field. >>>>> + */ >>>>> +typedef uint32_t odp_flowsig_t; >>>>> + >>>>> + >>>>> +/** >>>>> + * This value is returned from odp_cos_create() on failure, >>>>> + * May also be used as a “sink” class of service that >>>>> + * results in packets being discarded. >>>>> + */ >>>>> +#define ODP_COS_INVALID ((odp_cos_t)~0) >>>>> +@endverbatim >>>>> + >>>>> +@subsection cos_routines Class of Service Routines >>>>> +Conforming ODP implementations MUST provide the following >>>>> Classification APIs: >>>>> +@subsubsection cos_create odp_cos_create >>>>> +@verbatim >>>>> +/** >>>>> + * Create a class-of-service >>>>> + * >>>>> + * @param name is a string intended for debugging purposes. >>>>> + * >>>>> + * @return Class of service instance identifier, >>>>> + * or ODP_COS_INVALID on error. >>>>> + */ >>>>> + >>>>> +odp_cos_t odp_cos_create(const char *name); >>>>> +@endverbatim >>>>> + >>>>> +This routine is used to create a class of service that can be the >>>>> target of classifier rules. >>>>> +The number of such classes supported is implementation-defined. >>>>> +Attempts to create more than are supported by the implementation will >>>>> result in an \c ODP_COS_INVALID return and errno being set to \c >>>>> ODP_IMPLEMENTATION_LIMIT. >>>>> + >>>>> +@subsubsection cos_destroy odp_cos_destroy >>>>> +@verbatim >>>>> +/** >>>>> + * Discard a class-of-service along with all its associated resources >>>>> + * >>>>> + * @param cos_id class-of-service instance. >>>>> + * >>>>> + * @return 0 on success, -1 on error. >>>>> + */ >>>>> + >>>>> +int odp_cos_destroy(odp_cos_t cos_id); >>>>> +@endverbatim >>>>> + >>>>> +This routine is the bracketing routine for odp_cos_create(). >>>>> +It is used to destroy an existing CoS. >>>>> +It is the caller’s responsibility to ensure that no active pattern >>>>> matching rules refer to the CoS prior to calling this routine. >>>>> +Results are unpredictable if this restriction is not met. >>>>> +@subsubsection cos_set_queue odp_cos_set_queue >>>>> +@verbatim >>>>> +/** >>>>> + * Assign a queue for a class-of-service >>>>> + * >>>>> + * @param cos_id class-of-service instance. >>>>> + * >>>>> + * @param queue_id is the identifier of a queue where all >>>>> packets >>>>> + * of this specific class of service will be enqueued. >>>>> + * >>>>> + * @return 0 on success, negative error code on failure. >>>>> + */ >>>>> + >>>>> +int odp_cos_set_queue(odp_cos_t cos_id, odp_queue_t queue_id); >>>>> +@endverbatim >>>>> + >>>>> +This routine associates a target queue with a CoS such that all >>>>> packets assigned to this CoS will be enqueued to the specified queue_id at >>>>> the end of classification processing. >>>>> +@subsubsection cos_set_queue_group odp_cos_set_queue_group >>>>> +@verbatim >>>>> +/** >>>>> + * Assign a homogenous queue-group to a class-of-service. >>>>> + * >>>>> + * @param cos_id identifier of class-of-service instance >>>>> + * @param queue_group_id identifier of the queue group to receive >>>>> packets >>>>> + * associated with this class of service. >>>>> + * >>>>> + * @return 0 on success, negative error code on failure. >>>>> + */ >>>>> + >>>>> +int odp_cos_set_queue_group(odp_cos_t cos_id, odp_queue_group_t >>>>> queue_group_id); >>>>> +@endverbatim >>>>> + >>>>> +This routine associates a target queue group with a CoS such that all >>>>> packets assigned to this CoS will be distributed to the specified >>>>> queue_group_id at the end of classification processing. >>>>> +@subsubsection cos_set_pool odp_cos_set_pool >>>>> +@verbatim >>>>> +/** >>>>> + * Assign packet buffer pool for specific class-of-service >>>>> + * >>>>> + * @param cos_id class-of-service instance. >>>>> + * @param pool_id is a buffer pool identifier where all packet buffers >>>>> + * will be sourced to store packet that belong to this >>>>> + * class of service. >>>>> + * >>>>> + * @return 0 on success negative error code on failure. >>>>> + * >>>>> + * >>>>> + */ >>>>> + >>>>> +int odp_cos_set_pool(odp_cos_t cos_id, odp_buffer_pool_t pool_id); >>>>> +@endverbatim >>>>> + >>>>> +This OPTIONAL routine associates a target buffer pool with a CoS such >>>>> that all packets assigned to this CoS will be stored in packet buffers >>>>> allocated from the designated pool_id. >>>>> + >>>>> + >>>>> +@subsection cos_drop_policy Class of Service Drop Policy Routines >>>>> +These routines control how drop policies are to be observed for a >>>>> given class of service. >>>>> +@subsubsection drop_data_types Data types >>>>> +~~~~~{.c} >>>>> +enum odp_cos_drop_e { >>>>> + ODP_COS_DROP_POOL, /**< Follow buffer pool drop policy >>>>> */ >>>>> + ODP_COS_DROP_NEVER, /**< Never drop, ignoring buffer >>>>> pool policy */ >>>>> +}; >>>>> +typedef enum odp_drop_e odp_drop_t; >>>>> +~~~~~ >>>>> + >>>>> +@subsubsection cos_set_drop odp_cos_set_drop >>>>> +@verbatim >>>>> +/** >>>>> + * Assign packet drop policy for specific class-of-service >>>>> + * >>>>> + * @param cos_id class-of-service instance. >>>>> + * @param drop_policy is the desired packet drop policy for this >>>>> class. >>>>> + * >>>>> + * @return 0 on success negative error code on failure. >>>>> + */ >>>>> + >>>>> +int odp_cos_set_drop(odp_cos_t cos_id, odp_drop_t drop_policy); >>>>> +@endverbatim >>>>> + >>>>> +This routine sets the drop policy for a class of service. >>>>> +It is an OPTIONAL routine. >>>>> +If an implementation does not provide this function it MUST supply a >>>>> definition of it that simply returns ODP_FUNCTION_NOT_AVAILABLE. >>>>> +@subsubsection pktio_set_default_cos odp_pktio_set_default_cos >>>>> +@verbatim >>>>> +/** >>>>> + * Setup per-port default class-of-service >>>>> + * >>>>> + * @param pktio_in ingress port identifier. >>>>> + * @param default_cos class-of-service set to all packets arriving >>>>> + * at the 'pktio_in' ingress port, unless overridden by >>>>> subsequent >>>>> + * header-based filters. >>>>> + * >>>>> + * @return 0 on success negative error code on failure. >>>>> + * >>>>> + * >>>>> + * @note This may replace the default queue per pktio. >>>>> + */ >>>>> + >>>>> +int odp_pktio_set_default_cos(odp_pktio_t pktio_in, odp_cos_t >>>>> default_cos); >>>>> +@endverbatim >>>>> + >>>>> +This routine specifies a default class of service for a given pktio >>>>> instance. >>>>> +Incoming packets on the specified pktio are assigned to this class of >>>>> service if no other pattern matching rule obtains. >>>>> +@subsubsection pktio_set_error_cos odp_pktio_set_error_cos >>>>> +@verbatim >>>>> +/** >>>>> + * Setup per-port error class-of-service >>>>> + * >>>>> + * @param pktio_in ingress port identifier. >>>>> + * @param error_cos class-of-service set to all packets arriving >>>>> + * at the 'pktio_in' ingress port that contain an error. >>>>> + * >>>>> + * @return 0 on success negative error code on failure. >>>>> + */ >>>>> + >>>>> +int odp_pktio_set_error_cos(odp_pktio_t pktio_in, odp_cos_t >>>>> error_cos); >>>>> +@endverbatim >>>>> + >>>>> +This OPTIONAL function assigns a class-of-service used to handle >>>>> packets containing various types of errors. The specific errors types >>>>> include L2 FCS and optionally L3/L4 checksum errors, malformed headers, >>>>> etc., depending on platform capabilities. >>>>> +The specified error_cos MAY simply discard these packets or deliver >>>>> them via a queue to the application for further processing. >>>>> +@subsubsection pktio_set_skip odp_pktio_set_skip >>>>> +@verbatim >>>>> +/** >>>>> + * Setup per-port header offset >>>>> + * >>>>> + * @param pktio_in ingress port identifier. >>>>> + * @param offset is the number of bytes the classifier must skip. >>>>> + * >>>>> + * @return Success or ODP_FUNCTION_NOT_AVAILABLE >>>>> + */ >>>>> + >>>>> +int odp_pktio_set_skip(odp_pktio_t pktio_in, size_t offset); >>>>> +@endverbatim >>>>> + >>>>> +This OPTIONAL function applies to ports that carry an additional >>>>> headers preceding the standard Ethernet header. Such headers are typically >>>>> vendor-specific and thus the classifier is not required to parse such >>>>> headers, but the size of a custom header is critical for the classifier to >>>>> be able to parse standard protocol headers that normally follow. >>>>> +@subsubsection cos_set_headroom odp_cos_set_headroom >>>>> +@verbatim >>>>> +/** >>>>> + * Specify per-port buffer headroom >>>>> + * >>>>> + * @param pktio_in ingress port identifier. >>>>> + * @param headroom number of bytes of space preceding packet data >>>>> to reserve >>>>> + * for use as headroom. Must not exceed the >>>>> implementation >>>>> + * defined ODP_PACKET_MAX_HEADROOM. >>>>> + * >>>>> + * @return Success or ODP_PARAMETER_ERROR, >>>>> + * or ODP_FUNCTION_NOT_AVAILABLE >>>>> + */ >>>>> + >>>>> +int odp_cos_set_headroom(odp_cos_t cos_id, size_t req_room); >>>>> +@endverbatim >>>>> + >>>>> +This OPTIONAL routine specifies the number of bytes of headroom that >>>>> should be reserved for each packet assigned to this class of service. >>>>> +Each implementation defines an ODP_PACKET_MAX_HEADROOM limit that >>>>> sets an upper bound on the size of the headroom that can be reserved for a >>>>> packet. >>>>> +@subsubsection cos_with_l2_priority odp_cos_with_l2_priority >>>>> +@verbatim >>>>> +/** >>>>> + * Request to override per-port class of service >>>>> + * based on Layer-2 priority field if present. >>>>> + * >>>>> + * @param pktio_in ingress port identifier. >>>>> + * @param num_qos is the number of QoS levels, typically 8. >>>>> + * @param qos_table are the values of the Layer-2 QoS header field. >>>>> + * @param cos_table is the class-of-service assigned to each of the >>>>> + * allowed Layer-2 QOS levels. >>>>> + * @return 0 on success negative error code on failure. >>>>> + */ >>>>> + >>>>> +int odp_cos_with_l2_priority(odp_pktio_t pktio_in, >>>>> + size_t num_qos, >>>>> + uint8_t qos_table[], /**< >>>>> 'num_qos' elements */ >>>>> + odp_cos_t cos_table[]); /**< >>>>> 'num_qos' elements */ >>>>> +@endverbatim >>>>> + >>>>> +This routine is used to assign classes of service based on the layer >>>>> 2 (L2) priority associated with input packets received on the specified >>>>> pktio_in. >>>>> +For each of the values in qos_table[], the corresponding value in >>>>> cos_table[] will be assigned. >>>>> +@subsubsection cos_with_l3_dscp odp_cos_with_l3_dscp >>>>> +@verbatim >>>>> +/** >>>>> + * >>>>> + * @param pktio_in ingress port identifier. >>>>> + * @param num_qos is the number of allowed Layer-3 QoS levels. >>>>> + * @param qos_table are the values of the Layer-3 QoS header field. >>>>> + * @param cos_table is the class-of-service assigned to each of the >>>>> + * allowed Layer-3 QOS levels. >>>>> + * @param l3_preference when true, Layer-3 QoS overrides L2 QoS >>>>> when present. >>>>> + * >>>>> + * @return 0 on success negative error code on failure. >>>>> + */ >>>>> + >>>>> +int odp_cos_with_l3_qos(odp_pktio_t pktio_in, >>>>> + size_t num_qos, >>>>> + uint8_t qos_table[], /**< 'num_qos' >>>>> elements */ >>>>> + odp_cos_t cos_table[], /**< >>>>> 'num_qos' elements */ >>>>> + odp_bool_t l3_preference); >>>>> +@endverbatim >>>>> + >>>>> +This OPTIONAL routine is used to assign classes of service based on >>>>> the layer 3 (L3) Differentiated Services (DS) designation. >>>>> +This is the DSCP field of an IPv4 header or the first six bits of the >>>>> Traffic Class of an IPv6 header. >>>>> +For each of the values in qos_table[], the corresponding value in >>>>> cos_table[] will be assigned. >>>>> +The l3_preference flag is use to control whether the CoS assigned by >>>>> this routine takes precedence over the CoS assigned by >>>>> odp_cos_with_l2_priority() in the event that both apply to the same packet. >>>>> + >>>>> +@subsection pmrs Pattern Matching Rules >>>>> +While the above routines permit class of service assignments to be >>>>> made based on static criteria, the real power of classification is the >>>>> ability to identify flows based on the variable contents of packet headers. >>>>> +To do this ODP provides support for defining pattern matching rules >>>>> (PMRs) that operate based on values contained in specified header fields. >>>>> + >>>>> +Associated with PMRs are enums that are used to specify standard >>>>> packet header fields: >>>>> +@subsubsection cos_hdr_flow_fields odp_cos_hdr_flow_fields_e >>>>> +@verbatim >>>>> +/** >>>>> + * Packet header field enumeration >>>>> + * for fields that may be used to calculate >>>>> + * the flow signature, if present in a packet. >>>>> + */ >>>>> + >>>>> +enum odp_cos_hdr_flow_fields_e { >>>>> + ODP_COS_FHDR_IN_PKTIO, /**< Ingress port number */ >>>>> + ODP_COS_FHDR_L2_SAP, /**< Ethernet Source MAC >>>>> address */ >>>>> + ODP_COS_FHDR_L2_DAP, /**< Ethernet Destination MAC >>>>> address */ >>>>> + ODP_COS_FHDR_L2_VID, /**< Ethernet VLAN ID */ >>>>> + ODP_COS_FHDR_L3_FLOW /**< IPv6 flow_id */ >>>>> + ODP_COS_FHDR_L3_SAP, /**< IP source address */ >>>>> + ODP_COS_FHDR_L3_DAP, /**< IP destination address */ >>>>> + ODP_COS_FHDR_L4_PROTO, /**< IP protocol (e.g. >>>>> TCP/UDP/ICMP) */ >>>>> + ODP_COS_FHDR_L4_SAP, /**< Transport source port */ >>>>> + ODP_COS_FHDR_L4_DAP, /**< Transport destination port >>>>> */ >>>>> + ODP_COS_FHDR_IPSEC_SPI, /**< IPsec session identifier */ >>>>> + ODP_COS_FHDR_LD_VNI, /**< NVGRE/VXLAN network >>>>> identifier */ >>>>> + ODP_COS_FHDR_USER /**< Application-specific >>>>> header field(s) */ >>>>> +}; >>>>> +@endverbatim >>>>> + >>>>> +Conforming ODP implementations SHOULD implement efficient flow set >>>>> management routines such as these: >>>>> + >>>>> +~~~~~{.c} >>>>> +/** >>>>> + * Set of header fields that take part in flow signature hash >>>>> calculation: >>>>> + * bit positions per 'odp_cos_hdr_flow_fields_e' enumeration. >>>>> + * >>>>> +typedef uint16_t odp_cos_flow_set_t; >>>>> + >>>>> + >>>>> +/** >>>>> + * Set a member of the flow signature fields data set >>>>> + * >>>>> +static inline odp_cos_flow_set_t >>>>> +odp_cos_flow_set( odp_cos_flow_set_t set, >>>>> + enum odp_cos_hdr_flow_fields_e field) >>>>> +{ >>>>> + return set | (1U << field); >>>>> +} >>>>> + >>>>> + >>>>> +/** >>>>> + * Test a member of the flow signature fields data set >>>>> + * >>>>> +static inline bool >>>>> +odp_cos_flow_is_set( odp_cos_flow_set_t set, >>>>> + enum odp_cos_hdr_flow_fields_e field) >>>>> +{ >>>>> + return (set & (1U << field)) != 0; >>>>> +} >>>>> +~~~~~ >>>>> + >>>>> +These routines are intended to be used in support of the following >>>>> flow signature APIs: >>>>> + >>>>> +@subsubsection cos_class_flow_sig odp_cos_class_flow_signature >>>>> +@verbatim >>>>> +/** >>>>> + * Set up set of headers used to calculate a flow signature >>>>> + * based on class-of-service. >>>>> + * >>>>> + * @param cos_id class of service instance identifier >>>>> + * @param req_data_set requested data-set for flow signature >>>>> calculation >>>>> + * >>>>> + * @return data-set that was successfully applied. All-zeros data set >>>>> + * indicates a failure to assign any of the requested fields, or other >>>>> + * error. >>>>> + */ >>>>> + >>>>> +odp_cos_flow_set_t >>>>> +odp_cos_class_flow_signature(odp_cos_t cos_id, >>>>> + odp_cos_flow_set_t req_data_set); >>>>> +@endverbatim >>>>> + >>>>> +This OPTIONAL routine associates a fow set with a class of service >>>>> for flow signature calculation. >>>>> + >>>>> +@subsubsection cos_port_flow_sig odp_cos_port_flow_signature >>>>> +@verbatim >>>>> +/** >>>>> + * Set up set of headers used to calculate a flow signature >>>>> + * based on ingress port. >>>>> + * >>>>> + * @param pktio_in ingress port identifier. >>>>> + * @param req_data_set requested data-set for flow signature >>>>> calculation >>>>> + * >>>>> + * @return data-set that was successfully applied. An all-zeros >>>>> data-set >>>>> + * indicates a failure to assign any of the requested fields, or other >>>>> + * error. >>>>> + */ >>>>> + >>>>> +odp_cos_flow_set_t >>>>> +odp_cos_port_flow_signature(odp_pktio_t pktio_in, >>>>> + odp_cos_flow_set_t req_data_set); >>>>> +@endverbatim >>>>> + >>>>> +@subsection pmr_routines Pattern Matching Rules Routines >>>>> +The following data structures SHOULD be implemented to support the >>>>> definition of pattern matching routines by conforming ODP implementations: >>>>> + >>>>> +~~~~~{.c} >>>>> +/** >>>>> + * PMR - Packet Matching Rule >>>>> + * Up to 32 bit of ternary matching of one of the available header >>>>> fields >>>>> + * >>>>> + >>>>> + >>>>> +#define ODP_PMR_INVAL ((odp_pmr_t)NULL) >>>>> +typedef struct odp_pmr_s *odp_pmr_t; >>>>> +~~~~~ >>>>> + >>>>> +@subsecion terms Terms >>>>> +Terms are the elements of a PMR and are identified by the following >>>>> enum: >>>>> + >>>>> +@verbatim >>>>> +enum odp_pmr_term_e { >>>>> + ODP_PMR_ETHTYPE_0, /**< Initial (outer) Ethertype only >>>>> (*val=uint16_t)*/ >>>>> + ODP_PMR_ETHTYPE_X, /**< Ethertype of most inner VLAN tag >>>>> (*val=uint16_t)*/ >>>>> + ODP_PMR_VLAN_ID_0, /**< First VLAN ID (outer) >>>>> (*val=uint16_t) */ >>>>> + ODP_PMR_VLAN_ID_X, /**< Last VLAN ID (inner) >>>>> (*val=uint16_t) */ >>>>> + ODP_PMR_DMAC, /**< destination MAC address >>>>> (*val=uint64_t) */ >>>>> + ODP_PMR_IPPROTO, /**< IP Protocol or IPv6 Next Header >>>>> (*val=uint8_t) */ >>>>> + ODP_PMR_UDP_DPORT, /**< Destination UDP port, implies >>>>> IPPROTO=17 */ >>>>> + ODP_PMR_TCP_DPORT, /**< Destination TCP port implies >>>>> IPPROTO=6 */ >>>>> + ODP_PMR_UDP_SPORT, /**< Source UDP Port (*val=uint16_t) */ >>>>> + ODP_PMR_TCP_SPORT, /**< Source TCP port (*val=uint16_t) */ >>>>> + ODP_PMR_SIP_ADDR, /**< Source IP address (uint32_t) */ >>>>> + ODP_PMR_DIP_ADDR, /**< Destination IP address (uint32_t) >>>>> */ >>>>> + ODP_PMR_SIP6_ADDR, /**< Source IP address (uint8_t[16]) */ >>>>> + ODP_PMR_DIP6_ADDR, /**< Destination IP address >>>>> (uint8_t[16]) */ >>>>> + ODP_PMR_IPSEC_SPI, /**< IPsec session >>>>> identifier(*val=uint32_t) */ >>>>> + ODP_PMR_LD_VNI, /**< NVGRE/VXLAN network identifier >>>>> (*val=uint32_t) */ >>>>> + >>>>> + >>>>> + /** Inner header may repeat above values with this offset */ >>>>> + ODP_PMR_INNER_HDR_OFF=32 >>>>> +}; >>>>> +@endverbatim >>>>> + >>>>> +@subsubsection tunnel_considerations Tunnel Considerations >>>>> +Note that PMRs may be extended to support tunnels and tenants >>>>> (NVGRE, VXLAN) via the ODP_PMR_INNER_HDR_OFF enum. >>>>> +This enum is intended to be used as an “adder” to a PMR to indicate >>>>> that the term refers to an inner header. >>>>> +For example, the term ODP_PMR_DMAC would refer to the destination MAC >>>>> address of the packet if the packet is not a tunnel, or of the outer header >>>>> (the tunnel) if the packet is a tunnel. >>>>> +To refer to the inner (tenant) destination MAC, the term would be >>>>> specified as ODP_PMR_INNER_HDR_OFF+ODP_PMR_DMAC. >>>>> + >>>>> +@subsection pmr_apis PMR APIs >>>>> +The following APIs are provided to enable an ODP application to >>>>> specify PMRs as a series of individual or cascaded terms: >>>>> +@subsubsection pmr_create_match odp_pmr_create_match >>>>> +@verbatim >>>>> +/** >>>>> + * Create a packet match rule with mask and value >>>>> + * >>>>> + * @param term is one value of the enumerated values supported >>>>> + * @param val is the value to match against the packet header >>>>> + * in native byte order. >>>>> + * @param mask is the mask to indicate which bits of the header >>>>> + * should be matched ('1') and which should be >>>>> ignored ('0') >>>>> + * @param val_sz size of the ‘val’ and ‘mask’ arguments, >>>>> + * that must match the value size requirement of the >>>>> + * specific ‘term’. >>>>> + * >>>>> + * @return a handle of the matching rule or ODP_PMR_INVAL on error >>>>> + */ >>>>> + >>>>> +odp_pmr_t odp_pmr_create_match(enum odp_pmr_term_e term, >>>>> + const void *val, const void *mask, >>>>> size_t val_sz); >>>>> +@endverbatim >>>>> + >>>>> +This routine creates a PMR that matches a single value to a term. >>>>> + >>>>> +@subsubsection pmr_create_range odp_pmr_create_range >>>>> +@verbatim >>>>> +/** >>>>> + * Create a packet match rule with value range >>>>> + * >>>>> + * @param term is one value of the enumerated values supported >>>>> + * @param val1 is the lower bound of the header field range. >>>>> + * @param val2 is the upper bound of the header field range. >>>>> + * @param val_sz size of the ‘val1’ and ‘val2’ arguments, >>>>> + * that must match the value size requirement of the >>>>> + * specific ‘term’. >>>>> + * >>>>> + * @return a handle of the matching rule or ODP_PMR_INVAL on error >>>>> + * @note: Range is inclusive [val1..val2]. >>>>> + */ >>>>> + >>>>> +odp_pmr_t odp_pmr_create_range(enum odp_pmr_term_e term, >>>>> + const void *val1, const void >>>>> *val2, size_t val_sz); >>>>> +@endverbatim >>>>> + >>>>> +This routine creates a PMR that matches an inclusive range of values >>>>> to a term. >>>>> + >>>>> +@subsubsection pmr_destroy odp_pmr_destroy >>>>> +@verbatim >>>>> +/** >>>>> + * Invalidate a packet match rule and vacate its resources >>>>> + * >>>>> + * @param pmr_id the identifier of the PMR to be destroyed >>>>> + * >>>>> + * @return Success or ODP_PMR_INVALID if the specified pmr_id not >>>>> found. >>>>> + */ >>>>> + >>>>> +int odp_pmr_destroy(odp_omr_t pmr_id); >>>>> +@endverbatim >>>>> + >>>>> +This routine destroys a previously created PMR. >>>>> +If the PMR is currently associated with an active class of service it >>>>> is unpredictable at which point the match defined by the PMR is deactivated >>>>> in terms of packet flow. >>>>> +However, implementations MUST ensure that a PMR is either matched or >>>>> not matched in its entirety such that dynamic changes to PMRs do not result >>>>> in partial matches. >>>>> + >>>>> +@subsubsection pktio_pmr_cos odp_pktio_pmr_cos >>>>> +@verbatim >>>>> +/** >>>>> + * Apply a PMR to a pktio to assign a CoS. >>>>> + * >>>>> + * @param pmr_id the id of the PMR to be activated >>>>> + * @param src_pktio the pktio to which this PMR is to be applied >>>>> + * @param dst_cos the CoS to be assigned by this PMR >>>>> + * >>>>> + * @return Success or ODP_PARAMETER_ERROR >>>>> + */ >>>>> + >>>>> +int odp_pktio_pmr_cos(odp_pmr_t pmr_id, odp_pktio_t src_pktio, >>>>> odp_cos_t dst_cos); >>>>> +@endverbatim >>>>> + >>>>> +This routine links a pktio to a corresponding class of service via a >>>>> specified PMR. >>>>> +Any packet received on the specified src_pktio that matches the >>>>> specified pmr_id will be assigned to the specified dst_cos. >>>>> +If multiple PMRs match the implementation MAY define an inherent >>>>> precedence or it MAY be unpredictable as to which PMR will determine the >>>>> assigned CoS. >>>>> +For this reason applications SHOULD NOT be written to use conflicting >>>>> or ambiguous PMR definitions. >>>>> + >>>>> +@subsubsection cos_pmr_cos odp_cos_pmr_cos >>>>> +@verbatim >>>>> +/** >>>>> + * Cascade a PMR to refine packets from one CoS to another. >>>>> + * >>>>> + * @param pmr_id the id of the PMR to be activated >>>>> + * @param src_cos the id of the CoS to be filtered >>>>> + * @param dst_cos the id of the CoS to be assigned to packets >>>>> filtered >>>>> + * from src_cos that match pmr_id. >>>>> + * >>>>> + * @return Success or ODP_PARAMETER_ERROR if an input is in error >>>>> + * or ODP_IMPLEMENTATION_LIMIT if cascade depth is >>>>> exceeded >>>>> + */ >>>>> + >>>>> +int odp_cos_pmr_cos(odp_pmr_t pmr_id, odp_cos_t src_cos, odp_cos_t >>>>> dst_cos); >>>>> +@endverbatim >>>>> + >>>>> +This routine is used to cascade PMRs by passing packets assigned to >>>>> the src_cos through another PMR. >>>>> +Those matching are reassigned to the specified dst_cos. >>>>> +Note that this process can be repeated to an implementation-defined >>>>> maximum supported cascade depth. >>>>> +When cascades are defined, the actual class of service assigned to a >>>>> packet is the result of the longest chain of PMRs that can be matched >>>>> against the packet. >>>>> + >>>>> +For example, suppose the following sequence of PMRs is in effect: >>>>> + >>>>> +@verbatim >>>>> +odp_pktio_pmr_cos(pmr_idA, pktio_id, cos_idA); >>>>> +odp_cos_pmr_cos(pmr_idB, cos_idA, cos_idB); >>>>> +odp_cos_pmr_cos(pmr_idC, cos_idB, cos_idC); >>>>> +odp_cos_pmr_cos(pmr_idD, cos_idC, cos_idD); >>>>> +@endverbatim >>>>> + >>>>> +If a packet arrives on pktio_id that matches pmr_idA it is assigned >>>>> to cos_idA. >>>>> +But since it is now on cos_idA it is further filtered by pmr_idB and >>>>> if it matches is reassigned to cos_idB. >>>>> +This process continues until no further more specific match is found >>>>> to determine the final CoS that the packet receives. >>>>> + >>>>> +Note that given this rule set a packet that matched pmr_idA and >>>>> pmr_idC it would be assigned to cos_idA because the rule that can assign >>>>> packets to pmr_idC is only applicable to packets that are assigned to >>>>> cos_idB, not cos_idA. >>>>> + >>>>> +Using cascaded PMRs it is possible to build quite sophisticated >>>>> filters (up to the implementation limits supported by a given platform). >>>>> +For example, one could add additional rules to the above set: >>>>> + >>>>> +@verbatim >>>>> +odp_cos_pmr_cos(pmr_idAC, cos_idA, cos_idC); >>>>> +odp_cos_pmr_cos(pmr_idAD, cos_idA, cos_idD); >>>>> +@endverbatim >>>>> + >>>>> +To cover cases where some packets on cos_idA should be further sorted >>>>> to cos_idB while others should be sorted directly to cos_idC or cos_idD. >>>>> +Again it is the application’s responsibility to ensure that the >>>>> cascades remain unambiguous and that loops be avoided (e.g., having rules >>>>> that bounce packets between cos_idA and cos_idB endlessly). >>>>> + >>>>> +@subsection pmr_stats PMR Statistics >>>>> +Conforming ODP implementations SHOULD maintain statistics regarding >>>>> PMRs and provide the following routines for retrieving them: >>>>> + >>>>> +@subsubsection pmr_match_count odp_pmr_match_count >>>>> +@verbatim >>>>> +/** >>>>> + * Retrieve packet matcher statistics >>>>> + * >>>>> + * @param pmr_id the id of the PMR from which to retrieve the count >>>>> + * >>>>> + * @return The current number of matches for a given matcher instance. >>>>> + */ >>>>> + >>>>> +signed long odp_pmr_match_count(odp_pmr_t pmr_id); >>>>> +@endverbatim >>>>> + >>>>> +@subsubsection pmr_terms_cap odp_pmr_terms_cap >>>>> +@verbatim >>>>> +/** >>>>> + * Inquire about matching terms supported by the classifier >>>>> + * >>>>> + * @return A mask one bit per enumerated term, one for each of >>>>> op_pmr_term_e >>>>> + */ >>>>> + >>>>> +unsigned long long odp_pmr_terms_cap(void); >>>>> +@endverbatim >>>>> + >>>>> +@subsubsection pmr_terms_avail odp_pmr_terms_avail >>>>> +@verbatim >>>>> +/** >>>>> + * Return the number of packet matching terms available for use >>>>> + * >>>>> + * @return A number of packet matcher resources available for use. >>>>> + */ >>>>> + >>>>> +unsigned odp_pmr_terms_avail(void); >>>>> +@endverbatim >>>>> + >>>>> +@subsection pmr_composite_rules Pattern Matching Composite Routines >>>>> +As a shorthand, applications MAY express pattern matching rules using >>>>> a table rather than constructing them term-by-term. >>>>> +ODP implementations MUST support both methods of rule specification >>>>> but MAY have implementation-specific restrictions on the complexity of >>>>> table-based rules they support. >>>>> +Note that some implementations MAY be able to implement tables >>>>> directly while others MAY choose to implement tables by internally >>>>> generating the equivalent set of term generating calls. >>>>> + >>>>> +@subsubsection pmr_table_structure PMR Table Structure >>>>> +@verbatim >>>>> +/** >>>>> + * Following structure is used to define composite packet matching >>>>> rules >>>>> + * in the form of an array of individual match or range rules. >>>>> + * The underlying platform may not support all or any specific >>>>> combination >>>>> + * of value match or range rules, and the application should take care >>>>> + * of inspecting the return value when installing such rules, and >>>>> perform >>>>> + * appropriate fallback action. >>>>> + */ >>>>> + >>>>> +typedef struct odp_pmr_match_t { >>>>> + enum odp_pmr_match_type_e { >>>>> + ODP_PMR_MASK, /**< Match a masked set >>>>> of bits */ >>>>> + ODP_PMR_RANGE, /**< Match an integer >>>>> range */ >>>>> + } match_type; >>>>> + union { >>>>> + struct { >>>>> + enum odp_pmr_term_e term; >>>>> + const void *val; >>>>> + const void *mask; >>>>> + unsigned int val_sz; >>>>> + } mask; /**< Match a masked set of bits */ >>>>> + struct { >>>>> + enum odp_pmr_term_e term; >>>>> + const void *val1; >>>>> + const void *val2; >>>>> + unsigned int val_sz; >>>>> + } range; /**< Match an integer range */ >>>>> + }; >>>>> +} odp_pmr_match_t; >>>>> + >>>>> + >>>>> +/** An opaque handle to a composite packet match rule-set */ >>>>> +typedef struct odp_pmr_set_s *odp_pmr_set_t; >>>>> +@endverbatim; >>>>> + >>>>> +The above structure is used with the following APIs to implement >>>>> table-based PMRs: >>>>> + >>>>> +@subsubsection pmr_match_set_create odp_pmr_match_set_create >>>>> +@verbatim >>>>> +/** >>>>> + * Create a composite packet match rule >>>>> + * >>>>> + * @param num_terms is the number of terms in the match rule. >>>>> + * @param terms is an array of num_terms entries, one entry per >>>>> + * term desired. >>>>> + * @param dst_cos is the class-of-service to be assigned to packets >>>>> + * that match the compound rule-set, or a subset >>>>> thereof, >>>>> + * if partly applied. >>>>> + * @param pmr_set_id is the returned handle to the composite rule set. >>>>> + * >>>>> + * @return The return value may be a negative number indicating a >>>>> general >>>>> + * error, or a positive number indicating the number of ‘terms’ >>>>> elements that >>>>> + * have been successfully mapped to the underlying platform >>>>> classification engine, >>>>> + * and may be in the range from 1 to ‘num_terms’. >>>>> + */ >>>>> + >>>>> +int odp_pmr_match_set_create(int num_terms, odp_pmr_match_t *terms, >>>>> + odp_pmr_set_t *pmr_set_id); >>>>> +@endverbatim >>>>> + >>>>> +This routine is used to create a PMR match set. >>>>> + It is the equivalent to a cascade of PMRs except that there are no >>>>> “intermediate” classes of service defined. >>>>> +Instead, the entire match set either matches or does not match as a >>>>> single entity. >>>>> + >>>>> +@subsubsection pmr_match_set_destroy odp_pmr_match_set_destroy >>>>> +@verbatim >>>>> +/** >>>>> + * Function to delete a composite packet match rule set >>>>> + * >>>>> + * All of the resources pertaining to the match set associated with >>>>> the >>>>> + * class-of-service will be released, but the class-of-service will >>>>> + * remain intact. >>>>> + * >>>>> + * @param pmr_set_id a composite rule-set handle returned when >>>>> created. >>>>> + * >>>>> + * @note Depending on the implementation details, destroying a >>>>> rule-set >>>>> + * may not guarantee the availability of hardware resources to create >>>>> the >>>>> + * same or essentially similar rule-set. >>>>> + */ >>>>> + >>>>> +int odp_pmr_match_set_destroy(odp_pmr_set_t pmr_set_id); >>>>> +@endverbatim >>>>> + >>>>> +This routine destroys a PMR match set previously created by >>>>> odp_pmr_match_set_create(). >>>>> + >>>>> +@subsubsection pktio_pmr_match_set_cos odp_pktio_pmr_match_set_cos >>>>> +@verbatim >>>>> +/** >>>>> + * Apply a PMR Match Set to a pktio to assign a CoS. >>>>> + * >>>>> + * @param pmr_set_id the id of the PMR match set to be activated >>>>> + * @param src_pktio the pktio to which this PMR match set is to be >>>>> applied >>>>> + * @param dst_cos the CoS to be assigned by this PMR match set >>>>> + * >>>>> + * @return Success or ODP_PARAMETER_ERROR >>>>> + */ >>>>> + >>>>> +int odp_pktio_pmr_match_set_cos(odp_pmr_t pmr_id, odp_pktio_t >>>>> src_pktio, >>>>> + odp_cos_t dst_cos); >>>>> +@endverbatim >>>>> + >>>>> +This routine is the same as odp_pktio_pmr_cos() except that it >>>>> operates on PMR match sets rather than individual PMRs. >>>>> + >>>>> +@section items_pending Items pending resolution >>>>> +- Revise ‘odp_packet_io.h’ API with respect of default input queue >>>>> per ‘pktio’ instance. >>>>> +- Revise ‘odp_queue.h’ API to support an arbitrary priority range, >>>>> typically 8 priority levels with numeric priority values are >>>>> platform-specific. >>>>> +- Add specific packet meta data fields to go into packet buffer which >>>>> contain all meta data fields parsed and generated by the classifier, for >>>>> later application use. >>>>> + >>>>> +@section implementation_notes Implementation Notes >>>>> +The following sections are not part of the specification, but shed >>>>> light into the intent of the specification in several areas, describing >>>>> some specific implementation approaches of these aspects. >>>>> + >>>>> +@subsection supporting_multi_pools Supporting multiple buffer pools >>>>> +The support of multiple buffer pools for containing packet buffers is >>>>> optional, and may not be supported by some platforms. >>>>> +The importance of this feature stems from the need of protecting a >>>>> networking application in the event of a congestion, or an attempted denial >>>>> of service attack. >>>>> +Separating different classes of service to dedicated buffer pools >>>>> allows the system to limit the memory resources that may be consumed by a >>>>> particular type of traffic, thereby reserving buffer resources for other >>>>> classes of traffic. >>>>> + >>>>> +In a software implementation, a packet would already be stored in >>>>> memory when the classifier is invoked, and so it seems the classifier is >>>>> unable to insert itself into the process of selecting a buffer pool. >>>>> +For obvious reasons the copying of a packet into a new buffer >>>>> allocated from a different pool by the classifier is not a desirable >>>>> solution. >>>>> + >>>>> +The recommended solution is to implement buffer pools in the form of >>>>> buffer counters, while the actual buffers all belong to a single free list >>>>> when not used to store a packet. >>>>> +In such an implementation, the classifier will be able to associate a >>>>> packet already occupying a buffer to a different pool than the default by >>>>> incrementing the buffer counter of the newly selected pool, and >>>>> decrementing the counter representing the default pool. >>>>> +If however the selected pool counter has already reached a certain >>>>> limit, the classifier would be able to e.g discard the packet instead of >>>>> incrementing the destination pool counter, and thereby enforce the >>>>> desirable semantics of distinct buffer pools per class of service. >>>>> + >>>>> +Other possible action that may be taken in response to running out of >>>>> buffers or coming too low on buffers include back-pressure and >>>>> random-early-detect with a discard probability inversely proportional to >>>>> the number of free buffers in a pool. >>>>> +A related implementation topic is the ability to begin dropping some >>>>> packets before a buffer pool is entirely exhausted. >>>>> +This is typically referred to as <em>Random Early Detect</em> (or >>>>> “RED”). >>>>> +This is deemed to be a feature of the buffer pool implementation on a >>>>> given platform, where in addition to a hard limit on the number of buffers >>>>> that can be allocated to a pool, there can also be an option discard >>>>> packets with a probability the increases as the number of outstanding >>>>> buffers approaches that hard limit. >>>>> + >>>>> +@subsection resolving_gaps Resolving gaps between the API and >>>>> hardware capabilities >>>>> +On platforms that support hardware packet accelerators, it is >>>>> possible that the packet parsing and classification functionality is >>>>> sufficient to address only a portion of the functionality specified within >>>>> this document. >>>>> +This gap may be potentially bridged by augmenting the hardware >>>>> classification capabilities with a software logic implemented as part of >>>>> the platform. >>>>> +In that case, the platform will have to curve out a fraction of the >>>>> processing resources and dedicate those to the software classification >>>>> logic, which would be invoked for packets that the hardware platform was >>>>> unable to classify completely. >>>>> +At the time of this writing, it is believed however that the >>>>> performance penalty that will be incurred as a result of software >>>>> augmentation is unjustified for most application, i.e. >>>>> +it is preferred to lose the precision of packet prioritization while >>>>> maintaining full hardware packet processing speed. >>>>> + >>>>> +@subsection loopback_case The case for loopback ports, and some of >>>>> their uses >>>>> +In some applications, it may be desirable to be able to run a single >>>>> packet through the classifier more than once. >>>>> +For example, an encrypted IPsec packet is received from a physical >>>>> port. >>>>> +The encrypted packet is assigned a class of service based on its >>>>> outer unencrypted header fields. >>>>> +Later, processing the packet entails decrypting the payload of the >>>>> packet, authenticating it, and removing the original outer headers, which >>>>> reveals a new set of protocol headers which need to be used to re-classify >>>>> the packet, and assign it a new priority and buffer pool. >>>>> +An elegant solution for this use case would be to take advantage of >>>>> “loopback” logical ports that may be implemented in certain platforms, by >>>>> transmitting decapsulated packet into a loop-back port. >>>>> +The same packet then is received from a loop-back port and is >>>>> examined by the classifier in accordance to the rules assigned to the >>>>> loopback odp_pktio logical port instance. >>>>> +Similar mechanism may be applied to tunnel termination processing, >>>>> fragment reassembly et al. >>>>> + >>>>> +@section related_topics Related Topics >>>>> +The following section discusses aspects of the ODP API that are not >>>>> integral to the classifier, which only applies to ingress preprocessing. >>>>> +This section covers miscellaneous aspects of the API that need to be >>>>> addressed, and are related to packet buffer processing and egress >>>>> post-processing. >>>>> +Additional packet buffer manipulation APIs >>>>> +The need for these following calls are made evident by the need to >>>>> encapsulate, i.e., remove some headers and add other, thereby changing the >>>>> size of the headers of a packet during processing. >>>>> + >>>>> +@subsection initial_headroom Configuring initial packet buffer >>>>> headroom >>>>> +The following function is provided to configure the pktio receive >>>>> mechanism to (optionally)reserve some headroom between start of the first >>>>> buffer to the first byte of the first packet data byte, which subsequently >>>>> could be used to increase the header size “in-place”, without allocating >>>>> additional gather list elements. >>>>> +If the request is granted, at least <req_bytes> bytes will be >>>>> reserved in the front of the packet data: >>>>> +@verbatim >>>>> +int odp_pktio_set_headroom(odp_pktio_t port_id, unsigned req_bytes); >>>>> +@endverbatim >>>>> +The return value should be negative if the request can not be >>>>> satisfied, or positive otherwise indicating the actual minimum headroom >>>>> reserved. >>>>> +Note that the implementation may reserve more than the requested >>>>> amount of headroom, and hence on platforms that are unable to support >>>>> per-port (or per CoS) headroom configuration, a system-wide headroom >>>>> configuration may be set to the largest of all such requests, and thus >>>>> satisfy the requirement. >>>>> +In addition to the above per-port headroom configuration call, there >>>>> should be an optional, per-CoS call that allows the reservation of >>>>> different amounts of packet buffer headroom for packets that match certain >>>>> criteria: for example, the following call allows the application to request >>>>> that only packets that are expected to be encapsulated in a tunnel, be >>>>> augmented with a large headroom amount, while packets that are received >>>>> from a tunnel, and are IP fragments, be assigned a different headroom >>>>> requirement (see definition for odp_cos_set_headroom() above. >>>>> +Egress packet scheduling, prioritization and ordering >>>>> + >>>>> + >>>>> +Open Issues >>>>> +* Parallel matching rules relative precedence. >>>>> +* Specify application-defined header field declaration APIs. >>>>> +* Review RFC 4301 for match requirements for IPsec SA, consider the >>>>> use of L4 port ranges instead of or in addition to value & mask matching >>>>> criteria. >>>>> +* Consider the type of packet checks should route a packet through >>>>> the error CoS: L2 is a safe choice, but L3/L4 checksum or other exceptions >>>>> deserve consideration. >>>>> +Usage Examples >>>>> +Following is a simple sample configuration using the API elements >>>>> described above. >>>>> +TBD. >>>>> + >>>>> +*/ >>>>> diff --git a/images/classification_flow.png >>>>> b/images/classification_flow.png >>>>> new file mode 100644 >>>>> index 0000000000000000000000000000000000000000.. >>>>> 2d94ff64ec772aa97f3687181c121fb91d671889 >>>>> GIT binary patch >>>>> literal 35193 >>>>> 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 >>>>> >>>> >>> >>> _______________________________________________ >>> lng-odp mailing list >>> lng-odp@lists.linaro.org >>> http://lists.linaro.org/mailman/listinfo/lng-odp >>> >>> >> >> >> -- >> *Mike Holmes* >> Linaro Technical Manager / Lead >> LNG - ODP >> > >
diff --git a/classification_design.dox b/classification_design.dox new file mode 100644 index 0000000..0cb7134 --- /dev/null +++ b/classification_design.dox @@ -0,0 +1,879 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*! +@page classification_design ODP Design - Classification API +For the implementation of the ODP classification API please see @ref odp_classify.h + +@tableofcontents + +@section introduction Introduction +This document defines the Classification APIs supported by ODP v1.0. +Classification is logically composed of two stages: Parsing and Rule Matching. +Parsing takes a raw packet and validates its structure and identifies fields of interest in the various headers that comprise the layers of the packet. +Rule Matching, in turn, takes the result of parsing and sorts packets into Classes of Service (CoS) based on application-defined rule sets. +@subsection use_of_terms Use of Terms +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc21199). +@subsection purpose Purpose +ODP is a framework for software-based packet forwarding/filtering applications, and the purpose of the Packet Classifier API is to enable applications to program the platform hardware or software implementation to assist in prioritization, classification and scheduling of each packet, so that the software application can run faster, scale better and adhere to QoS requirements. +The following API abstraction are not modelled after any existing product implementation, but is instead defined in terms of what a typical data-plane application may require from such a platform, without sacrificing simplicity and avoiding ambiguity. Certain terms that are being used within the context of existing products in relation to packet parsing and classification, such as “access lists” are avoided such that not to suggest any relationship between the abstraction used within this API and any particular manner in which they may be implemented in hardware. +These are the key ODP objects that the parser needs to employ, that are presently defined in ODP: +@subsubsection odp_pktio odp_pktio +odp_pktio specifies an individual packet I/O channel instance. In other words, it would translate to a physical interface or a logical port, or in the case of channelized protocols (e.g., [Interlaken](https://www.google.com/url?q=https%3A%2F%2Fwww.cortina-systems.com%2Fimages%2Fdocuments%2F400023_Interlaken_Technology_White_Paper.pdf&sa=D&sntz=1&usg=AFQjCNEBdJTBmA1XaNGY3pmumQTfgSi1oA)) it would map to a logical channel on that interface. +Since the classifier API deals exclusively with ingress, this object represents the source of packets into the classifier. In order to support any non-trivial use case, the classifier API needs to be able to assign multiple odp_queue instances for any single odp_pktio object, and may also assign any odp_queue instance to more than one odp_pktio object. +@subsubsection odp_queue odp_queue +odp_queue specifies a logical queue for packets, and in the case of ingress, this would represent a stream of packets which share several attributes, that are delivered to the ODP application for processing. The per-queue attributes currently defined are: queue type, sync (ordering); priority; and schedule group (set of processor cores). +@subsubsection odp_buffer_pool odp_buffer_pool +odp_buffer_pool specifies a collection of buffers of same size and alignment, as well as a set of policies such as flow control and processor affinity. The classifier API refers to such pools that are designated for storing ingress packets. +@section functional_description Functional Description +Following is the functionality that is required of the classifier API, and its underlying implementation. The details and order of the following paragraph is informative, and is only intended to help convey the functional scope of a classifier and provide context for the API. In reality, implementations may execute many of these steps concurrently, or in different order while maintaining the evident dependencies: + +-# Apply a set of \e classification \e rules to the header of an incoming packet, identify the header fields, e.g., \e ethertype, IP version, IP protocol, transport layer port numbers, IP DiffServ, VLAN id, 802.1p priority. + +-# Store these fields as packet meta data for application use, and for the remainder of parser operations. The \e odp_pktio is also stored as one of the meta data fields for subsequent use. + +-# Compute an \e odp_cos (Class of Service) value from a subset of supported fields from 1) above. + + +-# Based on the \e odp_cos from 3) above, select the \e odp_queue through which the packet is delivered to the application. + +-# Validate the packet data integrity (checksums, FCS) and correctness (e.g., length fields) and store the validation result, along with optional error layer and type indicator, in packet meta data. Optionally, if a packet fails validation, override the \e odp_cos selection in step 3 to a class of service designated for errored packets. + +-# Since the selected \e odp_queue may require preservation of packet order, i.e., SYNC_ATOMIC or SYNC_ORDERED, optionally select the packet header fields from which the parser calculates a \e odp_flow_signature, which may be a unique flow identifier or a hash, such that the packets which are assigned the same \e odp_flow_signature are scheduled in the same order they are received. + +-# Based on the \e odp_cos from 3) above, select the \e odp_buffer_pool that should be used to acquire a buffer to store the packet data and meta data. + +-# Allocate a buffer from \e odp_buffer_pool selected in 6) above and logically store the packet data and meta data to the allocated buffer, or in accordance with class-of-service drop policy and subject to pool buffer availability, optionally discard the packet. + +-# Enqueue the buffer into the \e odp_queue selected in 4) above. + +The above is an abstract description of the classifier functionality, and may be applied to a variety of applications in many different ways. +The ultimate meaning of how this functionality applies to an application also depends on other ODP modules, so the above may not complete a full depiction. +For instance, the exact meaning of \e priority, which is a per-queue attribute is influenced by the ODP scheduler semantics, and the system behavior under stress depends on the ODP buffer pool module behavior. + +For the sole purpose of illustrating the above abstract functionality, here is an example of a Layer-2 (IEEE 802.1D) bridge application: +Such a forwarding application that also adheres to IEEE 802.1p/q priority, which has 8 traffic priority levels, might create 8 \e odp_buffer_pool instances, one for each PCP priority level, and 8 \e odp_queue instances one per priority level. +Incoming packets will be inspected for a VLAN header; the PCP field will be extracted, and used to select both the pool and the queue. +Because each queue will be assigned a priority value, the packets with highest PCP values will be scheduled before any packet with a lower PCP value. +Also, in a case of congestion, buffer pools for lower priority packets will be depleted earlier than the pools containing packets of the high priority, and hence the lower priority packets will be dropped (assuming that is the only flow control method that is supported in the platform) while higher priority packets will continue to be received into buffers and processed. +@subsection flow_diagram Classification Processing Flow Diagram +@image html classification_flow.png "Figure 1: Classification Flow Diagram" +@image latex classification_flow.eps "Figure 1: Classification Flow Diagram" + +@section api_elements API Elements +While the above description refers to the abstracted packet classifier, the following is the description of the API designed to program the packet classifier, and is intended to add clarity to the functions provided further below. +@subsection cos_creation Class of Service Creation and Binding +To program the classifier, a class-of-service instance must be created, which will contain the packet filtering resources that it may require. +All subsequent calls refer to one or more of these resources. +Each class of service instance must be associated with a single queue or queue group, which will be the destination of all packets matching that particular filter. +The queue assignment is implemented as a separate function call such that the queue may be modified at any time, without tearing down the filters that define the class of service. In other words, it is possible to change the destination queue for a class of service defined by its filters quickly and dynamically. +Optionally, on platforms that support multiple packet buffer pools, each class of service may be assigned a different pool such that when buffers are exhausted for one class of service, other classes are not negatively impacted and continue to be processed. + +@subsection default_packet_handling Default packet handling +There SHOULD be one \b odp_cos assigned to each port with the \c odp_cos_pktio_set() function, which will function as the default class-of-service for all packets received from an ingress port, that do not match any of the filters defined subsequently. At minimum this default class-of-service MUST have a queue and a buffer pool assigned to it on platforms that support multiple packet buffer pools. Multiple odp_pktio instances (i.e., multiple ports) MAY each have their own default odp_cos, or MAY share a odp_cos with other ports, based on application requirements. + +@subsection packet_classification Packet Classification +For each odp_pktio port, the API allows the assignment of a class-of-service to a packet using one of three methods: + +-# The packet may be assigned a specific class-of-service based on its Layer-2 (802.1P/902.1Q VLAN tag) priority field. Since the standard field defines 8 discrete priority levels, the API allows to assign an odp_cos to each of these priority levels with the \c odp_cos_with_l2_priority() function. + +-# Similarly, a class-of-service may be assigned using the Layer-3 (IP DiffServ) header field. The application supplies an array of \e odp_cos values that covers the entire range of the standard protocol header field, where array elements do not need to contain unique values. There is also a need to specify if Layer-3 priority takes precedence over Layer-2 priority in a packet with both headers present. + +-# Additionally, the application may also program a number of \e pattern \e matching \e rules that assign a class-of-service for packets with header fields matching specified values. The field-matching rules take precedence over the previously described priority-based assignment of a class-of-service. Using these matching rules the application should be able for example to identify all packets containing VoIP traffic based on the protocol being UDP, and a specific destination or source port numbers, and appropriately assign these packets an class-of-service that maps to a higher priority queue, assuring voice packets a lower and bound latency. + +@subsection scaling_and_flow Scaling and Flow Discrimination +In addition to classifying packets and routing them to those queues with the appropriate priority, and optionally limiting their memory consumption by designating certain classes of packets to specific buffer pools, the classifier API also facilitates the scaling of data-plane application on multi-core systems by creating a mechanism to define which packet headers need to be combined to result in a value representing a specific packet flow. The classifier generates a signature, which can be a checksum or hash of arbitrary strength that covers those packet header fields that are identified by the application as identifying flows. + +The \e flow \e signatures that result from hashing are then stored with the packet meta data (along with its class-of-service and its ingress \e odp_pktio port), and subsequently may be utilized by the implementation of a scheduler queue to maintain the order of packets with the same flow signature, while allowing packets with different signatures to be processed concurrently and independently on different processing cores. + +@subsection packet_meta_data Packet meta data Elements +Here are the specific information elements that SHOULD be stored within the packet meta data structure: +- Protocol fields that are decoded and extracted by the parsing phase +- Flow-signature calculated from a prescribed collection of protocol fields +- The class-of-service identifier that is selected for the packet +- The ingress port identifier +- The result of packet validation, including an indication of the type of error detected, if any + +The ODP packet API module SHALL provide accessors for retrieving the above meta data fields from the container buffer in an implementation-independent manner. + +@section api_definitions API Definitions +@subsection data_types Data Types +The following data types are referenced in the API descriptions described below. +The names are part of the ODP API and MUST be present in any conforming implementation, however the type values shown here are illustrative and implementations SHOULD either use these or substitute their own type values that are appropriate to the underlying platform. + +@verbatim +/** + * 'odp_pktio_t' value to indicate any port + */ +#define ODP_PKTIO_ANY ((odp_pktio_t)~0) + + +/** + * 'odp_pktio_t' value to indicate an error + */ +#define ODP_PKTIO_INVALID ((odp_pktio_t)0) + + +/** + * Class of service instance type + */ +typedef uint32_t odp_cos_t; + + +/** + * flow signature type, only used for packet meta data field. + */ +typedef uint32_t odp_flowsig_t; + + +/** + * This value is returned from odp_cos_create() on failure, + * May also be used as a “sink” class of service that + * results in packets being discarded. + */ +#define ODP_COS_INVALID ((odp_cos_t)~0) +@endverbatim + +@subsection cos_routines Class of Service Routines +Conforming ODP implementations MUST provide the following Classification APIs: +@subsubsection cos_create odp_cos_create +@verbatim +/** + * Create a class-of-service + * + * @param name is a string intended for debugging purposes. + * + * @return Class of service instance identifier, + * or ODP_COS_INVALID on error. + */ + +odp_cos_t odp_cos_create(const char *name); +@endverbatim + +This routine is used to create a class of service that can be the target of classifier rules. +The number of such classes supported is implementation-defined. +Attempts to create more than are supported by the implementation will result in an \c ODP_COS_INVALID return and errno being set to \c ODP_IMPLEMENTATION_LIMIT. + +@subsubsection cos_destroy odp_cos_destroy +@verbatim +/** + * Discard a class-of-service along with all its associated resources + * + * @param cos_id class-of-service instance. + * + * @return 0 on success, -1 on error. + */ + +int odp_cos_destroy(odp_cos_t cos_id); +@endverbatim + +This routine is the bracketing routine for odp_cos_create(). +It is used to destroy an existing CoS. +It is the caller’s responsibility to ensure that no active pattern matching rules refer to the CoS prior to calling this routine. +Results are unpredictable if this restriction is not met. +@subsubsection cos_set_queue odp_cos_set_queue +@verbatim +/** + * Assign a queue for a class-of-service + * + * @param cos_id class-of-service instance. + * + * @param queue_id is the identifier of a queue where all packets + * of this specific class of service will be enqueued. + * + * @return 0 on success, negative error code on failure. + */ + +int odp_cos_set_queue(odp_cos_t cos_id, odp_queue_t queue_id); +@endverbatim + +This routine associates a target queue with a CoS such that all packets assigned to this CoS will be enqueued to the specified queue_id at the end of classification processing. +@subsubsection cos_set_queue_group odp_cos_set_queue_group +@verbatim +/** + * Assign a homogenous queue-group to a class-of-service. + * + * @param cos_id identifier of class-of-service instance + * @param queue_group_id identifier of the queue group to receive packets + * associated with this class of service. + * + * @return 0 on success, negative error code on failure. + */ + +int odp_cos_set_queue_group(odp_cos_t cos_id, odp_queue_group_t queue_group_id); +@endverbatim + +This routine associates a target queue group with a CoS such that all packets assigned to this CoS will be distributed to the specified queue_group_id at the end of classification processing. +@subsubsection cos_set_pool odp_cos_set_pool +@verbatim +/** + * Assign packet buffer pool for specific class-of-service + * + * @param cos_id class-of-service instance. + * @param pool_id is a buffer pool identifier where all packet buffers + * will be sourced to store packet that belong to this + * class of service. + * + * @return 0 on success negative error code on failure. + * + * + */ + +int odp_cos_set_pool(odp_cos_t cos_id, odp_buffer_pool_t pool_id); +@endverbatim + +This OPTIONAL routine associates a target buffer pool with a CoS such that all packets assigned to this CoS will be stored in packet buffers allocated from the designated pool_id. + + +@subsection cos_drop_policy Class of Service Drop Policy Routines +These routines control how drop policies are to be observed for a given class of service. +@subsubsection drop_data_types Data types +~~~~~{.c} +enum odp_cos_drop_e { + ODP_COS_DROP_POOL, /**< Follow buffer pool drop policy */ + ODP_COS_DROP_NEVER, /**< Never drop, ignoring buffer pool policy */ +}; +typedef enum odp_drop_e odp_drop_t; +~~~~~ + +@subsubsection cos_set_drop odp_cos_set_drop +@verbatim +/** + * Assign packet drop policy for specific class-of-service + * + * @param cos_id class-of-service instance. + * @param drop_policy is the desired packet drop policy for this class. + * + * @return 0 on success negative error code on failure. + */ + +int odp_cos_set_drop(odp_cos_t cos_id, odp_drop_t drop_policy); +@endverbatim + +This routine sets the drop policy for a class of service. +It is an OPTIONAL routine. +If an implementation does not provide this function it MUST supply a definition of it that simply returns ODP_FUNCTION_NOT_AVAILABLE. +@subsubsection pktio_set_default_cos odp_pktio_set_default_cos +@verbatim +/** + * Setup per-port default class-of-service + * + * @param pktio_in ingress port identifier. + * @param default_cos class-of-service set to all packets arriving + * at the 'pktio_in' ingress port, unless overridden by subsequent + * header-based filters. + * + * @return 0 on success negative error code on failure. + * + * + * @note This may replace the default queue per pktio. + */ + +int odp_pktio_set_default_cos(odp_pktio_t pktio_in, odp_cos_t default_cos); +@endverbatim + +This routine specifies a default class of service for a given pktio instance. +Incoming packets on the specified pktio are assigned to this class of service if no other pattern matching rule obtains. +@subsubsection pktio_set_error_cos odp_pktio_set_error_cos +@verbatim +/** + * Setup per-port error class-of-service + * + * @param pktio_in ingress port identifier. + * @param error_cos class-of-service set to all packets arriving + * at the 'pktio_in' ingress port that contain an error. + * + * @return 0 on success negative error code on failure. + */ + +int odp_pktio_set_error_cos(odp_pktio_t pktio_in, odp_cos_t error_cos); +@endverbatim + +This OPTIONAL function assigns a class-of-service used to handle packets containing various types of errors. The specific errors types include L2 FCS and optionally L3/L4 checksum errors, malformed headers, etc., depending on platform capabilities. +The specified error_cos MAY simply discard these packets or deliver them via a queue to the application for further processing. +@subsubsection pktio_set_skip odp_pktio_set_skip +@verbatim +/** + * Setup per-port header offset + * + * @param pktio_in ingress port identifier. + * @param offset is the number of bytes the classifier must skip. + * + * @return Success or ODP_FUNCTION_NOT_AVAILABLE + */ + +int odp_pktio_set_skip(odp_pktio_t pktio_in, size_t offset); +@endverbatim + +This OPTIONAL function applies to ports that carry an additional headers preceding the standard Ethernet header. Such headers are typically vendor-specific and thus the classifier is not required to parse such headers, but the size of a custom header is critical for the classifier to be able to parse standard protocol headers that normally follow. +@subsubsection cos_set_headroom odp_cos_set_headroom +@verbatim +/** + * Specify per-port buffer headroom + * + * @param pktio_in ingress port identifier. + * @param headroom number of bytes of space preceding packet data to reserve + * for use as headroom. Must not exceed the implementation + * defined ODP_PACKET_MAX_HEADROOM. + * + * @return Success or ODP_PARAMETER_ERROR, + * or ODP_FUNCTION_NOT_AVAILABLE + */ + +int odp_cos_set_headroom(odp_cos_t cos_id, size_t req_room); +@endverbatim + +This OPTIONAL routine specifies the number of bytes of headroom that should be reserved for each packet assigned to this class of service. +Each implementation defines an ODP_PACKET_MAX_HEADROOM limit that sets an upper bound on the size of the headroom that can be reserved for a packet. +@subsubsection cos_with_l2_priority odp_cos_with_l2_priority +@verbatim +/** + * Request to override per-port class of service + * based on Layer-2 priority field if present. + * + * @param pktio_in ingress port identifier. + * @param num_qos is the number of QoS levels, typically 8. + * @param qos_table are the values of the Layer-2 QoS header field. + * @param cos_table is the class-of-service assigned to each of the + * allowed Layer-2 QOS levels. + * @return 0 on success negative error code on failure. + */ + +int odp_cos_with_l2_priority(odp_pktio_t pktio_in, + size_t num_qos, + uint8_t qos_table[], /**< 'num_qos' elements */ + odp_cos_t cos_table[]); /**< 'num_qos' elements */ +@endverbatim + +This routine is used to assign classes of service based on the layer 2 (L2) priority associated with input packets received on the specified pktio_in. +For each of the values in qos_table[], the corresponding value in cos_table[] will be assigned. +@subsubsection cos_with_l3_dscp odp_cos_with_l3_dscp +@verbatim +/** + * + * @param pktio_in ingress port identifier. + * @param num_qos is the number of allowed Layer-3 QoS levels. + * @param qos_table are the values of the Layer-3 QoS header field. + * @param cos_table is the class-of-service assigned to each of the + * allowed Layer-3 QOS levels. + * @param l3_preference when true, Layer-3 QoS overrides L2 QoS when present. + * + * @return 0 on success negative error code on failure. + */ + +int odp_cos_with_l3_qos(odp_pktio_t pktio_in, + size_t num_qos, + uint8_t qos_table[], /**< 'num_qos' elements */ + odp_cos_t cos_table[], /**< 'num_qos' elements */ + odp_bool_t l3_preference); +@endverbatim + +This OPTIONAL routine is used to assign classes of service based on the layer 3 (L3) Differentiated Services (DS) designation. +This is the DSCP field of an IPv4 header or the first six bits of the Traffic Class of an IPv6 header. +For each of the values in qos_table[], the corresponding value in cos_table[] will be assigned. +The l3_preference flag is use to control whether the CoS assigned by this routine takes precedence over the CoS assigned by odp_cos_with_l2_priority() in the event that both apply to the same packet. + +@subsection pmrs Pattern Matching Rules +While the above routines permit class of service assignments to be made based on static criteria, the real power of classification is the ability to identify flows based on the variable contents of packet headers. +To do this ODP provides support for defining pattern matching rules (PMRs) that operate based on values contained in specified header fields. + +Associated with PMRs are enums that are used to specify standard packet header fields: +@subsubsection cos_hdr_flow_fields odp_cos_hdr_flow_fields_e +@verbatim +/** + * Packet header field enumeration + * for fields that may be used to calculate + * the flow signature, if present in a packet. + */ + +enum odp_cos_hdr_flow_fields_e { + ODP_COS_FHDR_IN_PKTIO, /**< Ingress port number */ + ODP_COS_FHDR_L2_SAP, /**< Ethernet Source MAC address */ + ODP_COS_FHDR_L2_DAP, /**< Ethernet Destination MAC address */ + ODP_COS_FHDR_L2_VID, /**< Ethernet VLAN ID */ + ODP_COS_FHDR_L3_FLOW /**< IPv6 flow_id */ + ODP_COS_FHDR_L3_SAP, /**< IP source address */ + ODP_COS_FHDR_L3_DAP, /**< IP destination address */ + ODP_COS_FHDR_L4_PROTO, /**< IP protocol (e.g. TCP/UDP/ICMP) */ + ODP_COS_FHDR_L4_SAP, /**< Transport source port */ + ODP_COS_FHDR_L4_DAP, /**< Transport destination port */ + ODP_COS_FHDR_IPSEC_SPI, /**< IPsec session identifier */ + ODP_COS_FHDR_LD_VNI, /**< NVGRE/VXLAN network identifier */ + ODP_COS_FHDR_USER /**< Application-specific header field(s) */ +}; +@endverbatim + +Conforming ODP implementations SHOULD implement efficient flow set management routines such as these: + +~~~~~{.c} +/** + * Set of header fields that take part in flow signature hash calculation: + * bit positions per 'odp_cos_hdr_flow_fields_e' enumeration. + * +typedef uint16_t odp_cos_flow_set_t; + + +/** + * Set a member of the flow signature fields data set + * +static inline odp_cos_flow_set_t +odp_cos_flow_set( odp_cos_flow_set_t set, + enum odp_cos_hdr_flow_fields_e field) +{ + return set | (1U << field); +} + + +/** + * Test a member of the flow signature fields data set + * +static inline bool +odp_cos_flow_is_set( odp_cos_flow_set_t set, + enum odp_cos_hdr_flow_fields_e field) +{ + return (set & (1U << field)) != 0; +} +~~~~~ + +These routines are intended to be used in support of the following flow signature APIs: + +@subsubsection cos_class_flow_sig odp_cos_class_flow_signature +@verbatim +/** + * Set up set of headers used to calculate a flow signature + * based on class-of-service. + * + * @param cos_id class of service instance identifier + * @param req_data_set requested data-set for flow signature calculation + * + * @return data-set that was successfully applied. All-zeros data set + * indicates a failure to assign any of the requested fields, or other + * error. + */ + +odp_cos_flow_set_t +odp_cos_class_flow_signature(odp_cos_t cos_id, + odp_cos_flow_set_t req_data_set); +@endverbatim + +This OPTIONAL routine associates a fow set with a class of service for flow signature calculation. + +@subsubsection cos_port_flow_sig odp_cos_port_flow_signature +@verbatim +/** + * Set up set of headers used to calculate a flow signature + * based on ingress port. + * + * @param pktio_in ingress port identifier. + * @param req_data_set requested data-set for flow signature calculation + * + * @return data-set that was successfully applied. An all-zeros data-set + * indicates a failure to assign any of the requested fields, or other + * error. + */ + +odp_cos_flow_set_t +odp_cos_port_flow_signature(odp_pktio_t pktio_in, + odp_cos_flow_set_t req_data_set); +@endverbatim + +@subsection pmr_routines Pattern Matching Rules Routines +The following data structures SHOULD be implemented to support the definition of pattern matching routines by conforming ODP implementations: + +~~~~~{.c} +/** + * PMR - Packet Matching Rule + * Up to 32 bit of ternary matching of one of the available header fields + * + + +#define ODP_PMR_INVAL ((odp_pmr_t)NULL) +typedef struct odp_pmr_s *odp_pmr_t; +~~~~~ + +@subsecion terms Terms +Terms are the elements of a PMR and are identified by the following enum: + +@verbatim +enum odp_pmr_term_e { + ODP_PMR_ETHTYPE_0, /**< Initial (outer) Ethertype only (*val=uint16_t)*/ + ODP_PMR_ETHTYPE_X, /**< Ethertype of most inner VLAN tag (*val=uint16_t)*/ + ODP_PMR_VLAN_ID_0, /**< First VLAN ID (outer) (*val=uint16_t) */ + ODP_PMR_VLAN_ID_X, /**< Last VLAN ID (inner) (*val=uint16_t) */ + ODP_PMR_DMAC, /**< destination MAC address (*val=uint64_t) */ + ODP_PMR_IPPROTO, /**< IP Protocol or IPv6 Next Header (*val=uint8_t) */ + ODP_PMR_UDP_DPORT, /**< Destination UDP port, implies IPPROTO=17 */ + ODP_PMR_TCP_DPORT, /**< Destination TCP port implies IPPROTO=6 */ + ODP_PMR_UDP_SPORT, /**< Source UDP Port (*val=uint16_t) */ + ODP_PMR_TCP_SPORT, /**< Source TCP port (*val=uint16_t) */ + ODP_PMR_SIP_ADDR, /**< Source IP address (uint32_t) */ + ODP_PMR_DIP_ADDR, /**< Destination IP address (uint32_t) */ + ODP_PMR_SIP6_ADDR, /**< Source IP address (uint8_t[16]) */ + ODP_PMR_DIP6_ADDR, /**< Destination IP address (uint8_t[16]) */ + ODP_PMR_IPSEC_SPI, /**< IPsec session identifier(*val=uint32_t) */ + ODP_PMR_LD_VNI, /**< NVGRE/VXLAN network identifier (*val=uint32_t) */ + + + /** Inner header may repeat above values with this offset */ + ODP_PMR_INNER_HDR_OFF=32 +}; +@endverbatim + +@subsubsection tunnel_considerations Tunnel Considerations +Note that PMRs may be extended to support tunnels and tenants (NVGRE, VXLAN) via the ODP_PMR_INNER_HDR_OFF enum. +This enum is intended to be used as an “adder” to a PMR to indicate that the term refers to an inner header. +For example, the term ODP_PMR_DMAC would refer to the destination MAC address of the packet if the packet is not a tunnel, or of the outer header (the tunnel) if the packet is a tunnel. +To refer to the inner (tenant) destination MAC, the term would be specified as ODP_PMR_INNER_HDR_OFF+ODP_PMR_DMAC. + +@subsection pmr_apis PMR APIs +The following APIs are provided to enable an ODP application to specify PMRs as a series of individual or cascaded terms: +@subsubsection pmr_create_match odp_pmr_create_match +@verbatim +/** + * Create a packet match rule with mask and value + * + * @param term is one value of the enumerated values supported + * @param val is the value to match against the packet header + * in native byte order. + * @param mask is the mask to indicate which bits of the header + * should be matched ('1') and which should be ignored ('0') + * @param val_sz size of the ‘val’ and ‘mask’ arguments, + * that must match the value size requirement of the + * specific ‘term’. + * + * @return a handle of the matching rule or ODP_PMR_INVAL on error + */ + +odp_pmr_t odp_pmr_create_match(enum odp_pmr_term_e term, + const void *val, const void *mask, size_t val_sz); +@endverbatim + +This routine creates a PMR that matches a single value to a term. + +@subsubsection pmr_create_range odp_pmr_create_range +@verbatim +/** + * Create a packet match rule with value range + * + * @param term is one value of the enumerated values supported + * @param val1 is the lower bound of the header field range. + * @param val2 is the upper bound of the header field range. + * @param val_sz size of the ‘val1’ and ‘val2’ arguments, + * that must match the value size requirement of the + * specific ‘term’. + * + * @return a handle of the matching rule or ODP_PMR_INVAL on error + * @note: Range is inclusive [val1..val2]. + */ + +odp_pmr_t odp_pmr_create_range(enum odp_pmr_term_e term, + const void *val1, const void *val2, size_t val_sz); +@endverbatim + +This routine creates a PMR that matches an inclusive range of values to a term. + +@subsubsection pmr_destroy odp_pmr_destroy +@verbatim +/** + * Invalidate a packet match rule and vacate its resources + * + * @param pmr_id the identifier of the PMR to be destroyed + * + * @return Success or ODP_PMR_INVALID if the specified pmr_id not found. + */ + +int odp_pmr_destroy(odp_omr_t pmr_id); +@endverbatim + +This routine destroys a previously created PMR. +If the PMR is currently associated with an active class of service it is unpredictable at which point the match defined by the PMR is deactivated in terms of packet flow. +However, implementations MUST ensure that a PMR is either matched or not matched in its entirety such that dynamic changes to PMRs do not result in partial matches. + +@subsubsection pktio_pmr_cos odp_pktio_pmr_cos +@verbatim +/** + * Apply a PMR to a pktio to assign a CoS. + * + * @param pmr_id the id of the PMR to be activated + * @param src_pktio the pktio to which this PMR is to be applied + * @param dst_cos the CoS to be assigned by this PMR + * + * @return Success or ODP_PARAMETER_ERROR + */ + +int odp_pktio_pmr_cos(odp_pmr_t pmr_id, odp_pktio_t src_pktio, odp_cos_t dst_cos); +@endverbatim + +This routine links a pktio to a corresponding class of service via a specified PMR. +Any packet received on the specified src_pktio that matches the specified pmr_id will be assigned to the specified dst_cos. +If multiple PMRs match the implementation MAY define an inherent precedence or it MAY be unpredictable as to which PMR will determine the assigned CoS. +For this reason applications SHOULD NOT be written to use conflicting or ambiguous PMR definitions. + +@subsubsection cos_pmr_cos odp_cos_pmr_cos +@verbatim +/** + * Cascade a PMR to refine packets from one CoS to another. + * + * @param pmr_id the id of the PMR to be activated + * @param src_cos the id of the CoS to be filtered + * @param dst_cos the id of the CoS to be assigned to packets filtered + * from src_cos that match pmr_id. + * + * @return Success or ODP_PARAMETER_ERROR if an input is in error + * or ODP_IMPLEMENTATION_LIMIT if cascade depth is exceeded + */ + +int odp_cos_pmr_cos(odp_pmr_t pmr_id, odp_cos_t src_cos, odp_cos_t dst_cos); +@endverbatim + +This routine is used to cascade PMRs by passing packets assigned to the src_cos through another PMR. +Those matching are reassigned to the specified dst_cos. +Note that this process can be repeated to an implementation-defined maximum supported cascade depth. +When cascades are defined, the actual class of service assigned to a packet is the result of the longest chain of PMRs that can be matched against the packet. + +For example, suppose the following sequence of PMRs is in effect: + +@verbatim +odp_pktio_pmr_cos(pmr_idA, pktio_id, cos_idA); +odp_cos_pmr_cos(pmr_idB, cos_idA, cos_idB); +odp_cos_pmr_cos(pmr_idC, cos_idB, cos_idC); +odp_cos_pmr_cos(pmr_idD, cos_idC, cos_idD); +@endverbatim + +If a packet arrives on pktio_id that matches pmr_idA it is assigned to cos_idA. +But since it is now on cos_idA it is further filtered by pmr_idB and if it matches is reassigned to cos_idB. +This process continues until no further more specific match is found to determine the final CoS that the packet receives. + +Note that given this rule set a packet that matched pmr_idA and pmr_idC it would be assigned to cos_idA because the rule that can assign packets to pmr_idC is only applicable to packets that are assigned to cos_idB, not cos_idA. + +Using cascaded PMRs it is possible to build quite sophisticated filters (up to the implementation limits supported by a given platform). +For example, one could add additional rules to the above set: + +@verbatim +odp_cos_pmr_cos(pmr_idAC, cos_idA, cos_idC); +odp_cos_pmr_cos(pmr_idAD, cos_idA, cos_idD); +@endverbatim + +To cover cases where some packets on cos_idA should be further sorted to cos_idB while others should be sorted directly to cos_idC or cos_idD. +Again it is the application’s responsibility to ensure that the cascades remain unambiguous and that loops be avoided (e.g., having rules that bounce packets between cos_idA and cos_idB endlessly). + +@subsection pmr_stats PMR Statistics +Conforming ODP implementations SHOULD maintain statistics regarding PMRs and provide the following routines for retrieving them: + +@subsubsection pmr_match_count odp_pmr_match_count +@verbatim +/** + * Retrieve packet matcher statistics + * + * @param pmr_id the id of the PMR from which to retrieve the count + * + * @return The current number of matches for a given matcher instance. + */ + +signed long odp_pmr_match_count(odp_pmr_t pmr_id); +@endverbatim + +@subsubsection pmr_terms_cap odp_pmr_terms_cap +@verbatim +/** + * Inquire about matching terms supported by the classifier + * + * @return A mask one bit per enumerated term, one for each of op_pmr_term_e + */ + +unsigned long long odp_pmr_terms_cap(void); +@endverbatim + +@subsubsection pmr_terms_avail odp_pmr_terms_avail +@verbatim +/** + * Return the number of packet matching terms available for use + * + * @return A number of packet matcher resources available for use. + */ + +unsigned odp_pmr_terms_avail(void); +@endverbatim + +@subsection pmr_composite_rules Pattern Matching Composite Routines +As a shorthand, applications MAY express pattern matching rules using a table rather than constructing them term-by-term. +ODP implementations MUST support both methods of rule specification but MAY have implementation-specific restrictions on the complexity of table-based rules they support. +Note that some implementations MAY be able to implement tables directly while others MAY choose to implement tables by internally generating the equivalent set of term generating calls. + +@subsubsection pmr_table_structure PMR Table Structure +@verbatim +/** + * Following structure is used to define composite packet matching rules + * in the form of an array of individual match or range rules. + * The underlying platform may not support all or any specific combination + * of value match or range rules, and the application should take care + * of inspecting the return value when installing such rules, and perform + * appropriate fallback action. + */ + +typedef struct odp_pmr_match_t { + enum odp_pmr_match_type_e { + ODP_PMR_MASK, /**< Match a masked set of bits */ + ODP_PMR_RANGE, /**< Match an integer range */ + } match_type; + union { + struct { + enum odp_pmr_term_e term; + const void *val; + const void *mask; + unsigned int val_sz; + } mask; /**< Match a masked set of bits */ + struct { + enum odp_pmr_term_e term; + const void *val1; + const void *val2; + unsigned int val_sz; + } range; /**< Match an integer range */ + }; +} odp_pmr_match_t; + + +/** An opaque handle to a composite packet match rule-set */ +typedef struct odp_pmr_set_s *odp_pmr_set_t; +@endverbatim; + +The above structure is used with the following APIs to implement table-based PMRs: + +@subsubsection pmr_match_set_create odp_pmr_match_set_create +@verbatim +/** + * Create a composite packet match rule + * + * @param num_terms is the number of terms in the match rule. + * @param terms is an array of num_terms entries, one entry per + * term desired. + * @param dst_cos is the class-of-service to be assigned to packets + * that match the compound rule-set, or a subset thereof, + * if partly applied. + * @param pmr_set_id is the returned handle to the composite rule set. + * + * @return The return value may be a negative number indicating a general + * error, or a positive number indicating the number of ‘terms’ elements that + * have been successfully mapped to the underlying platform classification engine, + * and may be in the range from 1 to ‘num_terms’. + */ + +int odp_pmr_match_set_create(int num_terms, odp_pmr_match_t *terms, + odp_pmr_set_t *pmr_set_id); +@endverbatim + +This routine is used to create a PMR match set. + It is the equivalent to a cascade of PMRs except that there are no “intermediate” classes of service defined. +Instead, the entire match set either matches or does not match as a single entity. + +@subsubsection pmr_match_set_destroy odp_pmr_match_set_destroy +@verbatim +/** + * Function to delete a composite packet match rule set + * + * All of the resources pertaining to the match set associated with the + * class-of-service will be released, but the class-of-service will + * remain intact. + * + * @param pmr_set_id a composite rule-set handle returned when created. + * + * @note Depending on the implementation details, destroying a rule-set + * may not guarantee the availability of hardware resources to create the + * same or essentially similar rule-set. + */ + +int odp_pmr_match_set_destroy(odp_pmr_set_t pmr_set_id); +@endverbatim + +This routine destroys a PMR match set previously created by odp_pmr_match_set_create(). + +@subsubsection pktio_pmr_match_set_cos odp_pktio_pmr_match_set_cos +@verbatim +/** + * Apply a PMR Match Set to a pktio to assign a CoS. + * + * @param pmr_set_id the id of the PMR match set to be activated + * @param src_pktio the pktio to which this PMR match set is to be applied + * @param dst_cos the CoS to be assigned by this PMR match set + * + * @return Success or ODP_PARAMETER_ERROR + */ + +int odp_pktio_pmr_match_set_cos(odp_pmr_t pmr_id, odp_pktio_t src_pktio, + odp_cos_t dst_cos); +@endverbatim + +This routine is the same as odp_pktio_pmr_cos() except that it operates on PMR match sets rather than individual PMRs. + +@section items_pending Items pending resolution +- Revise ‘odp_packet_io.h’ API with respect of default input queue per ‘pktio’ instance. +- Revise ‘odp_queue.h’ API to support an arbitrary priority range, typically 8 priority levels with numeric priority values are platform-specific. +- Add specific packet meta data fields to go into packet buffer which contain all meta data fields parsed and generated by the classifier, for later application use. + +@section implementation_notes Implementation Notes +The following sections are not part of the specification, but shed light into the intent of the specification in several areas, describing some specific implementation approaches of these aspects. + +@subsection supporting_multi_pools Supporting multiple buffer pools +The support of multiple buffer pools for containing packet buffers is optional, and may not be supported by some platforms. +The importance of this feature stems from the need of protecting a networking application in the event of a congestion, or an attempted denial of service attack. +Separating different classes of service to dedicated buffer pools allows the system to limit the memory resources that may be consumed by a particular type of traffic, thereby reserving buffer resources for other classes of traffic. + +In a software implementation, a packet would already be stored in memory when the classifier is invoked, and so it seems the classifier is unable to insert itself into the process of selecting a buffer pool. +For obvious reasons the copying of a packet into a new buffer allocated from a different pool by the classifier is not a desirable solution. + +The recommended solution is to implement buffer pools in the form of buffer counters, while the actual buffers all belong to a single free list when not used to store a packet. +In such an implementation, the classifier will be able to associate a packet already occupying a buffer to a different pool than the default by incrementing the buffer counter of the newly selected pool, and decrementing the counter representing the default pool. +If however the selected pool counter has already reached a certain limit, the classifier would be able to e.g discard the packet instead of incrementing the destination pool counter, and thereby enforce the desirable semantics of distinct buffer pools per class of service. + +Other possible action that may be taken in response to running out of buffers or coming too low on buffers include back-pressure and random-early-detect with a discard probability inversely proportional to the number of free buffers in a pool. +A related implementation topic is the ability to begin dropping some packets before a buffer pool is entirely exhausted. +This is typically referred to as <em>Random Early Detect</em> (or “RED”). +This is deemed to be a feature of the buffer pool implementation on a given platform, where in addition to a hard limit on the number of buffers that can be allocated to a pool, there can also be an option discard packets with a probability the increases as the number of outstanding buffers approaches that hard limit. + +@subsection resolving_gaps Resolving gaps between the API and hardware capabilities +On platforms that support hardware packet accelerators, it is possible that the packet parsing and classification functionality is sufficient to address only a portion of the functionality specified within this document. +This gap may be potentially bridged by augmenting the hardware classification capabilities with a software logic implemented as part of the platform. +In that case, the platform will have to curve out a fraction of the processing resources and dedicate those to the software classification logic, which would be invoked for packets that the hardware platform was unable to classify completely. +At the time of this writing, it is believed however that the performance penalty that will be incurred as a result of software augmentation is unjustified for most application, i.e. +it is preferred to lose the precision of packet prioritization while maintaining full hardware packet processing speed. + +@subsection loopback_case The case for loopback ports, and some of their uses +In some applications, it may be desirable to be able to run a single packet through the classifier more than once. +For example, an encrypted IPsec packet is received from a physical port. +The encrypted packet is assigned a class of service based on its outer unencrypted header fields. +Later, processing the packet entails decrypting the payload of the packet, authenticating it, and removing the original outer headers, which reveals a new set of protocol headers which need to be used to re-classify the packet, and assign it a new priority and buffer pool. +An elegant solution for this use case would be to take advantage of “loopback” logical ports that may be implemented in certain platforms, by transmitting decapsulated packet into a loop-back port. +The same packet then is received from a loop-back port and is examined by the classifier in accordance to the rules assigned to the loopback odp_pktio logical port instance. +Similar mechanism may be applied to tunnel termination processing, fragment reassembly et al. + +@section related_topics Related Topics +The following section discusses aspects of the ODP API that are not integral to the classifier, which only applies to ingress preprocessing. +This section covers miscellaneous aspects of the API that need to be addressed, and are related to packet buffer processing and egress post-processing. +Additional packet buffer manipulation APIs +The need for these following calls are made evident by the need to encapsulate, i.e., remove some headers and add other, thereby changing the size of the headers of a packet during processing. + +@subsection initial_headroom Configuring initial packet buffer headroom +The following function is provided to configure the pktio receive mechanism to (optionally)reserve some headroom between start of the first buffer to the first byte of the first packet data byte, which subsequently could be used to increase the header size “in-place”, without allocating additional gather list elements. +If the request is granted, at least <req_bytes> bytes will be reserved in the front of the packet data: +@verbatim +int odp_pktio_set_headroom(odp_pktio_t port_id, unsigned req_bytes); +@endverbatim +The return value should be negative if the request can not be satisfied, or positive otherwise indicating the actual minimum headroom reserved. +Note that the implementation may reserve more than the requested amount of headroom, and hence on platforms that are unable to support per-port (or per CoS) headroom configuration, a system-wide headroom configuration may be set to the largest of all such requests, and thus satisfy the requirement. +In addition to the above per-port headroom configuration call, there should be an optional, per-CoS call that allows the reservation of different amounts of packet buffer headroom for packets that match certain criteria: for example, the following call allows the application to request that only packets that are expected to be encapsulated in a tunnel, be augmented with a large headroom amount, while packets that are received from a tunnel, and are IP fragments, be assigned a different headroom requirement (see definition for odp_cos_set_headroom() above. +Egress packet scheduling, prioritization and ordering + + +Open Issues +* Parallel matching rules relative precedence. +* Specify application-defined header field declaration APIs. +* Review RFC 4301 for match requirements for IPsec SA, consider the use of L4 port ranges instead of or in addition to value & mask matching criteria. +* Consider the type of packet checks should route a packet through the error CoS: L2 is a safe choice, but L3/L4 checksum or other exceptions deserve consideration. +Usage Examples +Following is a simple sample configuration using the API elements described above. +TBD. + +*/
Switch to use of @verbatim/@endverbatim to avoid formatting issues with different levels of doxygen. Signed-off-by: Bill Fischofer <bill.fischofer@linaro.org> --- classification_design.dox | 879 +++++++++++++++++++++++++++++++++++++++++ images/classification_flow.png | Bin 0 -> 35193 bytes 2 files changed, 879 insertions(+) create mode 100644 classification_design.dox create mode 100644 images/classification_flow.png diff --git a/images/classification_flow.png b/images/classification_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..2d94ff64ec772aa97f3687181c121fb91d671889 GIT binary patch literal 35193 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 Ru|ekbQbt9(O42mw{{YqNUv2;Z literal 0 HcmV?d00001