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