2 * nsh_api.c - nsh mapping api
4 * Copyright (c) 2019 Cisco and/or its affiliates.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 #include <vnet/vnet.h>
18 #include <vnet/plugin/plugin.h>
21 #include <vlibapi/api.h>
22 #include <vlibmemory/api.h>
23 #include <vpp/app/version.h>
25 /* define message IDs */
26 #define vl_msg_id(n,h) n,
29 #include <nsh/nsh.api.h>
30 /* We'll want to know how many messages IDs we need... */
31 VL_MSG_FIRST_AVAILABLE,
35 /* define message structures */
37 #include <nsh/nsh.api.h>
40 /* define generated endian-swappers */
42 #include <nsh/nsh.api.h>
45 /* instantiate all the print functions we know about */
46 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
48 #include <nsh/nsh.api.h>
51 /* Get the API version number */
52 #define vl_api_version(n,v) static u32 api_version=(v);
53 #include <nsh/nsh.api.h>
56 #define vl_msg_name_crc_list
57 #include <nsh/nsh.api.h>
58 #undef vl_msg_name_crc_list
61 * A handy macro to set up a message reply.
62 * Assumes that the following variables are available:
63 * mp - pointer to request message
64 * rmp - pointer to reply message type
68 #define REPLY_MACRO(t) \
70 unix_shared_memory_queue_t * q = \
71 vl_api_client_index_to_input_queue (mp->client_index); \
75 rmp = vl_msg_api_alloc (sizeof (*rmp)); \
76 rmp->_vl_msg_id = ntohs((t)+nm->msg_id_base); \
77 rmp->context = mp->context; \
78 rmp->retval = ntohl(rv); \
80 vl_msg_api_send_shmem (q, (u8 *)&rmp); \
83 #define REPLY_MACRO2(t, body) \
85 unix_shared_memory_queue_t * q; \
86 rv = vl_msg_api_pd_handler (mp, rv); \
87 q = vl_api_client_index_to_input_queue (mp->client_index); \
91 rmp = vl_msg_api_alloc (sizeof (*rmp)); \
92 rmp->_vl_msg_id = ntohs((t)+nm->msg_id_base); \
93 rmp->context = mp->context; \
94 rmp->retval = ntohl(rv); \
95 do {body;} while (0); \
96 vl_msg_api_send_shmem (q, (u8 *)&rmp); \
99 /* List of message types that this plugin understands */
101 #define foreach_nsh_plugin_api_msg \
102 _(NSH_ADD_DEL_ENTRY, nsh_add_del_entry) \
103 _(NSH_ENTRY_DUMP, nsh_entry_dump) \
104 _(NSH_ADD_DEL_MAP, nsh_add_del_map) \
105 _(NSH_MAP_DUMP, nsh_map_dump)
108 * @brief CLI function for NSH admin up/down
117 static clib_error_t *
118 nsh_interface_admin_up_down (vnet_main_t * vnm, u32 nsh_hw_if, u32 flags)
120 if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
121 vnet_hw_interface_set_flags (vnm, nsh_hw_if,
122 VNET_HW_INTERFACE_FLAG_LINK_UP);
124 vnet_hw_interface_set_flags (vnm, nsh_hw_if, 0);
130 dummy_interface_tx (vlib_main_t * vm,
131 vlib_node_runtime_t * node, vlib_frame_t * frame)
133 clib_warning ("you shouldn't be here, leaking buffers...");
134 return frame->n_vectors;
138 * @brief Naming for NSH tunnel
140 * @param *s formatting string
143 * @return *s formatted string
147 format_nsh_name (u8 * s, va_list * args)
149 u32 dev_instance = va_arg (*args, u32);
150 return format (s, "nsh_tunnel%d", dev_instance);
154 VNET_DEVICE_CLASS (nsh_device_class, static) = {
156 .format_device_name = format_nsh_name,
157 .tx_function = dummy_interface_tx,
158 .admin_up_down_function = nsh_interface_admin_up_down,
162 static void send_nsh_entry_details
163 (nsh_entry_t * t, unix_shared_memory_queue_t * q, u32 context)
165 vl_api_nsh_entry_details_t *rmp;
166 nsh_main_t *nm = &nsh_main;
168 rmp = vl_msg_api_alloc (sizeof (*rmp));
169 clib_memset (rmp, 0, sizeof (*rmp));
171 rmp->_vl_msg_id = ntohs ((VL_API_NSH_ENTRY_DETAILS) + nm->msg_id_base);
172 rmp->ver_o_c = t->nsh_base.ver_o_c;
173 rmp->ttl = (t->nsh_base.ver_o_c & NSH_TTL_H4_MASK) << 2 |
174 (t->nsh_base.length & NSH_TTL_L2_MASK) >> 6;
175 rmp->length = t->nsh_base.length & NSH_LEN_MASK;
176 rmp->md_type = t->nsh_base.md_type;
177 rmp->next_protocol = t->nsh_base.next_protocol;
178 rmp->nsp_nsi = htonl (t->nsh_base.nsp_nsi);
180 if (t->nsh_base.md_type == 1)
183 rmp->c1 = htonl (t->md.md1_data.c1);
184 rmp->c2 = htonl (t->md.md1_data.c2);
185 rmp->c3 = htonl (t->md.md1_data.c3);
186 rmp->c4 = htonl (t->md.md1_data.c4);
188 else if (t->nsh_base.md_type == 2)
190 rmp->tlv_length = t->tlvs_len;
191 clib_memcpy (rmp->tlv, t->tlvs_data, t->tlvs_len);
194 rmp->context = context;
196 vl_msg_api_send_shmem (q, (u8 *) & rmp);
199 static void send_nsh_map_details
200 (nsh_map_t * t, unix_shared_memory_queue_t * q, u32 context)
202 vl_api_nsh_map_details_t *rmp;
203 nsh_main_t *nm = &nsh_main;
205 rmp = vl_msg_api_alloc (sizeof (*rmp));
206 clib_memset (rmp, 0, sizeof (*rmp));
208 rmp->_vl_msg_id = ntohs ((VL_API_NSH_MAP_DETAILS) + nm->msg_id_base);
209 rmp->nsp_nsi = htonl (t->nsp_nsi);
210 rmp->mapped_nsp_nsi = htonl (t->mapped_nsp_nsi);
211 rmp->nsh_action = htonl (t->nsh_action);
212 rmp->sw_if_index = htonl (t->sw_if_index);
213 rmp->rx_sw_if_index = htonl (t->rx_sw_if_index);
214 rmp->next_node = htonl (t->next_node);
216 rmp->context = context;
218 vl_msg_api_send_shmem (q, (u8 *) & rmp);
222 vl_api_nsh_map_dump_t_handler (vl_api_nsh_map_dump_t * mp)
224 unix_shared_memory_queue_t *q;
225 nsh_main_t *nm = &nsh_main;
229 q = vl_api_client_index_to_input_queue (mp->client_index);
235 map_index = ntohl (mp->map_index);
239 pool_foreach (t, nm->nsh_mappings, (
241 send_nsh_map_details (t, q,
248 if (map_index >= vec_len (nm->nsh_mappings))
252 t = &nm->nsh_mappings[map_index];
253 send_nsh_map_details (t, q, mp->context);
257 /** API message handler */
259 vl_api_nsh_add_del_map_t_handler (vl_api_nsh_add_del_map_t * mp)
261 vl_api_nsh_add_del_map_reply_t *rmp;
262 nsh_main_t *nm = &nsh_main;
264 nsh_add_del_map_args_t _a, *a = &_a;
267 a->is_add = mp->is_add;
268 a->map.nsp_nsi = ntohl (mp->nsp_nsi);
269 a->map.mapped_nsp_nsi = ntohl (mp->mapped_nsp_nsi);
270 a->map.nsh_action = ntohl (mp->nsh_action);
271 a->map.sw_if_index = ntohl (mp->sw_if_index);
272 a->map.rx_sw_if_index = ntohl (mp->rx_sw_if_index);
273 a->map.next_node = ntohl (mp->next_node);
275 rv = nsh_add_del_map (a, &map_index);
277 if ((a->map.next_node == NSH_NODE_NEXT_ENCAP_VXLAN4)
278 | (a->map.next_node == NSH_NODE_NEXT_ENCAP_VXLAN6))
280 rv = nsh_add_del_proxy_session (a);
283 REPLY_MACRO2 (VL_API_NSH_ADD_DEL_MAP_REPLY, (
292 nsh_header_rewrite (nsh_entry_t * nsh_entry)
296 nsh_base_header_t *nsh_base;
297 nsh_md1_data_t *nsh_md1;
298 nsh_main_t *nm = &nsh_main;
299 nsh_md2_data_t *opt0;
300 nsh_md2_data_t *limit0;
301 nsh_md2_data_t *nsh_md2;
302 nsh_option_map_t _nsh_option, *nsh_option = &_nsh_option;
303 u8 old_option_size = 0;
304 u8 new_option_size = 0;
306 vec_free (nsh_entry->rewrite);
307 if (nsh_entry->nsh_base.md_type == 1)
309 len = sizeof (nsh_base_header_t) + sizeof (nsh_md1_data_t);
311 else if (nsh_entry->nsh_base.md_type == 2)
313 /* set to maxim, maybe dataplane will add more TLVs */
314 len = MAX_NSH_HEADER_LEN;
316 vec_validate_aligned (rw, len - 1, CLIB_CACHE_LINE_BYTES);
317 clib_memset (rw, 0, len);
319 nsh_base = (nsh_base_header_t *) rw;
320 nsh_base->ver_o_c = nsh_entry->nsh_base.ver_o_c;
321 nsh_base->length = nsh_entry->nsh_base.length;
322 nsh_base->md_type = nsh_entry->nsh_base.md_type;
323 nsh_base->next_protocol = nsh_entry->nsh_base.next_protocol;
324 nsh_base->nsp_nsi = clib_host_to_net_u32 (nsh_entry->nsh_base.nsp_nsi);
326 if (nsh_base->md_type == 1)
328 nsh_md1 = (nsh_md1_data_t *) (rw + sizeof (nsh_base_header_t));
329 nsh_md1->c1 = clib_host_to_net_u32 (nsh_entry->md.md1_data.c1);
330 nsh_md1->c2 = clib_host_to_net_u32 (nsh_entry->md.md1_data.c2);
331 nsh_md1->c3 = clib_host_to_net_u32 (nsh_entry->md.md1_data.c3);
332 nsh_md1->c4 = clib_host_to_net_u32 (nsh_entry->md.md1_data.c4);
333 nsh_entry->rewrite_size = 24;
335 else if (nsh_base->md_type == 2)
337 opt0 = (nsh_md2_data_t *) (nsh_entry->tlvs_data);
338 limit0 = (nsh_md2_data_t *) ((u8 *) opt0 + nsh_entry->tlvs_len);
340 nsh_md2 = (nsh_md2_data_t *) (rw + sizeof (nsh_base_header_t));
341 nsh_entry->rewrite_size = sizeof (nsh_base_header_t);
343 while (opt0 < limit0)
345 old_option_size = sizeof (nsh_md2_data_t) + opt0->length;
346 /* round to 4-byte */
347 old_option_size = ((old_option_size + 3) >> 2) << 2;
349 nsh_option = nsh_md2_lookup_option (opt0->class, opt0->type);
350 if (nsh_option == NULL)
355 if (nm->add_options[nsh_option->option_id] != NULL)
357 if (0 != nm->add_options[nsh_option->option_id] ((u8 *) nsh_md2,
363 /* round to 4-byte */
364 new_option_size = ((new_option_size + 3) >> 2) << 2;
366 nsh_entry->rewrite_size += new_option_size;
368 (nsh_md2_data_t *) (((u8 *) nsh_md2) + new_option_size);
369 opt0 = (nsh_md2_data_t *) (((u8 *) opt0) + old_option_size);
374 opt0 = (nsh_md2_data_t *) (((u8 *) opt0) + old_option_size);
380 nsh_entry->rewrite = rw;
381 nsh_base->length = (nsh_base->length & NSH_TTL_L2_MASK) |
382 ((nsh_entry->rewrite_size >> 2) & NSH_LEN_MASK);
387 extern vnet_hw_interface_class_t nsh_hw_class;
390 * Action function to add or del an nsh map.
391 * Shared by both CLI and binary API
394 nsh_add_del_map (nsh_add_del_map_args_t * a, u32 * map_indexp)
396 nsh_main_t *nm = &nsh_main;
397 vnet_main_t *vnm = nm->vnet_main;
403 vnet_hw_interface_t *hi;
407 /* net order, so data plane could use nsh header to lookup directly */
408 key = clib_host_to_net_u32 (a->map.nsp_nsi);
410 entry = hash_get_mem (nm->nsh_mapping_by_key, &key);
414 /* adding an entry, must not already exist */
416 return -1; //TODO API_ERROR_INVALID_VALUE;
418 pool_get_aligned (nm->nsh_mappings, map, CLIB_CACHE_LINE_BYTES);
419 clib_memset (map, 0, sizeof (*map));
421 /* copy from arg structure */
422 map->nsp_nsi = a->map.nsp_nsi;
423 map->mapped_nsp_nsi = a->map.mapped_nsp_nsi;
424 map->nsh_action = a->map.nsh_action;
425 map->sw_if_index = a->map.sw_if_index;
426 map->rx_sw_if_index = a->map.rx_sw_if_index;
427 map->next_node = a->map.next_node;
428 map->adj_index = a->map.adj_index;
431 key_copy = clib_mem_alloc (sizeof (*key_copy));
432 clib_memcpy (key_copy, &key, sizeof (*key_copy));
434 hash_set_mem (nm->nsh_mapping_by_key, key_copy, map - nm->nsh_mappings);
435 map_index = map - nm->nsh_mappings;
437 if (vec_len (nm->free_nsh_tunnel_hw_if_indices) > 0)
439 nsh_hw_if = nm->free_nsh_tunnel_hw_if_indices
440 [vec_len (nm->free_nsh_tunnel_hw_if_indices) - 1];
441 _vec_len (nm->free_nsh_tunnel_hw_if_indices) -= 1;
443 hi = vnet_get_hw_interface (vnm, nsh_hw_if);
444 hi->dev_instance = map_index;
445 hi->hw_instance = hi->dev_instance;
449 nsh_hw_if = vnet_register_interface
450 (vnm, nsh_device_class.index, map_index, nsh_hw_class.index,
452 hi = vnet_get_hw_interface (vnm, nsh_hw_if);
453 hi->output_node_index = nsh_aware_vnf_proxy_node.index;
456 map->nsh_hw_if = nsh_hw_if;
457 map->nsh_sw_if = nsh_sw_if = hi->sw_if_index;
458 vec_validate_init_empty (nm->tunnel_index_by_sw_if_index, nsh_sw_if,
460 nm->tunnel_index_by_sw_if_index[nsh_sw_if] = key;
462 vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
463 VNET_SW_INTERFACE_FLAG_ADMIN_UP);
468 return -2; //TODO API_ERROR_NO_SUCH_ENTRY;
470 map = pool_elt_at_index (nm->nsh_mappings, entry[0]);
472 vnet_sw_interface_set_flags (vnm, map->nsh_sw_if,
473 VNET_SW_INTERFACE_FLAG_ADMIN_DOWN);
474 vec_add1 (nm->free_nsh_tunnel_hw_if_indices, map->nsh_sw_if);
475 nm->tunnel_index_by_sw_if_index[map->nsh_sw_if] = ~0;
477 hp = hash_get_pair (nm->nsh_mapping_by_key, &key);
478 key_copy = (void *) (hp->key);
479 hash_unset_mem (nm->nsh_mapping_by_key, &key);
480 clib_mem_free (key_copy);
482 pool_put (nm->nsh_mappings, map);
486 *map_indexp = map_index;
492 * Action function to add or del an nsh-proxy-session.
493 * Shared by both CLI and binary API
496 nsh_add_del_proxy_session (nsh_add_del_map_args_t * a)
498 nsh_main_t *nm = &nsh_main;
499 nsh_proxy_session_t *proxy = 0;
500 nsh_proxy_session_by_key_t key, *key_copy;
503 u32 nsp = 0, nsi = 0;
505 clib_memset (&key, 0, sizeof (key));
506 key.transport_type = a->map.next_node;
507 key.transport_index = a->map.sw_if_index;
509 entry = hash_get_mem (nm->nsh_proxy_session_by_key, &key);
513 /* adding an entry, must not already exist */
515 return -1; //TODO API_ERROR_INVALID_VALUE;
517 pool_get_aligned (nm->nsh_proxy_sessions, proxy, CLIB_CACHE_LINE_BYTES);
518 clib_memset (proxy, 0, sizeof (*proxy));
520 /* Nsi needs to minus 1 within NSH-Proxy */
521 nsp = (a->map.nsp_nsi >> NSH_NSP_SHIFT) & NSH_NSP_MASK;
522 nsi = a->map.nsp_nsi & NSH_NSI_MASK;
527 /* net order, so could use it to lookup nsh map table directly */
528 proxy->nsp_nsi = clib_host_to_net_u32 ((nsp << NSH_NSP_SHIFT) | nsi);
530 key_copy = clib_mem_alloc (sizeof (*key_copy));
531 clib_memcpy (key_copy, &key, sizeof (*key_copy));
533 hash_set_mem (nm->nsh_proxy_session_by_key, key_copy,
534 proxy - nm->nsh_proxy_sessions);
539 return -2; //TODO API_ERROR_NO_SUCH_ENTRY;
541 proxy = pool_elt_at_index (nm->nsh_proxy_sessions, entry[0]);
542 hp = hash_get_pair (nm->nsh_proxy_session_by_key, &key);
543 key_copy = (void *) (hp->key);
544 hash_unset_mem (nm->nsh_proxy_session_by_key, &key);
545 clib_mem_free (key_copy);
547 pool_put (nm->nsh_proxy_sessions, proxy);
554 * Action function for adding an NSH entry
555 * nsh_add_del_entry_args_t *a: host order
558 nsh_add_del_entry (nsh_add_del_entry_args_t * a, u32 * entry_indexp)
560 nsh_main_t *nm = &nsh_main;
561 nsh_entry_t *nsh_entry = 0;
565 u32 entry_index = ~0;
569 /* host order, because nsh map table stores nsp_nsi in host order */
570 key = a->nsh_entry.nsh_base.nsp_nsi;
572 entry_id = hash_get_mem (nm->nsh_entry_by_key, &key);
576 /* adding an entry, must not already exist */
578 return -1; // TODO VNET_API_ERROR_INVALID_VALUE;
580 pool_get_aligned (nm->nsh_entries, nsh_entry, CLIB_CACHE_LINE_BYTES);
581 clib_memset (nsh_entry, 0, sizeof (*nsh_entry));
583 /* copy from arg structure */
584 #define _(x) nsh_entry->nsh_base.x = a->nsh_entry.nsh_base.x;
585 foreach_copy_nsh_base_hdr_field;
588 if (a->nsh_entry.nsh_base.md_type == 1)
590 nsh_entry->md.md1_data.c1 = a->nsh_entry.md.md1_data.c1;
591 nsh_entry->md.md1_data.c2 = a->nsh_entry.md.md1_data.c2;
592 nsh_entry->md.md1_data.c3 = a->nsh_entry.md.md1_data.c3;
593 nsh_entry->md.md1_data.c4 = a->nsh_entry.md.md1_data.c4;
595 else if (a->nsh_entry.nsh_base.md_type == 2)
597 vec_free (nsh_entry->tlvs_data);
598 tlvs_len = a->nsh_entry.tlvs_len;
599 vec_validate_aligned (data, tlvs_len - 1, CLIB_CACHE_LINE_BYTES);
601 clib_memcpy (data, a->nsh_entry.tlvs_data, tlvs_len);
602 nsh_entry->tlvs_data = data;
603 nsh_entry->tlvs_len = tlvs_len;
604 vec_free (a->nsh_entry.tlvs_data);
607 nsh_header_rewrite (nsh_entry);
609 key_copy = clib_mem_alloc (sizeof (*key_copy));
610 clib_memcpy (key_copy, &key, sizeof (*key_copy));
612 hash_set_mem (nm->nsh_entry_by_key, key_copy,
613 nsh_entry - nm->nsh_entries);
614 entry_index = nsh_entry - nm->nsh_entries;
619 return -2; //TODO API_ERROR_NO_SUCH_ENTRY;
621 nsh_entry = pool_elt_at_index (nm->nsh_entries, entry_id[0]);
622 hp = hash_get_pair (nm->nsh_entry_by_key, &key);
623 key_copy = (void *) (hp->key);
624 hash_unset_mem (nm->nsh_entry_by_key, &key);
625 clib_mem_free (key_copy);
627 vec_free (nsh_entry->tlvs_data);
628 vec_free (nsh_entry->rewrite);
629 pool_put (nm->nsh_entries, nsh_entry);
633 *entry_indexp = entry_index;
639 /** API message handler */
640 static void vl_api_nsh_add_del_entry_t_handler
641 (vl_api_nsh_add_del_entry_t * mp)
643 vl_api_nsh_add_del_entry_reply_t *rmp;
644 nsh_main_t *nm = &nsh_main;
646 nsh_add_del_entry_args_t _a, *a = &_a;
647 u32 entry_index = ~0;
651 a->is_add = mp->is_add;
652 a->nsh_entry.nsh_base.ver_o_c =
653 (mp->ver_o_c & 0xF0) | ((mp->ttl & NSH_LEN_MASK) >> 2);
654 a->nsh_entry.nsh_base.length =
655 (mp->length & NSH_LEN_MASK) | ((mp->ttl & 0x3) << 6);
656 a->nsh_entry.nsh_base.md_type = mp->md_type;
657 a->nsh_entry.nsh_base.next_protocol = mp->next_protocol;
658 a->nsh_entry.nsh_base.nsp_nsi = ntohl (mp->nsp_nsi);
659 if (mp->md_type == 1)
661 a->nsh_entry.md.md1_data.c1 = ntohl (mp->c1);
662 a->nsh_entry.md.md1_data.c2 = ntohl (mp->c2);
663 a->nsh_entry.md.md1_data.c3 = ntohl (mp->c3);
664 a->nsh_entry.md.md1_data.c4 = ntohl (mp->c4);
666 else if (mp->md_type == 2)
668 tlvs_len = mp->tlv_length;
669 vec_validate_aligned (data, tlvs_len - 1, CLIB_CACHE_LINE_BYTES);
671 clib_memcpy (data, mp->tlv, tlvs_len);
672 a->nsh_entry.tlvs_data = data;
673 a->nsh_entry.tlvs_len = tlvs_len;
676 rv = nsh_add_del_entry (a, &entry_index);
678 REPLY_MACRO2 (VL_API_NSH_ADD_DEL_ENTRY_REPLY, (
687 vl_api_nsh_entry_dump_t_handler (vl_api_nsh_entry_dump_t * mp)
689 unix_shared_memory_queue_t *q;
690 nsh_main_t *nm = &nsh_main;
694 q = vl_api_client_index_to_input_queue (mp->client_index);
700 entry_index = ntohl (mp->entry_index);
702 if (~0 == entry_index)
704 pool_foreach (t, nm->nsh_entries, (
706 send_nsh_entry_details (t, q,
713 if (entry_index >= vec_len (nm->nsh_entries))
717 t = &nm->nsh_entries[entry_index];
718 send_nsh_entry_details (t, q, mp->context);
723 setup_message_id_table (nsh_main_t * nm, api_main_t * am)
725 #define _(id,n,crc) \
726 vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + nm->msg_id_base);
727 foreach_vl_msg_name_crc_nsh;
731 /* Set up the API message handling tables */
732 static clib_error_t *
733 nsh_plugin_api_hookup (vlib_main_t * vm)
735 nsh_main_t *nm __attribute__ ((unused)) = &nsh_main;
737 vl_msg_api_set_handlers((VL_API_##N + nm->msg_id_base), \
739 vl_api_##n##_t_handler, \
741 vl_api_##n##_t_endian, \
742 vl_api_##n##_t_print, \
743 sizeof(vl_api_##n##_t), 1);
744 foreach_nsh_plugin_api_msg;
751 nsh_api_init (vlib_main_t * vm, nsh_main_t * nm)
756 name = format (0, "nsh_%08x%c", api_version, 0);
759 nm->msg_id_base = vl_msg_api_get_msg_ids
760 ((char *) name, VL_MSG_FIRST_AVAILABLE);
762 error = nsh_plugin_api_hookup (vm);
764 /* Add our API messages to the global name_crc hash table */
765 setup_message_id_table (nm, &api_main);
773 * fd.io coding-style-patch-verification: ON
776 * eval: (c-set-style "gnu")