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