lisp: Move to plugin
[vpp.git] / src / plugins / lisp / lisp-gpe / decap.c
1 /*
2  * Copyright (c) 2016 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  * @file
17  * @brief L2 LISP-GPE decap code.
18  *
19  */
20 #include <vlib/vlib.h>
21 #include <vnet/pg/pg.h>
22 #include <lisp/lisp-gpe/lisp_gpe.h>
23
24 typedef struct
25 {
26   u32 next_index;
27   u32 tunnel_index;
28   u32 error;
29   lisp_gpe_header_t h;
30 } lisp_gpe_rx_trace_t;
31
32 static u8 *
33 format_lisp_gpe_rx_trace (u8 * s, va_list * args)
34 {
35   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
36   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
37   lisp_gpe_rx_trace_t *t = va_arg (*args, lisp_gpe_rx_trace_t *);
38
39   if (t->tunnel_index != ~0)
40     {
41       s = format (s, "LISP-GPE: tunnel %d next %d error %d", t->tunnel_index,
42                   t->next_index, t->error);
43     }
44   else
45     {
46       s = format (s, "LISP-GPE: no tunnel next %d error %d\n", t->next_index,
47                   t->error);
48     }
49   s = format (s, "\n  %U", format_lisp_gpe_header_with_length, &t->h,
50               (u32) sizeof (t->h) /* max size */ );
51   return s;
52 }
53
54 static u32 next_proto_to_next_index[LISP_GPE_NEXT_PROTOS] = {
55   LISP_GPE_INPUT_NEXT_DROP,
56   LISP_GPE_INPUT_NEXT_IP4_INPUT,
57   LISP_GPE_INPUT_NEXT_IP6_INPUT,
58   LISP_GPE_INPUT_NEXT_L2_INPUT,
59   LISP_GPE_INPUT_NEXT_DROP
60 };
61
62 always_inline u32
63 next_protocol_to_next_index (lisp_gpe_header_t * lgh, u8 * next_header)
64 {
65   lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main ();
66
67   /* lisp-gpe router */
68   if (PREDICT_TRUE ((lgh->flags & LISP_GPE_FLAGS_P)
69                     || GPE_ENCAP_VXLAN == lgm->encap_mode))
70     {
71       if (PREDICT_FALSE (lgh->next_protocol >= LISP_GPE_NEXT_PROTOS))
72         return LISP_GPE_INPUT_NEXT_DROP;
73
74       return next_proto_to_next_index[lgh->next_protocol];
75     }
76   /* legacy lisp router */
77   else if ((lgh->flags & LISP_GPE_FLAGS_P) == 0)
78     {
79       ip4_header_t *iph = (ip4_header_t *) next_header;
80       if ((iph->ip_version_and_header_length & 0xF0) == 0x40)
81         return LISP_GPE_INPUT_NEXT_IP4_INPUT;
82       else if ((iph->ip_version_and_header_length & 0xF0) == 0x60)
83         return LISP_GPE_INPUT_NEXT_IP6_INPUT;
84       else
85         return LISP_GPE_INPUT_NEXT_DROP;
86     }
87   else
88     return LISP_GPE_INPUT_NEXT_DROP;
89 }
90
91 always_inline tunnel_lookup_t *
92 next_index_to_iface (lisp_gpe_main_t * lgm, u32 next_index)
93 {
94   if (LISP_GPE_INPUT_NEXT_IP4_INPUT == next_index
95       || LISP_GPE_INPUT_NEXT_IP6_INPUT == next_index)
96     return &lgm->l3_ifaces;
97   else if (LISP_GPE_INPUT_NEXT_L2_INPUT == next_index)
98     return &lgm->l2_ifaces;
99   else if (LISP_GPE_INPUT_NEXT_NSH_INPUT == next_index)
100     return &lgm->nsh_ifaces;
101   clib_warning ("next_index not associated to an interface!");
102   return 0;
103 }
104
105 static_always_inline void
106 incr_decap_stats (vnet_main_t * vnm, u32 thread_index, u32 length,
107                   u32 sw_if_index, u32 * last_sw_if_index, u32 * n_packets,
108                   u32 * n_bytes)
109 {
110   vnet_interface_main_t *im;
111
112   if (PREDICT_TRUE (sw_if_index == *last_sw_if_index))
113     {
114       *n_packets += 1;
115       *n_bytes += length;
116     }
117   else
118     {
119       if (PREDICT_TRUE (*last_sw_if_index != ~0))
120         {
121           im = &vnm->interface_main;
122
123           vlib_increment_combined_counter (im->combined_sw_if_counters +
124                                            VNET_INTERFACE_COUNTER_RX,
125                                            thread_index, *last_sw_if_index,
126                                            *n_packets, *n_bytes);
127         }
128       *last_sw_if_index = sw_if_index;
129       *n_packets = 1;
130       *n_bytes = length;
131     }
132 }
133
134 /**
135  * @brief LISP-GPE decap dispatcher.
136  * @node lisp_gpe_input_inline
137  *
138  * LISP-GPE decap dispatcher.
139  *
140  * Decaps IP-UDP-LISP-GPE header and based on the next protocol and in the
141  * GPE header and the vni decides the next node to forward the packet to.
142  *
143  * @param[in]   vm      vlib_main_t corresponding to current thread.
144  * @param[in]   node    vlib_node_runtime_t data for this node.
145  * @param[in]   frame   vlib_frame_t whose contents should be dispatched.
146  *
147  * @return number of vectors in frame.
148  */
149 static uword
150 lisp_gpe_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
151                        vlib_frame_t * from_frame, u8 is_v4)
152 {
153   u32 n_left_from, next_index, *from, *to_next, thread_index;
154   u32 n_bytes = 0, n_packets = 0, last_sw_if_index = ~0, drops = 0;
155   lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main ();
156
157   thread_index = vm->thread_index;
158   from = vlib_frame_vector_args (from_frame);
159   n_left_from = from_frame->n_vectors;
160
161   next_index = node->cached_next_index;
162
163   while (n_left_from > 0)
164     {
165       u32 n_left_to_next;
166
167       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
168
169       while (n_left_from >= 4 && n_left_to_next >= 2)
170         {
171           u32 bi0, bi1;
172           vlib_buffer_t *b0, *b1;
173           ip4_udp_lisp_gpe_header_t *iul4_0, *iul4_1;
174           ip6_udp_lisp_gpe_header_t *iul6_0, *iul6_1;
175           lisp_gpe_header_t *lh0, *lh1;
176           u32 next0, next1, error0, error1;
177           uword *si0, *si1;
178           tunnel_lookup_t *tl0, *tl1;
179
180           /* Prefetch next iteration. */
181           {
182             vlib_buffer_t *p2, *p3;
183
184             p2 = vlib_get_buffer (vm, from[2]);
185             p3 = vlib_get_buffer (vm, from[3]);
186
187             vlib_prefetch_buffer_header (p2, LOAD);
188             vlib_prefetch_buffer_header (p3, LOAD);
189
190             CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
191             CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
192           }
193
194           bi0 = from[0];
195           bi1 = from[1];
196           to_next[0] = bi0;
197           to_next[1] = bi1;
198           from += 2;
199           to_next += 2;
200           n_left_to_next -= 2;
201           n_left_from -= 2;
202
203           b0 = vlib_get_buffer (vm, bi0);
204           b1 = vlib_get_buffer (vm, bi1);
205
206           /* udp leaves current_data pointing at the lisp header */
207           if (is_v4)
208             {
209               vlib_buffer_advance (b0,
210                                    -(word) (sizeof (udp_header_t) +
211                                             sizeof (ip4_header_t)));
212               vlib_buffer_advance (b1,
213                                    -(word) (sizeof (udp_header_t) +
214                                             sizeof (ip4_header_t)));
215
216               iul4_0 = vlib_buffer_get_current (b0);
217               iul4_1 = vlib_buffer_get_current (b1);
218
219               /* pop (ip, udp, lisp-gpe) */
220               vlib_buffer_advance (b0, sizeof (*iul4_0));
221               vlib_buffer_advance (b1, sizeof (*iul4_1));
222
223               lh0 = &iul4_0->lisp;
224               lh1 = &iul4_1->lisp;
225             }
226           else
227             {
228               vlib_buffer_advance (b0,
229                                    -(word) (sizeof (udp_header_t) +
230                                             sizeof (ip6_header_t)));
231               vlib_buffer_advance (b1,
232                                    -(word) (sizeof (udp_header_t) +
233                                             sizeof (ip6_header_t)));
234
235               iul6_0 = vlib_buffer_get_current (b0);
236               iul6_1 = vlib_buffer_get_current (b1);
237
238               /* pop (ip, udp, lisp-gpe) */
239               vlib_buffer_advance (b0, sizeof (*iul6_0));
240               vlib_buffer_advance (b1, sizeof (*iul6_1));
241
242               lh0 = &iul6_0->lisp;
243               lh1 = &iul6_1->lisp;
244             }
245
246           /* determine next_index from lisp-gpe header */
247           next0 = next_protocol_to_next_index (lh0,
248                                                vlib_buffer_get_current (b0));
249           next1 = next_protocol_to_next_index (lh1,
250                                                vlib_buffer_get_current (b1));
251
252           /* determine if tunnel is l2 or l3 */
253           tl0 = next_index_to_iface (lgm, next0);
254           tl1 = next_index_to_iface (lgm, next1);
255
256           /* map iid/vni to lisp-gpe sw_if_index which is used by ipx_input to
257            * decide the rx vrf and the input features to be applied */
258           si0 = hash_get (tl0->sw_if_index_by_vni,
259                           clib_net_to_host_u32 (lh0->iid << 8));
260           si1 = hash_get (tl1->sw_if_index_by_vni,
261                           clib_net_to_host_u32 (lh1->iid << 8));
262
263
264           /* Required to make the l2 tag push / pop code work on l2 subifs */
265           vnet_update_l2_len (b0);
266           vnet_update_l2_len (b1);
267
268           if (si0)
269             {
270               incr_decap_stats (lgm->vnet_main, thread_index,
271                                 vlib_buffer_length_in_chain (vm, b0), si0[0],
272                                 &last_sw_if_index, &n_packets, &n_bytes);
273               vnet_buffer (b0)->sw_if_index[VLIB_RX] = si0[0];
274               error0 = 0;
275             }
276           else
277             {
278               next0 = LISP_GPE_INPUT_NEXT_DROP;
279               error0 = LISP_GPE_ERROR_NO_TUNNEL;
280               drops++;
281             }
282
283           if (si1)
284             {
285               incr_decap_stats (lgm->vnet_main, thread_index,
286                                 vlib_buffer_length_in_chain (vm, b1), si1[0],
287                                 &last_sw_if_index, &n_packets, &n_bytes);
288               vnet_buffer (b1)->sw_if_index[VLIB_RX] = si1[0];
289               error1 = 0;
290             }
291           else
292             {
293               next1 = LISP_GPE_INPUT_NEXT_DROP;
294               error1 = LISP_GPE_ERROR_NO_TUNNEL;
295               drops++;
296             }
297
298           b0->error = error0 ? node->errors[error0] : 0;
299           b1->error = error1 ? node->errors[error1] : 0;
300
301           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
302             {
303               lisp_gpe_rx_trace_t *tr = vlib_add_trace (vm, node, b0,
304                                                         sizeof (*tr));
305               tr->next_index = next0;
306               tr->error = error0;
307               tr->h = lh0[0];
308             }
309
310           if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
311             {
312               lisp_gpe_rx_trace_t *tr = vlib_add_trace (vm, node, b1,
313                                                         sizeof (*tr));
314               tr->next_index = next1;
315               tr->error = error1;
316               tr->h = lh1[0];
317             }
318
319           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
320                                            n_left_to_next, bi0, bi1, next0,
321                                            next1);
322         }
323
324       while (n_left_from > 0 && n_left_to_next > 0)
325         {
326           u32 bi0;
327           vlib_buffer_t *b0;
328           u32 next0;
329           ip4_udp_lisp_gpe_header_t *iul4_0;
330           ip6_udp_lisp_gpe_header_t *iul6_0;
331           lisp_gpe_header_t *lh0;
332           u32 error0;
333           uword *si0;
334           tunnel_lookup_t *tl0;
335
336           bi0 = from[0];
337           to_next[0] = bi0;
338           from += 1;
339           to_next += 1;
340           n_left_from -= 1;
341           n_left_to_next -= 1;
342
343           b0 = vlib_get_buffer (vm, bi0);
344
345           /* udp leaves current_data pointing at the lisp header
346            * TODO: there's no difference in processing between v4 and v6
347            * encapsulated packets so the code should be simplified if ip header
348            * info is not going to be used for dp smrs/dpsec */
349           if (is_v4)
350             {
351               vlib_buffer_advance (b0,
352                                    -(word) (sizeof (udp_header_t) +
353                                             sizeof (ip4_header_t)));
354
355               iul4_0 = vlib_buffer_get_current (b0);
356
357               /* pop (ip, udp, lisp-gpe) */
358               vlib_buffer_advance (b0, sizeof (*iul4_0));
359
360               lh0 = &iul4_0->lisp;
361             }
362           else
363             {
364               vlib_buffer_advance (b0,
365                                    -(word) (sizeof (udp_header_t) +
366                                             sizeof (ip6_header_t)));
367
368               iul6_0 = vlib_buffer_get_current (b0);
369
370               /* pop (ip, udp, lisp-gpe) */
371               vlib_buffer_advance (b0, sizeof (*iul6_0));
372
373               lh0 = &iul6_0->lisp;
374             }
375
376           /* TODO if security is to be implemented, something similar to RPF,
377            * probably we'd like to check that the peer is allowed to send us
378            * packets. For this, we should use the tunnel table OR check that
379            * we have a mapping for the source eid and that the outer source of
380            * the packet is one of its locators */
381
382           /* determine next_index from lisp-gpe header */
383           next0 = next_protocol_to_next_index (lh0,
384                                                vlib_buffer_get_current (b0));
385
386           /* determine if tunnel is l2 or l3 */
387           tl0 = next_index_to_iface (lgm, next0);
388
389           /* map iid/vni to lisp-gpe sw_if_index which is used by ipx_input to
390            * decide the rx vrf and the input features to be applied.
391            * NOTE: vni uses only the first 24 bits */
392           si0 = hash_get (tl0->sw_if_index_by_vni,
393                           clib_net_to_host_u32 (lh0->iid << 8));
394
395           /* Required to make the l2 tag push / pop code work on l2 subifs */
396           vnet_update_l2_len (b0);
397
398           if (si0)
399             {
400               incr_decap_stats (lgm->vnet_main, thread_index,
401                                 vlib_buffer_length_in_chain (vm, b0), si0[0],
402                                 &last_sw_if_index, &n_packets, &n_bytes);
403               vnet_buffer (b0)->sw_if_index[VLIB_RX] = si0[0];
404               error0 = 0;
405             }
406           else
407             {
408               next0 = LISP_GPE_INPUT_NEXT_DROP;
409               error0 = LISP_GPE_ERROR_NO_TUNNEL;
410               drops++;
411             }
412
413           /* TODO error handling if security is implemented */
414           b0->error = error0 ? node->errors[error0] : 0;
415
416           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
417             {
418               lisp_gpe_rx_trace_t *tr = vlib_add_trace (vm, node, b0,
419                                                         sizeof (*tr));
420               tr->next_index = next0;
421               tr->error = error0;
422               tr->h = lh0[0];
423             }
424
425           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
426                                            n_left_to_next, bi0, next0);
427         }
428
429       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
430     }
431
432   /* flush iface stats */
433   incr_decap_stats (lgm->vnet_main, thread_index, 0, ~0, &last_sw_if_index,
434                     &n_packets, &n_bytes);
435   vlib_node_increment_counter (vm, lisp_gpe_ip4_input_node.index,
436                                LISP_GPE_ERROR_NO_TUNNEL, drops);
437   return from_frame->n_vectors;
438 }
439
440 static uword
441 lisp_gpe_ip4_input (vlib_main_t * vm, vlib_node_runtime_t * node,
442                     vlib_frame_t * from_frame)
443 {
444   return lisp_gpe_input_inline (vm, node, from_frame, 1);
445 }
446
447 static uword
448 lisp_gpe_ip6_input (vlib_main_t * vm, vlib_node_runtime_t * node,
449                     vlib_frame_t * from_frame)
450 {
451   return lisp_gpe_input_inline (vm, node, from_frame, 0);
452 }
453
454 static char *lisp_gpe_ip4_input_error_strings[] = {
455 #define lisp_gpe_error(n,s) s,
456 #include <lisp/lisp-gpe/lisp_gpe_error.def>
457 #undef lisp_gpe_error
458 };
459
460 /* *INDENT-OFF* */
461 VLIB_REGISTER_NODE (lisp_gpe_ip4_input_node) = {
462   .function = lisp_gpe_ip4_input,
463   .name = "lisp-gpe-ip4-input",
464   /* Takes a vector of packets. */
465   .vector_size = sizeof (u32),
466   .n_next_nodes = LISP_GPE_INPUT_N_NEXT,
467   .next_nodes = {
468 #define _(s,n) [LISP_GPE_INPUT_NEXT_##s] = n,
469     foreach_lisp_gpe_ip_input_next
470 #undef _
471   },
472
473   .n_errors = ARRAY_LEN (lisp_gpe_ip4_input_error_strings),
474   .error_strings = lisp_gpe_ip4_input_error_strings,
475
476   .format_buffer = format_lisp_gpe_header_with_length,
477   .format_trace = format_lisp_gpe_rx_trace,
478   // $$$$ .unformat_buffer = unformat_lisp_gpe_header,
479 };
480 /* *INDENT-ON* */
481
482 /* *INDENT-OFF* */
483 VLIB_REGISTER_NODE (lisp_gpe_ip6_input_node) = {
484   .function = lisp_gpe_ip6_input,
485   .name = "lisp-gpe-ip6-input",
486   /* Takes a vector of packets. */
487   .vector_size = sizeof (u32),
488   .n_next_nodes = LISP_GPE_INPUT_N_NEXT,
489   .next_nodes = {
490 #define _(s,n) [LISP_GPE_INPUT_NEXT_##s] = n,
491     foreach_lisp_gpe_ip_input_next
492 #undef _
493   },
494
495   .n_errors = ARRAY_LEN (lisp_gpe_ip4_input_error_strings),
496   .error_strings = lisp_gpe_ip4_input_error_strings,
497
498   .format_buffer = format_lisp_gpe_header_with_length,
499   .format_trace = format_lisp_gpe_rx_trace,
500   // $$$$ .unformat_buffer = unformat_lisp_gpe_header,
501 };
502 /* *INDENT-ON* */
503
504 /**
505  * Adds arc from lisp-gpe-input to nsh-input if nsh-input is available
506  */
507 static void
508 gpe_add_arc_from_input_to_nsh ()
509 {
510   lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main ();
511   vlib_main_t *vm = lgm->vlib_main;
512   vlib_node_t *nsh_input;
513
514   /* Arc already exists */
515   if (next_proto_to_next_index[LISP_GPE_NEXT_PROTO_NSH]
516       != LISP_GPE_INPUT_NEXT_DROP)
517     return;
518
519   /* Check if nsh-input is available */
520   if ((nsh_input = vlib_get_node_by_name (vm, (u8 *) "nsh-input")))
521     {
522       u32 slot4, slot6;
523       slot4 = vlib_node_add_next_with_slot (vm, lisp_gpe_ip4_input_node.index,
524                                             nsh_input->index,
525                                             LISP_GPE_NEXT_PROTO_NSH);
526       slot6 = vlib_node_add_next_with_slot (vm, lisp_gpe_ip6_input_node.index,
527                                             nsh_input->index,
528                                             LISP_GPE_NEXT_PROTO_NSH);
529       ASSERT (slot4 == slot6 && slot4 == LISP_GPE_INPUT_NEXT_NSH_INPUT);
530
531       next_proto_to_next_index[LISP_GPE_NEXT_PROTO_NSH] = slot4;
532     }
533 }
534
535 /** GPE decap init function. */
536 clib_error_t *
537 gpe_decap_init (vlib_main_t * vm)
538 {
539   clib_error_t *error = 0;
540
541   if ((error = vlib_call_init_function (vm, lisp_gpe_init)))
542     return error;
543
544   gpe_add_arc_from_input_to_nsh ();
545   return 0;
546 }
547
548 static uword
549 lisp_gpe_nsh_placeholder_input (vlib_main_t * vm, vlib_node_runtime_t * node,
550                                 vlib_frame_t * from_frame)
551 {
552   vlib_node_increment_counter (vm, node->node_index, 0, 1);
553   return from_frame->n_vectors;
554 }
555
556 static char *lisp_gpe_nsh_placeholder_error_strings[] = {
557   "lisp gpe placeholder nsh decap",
558 };
559
560 /* *INDENT-OFF* */
561 VLIB_REGISTER_NODE (lisp_gpe_nsh_placeholder_input_node) = {
562   .function = lisp_gpe_nsh_placeholder_input,
563   .name = "lisp-gpe-nsh-placeholder-input",
564   .vector_size = sizeof (u32),
565   .type = VLIB_NODE_TYPE_INTERNAL,
566   .n_next_nodes = 1,
567
568   .n_errors = 1,
569   .error_strings = lisp_gpe_nsh_placeholder_error_strings,
570
571   .next_nodes = {
572       [0] = "error-drop",
573   },
574 };
575 /* *INDENT-ON* */
576
577 static clib_error_t *
578 lisp_add_placeholder_nsh_node_command_fn (vlib_main_t * vm,
579                                           unformat_input_t * input,
580                                           vlib_cli_command_t * cmd)
581 {
582   lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main ();
583   vlib_node_add_next (lgm->vlib_main, lisp_gpe_ip4_input_node.index,
584                       lisp_gpe_nsh_placeholder_input_node.index);
585   next_proto_to_next_index[LISP_GPE_NEXT_PROTO_NSH] =
586     LISP_GPE_INPUT_NEXT_NSH_INPUT;
587   return 0;
588 }
589
590 /* *INDENT-OFF* */
591 VLIB_CLI_COMMAND (lisp_add_placeholder_nsh_node_command, static) = {
592   .path = "test one nsh add-placeholder-decap-node",
593   .function = lisp_add_placeholder_nsh_node_command_fn,
594 };
595 /* *INDENT-ON* */
596
597 VLIB_INIT_FUNCTION (gpe_decap_init);
598
599 /*
600  * fd.io coding-style-patch-verification: ON
601  *
602  * Local Variables:
603  * eval: (c-set-style "gnu")
604  * End:
605  */