da6650dac7cb30a6574c7d089660940b414cda5e
[vpp.git] / src / plugins / nsh / nsh_api.c
1 /*
2  * nsh_api.c - nsh mapping api
3  *
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:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17 #include <vnet/vnet.h>
18 #include <vnet/plugin/plugin.h>
19 #include <nsh/nsh.h>
20
21 #include <vlibapi/api.h>
22 #include <vlibmemory/api.h>
23 #include <vpp/app/version.h>
24 #include <vlibapi/api_helper_macros.h>
25
26 #include <plugins/nsh/nsh.api_enum.h>
27 #include <plugins/nsh/nsh.api_types.h>
28
29 /**
30  * @brief CLI function for NSH admin up/down
31  *
32  * @param *vnm
33  * @param nsh_hw_if
34  * @param flag
35  *
36  * @return *rc
37  *
38  */
39 static clib_error_t *
40 nsh_interface_admin_up_down (vnet_main_t * vnm, u32 nsh_hw_if, u32 flags)
41 {
42   if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
43     vnet_hw_interface_set_flags (vnm, nsh_hw_if,
44                                  VNET_HW_INTERFACE_FLAG_LINK_UP);
45   else
46     vnet_hw_interface_set_flags (vnm, nsh_hw_if, 0);
47
48   return 0;
49 }
50
51 /**
52  * @brief Naming for NSH tunnel
53  *
54  * @param *s formatting string
55  * @param *args
56  *
57  * @return *s formatted string
58  *
59  */
60 static u8 *
61 format_nsh_name (u8 * s, va_list * args)
62 {
63   u32 dev_instance = va_arg (*args, u32);
64   return format (s, "nsh_tunnel%d", dev_instance);
65 }
66
67 /* *INDENT-OFF* */
68 VNET_DEVICE_CLASS (nsh_device_class, static) = {
69   .name = "NSH",
70   .format_device_name = format_nsh_name,
71   .admin_up_down_function = nsh_interface_admin_up_down,
72 };
73 /* *INDENT-ON* */
74
75 static void send_nsh_entry_details
76   (nsh_entry_t * t, vl_api_registration_t * rp, u32 context)
77 {
78   vl_api_nsh_entry_details_t *rmp;
79   nsh_main_t *nm = &nsh_main;
80
81   rmp = vl_msg_api_alloc (sizeof (*rmp));
82   clib_memset (rmp, 0, sizeof (*rmp));
83
84   rmp->_vl_msg_id = ntohs ((VL_API_NSH_ENTRY_DETAILS) + nm->msg_id_base);
85   rmp->ver_o_c = t->nsh_base.ver_o_c;
86   rmp->ttl = (t->nsh_base.ver_o_c & NSH_TTL_H4_MASK) << 2 |
87     (t->nsh_base.length & NSH_TTL_L2_MASK) >> 6;
88   rmp->length = t->nsh_base.length & NSH_LEN_MASK;
89   rmp->md_type = t->nsh_base.md_type;
90   rmp->next_protocol = t->nsh_base.next_protocol;
91   rmp->nsp_nsi = htonl (t->nsh_base.nsp_nsi);
92
93   if (t->nsh_base.md_type == 1)
94     {
95       rmp->tlv_length = 4;
96       rmp->c1 = htonl (t->md.md1_data.c1);
97       rmp->c2 = htonl (t->md.md1_data.c2);
98       rmp->c3 = htonl (t->md.md1_data.c3);
99       rmp->c4 = htonl (t->md.md1_data.c4);
100     }
101   else if (t->nsh_base.md_type == 2)
102     {
103       rmp->tlv_length = t->tlvs_len;
104       clib_memcpy (rmp->tlv, t->tlvs_data, t->tlvs_len);
105     }
106
107   rmp->context = context;
108
109   vl_api_send_msg (rp, (u8 *) rmp);
110 }
111
112 static void send_nsh_map_details
113   (nsh_map_t * t, vl_api_registration_t * rp, u32 context)
114 {
115   vl_api_nsh_map_details_t *rmp;
116   nsh_main_t *nm = &nsh_main;
117
118   rmp = vl_msg_api_alloc (sizeof (*rmp));
119   clib_memset (rmp, 0, sizeof (*rmp));
120
121   rmp->_vl_msg_id = ntohs ((VL_API_NSH_MAP_DETAILS) + nm->msg_id_base);
122   rmp->nsp_nsi = htonl (t->nsp_nsi);
123   rmp->mapped_nsp_nsi = htonl (t->mapped_nsp_nsi);
124   rmp->nsh_action = htonl (t->nsh_action);
125   rmp->sw_if_index = htonl (t->sw_if_index);
126   rmp->rx_sw_if_index = htonl (t->rx_sw_if_index);
127   rmp->next_node = htonl (t->next_node);
128
129   rmp->context = context;
130
131   vl_api_send_msg (rp, (u8 *) rmp);
132 }
133
134 static void
135 vl_api_nsh_map_dump_t_handler (vl_api_nsh_map_dump_t * mp)
136 {
137   nsh_main_t *nm = &nsh_main;
138   nsh_map_t *t;
139   u32 map_index;
140   vl_api_registration_t *rp;
141
142   rp = vl_api_client_index_to_registration (mp->client_index);
143   if (rp == 0)
144     return;
145
146   map_index = ntohl (mp->map_index);
147
148   if (~0 == map_index)
149     {
150       pool_foreach (t, nm->nsh_mappings)
151       {
152         send_nsh_map_details (t, rp, mp->context);
153       }
154     }
155   else
156     {
157       if (map_index >= vec_len (nm->nsh_mappings))
158         {
159           return;
160         }
161       t = &nm->nsh_mappings[map_index];
162       send_nsh_map_details (t, rp, mp->context);
163     }
164 }
165
166 /** API message handler */
167 static void
168 vl_api_nsh_add_del_map_t_handler (vl_api_nsh_add_del_map_t * mp)
169 {
170   vl_api_nsh_add_del_map_reply_t *rmp;
171   int rv;
172   nsh_add_del_map_args_t _a = { 0 }, *a = &_a;
173   u32 map_index = ~0;
174
175   a->is_add = mp->is_add;
176   a->map.nsp_nsi = ntohl (mp->nsp_nsi);
177   a->map.mapped_nsp_nsi = ntohl (mp->mapped_nsp_nsi);
178   a->map.nsh_action = ntohl (mp->nsh_action);
179   a->map.sw_if_index = ntohl (mp->sw_if_index);
180   a->map.rx_sw_if_index = ntohl (mp->rx_sw_if_index);
181   a->map.next_node = ntohl (mp->next_node);
182
183   rv = nsh_add_del_map (a, &map_index);
184
185   if ((a->map.next_node == NSH_NODE_NEXT_ENCAP_VXLAN4)
186       | (a->map.next_node == NSH_NODE_NEXT_ENCAP_VXLAN6))
187     {
188       rv = nsh_add_del_proxy_session (a);
189     }
190
191   REPLY_MACRO2 (VL_API_NSH_ADD_DEL_MAP_REPLY, (
192                                                 {
193                                                 rmp->map_index =
194                                                 htonl (map_index);
195                                                 }
196                 ));
197 }
198
199 int
200 nsh_header_rewrite (nsh_entry_t * nsh_entry)
201 {
202   u8 *rw = 0;
203   int len = 0;
204   nsh_base_header_t *nsh_base;
205   nsh_md1_data_t *nsh_md1;
206   nsh_main_t *nm = &nsh_main;
207   nsh_md2_data_t *opt0;
208   nsh_md2_data_t *limit0;
209   nsh_md2_data_t *nsh_md2;
210   nsh_option_map_t _nsh_option, *nsh_option = &_nsh_option;
211   u8 old_option_size = 0;
212   u8 new_option_size = 0;
213
214   vec_free (nsh_entry->rewrite);
215   if (nsh_entry->nsh_base.md_type == 1)
216     {
217       len = sizeof (nsh_base_header_t) + sizeof (nsh_md1_data_t);
218     }
219   else if (nsh_entry->nsh_base.md_type == 2)
220     {
221       /* set to maxim, maybe dataplane will add more TLVs */
222       len = MAX_NSH_HEADER_LEN;
223     }
224   vec_validate_aligned (rw, len - 1, CLIB_CACHE_LINE_BYTES);
225   clib_memset (rw, 0, len);
226
227   nsh_base = (nsh_base_header_t *) rw;
228   nsh_base->ver_o_c = nsh_entry->nsh_base.ver_o_c;
229   nsh_base->length = nsh_entry->nsh_base.length;
230   nsh_base->md_type = nsh_entry->nsh_base.md_type;
231   nsh_base->next_protocol = nsh_entry->nsh_base.next_protocol;
232   nsh_base->nsp_nsi = clib_host_to_net_u32 (nsh_entry->nsh_base.nsp_nsi);
233
234   if (nsh_base->md_type == 1)
235     {
236       nsh_md1 = (nsh_md1_data_t *) (rw + sizeof (nsh_base_header_t));
237       nsh_md1->c1 = clib_host_to_net_u32 (nsh_entry->md.md1_data.c1);
238       nsh_md1->c2 = clib_host_to_net_u32 (nsh_entry->md.md1_data.c2);
239       nsh_md1->c3 = clib_host_to_net_u32 (nsh_entry->md.md1_data.c3);
240       nsh_md1->c4 = clib_host_to_net_u32 (nsh_entry->md.md1_data.c4);
241       nsh_entry->rewrite_size = 24;
242     }
243   else if (nsh_base->md_type == 2)
244     {
245       opt0 = (nsh_md2_data_t *) (nsh_entry->tlvs_data);
246       limit0 = (nsh_md2_data_t *) ((u8 *) opt0 + nsh_entry->tlvs_len);
247
248       nsh_md2 = (nsh_md2_data_t *) (rw + sizeof (nsh_base_header_t));
249       nsh_entry->rewrite_size = sizeof (nsh_base_header_t);
250
251       while (opt0 < limit0)
252         {
253           old_option_size = sizeof (nsh_md2_data_t) + opt0->length;
254           /* round to 4-byte */
255           old_option_size = ((old_option_size + 3) >> 2) << 2;
256
257           nsh_option = nsh_md2_lookup_option (opt0->class, opt0->type);
258           if (nsh_option == NULL)
259             {
260               goto next_tlv_md2;
261             }
262
263           if (nm->add_options[nsh_option->option_id] != NULL)
264             {
265               if (0 != nm->add_options[nsh_option->option_id] ((u8 *) nsh_md2,
266                                                                &new_option_size))
267                 {
268                   goto next_tlv_md2;
269                 }
270
271               /* round to 4-byte */
272               new_option_size = ((new_option_size + 3) >> 2) << 2;
273
274               nsh_entry->rewrite_size += new_option_size;
275               nsh_md2 =
276                 (nsh_md2_data_t *) (((u8 *) nsh_md2) + new_option_size);
277               opt0 = (nsh_md2_data_t *) (((u8 *) opt0) + old_option_size);
278             }
279           else
280             {
281             next_tlv_md2:
282               opt0 = (nsh_md2_data_t *) (((u8 *) opt0) + old_option_size);
283             }
284
285         }
286     }
287
288   nsh_entry->rewrite = rw;
289   nsh_base->length = (nsh_base->length & NSH_TTL_L2_MASK) |
290     ((nsh_entry->rewrite_size >> 2) & NSH_LEN_MASK);
291
292   return 0;
293 }
294
295 extern vnet_hw_interface_class_t nsh_hw_class;
296
297 /**
298  * Action function to add or del an nsh map.
299  * Shared by both CLI and binary API
300  **/
301 int
302 nsh_add_del_map (nsh_add_del_map_args_t * a, u32 * map_indexp)
303 {
304   nsh_main_t *nm = &nsh_main;
305   vnet_main_t *vnm = nm->vnet_main;
306   nsh_map_t *map = 0;
307   u32 key, *key_copy;
308   uword *entry;
309   hash_pair_t *hp;
310   u32 map_index = ~0;
311   vnet_hw_interface_t *hi;
312   u32 nsh_hw_if = ~0;
313   u32 nsh_sw_if = ~0;
314
315   /* net order, so data plane could use nsh header to lookup directly */
316   key = clib_host_to_net_u32 (a->map.nsp_nsi);
317
318   entry = hash_get_mem (nm->nsh_mapping_by_key, &key);
319
320   if (a->is_add)
321     {
322       /* adding an entry, must not already exist */
323       if (entry)
324         return -1;              //TODO API_ERROR_INVALID_VALUE;
325
326       pool_get_aligned (nm->nsh_mappings, map, CLIB_CACHE_LINE_BYTES);
327       clib_memset (map, 0, sizeof (*map));
328
329       /* copy from arg structure */
330       map->nsp_nsi = a->map.nsp_nsi;
331       map->mapped_nsp_nsi = a->map.mapped_nsp_nsi;
332       map->nsh_action = a->map.nsh_action;
333       map->sw_if_index = a->map.sw_if_index;
334       map->rx_sw_if_index = a->map.rx_sw_if_index;
335       map->next_node = a->map.next_node;
336       map->adj_index = a->map.adj_index;
337
338
339       key_copy = clib_mem_alloc (sizeof (*key_copy));
340       clib_memcpy (key_copy, &key, sizeof (*key_copy));
341
342       hash_set_mem (nm->nsh_mapping_by_key, key_copy, map - nm->nsh_mappings);
343       map_index = map - nm->nsh_mappings;
344
345       if (vec_len (nm->free_nsh_tunnel_hw_if_indices) > 0)
346         {
347           nsh_hw_if = nm->free_nsh_tunnel_hw_if_indices
348             [vec_len (nm->free_nsh_tunnel_hw_if_indices) - 1];
349           vec_dec_len (nm->free_nsh_tunnel_hw_if_indices, 1);
350
351           hi = vnet_get_hw_interface (vnm, nsh_hw_if);
352           hi->dev_instance = map_index;
353           hi->hw_instance = hi->dev_instance;
354         }
355       else
356         {
357           nsh_hw_if = vnet_register_interface
358             (vnm, nsh_device_class.index, map_index, nsh_hw_class.index,
359              map_index);
360           hi = vnet_get_hw_interface (vnm, nsh_hw_if);
361           hi->output_node_index = nsh_aware_vnf_proxy_node.index;
362         }
363
364       map->nsh_hw_if = nsh_hw_if;
365       map->nsh_sw_if = nsh_sw_if = hi->sw_if_index;
366       vec_validate_init_empty (nm->tunnel_index_by_sw_if_index, nsh_sw_if,
367                                ~0);
368       nm->tunnel_index_by_sw_if_index[nsh_sw_if] = key;
369
370       vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
371                                    VNET_SW_INTERFACE_FLAG_ADMIN_UP);
372     }
373   else
374     {
375       if (!entry)
376         return -2;              //TODO API_ERROR_NO_SUCH_ENTRY;
377
378       map = pool_elt_at_index (nm->nsh_mappings, entry[0]);
379
380       vnet_sw_interface_set_flags (vnm, map->nsh_sw_if,
381                                    VNET_SW_INTERFACE_FLAG_ADMIN_DOWN);
382       vec_add1 (nm->free_nsh_tunnel_hw_if_indices, map->nsh_sw_if);
383       nm->tunnel_index_by_sw_if_index[map->nsh_sw_if] = ~0;
384
385       hp = hash_get_pair (nm->nsh_mapping_by_key, &key);
386       key_copy = (void *) (hp->key);
387       hash_unset_mem (nm->nsh_mapping_by_key, &key);
388       clib_mem_free (key_copy);
389
390       pool_put (nm->nsh_mappings, map);
391     }
392
393   if (map_indexp)
394     *map_indexp = map_index;
395
396   return 0;
397 }
398
399 /**
400  * Action function to add or del an nsh-proxy-session.
401  * Shared by both CLI and binary API
402  **/
403 int
404 nsh_add_del_proxy_session (nsh_add_del_map_args_t * a)
405 {
406   nsh_main_t *nm = &nsh_main;
407   nsh_proxy_session_t *proxy = 0;
408   nsh_proxy_session_by_key_t key, *key_copy;
409   uword *entry;
410   hash_pair_t *hp;
411   u32 nsp = 0, nsi = 0;
412
413   clib_memset (&key, 0, sizeof (key));
414   key.transport_type = a->map.next_node;
415   key.transport_index = a->map.sw_if_index;
416
417   entry = hash_get_mem (nm->nsh_proxy_session_by_key, &key);
418
419   if (a->is_add)
420     {
421       /* adding an entry, must not already exist */
422       if (entry)
423         return -1;              //TODO API_ERROR_INVALID_VALUE;
424
425       pool_get_aligned (nm->nsh_proxy_sessions, proxy, CLIB_CACHE_LINE_BYTES);
426       clib_memset (proxy, 0, sizeof (*proxy));
427
428       /* Nsi needs to minus 1 within NSH-Proxy */
429       nsp = (a->map.nsp_nsi >> NSH_NSP_SHIFT) & NSH_NSP_MASK;
430       nsi = a->map.nsp_nsi & NSH_NSI_MASK;
431       if (nsi == 0)
432         return -1;
433
434       nsi = nsi - 1;
435       /* net order, so could use it to lookup nsh map table directly */
436       proxy->nsp_nsi = clib_host_to_net_u32 ((nsp << NSH_NSP_SHIFT) | nsi);
437
438       key_copy = clib_mem_alloc (sizeof (*key_copy));
439       clib_memcpy (key_copy, &key, sizeof (*key_copy));
440
441       hash_set_mem (nm->nsh_proxy_session_by_key, key_copy,
442                     proxy - nm->nsh_proxy_sessions);
443     }
444   else
445     {
446       if (!entry)
447         return -2;              //TODO API_ERROR_NO_SUCH_ENTRY;
448
449       proxy = pool_elt_at_index (nm->nsh_proxy_sessions, entry[0]);
450       hp = hash_get_pair (nm->nsh_proxy_session_by_key, &key);
451       key_copy = (void *) (hp->key);
452       hash_unset_mem (nm->nsh_proxy_session_by_key, &key);
453       clib_mem_free (key_copy);
454
455       pool_put (nm->nsh_proxy_sessions, proxy);
456     }
457
458   return 0;
459 }
460
461 /**
462  * Action function for adding an NSH entry
463  * nsh_add_del_entry_args_t *a: host order
464  */
465 int
466 nsh_add_del_entry (nsh_add_del_entry_args_t * a, u32 * entry_indexp)
467 {
468   nsh_main_t *nm = &nsh_main;
469   nsh_entry_t *nsh_entry = 0;
470   u32 key, *key_copy;
471   uword *entry_id;
472   hash_pair_t *hp;
473   u32 entry_index = ~0;
474   u8 tlvs_len = 0;
475   u8 *data = 0;
476
477   /* host order, because nsh map table stores nsp_nsi in host order */
478   key = a->nsh_entry.nsh_base.nsp_nsi;
479
480   entry_id = hash_get_mem (nm->nsh_entry_by_key, &key);
481
482   if (a->is_add)
483     {
484       /* adding an entry, must not already exist */
485       if (entry_id)
486         return -1;              // TODO VNET_API_ERROR_INVALID_VALUE;
487
488       pool_get_aligned (nm->nsh_entries, nsh_entry, CLIB_CACHE_LINE_BYTES);
489       clib_memset (nsh_entry, 0, sizeof (*nsh_entry));
490
491       /* copy from arg structure */
492 #define _(x) nsh_entry->nsh_base.x = a->nsh_entry.nsh_base.x;
493       foreach_copy_nsh_base_hdr_field;
494 #undef _
495
496       if (a->nsh_entry.nsh_base.md_type == 1)
497         {
498           nsh_entry->md.md1_data.c1 = a->nsh_entry.md.md1_data.c1;
499           nsh_entry->md.md1_data.c2 = a->nsh_entry.md.md1_data.c2;
500           nsh_entry->md.md1_data.c3 = a->nsh_entry.md.md1_data.c3;
501           nsh_entry->md.md1_data.c4 = a->nsh_entry.md.md1_data.c4;
502         }
503       else if (a->nsh_entry.nsh_base.md_type == 2)
504         {
505           vec_free (nsh_entry->tlvs_data);
506           tlvs_len = a->nsh_entry.tlvs_len;
507           vec_validate_aligned (data, tlvs_len - 1, CLIB_CACHE_LINE_BYTES);
508
509           clib_memcpy (data, a->nsh_entry.tlvs_data, tlvs_len);
510           nsh_entry->tlvs_data = data;
511           nsh_entry->tlvs_len = tlvs_len;
512           vec_free (a->nsh_entry.tlvs_data);
513         }
514
515       nsh_header_rewrite (nsh_entry);
516
517       key_copy = clib_mem_alloc (sizeof (*key_copy));
518       clib_memcpy (key_copy, &key, sizeof (*key_copy));
519
520       hash_set_mem (nm->nsh_entry_by_key, key_copy,
521                     nsh_entry - nm->nsh_entries);
522       entry_index = nsh_entry - nm->nsh_entries;
523     }
524   else
525     {
526       if (!entry_id)
527         return -2;              //TODO API_ERROR_NO_SUCH_ENTRY;
528
529       nsh_entry = pool_elt_at_index (nm->nsh_entries, entry_id[0]);
530       hp = hash_get_pair (nm->nsh_entry_by_key, &key);
531       key_copy = (void *) (hp->key);
532       hash_unset_mem (nm->nsh_entry_by_key, &key);
533       clib_mem_free (key_copy);
534
535       vec_free (nsh_entry->tlvs_data);
536       vec_free (nsh_entry->rewrite);
537       pool_put (nm->nsh_entries, nsh_entry);
538     }
539
540   if (entry_indexp)
541     *entry_indexp = entry_index;
542
543   return 0;
544 }
545
546
547 /** API message handler */
548 static void vl_api_nsh_add_del_entry_t_handler
549   (vl_api_nsh_add_del_entry_t * mp)
550 {
551   vl_api_nsh_add_del_entry_reply_t *rmp;
552   int rv;
553   nsh_add_del_entry_args_t _a = { 0 }, *a = &_a;
554   u32 entry_index = ~0;
555   u8 tlvs_len = 0;
556   u8 *data = 0;
557
558   a->is_add = mp->is_add;
559   a->nsh_entry.nsh_base.ver_o_c =
560     (mp->ver_o_c & 0xF0) | ((mp->ttl & NSH_LEN_MASK) >> 2);
561   a->nsh_entry.nsh_base.length =
562     (mp->length & NSH_LEN_MASK) | ((mp->ttl & 0x3) << 6);
563   a->nsh_entry.nsh_base.md_type = mp->md_type;
564   a->nsh_entry.nsh_base.next_protocol = mp->next_protocol;
565   a->nsh_entry.nsh_base.nsp_nsi = ntohl (mp->nsp_nsi);
566   if (mp->md_type == 1)
567     {
568       a->nsh_entry.md.md1_data.c1 = ntohl (mp->c1);
569       a->nsh_entry.md.md1_data.c2 = ntohl (mp->c2);
570       a->nsh_entry.md.md1_data.c3 = ntohl (mp->c3);
571       a->nsh_entry.md.md1_data.c4 = ntohl (mp->c4);
572     }
573   else if (mp->md_type == 2)
574     {
575       tlvs_len = mp->tlv_length;
576       vec_validate_aligned (data, tlvs_len - 1, CLIB_CACHE_LINE_BYTES);
577
578       clib_memcpy (data, mp->tlv, tlvs_len);
579       a->nsh_entry.tlvs_data = data;
580       a->nsh_entry.tlvs_len = tlvs_len;
581     }
582
583   rv = nsh_add_del_entry (a, &entry_index);
584
585   REPLY_MACRO2 (VL_API_NSH_ADD_DEL_ENTRY_REPLY, (
586                                                   {
587                                                   rmp->entry_index =
588                                                   htonl (entry_index);
589                                                   }
590                 ));
591 }
592
593 static void
594 vl_api_nsh_entry_dump_t_handler (vl_api_nsh_entry_dump_t * mp)
595 {
596   nsh_main_t *nm = &nsh_main;
597   nsh_entry_t *t;
598   u32 entry_index;
599   vl_api_registration_t *rp;
600
601   rp = vl_api_client_index_to_registration (mp->client_index);
602   if (rp == 0)
603     return;
604
605   entry_index = ntohl (mp->entry_index);
606
607   if (~0 == entry_index)
608     {
609       pool_foreach (t, nm->nsh_entries)
610       {
611         send_nsh_entry_details (t, rp, mp->context);
612       }
613     }
614   else
615     {
616       if (entry_index >= vec_len (nm->nsh_entries))
617         {
618           return;
619         }
620       t = &nm->nsh_entries[entry_index];
621       send_nsh_entry_details (t, rp, mp->context);
622     }
623 }
624
625 #include <nsh/nsh.api.c>
626
627 /* Set up the API message handling tables */
628 clib_error_t *
629 nsh_api_init (vlib_main_t * vm, nsh_main_t * nm)
630 {
631   /* Add our API messages to the global name_crc hash table */
632   nm->msg_id_base = setup_message_id_table ();
633
634   return 0;
635 }
636
637 /*
638  * fd.io coding-style-patch-verification: ON
639  *
640  * Local Variables:
641  * eval: (c-set-style "gnu")
642  * End:
643  */