c11 safe string handling support
[vpp.git] / src / plugins / srv6-ad / ad.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  *------------------------------------------------------------------
17  * ad.c - SRv6 Dynamic Proxy (AD) function
18  *------------------------------------------------------------------
19  */
20
21 #include <vnet/vnet.h>
22 #include <vnet/adj/adj.h>
23 #include <vnet/plugin/plugin.h>
24 #include <vpp/app/version.h>
25 #include <srv6-ad/ad.h>
26
27 #define SID_CREATE_IFACE_FEATURE_ERROR  -1
28 #define SID_CREATE_INVALID_IFACE_TYPE   -3
29 #define SID_CREATE_INVALID_IFACE_INDEX  -4
30 #define SID_CREATE_INVALID_ADJ_INDEX    -5
31
32 unsigned char function_name[] = "SRv6-AD-plugin";
33 unsigned char keyword_str[] = "End.AD";
34 unsigned char def_str[] =
35   "Endpoint with dynamic proxy to SR-unaware appliance";
36 unsigned char params_str[] = "nh <next-hop> oif <iface-out> iif <iface-in>";
37
38
39 /*****************************************/
40 /* SRv6 LocalSID instantiation and removal functions */
41 static int
42 srv6_ad_localsid_creation_fn (ip6_sr_localsid_t * localsid)
43 {
44   ip6_sr_main_t *srm = &sr_main;
45   srv6_ad_main_t *sm = &srv6_ad_main;
46   srv6_ad_localsid_t *ls_mem = localsid->plugin_mem;
47   u32 localsid_index = localsid - srm->localsids;
48
49   /* Step 1: Prepare xconnect adjacency for sending packets to the VNF */
50
51   /* Retrieve the adjacency corresponding to the (OIF, next_hop) */
52   adj_index_t nh_adj_index = ADJ_INDEX_INVALID;
53   if (ls_mem->inner_type != AD_TYPE_L2)
54     {
55       if (ls_mem->inner_type == AD_TYPE_IP4)
56         nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4,
57                                             VNET_LINK_IP4, &ls_mem->nh_addr,
58                                             ls_mem->sw_if_index_out);
59       else if (ls_mem->inner_type == AD_TYPE_IP6)
60         nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6,
61                                             VNET_LINK_IP6, &ls_mem->nh_addr,
62                                             ls_mem->sw_if_index_out);
63       if (nh_adj_index == ADJ_INDEX_INVALID)
64         {
65           clib_mem_free (ls_mem);
66           return SID_CREATE_INVALID_ADJ_INDEX;
67         }
68     }
69
70   ls_mem->nh_adj = nh_adj_index;
71
72
73   /* Step 2: Prepare inbound policy for packets returning from the VNF */
74
75   /* Sanitise the SW_IF_INDEX */
76   if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces,
77                           ls_mem->sw_if_index_in))
78     {
79       adj_unlock (ls_mem->nh_adj);
80       clib_mem_free (ls_mem);
81       return SID_CREATE_INVALID_IFACE_INDEX;
82     }
83
84   vnet_sw_interface_t *sw = vnet_get_sw_interface (sm->vnet_main,
85                                                    ls_mem->sw_if_index_in);
86   if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
87     {
88       adj_unlock (ls_mem->nh_adj);
89       clib_mem_free (ls_mem);
90       return SID_CREATE_INVALID_IFACE_TYPE;
91     }
92
93   if (ls_mem->inner_type == AD_TYPE_L2)
94     {
95       /* Enable End.AD2 rewrite node for this interface */
96       int ret =
97         vnet_feature_enable_disable ("device-input", "srv6-ad2-rewrite",
98                                      ls_mem->sw_if_index_in, 1, 0, 0);
99       if (ret != 0)
100         {
101           clib_mem_free (ls_mem);
102           return SID_CREATE_IFACE_FEATURE_ERROR;
103         }
104
105       /* Set interface in promiscuous mode */
106       vnet_main_t *vnm = vnet_get_main ();
107       ethernet_set_flags (vnm, ls_mem->sw_if_index_in,
108                           ETHERNET_INTERFACE_FLAG_ACCEPT_ALL);
109
110       /* Associate local SID index to this interface (resize vector if needed) */
111       if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid2))
112         {
113           vec_resize (sm->sw_iface_localsid2,
114                       (pool_len (sm->vnet_main->interface_main.sw_interfaces)
115                        - vec_len (sm->sw_iface_localsid2)));
116         }
117       sm->sw_iface_localsid2[ls_mem->sw_if_index_in] = localsid_index;
118     }
119   else if (ls_mem->inner_type == AD_TYPE_IP4)
120     {
121       /* Enable End.AD4 rewrite node for this interface */
122       int ret =
123         vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-rewrite",
124                                      ls_mem->sw_if_index_in, 1, 0, 0);
125       if (ret != 0)
126         {
127           adj_unlock (ls_mem->nh_adj);
128           clib_mem_free (ls_mem);
129           return SID_CREATE_IFACE_FEATURE_ERROR;
130         }
131
132       /* Associate local SID index to this interface (resize vector if needed) */
133       if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid4))
134         {
135           vec_resize (sm->sw_iface_localsid4,
136                       (pool_len (sm->vnet_main->interface_main.sw_interfaces)
137                        - vec_len (sm->sw_iface_localsid4)));
138         }
139       sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = localsid_index;
140     }
141   else if (ls_mem->inner_type == AD_TYPE_IP6)
142     {
143       /* Enable End.AD6 rewrite node for this interface */
144       int ret =
145         vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-rewrite",
146                                      ls_mem->sw_if_index_in, 1, 0, 0);
147       if (ret != 0)
148         {
149           adj_unlock (ls_mem->nh_adj);
150           clib_mem_free (ls_mem);
151           return SID_CREATE_IFACE_FEATURE_ERROR;
152         }
153
154       /* Associate local SID index to this interface (resize vector if needed) */
155       if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid6))
156         {
157           vec_resize (sm->sw_iface_localsid6,
158                       (pool_len (sm->vnet_main->interface_main.sw_interfaces)
159                        - vec_len (sm->sw_iface_localsid6)));
160         }
161       sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = localsid_index;
162     }
163
164   ls_mem->rw_len = 0;
165
166   /* Step 3: Initialize rewrite counters */
167   srv6_ad_localsid_t **ls_p;
168   pool_get (sm->sids, ls_p);
169   *ls_p = ls_mem;
170   ls_mem->index = ls_p - sm->sids;
171
172   vlib_validate_combined_counter (&(sm->valid_counters), ls_mem->index);
173   vlib_validate_combined_counter (&(sm->invalid_counters), ls_mem->index);
174
175   vlib_zero_combined_counter (&(sm->valid_counters), ls_mem->index);
176   vlib_zero_combined_counter (&(sm->invalid_counters), ls_mem->index);
177
178   return 0;
179 }
180
181 static int
182 srv6_ad_localsid_removal_fn (ip6_sr_localsid_t * localsid)
183 {
184   srv6_ad_main_t *sm = &srv6_ad_main;
185   srv6_ad_localsid_t *ls_mem = localsid->plugin_mem;
186
187   if (ls_mem->inner_type == AD_TYPE_L2)
188     {
189       /* Disable End.AD2 rewrite node for this interface */
190       int ret =
191         vnet_feature_enable_disable ("device-input", "srv6-ad2-rewrite",
192                                      ls_mem->sw_if_index_in, 0, 0, 0);
193       if (ret != 0)
194         return -1;
195
196       /* Disable promiscuous mode on the interface */
197       vnet_main_t *vnm = vnet_get_main ();
198       ethernet_set_flags (vnm, ls_mem->sw_if_index_in, 0);
199
200       /* Remove local SID index from interface table */
201       sm->sw_iface_localsid2[ls_mem->sw_if_index_in] = ~(u32) 0;
202     }
203   else if (ls_mem->inner_type == AD_TYPE_IP4)
204     {
205       /* Disable End.AD4 rewrite node for this interface */
206       int ret =
207         vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-rewrite",
208                                      ls_mem->sw_if_index_in, 0, 0, 0);
209       if (ret != 0)
210         return -1;
211
212       /* Remove local SID pointer from interface table */
213       sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = ~(u32) 0;
214     }
215   else if (ls_mem->inner_type == AD_TYPE_IP6)
216     {
217       /* Disable End.AD6 rewrite node for this interface */
218       int ret =
219         vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-rewrite",
220                                      ls_mem->sw_if_index_in, 0, 0, 0);
221       if (ret != 0)
222         return -1;
223
224       /* Remove local SID pointer from interface table */
225       sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = ~(u32) 0;
226     }
227
228
229   /* Unlock (OIF, NHOP) adjacency */
230   adj_unlock (ls_mem->nh_adj);
231
232   /* Delete SID entry */
233   pool_put (sm->sids, pool_elt_at_index (sm->sids, ls_mem->index));
234
235   /* Clean up local SID memory */
236   vec_free (ls_mem->rewrite);
237   clib_mem_free (localsid->plugin_mem);
238
239   return 0;
240 }
241
242 /**********************************/
243 /* SRv6 LocalSID format functions */
244 /*
245  * Prints nicely the parameters of a localsid
246  * Example: print "Table 5"
247  */
248 u8 *
249 format_srv6_ad_localsid (u8 * s, va_list * args)
250 {
251   srv6_ad_localsid_t *ls_mem = va_arg (*args, void *);
252
253   vnet_main_t *vnm = vnet_get_main ();
254   srv6_ad_main_t *sm = &srv6_ad_main;
255
256   if (ls_mem->inner_type == AD_TYPE_IP4)
257     {
258       s =
259         format (s, "Next-hop:\t%U\n\t", format_ip4_address,
260                 &ls_mem->nh_addr.ip4);
261     }
262   else if (ls_mem->inner_type == AD_TYPE_IP6)
263     {
264       s =
265         format (s, "Next-hop:\t%U\n\t", format_ip6_address,
266                 &ls_mem->nh_addr.ip6);
267     }
268
269   s = format (s, "Outgoing iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
270               ls_mem->sw_if_index_out);
271   s = format (s, "\tIncoming iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
272               ls_mem->sw_if_index_in);
273
274   vlib_counter_t valid, invalid;
275   vlib_get_combined_counter (&(sm->valid_counters), ls_mem->index, &valid);
276   vlib_get_combined_counter (&(sm->invalid_counters), ls_mem->index,
277                              &invalid);
278   s = format (s, "\tGood rewrite traffic: \t[%Ld packets : %Ld bytes]\n",
279               valid.packets, valid.bytes);
280   s = format (s, "\tBad rewrite traffic:  \t[%Ld packets : %Ld bytes]\n",
281               invalid.packets, invalid.bytes);
282
283   return s;
284 }
285
286 /*
287  * Process the parameters of a localsid
288  * Example: process from:
289  * sr localsid address cafe::1 behavior new_srv6_localsid 5
290  * everything from behavior on... so in this case 'new_srv6_localsid 5'
291  * Notice that it MUST match the keyword_str and params_str defined above.
292  */
293 uword
294 unformat_srv6_ad_localsid (unformat_input_t * input, va_list * args)
295 {
296   void **plugin_mem_p = va_arg (*args, void **);
297   srv6_ad_localsid_t *ls_mem;
298
299   vnet_main_t *vnm = vnet_get_main ();
300
301   u8 inner_type = AD_TYPE_L2;
302   ip46_address_t nh_addr;
303   u32 sw_if_index_out;
304   u32 sw_if_index_in;
305
306   u8 params = 0;
307 #define PARAM_AD_NH   (1 << 0)
308 #define PARAM_AD_OIF  (1 << 1)
309 #define PARAM_AD_IIF  (1 << 2)
310
311   if (!unformat (input, "end.ad"))
312     return 0;
313
314   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
315     {
316       if (!(params & PARAM_AD_NH) && unformat (input, "nh %U",
317                                                unformat_ip4_address,
318                                                &nh_addr.ip4))
319         {
320           inner_type = AD_TYPE_IP4;
321           params |= PARAM_AD_NH;
322         }
323       if (!(params & PARAM_AD_NH) && unformat (input, "nh %U",
324                                                unformat_ip6_address,
325                                                &nh_addr.ip6))
326         {
327           inner_type = AD_TYPE_IP6;
328           params |= PARAM_AD_NH;
329         }
330       else if (!(params & PARAM_AD_OIF) && unformat (input, "oif %U",
331                                                      unformat_vnet_sw_interface,
332                                                      vnm, &sw_if_index_out))
333         {
334           params |= PARAM_AD_OIF;
335         }
336       else if (!(params & PARAM_AD_IIF) && unformat (input, "iif %U",
337                                                      unformat_vnet_sw_interface,
338                                                      vnm, &sw_if_index_in))
339         {
340           params |= PARAM_AD_IIF;
341         }
342       else
343         {
344           break;
345         }
346     }
347
348   /* Make sure that all parameters are supplied */
349   u8 params_chk = (PARAM_AD_OIF | PARAM_AD_IIF);
350   if ((params & params_chk) != params_chk)
351     {
352       return 0;
353     }
354
355   /* Allocate and initialize memory block for local SID parameters */
356   ls_mem = clib_mem_alloc_aligned_at_offset (sizeof *ls_mem, 0, 0, 1);
357   clib_memset (ls_mem, 0, sizeof *ls_mem);
358   *plugin_mem_p = ls_mem;
359
360   /* Set local SID parameters */
361   ls_mem->inner_type = inner_type;
362   if (inner_type == AD_TYPE_IP4)
363     ls_mem->nh_addr.ip4 = nh_addr.ip4;
364   else if (inner_type == AD_TYPE_IP6)
365     ls_mem->nh_addr.ip6 = nh_addr.ip6;
366   ls_mem->sw_if_index_out = sw_if_index_out;
367   ls_mem->sw_if_index_in = sw_if_index_in;
368
369   return 1;
370 }
371
372 /*************************/
373 /* SRv6 LocalSID FIB DPO */
374 static u8 *
375 format_srv6_ad_dpo (u8 * s, va_list * args)
376 {
377   index_t index = va_arg (*args, index_t);
378   CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
379
380   return (format (s, "SR: dynamic_proxy_index:[%u]", index));
381 }
382
383 void
384 srv6_ad_dpo_lock (dpo_id_t * dpo)
385 {
386 }
387
388 void
389 srv6_ad_dpo_unlock (dpo_id_t * dpo)
390 {
391 }
392
393 const static dpo_vft_t srv6_ad_vft = {
394   .dv_lock = srv6_ad_dpo_lock,
395   .dv_unlock = srv6_ad_dpo_unlock,
396   .dv_format = format_srv6_ad_dpo,
397 };
398
399 const static char *const srv6_ad_ip6_nodes[] = {
400   "srv6-ad-localsid",
401   NULL,
402 };
403
404 const static char *const *const srv6_ad_nodes[DPO_PROTO_NUM] = {
405   [DPO_PROTO_IP6] = srv6_ad_ip6_nodes,
406 };
407
408 /**********************/
409 static clib_error_t *
410 srv6_ad_init (vlib_main_t * vm)
411 {
412   srv6_ad_main_t *sm = &srv6_ad_main;
413   int rv = 0;
414
415   sm->vlib_main = vm;
416   sm->vnet_main = vnet_get_main ();
417
418   /* Create DPO */
419   sm->srv6_ad_dpo_type = dpo_register_new_type (&srv6_ad_vft, srv6_ad_nodes);
420
421   /* Register SRv6 LocalSID */
422   rv = sr_localsid_register_function (vm,
423                                       function_name,
424                                       keyword_str,
425                                       def_str,
426                                       params_str,
427                                       &sm->srv6_ad_dpo_type,
428                                       format_srv6_ad_localsid,
429                                       unformat_srv6_ad_localsid,
430                                       srv6_ad_localsid_creation_fn,
431                                       srv6_ad_localsid_removal_fn);
432   if (rv < 0)
433     clib_error_return (0, "SRv6 LocalSID function could not be registered.");
434   else
435     sm->srv6_localsid_behavior_id = rv;
436
437   return 0;
438 }
439
440 /* *INDENT-OFF* */
441 VNET_FEATURE_INIT (srv6_ad2_rewrite, static) =
442 {
443   .arc_name = "device-input",
444   .node_name = "srv6-ad2-rewrite",
445   .runs_before = VNET_FEATURES ("ethernet-input"),
446 };
447
448 VNET_FEATURE_INIT (srv6_ad4_rewrite, static) =
449 {
450   .arc_name = "ip4-unicast",
451   .node_name = "srv6-ad4-rewrite",
452   .runs_before = 0,
453 };
454
455 VNET_FEATURE_INIT (srv6_ad6_rewrite, static) =
456 {
457   .arc_name = "ip6-unicast",
458   .node_name = "srv6-ad6-rewrite",
459   .runs_before = 0,
460 };
461
462 VLIB_INIT_FUNCTION (srv6_ad_init);
463
464 VLIB_PLUGIN_REGISTER () = {
465   .version = VPP_BUILD_VER,
466   .description = "Dynamic SRv6 proxy",
467 };
468 /* *INDENT-ON* */
469
470 /*
471 * fd.io coding-style-patch-verification: ON
472 *
473 * Local Variables:
474 * eval: (c-set-style "gnu")
475 * End:
476 */