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