blob: f93bbc296fcb0b88592be1efe002f15cc0d7abd0 [file] [log] [blame]
andrew74030262018-08-05 21:18:45 -04001.. _binary_api_support:
2
3.. toctree::
4
5Binary API Support
6==================
7
8VPP provides a binary API scheme to allow a wide variety of client
9codes to program data-plane tables. As of this writing, there are
10hundreds of binary APIs.
11
12Messages are defined in \*.api files. Today, there are about 80 api
13files, with more arriving as folks add programmable features. The API
14file compiler sources reside in src/tools/vppapigen.
15
16From `src/vnet/interface.api
17<https://docs.fd.io/vpp/18.11/de/d75/interface_8api.html>`_, here's a
18typical request/response message definition:
19
20.. code-block:: console
21
22 autoreply define sw_interface_set_flags
23 {
24 u32 client_index;
25 u32 context;
26 u32 sw_if_index;
27 /* 1 = up, 0 = down */
28 u8 admin_up_down;
29 };
30
31To a first approximation, the API compiler renders this definition
32into
33*vpp/build-root/install-vpp_debug-native/vpp/include/vnet/interface.api.h*
34as follows:
35
36.. code-block:: C
37
38 /****** Message ID / handler enum ******/
39
40 #ifdef vl_msg_id
41 vl_msg_id(VL_API_SW_INTERFACE_SET_FLAGS, vl_api_sw_interface_set_flags_t_handler)
42 vl_msg_id(VL_API_SW_INTERFACE_SET_FLAGS_REPLY, vl_api_sw_interface_set_flags_reply_t_handler)
Dave Barachaf577992019-07-26 08:26:03 -040043 #endif
andrew74030262018-08-05 21:18:45 -040044 /****** Message names ******/
45
46 #ifdef vl_msg_name
47 vl_msg_name(vl_api_sw_interface_set_flags_t, 1)
48 vl_msg_name(vl_api_sw_interface_set_flags_reply_t, 1)
Dave Barachaf577992019-07-26 08:26:03 -040049 #endif
andrew74030262018-08-05 21:18:45 -040050 /****** Message name, crc list ******/
51
52 #ifdef vl_msg_name_crc_list
53 #define foreach_vl_msg_name_crc_interface \
54 _(VL_API_SW_INTERFACE_SET_FLAGS, sw_interface_set_flags, f890584a) \
55 _(VL_API_SW_INTERFACE_SET_FLAGS_REPLY, sw_interface_set_flags_reply, dfbf3afa) \
Dave Barachaf577992019-07-26 08:26:03 -040056 #endif
andrew74030262018-08-05 21:18:45 -040057 /****** Typedefs *****/
58
59 #ifdef vl_typedefs
60 #ifndef defined_sw_interface_set_flags
61 #define defined_sw_interface_set_flags
62 typedef VL_API_PACKED(struct _vl_api_sw_interface_set_flags {
63 u16 _vl_msg_id;
64 u32 client_index;
65 u32 context;
66 u32 sw_if_index;
67 u8 admin_up_down;
68 }) vl_api_sw_interface_set_flags_t;
69 #endif
70
71 #ifndef defined_sw_interface_set_flags_reply
72 #define defined_sw_interface_set_flags_reply
73 typedef VL_API_PACKED(struct _vl_api_sw_interface_set_flags_reply {
74 u16 _vl_msg_id;
75 u32 context;
76 i32 retval;
77 }) vl_api_sw_interface_set_flags_reply_t;
78 #endif
79 ...
80 #endif /* vl_typedefs */
81
82To change the admin state of an interface, a binary api client sends a
83`vl_api_sw_interface_set_flags_t
84<https://docs.fd.io/vpp/18.11/dc/da3/structvl__api__sw__interface__set__flags__t.html>`_
85to VPP, which will respond with a
86vl_api_sw_interface_set_flags_reply_t message.
87
88Multiple layers of software, transport types, and shared libraries
89implement a variety of features:
90
91* API message allocation, tracing, pretty-printing, and replay.
92* Message transport via global shared memory, pairwise/private shared memory, and sockets.
93* Barrier synchronization of worker threads across thread-unsafe message handlers.
94
95Correctly-coded message handlers know nothing about the transport used
Paul Vinciguerra7fa3dd22019-10-27 17:28:10 -040096to deliver messages to/from VPP. It's reasonably straightforward to use
andrew74030262018-08-05 21:18:45 -040097multiple API message transport types simultaneously.
98
99For historical reasons, binary api messages are (putatively) sent in
100network byte order. As of this writing, we're seriously considering
101whether that choice makes sense.
102
103Message Allocation
104__________________
105
106Since binary API messages are always processed in order, we allocate
107messages using a ring allocator whenever possible. This scheme is
108extremely fast when compared with a traditional memory allocator, and
109doesn't cause heap fragmentation. See `src/vlibmemory/memory_shared.c
110<https://docs.fd.io/vpp/18.11/dd/d0d/memory__shared_8c.html>`_
111`vl_msg_api_alloc_internal()
112<https://docs.fd.io/vpp/18.11/dd/d0d/memory__shared_8c.html#ac6b6797850e1a53bc68b206e6b8413fb>`_.
113
114Regardless of transport, binary api messages always follow a `msgbuf_t <https://docs.fd.io/vpp/18.11/d9/d65/structmsgbuf__.html>`_ header:
115
116.. code-block:: C
117
118 /** Message header structure */
119 typedef struct msgbuf_
120 {
121 svm_queue_t *q; /**< message allocated in this shmem ring */
122 u32 data_len; /**< message length not including header */
123 u32 gc_mark_timestamp; /**< message garbage collector mark TS */
124 u8 data[0]; /**< actual message begins here */
125 } msgbuf_t;
126
127This structure makes it easy to trace messages without having to
128decode them - simply save data_len bytes - and allows
129`vl_msg_api_free()
130<https://docs.fd.io/vpp/18.11/d6/d1b/api__common_8h.html#aff61e777fe5df789121d8e78134867e6>`_
131to rapidly dispose of message buffers:
132
133.. code-block:: C
134
135 void
136 vl_msg_api_free (void *a)
137 {
138 msgbuf_t *rv;
139 void *oldheap;
140 api_main_t *am = &api_main;
141
142 rv = (msgbuf_t *) (((u8 *) a) - offsetof (msgbuf_t, data));
143
144 /*
145 * Here's the beauty of the scheme. Only one proc/thread has
146 * control of a given message buffer. To free a buffer, we just clear the
147 * queue field, and leave. No locks, no hits, no errors...
148 */
149 if (rv->q)
150 {
151 rv->q = 0;
152 rv->gc_mark_timestamp = 0;
153 <more code...>
154 return;
155 }
156 <more code...>
157 }
158
159Message Tracing and Replay
160__________________________
161
162It's extremely important that VPP can capture and replay sizeable
163binary API traces. System-level issues involving hundreds of thousands
164of API transactions can be re-run in a second or less. Partial replay
165allows one to binary-search for the point where the wheels fall
166off. One can add scaffolding to the data plane, to trigger when
167complex conditions obtain.
168
169With binary API trace, print, and replay, system-level bug reports of
170the form "after 300,000 API transactions, the VPP data-plane stopped
171forwarding traffic, FIX IT!" can be solved offline.
172
173More often than not, one discovers that a control-plane client
174misprograms the data plane after a long time or under complex
175circumstances. Without direct evidence, "it's a data-plane problem!"
176
177See `src/vlibmemory/memory_vlib::c
178<https://docs.fd.io/vpp/18.11/dd/d3e/vpp__get__metrics_8c.html#a7c3855ed3c45b48ff92a7e881bfede73>`_
179`vl_msg_api_process_file()
180<https://docs.fd.io/vpp/18.11/d0/d5b/vlib__api__cli_8c.html#a60194e3e91c0dc6a75906ea06f4ec113>`_,
181and `src/vlibapi/api_shared.c
182<https://docs.fd.io/vpp/18.11/d6/dd1/api__shared_8c.html>`_. See also
183the debug CLI command "api trace"
184
Dave Barachaf577992019-07-26 08:26:03 -0400185API trace replay caveats
186________________________
187
188The vpp instance which replays a binary API trace must have the same
189message-ID numbering space as the vpp instance which captured the
190trace. The replay instance **must** load the same set of plugins as
191the capture instance. Otherwise, API messages will be processed by the
192**wrong** API message handlers!
193
194Always start vpp with command-line arguments which include an
195"api-trace on" stanza, so vpp will start tracing binary API messages
196from the beginning:
197
198.. code-block:: console
199
200 api-trace {
201 on
202 }
203
204Given a binary api trace in /tmp/api_trace, do the following to work
205out the set of plugins:
206
207.. code-block:: console
208
Filip Tehlar36217e32021-07-23 08:51:10 +0000209 DBGvpp# api trace dump /tmp/api_trace
210 vl_api_trace_plugin_msg_ids: arp_cfdf7292 first 49 last 56
211 vl_api_trace_plugin_msg_ids: ip6_nd_ac628462 first 57 last 69
212 vl_api_trace_plugin_msg_ids: rd_cp_8a996e86 first 70 last 71
Dave Barachaf577992019-07-26 08:26:03 -0400213 <etc>
214
215Here, we see the "abf," "acl," "cdp," and "flowprobe" plugins. Use the
216list of plugins to construct a matching "plugins" command-line argument
217stanza:
218
219.. code-block:: console
220
221 plugins {
222 ## Disable all plugins, selectively enable specific plugins
223 plugin default { disable }
224 plugin abf_plugin.so { enable }
225 plugin acl_plugin.so { enable }
226 plugin cdp_plugin.so { enable }
227 plugin flowprobe_plugin.so { enable }
228 }
229
230To begin with, use the same vpp image that captured a trace to replay
231it. It's perfectly fair to rebuild the vpp replay instance, to add
232scaffolding to facilitate setting gdb breakpoints on complex
233conditions or similar.
234
235API trace interface issues
236__________________________
237
238Along the same lines, it may be necessary to manufacture [simulated]
239physical interfaces so that an API trace will replay correctly. "show
240interface" on the trace origin system can help. An API trace
Filip Tehlar36217e32021-07-23 08:51:10 +0000241dump as shown above may make it obvious how many loopback
Dave Barachaf577992019-07-26 08:26:03 -0400242interfaces to create. If you see vhost interfaces being created and
243then configured, the first such configuration message in the trace
244will tell you how many physical interfaces were involved.
245
246.. code-block:: console
247
248 SCRIPT: create_vhost_user_if socket /tmp/foosock server
249 SCRIPT: sw_interface_set_flags sw_if_index 3 admin-up
250
251In this case, it's fair to guess that one needs to create two loopback
252interfaces to "help" the trace replay correctly.
253
254These issues can be mitigated to a certain extent by replaying the
255trace on the system which created it, but in a field debug case that's
256not a realistic.
257
andrew74030262018-08-05 21:18:45 -0400258Client connection details
259_________________________
260
261Establishing a binary API connection to VPP from a C-language client is easy:
262
263.. code-block:: C
264
265 int
266 connect_to_vpe (char *client_name, int client_message_queue_length)
267 {
268 vat_main_t *vam = &vat_main;
269 api_main_t *am = &api_main;
Dave Barachaf577992019-07-26 08:26:03 -0400270 if (vl_client_connect_to_vlib ("/vpe-api", client_name,
andrew74030262018-08-05 21:18:45 -0400271 client_message_queue_length) < 0)
272 return -1;
273 /* Memorize vpp's binary API message input queue address */
274 vam->vl_input_queue = am->shmem_hdr->vl_input_queue;
275 /* And our client index */
276 vam->my_client_index = am->my_client_index;
277 return 0;
Dave Barachaf577992019-07-26 08:26:03 -0400278 }
andrew74030262018-08-05 21:18:45 -0400279
28032 is a typical value for client_message_queue_length. VPP *cannot*
281block when it needs to send an API message to a binary API client. The
282VPP-side binary API message handlers are very fast. So, when sending
283asynchronous messages, make sure to scrape the binary API rx ring with
284some enthusiasm!
285
286**Binary API message RX pthread**
287
288Calling `vl_client_connect_to_vlib
289<https://docs.fd.io/vpp/18.11/da/d25/memory__client_8h.html#a6654b42c91be33bfb6a4b4bfd2327920>`_
290spins up a binary API message RX pthread:
291
292.. code-block:: C
293
294 static void *
295 rx_thread_fn (void *arg)
296 {
297 svm_queue_t *q;
298 memory_client_main_t *mm = &memory_client_main;
299 api_main_t *am = &api_main;
300 int i;
301
302 q = am->vl_input_queue;
303
304 /* So we can make the rx thread terminate cleanly */
305 if (setjmp (mm->rx_thread_jmpbuf) == 0)
306 {
307 mm->rx_thread_jmpbuf_valid = 1;
308 /*
309 * Find an unused slot in the per-cpu-mheaps array,
310 * and grab it for this thread. We need to be able to
311 * push/pop the thread heap without affecting other thread(s).
312 */
313 if (__os_thread_index == 0)
314 {
315 for (i = 0; i < ARRAY_LEN (clib_per_cpu_mheaps); i++)
316 {
317 if (clib_per_cpu_mheaps[i] == 0)
318 {
319 /* Copy the main thread mheap pointer */
320 clib_per_cpu_mheaps[i] = clib_per_cpu_mheaps[0];
321 __os_thread_index = i;
322 break;
323 }
324 }
325 ASSERT (__os_thread_index > 0);
326 }
327 while (1)
328 vl_msg_api_queue_handler (q);
329 }
330 pthread_exit (0);
331 }
332
333To handle the binary API message queue yourself, use
334`vl_client_connect_to_vlib_no_rx_pthread
335<https://docs.fd.io/vpp/18.11/da/d25/memory__client_8h.html#a11b9577297106c57c0783b96ab190c36>`_.
336
337**Queue non-empty signalling**
338
339vl_msg_api_queue_handler(...) uses mutex/condvar signalling to wake
340up, process VPP -> client traffic, then sleep. VPP supplies a condvar
341broadcast when the VPP -> client API message queue transitions from
342empty to nonempty.
343
344VPP checks its own binary API input queue at a very high rate. VPP
345invokes message handlers in "process" context [aka cooperative
346multitasking thread context] at a variable rate, depending on
347data-plane packet processing requirements.
348
349Client disconnection details
350____________________________
351
352To disconnect from VPP, call `vl_client_disconnect_from_vlib
353<https://docs.fd.io/vpp/18.11/da/d25/memory__client_8h.html#a82c9ba6e7ead8362ae2175eefcf2fd12>`_. Please
354arrange to call this function if the client application terminates
355abnormally. VPP makes every effort to hold a decent funeral for dead
356clients, but VPP can't guarantee to free leaked memory in the shared
357binary API segment.
358
359Sending binary API messages to VPP
360__________________________________
361
362The point of the exercise is to send binary API messages to VPP, and
363to receive replies from VPP. Many VPP binary APIs comprise a client
364request message, and a simple status reply. For example, to set the
365admin status of an interface:
366
367.. code-block:: C
368
369 vl_api_sw_interface_set_flags_t *mp;
370 mp = vl_msg_api_alloc (sizeof (*mp));
371 memset (mp, 0, sizeof (*mp));
372 mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_SW_INTERFACE_SET_FLAGS);
373 mp->client_index = api_main.my_client_index;
374 mp->sw_if_index = clib_host_to_net_u32 (<interface-sw-if-index>);
375 vl_msg_api_send (api_main.shmem_hdr->vl_input_queue, (u8 *)mp);
376
377Key points:
378
379* Use `vl_msg_api_alloc <https://docs.fd.io/vpp/18.11/dc/d5a/memory__shared_8h.html#a109ff1e95ebb2c968d43c100c4a1c55a>`_ to allocate message buffers
380* Allocated message buffers are not initialized, and must be presumed to contain trash.
381* Don't forget to set the _vl_msg_id field!
382* As of this writing, binary API message IDs and data are sent in network byte order
383* The client-library global data structure `api_main <https://docs.fd.io/vpp/18.11/d6/dd1/api__shared_8c.html#af58e3e46b569573e9622b826b2f47a22>`_ keeps track of sufficient pointers and handles used to communicate with VPP
384
385Receiving binary API messages from VPP
386______________________________________
387
388Unless you've made other arrangements (see
389`vl_client_connect_to_vlib_no_rx_pthread
390<https://docs.fd.io/vpp/18.11/da/d25/memory__client_8h.html#a11b9577297106c57c0783b96ab190c36>`_),
391*messages are received on a separate rx pthread*. Synchronization with
392the client application main thread is the responsibility of the
393application!
394
395Set up message handlers about as follows:
396
397.. code-block:: C
398
399 #define vl_typedefs /* define message structures */
400 #include <vpp/api/vpe_all_api_h.h>
401 #undef vl_typedefs
402 /* declare message handlers for each api */
403 #define vl_endianfun /* define message structures */
404 #include <vpp/api/vpe_all_api_h.h>
405 #undef vl_endianfun
406 /* instantiate all the print functions we know about */
407 #define vl_print(handle, ...)
408 #define vl_printfun
409 #include <vpp/api/vpe_all_api_h.h>
410 #undef vl_printfun
411 /* Define a list of all message that the client handles */
412 #define foreach_vpe_api_reply_msg \
Dave Barachaf577992019-07-26 08:26:03 -0400413 _(SW_INTERFACE_SET_FLAGS_REPLY, sw_interface_set_flags_reply)
andrew74030262018-08-05 21:18:45 -0400414 static clib_error_t *
415 my_api_hookup (vlib_main_t * vm)
416 {
417 api_main_t *am = &api_main;
418 #define _(N,n) \
419 vl_msg_api_set_handlers(VL_API_##N, #n, \
420 vl_api_##n##_t_handler, \
421 vl_noop_handler, \
422 vl_api_##n##_t_endian, \
423 vl_api_##n##_t_print, \
424 sizeof(vl_api_##n##_t), 1);
425 foreach_vpe_api_msg;
426 #undef _
427 return 0;
428 }
429
430The key API used to establish message handlers is
431`vl_msg_api_set_handlers
432<https://docs.fd.io/vpp/18.11/d6/dd1/api__shared_8c.html#aa8a8e1f3876ec1a02f283c1862ecdb7a>`_
433, which sets values in multiple parallel vectors in the `api_main_t
434<https://docs.fd.io/vpp/18.11/dd/db2/structapi__main__t.html>`_
435structure. As of this writing: not all vector element values can be
436set through the API. You'll see sporadic API message registrations
437followed by minor adjustments of this form:
438
439.. code-block:: C
440
441 /*
442 * Thread-safe API messages
443 */
444 am->is_mp_safe[VL_API_IP_ADD_DEL_ROUTE] = 1;
445 am->is_mp_safe[VL_API_GET_NODE_GRAPH] = 1;
446
447API message numbering in plugins
448--------------------------------
449
Dave Barachaf577992019-07-26 08:26:03 -0400450Binary API message numbering in plugins relies on vpp to issue a block
andrew74030262018-08-05 21:18:45 -0400451of message-ID's for the plugin to use:
452
453.. code-block:: C
454
455 static clib_error_t *
456 my_init (vlib_main_t * vm)
457 {
458 my_main_t *mm = &my_main;
459
460 name = format (0, "myplugin_%08x%c", api_version, 0);
461
462 /* Ask for a correctly-sized block of API message decode slots */
463 mm->msg_id_base = vl_msg_api_get_msg_ids
464 ((char *) name, VL_MSG_FIRST_AVAILABLE);
465
466 }
467
468Control-plane codes use the vl_client_get_first_plugin_msg_id (...) api
469to recover the message ID block base:
470
471.. code-block:: C
472
473 /* Ask the vpp engine for the first assigned message-id */
474 name = format (0, "myplugin_%08x%c", api_version, 0);
475 sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name);
476
477It's a fairly common error to forget to add msg_id_base when
478registering message handlers, or when sending messages. Using macros
479from .../src/vlibapi/api_helper_macros.h can automate the process, but
480remember to #define REPLY_MSG_ID_BASE before #including the file:
481
482.. code-block:: C
483
484 #define REPLY_MSG_ID_BASE mm->msg_id_base
485 #include <vlibapi/api_helper_macros.h>