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#include <vnet/gre/gre.h>
22#include <vnet/vxlan/vxlan.h>
23#include <vnet/vxlan-gpe/vxlan_gpe.h>
24#include <vnet/l2/l2_classify.h>
25#include <vnet/adj/adj.h>
26#include <vpp/app/version.h>
27
28nsh_main_t nsh_main;
29
30/* Uses network order's class and type to register */
31int
32nsh_md2_register_option (u16 class,
33			 u8 type,
34			 u8 option_size,
35			 int add_options (u8 * opt,
36					  u8 * opt_size),
37			 int options (vlib_buffer_t * b,
38				      nsh_tlv_header_t * opt),
39			 int swap_options (vlib_buffer_t * b,
40					   nsh_tlv_header_t * old_opt,
41					   nsh_tlv_header_t * new_opt),
42			 int pop_options (vlib_buffer_t * b,
43					  nsh_tlv_header_t * opt),
44			 u8 * trace (u8 * s, nsh_tlv_header_t * opt))
45{
46  nsh_main_t *nm = &nsh_main;
47  nsh_option_map_by_key_t key, *key_copy;
48  uword *p;
49  nsh_option_map_t *nsh_option;
50
51  key.class = class;
52  key.type = type;
53  key.pad = 0;
54
55  p = hash_get_mem (nm->nsh_option_map_by_key, &key);
56  /* Already registered */
57  if (p != 0)
58    {
59      return (-1);
60    }
61
62  pool_get_aligned (nm->nsh_option_mappings, nsh_option,
63		    CLIB_CACHE_LINE_BYTES);
64  clib_memset (nsh_option, 0, sizeof (*nsh_option));
65  nsh_option->option_id = nsh_option - nm->nsh_option_mappings;
66
67  key_copy = clib_mem_alloc (sizeof (*key_copy));
68  clib_memcpy (key_copy, &key, sizeof (*key_copy));
69  hash_set_mem (nm->nsh_option_map_by_key, key_copy,
70		nsh_option - nm->nsh_option_mappings);
71
72  if (option_size > (MAX_NSH_OPTION_LEN + sizeof (nsh_tlv_header_t)))
73    {
74      return (-1);
75    }
76  nm->options_size[nsh_option->option_id] = option_size;
77  nm->add_options[nsh_option->option_id] = add_options;
78  nm->options[nsh_option->option_id] = options;
79  nm->swap_options[nsh_option->option_id] = swap_options;
80  nm->pop_options[nsh_option->option_id] = pop_options;
81  nm->trace[nsh_option->option_id] = trace;
82
83  return (0);
84}
85
86/* Uses network order's class and type to lookup */
87nsh_option_map_t *
88nsh_md2_lookup_option (u16 class, u8 type)
89{
90  nsh_main_t *nm = &nsh_main;
91  nsh_option_map_by_key_t key;
92  uword *p;
93
94  key.class = class;
95  key.type = type;
96  key.pad = 0;
97
98  p = hash_get_mem (nm->nsh_option_map_by_key, &key);
99  /* not registered */
100  if (p == 0)
101    {
102      return NULL;
103    }
104
105  return pool_elt_at_index (nm->nsh_option_mappings, p[0]);
106
107}
108
109/* Uses network order's class and type to unregister */
110int
111nsh_md2_unregister_option (u16 class,
112			   u8 type,
113			   int options (vlib_buffer_t * b,
114					nsh_tlv_header_t * opt),
115			   u8 * trace (u8 * s, nsh_tlv_header_t * opt))
116{
117  nsh_main_t *nm = &nsh_main;
118  nsh_option_map_by_key_t key, *key_copy;
119  uword *p;
120  hash_pair_t *hp;
121  nsh_option_map_t *nsh_option;
122
123  key.class = class;
124  key.type = type;
125  key.pad = 0;
126
127  p = hash_get_mem (nm->nsh_option_map_by_key, &key);
128  /* not registered */
129  if (p == 0)
130    {
131      return (-1);
132    }
133
134  nsh_option = pool_elt_at_index (nm->nsh_option_mappings, p[0]);
135  nm->options[nsh_option->option_id] = NULL;
136  nm->add_options[nsh_option->option_id] = NULL;
137  nm->pop_options[nsh_option->option_id] = NULL;
138  nm->trace[nsh_option->option_id] = NULL;
139
140  hp = hash_get_pair (nm->nsh_option_map_by_key, &key);
141  key_copy = (void *) (hp->key);
142  hash_unset_mem (nm->nsh_option_map_by_key, &key_copy);
143  clib_mem_free (key_copy);
144
145  pool_put (nm->nsh_option_mappings, nsh_option);
146
147  return (0);
148}
149
150/**
151 * @brief Formatting function for tracing VXLAN GPE with length
152 *
153 * @param *s
154 * @param *args
155 *
156 * @return *s
157 *
158 */
159static u8 *
160format_nsh_tunnel_with_length (u8 * s, va_list * args)
161{
162  u32 dev_instance = va_arg (*args, u32);
163  s = format (s, "unimplemented dev %u", dev_instance);
164  return s;
165}
166
167/* *INDENT-OFF* */
168VNET_HW_INTERFACE_CLASS (nsh_hw_class) = {
169  .name = "NSH",
170  .format_header = format_nsh_tunnel_with_length,
171  .build_rewrite = default_build_rewrite,
172  .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
173};
174/* *INDENT-ON* */
175
176void
177nsh_md2_set_next_ioam_export_override (uword next)
178{
179  nsh_main_t *hm = &nsh_main;
180  hm->decap_v4_next_override = next;
181  return;
182}
183
184clib_error_t *
185nsh_init (vlib_main_t * vm)
186{
187  vlib_node_t *node;
188  nsh_main_t *nm = &nsh_main;
189  clib_error_t *error = 0;
190  uword next_node;
191
192  /* Init the main structures from VPP */
193  nm->vlib_main = vm;
194  nm->vnet_main = vnet_get_main ();
195
196  /* Various state maintenance mappings */
197  nm->nsh_mapping_by_key = hash_create_mem (0, sizeof (u32), sizeof (uword));
198
199  nm->nsh_mapping_by_mapped_key
200    = hash_create_mem (0, sizeof (u32), sizeof (uword));
201
202  nm->nsh_entry_by_key = hash_create_mem (0, sizeof (u32), sizeof (uword));
203
204  nm->nsh_proxy_session_by_key
205    =
206    hash_create_mem (0, sizeof (nsh_proxy_session_by_key_t), sizeof (uword));
207
208  nm->nsh_option_map_by_key
209    = hash_create_mem (0, sizeof (nsh_option_map_by_key_t), sizeof (uword));
210
211  error = nsh_api_init (vm, nm);
212  if (error)
213    return error;
214
215  node = vlib_get_node_by_name (vm, (u8 *) "nsh-input");
216  nm->nsh_input_node_index = node->index;
217
218  node = vlib_get_node_by_name (vm, (u8 *) "nsh-proxy");
219  nm->nsh_proxy_node_index = node->index;
220
221  node = vlib_get_node_by_name (vm, (u8 *) "nsh-classifier");
222  nm->nsh_classifier_node_index = node->index;
223
224  /* Add dispositions to nodes that feed nsh-input */
225  //alagalah - validate we don't really need to use the node value
226  next_node =
227    vlib_node_add_next (vm, vxlan4_gpe_input_node.index,
228			nm->nsh_input_node_index);
229  vlib_node_add_next (vm, vxlan4_gpe_input_node.index,
230		      nm->nsh_proxy_node_index);
231  vlib_node_add_next (vm, vxlan4_gpe_input_node.index,
232		      nsh_aware_vnf_proxy_node.index);
233  vxlan_gpe_register_decap_protocol (VXLAN_GPE_PROTOCOL_NSH, next_node);
234
235  vlib_node_add_next (vm, vxlan6_gpe_input_node.index,
236		      nm->nsh_input_node_index);
237  vlib_node_add_next (vm, vxlan6_gpe_input_node.index,
238		      nm->nsh_proxy_node_index);
239  vlib_node_add_next (vm, vxlan6_gpe_input_node.index,
240		      nsh_aware_vnf_proxy_node.index);
241
242  vlib_node_add_next (vm, gre4_input_node.index, nm->nsh_input_node_index);
243  vlib_node_add_next (vm, gre4_input_node.index, nm->nsh_proxy_node_index);
244  vlib_node_add_next (vm, gre4_input_node.index,
245		      nsh_aware_vnf_proxy_node.index);
246
247  vlib_node_add_next (vm, gre6_input_node.index, nm->nsh_input_node_index);
248  vlib_node_add_next (vm, gre6_input_node.index, nm->nsh_proxy_node_index);
249  vlib_node_add_next (vm, gre6_input_node.index,
250		      nsh_aware_vnf_proxy_node.index);
251
252  /* Add NSH-Proxy support */
253  vlib_node_add_next (vm, vxlan4_input_node.index, nm->nsh_proxy_node_index);
254  vlib_node_add_next (vm, vxlan6_input_node.index, nm->nsh_proxy_node_index);
255
256  /* Add NSH-Classifier support */
257  vlib_node_add_next (vm, ip4_classify_node.index,
258		      nm->nsh_classifier_node_index);
259  vlib_node_add_next (vm, ip6_classify_node.index,
260		      nm->nsh_classifier_node_index);
261  vlib_node_add_next (vm, l2_input_classify_node.index,
262		      nm->nsh_classifier_node_index);
263
264  /* Add Ethernet+NSH support */
265  ethernet_register_input_type (vm, ETHERNET_TYPE_NSH,
266				nm->nsh_input_node_index);
267
268  return error;
269}
270
271VLIB_INIT_FUNCTION (nsh_init);
272
273/* *INDENT-OFF* */
274VLIB_PLUGIN_REGISTER () = {
275    .version = VPP_BUILD_VER,
276    .description = "Network Service Header (NSH)",
277};
278/* *INDENT-ON* */
279
280/*
281 * fd.io coding-style-patch-verification: ON
282 *
283 * Local Variables:
284 * eval: (c-set-style "gnu")
285 * End:
286 */
287