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