blob: 7d2b80a2e064b385ab0b2fcfdd2ffc2b9022343a [file] [log] [blame]
Nathan Skrzypczakd4a70642021-10-08 14:01:27 +02001.. _api_doc:
2
3Writing API handlers
4====================
5
6VPP provides a binary API scheme to allow a wide variety of client codes
7to program data-plane tables. As of this writing, there are hundreds of
8binary APIs.
9
10Messages are defined in ``*.api`` files. Today, there are about 50 api
11files, with more arriving as folks add programmable features. The API
12file compiler sources reside in @ref src/tools/vppapigen.
13
14From @ref src/vnet/interface.api, heres a typical request/response
15message 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
28To 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
71To 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
75Multiple layers of software, transport types, and shared libraries
76implement 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
84Correctly-coded message handlers know nothing about the transport used
85to deliver messages to/from VPP. Its reasonably straightforward to use
86multiple API message transport types simultaneously.
87
88For historical reasons, binary api messages are (putatively) sent in
89network byte order. As of this writing, were seriously considering
90whether that choice makes sense.
91
92Message Allocation
93------------------
94
95Since binary API messages are always processed in order, we allocate
96messages using a ring allocator whenever possible. This scheme is
97extremely fast when compared with a traditional memory allocator, and
98doesnt cause heap fragmentation. See @ref
99src/vlibmemory/memory_shared.c @ref vl_msg_api_alloc_internal().
100
101Regardless of transport, binary api messages always follow a @ref
102msgbuf_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
114This structure makes it easy to trace messages without having to decode
115them - simply save data_len bytes - and allows @ref vl_msg_api_free() to
116rapidly 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
142Message Tracing and Replay
143--------------------------
144
145Its extremely important that VPP can capture and replay sizeable binary
146API traces. System-level issues involving hundreds of thousands of API
147transactions can be re-run in a second or less. Partial replay allows
148one to binary-search for the point where the wheels fall off. One can
149add scaffolding to the data plane, to trigger when complex conditions
150obtain.
151
152With binary API trace, print, and replay, system-level bug reports of
153the form after 300,000 API transactions, the VPP data-plane stopped
154forwarding traffic, FIX IT!” can be solved offline.
155
156More often than not, one discovers that a control-plane client
157misprograms the data plane after a long time or under complex
158circumstances. Without direct evidence, its a data-plane problem!”
159
160See @ref src/vlibmemory/memory_vlib.c @ref vl_msg_api_process_file(),
161and @ref src/vlibapi/api_shared.c. See also the debug CLI command api
162trace
163
164Client connection details
165-------------------------
166
167Establishing a binary API connection to VPP from a C-language client is
168easy:
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
18932 is a typical value for client_message_queue_length. VPP cannot block
190when it needs to send an API message to a binary API client, and the
191VPP-side binary API message handlers are very fast. When sending
192asynchronous messages, make sure to scrape the binary API rx ring with
193some enthusiasm.
194
195binary API message RX pthread
196~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
197
198Calling @ref vl_client_connect_to_vlib spins up a binary API message RX
199pthread:
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
224To handle the binary API message queue yourself, use @ref
225vl_client_connect_to_vlib_no_rx_pthread.
226
227In turn, vl_msg_api_queue_handler(…) uses mutex/condvar signalling to
228wake up, process VPP -> client traffic, then sleep. VPP supplies a
229condvar broadcast when the VPP -> client API message queue transitions
230from empty to nonempty.
231
232VPP checks its own binary API input queue at a very high rate. VPP
233invokes message handlers in process context [aka cooperative
234multitasking thread context] at a variable rate, depending on data-plane
235packet processing requirements.
236
237Client disconnection details
238----------------------------
239
240To disconnect from VPP, call @ref vl_client_disconnect_from_vlib. Please
241arrange to call this function if the client application terminates
242abnormally. VPP makes every effort to hold a decent funeral for dead
243clients, but VPP cant guarantee to free leaked memory in the shared
244binary API segment.
245
246Sending binary API messages to VPP
247----------------------------------
248
249The point of the exercise is to send binary API messages to VPP, and to
250receive replies from VPP. Many VPP binary APIs comprise a client request
251message, and a simple status reply. For example, to set the admin status
252of 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
265Key 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- Dont 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
280Receiving binary API messages from VPP
281--------------------------------------
282
283Unless youve made other arrangements (see @ref
284vl_client_connect_to_vlib_no_rx_pthread), *messages are received on a
285separate rx pthread*. Synchronization with the client application main
286thread is the responsibility of the application!
287
288Set 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_print(handle, ...)
304 #define vl_printfun
305 #include <vpp/api/vpe_all_api_h.h>
306 #undef vl_printfun
307
308 /* Define a list of all message that the client handles */
309 #define foreach_vpe_api_reply_msg \
310 _(SW_INTERFACE_SET_FLAGS_REPLY, sw_interface_set_flags_reply)
311
312 static clib_error_t *
313 my_api_hookup (vlib_main_t * vm)
314 {
315 api_main_t *am = &api_main;
316
317 #define _(N,n) \
318 vl_msg_api_set_handlers(VL_API_##N, #n, \
319 vl_api_##n##_t_handler, \
320 vl_noop_handler, \
321 vl_api_##n##_t_endian, \
322 vl_api_##n##_t_print, \
323 sizeof(vl_api_##n##_t), 1);
324 foreach_vpe_api_msg;
325 #undef _
326
327 return 0;
328 }
329
330The key API used to establish message handlers is @ref
331vl_msg_api_set_handlers , which sets values in multiple parallel vectors
332in the @ref api_main_t structure. As of this writing: not all vector
333element values can be set through the API. Youll see sporadic API
334message registrations followed by minor adjustments of this form:
335
336.. code:: c
337
338 /*
339 * Thread-safe API messages
340 */
341 am->is_mp_safe[VL_API_IP_ADD_DEL_ROUTE] = 1;
342 am->is_mp_safe[VL_API_GET_NODE_GRAPH] = 1;