| VPP API Language |
| ================ |
| |
| The VPP binary API is a message passing API. The VPP API language is |
| used to define a RPC interface between VPP and its control plane. The |
| API messages supports shared memory transport and Unix domain sockets |
| (SOCK_STREAM). |
| |
| The wire format is essentially that of a network formatted (big-endian) |
| packed C struct. |
| |
| The VPP API compiler is located in *src/tools/vppapigen* and can |
| currently compile to JSON or C (used by the VPP binary itself). |
| |
| Language definition |
| ------------------- |
| |
| Defining a messages |
| ~~~~~~~~~~~~~~~~~~~ |
| |
| There are 3 types of message exchanges: |
| |
| - Request/Reply The client sends a request message and the server |
| replies with a single reply message. The convention is that the reply |
| message is named as method_name + \_reply. |
| |
| - Dump/Detail The client sends a “bulk” request message to the server, |
| and the server replies with a set of detail messages. These messages |
| may be of different type. A dump/detail call must be enclosed in a |
| control ping block (Otherwise the client will not know the end of the |
| bulk transmission). The method name must end with method + “\_dump”, |
| the reply message should be named method + “\_details”. The exception |
| here is for the methods that return multiple message types |
| (e.g. sw_interface_dump). The Dump/Detail methods are typically used |
| for acquiring bulk information, like the complete FIB table. |
| |
| - Events The client can register for getting asynchronous notifications |
| from the server. This is useful for getting interface state changes, |
| and so on. The method name for requesting notifications is |
| conventionally prefixed with “want\_”. E.g. “want_interface_events”. |
| Which notification types results from an event registration is |
| defined in the service definition. |
| |
| A message from a client must include the ‘client_index’, an opaque |
| cookie identifying the sender, and a ‘context’ field to let the client |
| match request with reply. |
| |
| An example of a message definition. The client sends the show_version |
| request, the server replies with the show_version_reply. |
| |
| The *client_index* and *context* fields are required in all requests. |
| The *context* is returned by the server and is used by the client to |
| match up request and reply messages. |
| |
| .. code-block:: c |
| |
| define show_version |
| { |
| u32 client_index; |
| u32 context; |
| }; |
| define show_version_reply |
| { |
| u32 context; |
| i32 retval; |
| string program [32]; |
| string version [32]; |
| string build_date [32]; |
| /* The final field can be a variable length argument */ |
| string build_directory []; |
| }; |
| |
| The flags are not used by the clients, but have special meaning for some |
| of the tracing and debugging of the API. The *autoreply* flag is a |
| shorthand for a reply message with just a *retval* field. |
| |
| .. code-block:: c |
| |
| define : DEFINE ID '{' block_statements_opt '}' ';' |
| define : flist DEFINE ID '{' block_statements_opt '}' ';' |
| flist : flag |
| | flist flag |
| flag : MANUAL_PRINT |
| | MANUAL_ENDIAN |
| | DONT_TRACE |
| | AUTOREPLY |
| |
| block_statements_opt : block_statements |
| block_statements : block_statement |
| | block_statements block_statement |
| block_statement : declaration |
| | option |
| declaration : type_specifier ID ';' |
| | type_specifier ID '[' ID '=' assignee ']' ';' |
| declaration : type_specifier ID '[' NUM ']' ';' |
| | type_specifier ID '[' ID ']' ';' |
| type_specifier : U8 |
| | U16 |
| | U32 |
| | U64 |
| | I8 |
| | I16 |
| | I32 |
| | I64 |
| | F64 |
| | BOOL |
| | STRING |
| type_specifier : ID |
| |
| Options |
| ~~~~~~~ |
| |
| The *option* word is used to specify meta information. The only current |
| use is to specify a semantic version of the .api file itself. |
| |
| Example: |
| |
| .. code-block:: c |
| |
| option version = "1.0.0"; |
| |
| .. code-block:: c |
| |
| |
| option : OPTION ID '=' assignee ';' |
| assignee : NUM |
| | TRUE |
| | FALSE |
| | STRING_LITERAL |
| |
| Defining new types |
| ~~~~~~~~~~~~~~~~~~ |
| |
| New user defined types are defined just like messages. A typedef has two |
| forms. It can either define an alias for a different type (or array). |
| |
| Example: |
| |
| .. code-block:: c |
| |
| typedef u8 ip4_address[4]; |
| typedef u8 ip6_address[16]; |
| |
| Where the above defines two new types *vl_api_ip4_address_t* and |
| *vl_api_ip6_address_t*. These are aliases for the underlying u8 array. |
| |
| In the other form, it is used to specify an abstract data type. |
| |
| .. code-block:: c |
| |
| enum address_family { |
| ADDRESS_IP4 = 0, |
| ADDRESS_IP6, |
| }; |
| |
| union address_union { |
| vl_api_ip4_address_t ip4; |
| vl_api_ip6_address_t ip6; |
| }; |
| |
| typedef address { |
| vl_api_address_family_t af; |
| vl_api_address_union_t un; |
| }; |
| |
| Where the new type *vl_api_address_t* |
| |
| .. code-block:: c |
| |
| typedef : TYPEDEF ID '{' block_statements_opt '}' ';' |
| typedef : TYPEDEF declaration |
| |
| Importing Definitions |
| ~~~~~~~~~~~~~~~~~~~~~ |
| |
| You can use definitions from other .api files by importing them. To |
| import another .api’s definitions, you add an import statement to the |
| top of your file: |
| |
| import “vnet/ip/ip_types.api”; |
| |
| By default you can only use definitions from directly imported .api |
| files. |
| |
| The API compiler searches for imported files in a set of directories |
| specified on the API compiler command line using the –includedir flag. |
| |
| .. code-block:: c |
| |
| import : IMPORT STRING_LITERAL ';' |
| |
| Comments |
| ~~~~~~~~ |
| |
| The API language uses C style comments. |
| |
| .. code-block:: c |
| |
| /* */ |
| // |
| |
| Enumerations |
| ~~~~~~~~~~~~ |
| |
| Enums are similar to enums in C. |
| |
| Every enum definition must contain a constant that maps to zero as its |
| first element. This is because: |
| |
| There must be a zero value, so that we can use 0 as a numeric default |
| value. The zero value needs to be the first element. |
| |
| As in C, enums can be used as flags or just as numbers. The on-wire, and |
| in memory representation size of an enum can be specified. Not all |
| language bindings will support that. The default size is 4 (u32). |
| |
| Example |
| |
| .. code-block:: c |
| |
| enum ip_neighbor_flags |
| { |
| IP_API_NEIGHBOR_FLAG_NONE = 0, |
| IP_API_NEIGHBOR_FLAG_STATIC = 0x1, |
| IP_API_NEIGHBOR_FLAG_NO_FIB_ENTRY = 0x2, |
| }; |
| |
| Which generates the vl_api_ip_neighbor_flags_t in the C binding. In |
| Python that is represented as an IntFlag object |
| VppEnum.vl_api_ip_neighbor_flags_t. |
| |
| .. code-block:: c |
| |
| enum : ENUM ID '{' enum_statements '}' ';' |
| enum : ENUM ID ':' enum_size '{' enum_statements '}' ';' |
| enum_size : U8 |
| | U16 |
| | U32 |
| enum_statements : enum_statement |
| | enum_statements enum_statement |
| enum_statement : ID '=' NUM ',' |
| | ID ',' |
| |
| Services |
| ~~~~~~~~ |
| |
| The service statement defines the relationship between messages. For |
| request/response and dump/details messages it ties the request with the |
| reply. For events, it specifies which events that can be received for a |
| given ``want_*`` call. |
| |
| Example: |
| |
| .. code-block:: c |
| |
| service { |
| rpc want_interface_events returns want_interface_events_reply |
| events sw_interface_event; |
| }; |
| |
| Which states that the request want_interface_events returns a |
| want_interface_events_reply and if enabled the client will receive |
| sw_interface_event messages whenever interface states changes. |
| |
| .. code-block:: c |
| |
| service : SERVICE '{' service_statements '}' ';' |
| service_statements : service_statement |
| | service_statements service_statement |
| service_statement : RPC ID RETURNS NULL ';' |
| | RPC ID RETURNS ID ';' |
| | RPC ID RETURNS STREAM ID ';' |
| | RPC ID RETURNS ID EVENTS event_list ';' |
| event_list : events |
| | event_list events |
| events : ID |
| | ID ',' |
| |
| Types |
| ----- |
| |
| Scalar Value Types |
| ~~~~~~~~~~~~~~~~~~ |
| |
| ========= ======== =============== =========== |
| .api type size C type Python type |
| ========= ======== =============== =========== |
| i8 1 i8 int |
| u8 1 u8 int |
| i16 2 i16 int |
| u16 2 u16 int |
| i32 4 i32 int |
| u32 4 u32 int |
| i64 8 i64 int |
| u64 8 u64 int |
| f64 8 f64 float |
| bool 1 bool boolean |
| string variable vl_api_string_t str |
| ========= ======== =============== =========== |
| |
| User Defined Types |
| ~~~~~~~~~~~~~~~~~~ |
| |
| vnet/ip/ip_types.api |
| ^^^^^^^^^^^^^^^^^^^^ |
| |
| +--------------------+--------+-------------+-------------------------+ |
| | .api type | size | C type | Python type | |
| +====================+========+=============+=========================+ |
| | vl_api_address_t | 20 | vl_ap | ` | |
| | | | i_address_t | `<class 'ipaddress.IPv4 | |
| | | | | Address'> or <class 'ip | |
| | | | | address.IPv6Address'>`` | |
| +--------------------+--------+-------------+-------------------------+ |
| | vl | 4 | vl_api_ip | ``<class 'ip | |
| | _api_ip4_address_t | | 4_address_t | address.IPv4Address'>`` | |
| +--------------------+--------+-------------+-------------------------+ |
| | vl | 16 | vl_api_ip | ``<class 'ip | |
| | _api_ip6_address_t | | 6_address_t | address.IPv6Address'>`` | |
| +--------------------+--------+-------------+-------------------------+ |
| | vl_api_prefix_t | 21 | vl_a | ` | |
| | | | pi_prefix_t | `<class 'ipaddress.IPv4 | |
| | | | | Network'> or <class 'ip | |
| | | | | address.IPv6Network'>`` | |
| +--------------------+--------+-------------+-------------------------+ |
| | v | 5 | vl_api_i | ``<class 'ip | |
| | l_api_ip4_prefix_t | | p4_prefix_t | address.IPv4Network'>`` | |
| +--------------------+--------+-------------+-------------------------+ |
| | v | 17 | vl_api_i | ``<class 'ip | |
| | l_api_ip6_prefix_t | | p6_prefix_t | address.IPv6Network'>`` | |
| +--------------------+--------+-------------+-------------------------+ |
| | vl_api_ip4_add | 5 | vl_api_ip4 | ``<class 'ipad | |
| | ress_with_prefix_t | | _address_wi | dress.IPv4Interface'>`` | |
| | | | th_prefix_t | | |
| +--------------------+--------+-------------+-------------------------+ |
| | vl_api_ip6_add | 17 | vl_api_ip6 | ``<class 'ipad | |
| | ress_with_prefix_t | | _address_wi | dress.IPv6Interface'>`` | |
| | | | th_prefix_t | | |
| +--------------------+--------+-------------+-------------------------+ |
| |
| vnet/ethernet/ethernet_types.api |
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| |
| +---------------------+------+---------------------+-------------------+ |
| | .api type | size | C type | Python type | |
| +=====================+======+=====================+===================+ |
| | ``vl_ | 6 | ``vl_ | ``class 'vpp_pa | |
| | api_mac_address_t`` | | api_mac_address_t`` | pi.MACAddress'>`` | |
| +---------------------+------+---------------------+-------------------+ |
| |
| vnet/interface_types.api |
| ^^^^^^^^^^^^^^^^^^^^^^^^ |
| |
| ======================== ==== ======================== =========== |
| .api type size C type Python type |
| ======================== ==== ======================== =========== |
| vl_api_interface_index_t 4 vl_api_interface_index_t int |
| ======================== ==== ======================== =========== |
| |
| New explicit types |
| ~~~~~~~~~~~~~~~~~~ |
| |
| String versus bytes |
| ^^^^^^^^^^^^^^^^^^^ |
| |
| A byte string with a maximum length of 64: |
| |
| .. code-block:: c |
| |
| u8 name[64]; |
| |
| Before the “string” type was added, text string were defined like this. |
| The implications of that was the user would have to know if the field |
| represented a \\0 ended C-string or a fixed length byte string. The wire |
| format of the ‘string’ type is a u32 length |
| |
| An IPv4 or IPv6 address was previously defined like: |
| |
| .. code-block:: c |
| |
| u8 is_ip6; |
| u8 address[16]; |
| |
| Which made it hard for language bindings to represent the address as |
| anything but a byte string. The new explicit address types are shown |
| above. |
| |
| Language generators |
| ------------------- |
| |
| The VPP API compiler currently has two output modules. One generating |
| JSON and one generating C header files that are directly used by the VPP |
| infrastructure and plugins. |
| |
| The C/C++, Python, Go Lua, and Java language bindings are generated |
| based on the JSON files. |
| |
| Future considerations |
| ~~~~~~~~~~~~~~~~~~~~~ |
| |
| - Generate C/C++ (vapi) client code directly from vppapigen |
| - Embed JSON definitions into the API server, so dynamic languages |
| can download them directly without going via the filesystem and JSON |
| files. |