nsh.c revision 9301d767
1/*
2 * nsh.c - nsh mapping
3 *
4 * Copyright (c) 2013 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
18#include <vnet/vnet.h>
19#include <vnet/plugin/plugin.h>
20#include <nsh/nsh.h>
21
22#include <vlibapi/api.h>
23#include <vlibmemory/api.h>
24#include <vlibsocket/api.h>
25
26/* define message IDs */
27#include <vpp-api/nsh_msg_enum.h>
28
29/* define message structures */
30#define vl_typedefs
31#include <vpp-api/nsh_all_api_h.h>
32#undef vl_typedefs
33
34/* define generated endian-swappers */
35#define vl_endianfun
36#include <vpp-api/nsh_all_api_h.h>
37#undef vl_endianfun
38
39/* instantiate all the print functions we know about */
40#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
41#define vl_printfun
42#include <vpp-api/nsh_all_api_h.h>
43#undef vl_printfun
44
45/* Get the API version number */
46#define vl_api_version(n,v) static u32 api_version=(v);
47#include <vpp-api/nsh_all_api_h.h>
48#undef vl_api_version
49
50/*
51 * A handy macro to set up a message reply.
52 * Assumes that the following variables are available:
53 * mp - pointer to request message
54 * rmp - pointer to reply message type
55 * rv - return value
56 */
57
58#define REPLY_MACRO(t)                                          \
59  do {								\
60    unix_shared_memory_queue_t * q =                            \
61      vl_api_client_index_to_input_queue (mp->client_index);	\
62    if (!q)                                                     \
63      return;							\
64                                                                \
65    rmp = vl_msg_api_alloc (sizeof (*rmp));                     \
66    rmp->_vl_msg_id = ntohs((t)+nm->msg_id_base);               \
67    rmp->context = mp->context;                                 \
68    rmp->retval = ntohl(rv);                                    \
69                                                                \
70    vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
71  } while(0);
72
73#define REPLY_MACRO2(t, body)                                   \
74  do {                                                          \
75    unix_shared_memory_queue_t * q;                             \
76    rv = vl_msg_api_pd_handler (mp, rv);                        \
77    q = vl_api_client_index_to_input_queue (mp->client_index);  \
78    if (!q)                                                     \
79        return;                                                 \
80                                                                \
81    rmp = vl_msg_api_alloc (sizeof (*rmp));                     \
82    rmp->_vl_msg_id = ntohs((t)+nm->msg_id_base);               \
83    rmp->context = mp->context;                                 \
84    rmp->retval = ntohl(rv);                                    \
85    do {body;} while (0);                                       \
86    vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
87  } while(0);
88
89#define FINISH                                  \
90    vec_add1 (s, 0);                            \
91    vl_print (handle, (char *)s);               \
92    vec_free (s);                               \
93    return handle;
94
95/* List of message types that this plugin understands */
96
97#define foreach_nsh_plugin_api_msg		\
98  _(NSH_ADD_DEL_ENTRY, nsh_add_del_entry)	\
99  _(NSH_ENTRY_DUMP, nsh_entry_dump)             \
100  _(NSH_ADD_DEL_MAP, nsh_add_del_map)           \
101  _(NSH_MAP_DUMP, nsh_map_dump)
102
103clib_error_t *
104vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
105                      int from_early_init)
106{
107  nsh_main_t * nm = &nsh_main;
108  clib_error_t * error = 0;
109
110  nm->vlib_main = vm;
111  nm->vnet_main = h->vnet_main;
112
113  return error;
114}
115
116typedef struct {
117  nsh_header_t nsh_header;
118} nsh_input_trace_t;
119
120u8 * format_nsh_header (u8 * s, va_list * args)
121{
122  nsh_header_t * nsh = va_arg (*args, nsh_header_t *);
123
124  s = format (s, "nsh ver %d ", (nsh->ver_o_c>>6));
125  if (nsh->ver_o_c & NSH_O_BIT)
126    s = format (s, "O-set ");
127
128  if (nsh->ver_o_c & NSH_C_BIT)
129    s = format (s, "C-set ");
130
131  s = format (s, "len %d (%d bytes) md_type %d next_protocol %d\n",
132              nsh->length, nsh->length * 4, nsh->md_type, nsh->next_protocol);
133
134  s = format (s, "  service path %d service index %d\n",
135              (nsh->nsp_nsi>>NSH_NSP_SHIFT) & NSH_NSP_MASK,
136              nsh->nsp_nsi & NSH_NSI_MASK);
137
138  s = format (s, "  c1 %d c2 %d c3 %d c4 %d\n",
139              nsh->c1, nsh->c2, nsh->c3, nsh->c4);
140
141  return s;
142}
143
144static u8 * format_nsh_action (u8 * s, va_list * args)
145{
146  u32 nsh_action = va_arg (*args, u32);
147
148  switch (nsh_action)
149    {
150    case NSH_ACTION_SWAP:
151      return format (s, "swap");
152    case NSH_ACTION_PUSH:
153      return format (s, "push");
154    case NSH_ACTION_POP:
155      return format (s, "pop");
156    default:
157      return format (s, "unknown %d", nsh_action);
158    }
159  return s;
160}
161
162u8 * format_nsh_map (u8 * s, va_list * args)
163{
164  nsh_map_t * map = va_arg (*args, nsh_map_t *);
165
166  s = format (s, "nsh entry nsp: %d nsi: %d ",
167              (map->nsp_nsi>>NSH_NSP_SHIFT) & NSH_NSP_MASK,
168              map->nsp_nsi & NSH_NSI_MASK);
169  s = format (s, "maps to nsp: %d nsi: %d ",
170              (map->mapped_nsp_nsi>>NSH_NSP_SHIFT) & NSH_NSP_MASK,
171              map->mapped_nsp_nsi & NSH_NSI_MASK);
172
173  s = format (s, " nsh_action %U\n", format_nsh_action, map->nsh_action);
174
175  switch (map->next_node)
176    {
177    case NSH_INPUT_NEXT_ENCAP_GRE:
178      {
179	s = format (s, "encapped by GRE intf: %d", map->sw_if_index);
180	break;
181      }
182    case NSH_INPUT_NEXT_ENCAP_VXLANGPE:
183      {
184	s = format (s, "encapped by VXLAN GPE intf: %d", map->sw_if_index);
185	break;
186      }
187    default:
188      s = format (s, "only GRE and VXLANGPE support in this rev");
189    }
190
191  return s;
192}
193
194u8 * format_nsh_header_with_length (u8 * s, va_list * args)
195{
196  nsh_header_t * h = va_arg (*args, nsh_header_t *);
197  u32 max_header_bytes = va_arg (*args, u32);
198  u32 tmp, header_bytes;
199
200  header_bytes = sizeof (h[0]);
201  if (max_header_bytes != 0 && header_bytes > max_header_bytes)
202    return format (s, "nsh header truncated");
203
204  tmp = clib_net_to_host_u32 (h->nsp_nsi);
205  s = format (s, "  nsp %d nsi %d ",
206              (tmp>>NSH_NSP_SHIFT) & NSH_NSP_MASK,
207              tmp & NSH_NSI_MASK);
208
209  s = format (s, "c1 %u c2 %u c3 %u c4 %u",
210              clib_net_to_host_u32 (h->c1),
211              clib_net_to_host_u32 (h->c2),
212              clib_net_to_host_u32 (h->c3),
213              clib_net_to_host_u32 (h->c4));
214
215  s = format (s, "ver %d ", h->ver_o_c>>6);
216
217  if (h->ver_o_c & NSH_O_BIT)
218    s = format (s, "O-set ");
219
220  if (h->ver_o_c & NSH_C_BIT)
221    s = format (s, "C-set ");
222
223  s = format (s, "len %d (%d bytes) md_type %d next_protocol %d\n",
224              h->length, h->length * 4, h->md_type, h->next_protocol);
225  return s;
226}
227
228u8 * format_nsh_input_map_trace (u8 * s, va_list * args)
229{
230  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
231  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
232  nsh_input_trace_t * t
233    = va_arg (*args, nsh_input_trace_t *);
234
235  s = format (s, "\n  %U", format_nsh_header, &t->nsh_header,
236              (u32) sizeof (t->nsh_header) );
237
238  return s;
239}
240
241/**
242 * Action function to add or del an nsh map.
243 * Shared by both CLI and binary API
244 **/
245
246int nsh_add_del_map (nsh_add_del_map_args_t *a, u32 * map_indexp)
247{
248  nsh_main_t * nm = &nsh_main;
249  nsh_map_t *map = 0;
250  u32 key, *key_copy;
251  uword * entry;
252  hash_pair_t *hp;
253  u32 map_index = ~0;
254
255  key = a->map.nsp_nsi;
256
257  entry = hash_get_mem (nm->nsh_mapping_by_key, &key);
258
259  if (a->is_add)
260    {
261      /* adding an entry, must not already exist */
262      if (entry)
263        return -1; //TODO API_ERROR_INVALID_VALUE;
264
265      pool_get_aligned (nm->nsh_mappings, map, CLIB_CACHE_LINE_BYTES);
266      memset (map, 0, sizeof (*map));
267
268      /* copy from arg structure */
269      map->nsp_nsi = a->map.nsp_nsi;
270      map->mapped_nsp_nsi = a->map.mapped_nsp_nsi;
271      map->nsh_action = a->map.nsh_action;
272      map->sw_if_index = a->map.sw_if_index;
273      map->next_node = a->map.next_node;
274
275
276      key_copy = clib_mem_alloc (sizeof (*key_copy));
277      clib_memcpy (key_copy, &key, sizeof (*key_copy));
278
279      hash_set_mem (nm->nsh_mapping_by_key, key_copy,
280                    map - nm->nsh_mappings);
281      map_index = map - nm->nsh_mappings;
282    }
283  else
284    {
285      if (!entry)
286	return -2 ; //TODO API_ERROR_NO_SUCH_ENTRY;
287
288      map = pool_elt_at_index (nm->nsh_mappings, entry[0]);
289      hp = hash_get_pair (nm->nsh_mapping_by_key, &key);
290      key_copy = (void *)(hp->key);
291      hash_unset_mem (nm->nsh_mapping_by_key, &key);
292      clib_mem_free (key_copy);
293
294      pool_put (nm->nsh_mappings, map);
295    }
296
297  if (map_indexp)
298      *map_indexp = map_index;
299
300  return 0;
301}
302
303/**
304 * CLI command for NSH map
305 */
306
307static uword unformat_nsh_action (unformat_input_t * input, va_list * args)
308{
309  u32 * result = va_arg (*args, u32 *);
310  u32 tmp;
311
312  if (unformat (input, "swap"))
313    *result = NSH_ACTION_SWAP;
314  else if (unformat (input, "push"))
315    *result = NSH_ACTION_PUSH;
316  else if (unformat (input, "pop"))
317    *result = NSH_ACTION_POP;
318  else if (unformat (input, "%d", &tmp))
319    *result = tmp;
320  else
321    return 0;
322
323  return 1;
324}
325
326static clib_error_t *
327nsh_add_del_map_command_fn (vlib_main_t * vm,
328			    unformat_input_t * input,
329			    vlib_cli_command_t * cmd)
330{
331  unformat_input_t _line_input, * line_input = &_line_input;
332  u8 is_add = 1;
333  u32 nsp, nsi, mapped_nsp, mapped_nsi, nsh_action;
334  int nsp_set = 0, nsi_set = 0, mapped_nsp_set = 0, mapped_nsi_set = 0;
335  int nsh_action_set = 0;
336  u32 next_node = ~0;
337  u32 sw_if_index = ~0; // temporary requirement to get this moved over to NSHSFC
338  nsh_add_del_map_args_t _a, * a = &_a;
339  u32 map_index;
340  int rv;
341
342  /* Get a line of input. */
343  if (! unformat_user (input, unformat_line_input, line_input))
344    return 0;
345
346  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
347    if (unformat (line_input, "del"))
348      is_add = 0;
349    else if (unformat (line_input, "nsp %d", &nsp))
350      nsp_set = 1;
351    else if (unformat (line_input, "nsi %d", &nsi))
352      nsi_set = 1;
353    else if (unformat (line_input, "mapped-nsp %d", &mapped_nsp))
354      mapped_nsp_set = 1;
355    else if (unformat (line_input, "mapped-nsi %d", &mapped_nsi))
356      mapped_nsi_set = 1;
357    else if (unformat (line_input, "nsh_action %U", unformat_nsh_action,
358                       &nsh_action))
359      nsh_action_set = 1;
360    else if (unformat (line_input, "encap-gre-intf %d", &sw_if_index))
361      next_node = NSH_INPUT_NEXT_ENCAP_GRE;
362    else if (unformat (line_input, "encap-vxlan-gpe-intf %d", &sw_if_index))
363      next_node = NSH_INPUT_NEXT_ENCAP_VXLANGPE;
364    else if (unformat (line_input, "encap-none"))
365      next_node = NSH_INPUT_NEXT_DROP; // Once moved to NSHSFC see nsh.h:foreach_nsh_input_next to handle this case
366    else
367      return clib_error_return (0, "parse error: '%U'",
368                                format_unformat_error, line_input);
369  }
370
371  unformat_free (line_input);
372
373  if (nsp_set == 0 || nsi_set == 0)
374    return clib_error_return (0, "nsp nsi pair required. Key: for NSH entry");
375
376  if (mapped_nsp_set == 0 || mapped_nsi_set == 0)
377    return clib_error_return (0, "mapped-nsp mapped-nsi pair required. Key: for NSH entry");
378
379  if (nsh_action_set == 0 )
380    return clib_error_return (0, "nsh_action required: swap|push|pop.");
381
382  if (next_node == ~0)
383    return clib_error_return (0, "must specific action: [encap-gre-intf <nn> | encap-vxlan-gpe-intf <nn> | encap-none]");
384
385  memset (a, 0, sizeof (*a));
386
387  /* set args structure */
388  a->is_add = is_add;
389  a->map.nsp_nsi = (nsp<< NSH_NSP_SHIFT) | nsi;
390  a->map.mapped_nsp_nsi = (mapped_nsp<< NSH_NSP_SHIFT) | mapped_nsi;
391  a->map.nsh_action = nsh_action;
392  a->map.sw_if_index = sw_if_index;
393  a->map.next_node = next_node;
394
395
396  rv = nsh_add_del_map (a, &map_index);
397
398  switch(rv)
399    {
400    case 0:
401      break;
402    case -1: //TODO API_ERROR_INVALID_VALUE:
403      return clib_error_return (0, "mapping already exists. Remove it first.");
404
405    case -2: // TODO API_ERROR_NO_SUCH_ENTRY:
406      return clib_error_return (0, "mapping does not exist.");
407
408    default:
409      return clib_error_return
410        (0, "nsh_add_del_map returned %d", rv);
411    }
412  return 0;
413}
414
415VLIB_CLI_COMMAND (create_nsh_map_command, static) = {
416  .path = "create nsh map",
417  .short_help =
418  "create nsh map nsp <nn> nsi <nn> [del] mapped-nsp <nn> mapped-nsi <nn> nsh_action [swap|push|pop] "
419  "[encap-gre-intf <nn> | encap-vxlan-gpe-intf <nn> | encap-none]\n",
420  .function = nsh_add_del_map_command_fn,
421};
422
423/** API message handler */
424static void vl_api_nsh_add_del_map_t_handler
425(vl_api_nsh_add_del_map_t * mp)
426{
427  vl_api_nsh_add_del_map_reply_t * rmp;
428  nsh_main_t * nm = &nsh_main;
429  int rv;
430  nsh_add_del_map_args_t _a, *a = &_a;
431  u32 map_index = ~0;
432
433  a->is_add = mp->is_add;
434  a->map.nsp_nsi = ntohl(mp->nsp_nsi);
435  a->map.mapped_nsp_nsi = ntohl(mp->mapped_nsp_nsi);
436  a->map.nsh_action = ntohl(mp->nsh_action);
437  a->map.sw_if_index = ntohl(mp->sw_if_index);
438  a->map.next_node = ntohl(mp->next_node);
439
440  rv = nsh_add_del_map (a, &map_index);
441
442  REPLY_MACRO2(VL_API_NSH_ADD_DEL_MAP_REPLY,
443  ({
444    rmp->map_index = htonl (map_index);
445  }));
446}
447
448/**
449 * CLI command for showing the mapping between NSH entries
450 */
451static clib_error_t *
452show_nsh_map_command_fn (vlib_main_t * vm,
453			 unformat_input_t * input,
454			 vlib_cli_command_t * cmd)
455{
456  nsh_main_t * nm = &nsh_main;
457  nsh_map_t * map;
458
459  if (pool_elts (nm->nsh_mappings) == 0)
460    vlib_cli_output (vm, "No nsh maps configured.");
461
462  pool_foreach (map, nm->nsh_mappings,
463		({
464		  vlib_cli_output (vm, "%U", format_nsh_map, map);
465		}));
466
467  return 0;
468}
469
470VLIB_CLI_COMMAND (show_nsh_map_command, static) = {
471  .path = "show nsh map",
472  .function = show_nsh_map_command_fn,
473};
474
475/**
476 * Action function for adding an NSH entry
477 */
478
479int nsh_add_del_entry (nsh_add_del_entry_args_t *a, u32 * entry_indexp)
480{
481  nsh_main_t * nm = &nsh_main;
482  nsh_header_t *hdr = 0;
483  u32 key, *key_copy;
484  uword * entry;
485  hash_pair_t *hp;
486  u32 entry_index = ~0;
487
488  key = a->nsh.nsp_nsi;
489
490  entry = hash_get_mem (nm->nsh_entry_by_key, &key);
491
492  if (a->is_add)
493    {
494      /* adding an entry, must not already exist */
495      if (entry)
496        return -1; // TODO VNET_API_ERROR_INVALID_VALUE;
497
498      pool_get_aligned (nm->nsh_entries, hdr, CLIB_CACHE_LINE_BYTES);
499      memset (hdr, 0, sizeof (*hdr));
500
501      /* copy from arg structure */
502#define _(x) hdr->x = a->nsh.x;
503      foreach_copy_nshhdr_field;
504#undef _
505
506      key_copy = clib_mem_alloc (sizeof (*key_copy));
507      clib_memcpy (key_copy, &key, sizeof (*key_copy));
508
509      hash_set_mem (nm->nsh_entry_by_key, key_copy,
510                    hdr - nm->nsh_entries);
511      entry_index = hdr - nm->nsh_entries;
512    }
513  else
514    {
515      if (!entry)
516	return -2; //TODO API_ERROR_NO_SUCH_ENTRY;
517
518      hdr = pool_elt_at_index (nm->nsh_entries, entry[0]);
519      hp = hash_get_pair (nm->nsh_entry_by_key, &key);
520      key_copy = (void *)(hp->key);
521      hash_unset_mem (nm->nsh_entry_by_key, &key);
522      clib_mem_free (key_copy);
523
524      pool_put (nm->nsh_entries, hdr);
525    }
526
527  if (entry_indexp)
528      *entry_indexp = entry_index;
529
530  return 0;
531}
532
533
534/**
535 * CLI command for adding NSH entry
536 */
537
538static clib_error_t *
539nsh_add_del_entry_command_fn (vlib_main_t * vm,
540			      unformat_input_t * input,
541			      vlib_cli_command_t * cmd)
542{
543  unformat_input_t _line_input, * line_input = &_line_input;
544  u8 is_add = 1;
545  u8 ver_o_c = 0;
546  u8 length = 0;
547  u8 md_type = 0;
548  u8 next_protocol = 1; /* default: ip4 */
549  u32 nsp;
550  u8 nsp_set = 0;
551  u32 nsi;
552  u8 nsi_set = 0;
553  u32 nsp_nsi;
554  u32 c1 = 0;
555  u32 c2 = 0;
556  u32 c3 = 0;
557  u32 c4 = 0;
558  u32 tmp;
559  int rv;
560  u32 entry_index;
561  nsh_add_del_entry_args_t _a, * a = &_a;
562
563  /* Get a line of input. */
564  if (! unformat_user (input, unformat_line_input, line_input))
565    return 0;
566
567  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
568    if (unformat (line_input, "del"))
569      is_add = 0;
570    else if (unformat (line_input, "version %d", &tmp))
571      ver_o_c |= (tmp & 3) << 6;
572    else if (unformat (line_input, "o-bit %d", &tmp))
573      ver_o_c |= (tmp & 1) << 5;
574    else if (unformat (line_input, "c-bit %d", &tmp))
575      ver_o_c |= (tmp & 1) << 4;
576    else if (unformat (line_input, "md-type %d", &tmp))
577      md_type = tmp;
578    else if (unformat(line_input, "next-ip4"))
579      next_protocol = 1;
580    else if (unformat(line_input, "next-ip6"))
581      next_protocol = 2;
582    else if (unformat(line_input, "next-ethernet"))
583      next_protocol = 3;
584    else if (unformat (line_input, "c1 %d", &c1))
585      ;
586    else if (unformat (line_input, "c2 %d", &c2))
587      ;
588    else if (unformat (line_input, "c3 %d", &c3))
589      ;
590    else if (unformat (line_input, "c4 %d", &c4))
591      ;
592    else if (unformat (line_input, "nsp %d", &nsp))
593      nsp_set = 1;
594    else if (unformat (line_input, "nsi %d", &nsi))
595      nsi_set = 1;
596    else
597      return clib_error_return (0, "parse error: '%U'",
598                                format_unformat_error, line_input);
599  }
600
601  unformat_free (line_input);
602
603  if (nsp_set == 0)
604    return clib_error_return (0, "nsp not specified");
605
606  if (nsi_set == 0)
607    return clib_error_return (0, "nsi not specified");
608
609  if (md_type != 1)
610    return clib_error_return (0, "md-type 1 only supported at this time");
611
612  md_type = 1;
613  length = 6;
614
615  nsp_nsi = (nsp<<8) | nsi;
616
617  memset (a, 0, sizeof (*a));
618
619  a->is_add = is_add;
620
621#define _(x) a->nsh.x = x;
622  foreach_copy_nshhdr_field;
623#undef _
624
625  rv = nsh_add_del_entry (a, &entry_index);
626
627  switch(rv)
628    {
629    case 0:
630      break;
631    default:
632      return clib_error_return
633        (0, "nsh_add_del_entry returned %d", rv);
634    }
635
636  return 0;
637}
638
639VLIB_CLI_COMMAND (create_nsh_entry_command, static) = {
640  .path = "create nsh entry",
641  .short_help =
642  "create nsh entry {nsp <nn> nsi <nn>} c1 <nn> c2 <nn> c3 <nn> c4 <nn>"
643  " [md-type <nn>] [tlv <xx>] [del]\n",
644  .function = nsh_add_del_entry_command_fn,
645};
646
647/** API message handler */
648static void vl_api_nsh_add_del_entry_t_handler
649(vl_api_nsh_add_del_entry_t * mp)
650{
651  vl_api_nsh_add_del_entry_reply_t * rmp;
652  nsh_main_t * nm = &nsh_main;
653  int rv;
654  nsh_add_del_entry_args_t _a, *a = &_a;
655  u32 entry_index = ~0;
656
657  a->is_add = mp->is_add;
658  a->nsh.ver_o_c = mp->ver_o_c;
659  a->nsh.length = mp->length;
660  a->nsh.md_type = mp->md_type;
661  a->nsh.next_protocol = mp->next_protocol;
662  a->nsh.nsp_nsi = ntohl(mp->nsp_nsi);
663  a->nsh.c1 = ntohl(mp->c1);
664  a->nsh.c2 = ntohl(mp->c2);
665  a->nsh.c3 = ntohl(mp->c3);
666  a->nsh.c4 = ntohl(mp->c4);
667
668  rv = nsh_add_del_entry (a, &entry_index);
669
670  REPLY_MACRO2(VL_API_NSH_ADD_DEL_ENTRY_REPLY,
671  ({
672    rmp->entry_index = htonl (entry_index);
673  }));
674}
675
676static void send_nsh_entry_details
677(nsh_header_t * t, unix_shared_memory_queue_t * q, u32 context)
678{
679    vl_api_nsh_entry_details_t * rmp;
680    nsh_main_t * nm = &nsh_main;
681
682    rmp = vl_msg_api_alloc (sizeof (*rmp));
683    memset (rmp, 0, sizeof (*rmp));
684
685    rmp->_vl_msg_id = ntohs((VL_API_NSH_ENTRY_DETAILS)+nm->msg_id_base);
686    rmp->ver_o_c = t->ver_o_c;
687    rmp->length = t->length;
688    rmp->md_type = t->md_type;
689    rmp->next_protocol = t->next_protocol;
690    rmp->nsp_nsi = htonl(t->nsp_nsi);
691    rmp->c1 = htonl(t->c1);
692    rmp->c2 = htonl(t->c2);
693    rmp->c3 = htonl(t->c3);
694    rmp->c4 = htonl(t->c4);
695
696    rmp->context = context;
697
698    vl_msg_api_send_shmem (q, (u8 *)&rmp);
699}
700
701static void vl_api_nsh_entry_dump_t_handler
702(vl_api_nsh_entry_dump_t * mp)
703{
704    unix_shared_memory_queue_t * q;
705    nsh_main_t * nm = &nsh_main;
706    nsh_header_t * t;
707    u32 entry_index;
708
709    q = vl_api_client_index_to_input_queue (mp->client_index);
710    if (q == 0) {
711        return;
712    }
713
714    entry_index = ntohl (mp->entry_index);
715
716    if (~0 == entry_index)
717      {
718	pool_foreach (t, nm->nsh_entries,
719	({
720	    send_nsh_entry_details(t, q, mp->context);
721	}));
722      }
723    else
724      {
725        if (entry_index >= vec_len (nm->nsh_entries))
726  	{
727  	  return;
728  	}
729        t = &nm->nsh_entries[entry_index];
730        send_nsh_entry_details(t, q, mp->context);
731      }
732}
733
734static void send_nsh_map_details
735(nsh_map_t * t, unix_shared_memory_queue_t * q, u32 context)
736{
737    vl_api_nsh_map_details_t * rmp;
738    nsh_main_t * nm = &nsh_main;
739
740    rmp = vl_msg_api_alloc (sizeof (*rmp));
741    memset (rmp, 0, sizeof (*rmp));
742
743    rmp->_vl_msg_id = ntohs((VL_API_NSH_MAP_DETAILS)+nm->msg_id_base);
744    rmp->nsp_nsi = htonl(t->nsp_nsi);
745    rmp->mapped_nsp_nsi = htonl(t->mapped_nsp_nsi);
746    rmp->nsh_action = htonl(t->nsh_action);
747    rmp->sw_if_index = htonl(t->sw_if_index);
748    rmp->next_node = htonl(t->next_node);
749
750    rmp->context = context;
751
752    vl_msg_api_send_shmem (q, (u8 *)&rmp);
753}
754
755static void vl_api_nsh_map_dump_t_handler
756(vl_api_nsh_map_dump_t * mp)
757{
758    unix_shared_memory_queue_t * q;
759    nsh_main_t * nm = &nsh_main;
760    nsh_map_t * t;
761    u32 map_index;
762
763    q = vl_api_client_index_to_input_queue (mp->client_index);
764    if (q == 0) {
765        return;
766    }
767
768    map_index = ntohl (mp->map_index);
769
770    if (~0 == map_index)
771      {
772	pool_foreach (t, nm->nsh_mappings,
773	({
774	    send_nsh_map_details(t, q, mp->context);
775	}));
776      }
777    else
778      {
779        if (map_index >= vec_len (nm->nsh_mappings))
780  	{
781  	  return;
782  	}
783        t = &nm->nsh_mappings[map_index];
784        send_nsh_map_details(t, q, mp->context);
785      }
786}
787
788static clib_error_t *
789show_nsh_entry_command_fn (vlib_main_t * vm,
790			   unformat_input_t * input,
791			   vlib_cli_command_t * cmd)
792{
793  nsh_main_t * nm = &nsh_main;
794  nsh_header_t * hdr;
795
796  if (pool_elts (nm->nsh_entries) == 0)
797    vlib_cli_output (vm, "No nsh entries configured.");
798
799  pool_foreach (hdr, nm->nsh_entries,
800		({
801		  vlib_cli_output (vm, "%U", format_nsh_header, hdr);
802		}));
803
804  return 0;
805}
806
807VLIB_CLI_COMMAND (show_nsh_entry_command, static) = {
808  .path = "show nsh entry",
809  .function = show_nsh_entry_command_fn,
810};
811
812
813/* Set up the API message handling tables */
814static clib_error_t *
815nsh_plugin_api_hookup (vlib_main_t *vm)
816{
817  nsh_main_t * nm = &nsh_main;
818#define _(N,n)                                                  \
819  vl_msg_api_set_handlers((VL_API_##N + nm->msg_id_base),	\
820			  #n,					\
821			  vl_api_##n##_t_handler,		\
822			  vl_noop_handler,			\
823			  vl_api_##n##_t_endian,		\
824			  vl_api_##n##_t_print,			\
825			  sizeof(vl_api_##n##_t), 1);
826  foreach_nsh_plugin_api_msg;
827#undef _
828
829  return 0;
830}
831
832
833
834
835static uword
836nsh_input_map (vlib_main_t * vm,
837               vlib_node_runtime_t * node,
838               vlib_frame_t * from_frame)
839{
840  u32 n_left_from, next_index, *from, *to_next;
841  nsh_main_t * nm = &nsh_main;
842
843  from = vlib_frame_vector_args(from_frame);
844  n_left_from = from_frame->n_vectors;
845
846  next_index = node->cached_next_index;
847
848  while (n_left_from > 0)
849    {
850      u32 n_left_to_next;
851
852      vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
853
854      while (n_left_from >= 4 && n_left_to_next >= 2)
855	{
856	  u32 bi0, bi1;
857	  vlib_buffer_t * b0, *b1;
858	  u32 next0 = NSH_INPUT_NEXT_DROP, next1 = NSH_INPUT_NEXT_DROP;
859	  uword * entry0, *entry1;
860	  nsh_header_t * hdr0 = 0, *hdr1 = 0;
861	  u32 header_len0 = 0, header_len1 = 0;
862	  u32 nsp_nsi0, nsp_nsi1;
863	  u32 error0, error1;
864	  nsh_map_t * map0 = 0, *map1 = 0;
865	  nsh_header_t *encap_hdr0 = 0, *encap_hdr1 = 0;
866	  u32 encap_hdr_len0 = 0, encap_hdr_len1 = 0;
867
868	  /* Prefetch next iteration. */
869	  {
870	    vlib_buffer_t * p2, *p3;
871
872	    p2 = vlib_get_buffer(vm, from[2]);
873	    p3 = vlib_get_buffer(vm, from[3]);
874
875	    vlib_prefetch_buffer_header(p2, LOAD);
876	    vlib_prefetch_buffer_header(p3, LOAD);
877
878	    CLIB_PREFETCH(p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
879	    CLIB_PREFETCH(p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
880	  }
881
882	  bi0 = from[0];
883	  bi1 = from[1];
884	  to_next[0] = bi0;
885	  to_next[1] = bi1;
886	  from += 2;
887	  to_next += 2;
888	  n_left_from -= 2;
889	  n_left_to_next -= 2;
890
891	  error0 = 0;
892	  error1 = 0;
893
894	  b0 = vlib_get_buffer(vm, bi0);
895	  hdr0 = vlib_buffer_get_current(b0);
896	  header_len0 = hdr0->length * 4;
897	  nsp_nsi0 = clib_net_to_host_u32(hdr0->nsp_nsi);
898
899	  b1 = vlib_get_buffer(vm, bi1);
900	  hdr1 = vlib_buffer_get_current(b1);
901	  header_len1 = hdr1->length * 4;
902	  nsp_nsi1 = clib_net_to_host_u32(hdr1->nsp_nsi);
903
904	  /* Process packet 0 */
905	  entry0 = hash_get_mem(nm->nsh_mapping_by_key, &nsp_nsi0);
906	  if (PREDICT_FALSE(entry0 == 0))
907	    {
908	      error0 = NSH_INPUT_ERROR_NO_MAPPING;
909	      goto trace0;
910	    }
911
912	  /* Entry should point to a mapping ...*/
913	  map0 = pool_elt_at_index(nm->nsh_mappings, entry0[0]);
914	  if (PREDICT_FALSE(map0 == 0))
915	    {
916	      error0 = NSH_INPUT_ERROR_NO_MAPPING;
917	      goto trace0;
918	    }
919
920	  /* set up things for next node to transmit ie which node to handle it and where */
921	  next0 = map0->next_node;
922	  vnet_buffer(b0)->sw_if_index[VLIB_TX] = map0->sw_if_index;
923
924	  if(PREDICT_FALSE(map0->nsh_action == NSH_ACTION_POP))
925	    {
926              /* Pop NSH header */
927	      vlib_buffer_advance(b0, (word)header_len0);
928	      goto trace0;
929	    }
930
931	  entry0 = hash_get_mem(nm->nsh_entry_by_key, &map0->mapped_nsp_nsi);
932	  if (PREDICT_FALSE(entry0 == 0))
933	    {
934	      error0 = NSH_INPUT_ERROR_NO_ENTRY;
935	      goto trace0;
936	    }
937
938	  encap_hdr0 = pool_elt_at_index(nm->nsh_entries, entry0[0]);
939	  encap_hdr_len0 = encap_hdr0->length * 4;
940
941          if(PREDICT_TRUE(map0->nsh_action == NSH_ACTION_SWAP))
942            {
943              /* Pop old NSH header */
944              vlib_buffer_advance(b0, (word)header_len0);
945
946              /* Push new NSH header */
947              vlib_buffer_advance(b0, -(word)encap_hdr_len0);
948              hdr0 = vlib_buffer_get_current(b0);
949              clib_memcpy(hdr0, encap_hdr0, (word)encap_hdr_len0);
950
951              goto trace0;
952           }
953
954          if(PREDICT_FALSE(map0->nsh_action == NSH_ACTION_PUSH))
955            {
956              /* Push new NSH header */
957              vlib_buffer_advance(b0, -(word)encap_hdr_len0);
958              hdr0 = vlib_buffer_get_current(b0);
959              clib_memcpy(hdr0, encap_hdr0, (word)encap_hdr_len0);
960            }
961
962        trace0: b0->error = error0 ? node->errors[error0] : 0;
963
964          if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
965            {
966              nsh_input_trace_t *tr = vlib_add_trace(vm, node, b0, sizeof(*tr));
967              tr->nsh_header = *hdr0;
968            }
969
970	  /* Process packet 1 */
971	  entry1 = hash_get_mem(nm->nsh_mapping_by_key, &nsp_nsi1);
972	  if (PREDICT_FALSE(entry1 == 0))
973	    {
974	      error1 = NSH_INPUT_ERROR_NO_MAPPING;
975	      goto trace1;
976	    }
977
978	  /* Entry should point to a mapping ...*/
979	  map1 = pool_elt_at_index(nm->nsh_mappings, entry1[0]);
980	  if (PREDICT_FALSE(map1 == 0))
981	    {
982	      error1 = NSH_INPUT_ERROR_NO_MAPPING;
983	      goto trace1;
984	    }
985
986	  /* set up things for next node to transmit ie which node to handle it and where */
987	  next1 = map1->next_node;
988	  vnet_buffer(b1)->sw_if_index[VLIB_TX] = map1->sw_if_index;
989
990	  if(PREDICT_FALSE(map1->nsh_action == NSH_ACTION_POP))
991	    {
992          /* Pop NSH header */
993	      vlib_buffer_advance(b1, (word)header_len1);
994	      goto trace1;
995	    }
996
997	  entry1 = hash_get_mem(nm->nsh_entry_by_key, &map1->mapped_nsp_nsi);
998	  if (PREDICT_FALSE(entry1 == 0))
999	    {
1000	      error1 = NSH_INPUT_ERROR_NO_ENTRY;
1001	      goto trace1;
1002	    }
1003
1004	  encap_hdr1 = pool_elt_at_index(nm->nsh_entries, entry1[0]);
1005	  encap_hdr_len1 = encap_hdr1->length * 4;
1006
1007          if(PREDICT_TRUE(map1->nsh_action == NSH_ACTION_SWAP))
1008            {
1009              /* Pop old NSH header */
1010              vlib_buffer_advance(b1, (word)header_len1);
1011
1012              /* Push new NSH header */
1013              vlib_buffer_advance(b1, -(word)encap_hdr_len1);
1014              hdr1 = vlib_buffer_get_current(b1);
1015              clib_memcpy(hdr1, encap_hdr1, (word)encap_hdr_len1);
1016
1017              goto trace1;
1018            }
1019
1020          if(PREDICT_FALSE(map1->nsh_action == NSH_ACTION_PUSH))
1021            {
1022              /* Push new NSH header */
1023              vlib_buffer_advance(b1, -(word)encap_hdr_len1);
1024              hdr1 = vlib_buffer_get_current(b1);
1025              clib_memcpy(hdr1, encap_hdr1, (word)encap_hdr_len1);
1026            }
1027
1028	trace1: b1->error = error1 ? node->errors[error1] : 0;
1029
1030	  if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
1031	    {
1032	      nsh_input_trace_t *tr = vlib_add_trace(vm, node, b1, sizeof(*tr));
1033	      tr->nsh_header = *hdr1;
1034	    }
1035
1036	  vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
1037					  n_left_to_next, bi0, bi1, next0, next1);
1038
1039	}
1040
1041      while (n_left_from > 0 && n_left_to_next > 0)
1042	{
1043	  u32 bi0;
1044	  vlib_buffer_t * b0;
1045	  u32 next0 = NSH_INPUT_NEXT_DROP;
1046	  uword * entry0;
1047	  nsh_header_t * hdr0 = 0;
1048	  u32 header_len0 = 0;
1049	  u32 nsp_nsi0;
1050	  u32 error0;
1051	  nsh_map_t * map0 = 0;
1052	  nsh_header_t * encap_hdr0 = 0;
1053	  u32 encap_hdr_len0 = 0;
1054
1055	  bi0 = from[0];
1056	  to_next[0] = bi0;
1057	  from += 1;
1058	  to_next += 1;
1059	  n_left_from -= 1;
1060	  n_left_to_next -= 1;
1061	  error0 = 0;
1062
1063	  b0 = vlib_get_buffer(vm, bi0);
1064	  hdr0 = vlib_buffer_get_current(b0);
1065	  header_len0 = hdr0->length * 4;
1066	  nsp_nsi0 = clib_net_to_host_u32(hdr0->nsp_nsi);
1067	  entry0 = hash_get_mem(nm->nsh_mapping_by_key, &nsp_nsi0);
1068
1069	  if (PREDICT_FALSE(entry0 == 0))
1070	    {
1071	      error0 = NSH_INPUT_ERROR_NO_MAPPING;
1072	      goto trace00;
1073	    }
1074
1075	  /* Entry should point to a mapping ...*/
1076	  map0 = pool_elt_at_index(nm->nsh_mappings, entry0[0]);
1077
1078	  if (PREDICT_FALSE(map0 == 0))
1079	    {
1080	      error0 = NSH_INPUT_ERROR_NO_MAPPING;
1081	      goto trace00;
1082	    }
1083
1084	  /* set up things for next node to transmit ie which node to handle it and where */
1085	  next0 = map0->next_node;
1086	  vnet_buffer(b0)->sw_if_index[VLIB_TX] = map0->sw_if_index;
1087
1088	  if(PREDICT_FALSE(map0->nsh_action == NSH_ACTION_POP))
1089	    {
1090          /* Pop NSH header */
1091	      vlib_buffer_advance(b0, (word)header_len0);
1092	      goto trace00;
1093	    }
1094
1095	  entry0 = hash_get_mem(nm->nsh_entry_by_key, &map0->mapped_nsp_nsi);
1096
1097	  if (PREDICT_FALSE(entry0 == 0))
1098	    {
1099	      error0 = NSH_INPUT_ERROR_NO_ENTRY;
1100	      goto trace00;
1101	    }
1102
1103	  encap_hdr0 = pool_elt_at_index(nm->nsh_entries, entry0[0]);
1104	  encap_hdr_len0 = encap_hdr0->length * 4;
1105
1106	  if(PREDICT_TRUE(map0->nsh_action == NSH_ACTION_SWAP))
1107	    {
1108              /* Pop old NSH header */
1109	      vlib_buffer_advance(b0, (word)header_len0);
1110
1111	      /* Push new NSH header */
1112	      vlib_buffer_advance(b0, -(word)encap_hdr_len0);
1113	      hdr0 = vlib_buffer_get_current(b0);
1114	      clib_memcpy(hdr0, encap_hdr0, (word)encap_hdr_len0);
1115
1116	      goto trace00;
1117	    }
1118
1119	  if(PREDICT_TRUE(map0->nsh_action == NSH_ACTION_PUSH))
1120	    {
1121	      /* Push new NSH header */
1122	      vlib_buffer_advance(b0, -(word)encap_hdr_len0);
1123	      hdr0 = vlib_buffer_get_current(b0);
1124	      clib_memcpy(hdr0, encap_hdr0, (word)encap_hdr_len0);
1125	    }
1126
1127	trace00: b0->error = error0 ? node->errors[error0] : 0;
1128
1129	  if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
1130	    {
1131	      nsh_input_trace_t *tr = vlib_add_trace(vm, node, b0, sizeof(*tr));
1132	      tr->nsh_header = *hdr0;
1133	    }
1134
1135	  vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
1136					  n_left_to_next, bi0, next0);
1137	}
1138
1139      vlib_put_next_frame(vm, node, next_index, n_left_to_next);
1140
1141    }
1142
1143  return from_frame->n_vectors;
1144}
1145
1146
1147static char * nsh_input_error_strings[] = {
1148#define _(sym,string) string,
1149  foreach_nsh_input_error
1150#undef _
1151};
1152
1153VLIB_REGISTER_NODE (nsh_input_node) = {
1154  .function = nsh_input_map,
1155  .name = "nsh-input",
1156  .vector_size = sizeof (u32),
1157  .format_trace = format_nsh_input_map_trace,
1158  .format_buffer = format_nsh_header_with_length,
1159  .type = VLIB_NODE_TYPE_INTERNAL,
1160
1161  .n_errors = ARRAY_LEN(nsh_input_error_strings),
1162  .error_strings = nsh_input_error_strings,
1163
1164  .n_next_nodes = NSH_INPUT_N_NEXT,
1165
1166  .next_nodes = {
1167#define _(s,n) [NSH_INPUT_NEXT_##s] = n,
1168    foreach_nsh_input_next
1169#undef _
1170  },
1171};
1172
1173clib_error_t *nsh_init (vlib_main_t *vm)
1174{
1175  nsh_main_t *nm = &nsh_main;
1176  clib_error_t * error = 0;
1177  vlib_node_t * vxlan4_gpe_input_node = 0;
1178  vlib_node_t * vxlan6_gpe_input_node = 0;
1179  vlib_node_t * gre_input_node = 0;
1180  u8 * name;
1181
1182  /* Init the main structures from VPP */
1183  nm->vlib_main = vm;
1184  nm->vnet_main = vnet_get_main();
1185
1186  /* Various state maintenance mappings */
1187  nm->nsh_mapping_by_key
1188    = hash_create_mem (0, sizeof(u32), sizeof (uword));
1189
1190  nm->nsh_mapping_by_mapped_key
1191    = hash_create_mem (0, sizeof(u32), sizeof (uword));
1192
1193  nm->nsh_entry_by_key
1194    = hash_create_mem (0, sizeof(u32), sizeof (uword));
1195
1196  name = format (0, "nsh_%08x%c", api_version, 0);
1197
1198  /* Set up the API */
1199  nm->msg_id_base = vl_msg_api_get_msg_ids
1200    ((char *) name, VL_MSG_FIRST_AVAILABLE);
1201
1202  error = nsh_plugin_api_hookup (vm);
1203
1204  /* Add dispositions to nodes that feed nsh-input */
1205  vxlan4_gpe_input_node = vlib_get_node_by_name (vm, (u8 *)"vxlan4-gpe-input");
1206  ASSERT(vxlan4_gpe_input_node);
1207  //alagalah - validate we don't really need to use the node value
1208  vlib_node_add_next (vm, vxlan4_gpe_input_node->index, nsh_input_node.index);
1209
1210  vxlan6_gpe_input_node = vlib_get_node_by_name (vm, (u8 *)"vxlan6-gpe-input");
1211  ASSERT(vxlan6_gpe_input_node);
1212  vlib_node_add_next (vm, vxlan6_gpe_input_node->index, nsh_input_node.index);
1213
1214  gre_input_node = vlib_get_node_by_name (vm, (u8 *)"gre-input");
1215  ASSERT(gre_input_node);
1216  vlib_node_add_next (vm, gre_input_node->index, nsh_input_node.index);
1217
1218  vec_free(name);
1219
1220  return error;
1221}
1222
1223VLIB_INIT_FUNCTION(nsh_init);
1224