VPP-307: Documentation for vnet/vnet/vxlan-gpe
[vpp.git] / vnet / vnet / lisp-gpe / interface.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 #include <vppinfra/error.h>
17 #include <vppinfra/hash.h>
18 #include <vnet/vnet.h>
19 #include <vnet/ip/ip.h>
20 #include <vnet/ip/udp.h>
21 #include <vnet/ethernet/ethernet.h>
22 #include <vnet/lisp-gpe/lisp_gpe.h>
23
24 #define foreach_lisp_gpe_tx_next        \
25   _(DROP, "error-drop")                 \
26   _(IP4_LOOKUP, "ip4-lookup")           \
27   _(IP6_LOOKUP, "ip6-lookup")
28
29 typedef enum
30 {
31 #define _(sym,str) LISP_GPE_TX_NEXT_##sym,
32   foreach_lisp_gpe_tx_next
33 #undef _
34     LISP_GPE_TX_N_NEXT,
35 } lisp_gpe_tx_next_t;
36
37 typedef struct
38 {
39   u32 tunnel_index;
40 } lisp_gpe_tx_trace_t;
41
42 u8 *
43 format_lisp_gpe_tx_trace (u8 * s, va_list * args)
44 {
45   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
46   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
47   lisp_gpe_tx_trace_t *t = va_arg (*args, lisp_gpe_tx_trace_t *);
48
49   s = format (s, "LISP-GPE-TX: tunnel %d", t->tunnel_index);
50   return s;
51 }
52
53 always_inline void
54 get_one_tunnel_inline (lisp_gpe_main_t * lgm, vlib_buffer_t * b0,
55                        lisp_gpe_tunnel_t ** t0, u8 is_v4)
56 {
57   u32 adj_index0, tunnel_index0;
58   ip_adjacency_t *adj0;
59
60   /* Get adjacency and from it the tunnel_index */
61   adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
62
63   if (is_v4)
64     adj0 = ip_get_adjacency (lgm->lm4, adj_index0);
65   else
66     adj0 = ip_get_adjacency (lgm->lm6, adj_index0);
67
68   tunnel_index0 = adj0->if_address_index;
69   t0[0] = pool_elt_at_index (lgm->tunnels, tunnel_index0);
70
71   ASSERT (t0[0] != 0);
72 }
73
74 always_inline void
75 encap_one_inline (lisp_gpe_main_t * lgm, vlib_buffer_t * b0,
76                   lisp_gpe_tunnel_t * t0, u32 * next0)
77 {
78   ASSERT (sizeof (ip4_udp_lisp_gpe_header_t) == 36);
79   ASSERT (sizeof (ip6_udp_lisp_gpe_header_t) == 56);
80
81   lisp_gpe_sub_tunnel_t *st0;
82   u32 *sti0;
83
84   sti0 = vec_elt_at_index (t0->sub_tunnels_lbv,
85                            vnet_buffer (b0)->ip.flow_hash %
86                            t0->sub_tunnels_lbv_count);
87   st0 = vec_elt_at_index (t0->sub_tunnels, sti0[0]);
88   if (st0->is_ip4)
89     {
90       ip_udp_encap_one (lgm->vlib_main, b0, st0->rewrite, 36, 1);
91       next0[0] = LISP_GPE_TX_NEXT_IP4_LOOKUP;
92     }
93   else
94     {
95       ip_udp_encap_one (lgm->vlib_main, b0, st0->rewrite, 56, 0);
96       next0[0] = LISP_GPE_TX_NEXT_IP6_LOOKUP;
97     }
98
99   /* Reset to look up tunnel partner in the configured FIB */
100   vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->encap_fib_index;
101 }
102
103 always_inline void
104 get_two_tunnels_inline (lisp_gpe_main_t * lgm, vlib_buffer_t * b0,
105                         vlib_buffer_t * b1, lisp_gpe_tunnel_t ** t0,
106                         lisp_gpe_tunnel_t ** t1, u8 is_v4)
107 {
108   u32 adj_index0, adj_index1, tunnel_index0, tunnel_index1;
109   ip_adjacency_t *adj0, *adj1;
110
111   /* Get adjacency and from it the tunnel_index */
112   adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
113   adj_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX];
114
115   if (is_v4)
116     {
117       adj0 = ip_get_adjacency (lgm->lm4, adj_index0);
118       adj1 = ip_get_adjacency (lgm->lm4, adj_index1);
119     }
120   else
121     {
122       adj0 = ip_get_adjacency (lgm->lm6, adj_index0);
123       adj1 = ip_get_adjacency (lgm->lm6, adj_index1);
124     }
125
126   tunnel_index0 = adj0->if_address_index;
127   tunnel_index1 = adj1->if_address_index;
128
129   t0[0] = pool_elt_at_index (lgm->tunnels, tunnel_index0);
130   t1[0] = pool_elt_at_index (lgm->tunnels, tunnel_index1);
131
132   ASSERT (t0[0] != 0);
133   ASSERT (t1[0] != 0);
134 }
135
136 always_inline void
137 encap_two_inline (lisp_gpe_main_t * lgm, vlib_buffer_t * b0,
138                   vlib_buffer_t * b1, lisp_gpe_tunnel_t * t0,
139                   lisp_gpe_tunnel_t * t1, u32 * next0, u32 * next1)
140 {
141   ASSERT (sizeof (ip4_udp_lisp_gpe_header_t) == 36);
142   ASSERT (sizeof (ip6_udp_lisp_gpe_header_t) == 56);
143
144   lisp_gpe_sub_tunnel_t *st0, *st1;
145   u32 *sti0, *sti1;
146   sti0 = vec_elt_at_index (t0->sub_tunnels_lbv,
147                            vnet_buffer (b0)->ip.flow_hash %
148                            t0->sub_tunnels_lbv_count);
149   sti1 =
150     vec_elt_at_index (t1->sub_tunnels_lbv,
151                       vnet_buffer (b1)->ip.flow_hash %
152                       t1->sub_tunnels_lbv_count);
153   st0 = vec_elt_at_index (t0->sub_tunnels, sti0[0]);
154   st1 = vec_elt_at_index (t1->sub_tunnels, sti1[0]);
155
156   if (PREDICT_TRUE (st0->is_ip4 == st1->is_ip4))
157     {
158       if (st0->is_ip4)
159         {
160           ip_udp_encap_one (lgm->vlib_main, b0, st0->rewrite, 36, 1);
161           ip_udp_encap_one (lgm->vlib_main, b1, st1->rewrite, 36, 1);
162           next0[0] = next1[0] = LISP_GPE_TX_NEXT_IP4_LOOKUP;
163         }
164       else
165         {
166           ip_udp_encap_one (lgm->vlib_main, b0, st0->rewrite, 56, 0);
167           ip_udp_encap_one (lgm->vlib_main, b1, st1->rewrite, 56, 0);
168           next0[0] = next1[0] = LISP_GPE_TX_NEXT_IP6_LOOKUP;
169         }
170     }
171   else
172     {
173       if (st0->is_ip4)
174         {
175           ip_udp_encap_one (lgm->vlib_main, b0, st0->rewrite, 36, 1);
176           ip_udp_encap_one (lgm->vlib_main, b1, st1->rewrite, 56, 1);
177           next0[0] = LISP_GPE_TX_NEXT_IP4_LOOKUP;
178           next1[0] = LISP_GPE_TX_NEXT_IP6_LOOKUP;
179         }
180       else
181         {
182           ip_udp_encap_one (lgm->vlib_main, b0, st0->rewrite, 56, 1);
183           ip_udp_encap_one (lgm->vlib_main, b1, st1->rewrite, 36, 1);
184           next0[0] = LISP_GPE_TX_NEXT_IP6_LOOKUP;
185           next1[0] = LISP_GPE_TX_NEXT_IP4_LOOKUP;
186         }
187     }
188
189   /* Reset to look up tunnel partner in the configured FIB */
190   vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->encap_fib_index;
191   vnet_buffer (b1)->sw_if_index[VLIB_TX] = t1->encap_fib_index;
192 }
193
194 #define is_v4_packet(_h) ((*(u8*) _h) & 0xF0) == 0x40
195
196 static uword
197 lisp_gpe_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node,
198                        vlib_frame_t * from_frame)
199 {
200   u32 n_left_from, next_index, *from, *to_next;
201   lisp_gpe_main_t *lgm = &lisp_gpe_main;
202
203   from = vlib_frame_vector_args (from_frame);
204   n_left_from = from_frame->n_vectors;
205
206   next_index = node->cached_next_index;
207
208   while (n_left_from > 0)
209     {
210       u32 n_left_to_next;
211
212       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
213
214       while (n_left_from >= 4 && n_left_to_next >= 2)
215         {
216           u32 bi0, bi1;
217           vlib_buffer_t *b0, *b1;
218           u32 next0, next1;
219           lisp_gpe_tunnel_t *t0 = 0, *t1 = 0;
220           u8 is_v4_eid0, is_v4_eid1;
221
222           next0 = next1 = LISP_GPE_TX_NEXT_IP4_LOOKUP;
223
224           /* Prefetch next iteration. */
225           {
226             vlib_buffer_t *p2, *p3;
227
228             p2 = vlib_get_buffer (vm, from[2]);
229             p3 = vlib_get_buffer (vm, from[3]);
230
231             vlib_prefetch_buffer_header (p2, LOAD);
232             vlib_prefetch_buffer_header (p3, LOAD);
233
234             CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
235             CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
236           }
237
238           bi0 = from[0];
239           bi1 = from[1];
240           to_next[0] = bi0;
241           to_next[1] = bi1;
242           from += 2;
243           to_next += 2;
244           n_left_to_next -= 2;
245           n_left_from -= 2;
246
247           b0 = vlib_get_buffer (vm, bi0);
248           b1 = vlib_get_buffer (vm, bi1);
249
250           is_v4_eid0 = is_v4_packet (vlib_buffer_get_current (b0));
251           is_v4_eid1 = is_v4_packet (vlib_buffer_get_current (b1));
252
253           if (PREDICT_TRUE (is_v4_eid0 == is_v4_eid1))
254             {
255               get_two_tunnels_inline (lgm, b0, b1, &t0, &t1,
256                                       is_v4_eid0 ? 1 : 0);
257             }
258           else
259             {
260               get_one_tunnel_inline (lgm, b0, &t0, is_v4_eid0 ? 1 : 0);
261               get_one_tunnel_inline (lgm, b1, &t1, is_v4_eid1 ? 1 : 0);
262             }
263
264           encap_two_inline (lgm, b0, b1, t0, t1, &next0, &next1);
265
266           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
267             {
268               lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0,
269                                                         sizeof (*tr));
270               tr->tunnel_index = t0 - lgm->tunnels;
271             }
272           if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
273             {
274               lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b1,
275                                                         sizeof (*tr));
276               tr->tunnel_index = t1 - lgm->tunnels;
277             }
278
279           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
280                                            n_left_to_next, bi0, bi1, next0,
281                                            next1);
282         }
283
284       while (n_left_from > 0 && n_left_to_next > 0)
285         {
286           vlib_buffer_t *b0;
287           u32 bi0, next0 = LISP_GPE_TX_NEXT_IP4_LOOKUP;
288           lisp_gpe_tunnel_t *t0 = 0;
289           u8 is_v4_0;
290
291           bi0 = from[0];
292           to_next[0] = bi0;
293           from += 1;
294           to_next += 1;
295           n_left_from -= 1;
296           n_left_to_next -= 1;
297
298           b0 = vlib_get_buffer (vm, bi0);
299
300           is_v4_0 = is_v4_packet (vlib_buffer_get_current (b0));
301           get_one_tunnel_inline (lgm, b0, &t0, is_v4_0 ? 1 : 0);
302
303           encap_one_inline (lgm, b0, t0, &next0);
304
305           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
306             {
307               lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0,
308                                                         sizeof (*tr));
309               tr->tunnel_index = t0 - lgm->tunnels;
310             }
311           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
312                                            n_left_to_next, bi0, next0);
313         }
314
315       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
316     }
317
318   return from_frame->n_vectors;
319 }
320
321 static u8 *
322 format_lisp_gpe_name (u8 * s, va_list * args)
323 {
324   u32 dev_instance = va_arg (*args, u32);
325   return format (s, "lisp_gpe%d", dev_instance);
326 }
327
328 /* *INDENT-OFF* */
329 VNET_DEVICE_CLASS (lisp_gpe_device_class,static) = {
330   .name = "LISP_GPE",
331   .format_device_name = format_lisp_gpe_name,
332   .format_tx_trace = format_lisp_gpe_tx_trace,
333   .tx_function = lisp_gpe_interface_tx,
334   .no_flatten_output_chains = 1,
335 };
336 /* *INDENT-ON* */
337
338 static uword
339 dummy_set_rewrite (vnet_main_t * vnm, u32 sw_if_index, u32 l3_type,
340                    void *dst_address, void *rewrite, uword max_rewrite_bytes)
341 {
342   return 0;
343 }
344
345 u8 *
346 format_lisp_gpe_header_with_length (u8 * s, va_list * args)
347 {
348   lisp_gpe_header_t *h = va_arg (*args, lisp_gpe_header_t *);
349   u32 max_header_bytes = va_arg (*args, u32);
350   u32 header_bytes;
351
352   header_bytes = sizeof (h[0]);
353   if (max_header_bytes != 0 && header_bytes > max_header_bytes)
354     return format (s, "lisp-gpe header truncated");
355
356   s = format (s, "flags: ");
357 #define _(n,v) if (h->flags & v) s = format (s, "%s ", #n);
358   foreach_lisp_gpe_flag_bit;
359 #undef _
360
361   s = format (s, "\n  ver_res %d res %d next_protocol %d iid %d(%x)",
362               h->ver_res, h->res, h->next_protocol,
363               clib_net_to_host_u32 (h->iid), clib_net_to_host_u32 (h->iid));
364   return s;
365 }
366
367 /* *INDENT-OFF* */
368 VNET_HW_INTERFACE_CLASS (lisp_gpe_hw_class) = {
369   .name = "LISP_GPE",
370   .format_header = format_lisp_gpe_header_with_length,
371   .set_rewrite = dummy_set_rewrite,
372 };
373 /* *INDENT-ON* */
374
375 int
376 add_del_ip_prefix_route (ip_prefix_t * dst_prefix, u32 table_id,
377                          ip_adjacency_t * add_adj, u8 is_add, u32 * adj_index)
378 {
379   uword *p;
380
381   if (ip_prefix_version (dst_prefix) == IP4)
382     {
383       ip4_main_t *im4 = &ip4_main;
384       ip4_add_del_route_args_t a;
385       ip4_address_t addr = ip_prefix_v4 (dst_prefix);
386
387       memset (&a, 0, sizeof (a));
388       a.flags = IP4_ROUTE_FLAG_TABLE_ID;
389       a.table_index_or_table_id = table_id;
390       a.adj_index = ~0;
391       a.dst_address_length = ip_prefix_len (dst_prefix);
392       a.dst_address = addr;
393       a.flags |= is_add ? IP4_ROUTE_FLAG_ADD : IP4_ROUTE_FLAG_DEL;
394       a.add_adj = add_adj;
395       a.n_add_adj = is_add ? 1 : 0;
396
397       ip4_add_del_route (im4, &a);
398
399       if (is_add)
400         {
401           p = ip4_get_route (im4, table_id, 0, addr.as_u8,
402                              ip_prefix_len (dst_prefix));
403           if (p == 0)
404             {
405               clib_warning ("Failed to insert route for eid %U!",
406                             format_ip4_address_and_length, addr.as_u8,
407                             ip_prefix_len (dst_prefix));
408               return -1;
409             }
410           adj_index[0] = p[0];
411         }
412     }
413   else
414     {
415       ip6_main_t *im6 = &ip6_main;
416       ip6_add_del_route_args_t a;
417       ip6_address_t addr = ip_prefix_v6 (dst_prefix);
418
419       memset (&a, 0, sizeof (a));
420       a.flags = IP6_ROUTE_FLAG_TABLE_ID;
421       a.table_index_or_table_id = table_id;
422       a.adj_index = ~0;
423       a.dst_address_length = ip_prefix_len (dst_prefix);
424       a.dst_address = addr;
425       a.flags |= is_add ? IP6_ROUTE_FLAG_ADD : IP6_ROUTE_FLAG_DEL;
426       a.add_adj = add_adj;
427       a.n_add_adj = is_add ? 1 : 0;
428
429       ip6_add_del_route (im6, &a);
430
431       if (is_add)
432         {
433           adj_index[0] = ip6_get_route (im6, table_id, 0, &addr,
434                                         ip_prefix_len (dst_prefix));
435           if (adj_index[0] == 0)
436             {
437               clib_warning ("Failed to insert route for eid %U!",
438                             format_ip6_address_and_length, addr.as_u8,
439                             ip_prefix_len (dst_prefix));
440               return -1;
441             }
442         }
443     }
444   return 0;
445 }
446
447 static void
448 add_del_lisp_gpe_default_route (u32 table_id, u8 is_v4, u8 is_add)
449 {
450   lisp_gpe_main_t *lgm = &lisp_gpe_main;
451   ip_adjacency_t adj;
452   ip_prefix_t prefix;
453   u32 adj_index = 0;
454
455   /* setup adjacency */
456   memset (&adj, 0, sizeof (adj));
457
458   adj.n_adj = 1;
459   adj.explicit_fib_index = ~0;
460   adj.lookup_next_index = is_v4 ? lgm->ip4_lookup_next_lgpe_ip4_lookup :
461     lgm->ip6_lookup_next_lgpe_ip6_lookup;
462   /* default route has tunnel_index ~0 */
463   adj.rewrite_header.sw_if_index = ~0;
464
465   /* set prefix to 0/0 */
466   memset (&prefix, 0, sizeof (prefix));
467   ip_prefix_version (&prefix) = is_v4 ? IP4 : IP6;
468
469   /* add/delete route for prefix */
470   add_del_ip_prefix_route (&prefix, table_id, &adj, is_add, &adj_index);
471 }
472
473 static void
474 lisp_gpe_iface_set_table (u32 sw_if_index, u32 table_id, u8 is_ip4)
475 {
476   if (is_ip4)
477     {
478       ip4_main_t *im4 = &ip4_main;
479       ip4_fib_t *fib;
480       fib = find_ip4_fib_by_table_index_or_id (im4, table_id,
481                                                IP4_ROUTE_FLAG_TABLE_ID);
482
483       /* fib's created if it doesn't exist */
484       ASSERT (fib != 0);
485
486       vec_validate (im4->fib_index_by_sw_if_index, sw_if_index);
487       im4->fib_index_by_sw_if_index[sw_if_index] = fib->index;
488     }
489   else
490     {
491       ip6_main_t *im6 = &ip6_main;
492       ip6_fib_t *fib;
493       fib = find_ip6_fib_by_table_index_or_id (im6, table_id,
494                                                IP6_ROUTE_FLAG_TABLE_ID);
495
496       /* fib's created if it doesn't exist */
497       ASSERT (fib != 0);
498
499       vec_validate (im6->fib_index_by_sw_if_index, sw_if_index);
500       im6->fib_index_by_sw_if_index[sw_if_index] = fib->index;
501     }
502 }
503
504 #define foreach_l2_lisp_gpe_tx_next     \
505   _(DROP, "error-drop")                 \
506   _(IP4_LOOKUP, "ip4-lookup")           \
507   _(IP6_LOOKUP, "ip6-lookup")           \
508   _(LISP_CP_LOOKUP, "lisp-cp-lookup")
509
510 typedef enum
511 {
512 #define _(sym,str) L2_LISP_GPE_TX_NEXT_##sym,
513   foreach_l2_lisp_gpe_tx_next
514 #undef _
515     L2_LISP_GPE_TX_N_NEXT,
516 } l2_lisp_gpe_tx_next_t;
517
518 typedef struct
519 {
520   u32 tunnel_index;
521 } l2_lisp_gpe_tx_trace_t;
522
523 u8 *
524 format_l2_lisp_gpe_tx_trace (u8 * s, va_list * args)
525 {
526   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
527   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
528   l2_lisp_gpe_tx_trace_t *t = va_arg (*args, l2_lisp_gpe_tx_trace_t *);
529
530   s = format (s, "L2-LISP-GPE-TX: tunnel %d", t->tunnel_index);
531   return s;
532 }
533
534 always_inline void
535 l2_process_tunnel_action (vlib_buffer_t * b0, u8 action, u32 * next0)
536 {
537   if (LISP_SEND_MAP_REQUEST == action)
538     {
539       next0[0] = L2_LISP_GPE_TX_NEXT_LISP_CP_LOOKUP;
540       vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_MAC;
541     }
542   else
543     {
544       next0[0] = L2_LISP_GPE_TX_NEXT_DROP;
545     }
546 }
547
548 always_inline u32
549 ip_flow_hash (void *data)
550 {
551   ip4_header_t *iph = (ip4_header_t *) data;
552
553   if ((iph->ip_version_and_header_length & 0xF0) == 0x40)
554     return ip4_compute_flow_hash (iph, IP_FLOW_HASH_DEFAULT);
555   else
556     return ip6_compute_flow_hash ((ip6_header_t *) iph, IP_FLOW_HASH_DEFAULT);
557 }
558
559 always_inline u32
560 l2_flow_hash (vlib_buffer_t * b0)
561 {
562   ethernet_header_t *eh;
563   u64 a, b, c;
564   uword is_ip, eh_size;
565   u16 eh_type;
566
567   eh = vlib_buffer_get_current (b0);
568   eh_type = clib_net_to_host_u16 (eh->type);
569   eh_size = ethernet_buffer_header_size (b0);
570
571   is_ip = (eh_type == ETHERNET_TYPE_IP4 || eh_type == ETHERNET_TYPE_IP6);
572
573   /* since we have 2 cache lines, use them */
574   if (is_ip)
575     a = ip_flow_hash ((u8 *) vlib_buffer_get_current (b0) + eh_size);
576   else
577     a = eh->type;
578
579   b = mac_to_u64 ((u8 *) eh->dst_address);
580   c = mac_to_u64 ((u8 *) eh->src_address);
581   hash_mix64 (a, b, c);
582
583   return (u32) c;
584 }
585
586 always_inline void
587 l2_process_one (lisp_gpe_main_t * lgm, vlib_buffer_t * b0, u32 ti0,
588                 u32 * next0)
589 {
590   lisp_gpe_tunnel_t *t0;
591
592   t0 = pool_elt_at_index (lgm->tunnels, ti0);
593   ASSERT (0 != t0);
594
595   if (PREDICT_TRUE (LISP_NO_ACTION == t0->action))
596     {
597       /* compute 'flow' hash */
598       if (PREDICT_TRUE (t0->sub_tunnels_lbv_count > 1))
599         vnet_buffer (b0)->ip.flow_hash = l2_flow_hash (b0);
600       encap_one_inline (lgm, b0, t0, next0);
601     }
602   else
603     {
604       l2_process_tunnel_action (b0, t0->action, next0);
605     }
606 }
607
608 always_inline void
609 l2_process_two (lisp_gpe_main_t * lgm, vlib_buffer_t * b0, vlib_buffer_t * b1,
610                 u32 ti0, u32 ti1, u32 * next0, u32 * next1)
611 {
612   lisp_gpe_tunnel_t *t0, *t1;
613
614   t0 = pool_elt_at_index (lgm->tunnels, ti0);
615   t1 = pool_elt_at_index (lgm->tunnels, ti1);
616
617   ASSERT (0 != t0 && 0 != t1);
618
619   if (PREDICT_TRUE (LISP_NO_ACTION == t0->action
620                     && LISP_NO_ACTION == t1->action))
621     {
622       if (PREDICT_TRUE (t0->sub_tunnels_lbv_count > 1))
623         vnet_buffer (b0)->ip.flow_hash = l2_flow_hash (b0);
624       if (PREDICT_TRUE (t1->sub_tunnels_lbv_count > 1))
625         vnet_buffer (b1)->ip.flow_hash = l2_flow_hash (b1);
626       encap_two_inline (lgm, b0, b1, t0, t1, next0, next1);
627     }
628   else
629     {
630       if (LISP_NO_ACTION == t0->action)
631         {
632           if (PREDICT_TRUE (t0->sub_tunnels_lbv_count > 1))
633             vnet_buffer (b0)->ip.flow_hash = l2_flow_hash (b0);
634           encap_one_inline (lgm, b0, t0, next0);
635           l2_process_tunnel_action (b1, t1->action, next1);
636         }
637       else if (LISP_NO_ACTION == t1->action)
638         {
639           if (PREDICT_TRUE (t1->sub_tunnels_lbv_count > 1))
640             vnet_buffer (b1)->ip.flow_hash = l2_flow_hash (b1);
641           encap_one_inline (lgm, b1, t1, next1);
642           l2_process_tunnel_action (b0, t0->action, next0);
643         }
644       else
645         {
646           l2_process_tunnel_action (b0, t0->action, next0);
647           l2_process_tunnel_action (b1, t1->action, next1);
648         }
649     }
650 }
651
652 static uword
653 l2_lisp_gpe_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node,
654                           vlib_frame_t * from_frame)
655 {
656   u32 n_left_from, next_index, *from, *to_next;
657   lisp_gpe_main_t *lgm = &lisp_gpe_main;
658
659   from = vlib_frame_vector_args (from_frame);
660   n_left_from = from_frame->n_vectors;
661
662   next_index = node->cached_next_index;
663
664   while (n_left_from > 0)
665     {
666       u32 n_left_to_next;
667
668       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
669
670       while (n_left_from >= 4 && n_left_to_next >= 2)
671         {
672           u32 bi0, bi1;
673           vlib_buffer_t *b0, *b1;
674           u32 next0, next1, ti0, ti1;
675           lisp_gpe_tunnel_t *t0 = 0, *t1 = 0;
676           ethernet_header_t *e0, *e1;
677
678           next0 = next1 = L2_LISP_GPE_TX_NEXT_LISP_CP_LOOKUP;
679
680           /* Prefetch next iteration. */
681           {
682             vlib_buffer_t *p2, *p3;
683
684             p2 = vlib_get_buffer (vm, from[2]);
685             p3 = vlib_get_buffer (vm, from[3]);
686
687             vlib_prefetch_buffer_header (p2, LOAD);
688             vlib_prefetch_buffer_header (p3, LOAD);
689
690             CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
691             CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
692           }
693
694           bi0 = from[0];
695           bi1 = from[1];
696           to_next[0] = bi0;
697           to_next[1] = bi1;
698           from += 2;
699           to_next += 2;
700           n_left_to_next -= 2;
701           n_left_from -= 2;
702
703           b0 = vlib_get_buffer (vm, bi0);
704           b1 = vlib_get_buffer (vm, bi1);
705
706           e0 = vlib_buffer_get_current (b0);
707           e1 = vlib_buffer_get_current (b1);
708
709           /* lookup dst + src mac */
710           ti0 = lisp_l2_fib_lookup (lgm, vnet_buffer (b0)->l2.bd_index,
711                                     e0->src_address, e0->dst_address);
712           ti1 = lisp_l2_fib_lookup (lgm, vnet_buffer (b1)->l2.bd_index,
713                                     e1->src_address, e1->dst_address);
714
715           if (PREDICT_TRUE ((u32) ~ 0 != ti0) && (u32) ~ 0 != ti1)
716             {
717               /* process both tunnels */
718               l2_process_two (lgm, b0, b1, ti0, ti1, &next0, &next1);
719             }
720           else
721             {
722               if ((u32) ~ 0 != ti0)
723                 {
724                   /* process tunnel for b0 */
725                   l2_process_one (lgm, b0, ti0, &next0);
726
727                   /* no tunnel found for b1, send to control plane */
728                   next1 = L2_LISP_GPE_TX_NEXT_LISP_CP_LOOKUP;
729                   vnet_buffer (b1)->lisp.overlay_afi = LISP_AFI_MAC;
730                 }
731               else if ((u32) ~ 0 != ti1)
732                 {
733                   /* process tunnel for b1 */
734                   l2_process_one (lgm, b1, ti1, &next1);
735
736                   /* no tunnel found b0, send to control plane */
737                   next0 = L2_LISP_GPE_TX_NEXT_LISP_CP_LOOKUP;
738                   vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_MAC;
739                 }
740               else
741                 {
742                   /* no tunnels found */
743                   next0 = L2_LISP_GPE_TX_NEXT_LISP_CP_LOOKUP;
744                   vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_MAC;
745                   next1 = L2_LISP_GPE_TX_NEXT_LISP_CP_LOOKUP;
746                   vnet_buffer (b1)->lisp.overlay_afi = LISP_AFI_MAC;
747                 }
748             }
749
750           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
751             {
752               l2_lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0,
753                                                            sizeof (*tr));
754               tr->tunnel_index = t0 - lgm->tunnels;
755             }
756           if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
757             {
758               l2_lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b1,
759                                                            sizeof (*tr));
760               tr->tunnel_index = t1 - lgm->tunnels;
761             }
762
763           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
764                                            n_left_to_next, bi0, bi1, next0,
765                                            next1);
766         }
767
768       while (n_left_from > 0 && n_left_to_next > 0)
769         {
770           vlib_buffer_t *b0;
771           u32 bi0, ti0, next0 = L2_LISP_GPE_TX_NEXT_LISP_CP_LOOKUP;
772           ethernet_header_t *e0;
773
774           bi0 = from[0];
775           to_next[0] = bi0;
776           from += 1;
777           to_next += 1;
778           n_left_from -= 1;
779           n_left_to_next -= 1;
780
781           b0 = vlib_get_buffer (vm, bi0);
782           e0 = vlib_buffer_get_current (b0);
783
784           /* lookup dst + src mac */
785           ti0 = lisp_l2_fib_lookup (lgm, vnet_buffer (b0)->l2.bd_index,
786                                     e0->src_address, e0->dst_address);
787
788           if (PREDICT_TRUE ((u32) ~ 0 != ti0))
789             {
790               l2_process_one (lgm, b0, ti0, &next0);
791             }
792           else
793             {
794               /* no tunnel found send to control plane */
795               next0 = L2_LISP_GPE_TX_NEXT_LISP_CP_LOOKUP;
796               vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_MAC;
797             }
798
799           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
800             {
801               l2_lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0,
802                                                            sizeof (*tr));
803               tr->tunnel_index = ti0 ? ti0 : ~0;
804             }
805           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
806                                            n_left_to_next, bi0, next0);
807         }
808
809       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
810     }
811
812   return from_frame->n_vectors;
813 }
814
815 static u8 *
816 format_l2_lisp_gpe_name (u8 * s, va_list * args)
817 {
818   u32 dev_instance = va_arg (*args, u32);
819   return format (s, "l2_lisp_gpe%d", dev_instance);
820 }
821
822 /* *INDENT-OFF* */
823 VNET_DEVICE_CLASS (l2_lisp_gpe_device_class,static) = {
824   .name = "L2_LISP_GPE",
825   .format_device_name = format_l2_lisp_gpe_name,
826   .format_tx_trace = format_lisp_gpe_tx_trace,
827   .tx_function = l2_lisp_gpe_interface_tx,
828   .no_flatten_output_chains = 1,
829 };
830 /* *INDENT-ON* */
831
832 static vnet_hw_interface_t *
833 create_lisp_gpe_iface (lisp_gpe_main_t * lgm, u32 vni, u32 dp_table,
834                        vnet_device_class_t * dev_class,
835                        tunnel_lookup_t * tuns)
836 {
837   u32 flen;
838   u32 hw_if_index = ~0;
839   u8 *new_name;
840   vnet_hw_interface_t *hi;
841   vnet_main_t *vnm = lgm->vnet_main;
842
843   /* create hw lisp_gpeX iface if needed, otherwise reuse existing */
844   flen = vec_len (lgm->free_tunnel_hw_if_indices);
845   if (flen > 0)
846     {
847       hw_if_index = lgm->free_tunnel_hw_if_indices[flen - 1];
848       _vec_len (lgm->free_tunnel_hw_if_indices) -= 1;
849
850       hi = vnet_get_hw_interface (vnm, hw_if_index);
851
852       /* rename interface */
853       new_name = format (0, "%U", dev_class->format_device_name, vni);
854
855       vec_add1 (new_name, 0);
856       vnet_rename_interface (vnm, hw_if_index, (char *) new_name);
857       vec_free (new_name);
858
859       /* clear old stats of freed interface before reuse */
860       vnet_interface_main_t *im = &vnm->interface_main;
861       vnet_interface_counter_lock (im);
862       vlib_zero_combined_counter (&im->combined_sw_if_counters
863                                   [VNET_INTERFACE_COUNTER_TX],
864                                   hi->sw_if_index);
865       vlib_zero_combined_counter (&im->combined_sw_if_counters
866                                   [VNET_INTERFACE_COUNTER_RX],
867                                   hi->sw_if_index);
868       vlib_zero_simple_counter (&im->sw_if_counters
869                                 [VNET_INTERFACE_COUNTER_DROP],
870                                 hi->sw_if_index);
871       vnet_interface_counter_unlock (im);
872     }
873   else
874     {
875       hw_if_index = vnet_register_interface (vnm, dev_class->index, vni,
876                                              lisp_gpe_hw_class.index, 0);
877       hi = vnet_get_hw_interface (vnm, hw_if_index);
878     }
879
880   hash_set (tuns->hw_if_index_by_dp_table, dp_table, hw_if_index);
881
882   /* set tunnel termination: post decap, packets are tagged as having been
883    * originated by lisp-gpe interface */
884   hash_set (tuns->sw_if_index_by_vni, vni, hi->sw_if_index);
885   hash_set (tuns->vni_by_sw_if_index, hi->sw_if_index, vni);
886
887   return hi;
888 }
889
890 static void
891 remove_lisp_gpe_iface (lisp_gpe_main_t * lgm, u32 hi_index, u32 dp_table,
892                        tunnel_lookup_t * tuns)
893 {
894   vnet_main_t *vnm = lgm->vnet_main;
895   vnet_hw_interface_t *hi;
896   uword *vnip;
897
898   hi = vnet_get_hw_interface (vnm, hi_index);
899
900   /* disable interface */
901   vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 0 /* down */ );
902   vnet_hw_interface_set_flags (vnm, hi->hw_if_index, 0 /* down */ );
903   hash_unset (tuns->hw_if_index_by_dp_table, dp_table);
904   vec_add1 (lgm->free_tunnel_hw_if_indices, hi->hw_if_index);
905
906   /* clean tunnel termination and vni to sw_if_index binding */
907   vnip = hash_get (tuns->vni_by_sw_if_index, hi->sw_if_index);
908   if (0 == vnip)
909     {
910       clib_warning ("No vni associated to interface %d", hi->sw_if_index);
911       return;
912     }
913   hash_unset (tuns->sw_if_index_by_vni, vnip[0]);
914   hash_unset (tuns->vni_by_sw_if_index, hi->sw_if_index);
915 }
916
917 static int
918 lisp_gpe_add_del_l3_iface (lisp_gpe_main_t * lgm,
919                            vnet_lisp_gpe_add_del_iface_args_t * a)
920 {
921   vnet_main_t *vnm = lgm->vnet_main;
922   tunnel_lookup_t *l3_ifaces = &lgm->l3_ifaces;
923   vnet_hw_interface_t *hi;
924   u32 lookup_next_index4, lookup_next_index6;
925   uword *hip, *si;
926
927   hip = hash_get (l3_ifaces->hw_if_index_by_dp_table, a->table_id);
928
929   if (a->is_add)
930     {
931       if (hip)
932         {
933           clib_warning ("vrf %d already mapped to a vni", a->table_id);
934           return -1;
935         }
936
937       si = hash_get (l3_ifaces->sw_if_index_by_vni, a->vni);
938       if (si)
939         {
940           clib_warning ("Interface for vni %d already exists", a->vni);
941           return -1;
942         }
943
944       /* create lisp iface and populate tunnel tables */
945       hi = create_lisp_gpe_iface (lgm, a->vni, a->table_id,
946                                   &lisp_gpe_device_class, l3_ifaces);
947
948       /* set ingress arc from lgpe_ipX_lookup */
949       lookup_next_index4 = vlib_node_add_next (lgm->vlib_main,
950                                                lgpe_ip4_lookup_node.index,
951                                                hi->output_node_index);
952       lookup_next_index6 = vlib_node_add_next (lgm->vlib_main,
953                                                lgpe_ip6_lookup_node.index,
954                                                hi->output_node_index);
955       hash_set (lgm->lgpe_ip4_lookup_next_index_by_table_id, a->table_id,
956                 lookup_next_index4);
957       hash_set (lgm->lgpe_ip6_lookup_next_index_by_table_id, a->table_id,
958                 lookup_next_index6);
959
960       /* insert default routes that point to lgpe-ipx-lookup */
961       add_del_lisp_gpe_default_route (a->table_id, /* is_v4 */ 1, 1);
962       add_del_lisp_gpe_default_route (a->table_id, /* is_v4 */ 0, 1);
963
964       /* set egress arcs */
965 #define _(sym,str) vlib_node_add_named_next_with_slot (vnm->vlib_main, \
966                     hi->tx_node_index, str, LISP_GPE_TX_NEXT_##sym);
967       foreach_lisp_gpe_tx_next
968 #undef _
969         /* set interface in appropriate v4 and v6 FIBs */
970         lisp_gpe_iface_set_table (hi->sw_if_index, a->table_id, 1);
971       lisp_gpe_iface_set_table (hi->sw_if_index, a->table_id, 0);
972
973       /* enable interface */
974       vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
975                                    VNET_SW_INTERFACE_FLAG_ADMIN_UP);
976       vnet_hw_interface_set_flags (vnm, hi->hw_if_index,
977                                    VNET_HW_INTERFACE_FLAG_LINK_UP);
978     }
979   else
980     {
981       if (hip == 0)
982         {
983           clib_warning ("The interface for vrf %d doesn't exist",
984                         a->table_id);
985           return -1;
986         }
987
988       remove_lisp_gpe_iface (lgm, hip[0], a->table_id, &lgm->l3_ifaces);
989
990       /* unset default routes */
991       add_del_lisp_gpe_default_route (a->table_id, /* is_v4 */ 1, 0);
992       add_del_lisp_gpe_default_route (a->table_id, /* is_v4 */ 0, 0);
993     }
994
995   return 0;
996 }
997
998 static int
999 lisp_gpe_add_del_l2_iface (lisp_gpe_main_t * lgm,
1000                            vnet_lisp_gpe_add_del_iface_args_t * a)
1001 {
1002   vnet_main_t *vnm = lgm->vnet_main;
1003   tunnel_lookup_t *l2_ifaces = &lgm->l2_ifaces;
1004   vnet_hw_interface_t *hi;
1005   uword *hip, *si;
1006   u16 bd_index;
1007
1008   bd_index = bd_find_or_add_bd_index (&bd_main, a->bd_id);
1009   hip = hash_get (l2_ifaces->hw_if_index_by_dp_table, bd_index);
1010
1011   if (a->is_add)
1012     {
1013       if (hip)
1014         {
1015           clib_warning ("bridge domain %d already mapped to a vni", a->bd_id);
1016           return -1;
1017         }
1018
1019       si = hash_get (l2_ifaces->sw_if_index_by_vni, a->vni);
1020       if (si)
1021         {
1022           clib_warning ("Interface for vni %d already exists", a->vni);
1023           return -1;
1024         }
1025
1026       /* create lisp iface and populate tunnel tables */
1027       hi = create_lisp_gpe_iface (lgm, a->vni, bd_index,
1028                                   &l2_lisp_gpe_device_class, &lgm->l2_ifaces);
1029
1030       /* add iface to l2 bridge domain */
1031       set_int_l2_mode (lgm->vlib_main, vnm, MODE_L2_BRIDGE, hi->sw_if_index,
1032                        bd_index, 0, 0, 0);
1033
1034       /* set egress arcs */
1035 #define _(sym,str) vlib_node_add_named_next_with_slot (vnm->vlib_main, \
1036                     hi->tx_node_index, str, L2_LISP_GPE_TX_NEXT_##sym);
1037       foreach_l2_lisp_gpe_tx_next
1038 #undef _
1039         /* enable interface */
1040         vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
1041                                      VNET_SW_INTERFACE_FLAG_ADMIN_UP);
1042       vnet_hw_interface_set_flags (vnm, hi->hw_if_index,
1043                                    VNET_HW_INTERFACE_FLAG_LINK_UP);
1044     }
1045   else
1046     {
1047       if (hip == 0)
1048         {
1049           clib_warning ("The interface for bridge domain %d doesn't exist",
1050                         a->bd_id);
1051           return -1;
1052         }
1053       remove_lisp_gpe_iface (lgm, hip[0], bd_index, &lgm->l2_ifaces);
1054     }
1055
1056   return 0;
1057 }
1058
1059 int
1060 vnet_lisp_gpe_add_del_iface (vnet_lisp_gpe_add_del_iface_args_t * a,
1061                              u32 * hw_if_indexp)
1062 {
1063   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1064
1065   if (vnet_lisp_gpe_enable_disable_status () == 0)
1066     {
1067       clib_warning ("LISP is disabled!");
1068       return VNET_API_ERROR_LISP_DISABLED;
1069     }
1070
1071   if (!a->is_l2)
1072     return lisp_gpe_add_del_l3_iface (lgm, a);
1073   else
1074     return lisp_gpe_add_del_l2_iface (lgm, a);
1075 }
1076
1077 static clib_error_t *
1078 lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * input,
1079                                    vlib_cli_command_t * cmd)
1080 {
1081   unformat_input_t _line_input, *line_input = &_line_input;
1082   u8 is_add = 1;
1083   clib_error_t *error = 0;
1084   int rv = 0;
1085   u32 table_id, vni, bd_id;
1086   u8 vni_is_set = 0, vrf_is_set = 0, bd_index_is_set = 0;
1087
1088   vnet_lisp_gpe_add_del_iface_args_t _a, *a = &_a;
1089
1090   /* Get a line of input. */
1091   if (!unformat_user (input, unformat_line_input, line_input))
1092     return 0;
1093
1094   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1095     {
1096       if (unformat (line_input, "add"))
1097         is_add = 1;
1098       else if (unformat (line_input, "del"))
1099         is_add = 0;
1100       else if (unformat (line_input, "vrf %d", &table_id))
1101         {
1102           vrf_is_set = 1;
1103         }
1104       else if (unformat (line_input, "vni %d", &vni))
1105         {
1106           vni_is_set = 1;
1107         }
1108       else if (unformat (line_input, "bd %d", &bd_id))
1109         {
1110           bd_index_is_set = 1;
1111         }
1112       else
1113         {
1114           return clib_error_return (0, "parse error: '%U'",
1115                                     format_unformat_error, line_input);
1116         }
1117     }
1118
1119   if (vrf_is_set && bd_index_is_set)
1120     return clib_error_return (0,
1121                               "Cannot set both vrf and brdige domain index!");
1122
1123   if (!vni_is_set)
1124     return clib_error_return (0, "vni must be set!");
1125
1126   if (!vrf_is_set && !bd_index_is_set)
1127     return clib_error_return (0, "vrf or bridge domain index must be set!");
1128
1129   a->is_add = is_add;
1130   a->dp_table = vrf_is_set ? table_id : bd_id;
1131   a->vni = vni;
1132   a->is_l2 = bd_index_is_set;
1133
1134   rv = vnet_lisp_gpe_add_del_iface (a, 0);
1135   if (0 != rv)
1136     {
1137       error = clib_error_return (0, "failed to %s gpe iface!",
1138                                  is_add ? "add" : "delete");
1139     }
1140
1141   return error;
1142 }
1143
1144 /* *INDENT-OFF* */
1145 VLIB_CLI_COMMAND (add_del_lisp_gpe_iface_command, static) = {
1146   .path = "lisp gpe iface",
1147   .short_help = "lisp gpe iface add/del vni <vni> vrf <vrf>",
1148   .function = lisp_gpe_add_del_iface_command_fn,
1149 };
1150 /* *INDENT-ON* */
1151
1152 /*
1153  * fd.io coding-style-patch-verification: ON
1154  *
1155  * Local Variables:
1156  * eval: (c-set-style "gnu")
1157  * End:
1158  */