Nathan Skrzypczak | d4a7064 | 2021-10-08 14:01:27 +0200 | [diff] [blame] | 1 | VPP API Language |
| 2 | ================ |
| 3 | |
| 4 | The VPP binary API is a message passing API. The VPP API language is |
| 5 | used to define a RPC interface between VPP and its control plane. The |
| 6 | API messages supports shared memory transport and Unix domain sockets |
| 7 | (SOCK_STREAM). |
| 8 | |
| 9 | The wire format is essentially that of a network formatted (big-endian) |
| 10 | packed C struct. |
| 11 | |
| 12 | The VPP API compiler is located in *src/tools/vppapigen* and can |
| 13 | currently compile to JSON or C (used by the VPP binary itself). |
| 14 | |
| 15 | Language definition |
| 16 | ------------------- |
| 17 | |
| 18 | Defining a messages |
| 19 | ~~~~~~~~~~~~~~~~~~~ |
| 20 | |
| 21 | There are 3 types of message exchanges: |
| 22 | |
| 23 | - Request/Reply The client sends a request message and the server |
| 24 | replies with a single reply message. The convention is that the reply |
| 25 | message is named as method_name + \_reply. |
| 26 | |
| 27 | - Dump/Detail The client sends a “bulk” request message to the server, |
| 28 | and the server replies with a set of detail messages. These messages |
| 29 | may be of different type. A dump/detail call must be enclosed in a |
| 30 | control ping block (Otherwise the client will not know the end of the |
| 31 | bulk transmission). The method name must end with method + “\_dump”, |
| 32 | the reply message should be named method + “\_details”. The exception |
| 33 | here is for the methods that return multiple message types |
| 34 | (e.g. sw_interface_dump). The Dump/Detail methods are typically used |
| 35 | for acquiring bulk information, like the complete FIB table. |
| 36 | |
| 37 | - Events The client can register for getting asynchronous notifications |
| 38 | from the server. This is useful for getting interface state changes, |
| 39 | and so on. The method name for requesting notifications is |
| 40 | conventionally prefixed with “want\_”. E.g. “want_interface_events”. |
| 41 | Which notification types results from an event registration is |
| 42 | defined in the service definition. |
| 43 | |
| 44 | A message from a client must include the ‘client_index’, an opaque |
| 45 | cookie identifying the sender, and a ‘context’ field to let the client |
| 46 | match request with reply. |
| 47 | |
| 48 | An example of a message definition. The client sends the show_version |
| 49 | request, the server replies with the show_version_reply. |
| 50 | |
| 51 | The *client_index* and *context* fields are required in all requests. |
| 52 | The *context* is returned by the server and is used by the client to |
| 53 | match up request and reply messages. |
| 54 | |
| 55 | .. code-block:: c |
| 56 | |
| 57 | define show_version |
| 58 | { |
| 59 | u32 client_index; |
| 60 | u32 context; |
| 61 | }; |
| 62 | define show_version_reply |
| 63 | { |
| 64 | u32 context; |
| 65 | i32 retval; |
| 66 | string program [32]; |
| 67 | string version [32]; |
| 68 | string build_date [32]; |
| 69 | /* The final field can be a variable length argument */ |
| 70 | string build_directory []; |
| 71 | }; |
| 72 | |
| 73 | The flags are not used by the clients, but have special meaning for some |
| 74 | of the tracing and debugging of the API. The *autoreply* flag is a |
| 75 | shorthand for a reply message with just a *retval* field. |
| 76 | |
| 77 | .. code-block:: c |
| 78 | |
| 79 | define : DEFINE ID '{' block_statements_opt '}' ';' |
| 80 | define : flist DEFINE ID '{' block_statements_opt '}' ';' |
| 81 | flist : flag |
| 82 | | flist flag |
| 83 | flag : MANUAL_PRINT |
| 84 | | MANUAL_ENDIAN |
| 85 | | DONT_TRACE |
| 86 | | AUTOREPLY |
| 87 | |
| 88 | block_statements_opt : block_statements |
| 89 | block_statements : block_statement |
| 90 | | block_statements block_statement |
| 91 | block_statement : declaration |
| 92 | | option |
| 93 | declaration : type_specifier ID ';' |
| 94 | | type_specifier ID '[' ID '=' assignee ']' ';' |
| 95 | declaration : type_specifier ID '[' NUM ']' ';' |
| 96 | | type_specifier ID '[' ID ']' ';' |
| 97 | type_specifier : U8 |
| 98 | | U16 |
| 99 | | U32 |
| 100 | | U64 |
| 101 | | I8 |
| 102 | | I16 |
| 103 | | I32 |
| 104 | | I64 |
| 105 | | F64 |
| 106 | | BOOL |
| 107 | | STRING |
| 108 | type_specifier : ID |
| 109 | |
| 110 | Options |
| 111 | ~~~~~~~ |
| 112 | |
| 113 | The *option* word is used to specify meta information. The only current |
| 114 | use is to specify a semantic version of the .api file itself. |
| 115 | |
| 116 | Example: |
| 117 | |
| 118 | .. code-block:: c |
| 119 | |
| 120 | option version = "1.0.0"; |
| 121 | |
| 122 | .. code-block:: c |
| 123 | |
| 124 | |
| 125 | option : OPTION ID '=' assignee ';' |
| 126 | assignee : NUM |
| 127 | | TRUE |
| 128 | | FALSE |
| 129 | | STRING_LITERAL |
| 130 | |
| 131 | Defining new types |
| 132 | ~~~~~~~~~~~~~~~~~~ |
| 133 | |
| 134 | New user defined types are defined just like messages. A typedef has two |
| 135 | forms. It can either define an alias for a different type (or array). |
| 136 | |
| 137 | Example: |
| 138 | |
| 139 | .. code-block:: c |
| 140 | |
| 141 | typedef u8 ip4_address[4]; |
| 142 | typedef u8 ip6_address[16]; |
| 143 | |
| 144 | Where the above defines two new types *vl_api_ip4_address_t* and |
| 145 | *vl_api_ip6_address_t*. These are aliases for the underlying u8 array. |
| 146 | |
| 147 | In the other form, it is used to specify an abstract data type. |
| 148 | |
| 149 | .. code-block:: c |
| 150 | |
| 151 | enum address_family { |
| 152 | ADDRESS_IP4 = 0, |
| 153 | ADDRESS_IP6, |
| 154 | }; |
| 155 | |
| 156 | union address_union { |
| 157 | vl_api_ip4_address_t ip4; |
| 158 | vl_api_ip6_address_t ip6; |
| 159 | }; |
| 160 | |
| 161 | typedef address { |
| 162 | vl_api_address_family_t af; |
| 163 | vl_api_address_union_t un; |
| 164 | }; |
| 165 | |
| 166 | Where the new type *vl_api_address_t* |
| 167 | |
| 168 | .. code-block:: c |
| 169 | |
| 170 | typedef : TYPEDEF ID '{' block_statements_opt '}' ';' |
| 171 | typedef : TYPEDEF declaration |
| 172 | |
| 173 | Importing Definitions |
| 174 | ~~~~~~~~~~~~~~~~~~~~~ |
| 175 | |
| 176 | You can use definitions from other .api files by importing them. To |
| 177 | import another .api’s definitions, you add an import statement to the |
| 178 | top of your file: |
| 179 | |
| 180 | import “vnet/ip/ip_types.api”; |
| 181 | |
| 182 | By default you can only use definitions from directly imported .api |
| 183 | files. |
| 184 | |
| 185 | The API compiler searches for imported files in a set of directories |
| 186 | specified on the API compiler command line using the –includedir flag. |
| 187 | |
| 188 | .. code-block:: c |
| 189 | |
| 190 | import : IMPORT STRING_LITERAL ';' |
| 191 | |
| 192 | Comments |
| 193 | ~~~~~~~~ |
| 194 | |
| 195 | The API language uses C style comments. |
| 196 | |
| 197 | .. code-block:: c |
| 198 | |
| 199 | /* */ |
| 200 | // |
| 201 | |
| 202 | Enumerations |
| 203 | ~~~~~~~~~~~~ |
| 204 | |
| 205 | Enums are similar to enums in C. |
| 206 | |
| 207 | Every enum definition must contain a constant that maps to zero as its |
| 208 | first element. This is because: |
| 209 | |
| 210 | There must be a zero value, so that we can use 0 as a numeric default |
| 211 | value. The zero value needs to be the first element. |
| 212 | |
| 213 | As in C, enums can be used as flags or just as numbers. The on-wire, and |
| 214 | in memory representation size of an enum can be specified. Not all |
| 215 | language bindings will support that. The default size is 4 (u32). |
| 216 | |
| 217 | Example |
| 218 | |
| 219 | .. code-block:: c |
| 220 | |
| 221 | enum ip_neighbor_flags |
| 222 | { |
| 223 | IP_API_NEIGHBOR_FLAG_NONE = 0, |
| 224 | IP_API_NEIGHBOR_FLAG_STATIC = 0x1, |
| 225 | IP_API_NEIGHBOR_FLAG_NO_FIB_ENTRY = 0x2, |
| 226 | }; |
| 227 | |
| 228 | Which generates the vl_api_ip_neighbor_flags_t in the C binding. In |
| 229 | Python that is represented as an IntFlag object |
| 230 | VppEnum.vl_api_ip_neighbor_flags_t. |
| 231 | |
| 232 | .. code-block:: c |
| 233 | |
| 234 | enum : ENUM ID '{' enum_statements '}' ';' |
| 235 | enum : ENUM ID ':' enum_size '{' enum_statements '}' ';' |
| 236 | enum_size : U8 |
| 237 | | U16 |
| 238 | | U32 |
| 239 | enum_statements : enum_statement |
| 240 | | enum_statements enum_statement |
| 241 | enum_statement : ID '=' NUM ',' |
| 242 | | ID ',' |
| 243 | |
| 244 | Services |
| 245 | ~~~~~~~~ |
| 246 | |
| 247 | The service statement defines the relationship between messages. For |
| 248 | request/response and dump/details messages it ties the request with the |
| 249 | reply. For events, it specifies which events that can be received for a |
| 250 | given ``want_*`` call. |
| 251 | |
| 252 | Example: |
| 253 | |
| 254 | .. code-block:: c |
| 255 | |
| 256 | service { |
| 257 | rpc want_interface_events returns want_interface_events_reply |
| 258 | events sw_interface_event; |
| 259 | }; |
| 260 | |
| 261 | Which states that the request want_interface_events returns a |
| 262 | want_interface_events_reply and if enabled the client will receive |
| 263 | sw_interface_event messages whenever interface states changes. |
| 264 | |
| 265 | .. code-block:: c |
| 266 | |
| 267 | service : SERVICE '{' service_statements '}' ';' |
| 268 | service_statements : service_statement |
| 269 | | service_statements service_statement |
| 270 | service_statement : RPC ID RETURNS NULL ';' |
| 271 | | RPC ID RETURNS ID ';' |
| 272 | | RPC ID RETURNS STREAM ID ';' |
| 273 | | RPC ID RETURNS ID EVENTS event_list ';' |
| 274 | event_list : events |
| 275 | | event_list events |
| 276 | events : ID |
| 277 | | ID ',' |
| 278 | |
| 279 | Types |
| 280 | ----- |
| 281 | |
| 282 | Scalar Value Types |
| 283 | ~~~~~~~~~~~~~~~~~~ |
| 284 | |
| 285 | ========= ======== =============== =========== |
| 286 | .api type size C type Python type |
| 287 | ========= ======== =============== =========== |
| 288 | i8 1 i8 int |
| 289 | u8 1 u8 int |
| 290 | i16 2 i16 int |
| 291 | u16 2 u16 int |
| 292 | i32 4 i32 int |
| 293 | u32 4 u32 int |
| 294 | i64 8 i64 int |
| 295 | u64 8 u64 int |
| 296 | f64 8 f64 float |
| 297 | bool 1 bool boolean |
| 298 | string variable vl_api_string_t str |
| 299 | ========= ======== =============== =========== |
| 300 | |
| 301 | User Defined Types |
| 302 | ~~~~~~~~~~~~~~~~~~ |
| 303 | |
| 304 | vnet/ip/ip_types.api |
| 305 | ^^^^^^^^^^^^^^^^^^^^ |
| 306 | |
| 307 | +--------------------+--------+-------------+-------------------------+ |
| 308 | | .api type | size | C type | Python type | |
| 309 | +====================+========+=============+=========================+ |
| 310 | | vl_api_address_t | 20 | vl_ap | ` | |
| 311 | | | | i_address_t | `<class 'ipaddress.IPv4 | |
| 312 | | | | | Address'> or <class 'ip | |
| 313 | | | | | address.IPv6Address'>`` | |
| 314 | +--------------------+--------+-------------+-------------------------+ |
| 315 | | vl | 4 | vl_api_ip | ``<class 'ip | |
| 316 | | _api_ip4_address_t | | 4_address_t | address.IPv4Address'>`` | |
| 317 | +--------------------+--------+-------------+-------------------------+ |
| 318 | | vl | 16 | vl_api_ip | ``<class 'ip | |
| 319 | | _api_ip6_address_t | | 6_address_t | address.IPv6Address'>`` | |
| 320 | +--------------------+--------+-------------+-------------------------+ |
| 321 | | vl_api_prefix_t | 21 | vl_a | ` | |
| 322 | | | | pi_prefix_t | `<class 'ipaddress.IPv4 | |
| 323 | | | | | Network'> or <class 'ip | |
| 324 | | | | | address.IPv6Network'>`` | |
| 325 | +--------------------+--------+-------------+-------------------------+ |
| 326 | | v | 5 | vl_api_i | ``<class 'ip | |
| 327 | | l_api_ip4_prefix_t | | p4_prefix_t | address.IPv4Network'>`` | |
| 328 | +--------------------+--------+-------------+-------------------------+ |
| 329 | | v | 17 | vl_api_i | ``<class 'ip | |
| 330 | | l_api_ip6_prefix_t | | p6_prefix_t | address.IPv6Network'>`` | |
| 331 | +--------------------+--------+-------------+-------------------------+ |
| 332 | | vl_api_ip4_add | 5 | vl_api_ip4 | ``<class 'ipad | |
| 333 | | ress_with_prefix_t | | _address_wi | dress.IPv4Interface'>`` | |
| 334 | | | | th_prefix_t | | |
| 335 | +--------------------+--------+-------------+-------------------------+ |
| 336 | | vl_api_ip6_add | 17 | vl_api_ip6 | ``<class 'ipad | |
| 337 | | ress_with_prefix_t | | _address_wi | dress.IPv6Interface'>`` | |
| 338 | | | | th_prefix_t | | |
| 339 | +--------------------+--------+-------------+-------------------------+ |
| 340 | |
| 341 | vnet/ethernet/ethernet_types.api |
| 342 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 343 | |
| 344 | +---------------------+------+---------------------+-------------------+ |
| 345 | | .api type | size | C type | Python type | |
| 346 | +=====================+======+=====================+===================+ |
| 347 | | ``vl_ | 6 | ``vl_ | ``class 'vpp_pa | |
| 348 | | api_mac_address_t`` | | api_mac_address_t`` | pi.MACAddress'>`` | |
| 349 | +---------------------+------+---------------------+-------------------+ |
| 350 | |
| 351 | vnet/interface_types.api |
| 352 | ^^^^^^^^^^^^^^^^^^^^^^^^ |
| 353 | |
| 354 | ======================== ==== ======================== =========== |
| 355 | .api type size C type Python type |
| 356 | ======================== ==== ======================== =========== |
| 357 | vl_api_interface_index_t 4 vl_api_interface_index_t int |
| 358 | ======================== ==== ======================== =========== |
| 359 | |
| 360 | New explicit types |
| 361 | ~~~~~~~~~~~~~~~~~~ |
| 362 | |
| 363 | String versus bytes |
| 364 | ^^^^^^^^^^^^^^^^^^^ |
| 365 | |
| 366 | A byte string with a maximum length of 64: |
| 367 | |
| 368 | .. code-block:: c |
| 369 | |
| 370 | u8 name[64]; |
| 371 | |
| 372 | Before the “string” type was added, text string were defined like this. |
| 373 | The implications of that was the user would have to know if the field |
| 374 | represented a \\0 ended C-string or a fixed length byte string. The wire |
| 375 | format of the ‘string’ type is a u32 length |
| 376 | |
| 377 | An IPv4 or IPv6 address was previously defined like: |
| 378 | |
| 379 | .. code-block:: c |
| 380 | |
| 381 | u8 is_ip6; |
| 382 | u8 address[16]; |
| 383 | |
| 384 | Which made it hard for language bindings to represent the address as |
| 385 | anything but a byte string. The new explicit address types are shown |
| 386 | above. |
| 387 | |
| 388 | Language generators |
| 389 | ------------------- |
| 390 | |
| 391 | The VPP API compiler currently has two output modules. One generating |
| 392 | JSON and one generating C header files that are directly used by the VPP |
| 393 | infrastructure and plugins. |
| 394 | |
| 395 | The C/C++, Python, Go Lua, and Java language bindings are generated |
| 396 | based on the JSON files. |
| 397 | |
| 398 | Future considerations |
| 399 | ~~~~~~~~~~~~~~~~~~~~~ |
| 400 | |
| 401 | - Generate C/C++ (vapi) client code directly from vppapigen |
| 402 | - Embed JSON definitions into the API server, so dynamic languages |
| 403 | can download them directly without going via the filesystem and JSON |
| 404 | files. |