hs-test: more debug output in http3 test
[vpp.git] / src / vlibapi / api_doc.rst
1 .. _api_doc:
2
3 Writing API handlers
4 ====================
5
6 VPP provides a binary API scheme to allow a wide variety of client codes
7 to program data-plane tables. As of this writing, there are hundreds of
8 binary APIs.
9
10 Messages are defined in ``*.api`` files. Today, there are about 50 api
11 files, with more arriving as folks add programmable features. The API
12 file compiler sources reside in @ref src/tools/vppapigen.
13
14 From @ref src/vnet/interface.api, here’s a typical request/response
15 message definition:
16
17 .. code:: c
18
19         autoreply define sw_interface_set_flags
20         {
21           u32 client_index;
22           u32 context;
23           u32 sw_if_index;
24           /* 1 = up, 0 = down */
25           u8 admin_up_down;
26         };
27
28 To a first approximation, the API compiler renders this definition into
29 ``build-root/.../vpp/include/vnet/interface.api.h`` as follows:
30
31 .. code:: c
32
33        /****** Message ID / handler enum ******/
34        #ifdef vl_msg_id
35        vl_msg_id(VL_API_SW_INTERFACE_SET_FLAGS, vl_api_sw_interface_set_flags_t_handler)
36        vl_msg_id(VL_API_SW_INTERFACE_SET_FLAGS_REPLY, vl_api_sw_interface_set_flags_reply_t_handler)
37        #endif
38
39        /****** Message names ******/
40        #ifdef vl_msg_name
41        vl_msg_name(vl_api_sw_interface_set_flags_t, 1)
42        vl_msg_name(vl_api_sw_interface_set_flags_reply_t, 1)
43        #endif
44
45        /****** Message name, crc list ******/
46        #ifdef vl_msg_name_crc_list
47        #define foreach_vl_msg_name_crc_interface \
48        _(VL_API_SW_INTERFACE_SET_FLAGS, sw_interface_set_flags, f890584a) \
49        _(VL_API_SW_INTERFACE_SET_FLAGS_REPLY, sw_interface_set_flags_reply, dfbf3afa) \
50        #endif
51
52        /****** Typedefs *****/
53        #ifdef vl_typedefs
54        typedef VL_API_PACKED(struct _vl_api_sw_interface_set_flags {
55            u16 _vl_msg_id;
56            u32 client_index;
57            u32 context;
58            u32 sw_if_index;
59            u8 admin_up_down;
60        }) vl_api_sw_interface_set_flags_t;
61
62        typedef VL_API_PACKED(struct _vl_api_sw_interface_set_flags_reply {
63            u16 _vl_msg_id;
64            u32 context;
65            i32 retval;
66        }) vl_api_sw_interface_set_flags_reply_t;
67
68        ...
69        #endif /* vl_typedefs */
70
71 To change the admin state of an interface, a binary api client sends a
72 @ref vl_api_sw_interface_set_flags_t to VPP, which will respond with a
73 @ref vl_api_sw_interface_set_flags_reply_t message.
74
75 Multiple layers of software, transport types, and shared libraries
76 implement a variety of features:
77
78 -  API message allocation, tracing, pretty-printing, and replay.
79 -  Message transport via global shared memory, pairwise/private shared
80    memory, and sockets.
81 -  Barrier synchronization of worker threads across thread-unsafe
82    message handlers.
83
84 Correctly-coded message handlers know nothing about the transport used
85 to deliver messages to/from VPP. It’s reasonably straightforward to use
86 multiple API message transport types simultaneously.
87
88 For historical reasons, binary api messages are (putatively) sent in
89 network byte order. As of this writing, we’re seriously considering
90 whether that choice makes sense.
91
92 Message Allocation
93 ------------------
94
95 Since binary API messages are always processed in order, we allocate
96 messages using a ring allocator whenever possible. This scheme is
97 extremely fast when compared with a traditional memory allocator, and
98 doesn’t cause heap fragmentation. See @ref
99 src/vlibmemory/memory_shared.c @ref vl_msg_api_alloc_internal().
100
101 Regardless of transport, binary api messages always follow a @ref
102 msgbuf_t header:
103
104 .. code:: c
105
106        typedef struct msgbuf_
107        {
108          unix_shared_memory_queue_t *q;
109          u32 data_len;
110          u32 gc_mark_timestamp;
111          u8 data[0];
112        } msgbuf_t;
113
114 This structure makes it easy to trace messages without having to decode
115 them - simply save data_len bytes - and allows @ref vl_msg_api_free() to
116 rapidly dispose of message buffers:
117
118 .. code:: c
119
120        void
121        vl_msg_api_free (void *a)
122        {
123          msgbuf_t *rv;
124          api_main_t *am = &api_main;
125
126          rv = (msgbuf_t *) (((u8 *) a) - offsetof (msgbuf_t, data));
127
128          /*
129           * Here's the beauty of the scheme.  Only one proc/thread has
130           * control of a given message buffer. To free a buffer, we just
131           * clear the queue field, and leave. No locks, no hits, no errors...
132           */
133          if (rv->q)
134            {
135              rv->q = 0;
136              rv->gc_mark_timestamp = 0;
137              return;
138            }
139          <snip>
140        }
141
142 Message Tracing and Replay
143 --------------------------
144
145 It’s extremely important that VPP can capture and replay sizeable binary
146 API traces. System-level issues involving hundreds of thousands of API
147 transactions can be re-run in a second or less. Partial replay allows
148 one to binary-search for the point where the wheels fall off. One can
149 add scaffolding to the data plane, to trigger when complex conditions
150 obtain.
151
152 With binary API trace, print, and replay, system-level bug reports of
153 the form “after 300,000 API transactions, the VPP data-plane stopped
154 forwarding traffic, FIX IT!” can be solved offline.
155
156 More often than not, one discovers that a control-plane client
157 misprograms the data plane after a long time or under complex
158 circumstances. Without direct evidence, “it’s a data-plane problem!”
159
160 See @ref src/vlibmemory/memory_vlib.c @ref vl_msg_api_process_file(),
161 and @ref src/vlibapi/api_shared.c. See also the debug CLI command “api
162 trace”
163
164 Client connection details
165 -------------------------
166
167 Establishing a binary API connection to VPP from a C-language client is
168 easy:
169
170 .. code:: c
171
172            int
173            connect_to_vpe (char *client_name, int client_message_queue_length)
174            {
175              vat_main_t *vam = &vat_main;
176              api_main_t *am = &api_main;
177
178              if (vl_client_connect_to_vlib ("/vpe-api", client_name,
179                                            client_message_queue_length) < 0)
180                return -1;
181
182              /* Memorize vpp's binary API message input queue address */
183              vam->vl_input_queue = am->shmem_hdr->vl_input_queue;
184              /* And our client index */
185              vam->my_client_index = am->my_client_index;
186              return 0;
187            }
188
189 32 is a typical value for client_message_queue_length. VPP cannot block
190 when it needs to send an API message to a binary API client, and the
191 VPP-side binary API message handlers are very fast. When sending
192 asynchronous messages, make sure to scrape the binary API rx ring with
193 some enthusiasm.
194
195 binary API message RX pthread
196 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
197
198 Calling @ref vl_client_connect_to_vlib spins up a binary API message RX
199 pthread:
200
201 .. code:: c
202
203            static void *
204            rx_thread_fn (void *arg)
205            {
206              unix_shared_memory_queue_t *q;
207              memory_client_main_t *mm = &memory_client_main;
208              api_main_t *am = &api_main;
209
210              q = am->vl_input_queue;
211
212              /* So we can make the rx thread terminate cleanly */
213              if (setjmp (mm->rx_thread_jmpbuf) == 0)
214                {
215                  mm->rx_thread_jmpbuf_valid = 1;
216                  while (1)
217                {
218                  vl_msg_api_queue_handler (q);
219                }
220                }
221              pthread_exit (0);
222            }
223
224 To handle the binary API message queue yourself, use @ref
225 vl_client_connect_to_vlib_no_rx_pthread.
226
227 In turn, vl_msg_api_queue_handler(…) uses mutex/condvar signalling to
228 wake up, process VPP -> client traffic, then sleep. VPP supplies a
229 condvar broadcast when the VPP -> client API message queue transitions
230 from empty to nonempty.
231
232 VPP checks its own binary API input queue at a very high rate. VPP
233 invokes message handlers in “process” context [aka cooperative
234 multitasking thread context] at a variable rate, depending on data-plane
235 packet processing requirements.
236
237 Client disconnection details
238 ----------------------------
239
240 To disconnect from VPP, call @ref vl_client_disconnect_from_vlib. Please
241 arrange to call this function if the client application terminates
242 abnormally. VPP makes every effort to hold a decent funeral for dead
243 clients, but VPP can’t guarantee to free leaked memory in the shared
244 binary API segment.
245
246 Sending binary API messages to VPP
247 ----------------------------------
248
249 The point of the exercise is to send binary API messages to VPP, and to
250 receive replies from VPP. Many VPP binary APIs comprise a client request
251 message, and a simple status reply. For example, to set the admin status
252 of an interface, one codes:
253
254 .. code:: c
255
256        vl_api_sw_interface_set_flags_t *mp;
257
258        mp = vl_msg_api_alloc (sizeof (*mp));
259        memset (mp, 0, sizeof (*mp));
260        mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_SW_INTERFACE_SET_FLAGS);
261        mp->client_index = api_main.my_client_index;
262        mp->sw_if_index = clib_host_to_net_u32 (<interface-sw-if-index>);
263        vl_msg_api_send (api_main.shmem_hdr->vl_input_queue, (u8 *)mp);
264
265 Key points:
266
267 -  Use @ref vl_msg_api_alloc to allocate message buffers
268
269 -  Allocated message buffers are not initialized, and must be presumed
270    to contain trash.
271
272 -  Don’t forget to set the \_vl_msg_id field!
273
274 -  As of this writing, binary API message IDs and data are sent in
275    network byte order
276
277 -  The client-library global data structure @ref api_main keeps track of
278    sufficient pointers and handles used to communicate with VPP
279
280 Receiving binary API messages from VPP
281 --------------------------------------
282
283 Unless you’ve made other arrangements (see @ref
284 vl_client_connect_to_vlib_no_rx_pthread), *messages are received on a
285 separate rx pthread*. Synchronization with the client application main
286 thread is the responsibility of the application!
287
288 Set up message handlers about as follows:
289
290 .. code:: c
291
292        #define vl_typedefs     /* define message structures */
293        #include <vpp/api/vpe_all_api_h.h>
294        #undef vl_typedefs
295
296        /* declare message handlers for each api */
297
298        #define vl_endianfun        /* define message structures */
299        #include <vpp/api/vpe_all_api_h.h>
300        #undef vl_endianfun
301
302        /* instantiate all the print functions we know about */
303        #define vl_printfun
304        #include <vpp/api/vpe_all_api_h.h>
305        #undef vl_printfun
306
307        /* Define a list of all message that the client handles */
308        #define foreach_vpe_api_reply_msg                            \
309           _(SW_INTERFACE_SET_FLAGS_REPLY, sw_interface_set_flags_reply)
310
311           static clib_error_t *
312           my_api_hookup (vlib_main_t * vm)
313           {
314             api_main_t *am = &api_main;
315
316           #define _(N,n)                                                  \
317               vl_msg_api_set_handlers(VL_API_##N, #n,                     \
318                                      vl_api_##n##_t_handler,              \
319                                      vl_noop_handler,                     \
320                                      vl_api_##n##_t_endian,               \
321                                      vl_api_##n##_t_print,                \
322                                      sizeof(vl_api_##n##_t), 1);
323             foreach_vpe_api_msg;
324           #undef _
325
326             return 0;
327            }
328
329 The key API used to establish message handlers is @ref
330 vl_msg_api_set_handlers , which sets values in multiple parallel vectors
331 in the @ref api_main_t structure. As of this writing: not all vector
332 element values can be set through the API. You’ll see sporadic API
333 message registrations followed by minor adjustments of this form:
334
335 .. code:: c
336
337        /*
338         * Thread-safe API messages
339         */
340        am->is_mp_safe[VL_API_IP_ADD_DEL_ROUTE] = 1;
341        am->is_mp_safe[VL_API_GET_NODE_GRAPH] = 1;