misc: remove GNU Indent directives
[vpp.git] / src / plugins / srv6-as / as.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  * as.c - SRv6 Static Proxy (AS) 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-as/as.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-AS-plugin";
33 unsigned char keyword_str[] = "End.AS";
34 unsigned char def_str[] =
35   "Endpoint with static proxy to SR-unaware appliance";
36 unsigned char params_str[] =
37   "nh <next-hop> oif <iface-out> iif <iface-in> src <src-addr> next <sid> [next <sid> ...]";
38
39 srv6_as_main_t srv6_as_main;
40
41 static inline u8 *
42 prepare_rewrite (ip6_address_t src_addr, ip6_address_t * sid_list,
43                  u8 protocol)
44 {
45   u8 *rewrite_str = NULL;
46   u32 rewrite_len = IPv6_DEFAULT_HEADER_LENGTH;
47
48   u8 num_sids = vec_len (sid_list);
49   u32 sr_hdr_len = 0;
50
51   if (num_sids > 1)
52     {
53       sr_hdr_len =
54         sizeof (ip6_sr_header_t) + num_sids * sizeof (ip6_address_t);
55       rewrite_len += sr_hdr_len;
56     }
57
58   vec_validate (rewrite_str, rewrite_len - 1);
59
60   /* Fill IP header */
61   ip6_header_t *iph = (ip6_header_t *) rewrite_str;
62   iph->ip_version_traffic_class_and_flow_label =
63     clib_host_to_net_u32 (0 | ((6 & 0xF) << 28));
64   iph->src_address = src_addr;
65   iph->dst_address = sid_list[0];
66   iph->payload_length = sr_hdr_len;
67   iph->hop_limit = sr_get_hop_limit ();
68
69   if (num_sids > 1)
70     {
71       /* Set Next Header value to Routing Extension */
72       iph->protocol = IP_PROTOCOL_IPV6_ROUTE;
73
74       /* Fill SR header */
75       ip6_sr_header_t *srh = (ip6_sr_header_t *) (iph + 1);
76       srh->protocol = protocol;
77       srh->length = sr_hdr_len / 8 - 1;
78       srh->type = ROUTING_HEADER_TYPE_SR;
79       srh->segments_left = num_sids - 1;
80       srh->last_entry = num_sids - 1;
81       srh->flags = 0x00;
82       srh->tag = 0x0000;
83
84       /* Fill segment list */
85       ip6_address_t *this_address;
86       ip6_address_t *addrp = srh->segments + srh->last_entry;
87       vec_foreach (this_address, sid_list)
88       {
89         *addrp = *this_address;
90         addrp--;
91       }
92     }
93   else
94     {
95       /* Set Next Header value to inner protocol */
96       iph->protocol = protocol;
97     }
98
99   return rewrite_str;
100 }
101
102 static inline void
103 free_ls_mem (srv6_as_localsid_t * ls_mem)
104 {
105   vec_free (ls_mem->rewrite);
106   vec_free (ls_mem->sid_list);
107   clib_mem_free (ls_mem);
108 }
109
110
111 /*****************************************/
112 /* SRv6 LocalSID instantiation and removal functions */
113 static int
114 srv6_as_localsid_creation_fn (ip6_sr_localsid_t * localsid)
115 {
116   ip6_sr_main_t *srm = &sr_main;
117   srv6_as_main_t *sm = &srv6_as_main;
118   srv6_as_localsid_t *ls_mem = localsid->plugin_mem;
119   u32 localsid_index = localsid - srm->localsids;
120
121   /* Step 1: Prepare xconnect adjacency for sending packets to the VNF */
122
123   /* Retrieve the adjacency corresponding to the (OIF, next_hop) */
124   adj_index_t nh_adj_index = ADJ_INDEX_INVALID;
125   if (ls_mem->inner_type != AS_TYPE_L2)
126     {
127       if (ls_mem->inner_type == AS_TYPE_IP4)
128         nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4,
129                                             VNET_LINK_IP4, &ls_mem->nh_addr,
130                                             ls_mem->sw_if_index_out);
131       else if (ls_mem->inner_type == AS_TYPE_IP6)
132         nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6,
133                                             VNET_LINK_IP6, &ls_mem->nh_addr,
134                                             ls_mem->sw_if_index_out);
135       if (nh_adj_index == ADJ_INDEX_INVALID)
136         {
137           free_ls_mem (ls_mem);
138           return SID_CREATE_INVALID_ADJ_INDEX;
139         }
140     }
141
142   ls_mem->nh_adj = nh_adj_index;
143
144
145   /* Step 2: Prepare inbound policy for packets returning from the VNF */
146
147   /* Make sure the provided incoming interface index is valid */
148   if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces,
149                           ls_mem->sw_if_index_in))
150     {
151       adj_unlock (ls_mem->nh_adj);
152       free_ls_mem (ls_mem);
153       return SID_CREATE_INVALID_IFACE_INDEX;
154     }
155
156   /* Retrieve associated interface structure */
157   vnet_sw_interface_t *sw = vnet_get_sw_interface (sm->vnet_main,
158                                                    ls_mem->sw_if_index_in);
159   if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
160     {
161       adj_unlock (ls_mem->nh_adj);
162       free_ls_mem (ls_mem);
163       return SID_CREATE_INVALID_IFACE_TYPE;
164     }
165
166   if (ls_mem->inner_type == AS_TYPE_L2)
167     {
168       /* Enable End.AS2 rewrite node for this interface */
169       int ret =
170         vnet_feature_enable_disable ("device-input", "srv6-as2-rewrite",
171                                      ls_mem->sw_if_index_in, 1, 0, 0);
172       if (ret != 0)
173         {
174           free_ls_mem (ls_mem);
175           return SID_CREATE_IFACE_FEATURE_ERROR;
176         }
177
178       /* Set interface in promiscuous mode */
179       vnet_main_t *vnm = vnet_get_main ();
180       vnet_hw_interface_t *hi =
181         vnet_get_sup_hw_interface (vnm, ls_mem->sw_if_index_in);
182       /* Make sure it is main interface */
183       if (hi->sw_if_index == ls_mem->sw_if_index_in)
184         ethernet_set_flags (vnm, hi->hw_if_index,
185                             ETHERNET_INTERFACE_FLAG_ACCEPT_ALL);
186
187       /* Prepare rewrite string */
188       ls_mem->rewrite = prepare_rewrite (ls_mem->src_addr, ls_mem->sid_list,
189                                          IP_PROTOCOL_IP6_ETHERNET);
190
191       /* Associate local SID index to this interface (resize vector if needed) */
192       if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid2))
193         {
194           vec_resize (sm->sw_iface_localsid2,
195                       (pool_len (sm->vnet_main->interface_main.sw_interfaces)
196                        - vec_len (sm->sw_iface_localsid2)));
197         }
198       sm->sw_iface_localsid2[ls_mem->sw_if_index_in] = localsid_index;
199     }
200   else if (ls_mem->inner_type == AS_TYPE_IP4)
201     {
202       /* Enable End.AS4 rewrite node for this interface */
203       int ret =
204         vnet_feature_enable_disable ("ip4-unicast", "srv6-as4-rewrite",
205                                      ls_mem->sw_if_index_in, 1, 0, 0);
206       if (ret != 0)
207         {
208           adj_unlock (ls_mem->nh_adj);
209           free_ls_mem (ls_mem);
210           return SID_CREATE_IFACE_FEATURE_ERROR;
211         }
212
213       /* Prepare rewrite string */
214       ls_mem->rewrite = prepare_rewrite (ls_mem->src_addr, ls_mem->sid_list,
215                                          IP_PROTOCOL_IP_IN_IP);
216
217       /* Associate local SID index to this interface (resize vector if needed) */
218       if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid4))
219         {
220           vec_resize (sm->sw_iface_localsid4,
221                       (pool_len (sm->vnet_main->interface_main.sw_interfaces)
222                        - vec_len (sm->sw_iface_localsid4)));
223         }
224       sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = localsid_index;
225     }
226   else if (ls_mem->inner_type == AS_TYPE_IP6)
227     {
228       /* Enable End.AS6 rewrite node for this interface */
229       int ret =
230         vnet_feature_enable_disable ("ip6-unicast", "srv6-as6-rewrite",
231                                      ls_mem->sw_if_index_in, 1, 0, 0);
232       if (ret != 0)
233         {
234           adj_unlock (ls_mem->nh_adj);
235           free_ls_mem (ls_mem);
236           return SID_CREATE_IFACE_FEATURE_ERROR;
237         }
238
239       /* Prepare rewrite string */
240       ls_mem->rewrite = prepare_rewrite (ls_mem->src_addr, ls_mem->sid_list,
241                                          IP_PROTOCOL_IPV6);
242
243       /* Associate local SID index to this interface (resize vector if needed) */
244       if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid6))
245         {
246           vec_resize (sm->sw_iface_localsid6,
247                       (pool_len (sm->vnet_main->interface_main.sw_interfaces)
248                        - vec_len (sm->sw_iface_localsid6)));
249         }
250       sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = localsid_index;
251     }
252
253   /* Step 3: Initialize rewrite counters */
254   srv6_as_localsid_t **ls_p;
255   pool_get (sm->sids, ls_p);
256   *ls_p = ls_mem;
257   ls_mem->index = ls_p - sm->sids;
258
259   vlib_validate_combined_counter (&(sm->valid_counters), ls_mem->index);
260   vlib_validate_combined_counter (&(sm->invalid_counters), ls_mem->index);
261
262   vlib_zero_combined_counter (&(sm->valid_counters), ls_mem->index);
263   vlib_zero_combined_counter (&(sm->invalid_counters), ls_mem->index);
264
265   return 0;
266 }
267
268 static int
269 srv6_as_localsid_removal_fn (ip6_sr_localsid_t * localsid)
270 {
271   srv6_as_main_t *sm = &srv6_as_main;
272   srv6_as_localsid_t *ls_mem = localsid->plugin_mem;
273
274   if (ls_mem->inner_type == AS_TYPE_L2)
275     {
276       /* Disable End.AS2 rewrite node for this interface */
277       int ret;
278       ret = vnet_feature_enable_disable ("device-input", "srv6-as2-rewrite",
279                                          ls_mem->sw_if_index_in, 0, 0, 0);
280       if (ret != 0)
281         return -1;
282
283       /* Disable promiscuous mode on the interface */
284       vnet_main_t *vnm = vnet_get_main ();
285       vnet_hw_interface_t *hi =
286         vnet_get_sup_hw_interface (vnm, ls_mem->sw_if_index_in);
287       /* Make sure it is main interface */
288       if (hi->sw_if_index == ls_mem->sw_if_index_in)
289         ethernet_set_flags (vnm, hi->hw_if_index, 0);
290
291       /* Remove local SID index from interface table */
292       sm->sw_iface_localsid2[ls_mem->sw_if_index_in] = ~(u32) 0;
293     }
294   else if (ls_mem->inner_type == AS_TYPE_IP4)
295     {
296       /* Disable End.AS4 rewrite node for this interface */
297       int ret;
298       ret = vnet_feature_enable_disable ("ip4-unicast", "srv6-as4-rewrite",
299                                          ls_mem->sw_if_index_in, 0, 0, 0);
300       if (ret != 0)
301         return -1;
302
303       /* Remove local SID index from interface table */
304       sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = ~(u32) 0;
305     }
306   else if (ls_mem->inner_type == AS_TYPE_IP6)
307     {
308       /* Disable End.AS6 rewrite node for this interface */
309       int ret;
310       ret = vnet_feature_enable_disable ("ip6-unicast", "srv6-as6-rewrite",
311                                          ls_mem->sw_if_index_in, 0, 0, 0);
312       if (ret != 0)
313         return -1;
314
315       /* Remove local SID index from interface table */
316       sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = ~(u32) 0;
317     }
318
319
320   /* Unlock (OIF, NHOP) adjacency (from sr_localsid.c:103) */
321   adj_unlock (ls_mem->nh_adj);
322
323   /* Delete SID entry */
324   pool_put (sm->sids, pool_elt_at_index (sm->sids, ls_mem->index));
325
326   /* Clean up local SID memory */
327   free_ls_mem (ls_mem);
328
329   return 0;
330 }
331
332 /**********************************/
333 /* SRv6 LocalSID format functions */
334 /*
335  * Prints nicely the parameters of a localsid
336  * Example: print "Table 5"
337  */
338 u8 *
339 format_srv6_as_localsid (u8 * s, va_list * args)
340 {
341   srv6_as_localsid_t *ls_mem = va_arg (*args, void *);
342
343   vnet_main_t *vnm = vnet_get_main ();
344   srv6_as_main_t *sm = &srv6_as_main;
345
346   if (ls_mem->inner_type == AS_TYPE_IP4)
347     {
348       s =
349         format (s, "Next-hop:\t%U\n\t", format_ip4_address,
350                 &ls_mem->nh_addr.ip4);
351     }
352   else if (ls_mem->inner_type == AS_TYPE_IP6)
353     {
354       s =
355         format (s, "Next-hop:\t%U\n\t", format_ip6_address,
356                 &ls_mem->nh_addr.ip6);
357     }
358
359   s = format (s, "Outgoing iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
360               ls_mem->sw_if_index_out);
361   s = format (s, "\tIncoming iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
362               ls_mem->sw_if_index_in);
363   s = format (s, "\tSource address:\t%U\n", format_ip6_address,
364               &ls_mem->src_addr);
365
366   s = format (s, "\tSegment list:\t< ");
367   ip6_address_t *addr;
368   vec_foreach (addr, ls_mem->sid_list)
369   {
370     s = format (s, "%U, ", format_ip6_address, addr);
371   }
372   s = format (s, "\b\b >\n");
373
374   vlib_counter_t valid, invalid;
375   vlib_get_combined_counter (&(sm->valid_counters), ls_mem->index, &valid);
376   vlib_get_combined_counter (&(sm->invalid_counters), ls_mem->index,
377                              &invalid);
378   s =
379     format (s, "\tGood rewrite traffic: \t[%Ld packets : %Ld bytes]\n",
380             valid.packets, valid.bytes);
381   s =
382     format (s, "\tBad rewrite traffic:  \t[%Ld packets : %Ld bytes]\n",
383             invalid.packets, invalid.bytes);
384
385   return s;
386 }
387
388 /*
389  * Process the parameters of a localsid
390  * Example: process from:
391  * sr localsid address cafe::1 behavior new_srv6_localsid 5
392  * everything from behavior on... so in this case 'new_srv6_localsid 5'
393  * Notice that it MUST match the keyword_str and params_str defined above.
394  */
395 uword
396 unformat_srv6_as_localsid (unformat_input_t * input, va_list * args)
397 {
398   void **plugin_mem_p = va_arg (*args, void **);
399   srv6_as_localsid_t *ls_mem;
400
401   vnet_main_t *vnm = vnet_get_main ();
402
403   u8 inner_type = AS_TYPE_L2;
404   ip46_address_t nh_addr;
405   u32 sw_if_index_out;
406   u32 sw_if_index_in;
407   ip6_address_t src_addr;
408   ip6_address_t next_sid;
409   ip6_address_t *sid_list = NULL;
410
411   u8 params = 0;
412 #define PARAM_AS_NH   (1 << 0)
413 #define PARAM_AS_OIF  (1 << 1)
414 #define PARAM_AS_IIF  (1 << 2)
415 #define PARAM_AS_SRC  (1 << 3)
416
417   if (!unformat (input, "end.as"))
418     return 0;
419
420   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
421     {
422       if (!(params & PARAM_AS_NH) && unformat (input, "nh %U",
423                                                unformat_ip4_address,
424                                                &nh_addr.ip4))
425         {
426           inner_type = AS_TYPE_IP4;
427           params |= PARAM_AS_NH;
428         }
429       if (!(params & PARAM_AS_NH) && unformat (input, "nh %U",
430                                                unformat_ip6_address,
431                                                &nh_addr.ip6))
432         {
433           inner_type = AS_TYPE_IP6;
434           params |= PARAM_AS_NH;
435         }
436       else if (!(params & PARAM_AS_OIF) && unformat (input, "oif %U",
437                                                      unformat_vnet_sw_interface,
438                                                      vnm, &sw_if_index_out))
439         {
440           params |= PARAM_AS_OIF;
441         }
442       else if (!(params & PARAM_AS_IIF) && unformat (input, "iif %U",
443                                                      unformat_vnet_sw_interface,
444                                                      vnm, &sw_if_index_in))
445         {
446           params |= PARAM_AS_IIF;
447         }
448       else if (!(params & PARAM_AS_SRC) && unformat (input, "src %U",
449                                                      unformat_ip6_address,
450                                                      &src_addr))
451         {
452           params |= PARAM_AS_SRC;
453         }
454       else if (unformat (input, "next %U", unformat_ip6_address, &next_sid))
455         {
456           vec_add1 (sid_list, next_sid);
457         }
458       else
459         {
460           break;
461         }
462     }
463
464   /* Make sure that all parameters are supplied */
465   u8 params_chk = (PARAM_AS_OIF | PARAM_AS_IIF | PARAM_AS_SRC);
466   if ((params & params_chk) != params_chk || sid_list == NULL)
467     {
468       vec_free (sid_list);
469       return 0;
470     }
471
472   /* Allocate and initialize memory block for local SID parameters */
473   ls_mem = clib_mem_alloc (sizeof *ls_mem);
474   clib_memset (ls_mem, 0, sizeof *ls_mem);
475   *plugin_mem_p = ls_mem;
476
477   /* Set local SID parameters */
478   ls_mem->inner_type = inner_type;
479   if (inner_type == AS_TYPE_IP4)
480     ls_mem->nh_addr.ip4 = nh_addr.ip4;
481   else if (inner_type == AS_TYPE_IP6)
482     ls_mem->nh_addr.ip6 = nh_addr.ip6;
483   ls_mem->sw_if_index_out = sw_if_index_out;
484   ls_mem->sw_if_index_in = sw_if_index_in;
485   ls_mem->src_addr = src_addr;
486   ls_mem->sid_list = sid_list;
487
488   return 1;
489 }
490
491 /*************************/
492 /* SRv6 LocalSID FIB DPO */
493 static u8 *
494 format_srv6_as_dpo (u8 * s, va_list * args)
495 {
496   index_t index = va_arg (*args, index_t);
497   CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
498
499   return (format (s, "SR: static_proxy_index:[%u]", index));
500 }
501
502 void
503 srv6_as_dpo_lock (dpo_id_t * dpo)
504 {
505 }
506
507 void
508 srv6_as_dpo_unlock (dpo_id_t * dpo)
509 {
510 }
511
512 const static dpo_vft_t srv6_as_vft = {
513   .dv_lock = srv6_as_dpo_lock,
514   .dv_unlock = srv6_as_dpo_unlock,
515   .dv_format = format_srv6_as_dpo,
516 };
517
518 const static char *const srv6_as_ip6_nodes[] = {
519   "srv6-as-localsid",
520   NULL,
521 };
522
523 const static char *const *const srv6_as_nodes[DPO_PROTO_NUM] = {
524   [DPO_PROTO_IP6] = srv6_as_ip6_nodes,
525 };
526
527 /**********************/
528 static clib_error_t *
529 srv6_as_init (vlib_main_t * vm)
530 {
531   srv6_as_main_t *sm = &srv6_as_main;
532   int rv = 0;
533
534   sm->vlib_main = vm;
535   sm->vnet_main = vnet_get_main ();
536
537   /* Create DPO */
538   sm->srv6_as_dpo_type = dpo_register_new_type (&srv6_as_vft, srv6_as_nodes);
539
540   /* Register SRv6 LocalSID */
541   rv = sr_localsid_register_function (vm,
542                                       function_name,
543                                       keyword_str,
544                                       def_str,
545                                       params_str,
546                                       128,
547                                       &sm->srv6_as_dpo_type,
548                                       format_srv6_as_localsid,
549                                       unformat_srv6_as_localsid,
550                                       srv6_as_localsid_creation_fn,
551                                       srv6_as_localsid_removal_fn);
552   if (rv < 0)
553     clib_error_return (0, "SRv6 LocalSID function could not be registered.");
554   else
555     sm->srv6_localsid_behavior_id = rv;
556
557   return 0;
558 }
559
560 VNET_FEATURE_INIT (srv6_as2_rewrite, static) =
561 {
562   .arc_name = "device-input",
563   .node_name = "srv6-as2-rewrite",
564   .runs_before = VNET_FEATURES ("ethernet-input"),
565 };
566
567 VNET_FEATURE_INIT (srv6_as4_rewrite, static) =
568 {
569   .arc_name = "ip4-unicast",
570   .node_name = "srv6-as4-rewrite",
571   .runs_before = 0,
572 };
573
574 VNET_FEATURE_INIT (srv6_as6_rewrite, static) =
575 {
576   .arc_name = "ip6-unicast",
577   .node_name = "srv6-as6-rewrite",
578   .runs_before = 0,
579 };
580
581 VLIB_INIT_FUNCTION (srv6_as_init);
582
583 VLIB_PLUGIN_REGISTER () = {
584   .version = VPP_BUILD_VER,
585   .description = "Static Segment Routing for IPv6 (SRv6) Proxy",
586 };
587
588 /*
589 * fd.io coding-style-patch-verification: ON
590 *
591 * Local Variables:
592 * eval: (c-set-style "gnu")
593 * End:
594 */