Move doxytags file to html output directory
[vpp.git] / vnet / vnet / mpls / interface.c
1 /*
2  * interface.c: mpls interfaces
3  *
4  * Copyright (c) 2012 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vnet/vnet.h>
19 #include <vnet/pg/pg.h>
20 #include <vnet/gre/gre.h>
21 #include <vnet/mpls/mpls.h>
22 #include <vnet/fib/ip4_fib.h>
23 #include <vnet/adj/adj_midchain.h>
24 #include <vnet/dpo/classify_dpo.h>
25
26 static uword mpls_gre_set_rewrite (vnet_main_t * vnm,
27                                u32 sw_if_index,
28                                u32 l3_type,
29                                void * dst_address,
30                                void * rewrite,
31                                uword max_rewrite_bytes)
32 {
33   /*
34    * Conundrum: packets from tun/tap destined for the tunnel
35    * actually have this rewrite applied. Transit packets do not.
36    * To make the two cases equivalent, don't generate a
37    * rewrite here, build the entire header in the fast path.
38    */
39   return 0;
40 }
41
42 /* manually added to the interface output node */
43 #define MPLS_GRE_OUTPUT_NEXT_POST_REWRITE       1
44
45 static uword
46 mpls_gre_interface_tx (vlib_main_t * vm,
47                        vlib_node_runtime_t * node,
48                        vlib_frame_t * frame)
49 {
50   mpls_main_t * gm = &mpls_main;
51   vnet_main_t * vnm = gm->vnet_main;
52   u32 next_index;
53   u32 * from, * to_next, n_left_from, n_left_to_next;
54
55   /* Vector of buffer / pkt indices we're supposed to process */
56   from = vlib_frame_vector_args (frame);
57
58   /* Number of buffers / pkts */
59   n_left_from = frame->n_vectors;   
60
61   /* Speculatively send the first buffer to the last disposition we used */
62   next_index = node->cached_next_index;
63   
64   while (n_left_from > 0)
65     {
66       /* set up to enqueue to our disposition with index = next_index */
67       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
68
69       /* 
70        * As long as we have enough pkts left to process two pkts
71        * and prefetch two pkts...
72        */
73       while (n_left_from >= 4 && n_left_to_next >= 2)
74         {
75           vlib_buffer_t * b0, * b1;
76           u32 bi0, next0, bi1, next1;
77           mpls_gre_tunnel_t * t0, * t1;
78           u32 sw_if_index0, sw_if_index1;
79           vnet_hw_interface_t * hi0, * hi1;
80           u8 * dst0, * dst1;
81       
82           /* Prefetch the next iteration */
83           {
84             vlib_buffer_t * p2, * p3;
85
86             p2 = vlib_get_buffer (vm, from[2]);
87             p3 = vlib_get_buffer (vm, from[3]);
88
89             vlib_prefetch_buffer_header (p2, LOAD);
90             vlib_prefetch_buffer_header (p3, LOAD);
91
92             /* 
93              * Prefetch packet data. We expect to overwrite
94              * the inbound L2 header with an ip header and a
95              * gre header. Might want to prefetch the last line
96              * of rewrite space as well; need profile data
97              */
98             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
99             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
100           }
101
102           /* Pick up the next two buffer indices */
103           bi0 = from[0];
104           bi1 = from[1];
105
106           /* Speculatively enqueue them where we sent the last buffer */
107           to_next[0] = bi0;
108           to_next[1] = bi1;
109           from += 2;
110           to_next += 2;
111           n_left_to_next -= 2;
112           n_left_from -= 2;
113       
114           b0 = vlib_get_buffer (vm, bi0);
115           b1 = vlib_get_buffer (vm, bi1);
116
117           sw_if_index0 = vnet_buffer(b0)->sw_if_index [VLIB_TX];
118           sw_if_index1 = vnet_buffer(b1)->sw_if_index [VLIB_TX];
119
120           /* get h/w intfcs */
121           hi0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
122           hi1 = vnet_get_sup_hw_interface (vnm, sw_if_index1);
123           
124           /* hw_instance = tunnel pool index */
125           t0 = pool_elt_at_index (gm->gre_tunnels, hi0->hw_instance);
126           t1 = pool_elt_at_index (gm->gre_tunnels, hi1->hw_instance);
127
128           /* Apply rewrite - $$$$$ fixme don't use memcpy */
129           vlib_buffer_advance (b0, -(word)vec_len(t0->rewrite_data));
130           vlib_buffer_advance (b1, -(word)vec_len(t1->rewrite_data));
131
132           dst0 = vlib_buffer_get_current (b0);
133           dst1 = vlib_buffer_get_current (b1);
134
135           clib_memcpy (dst0, t0->rewrite_data, vec_len(t0->rewrite_data));
136           clib_memcpy (dst1, t1->rewrite_data, vec_len(t1->rewrite_data));
137
138           /* Fix TX fib indices */
139           vnet_buffer(b0)->sw_if_index [VLIB_TX] = t0->outer_fib_index;
140           vnet_buffer(b1)->sw_if_index [VLIB_TX] = t1->outer_fib_index;
141
142           /* mpls-post-rewrite takes it from here... */
143           next0 = MPLS_GRE_OUTPUT_NEXT_POST_REWRITE;
144           next1 = MPLS_GRE_OUTPUT_NEXT_POST_REWRITE;
145
146           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
147             {
148               mpls_gre_tx_trace_t *tr = vlib_add_trace (vm, node, 
149                                                         b0, sizeof (*tr));
150               tr->tunnel_id = t0 - gm->gre_tunnels;
151               tr->length = b0->current_length;
152               tr->src.as_u32 = t0->tunnel_src.as_u32;
153               tr->dst.as_u32 = t0->tunnel_dst.as_u32;
154               tr->lookup_miss = 0;
155               tr->mpls_encap_index = t0->encap_index;
156             }
157           if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) 
158             {
159               mpls_gre_tx_trace_t *tr = vlib_add_trace (vm, node, 
160                                                         b1, sizeof (*tr));
161               tr->tunnel_id = t1 - gm->gre_tunnels;
162               tr->length = b1->current_length;
163               tr->src.as_u32 = t1->tunnel_src.as_u32;
164               tr->dst.as_u32 = t1->tunnel_dst.as_u32;
165               tr->lookup_miss = 0;
166               tr->mpls_encap_index = t1->encap_index;
167             }
168
169           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
170                                            to_next, n_left_to_next,
171                                            bi0, bi1, next0, next1);
172         }
173
174       while (n_left_from > 0 && n_left_to_next > 0)
175         {
176           vlib_buffer_t * b0;
177           u32 bi0, next0;
178           mpls_gre_tunnel_t * t0;
179           u32 sw_if_index0;
180           vnet_hw_interface_t * hi0;
181           u8 * dst0;
182
183           bi0 = from[0];
184           to_next[0] = bi0;
185           from += 1;
186           to_next += 1;
187           n_left_from -= 1;
188           n_left_to_next -= 1;
189
190           b0 = vlib_get_buffer (vm, bi0);
191
192           sw_if_index0 = vnet_buffer(b0)->sw_if_index [VLIB_TX];
193
194           hi0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
195           
196           t0 = pool_elt_at_index (gm->gre_tunnels, hi0->hw_instance);
197
198           /* Apply rewrite - $$$$$ fixme don't use memcpy */
199           vlib_buffer_advance (b0, -(word)vec_len(t0->rewrite_data));
200
201           dst0 = vlib_buffer_get_current (b0);
202
203           clib_memcpy (dst0, t0->rewrite_data, vec_len(t0->rewrite_data));
204
205           /* Fix the TX fib index */
206           vnet_buffer(b0)->sw_if_index [VLIB_TX] = t0->outer_fib_index;
207
208           /* mpls-post-rewrite takes it from here... */
209           next0 = MPLS_GRE_OUTPUT_NEXT_POST_REWRITE;
210
211           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
212             {
213               mpls_gre_tx_trace_t *tr = vlib_add_trace (vm, node, 
214                                                         b0, sizeof (*tr));
215               tr->tunnel_id = t0 - gm->gre_tunnels;
216               tr->length = b0->current_length;
217               tr->src.as_u32 = t0->tunnel_src.as_u32;
218               tr->dst.as_u32 = t0->tunnel_dst.as_u32;
219               tr->lookup_miss = 0;
220               tr->mpls_encap_index = t0->encap_index;
221             }
222
223           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
224                                            to_next, n_left_to_next,
225                                            bi0, next0);
226         }
227   
228       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
229     }
230
231   vlib_node_increment_counter (vm, gre_input_node.index,
232                                GRE_ERROR_PKTS_ENCAP, frame->n_vectors);
233
234   return frame->n_vectors;
235 }
236
237 static u8 * format_mpls_gre_tunnel_name (u8 * s, va_list * args)
238 {
239   u32 dev_instance = va_arg (*args, u32);
240   return format (s, "mpls-gre%d", dev_instance);
241 }
242
243 static u8 * format_mpls_gre_device (u8 * s, va_list * args)
244 {
245   u32 dev_instance = va_arg (*args, u32);
246   CLIB_UNUSED (int verbose) = va_arg (*args, int);
247
248   s = format (s, "MPLS-GRE tunnel: id %d\n", dev_instance);
249   return s;
250 }
251
252 VNET_DEVICE_CLASS (mpls_gre_device_class) = {
253   .name = "MPLS-GRE tunnel device",
254   .format_device_name = format_mpls_gre_tunnel_name,
255   .format_device = format_mpls_gre_device,
256   .format_tx_trace = format_mpls_gre_tx_trace,
257   .tx_function = mpls_gre_interface_tx,
258   .no_flatten_output_chains = 1,
259 #ifdef SOON
260   .clear counter = 0;
261   .admin_up_down_function = 0;
262 #endif
263 };
264
265 VLIB_DEVICE_TX_FUNCTION_MULTIARCH (mpls_gre_device_class,
266                                    mpls_gre_interface_tx)
267
268 VNET_HW_INTERFACE_CLASS (mpls_gre_hw_interface_class) = {
269   .name = "MPLS-GRE",
270   .format_header = format_mpls_gre_header_with_length,
271 #if 0
272   .unformat_header = unformat_mpls_gre_header,
273 #endif
274   .set_rewrite = mpls_gre_set_rewrite,
275 };
276
277
278 static uword mpls_eth_set_rewrite (vnet_main_t * vnm,
279                                u32 sw_if_index,
280                                u32 l3_type,
281                                void * dst_address,
282                                void * rewrite,
283                                uword max_rewrite_bytes)
284 {
285   /*
286    * Conundrum: packets from tun/tap destined for the tunnel
287    * actually have this rewrite applied. Transit packets do not.
288    * To make the two cases equivalent, don't generate a
289    * rewrite here, build the entire header in the fast path.
290    */
291   return 0;
292 }
293
294 /* manually added to the interface output node */
295 #define MPLS_ETH_OUTPUT_NEXT_OUTPUT     1
296
297 static uword
298 mpls_eth_interface_tx (vlib_main_t * vm,
299                        vlib_node_runtime_t * node,
300                        vlib_frame_t * frame)
301 {
302   mpls_main_t * gm = &mpls_main;
303   vnet_main_t * vnm = gm->vnet_main;
304   u32 next_index;
305   u32 * from, * to_next, n_left_from, n_left_to_next;
306
307   /* Vector of buffer / pkt indices we're supposed to process */
308   from = vlib_frame_vector_args (frame);
309
310   /* Number of buffers / pkts */
311   n_left_from = frame->n_vectors;   
312
313   /* Speculatively send the first buffer to the last disposition we used */
314   next_index = node->cached_next_index;
315   
316   while (n_left_from > 0)
317     {
318       /* set up to enqueue to our disposition with index = next_index */
319       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
320
321       /* 
322        * As long as we have enough pkts left to process two pkts
323        * and prefetch two pkts...
324        */
325       while (n_left_from >= 4 && n_left_to_next >= 2)
326         {
327           vlib_buffer_t * b0, * b1;
328           u32 bi0, next0, bi1, next1;
329           mpls_eth_tunnel_t * t0, * t1;
330           u32 sw_if_index0, sw_if_index1;
331           vnet_hw_interface_t * hi0, * hi1;
332           u8 * dst0, * dst1;
333       
334           /* Prefetch the next iteration */
335           {
336             vlib_buffer_t * p2, * p3;
337
338             p2 = vlib_get_buffer (vm, from[2]);
339             p3 = vlib_get_buffer (vm, from[3]);
340
341             vlib_prefetch_buffer_header (p2, LOAD);
342             vlib_prefetch_buffer_header (p3, LOAD);
343
344             /* 
345              * Prefetch packet data. We expect to overwrite
346              * the inbound L2 header with an ip header and a
347              * gre header. Might want to prefetch the last line
348              * of rewrite space as well; need profile data
349              */
350             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
351             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
352           }
353
354           /* Pick up the next two buffer indices */
355           bi0 = from[0];
356           bi1 = from[1];
357
358           /* Speculatively enqueue them where we sent the last buffer */
359           to_next[0] = bi0;
360           to_next[1] = bi1;
361           from += 2;
362           to_next += 2;
363           n_left_to_next -= 2;
364           n_left_from -= 2;
365       
366           b0 = vlib_get_buffer (vm, bi0);
367           b1 = vlib_get_buffer (vm, bi1);
368
369           sw_if_index0 = vnet_buffer(b0)->sw_if_index [VLIB_TX];
370           sw_if_index1 = vnet_buffer(b1)->sw_if_index [VLIB_TX];
371
372           /* get h/w intfcs */
373           hi0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
374           hi1 = vnet_get_sup_hw_interface (vnm, sw_if_index1);
375           
376           /* hw_instance = tunnel pool index */
377           t0 = pool_elt_at_index (gm->eth_tunnels, hi0->hw_instance);
378           t1 = pool_elt_at_index (gm->eth_tunnels, hi1->hw_instance);
379
380           /* Apply rewrite - $$$$$ fixme don't use memcpy */
381           vlib_buffer_advance (b0, -(word)vec_len(t0->rewrite_data));
382           vlib_buffer_advance (b1, -(word)vec_len(t1->rewrite_data));
383
384           dst0 = vlib_buffer_get_current (b0);
385           dst1 = vlib_buffer_get_current (b1);
386
387           clib_memcpy (dst0, t0->rewrite_data, vec_len(t0->rewrite_data));
388           clib_memcpy (dst1, t1->rewrite_data, vec_len(t1->rewrite_data));
389
390           /* Fix TX fib indices */
391           vnet_buffer(b0)->sw_if_index [VLIB_TX] = t0->tx_sw_if_index;
392           vnet_buffer(b1)->sw_if_index [VLIB_TX] = t1->tx_sw_if_index;
393
394           /* mpls-post-rewrite takes it from here... */
395           next0 = MPLS_ETH_OUTPUT_NEXT_OUTPUT;
396           next1 = MPLS_ETH_OUTPUT_NEXT_OUTPUT;
397
398           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
399             {
400               mpls_eth_tx_trace_t *tr = vlib_add_trace (vm, node, 
401                                                         b0, sizeof (*tr));
402               tr->lookup_miss = 0;
403               tr->tunnel_id = t0 - gm->eth_tunnels;
404               tr->tx_sw_if_index = t0->tx_sw_if_index;
405               tr->mpls_encap_index = t0->encap_index;
406               tr->length = b0->current_length;
407               hi0 = vnet_get_sup_hw_interface (vnm, t0->tx_sw_if_index);
408               clib_memcpy (tr->dst, hi0->hw_address, sizeof (tr->dst));
409             }
410           if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) 
411             {
412               mpls_eth_tx_trace_t *tr = vlib_add_trace (vm, node, 
413                                                         b1, sizeof (*tr));
414               tr->lookup_miss = 0;
415               tr->tunnel_id = t1 - gm->eth_tunnels;
416               tr->tx_sw_if_index = t1->tx_sw_if_index;
417               tr->mpls_encap_index = t1->encap_index;
418               tr->length = b1->current_length;
419               hi1 = vnet_get_sup_hw_interface (vnm, t1->tx_sw_if_index);
420               clib_memcpy (tr->dst, hi1->hw_address, sizeof (tr->dst));
421             }
422
423           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
424                                            to_next, n_left_to_next,
425                                            bi0, bi1, next0, next1);
426         }
427       while (n_left_from > 0 && n_left_to_next > 0)
428         {
429           vlib_buffer_t * b0;
430           u32 bi0, next0;
431           mpls_eth_tunnel_t * t0;
432           u32 sw_if_index0;
433           vnet_hw_interface_t * hi0;
434           u8 * dst0;
435
436           bi0 = from[0];
437           to_next[0] = bi0;
438           from += 1;
439           to_next += 1;
440           n_left_from -= 1;
441           n_left_to_next -= 1;
442
443           b0 = vlib_get_buffer (vm, bi0);
444
445           sw_if_index0 = vnet_buffer(b0)->sw_if_index [VLIB_TX];
446
447           hi0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
448           
449           t0 = pool_elt_at_index (gm->eth_tunnels, hi0->hw_instance);
450
451           /* Apply rewrite - $$$$$ fixme don't use memcpy */
452           vlib_buffer_advance (b0, -(word)vec_len(t0->rewrite_data));
453
454           dst0 = vlib_buffer_get_current (b0);
455
456           clib_memcpy (dst0, t0->rewrite_data, vec_len(t0->rewrite_data));
457
458           /* Fix the TX interface */
459           vnet_buffer(b0)->sw_if_index [VLIB_TX] = t0->tx_sw_if_index;
460
461           /* Send the packet */
462           next0 = MPLS_ETH_OUTPUT_NEXT_OUTPUT;
463
464           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
465             {
466               mpls_eth_tx_trace_t *tr = vlib_add_trace (vm, node, 
467                                                         b0, sizeof (*tr));
468               tr->lookup_miss = 0;
469               tr->tunnel_id = t0 - gm->eth_tunnels;
470               tr->tx_sw_if_index = t0->tx_sw_if_index;
471               tr->mpls_encap_index = t0->encap_index;
472               tr->length = b0->current_length;
473               hi0 = vnet_get_sup_hw_interface (vnm, t0->tx_sw_if_index);
474               clib_memcpy (tr->dst, hi0->hw_address, sizeof (tr->dst));
475             }
476
477           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
478                                            to_next, n_left_to_next,
479                                            bi0, next0);
480         }
481   
482       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
483     }
484
485   vlib_node_increment_counter (vm, mpls_input_node.index,
486                                MPLS_ERROR_PKTS_ENCAP, frame->n_vectors);
487
488   return frame->n_vectors;
489 }
490
491 static u8 * format_mpls_eth_tunnel_name (u8 * s, va_list * args)
492 {
493   u32 dev_instance = va_arg (*args, u32);
494   return format (s, "mpls-eth%d", dev_instance);
495 }
496
497 static u8 * format_mpls_eth_device (u8 * s, va_list * args)
498 {
499   u32 dev_instance = va_arg (*args, u32);
500   CLIB_UNUSED (int verbose) = va_arg (*args, int);
501
502   s = format (s, "MPLS-ETH tunnel: id %d\n", dev_instance);
503   return s;
504 }
505
506 VNET_DEVICE_CLASS (mpls_eth_device_class) = {
507   .name = "MPLS-ETH tunnel device",
508   .format_device_name = format_mpls_eth_tunnel_name,
509   .format_device = format_mpls_eth_device,
510   .format_tx_trace = format_mpls_eth_tx_trace,
511   .tx_function = mpls_eth_interface_tx,
512   .no_flatten_output_chains = 1,
513 #ifdef SOON
514   .clear counter = 0;
515   .admin_up_down_function = 0;
516 #endif
517 };
518
519 VLIB_DEVICE_TX_FUNCTION_MULTIARCH (mpls_eth_device_class,
520                                    mpls_eth_interface_tx)
521
522 VNET_HW_INTERFACE_CLASS (mpls_eth_hw_interface_class) = {
523   .name = "MPLS-ETH",
524   .format_header = format_mpls_eth_header_with_length,
525 #if 0
526   .unformat_header = unformat_mpls_eth_header,
527 #endif
528   .set_rewrite = mpls_eth_set_rewrite,
529 };
530
531 /**
532  * A conversion of DPO next object tpyes to VLIB graph next nodes from
533  * the mpls_post_rewrite node
534  */
535 static const int dpo_next_2_mpls_post_rewrite[DPO_LAST] = {
536     [DPO_LOAD_BALANCE] = IP_LOOKUP_NEXT_LOAD_BALANCE,
537 };
538
539 static uword
540 mpls_post_rewrite (vlib_main_t * vm,
541                    vlib_node_runtime_t * node,
542                    vlib_frame_t * from_frame)
543 {
544   ip4_main_t * im = &ip4_main;
545   ip_lookup_main_t * lm = &im->lookup_main;
546   u32 n_left_from, next_index, * from, * to_next;
547   u16 old_l0 = 0; //, old_l1 = 0;
548
549   from = vlib_frame_vector_args (from_frame);
550   n_left_from = from_frame->n_vectors;
551
552   next_index = node->cached_next_index;
553
554   while (n_left_from > 0)
555     {
556       u32 n_left_to_next;
557
558       vlib_get_next_frame (vm, node, next_index,
559                            to_next, n_left_to_next);
560
561       /* while (n_left_from >= 4 && n_left_to_next >= 2) */
562       /*        { */
563       /*          u32 bi0, bi1; */
564       /*          vlib_buffer_t * b0, * b1; */
565       /*     ip4_header_t * ip0, * ip1; */
566       /*          u32 next0; */
567       /*          u32 next1; */
568       /*     u16 new_l0, new_l1, adj_index0, adj_index1; */
569       /*     ip_csum_t sum0, sum1; */
570       /*          ip_adjacency_t *adj0, *adj1; */
571
572       /*          /\* Prefetch next iteration. *\/ */
573       /*          { */
574       /*            vlib_buffer_t * p2, * p3; */
575
576       /*            p2 = vlib_get_buffer (vm, from[2]); */
577       /*            p3 = vlib_get_buffer (vm, from[3]); */
578
579       /*            vlib_prefetch_buffer_header (p2, LOAD); */
580       /*            vlib_prefetch_buffer_header (p3, LOAD); */
581
582       /*            CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); */
583       /*            CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); */
584       /*          } */
585
586       /*          bi0 = from[0]; */
587       /*          bi1 = from[1]; */
588       /*          to_next[0] = bi0; */
589       /*          to_next[1] = bi1; */
590       /*          from += 2; */
591       /*          to_next += 2; */
592       /*          n_left_to_next -= 2; */
593       /*          n_left_from -= 2; */
594
595
596       /*          b0 = vlib_get_buffer (vm, bi0); */
597       /*          b1 = vlib_get_buffer (vm, bi1); */
598       /*     ip0 = vlib_buffer_get_current (b0); */
599       /*     ip1 = vlib_buffer_get_current (b1); */
600           
601       /*     /\* Note: the tunnel rewrite sets up sw_if_index[VLIB_TX] *\/ */
602
603       /*     /\* set the GRE (outer) ip packet length, fix the bloody checksum *\/ */
604       /*     sum0 = ip0->checksum; */
605       /*     sum1 = ip1->checksum; */
606
607       /*     /\* old_l0, old_l1 always 0, see the rewrite setup *\/ */
608       /*     new_l0 =  */
609       /*       clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); */
610       /*     new_l1 =  */
611       /*       clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)); */
612           
613       /*     sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t, */
614       /*                            length /\* changed member *\/); */
615       /*     sum1 = ip_csum_update (sum1, old_l1, new_l1, ip4_header_t, */
616       /*                            length /\* changed member *\/); */
617       /*     ip0->checksum = ip_csum_fold (sum0); */
618       /*     ip1->checksum = ip_csum_fold (sum1); */
619       /*     ip0->length = new_l0; */
620       /*     ip1->length = new_l1; */
621
622       /*          /\* replace the TX adj in the packet with the next in the chain *\/ */
623       /*          adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; */
624       /*          adj_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX]; */
625
626       /*          adj0 = ip_get_adjacency (lm, adj_index0); */
627       /*          adj1 = ip_get_adjacency (lm, adj_index1); */
628
629       /*          ASSERT(adj0->sub_type.midchain.adj_index != ADJ_INDEX_INVALID); */
630       /*          ASSERT(adj1->sub_type.midchain.adj_index != ADJ_INDEX_INVALID); */
631
632       /*          adj_index0 = adj0->sub_type.midchain.adj_index; */
633       /*          adj_index1 = adj1->sub_type.midchain.adj_index; */
634
635       /*          vnet_buffer (b0)->ip.adj_index[VLIB_TX] = adj_index0; */
636       /*          vnet_buffer (b1)->ip.adj_index[VLIB_TX] = adj_index1; */
637
638       /*          /\* get the next adj in the chain to determine the next graph node *\/ */
639       /*          adj0 = ip_get_adjacency (lm, adj_index0); */
640       /*          adj1 = ip_get_adjacency (lm, adj_index1); */
641
642       /*          next0 = adj0->lookup_next_index; */
643       /*          next1 = adj1->lookup_next_index; */
644
645       /*          vlib_validate_buffer_enqueue_x2 (vm, node, next_index, */
646       /*                                           to_next, n_left_to_next, */
647       /*                                           bi0, bi1, next0, next1); */
648       /*        } */
649
650       while (n_left_from > 0 && n_left_to_next > 0)
651         {
652           ip_adjacency_t * adj0;
653           u32 bi0;
654           vlib_buffer_t * b0;
655           ip4_header_t * ip0;
656           u32 next0;
657           u16 new_l0, adj_index0;
658           ip_csum_t sum0;
659
660           bi0 = from[0];
661           to_next[0] = bi0;
662           from += 1;
663           to_next += 1;
664           n_left_from -= 1;
665           n_left_to_next -= 1;
666
667           b0 = vlib_get_buffer (vm, bi0);
668           ip0 = vlib_buffer_get_current (b0);
669           
670           /* Note: the tunnel rewrite sets up sw_if_index[VLIB_TX] */
671
672           /* set the GRE (outer) ip packet length, fix the bloody checksum */
673           sum0 = ip0->checksum;
674           /* old_l0 always 0, see the rewrite setup */
675           new_l0 = 
676             clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
677           
678           sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
679                                  length /* changed member */);
680           ip0->checksum = ip_csum_fold (sum0);
681           ip0->length = new_l0;
682
683           /* replace the TX adj in the packet with the next in the chain */
684           adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
685
686           ASSERT(adj_index0);
687
688           adj0 = ip_get_adjacency (lm, adj_index0);
689           ASSERT(adj0->sub_type.midchain.next_dpo.dpoi_index != ADJ_INDEX_INVALID);
690           adj_index0 = adj0->sub_type.midchain.next_dpo.dpoi_index;
691           vnet_buffer (b0)->ip.adj_index[VLIB_TX] = adj_index0;
692
693           /* get the next adj in the chain to determine the next graph node */
694           ASSERT(0);
695           next0 = 0; //adj0->sub_type.midchain.next_dpo.dpoi_next;
696
697           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
698                                            to_next, n_left_to_next,
699                                            bi0, next0);
700         }
701
702       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
703     }
704   vlib_node_increment_counter (vm, mpls_input_node.index,
705                                MPLS_ERROR_PKTS_ENCAP, from_frame->n_vectors);
706   return from_frame->n_vectors;
707 }
708
709 VLIB_REGISTER_NODE (mpls_post_rewrite_node) = {
710   .function = mpls_post_rewrite,
711   .name = "mpls-post-rewrite",
712   /* Takes a vector of packets. */
713   .vector_size = sizeof (u32),
714
715   .runtime_data_bytes = 0,
716
717   .n_next_nodes = IP_LOOKUP_N_NEXT,
718   .next_nodes = IP4_LOOKUP_NEXT_NODES,
719 };
720
721 VLIB_NODE_FUNCTION_MULTIARCH (mpls_post_rewrite_node, mpls_post_rewrite)
722
723 static u8 * mpls_gre_rewrite (mpls_main_t *mm, mpls_gre_tunnel_t * t)
724 {
725   ip4_header_t * ip0;
726   ip4_gre_and_mpls_header_t * h0;
727   u8 * rewrite_data = 0;
728   mpls_encap_t * e;
729   mpls_unicast_header_t *lp0;
730   int i;
731
732  /* look up the encap label stack using the RX FIB */
733   e = mpls_encap_by_fib_and_dest (mm, t->inner_fib_index, t->tunnel_dst.as_u32);
734
735   if (e == 0)
736     {
737       clib_warning ("no label for inner fib index %d, dst %U",
738                     t->inner_fib_index, format_ip4_address, 
739                     &t->tunnel_dst);
740       return 0;
741     }
742  
743   vec_validate (rewrite_data, sizeof (*h0) 
744                 + sizeof (mpls_unicast_header_t) * vec_len(e->labels) -1);
745   memset (rewrite_data, 0, sizeof (*h0));
746
747   h0 = (ip4_gre_and_mpls_header_t *) rewrite_data;
748   /* Copy the encap label stack */
749   lp0 = h0->labels;
750   for (i = 0; i < vec_len(e->labels); i++)
751     lp0[i] = e->labels[i];
752   ip0 = &h0->ip4;
753   h0->gre.protocol = clib_host_to_net_u16(GRE_PROTOCOL_mpls_unicast);
754   ip0->ip_version_and_header_length = 0x45;
755   ip0->ttl = 254;
756   ip0->protocol = IP_PROTOCOL_GRE;
757   /* $$$ fixup ip4 header length and checksum after-the-fact */
758   ip0->src_address.as_u32 = t->tunnel_src.as_u32;
759   ip0->dst_address.as_u32 = t->tunnel_dst.as_u32;
760   ip0->checksum = ip4_header_checksum (ip0);
761
762   return (rewrite_data);
763 }
764
765 u8
766 mpls_sw_interface_is_enabled (u32 sw_if_index)
767 {
768     mpls_main_t * mm = &mpls_main;
769
770     if (vec_len(mm->mpls_enabled_by_sw_if_index) < sw_if_index)
771         return (0);
772
773     return (mm->mpls_enabled_by_sw_if_index[sw_if_index]);
774 }
775
776 void
777 mpls_sw_interface_enable_disable (mpls_main_t * mm,
778                                   u32 sw_if_index,
779                                   u8 is_enable)
780 {
781   mpls_interface_state_change_callback_t *callback;
782   vlib_main_t * vm = vlib_get_main();
783   ip_config_main_t * cm = &mm->rx_config_mains;
784   vnet_config_main_t * vcm = &cm->config_main;
785   u32 lookup_feature_index;
786   fib_node_index_t lfib_index;
787   u32 ci;
788
789   vec_validate_init_empty (mm->mpls_enabled_by_sw_if_index, sw_if_index, 0);
790
791   /*
792    * enable/disable only on the 1<->0 transition
793    */
794   if (is_enable)
795     {
796       if (1 != ++mm->mpls_enabled_by_sw_if_index[sw_if_index])
797         return;
798
799       lfib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_MPLS,
800                                                      MPLS_FIB_DEFAULT_TABLE_ID);
801       vec_validate(mm->fib_index_by_sw_if_index, 0);
802       mm->fib_index_by_sw_if_index[sw_if_index] = lfib_index;
803     }
804   else
805     {
806       ASSERT(mm->mpls_enabled_by_sw_if_index[sw_if_index] > 0);
807       if (0 != --mm->mpls_enabled_by_sw_if_index[sw_if_index])
808         return;
809
810       fib_table_unlock(mm->fib_index_by_sw_if_index[sw_if_index],
811                        FIB_PROTOCOL_MPLS);
812     }
813
814   vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0);
815   ci = cm->config_index_by_sw_if_index[sw_if_index];
816
817   lookup_feature_index = mm->mpls_rx_feature_lookup;
818
819   if (is_enable)
820     ci = vnet_config_add_feature (vm, vcm,
821                                   ci,
822                                   lookup_feature_index,
823                                   /* config data */ 0,
824                                   /* # bytes of config data */ 0);
825   else
826     ci = vnet_config_del_feature (vm, vcm, ci,
827                                   lookup_feature_index,
828                                   /* config data */ 0,
829                                   /* # bytes of config data */ 0);
830
831   cm->config_index_by_sw_if_index[sw_if_index] = ci;
832
833   /*
834    * notify all interested clients of the change of state.
835    */
836   vec_foreach(callback, mm->mpls_interface_state_change_callbacks)
837   {
838       (*callback)(sw_if_index, is_enable);
839   }
840 }
841
842 static mpls_gre_tunnel_t *
843 mpls_gre_tunnel_from_fib_node (fib_node_t *node)
844 {
845 #if (CLIB_DEBUG > 0)
846     ASSERT(FIB_NODE_TYPE_MPLS_GRE_TUNNEL == node->fn_type);
847 #endif
848     return ((mpls_gre_tunnel_t*)node);
849 }
850
851 /*
852  * mpls_gre_tunnel_stack
853  *
854  * 'stack' (resolve the recursion for) the tunnel's midchain adjacency
855  */
856 static void
857 mpls_gre_tunnel_stack (mpls_gre_tunnel_t *mgt)
858 {
859     /*
860      * find the adjacency that is contributed by the FIB entry
861      * that this tunnel resovles via, and use it as the next adj
862      * in the midchain
863      */
864     adj_nbr_midchain_stack(mgt->adj_index,
865                            fib_entry_contribute_ip_forwarding(mgt->fei));
866 }
867
868 /**
869  * Function definition to backwalk a FIB node
870  */
871 static fib_node_back_walk_rc_t
872 mpls_gre_tunnel_back_walk (fib_node_t *node,
873                            fib_node_back_walk_ctx_t *ctx)
874 {
875     mpls_gre_tunnel_stack(mpls_gre_tunnel_from_fib_node(node));
876
877     return (FIB_NODE_BACK_WALK_CONTINUE);
878 }
879
880 /**
881  * Function definition to get a FIB node from its index
882  */
883 static fib_node_t*
884 mpls_gre_tunnel_fib_node_get (fib_node_index_t index)
885 {
886     mpls_gre_tunnel_t * mgt;
887     mpls_main_t * mm;
888
889     mm  = &mpls_main;
890     mgt = pool_elt_at_index(mm->gre_tunnels, index);
891
892     return (&mgt->mgt_node);
893 }
894
895 /**
896  * Function definition to inform the FIB node that its last lock has gone.
897  */
898 static void
899 mpls_gre_tunnel_last_lock_gone (fib_node_t *node)
900 {
901     /*
902      * The MPLS GRE tunnel is a root of the graph. As such
903      * it never has children and thus is never locked.
904      */
905     ASSERT(0);
906 }
907
908 /*
909  * Virtual function table registered by MPLS GRE tunnels
910  * for participation in the FIB object graph.
911  */
912 const static fib_node_vft_t mpls_gre_vft = {
913     .fnv_get = mpls_gre_tunnel_fib_node_get,
914     .fnv_last_lock = mpls_gre_tunnel_last_lock_gone,
915     .fnv_back_walk = mpls_gre_tunnel_back_walk,
916 };
917  
918 static mpls_gre_tunnel_t *
919 mpls_gre_tunnel_find (ip4_address_t *src,
920                       ip4_address_t *dst,
921                       ip4_address_t *intfc,
922                       u32 inner_fib_index)
923 {
924     mpls_main_t * mm = &mpls_main;
925     mpls_gre_tunnel_t *tp;
926     int found_tunnel = 0;
927
928     /* suppress duplicate mpls interface generation. */
929     pool_foreach (tp, mm->gre_tunnels, 
930     ({
931         /* 
932          * If we have a tunnel which matches (src, dst, intfc/mask)
933          * AND the expected route is in the FIB, it's a dup 
934          */
935         if (!memcmp (&tp->tunnel_src, src, sizeof (*src))
936             && !memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
937             && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
938             && tp->inner_fib_index == inner_fib_index) 
939         {
940             found_tunnel = 1;
941             goto found;
942         }
943     }));
944
945 found:
946     if (found_tunnel)
947     {
948         return (tp);
949     }
950     return (NULL);
951 }
952
953 int mpls_gre_tunnel_add (ip4_address_t *src,
954                          ip4_address_t *dst,
955                          ip4_address_t *intfc,
956                          u32 mask_width,
957                          u32 inner_fib_index,
958                          u32 outer_fib_index,
959                          u32 * tunnel_sw_if_index,
960                          u8 l2_only)
961 {
962     mpls_main_t * mm = &mpls_main;
963     gre_main_t * gm = &gre_main;
964     vnet_main_t * vnm = vnet_get_main();
965     mpls_gre_tunnel_t *tp;
966     ip_adjacency_t adj;
967     u8 * rewrite_data;
968     mpls_encap_t * e = 0;
969     u32 hw_if_index = ~0;
970     vnet_hw_interface_t * hi;
971     u32 slot;
972     const ip46_address_t zero_nh = {
973         .ip4.as_u32 = 0,
974     };
975
976     tp = mpls_gre_tunnel_find(src,dst,intfc,inner_fib_index);
977
978     /* Add, duplicate */
979     if (NULL != tp)
980         return VNET_API_ERROR_NO_SUCH_ENTRY;
981
982     e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, dst->as_u32);
983     if (e == 0)
984         return VNET_API_ERROR_NO_SUCH_LABEL;
985
986     pool_get(mm->gre_tunnels, tp);
987     memset (tp, 0, sizeof (*tp));
988     fib_node_init(&tp->mgt_node,
989                   FIB_NODE_TYPE_MPLS_GRE_TUNNEL);
990
991     if (vec_len (mm->free_gre_sw_if_indices) > 0)
992     {
993         hw_if_index = 
994             mm->free_gre_sw_if_indices[vec_len(mm->free_gre_sw_if_indices)-1];
995         _vec_len (mm->free_gre_sw_if_indices) -= 1;
996         hi = vnet_get_hw_interface (vnm, hw_if_index);
997         hi->dev_instance = tp - mm->gre_tunnels;
998         hi->hw_instance = tp - mm->gre_tunnels;
999     }
1000     else 
1001     {
1002         hw_if_index = vnet_register_interface
1003             (vnm, mpls_gre_device_class.index, tp - mm->gre_tunnels,
1004              mpls_gre_hw_interface_class.index,
1005              tp - mm->gre_tunnels);
1006         hi = vnet_get_hw_interface (vnm, hw_if_index);
1007
1008         /* ... to make the IP and L2 x-connect cases identical */
1009         slot = vlib_node_add_named_next_with_slot
1010             (vnm->vlib_main, hi->tx_node_index, 
1011              "mpls-post-rewrite", MPLS_GRE_OUTPUT_NEXT_POST_REWRITE);
1012
1013         ASSERT (slot == MPLS_GRE_OUTPUT_NEXT_POST_REWRITE);
1014     }
1015   
1016     *tunnel_sw_if_index = hi->sw_if_index;
1017     vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1018                                  VNET_SW_INTERFACE_FLAG_ADMIN_UP);      
1019     vec_validate(ip4_main.fib_index_by_sw_if_index, *tunnel_sw_if_index);
1020     ip4_main.fib_index_by_sw_if_index[*tunnel_sw_if_index] = outer_fib_index;
1021
1022     tp->hw_if_index = hw_if_index;
1023
1024     /* bind the MPLS and IPv4 FIBs to the interface and enable */
1025     vec_validate(mm->fib_index_by_sw_if_index, hi->sw_if_index);
1026     mm->fib_index_by_sw_if_index[hi->sw_if_index] = inner_fib_index;
1027     mpls_sw_interface_enable_disable(mm, hi->sw_if_index, 1);
1028     ip4_main.fib_index_by_sw_if_index[hi->sw_if_index] = inner_fib_index;
1029     ip4_sw_interface_enable_disable(hi->sw_if_index, 1);
1030
1031     tp->tunnel_src.as_u32 = src->as_u32;
1032     tp->tunnel_dst.as_u32 = dst->as_u32;
1033     tp->intfc_address.as_u32 = intfc->as_u32;
1034     tp->mask_width = mask_width;
1035     tp->inner_fib_index = inner_fib_index;
1036     tp->outer_fib_index = outer_fib_index;
1037     tp->encap_index = e - mm->encaps;
1038     tp->l2_only = l2_only;
1039
1040     /* Add the tunnel to the hash table of all GRE tunnels */
1041     u64 key = (u64)src->as_u32 << 32 | (u64)dst->as_u32;
1042
1043     ASSERT(NULL == hash_get (gm->tunnel_by_key, key));
1044     hash_set (gm->tunnel_by_key, key, tp - mm->gre_tunnels);
1045
1046     /* Create the adjacency and add to v4 fib */
1047     memset(&adj, 0, sizeof (adj));
1048     adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
1049     
1050     rewrite_data = mpls_gre_rewrite (mm, tp);
1051     if (rewrite_data == 0)
1052     {
1053         if (*tunnel_sw_if_index != ~0)
1054         {
1055             hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1056             vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1057                                          0 /* admin down */);
1058             vec_add1 (mm->free_gre_sw_if_indices, tp->hw_if_index);
1059         }
1060         pool_put (mm->gre_tunnels, tp);
1061         return VNET_API_ERROR_NO_SUCH_LABEL;
1062     }
1063
1064     /* Save a copy of the rewrite data for L2 x-connect */
1065     vec_free (tp->rewrite_data);
1066
1067     tp->rewrite_data = rewrite_data;
1068   
1069     if (!l2_only)
1070     {
1071         /*
1072          * source the FIB entry for the tunnel's destination
1073          * and become a child thereof. The tunnel will then get poked
1074          * when the forwarding for the entry updates, and the tunnel can
1075          * re-stack accordingly
1076          */
1077         const fib_prefix_t tun_dst_pfx = {
1078             .fp_len = 32,
1079             .fp_proto = FIB_PROTOCOL_IP4,
1080             .fp_addr = {
1081                 .ip4 = *dst,
1082             }
1083         };
1084
1085         tp->fei = fib_table_entry_special_add(outer_fib_index,
1086                                               &tun_dst_pfx,
1087                                               FIB_SOURCE_RR,
1088                                               FIB_ENTRY_FLAG_NONE,
1089                                               ADJ_INDEX_INVALID);
1090         tp->sibling_index = fib_entry_child_add(tp->fei,
1091                                                 FIB_NODE_TYPE_MPLS_GRE_TUNNEL,
1092                                                 tp - mm->gre_tunnels);
1093
1094         /*
1095          * create and update the midchain adj this tunnel sources.
1096          * This is the adj the route we add below will resolve to.
1097          */
1098         tp->adj_index = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
1099                                             FIB_LINK_IP4,
1100                                             &zero_nh,
1101                                             hi->sw_if_index);
1102
1103         adj_nbr_midchain_update_rewrite(tp->adj_index,
1104                                         mpls_post_rewrite_node.index,
1105                                         rewrite_data);
1106         mpls_gre_tunnel_stack(tp);
1107
1108         /*
1109          * Update the route for the tunnel's subnet to point through the tunnel
1110          */
1111         const fib_prefix_t tun_sub_net_pfx = {
1112             .fp_len = tp->mask_width,
1113             .fp_proto = FIB_PROTOCOL_IP4,
1114             .fp_addr = {
1115                 .ip4 = tp->intfc_address,
1116             },
1117         };
1118
1119         fib_table_entry_update_one_path(inner_fib_index,
1120                                         &tun_sub_net_pfx,
1121                                         FIB_SOURCE_INTERFACE,
1122                                         (FIB_ENTRY_FLAG_CONNECTED |
1123                                          FIB_ENTRY_FLAG_ATTACHED),
1124                                         FIB_PROTOCOL_IP4,
1125                                         &zero_nh,
1126                                         hi->sw_if_index,
1127                                         ~0, // invalid fib index
1128                                         1,
1129                                         MPLS_LABEL_INVALID,
1130                                         FIB_ROUTE_PATH_FLAG_NONE);
1131     }
1132
1133     return 0;
1134 }
1135
1136 static int
1137 mpls_gre_tunnel_del (ip4_address_t *src,
1138                      ip4_address_t *dst,
1139                      ip4_address_t *intfc,
1140                      u32 mask_width,
1141                      u32 inner_fib_index,
1142                      u32 outer_fib_index,
1143                      u32 * tunnel_sw_if_index,
1144                      u8 l2_only)
1145 {
1146     mpls_main_t * mm = &mpls_main;
1147     vnet_main_t * vnm = vnet_get_main();
1148     gre_main_t * gm = &gre_main;
1149     mpls_gre_tunnel_t *tp;
1150     vnet_hw_interface_t * hi;
1151   
1152     tp = mpls_gre_tunnel_find(src,dst,intfc,inner_fib_index);
1153
1154     /* Delete, and we can't find the tunnel */
1155     if (NULL == tp)
1156         return VNET_API_ERROR_NO_SUCH_ENTRY;
1157
1158     hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1159
1160     if (!l2_only)
1161     {
1162         /*
1163          * unsource the FIB entry for the tunnel's destination
1164          */
1165         const fib_prefix_t tun_dst_pfx = {
1166             .fp_len = 32,
1167             .fp_proto = FIB_PROTOCOL_IP4,
1168             .fp_addr = {
1169                 .ip4 = *dst,
1170             }
1171         };
1172
1173         fib_entry_child_remove(tp->fei,
1174                                tp->sibling_index);
1175         fib_table_entry_special_remove(outer_fib_index,
1176                                        &tun_dst_pfx,
1177                                        FIB_SOURCE_RR);
1178         tp->fei = FIB_NODE_INDEX_INVALID;
1179         adj_unlock(tp->adj_index);
1180  
1181         /*
1182          * unsource the route for the tunnel's subnet
1183          */
1184         const fib_prefix_t tun_sub_net_pfx = {
1185             .fp_len = tp->mask_width,
1186             .fp_proto = FIB_PROTOCOL_IP4,
1187             .fp_addr = {
1188                 .ip4 = tp->intfc_address,
1189             },
1190         };
1191
1192         fib_table_entry_delete(inner_fib_index,
1193                                &tun_sub_net_pfx,
1194                                FIB_SOURCE_INTERFACE);
1195     }
1196
1197     u64 key = ((u64)tp->tunnel_src.as_u32 << 32 |
1198                (u64)tp->tunnel_src.as_u32);
1199
1200     hash_unset (gm->tunnel_by_key, key);
1201     mpls_sw_interface_enable_disable(mm, hi->sw_if_index, 0);
1202     ip4_sw_interface_enable_disable(hi->sw_if_index, 0);
1203
1204     vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1205                                  0 /* admin down */);
1206     vec_add1 (mm->free_gre_sw_if_indices, tp->hw_if_index);
1207     vec_free (tp->rewrite_data);
1208     fib_node_deinit(&tp->mgt_node);
1209     pool_put (mm->gre_tunnels, tp);
1210
1211     return 0;
1212 }
1213
1214 int
1215 vnet_mpls_gre_add_del_tunnel (ip4_address_t *src,
1216                               ip4_address_t *dst,
1217                               ip4_address_t *intfc,
1218                               u32 mask_width,
1219                               u32 inner_fib_id, u32 outer_fib_id,
1220                               u32 * tunnel_sw_if_index,
1221                               u8 l2_only,
1222                               u8 is_add)
1223 {
1224     u32 inner_fib_index = 0;
1225     u32 outer_fib_index = 0;
1226     u32 dummy;
1227     ip4_main_t * im = &ip4_main;
1228   
1229     /* No questions, no answers */
1230     if (NULL == tunnel_sw_if_index)
1231         tunnel_sw_if_index = &dummy;
1232
1233     *tunnel_sw_if_index = ~0;
1234
1235     if (inner_fib_id != (u32)~0)
1236     {
1237         uword * p;
1238       
1239         p = hash_get (im->fib_index_by_table_id, inner_fib_id);
1240         if (! p)
1241             return VNET_API_ERROR_NO_SUCH_INNER_FIB;
1242         inner_fib_index = p[0];
1243     }
1244
1245     if (outer_fib_id != 0)
1246     {
1247         uword * p;
1248       
1249         p = hash_get (im->fib_index_by_table_id, outer_fib_id);
1250         if (! p)
1251             return VNET_API_ERROR_NO_SUCH_FIB;
1252         outer_fib_index = p[0];
1253     }
1254
1255     if (is_add)
1256     {
1257         return (mpls_gre_tunnel_add(src,dst,intfc, mask_width,
1258                                     inner_fib_index,
1259                                     outer_fib_index,
1260                                     tunnel_sw_if_index,
1261                                     l2_only));
1262     }
1263     else
1264     {
1265         return (mpls_gre_tunnel_del(src,dst,intfc, mask_width,
1266                                     inner_fib_index,
1267                                     outer_fib_index,
1268                                     tunnel_sw_if_index,
1269                                     l2_only));
1270     }
1271 }
1272
1273 /*
1274  * Remove all mpls tunnels in the specified fib
1275  */
1276 int vnet_mpls_gre_delete_fib_tunnels (u32 fib_id)
1277 {
1278   mpls_main_t * mm = &mpls_main;
1279   vnet_main_t * vnm = mm->vnet_main;
1280   mpls_gre_tunnel_t *tp;
1281   u32 fib_index = 0;
1282   u32 * tunnels_to_delete = 0;
1283   vnet_hw_interface_t * hi;
1284   int i;
1285
1286   fib_index = ip4_fib_index_from_table_id(fib_id);
1287   if (~0 == fib_index)
1288       return VNET_API_ERROR_NO_SUCH_INNER_FIB;
1289
1290   pool_foreach (tp, mm->gre_tunnels, 
1291     ({
1292       if (tp->inner_fib_index == fib_index) 
1293         vec_add1 (tunnels_to_delete, tp - mm->gre_tunnels);
1294     }));
1295   
1296   for (i = 0; i < vec_len(tunnels_to_delete); i++) {
1297       tp = pool_elt_at_index (mm->gre_tunnels, tunnels_to_delete[i]);
1298
1299       /* Delete, the route if not already gone */
1300       if (FIB_NODE_INDEX_INVALID != tp->fei && !tp->l2_only) 
1301       {
1302           const fib_prefix_t tun_dst_pfx = {
1303               .fp_len = 32,
1304               .fp_proto = FIB_PROTOCOL_IP4,
1305               .fp_addr = {
1306                   .ip4 = tp->tunnel_dst,
1307               }
1308           };
1309
1310           fib_entry_child_remove(tp->fei,
1311                                  tp->sibling_index);
1312           fib_table_entry_special_remove(tp->outer_fib_index,
1313                                          &tun_dst_pfx,
1314                                          FIB_SOURCE_RR);
1315           tp->fei = FIB_NODE_INDEX_INVALID;
1316           adj_unlock(tp->adj_index);
1317  
1318           const fib_prefix_t tun_sub_net_pfx = {
1319               .fp_len = tp->mask_width,
1320               .fp_proto = FIB_PROTOCOL_IP4,
1321               .fp_addr = {
1322                   .ip4 = tp->intfc_address,
1323               },
1324           };
1325
1326           fib_table_entry_delete(tp->inner_fib_index,
1327                                  &tun_sub_net_pfx,
1328                                  FIB_SOURCE_INTERFACE);
1329       }
1330       
1331       hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1332       vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1333                                    0 /* admin down */);
1334       vec_add1 (mm->free_gre_sw_if_indices, tp->hw_if_index);
1335       vec_free (tp->rewrite_data);
1336       pool_put (mm->gre_tunnels, tp);
1337   }
1338   
1339   vec_free(tunnels_to_delete);
1340   
1341   return (0);
1342 }
1343
1344 static clib_error_t *
1345 create_mpls_gre_tunnel_command_fn (vlib_main_t * vm,
1346                  unformat_input_t * input,
1347                  vlib_cli_command_t * cmd)
1348 {
1349   unformat_input_t _line_input, * line_input = &_line_input;
1350   ip4_address_t src, dst, intfc;
1351   int src_set = 0, dst_set = 0, intfc_set = 0;
1352   u32 mask_width;
1353   u32 inner_fib_id = (u32)~0;
1354   u32 outer_fib_id = 0;
1355   int rv;
1356   u8 is_del = 0;
1357   u8 l2_only = 0;
1358   u32 tunnel_intfc_sw_if_index = ~0;
1359   
1360   /* Get a line of input. */
1361   if (! unformat_user (input, unformat_line_input, line_input))
1362     return 0;
1363
1364   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1365     {
1366       if (unformat (line_input, "src %U", 
1367                     unformat_ip4_address, &src))
1368         src_set = 1;
1369       else if (unformat (line_input, "dst %U", 
1370                          unformat_ip4_address, &dst))
1371         dst_set = 1;
1372       else if (unformat (line_input, "intfc %U/%d", 
1373                          unformat_ip4_address, &intfc, &mask_width))
1374         intfc_set = 1;
1375       else if (unformat (line_input, "inner-fib-id %d", &inner_fib_id))
1376         ;
1377       else if (unformat (line_input, "outer-fib-id %d", &outer_fib_id))
1378         ;
1379       else if (unformat (line_input, "del"))
1380         is_del = 1;
1381       else if (unformat (line_input, "l2-only"))
1382         l2_only = 1;
1383       else
1384         return clib_error_return (0, "unknown input '%U'",
1385                                   format_unformat_error, line_input);
1386     }
1387
1388   if (!src_set)
1389     return clib_error_return (0, "missing: src <ip-address>");
1390           
1391   if (!dst_set)
1392     return clib_error_return (0, "missing: dst <ip-address>");
1393           
1394   if (!intfc_set)
1395     return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
1396           
1397
1398   rv = vnet_mpls_gre_add_del_tunnel (&src, &dst, &intfc, mask_width, 
1399                                      inner_fib_id, outer_fib_id, 
1400                                      &tunnel_intfc_sw_if_index, 
1401                                      l2_only, !is_del);
1402
1403   switch (rv) 
1404     {
1405     case 0:
1406       if (!is_del)
1407         vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), tunnel_intfc_sw_if_index);
1408       break;
1409
1410     case VNET_API_ERROR_NO_SUCH_INNER_FIB:
1411       return clib_error_return (0, "inner fib ID %d doesn't exist\n",
1412                                 inner_fib_id);
1413     case VNET_API_ERROR_NO_SUCH_FIB:
1414       return clib_error_return (0, "outer fib ID %d doesn't exist\n",
1415                                 outer_fib_id);
1416
1417     case VNET_API_ERROR_NO_SUCH_ENTRY:
1418       return clib_error_return (0, "tunnel not found\n");
1419
1420     case VNET_API_ERROR_NO_SUCH_LABEL:
1421       /* 
1422        * This happens when there's no MPLS label for the dst address
1423        * no need for two error messages.
1424        */
1425       break;
1426       
1427     default:
1428       return clib_error_return (0, "vnet_mpls_gre_add_del_tunnel returned %d",
1429                                 rv);
1430     }
1431   return 0;
1432 }
1433
1434 VLIB_CLI_COMMAND (create_mpls_tunnel_command, static) = {
1435   .path = "create mpls gre tunnel",
1436   .short_help = 
1437   "create mpls gre tunnel [del] src <addr> dst <addr> intfc <addr>/<mw>",
1438   .function = create_mpls_gre_tunnel_command_fn,
1439 };
1440
1441 u8 * format_mpls_encap_index (u8 * s, va_list * args)
1442 {
1443   mpls_main_t * mm = va_arg (*args, mpls_main_t *);
1444   u32 entry_index = va_arg (*args, u32);
1445   mpls_encap_t * e;
1446   int i;
1447
1448   e = pool_elt_at_index (mm->encaps, entry_index);
1449   
1450   for (i = 0; i < vec_len (e->labels); i++)
1451     s = format 
1452         (s, "%d ", vnet_mpls_uc_get_label(clib_net_to_host_u32 
1453                                           (e->labels[i].label_exp_s_ttl)));
1454
1455   return s;
1456 }
1457
1458 u8 * format_mpls_gre_tunnel (u8 * s, va_list * args)
1459 {
1460   mpls_gre_tunnel_t * t = va_arg (*args, mpls_gre_tunnel_t *);
1461   mpls_main_t * mm = &mpls_main;
1462   
1463   if (t->l2_only == 0)
1464     {
1465       s = format (s, "[%d]: src %U, dst %U, adj %U/%d, labels %U\n",
1466                   t - mm->gre_tunnels, 
1467                   format_ip4_address, &t->tunnel_src,
1468                   format_ip4_address, &t->tunnel_dst,
1469                   format_ip4_address, &t->intfc_address,
1470                   t->mask_width, 
1471                   format_mpls_encap_index, mm, t->encap_index);
1472
1473       s = format (s, "      inner fib index %d, outer fib index %d",
1474                   t->inner_fib_index, t->outer_fib_index);
1475     }
1476   else
1477     {
1478       s = format (s, "[%d]: src %U, dst %U, key %U, labels %U\n",
1479                   t - mm->gre_tunnels, 
1480                   format_ip4_address, &t->tunnel_src,
1481                   format_ip4_address, &t->tunnel_dst,
1482                   format_ip4_address, &t->intfc_address,
1483                   format_mpls_encap_index, mm, t->encap_index);
1484
1485       s = format (s, "      l2 interface %d, outer fib index %d",
1486                   t->hw_if_index, t->outer_fib_index);
1487     }
1488
1489   return s;
1490 }
1491
1492 u8 * format_mpls_ethernet_tunnel (u8 * s, va_list * args)
1493 {
1494   mpls_eth_tunnel_t * t = va_arg (*args, mpls_eth_tunnel_t *);
1495   mpls_main_t * mm = &mpls_main;
1496   
1497   s = format (s, "[%d]: dst %U, adj %U/%d, labels %U\n",
1498               t - mm->eth_tunnels, 
1499               format_ethernet_address, &t->tunnel_dst,
1500               format_ip4_address, &t->intfc_address,
1501               t->mask_width, 
1502               format_mpls_encap_index, mm, t->encap_index);
1503
1504
1505   s = format (s, "      tx on %U, rx fib index %d", 
1506               format_vnet_sw_if_index_name, mm->vnet_main, t->tx_sw_if_index,
1507               t->inner_fib_index);
1508
1509   return s;
1510 }
1511
1512 static clib_error_t *
1513 show_mpls_tunnel_command_fn (vlib_main_t * vm,
1514                              unformat_input_t * input,
1515                              vlib_cli_command_t * cmd)
1516 {
1517   mpls_main_t * mm = &mpls_main;
1518   mpls_gre_tunnel_t * gt;
1519   mpls_eth_tunnel_t * et;
1520
1521   if (pool_elts (mm->gre_tunnels))
1522     {
1523       vlib_cli_output (vm, "MPLS-GRE tunnels");
1524       pool_foreach (gt, mm->gre_tunnels,
1525       ({
1526         vlib_cli_output (vm, "%U", format_mpls_gre_tunnel, gt);
1527       }));
1528     }
1529   else
1530     vlib_cli_output (vm, "No MPLS-GRE tunnels");
1531
1532   if (pool_elts (mm->eth_tunnels))
1533     {
1534       vlib_cli_output (vm, "MPLS-Ethernet tunnels");
1535       pool_foreach (et, mm->eth_tunnels,
1536       ({
1537         vlib_cli_output (vm, "%U", format_mpls_ethernet_tunnel, et);
1538       }));
1539     }
1540   else
1541     vlib_cli_output (vm, "No MPLS-Ethernet tunnels");
1542
1543   return 0;
1544 }
1545
1546 VLIB_CLI_COMMAND (show_mpls_tunnel_command, static) = {
1547     .path = "show mpls tunnel",
1548     .short_help = "show mpls tunnel",
1549     .function = show_mpls_tunnel_command_fn,
1550 };
1551
1552
1553 /* force inclusion from application's main.c */
1554 clib_error_t *mpls_interface_init (vlib_main_t *vm)
1555 {
1556   clib_error_t * error;
1557
1558   fib_node_register_type(FIB_NODE_TYPE_MPLS_GRE_TUNNEL,
1559                          &mpls_gre_vft);
1560
1561   if ((error = vlib_call_init_function (vm, mpls_policy_encap_init)))
1562       return error;
1563
1564   return 0;
1565 }
1566 VLIB_INIT_FUNCTION(mpls_interface_init);
1567
1568
1569 static u8 * mpls_ethernet_rewrite (mpls_main_t *mm, mpls_eth_tunnel_t * t)
1570 {
1571   u8 * rewrite_data = 0;
1572   mpls_encap_t * e;
1573   mpls_unicast_header_t *lp0;
1574   int i;
1575
1576  /* look up the encap label stack using the RX FIB and adjacency address*/
1577   e = mpls_encap_by_fib_and_dest (mm, t->inner_fib_index, 
1578                                   t->intfc_address.as_u32);
1579
1580   if (e == 0)
1581     {
1582       clib_warning ("no label for inner fib index %d, dst %U",
1583                     t->inner_fib_index, format_ip4_address, 
1584                     &t->intfc_address);
1585       return 0;
1586     }
1587  
1588   vec_validate (rewrite_data, 
1589                 sizeof (mpls_unicast_header_t) * vec_len(e->labels) -1);
1590
1591   /* Copy the encap label stack */
1592   lp0 = (mpls_unicast_header_t *) rewrite_data;
1593   
1594   for (i = 0; i < vec_len(e->labels); i++)
1595     lp0[i] = e->labels[i];
1596   
1597   return (rewrite_data);
1598 }
1599
1600 int vnet_mpls_ethernet_add_del_tunnel (u8 *dst,
1601                                        ip4_address_t *intfc,
1602                                        u32 mask_width,
1603                                        u32 inner_fib_id, 
1604                                        u32 tx_sw_if_index,
1605                                        u32 * tunnel_sw_if_index,
1606                                        u8 l2_only,
1607                                        u8 is_add)
1608 {
1609   ip4_main_t * im = &ip4_main;
1610   ip_lookup_main_t * lm = &im->lookup_main;
1611   mpls_main_t * mm = &mpls_main;
1612   vnet_main_t * vnm = vnet_get_main();
1613   mpls_eth_tunnel_t *tp;
1614   u32 inner_fib_index = 0;
1615   ip_adjacency_t adj;
1616   u32 adj_index;
1617   u8 * rewrite_data;
1618   int found_tunnel = 0;
1619   mpls_encap_t * e = 0;
1620   u32 hw_if_index = ~0;
1621   vnet_hw_interface_t * hi;
1622   u32 slot;
1623   u32 dummy;
1624   
1625   if (tunnel_sw_if_index == 0)
1626     tunnel_sw_if_index = &dummy;
1627
1628   *tunnel_sw_if_index = ~0;
1629
1630   if (inner_fib_id != (u32)~0)
1631     {
1632       uword * p;
1633       
1634       p = hash_get (im->fib_index_by_table_id, inner_fib_id);
1635       if (! p)
1636         return VNET_API_ERROR_NO_SUCH_FIB;
1637       inner_fib_index = p[0];
1638     }
1639
1640   /* suppress duplicate mpls interface generation. */
1641   pool_foreach (tp, mm->eth_tunnels, 
1642   ({
1643     /* 
1644      * If we have a tunnel which matches (src, dst, intfc/mask)
1645      * AND the expected route is in the FIB, it's a dup 
1646      */
1647     if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
1648         && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
1649         && tp->inner_fib_index == inner_fib_index
1650         && FIB_NODE_INDEX_INVALID != tp->fei)
1651       {
1652         found_tunnel = 1;
1653
1654         if (is_add)
1655           {
1656             if (l2_only)
1657               return 1;
1658             else
1659               {
1660                 e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, 
1661                                                 intfc->as_u32);
1662                 if (e == 0)
1663                   return VNET_API_ERROR_NO_SUCH_LABEL;
1664                 
1665                 goto reinstall_it;
1666               }
1667           }
1668         else
1669           {
1670             /* Delete */
1671             goto add_del_route;
1672           }
1673
1674       }
1675   }));
1676     
1677   /* Delete, and we can't find the tunnel */
1678   if (is_add == 0 && found_tunnel == 0)
1679     return VNET_API_ERROR_NO_SUCH_ENTRY;
1680
1681   e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, intfc->as_u32);
1682   if (e == 0)
1683     return VNET_API_ERROR_NO_SUCH_LABEL;
1684
1685   pool_get(mm->eth_tunnels, tp);
1686   memset (tp, 0, sizeof (*tp));
1687     
1688   if (vec_len (mm->free_eth_sw_if_indices) > 0)
1689     {
1690       hw_if_index = 
1691         mm->free_eth_sw_if_indices[vec_len(mm->free_eth_sw_if_indices)-1];
1692       _vec_len (mm->free_eth_sw_if_indices) -= 1;
1693       hi = vnet_get_hw_interface (vnm, hw_if_index);
1694       hi->dev_instance = tp - mm->eth_tunnels;
1695       hi->hw_instance = tp - mm->eth_tunnels;
1696     }
1697   else 
1698     {
1699       hw_if_index = vnet_register_interface
1700         (vnm, mpls_eth_device_class.index, tp - mm->eth_tunnels,
1701          mpls_eth_hw_interface_class.index,
1702          tp - mm->eth_tunnels);
1703       hi = vnet_get_hw_interface (vnm, hw_if_index);
1704
1705       /* ... to make the IP and L2 x-connect cases identical */
1706       slot = vlib_node_add_named_next_with_slot
1707         (vnm->vlib_main, hi->tx_node_index, 
1708          "interface-output", MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1709
1710       ASSERT (slot == MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1711     }
1712   
1713   *tunnel_sw_if_index = hi->sw_if_index;
1714   vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1715                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);      
1716
1717   tp->hw_if_index = hw_if_index;
1718
1719  reinstall_it:
1720   clib_memcpy(tp->tunnel_dst, dst, sizeof (tp->tunnel_dst));
1721   tp->intfc_address.as_u32 = intfc->as_u32;
1722   tp->mask_width = mask_width;
1723   tp->inner_fib_index = inner_fib_index;
1724   tp->encap_index = e - mm->encaps;
1725   tp->tx_sw_if_index = tx_sw_if_index;
1726   tp->l2_only = l2_only;
1727
1728   /* Create the adjacency and add to v4 fib */
1729   memset(&adj, 0, sizeof (adj));
1730   adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
1731     
1732   rewrite_data = mpls_ethernet_rewrite (mm, tp);
1733   if (rewrite_data == 0)
1734     {
1735       if (*tunnel_sw_if_index != ~0)
1736         {
1737           hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1738           vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1739                                        0 /* admin down */);
1740           vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
1741       }
1742
1743       pool_put (mm->eth_tunnels, tp);
1744       return VNET_API_ERROR_NO_SUCH_LABEL;
1745     }
1746   
1747   vnet_rewrite_for_sw_interface
1748     (vnm,
1749      VNET_L3_PACKET_TYPE_MPLS_UNICAST, 
1750      tx_sw_if_index,
1751      ip4_rewrite_node.index,
1752      tp->tunnel_dst,
1753      &adj.rewrite_header,
1754      sizeof (adj.rewrite_data));
1755   
1756   /* 
1757    * Prepend the (0,1,2) VLAN tag ethernet header 
1758    * we just built to the mpls header stack
1759    */
1760   vec_insert (rewrite_data, adj.rewrite_header.data_bytes, 0);
1761   clib_memcpy(rewrite_data, 
1762          vnet_rewrite_get_data_internal(&adj.rewrite_header, 
1763                                         sizeof (adj.rewrite_data)),
1764          adj.rewrite_header.data_bytes);
1765
1766   vnet_rewrite_set_data_internal (&adj.rewrite_header, 
1767                                   sizeof(adj.rewrite_data),
1768                                   rewrite_data, 
1769                                   vec_len(rewrite_data));
1770   
1771   vec_free (tp->rewrite_data);
1772   
1773   tp->rewrite_data = rewrite_data;
1774
1775   if (!l2_only)
1776     ip_add_adjacency (lm, &adj, 1 /* one adj */,
1777                       &adj_index);
1778   
1779  add_del_route:
1780
1781   if (!l2_only)
1782     {
1783       const fib_prefix_t pfx = {
1784           .fp_addr = {
1785               .ip4 = tp->intfc_address,
1786           },
1787           .fp_len = tp->mask_width,
1788           .fp_proto = FIB_PROTOCOL_IP4,
1789       };
1790       if (is_add)
1791           tp->fei = fib_table_entry_special_add(tp->inner_fib_index,
1792                                                 &pfx,
1793                                                 FIB_SOURCE_API,
1794                                                 FIB_ENTRY_FLAG_NONE,
1795                                                 adj_index);
1796       else
1797         {
1798           fib_table_entry_delete(tp->inner_fib_index, &pfx, FIB_SOURCE_API);
1799           tp->fei = FIB_NODE_INDEX_INVALID;
1800         }
1801     }
1802   if (is_add == 0 && found_tunnel)
1803     {
1804       hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1805       vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1806                                    0 /* admin down */);
1807       vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
1808       vec_free (tp->rewrite_data);
1809       pool_put (mm->eth_tunnels, tp);
1810     }
1811
1812   return 0;
1813 }
1814
1815 static clib_error_t *
1816 create_mpls_ethernet_tunnel_command_fn (vlib_main_t * vm,
1817                                         unformat_input_t * input,
1818                                         vlib_cli_command_t * cmd)
1819 {
1820   unformat_input_t _line_input, * line_input = &_line_input;
1821   vnet_main_t * vnm = vnet_get_main();
1822   ip4_address_t intfc;
1823   int adj_set = 0;
1824   u8 dst[6];
1825   int dst_set = 0, intfc_set = 0;
1826   u32 mask_width;
1827   u32 inner_fib_id = (u32)~0;
1828   int rv;
1829   u8 is_del = 0;
1830   u8 l2_only = 0;
1831   u32 tx_sw_if_index;
1832   u32 sw_if_index = ~0;
1833   
1834   /* Get a line of input. */
1835   if (! unformat_user (input, unformat_line_input, line_input))
1836     return 0;
1837
1838   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1839     {
1840       if (unformat (line_input, "dst %U", 
1841                     unformat_ethernet_address, &dst))
1842         dst_set = 1;
1843       else if (unformat (line_input, "adj %U/%d", 
1844                          unformat_ip4_address, &intfc, &mask_width))
1845         adj_set = 1;
1846       else if (unformat (line_input, "tx-intfc %U", 
1847                          unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
1848         intfc_set = 1;
1849       else if (unformat (line_input, "fib-id %d", &inner_fib_id))
1850         ;
1851       else if (unformat (line_input, "l2-only"))
1852         l2_only = 1;
1853       else if (unformat (line_input, "del"))
1854         is_del = 1;
1855       else
1856         return clib_error_return (0, "unknown input '%U'",
1857                                   format_unformat_error, line_input);
1858     }
1859
1860   if (!intfc_set)
1861     return clib_error_return (0, "missing tx-intfc");
1862
1863   if (!dst_set)
1864     return clib_error_return (0, "missing: dst <ethernet-address>");
1865           
1866   if (!adj_set)
1867     return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
1868   
1869
1870   rv = vnet_mpls_ethernet_add_del_tunnel (dst, &intfc, mask_width, 
1871                                           inner_fib_id, tx_sw_if_index, 
1872                                           &sw_if_index,
1873                                           l2_only, !is_del);
1874
1875   switch (rv) 
1876     {
1877     case 0:
1878       if (!is_del)
1879         vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), sw_if_index);
1880       break;
1881     case VNET_API_ERROR_NO_SUCH_FIB:
1882       return clib_error_return (0, "rx fib ID %d doesn't exist\n",
1883                                 inner_fib_id);
1884
1885     case VNET_API_ERROR_NO_SUCH_ENTRY:
1886       return clib_error_return (0, "tunnel not found\n");
1887
1888     case VNET_API_ERROR_NO_SUCH_LABEL:
1889       /* 
1890        * This happens when there's no MPLS label for the dst address
1891        * no need for two error messages.
1892        */
1893         return clib_error_return (0, "no label for %U in fib %d", 
1894                                   format_ip4_address, &intfc, inner_fib_id);
1895       break;
1896       
1897     default:
1898       return clib_error_return (0, "vnet_mpls_ethernet_add_del_tunnel returned %d", rv);
1899       break;
1900     }
1901   return 0;
1902 }
1903
1904
1905 VLIB_CLI_COMMAND (create_mpls_ethernet_tunnel_command, static) = {
1906   .path = "create mpls ethernet tunnel",
1907   .short_help = 
1908   "create mpls ethernet tunnel [del] dst <mac-addr> intfc <addr>/<mw>",
1909   .function = create_mpls_ethernet_tunnel_command_fn,
1910 };
1911
1912
1913 int vnet_mpls_policy_tunnel_add_rewrite (mpls_main_t * mm, 
1914                                          mpls_encap_t * e, 
1915                                          u32 policy_tunnel_index)
1916 {
1917   mpls_eth_tunnel_t * t;
1918   ip_adjacency_t adj;
1919   u8 * rewrite_data = 0;
1920   u8 * label_start;
1921   mpls_unicast_header_t *lp;
1922   int i;
1923
1924   if (pool_is_free_index (mm->eth_tunnels, policy_tunnel_index))
1925     return VNET_API_ERROR_NO_SUCH_ENTRY;
1926
1927   t = pool_elt_at_index (mm->eth_tunnels, policy_tunnel_index);
1928
1929   memset (&adj, 0, sizeof (adj));
1930
1931   /* Build L2 encap */
1932   vnet_rewrite_for_sw_interface
1933     (mm->vnet_main, 
1934      VNET_L3_PACKET_TYPE_MPLS_UNICAST, 
1935      t->tx_sw_if_index,
1936      mpls_policy_encap_node.index,
1937      t->tunnel_dst,
1938      &adj.rewrite_header,
1939      sizeof (adj.rewrite_data));
1940   
1941   vec_validate (rewrite_data, adj.rewrite_header.data_bytes -1);
1942
1943   clib_memcpy(rewrite_data, 
1944          vnet_rewrite_get_data_internal(&adj.rewrite_header, 
1945                                         sizeof (adj.rewrite_data)),
1946          adj.rewrite_header.data_bytes);
1947
1948   /* Append the label stack */
1949
1950   vec_add2 (rewrite_data, label_start, vec_len(e->labels) * sizeof (u32));
1951
1952   lp = (mpls_unicast_header_t *) label_start;
1953
1954   for (i = 0; i < vec_len(e->labels); i++)
1955     lp[i] = e->labels[i];
1956   
1957   /* Remember the rewrite data */
1958   e->rewrite = rewrite_data;
1959   e->output_next_index = adj.rewrite_header.next_index;
1960
1961   return 0;
1962 }
1963
1964 int vnet_mpls_ethernet_add_del_policy_tunnel (u8 *dst,
1965                                               ip4_address_t *intfc,
1966                                               u32 mask_width,
1967                                               u32 inner_fib_id, 
1968                                               u32 tx_sw_if_index,
1969                                               u32 * tunnel_sw_if_index,
1970                                               u32 classify_table_index,
1971                                               u32 * new_tunnel_index,
1972                                               u8 l2_only,
1973                                               u8 is_add)
1974 {
1975   ip4_main_t * im = &ip4_main;
1976   mpls_main_t * mm = &mpls_main;
1977   vnet_main_t * vnm = vnet_get_main();
1978   mpls_eth_tunnel_t *tp;
1979   u32 inner_fib_index = 0;
1980   int found_tunnel = 0;
1981   mpls_encap_t * e = 0;
1982   u32 hw_if_index = ~0;
1983   vnet_hw_interface_t * hi;
1984   u32 slot;
1985   u32 dummy;
1986   
1987   if (tunnel_sw_if_index == 0)
1988     tunnel_sw_if_index = &dummy;
1989
1990   *tunnel_sw_if_index = ~0;
1991
1992   if (inner_fib_id != (u32)~0)
1993     {
1994       uword * p;
1995       
1996       p = hash_get (im->fib_index_by_table_id, inner_fib_id);
1997       if (! p)
1998         return VNET_API_ERROR_NO_SUCH_FIB;
1999       inner_fib_index = p[0];
2000     }
2001
2002   /* suppress duplicate mpls interface generation. */
2003   pool_foreach (tp, mm->eth_tunnels, 
2004   ({
2005     /* 
2006      * If we have a tunnel which matches (src, dst, intfc/mask)
2007      * AND the expected route is in the FIB, it's a dup 
2008      */
2009     if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
2010         && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
2011         && tp->inner_fib_index == inner_fib_index
2012         && FIB_NODE_INDEX_INVALID != tp->fei)
2013       {
2014         found_tunnel = 1;
2015
2016         if (is_add)
2017           {
2018             if (l2_only)
2019               return 1;
2020             else
2021               {
2022                 goto reinstall_it;
2023               }
2024           }
2025         else
2026           {
2027             /* Delete */
2028             goto add_del_route;
2029           }
2030
2031       }
2032   }));
2033     
2034   /* Delete, and we can't find the tunnel */
2035   if (is_add == 0 && found_tunnel == 0)
2036     return VNET_API_ERROR_NO_SUCH_ENTRY;
2037
2038   pool_get(mm->eth_tunnels, tp);
2039   memset (tp, 0, sizeof (*tp));
2040     
2041   if (vec_len (mm->free_eth_sw_if_indices) > 0)
2042     {
2043       hw_if_index = 
2044         mm->free_eth_sw_if_indices[vec_len(mm->free_eth_sw_if_indices)-1];
2045       _vec_len (mm->free_eth_sw_if_indices) -= 1;
2046       hi = vnet_get_hw_interface (vnm, hw_if_index);
2047       hi->dev_instance = tp - mm->eth_tunnels;
2048       hi->hw_instance = tp - mm->eth_tunnels;
2049     }
2050   else 
2051     {
2052       hw_if_index = vnet_register_interface
2053         (vnm, mpls_eth_device_class.index, tp - mm->eth_tunnels,
2054          mpls_eth_hw_interface_class.index,
2055          tp - mm->eth_tunnels);
2056       hi = vnet_get_hw_interface (vnm, hw_if_index);
2057
2058       /* ... to make the IP and L2 x-connect cases identical */
2059       slot = vlib_node_add_named_next_with_slot
2060         (vnm->vlib_main, hi->tx_node_index, 
2061          "interface-output", MPLS_ETH_OUTPUT_NEXT_OUTPUT);
2062
2063       ASSERT (slot == MPLS_ETH_OUTPUT_NEXT_OUTPUT);
2064     }
2065   
2066   *tunnel_sw_if_index = hi->sw_if_index;
2067   vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
2068                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);      
2069
2070   tp->hw_if_index = hw_if_index;
2071
2072  reinstall_it:
2073   clib_memcpy(tp->tunnel_dst, dst, sizeof (tp->tunnel_dst));
2074   tp->intfc_address.as_u32 = intfc->as_u32;
2075   tp->mask_width = mask_width;
2076   tp->inner_fib_index = inner_fib_index;
2077   tp->encap_index = e - mm->encaps;
2078   tp->tx_sw_if_index = tx_sw_if_index;
2079   tp->l2_only = l2_only;
2080   tp->fei = FIB_NODE_INDEX_INVALID;
2081
2082   if (new_tunnel_index)
2083     *new_tunnel_index = tp - mm->eth_tunnels;
2084
2085  add_del_route:
2086
2087   if (!l2_only)
2088     {
2089       const fib_prefix_t pfx = {
2090           .fp_addr = {
2091               .ip4 = tp->intfc_address,
2092           },
2093           .fp_len = tp->mask_width,
2094           .fp_proto = FIB_PROTOCOL_IP4,
2095       };
2096       dpo_id_t dpo = DPO_NULL;
2097
2098       if (is_add)
2099         {
2100           dpo_set(&dpo,
2101                   DPO_CLASSIFY,
2102                   DPO_PROTO_IP4,
2103                   classify_dpo_create(FIB_PROTOCOL_IP4,
2104                                       classify_table_index));
2105
2106           tp->fei = fib_table_entry_special_dpo_add(tp->inner_fib_index,
2107                                                     &pfx,
2108                                                     FIB_SOURCE_API,
2109                                                     FIB_ENTRY_FLAG_EXCLUSIVE,
2110                                                     &dpo);
2111           dpo_reset(&dpo);
2112         }
2113       else
2114         {
2115           fib_table_entry_delete(tp->inner_fib_index, &pfx, FIB_SOURCE_API);
2116           tp->fei = FIB_NODE_INDEX_INVALID;
2117         }
2118     }
2119   if (is_add == 0 && found_tunnel)
2120     {
2121       hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
2122       vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
2123                                    0 /* admin down */);
2124       vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
2125       pool_put (mm->eth_tunnels, tp);
2126     }
2127
2128   return 0;
2129 }
2130
2131 static clib_error_t *
2132 create_mpls_ethernet_policy_tunnel_command_fn (vlib_main_t * vm,
2133                                                unformat_input_t * input,
2134                                                vlib_cli_command_t * cmd)
2135 {
2136   unformat_input_t _line_input, * line_input = &_line_input;
2137   vnet_main_t * vnm = vnet_get_main();
2138   ip4_address_t intfc;
2139   int adj_set = 0;
2140   u8 dst[6];
2141   int dst_set = 0, intfc_set = 0;
2142   u32 mask_width;
2143   u32 inner_fib_id = (u32)~0;
2144   u32 classify_table_index = (u32)~0;
2145   u32 new_tunnel_index;
2146   int rv;
2147   u8 is_del = 0;
2148   u8 l2_only = 0;
2149   u32 tx_sw_if_index;
2150   
2151   /* Get a line of input. */
2152   if (! unformat_user (input, unformat_line_input, line_input))
2153     return 0;
2154
2155   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
2156     {
2157       if (unformat (line_input, "dst %U", 
2158                     unformat_ethernet_address, &dst))
2159         dst_set = 1;
2160       else if (unformat (line_input, "adj %U/%d", 
2161                          unformat_ip4_address, &intfc, &mask_width))
2162         adj_set = 1;
2163       else if (unformat (line_input, "tx-intfc %U", 
2164                          unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
2165         intfc_set = 1;
2166       else if (unformat (line_input, "classify-table-index %d",
2167                          &classify_table_index))
2168         ;
2169       else if (unformat (line_input, "fib-id %d", &inner_fib_id))
2170         ;
2171       else if (unformat (line_input, "l2-only"))
2172         l2_only = 1;
2173       else if (unformat (line_input, "del"))
2174         is_del = 1;
2175       else
2176         return clib_error_return (0, "unknown input '%U'",
2177                                   format_unformat_error, line_input);
2178     }
2179
2180   if (classify_table_index == ~0)
2181     return clib_error_return (0, "missing classify_table_index");
2182
2183   if (!intfc_set)
2184     return clib_error_return (0, "missing tx-intfc");
2185
2186   if (!dst_set)
2187     return clib_error_return (0, "missing: dst <ethernet-address>");
2188           
2189   if (!adj_set)
2190     return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
2191   
2192
2193   rv = vnet_mpls_ethernet_add_del_policy_tunnel (dst, &intfc, mask_width, 
2194                                                  inner_fib_id, tx_sw_if_index, 
2195                                                  0 /* tunnel sw_if_index */, 
2196                                                  classify_table_index,
2197                                                  &new_tunnel_index,
2198                                                  l2_only, !is_del);
2199   switch (rv) 
2200     {
2201     case 0:
2202       if (!is_del)
2203         vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), new_tunnel_index);
2204       break;
2205     case VNET_API_ERROR_NO_SUCH_FIB:
2206       return clib_error_return (0, "rx fib ID %d doesn't exist\n",
2207                                 inner_fib_id);
2208
2209     case VNET_API_ERROR_NO_SUCH_ENTRY:
2210       return clib_error_return (0, "tunnel not found\n");
2211
2212     case VNET_API_ERROR_NO_SUCH_LABEL:
2213       /* 
2214        * This happens when there's no MPLS label for the dst address
2215        * no need for two error messages.
2216        */
2217         return clib_error_return (0, "no label for %U in fib %d", 
2218                                   format_ip4_address, &intfc, inner_fib_id);
2219       break;
2220       
2221     default:
2222       return clib_error_return (0, "vnet_mpls_ethernet_add_del_policy_tunnel returned %d", rv);
2223       break;
2224     }
2225
2226   return 0;
2227 }
2228
2229 VLIB_CLI_COMMAND (create_mpls_ethernet_policy_tunnel_command, static) = {
2230   .path = "create mpls ethernet policy tunnel",
2231   .short_help = 
2232   "create mpls ethernet policy tunnel [del] dst <mac-addr> intfc <addr>/<mw>\n"
2233   " classify-table-index <nn>",
2234   .function = create_mpls_ethernet_policy_tunnel_command_fn,
2235 };
2236
2237 static clib_error_t *
2238 mpls_interface_enable_disable (vlib_main_t * vm,
2239                                unformat_input_t * input,
2240                                vlib_cli_command_t * cmd)
2241 {
2242   vnet_main_t * vnm = vnet_get_main();
2243   clib_error_t * error = 0;
2244   u32 sw_if_index, enable;
2245
2246   sw_if_index = ~0;
2247
2248   if (! unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
2249     {
2250       error = clib_error_return (0, "unknown interface `%U'",
2251                                  format_unformat_error, input);
2252       goto done;
2253     }
2254
2255   if (unformat (input, "enable"))
2256       enable = 1;
2257   else if (unformat (input, "disable"))
2258       enable = 0;
2259   else
2260     {
2261       error = clib_error_return (0, "expected 'enable' or 'disable'",
2262                                  format_unformat_error, input);
2263       goto done;
2264     }
2265
2266   mpls_sw_interface_enable_disable(&mpls_main, sw_if_index, enable);
2267
2268  done:
2269   return error;
2270 }
2271
2272 VLIB_CLI_COMMAND (set_interface_ip_table_command, static) = {
2273   .path = "set interface mpls",
2274   .function = mpls_interface_enable_disable,
2275   .short_help = "Enable/Disable an interface for MPLS forwarding",
2276 };