SRv6 dynamic proxy plugin
[vpp.git] / src / plugins / srv6-ad / node.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 #include <vlib/vlib.h>
16 #include <vnet/vnet.h>
17 #include <vppinfra/error.h>
18 #include <srv6-ad/ad.h>
19
20
21 /******************************* Packet tracing *******************************/
22
23 typedef struct
24 {
25   u32 localsid_index;
26 } srv6_ad_localsid_trace_t;
27
28 typedef struct
29 {
30   ip6_address_t src, dst;
31 } srv6_ad_rewrite_trace_t;
32
33 static u8 *
34 format_srv6_ad_localsid_trace (u8 * s, va_list * args)
35 {
36   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
37   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
38   srv6_ad_localsid_trace_t *t = va_arg (*args, srv6_ad_localsid_trace_t *);
39
40   return format (s, "SRv6-AD-localsid: localsid_index %d", t->localsid_index);
41 }
42
43 static u8 *
44 format_srv6_ad_rewrite_trace (u8 * s, va_list * args)
45 {
46   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
47   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
48   srv6_ad_rewrite_trace_t *t = va_arg (*args, srv6_ad_rewrite_trace_t *);
49
50   return format (s, "SRv6-AD-rewrite: src %U dst %U",
51                  format_ip6_address, &t->src, format_ip6_address, &t->dst);
52 }
53
54
55 /***************************** Nodes registration *****************************/
56
57 vlib_node_registration_t srv6_ad4_rewrite_node;
58 vlib_node_registration_t srv6_ad6_rewrite_node;
59
60
61 /****************************** Packet counters *******************************/
62
63 #define foreach_srv6_ad_rewrite_counter \
64 _(PROCESSED, "srv6-ad rewritten packets") \
65 _(NO_RW, "(Error) No header for rewriting.")
66
67 typedef enum
68 {
69 #define _(sym,str) SRV6_AD_REWRITE_COUNTER_##sym,
70   foreach_srv6_ad_rewrite_counter
71 #undef _
72     SRV6_AD_REWRITE_N_COUNTERS,
73 } srv6_ad_rewrite_counters;
74
75 static char *srv6_ad_rewrite_counter_strings[] = {
76 #define _(sym,string) string,
77   foreach_srv6_ad_rewrite_counter
78 #undef _
79 };
80
81
82 /********************************* Next nodes *********************************/
83
84 typedef enum
85 {
86   SRV6_AD_LOCALSID_NEXT_ERROR,
87   SRV6_AD_LOCALSID_NEXT_REWRITE4,
88   SRV6_AD_LOCALSID_NEXT_REWRITE6,
89   SRV6_AD_LOCALSID_N_NEXT,
90 } srv6_ad_localsid_next_t;
91
92 typedef enum
93 {
94   SRV6_AD_REWRITE_NEXT_ERROR,
95   SRV6_AD_REWRITE_NEXT_LOOKUP,
96   SRV6_AD_REWRITE_N_NEXT,
97 } srv6_ad_rewrite_next_t;
98
99
100 /******************************* Local SID node *******************************/
101
102 /**
103  * @brief Function doing SRH processing for AD behavior
104  */
105 static_always_inline void
106 end_ad_processing (vlib_buffer_t * b0,
107                    ip6_header_t * ip0,
108                    ip6_sr_header_t * sr0,
109                    ip6_sr_localsid_t * ls0, u32 * next0)
110 {
111   ip6_address_t *new_dst0;
112   u16 total_size;
113   ip6_ext_header_t *next_ext_header;
114   u8 next_hdr;
115   srv6_ad_localsid_t *ls0_mem;
116
117   if (PREDICT_FALSE (ip0->protocol != IP_PROTOCOL_IPV6_ROUTE ||
118                      sr0->type != ROUTING_HEADER_TYPE_SR))
119     {
120       return;
121     }
122
123   if (PREDICT_FALSE (sr0->segments_left == 0))
124     {
125       return;
126     }
127
128   /* Decrement Segments Left and update Destination Address */
129   sr0->segments_left -= 1;
130   new_dst0 = (ip6_address_t *) (sr0->segments) + sr0->segments_left;
131   ip0->dst_address.as_u64[0] = new_dst0->as_u64[0];
132   ip0->dst_address.as_u64[1] = new_dst0->as_u64[1];
133
134   /* Compute the total size of the IPv6 header and extensions */
135   total_size = sizeof (ip6_header_t);
136   next_ext_header = (ip6_ext_header_t *) (ip0 + 1);
137   next_hdr = ip0->protocol;
138
139   while (ip6_ext_hdr (next_hdr))
140     {
141       total_size += ip6_ext_header_len (next_ext_header);
142       next_hdr = next_ext_header->next_hdr;
143       next_ext_header = ip6_ext_next_header (next_ext_header);
144     }
145
146   /* Make sure next header is IP */
147   if (PREDICT_FALSE
148       (next_hdr != IP_PROTOCOL_IPV6 && next_hdr != IP_PROTOCOL_IP_IN_IP))
149     {
150       return;
151     }
152
153   /* Retrieve SID memory */
154   ls0_mem = ls0->plugin_mem;
155
156   /* Cache IP header and extensions */
157   vec_validate (ls0_mem->rewrite, total_size - 1);
158   clib_memcpy (ls0_mem->rewrite, ip0, total_size);
159
160   /* Remove IP header and extensions */
161   vlib_buffer_advance (b0, total_size);
162
163   /* Set Xconnect adjacency to VNF */
164   vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ls0_mem->nh_adj;
165
166   if (ls0_mem->ip_version == DA_IP4)
167     *next0 = SRV6_AD_LOCALSID_NEXT_REWRITE4;
168   else if (ls0_mem->ip_version == DA_IP6)
169     *next0 = SRV6_AD_LOCALSID_NEXT_REWRITE6;
170 }
171
172 /**
173  * @brief SRv6 AD Localsid graph node
174  */
175 static uword
176 srv6_ad_localsid_fn (vlib_main_t * vm,
177                      vlib_node_runtime_t * node, vlib_frame_t * frame)
178 {
179   ip6_sr_main_t *sm = &sr_main;
180   u32 n_left_from, next_index, *from, *to_next;
181   u32 cnt_packets = 0;
182
183   from = vlib_frame_vector_args (frame);
184   n_left_from = frame->n_vectors;
185   next_index = node->cached_next_index;
186
187   u32 thread_index = vlib_get_thread_index ();
188
189   while (n_left_from > 0)
190     {
191       u32 n_left_to_next;
192
193       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
194
195       /* TODO: Dual/quad loop */
196
197       while (n_left_from > 0 && n_left_to_next > 0)
198         {
199           u32 bi0;
200           vlib_buffer_t *b0;
201           ip6_header_t *ip0 = 0;
202           ip6_sr_header_t *sr0;
203           ip6_sr_localsid_t *ls0;
204           u32 next0 = SRV6_AD_LOCALSID_NEXT_ERROR;
205
206           bi0 = from[0];
207           to_next[0] = bi0;
208           from += 1;
209           to_next += 1;
210           n_left_from -= 1;
211           n_left_to_next -= 1;
212
213           b0 = vlib_get_buffer (vm, bi0);
214           ip0 = vlib_buffer_get_current (b0);
215           sr0 = (ip6_sr_header_t *) (ip0 + 1);
216
217           /* Lookup the SR End behavior based on IP DA (adj) */
218           ls0 = pool_elt_at_index (sm->localsids,
219                                    vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
220
221           /* SRH processing */
222           end_ad_processing (b0, ip0, sr0, ls0, &next0);
223
224           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
225             {
226               srv6_ad_localsid_trace_t *tr =
227                 vlib_add_trace (vm, node, b0, sizeof *tr);
228               tr->localsid_index = ls0 - sm->localsids;
229             }
230
231           /* This increments the SRv6 per LocalSID counters. */
232           vlib_increment_combined_counter (((next0 ==
233                                              SRV6_AD_LOCALSID_NEXT_ERROR) ?
234                                             &(sm->sr_ls_invalid_counters) :
235                                             &(sm->sr_ls_valid_counters)),
236                                            thread_index, ls0 - sm->localsids,
237                                            1, vlib_buffer_length_in_chain (vm,
238                                                                            b0));
239
240           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
241                                            n_left_to_next, bi0, next0);
242
243           cnt_packets++;
244         }
245
246       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
247     }
248
249   return frame->n_vectors;
250 }
251
252 /* *INDENT-OFF* */
253 VLIB_REGISTER_NODE (srv6_ad_localsid_node) = {
254   .function = srv6_ad_localsid_fn,
255   .name = "srv6-ad-localsid",
256   .vector_size = sizeof (u32),
257   .format_trace = format_srv6_ad_localsid_trace,
258   .type = VLIB_NODE_TYPE_INTERNAL,
259   .n_next_nodes = SRV6_AD_LOCALSID_N_NEXT,
260   .next_nodes = {
261     [SRV6_AD_LOCALSID_NEXT_REWRITE4] = "ip4-rewrite",
262     [SRV6_AD_LOCALSID_NEXT_REWRITE6] = "ip6-rewrite",
263     [SRV6_AD_LOCALSID_NEXT_ERROR] = "error-drop",
264   },
265 };
266 /* *INDENT-ON* */
267
268
269 /******************************* Rewriting node *******************************/
270
271 /**
272  * @brief Graph node for applying a SR policy into an IPv6 packet. Encapsulation
273  */
274 static uword
275 srv6_ad4_rewrite_fn (vlib_main_t * vm,
276                      vlib_node_runtime_t * node, vlib_frame_t * frame)
277 {
278   ip6_sr_main_t *srm = &sr_main;
279   srv6_ad_main_t *sm = &srv6_ad_main;
280   u32 n_left_from, next_index, *from, *to_next;
281   u32 cnt_packets = 0;
282
283   from = vlib_frame_vector_args (frame);
284   n_left_from = frame->n_vectors;
285   next_index = node->cached_next_index;
286
287   while (n_left_from > 0)
288     {
289       u32 n_left_to_next;
290
291       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
292
293       /* TODO: Dual/quad loop */
294
295       while (n_left_from > 0 && n_left_to_next > 0)
296         {
297           u32 bi0;
298           vlib_buffer_t *b0;
299           ip4_header_t *ip0_encap = 0;
300           ip6_header_t *ip0 = 0;
301           ip6_sr_localsid_t *ls0;
302           srv6_ad_localsid_t *ls0_mem;
303           u32 next0 = SRV6_AD_REWRITE_NEXT_LOOKUP;
304           u16 new_l0 = 0;
305
306           bi0 = from[0];
307           to_next[0] = bi0;
308           from += 1;
309           to_next += 1;
310           n_left_from -= 1;
311           n_left_to_next -= 1;
312
313           b0 = vlib_get_buffer (vm, bi0);
314           ip0_encap = vlib_buffer_get_current (b0);
315           ls0 = pool_elt_at_index (srm->localsids,
316                                    sm->sw_iface_localsid4[vnet_buffer
317                                                           (b0)->sw_if_index
318                                                           [VLIB_RX]]);
319           ls0_mem = ls0->plugin_mem;
320
321           if (PREDICT_FALSE (ls0_mem == NULL || ls0_mem->rewrite == NULL))
322             {
323               next0 = SRV6_AD_REWRITE_NEXT_ERROR;
324               b0->error = node->errors[SRV6_AD_REWRITE_COUNTER_NO_RW];
325             }
326           else
327             {
328               ASSERT (VLIB_BUFFER_PRE_DATA_SIZE >=
329                       (vec_len (ls0_mem->rewrite) + b0->current_data));
330
331               clib_memcpy (((u8 *) ip0_encap) - vec_len (ls0_mem->rewrite),
332                            ls0_mem->rewrite, vec_len (ls0_mem->rewrite));
333               vlib_buffer_advance (b0, -(word) vec_len (ls0_mem->rewrite));
334
335               ip0 = vlib_buffer_get_current (b0);
336
337               /* Update inner IPv4 TTL and checksum */
338               u32 checksum0;
339               ip0_encap->ttl -= 1;
340               checksum0 = ip0_encap->checksum + clib_host_to_net_u16 (0x0100);
341               checksum0 += checksum0 >= 0xffff;
342               ip0_encap->checksum = checksum0;
343
344               /* Update outer IPv6 length (in case it has changed) */
345               new_l0 = vec_len (ls0_mem->rewrite) - sizeof (ip6_header_t) +
346                 clib_net_to_host_u16 (ip0_encap->length);
347               ip0->payload_length = clib_host_to_net_u16 (new_l0);
348             }
349
350           if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
351               PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
352             {
353               srv6_ad_rewrite_trace_t *tr =
354                 vlib_add_trace (vm, node, b0, sizeof *tr);
355               clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
356                            sizeof tr->src.as_u8);
357               clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
358                            sizeof tr->dst.as_u8);
359             }
360
361           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
362                                            n_left_to_next, bi0, next0);
363
364           cnt_packets++;
365         }
366
367       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
368     }
369
370   /* Update counters */
371   vlib_node_increment_counter (vm, srv6_ad4_rewrite_node.index,
372                                SRV6_AD_REWRITE_COUNTER_PROCESSED,
373                                cnt_packets);
374
375   return frame->n_vectors;
376 }
377
378 /* *INDENT-OFF* */
379 VLIB_REGISTER_NODE (srv6_ad4_rewrite_node) = {
380   .function = srv6_ad4_rewrite_fn,
381   .name = "srv6-ad4-rewrite",
382   .vector_size = sizeof (u32),
383   .format_trace = format_srv6_ad_rewrite_trace,
384   .type = VLIB_NODE_TYPE_INTERNAL,
385   .n_errors = SRV6_AD_REWRITE_N_COUNTERS,
386   .error_strings = srv6_ad_rewrite_counter_strings,
387   .n_next_nodes = SRV6_AD_REWRITE_N_NEXT,
388   .next_nodes = {
389       [SRV6_AD_REWRITE_NEXT_LOOKUP] = "ip6-lookup",
390       [SRV6_AD_REWRITE_NEXT_ERROR] = "error-drop",
391   },
392 };
393 /* *INDENT-ON* */
394
395
396 /**
397  * @brief Graph node for applying a SR policy into an IPv6 packet. Encapsulation
398  */
399 static uword
400 srv6_ad6_rewrite_fn (vlib_main_t * vm,
401                      vlib_node_runtime_t * node, vlib_frame_t * frame)
402 {
403   ip6_sr_main_t *srm = &sr_main;
404   srv6_ad_main_t *sm = &srv6_ad_main;
405   u32 n_left_from, next_index, *from, *to_next;
406   u32 cnt_packets = 0;
407
408   from = vlib_frame_vector_args (frame);
409   n_left_from = frame->n_vectors;
410   next_index = node->cached_next_index;
411
412   while (n_left_from > 0)
413     {
414       u32 n_left_to_next;
415
416       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
417
418       /* TODO: Dual/quad loop */
419
420       while (n_left_from > 0 && n_left_to_next > 0)
421         {
422           u32 bi0;
423           vlib_buffer_t *b0;
424           ip6_header_t *ip0 = 0, *ip0_encap = 0;
425           ip6_sr_localsid_t *ls0;
426           srv6_ad_localsid_t *ls0_mem;
427           u32 next0 = SRV6_AD_REWRITE_NEXT_LOOKUP;
428           u16 new_l0 = 0;
429
430           bi0 = from[0];
431           to_next[0] = bi0;
432           from += 1;
433           to_next += 1;
434           n_left_from -= 1;
435           n_left_to_next -= 1;
436
437           b0 = vlib_get_buffer (vm, bi0);
438           ip0_encap = vlib_buffer_get_current (b0);
439           ls0 = pool_elt_at_index (srm->localsids,
440                                    sm->sw_iface_localsid6[vnet_buffer
441                                                           (b0)->sw_if_index
442                                                           [VLIB_RX]]);
443           ls0_mem = ls0->plugin_mem;
444
445           if (PREDICT_FALSE (ls0_mem == NULL || ls0_mem->rewrite == NULL))
446             {
447               next0 = SRV6_AD_REWRITE_NEXT_ERROR;
448               b0->error = node->errors[SRV6_AD_REWRITE_COUNTER_NO_RW];
449             }
450           else
451             {
452               ASSERT (VLIB_BUFFER_PRE_DATA_SIZE >=
453                       (vec_len (ls0_mem->rewrite) + b0->current_data));
454
455               clib_memcpy (((u8 *) ip0_encap) - vec_len (ls0_mem->rewrite),
456                            ls0_mem->rewrite, vec_len (ls0_mem->rewrite));
457               vlib_buffer_advance (b0, -(word) vec_len (ls0_mem->rewrite));
458
459               ip0 = vlib_buffer_get_current (b0);
460
461               /* Update inner IPv6 hop limit */
462               ip0_encap->hop_limit -= 1;
463
464               /* Update outer IPv6 length (in case it has changed) */
465               new_l0 = vec_len (ls0_mem->rewrite) +
466                 clib_net_to_host_u16 (ip0_encap->payload_length);
467               ip0->payload_length = clib_host_to_net_u16 (new_l0);
468             }
469
470           if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
471               PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
472             {
473               srv6_ad_rewrite_trace_t *tr =
474                 vlib_add_trace (vm, node, b0, sizeof *tr);
475               clib_memcpy (tr->src.as_u8, ip0->src_address.as_u8,
476                            sizeof tr->src.as_u8);
477               clib_memcpy (tr->dst.as_u8, ip0->dst_address.as_u8,
478                            sizeof tr->dst.as_u8);
479             }
480
481           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
482                                            n_left_to_next, bi0, next0);
483
484           cnt_packets++;
485         }
486
487       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
488     }
489
490   /* Update counters */
491   vlib_node_increment_counter (vm, srv6_ad6_rewrite_node.index,
492                                SRV6_AD_REWRITE_COUNTER_PROCESSED,
493                                cnt_packets);
494
495   return frame->n_vectors;
496 }
497
498 /* *INDENT-OFF* */
499 VLIB_REGISTER_NODE (srv6_ad6_rewrite_node) = {
500   .function = srv6_ad6_rewrite_fn,
501   .name = "srv6-ad6-rewrite",
502   .vector_size = sizeof (u32),
503   .format_trace = format_srv6_ad_rewrite_trace,
504   .type = VLIB_NODE_TYPE_INTERNAL,
505   .n_errors = SRV6_AD_REWRITE_N_COUNTERS,
506   .error_strings = srv6_ad_rewrite_counter_strings,
507   .n_next_nodes = SRV6_AD_REWRITE_N_NEXT,
508   .next_nodes = {
509       [SRV6_AD_REWRITE_NEXT_LOOKUP] = "ip6-lookup",
510       [SRV6_AD_REWRITE_NEXT_ERROR] = "error-drop",
511   },
512 };
513 /* *INDENT-ON* */
514
515 /*
516 * fd.io coding-style-patch-verification: ON
517 *
518 * Local Variables:
519 * eval: (c-set-style "gnu")
520 * End:
521 */