L2 over LISP and GRE (VPP-457)
[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 void
540 mpls_gre_fixup (vlib_main_t *vm,
541                 ip_adjacency_t *adj,
542                 vlib_buffer_t * b0)
543 {
544     ip4_header_t * ip0;
545
546     ip0 = vlib_buffer_get_current (b0);
547
548     /* Fixup the checksum and len fields in the GRE tunnel encap
549      * that was applied at the midchain node */
550     ip0->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
551     ip0->checksum = ip4_header_checksum (ip0);
552 }
553
554 static u8 * mpls_gre_rewrite (mpls_main_t *mm, mpls_gre_tunnel_t * t)
555 {
556   ip4_header_t * ip0;
557   ip4_gre_and_mpls_header_t * h0;
558   u8 * rewrite_data = 0;
559   mpls_encap_t * e;
560   mpls_unicast_header_t *lp0;
561   int i;
562
563  /* look up the encap label stack using the RX FIB */
564   e = mpls_encap_by_fib_and_dest (mm, t->inner_fib_index, t->tunnel_dst.as_u32);
565
566   if (e == 0)
567     {
568       clib_warning ("no label for inner fib index %d, dst %U",
569                     t->inner_fib_index, format_ip4_address, 
570                     &t->tunnel_dst);
571       return 0;
572     }
573  
574   vec_validate (rewrite_data, sizeof (*h0) 
575                 + sizeof (mpls_unicast_header_t) * vec_len(e->labels) -1);
576   memset (rewrite_data, 0, sizeof (*h0));
577
578   h0 = (ip4_gre_and_mpls_header_t *) rewrite_data;
579   /* Copy the encap label stack */
580   lp0 = h0->labels;
581   for (i = 0; i < vec_len(e->labels); i++)
582     lp0[i] = e->labels[i];
583   ip0 = &h0->ip4;
584   h0->gre.protocol = clib_host_to_net_u16(GRE_PROTOCOL_mpls_unicast);
585   ip0->ip_version_and_header_length = 0x45;
586   ip0->ttl = 254;
587   ip0->protocol = IP_PROTOCOL_GRE;
588   /* $$$ fixup ip4 header length and checksum after-the-fact */
589   ip0->src_address.as_u32 = t->tunnel_src.as_u32;
590   ip0->dst_address.as_u32 = t->tunnel_dst.as_u32;
591   ip0->checksum = ip4_header_checksum (ip0);
592
593   return (rewrite_data);
594 }
595
596 u8
597 mpls_sw_interface_is_enabled (u32 sw_if_index)
598 {
599     mpls_main_t * mm = &mpls_main;
600
601     if (vec_len(mm->mpls_enabled_by_sw_if_index) < sw_if_index)
602         return (0);
603
604     return (mm->mpls_enabled_by_sw_if_index[sw_if_index]);
605 }
606
607 void
608 mpls_sw_interface_enable_disable (mpls_main_t * mm,
609                                   u32 sw_if_index,
610                                   u8 is_enable)
611 {
612   mpls_interface_state_change_callback_t *callback;
613   vlib_main_t * vm = vlib_get_main();
614   ip_config_main_t * cm = &mm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
615   vnet_config_main_t * vcm = &cm->config_main;
616   u32 lookup_feature_index;
617   fib_node_index_t lfib_index;
618   u32 ci;
619
620   vec_validate_init_empty (mm->mpls_enabled_by_sw_if_index, sw_if_index, 0);
621
622   /*
623    * enable/disable only on the 1<->0 transition
624    */
625   if (is_enable)
626     {
627       if (1 != ++mm->mpls_enabled_by_sw_if_index[sw_if_index])
628         return;
629
630       lfib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_MPLS,
631                                                      MPLS_FIB_DEFAULT_TABLE_ID);
632       vec_validate(mm->fib_index_by_sw_if_index, 0);
633       mm->fib_index_by_sw_if_index[sw_if_index] = lfib_index;
634     }
635   else
636     {
637       ASSERT(mm->mpls_enabled_by_sw_if_index[sw_if_index] > 0);
638       if (0 != --mm->mpls_enabled_by_sw_if_index[sw_if_index])
639         return;
640
641       fib_table_unlock(mm->fib_index_by_sw_if_index[sw_if_index],
642                        FIB_PROTOCOL_MPLS);
643     }
644
645   vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0);
646   ci = cm->config_index_by_sw_if_index[sw_if_index];
647
648   lookup_feature_index = mm->mpls_rx_feature_lookup;
649
650   if (is_enable)
651     ci = vnet_config_add_feature (vm, vcm,
652                                   ci,
653                                   lookup_feature_index,
654                                   /* config data */ 0,
655                                   /* # bytes of config data */ 0);
656   else
657     ci = vnet_config_del_feature (vm, vcm, ci,
658                                   lookup_feature_index,
659                                   /* config data */ 0,
660                                   /* # bytes of config data */ 0);
661
662   cm->config_index_by_sw_if_index[sw_if_index] = ci;
663
664   /*
665    * notify all interested clients of the change of state.
666    */
667   vec_foreach(callback, mm->mpls_interface_state_change_callbacks)
668   {
669       (*callback)(sw_if_index, is_enable);
670   }
671 }
672
673 static mpls_gre_tunnel_t *
674 mpls_gre_tunnel_from_fib_node (fib_node_t *node)
675 {
676 #if (CLIB_DEBUG > 0)
677     ASSERT(FIB_NODE_TYPE_MPLS_GRE_TUNNEL == node->fn_type);
678 #endif
679     return ((mpls_gre_tunnel_t*)node);
680 }
681
682 /*
683  * mpls_gre_tunnel_stack
684  *
685  * 'stack' (resolve the recursion for) the tunnel's midchain adjacency
686  */
687 static void
688 mpls_gre_tunnel_stack (mpls_gre_tunnel_t *mgt)
689 {
690     /*
691      * find the adjacency that is contributed by the FIB entry
692      * that this tunnel resovles via, and use it as the next adj
693      * in the midchain
694      */
695     adj_nbr_midchain_stack(mgt->adj_index,
696                            fib_entry_contribute_ip_forwarding(mgt->fei));
697 }
698
699 /**
700  * Function definition to backwalk a FIB node
701  */
702 static fib_node_back_walk_rc_t
703 mpls_gre_tunnel_back_walk (fib_node_t *node,
704                            fib_node_back_walk_ctx_t *ctx)
705 {
706     mpls_gre_tunnel_stack(mpls_gre_tunnel_from_fib_node(node));
707
708     return (FIB_NODE_BACK_WALK_CONTINUE);
709 }
710
711 /**
712  * Function definition to get a FIB node from its index
713  */
714 static fib_node_t*
715 mpls_gre_tunnel_fib_node_get (fib_node_index_t index)
716 {
717     mpls_gre_tunnel_t * mgt;
718     mpls_main_t * mm;
719
720     mm  = &mpls_main;
721     mgt = pool_elt_at_index(mm->gre_tunnels, index);
722
723     return (&mgt->mgt_node);
724 }
725
726 /**
727  * Function definition to inform the FIB node that its last lock has gone.
728  */
729 static void
730 mpls_gre_tunnel_last_lock_gone (fib_node_t *node)
731 {
732     /*
733      * The MPLS GRE tunnel is a root of the graph. As such
734      * it never has children and thus is never locked.
735      */
736     ASSERT(0);
737 }
738
739 /*
740  * Virtual function table registered by MPLS GRE tunnels
741  * for participation in the FIB object graph.
742  */
743 const static fib_node_vft_t mpls_gre_vft = {
744     .fnv_get = mpls_gre_tunnel_fib_node_get,
745     .fnv_last_lock = mpls_gre_tunnel_last_lock_gone,
746     .fnv_back_walk = mpls_gre_tunnel_back_walk,
747 };
748  
749 static mpls_gre_tunnel_t *
750 mpls_gre_tunnel_find (ip4_address_t *src,
751                       ip4_address_t *dst,
752                       ip4_address_t *intfc,
753                       u32 inner_fib_index)
754 {
755     mpls_main_t * mm = &mpls_main;
756     mpls_gre_tunnel_t *tp;
757     int found_tunnel = 0;
758
759     /* suppress duplicate mpls interface generation. */
760     pool_foreach (tp, mm->gre_tunnels, 
761     ({
762         /* 
763          * If we have a tunnel which matches (src, dst, intfc/mask)
764          * AND the expected route is in the FIB, it's a dup 
765          */
766         if (!memcmp (&tp->tunnel_src, src, sizeof (*src))
767             && !memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
768             && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
769             && tp->inner_fib_index == inner_fib_index) 
770         {
771             found_tunnel = 1;
772             goto found;
773         }
774     }));
775
776 found:
777     if (found_tunnel)
778     {
779         return (tp);
780     }
781     return (NULL);
782 }
783
784 int mpls_gre_tunnel_add (ip4_address_t *src,
785                          ip4_address_t *dst,
786                          ip4_address_t *intfc,
787                          u32 mask_width,
788                          u32 inner_fib_index,
789                          u32 outer_fib_index,
790                          u32 * tunnel_sw_if_index,
791                          u8 l2_only)
792 {
793     mpls_main_t * mm = &mpls_main;
794     gre_main_t * gm = &gre_main;
795     vnet_main_t * vnm = vnet_get_main();
796     mpls_gre_tunnel_t *tp;
797     ip_adjacency_t adj;
798     u8 * rewrite_data;
799     mpls_encap_t * e = 0;
800     u32 hw_if_index = ~0;
801     vnet_hw_interface_t * hi;
802     u32 slot;
803     const ip46_address_t zero_nh = {
804         .ip4.as_u32 = 0,
805     };
806
807     tp = mpls_gre_tunnel_find(src,dst,intfc,inner_fib_index);
808
809     /* Add, duplicate */
810     if (NULL != tp)
811         return VNET_API_ERROR_NO_SUCH_ENTRY;
812
813     e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, dst->as_u32);
814     if (e == 0)
815         return VNET_API_ERROR_NO_SUCH_LABEL;
816
817     pool_get(mm->gre_tunnels, tp);
818     memset (tp, 0, sizeof (*tp));
819     fib_node_init(&tp->mgt_node,
820                   FIB_NODE_TYPE_MPLS_GRE_TUNNEL);
821
822     if (vec_len (mm->free_gre_sw_if_indices) > 0)
823     {
824         hw_if_index = 
825             mm->free_gre_sw_if_indices[vec_len(mm->free_gre_sw_if_indices)-1];
826         _vec_len (mm->free_gre_sw_if_indices) -= 1;
827         hi = vnet_get_hw_interface (vnm, hw_if_index);
828         hi->dev_instance = tp - mm->gre_tunnels;
829         hi->hw_instance = tp - mm->gre_tunnels;
830     }
831     else 
832     {
833         hw_if_index = vnet_register_interface
834             (vnm, mpls_gre_device_class.index, tp - mm->gre_tunnels,
835              mpls_gre_hw_interface_class.index,
836              tp - mm->gre_tunnels);
837         hi = vnet_get_hw_interface (vnm, hw_if_index);
838
839         /* ... to make the IP and L2 x-connect cases identical */
840         slot = vlib_node_add_named_next_with_slot
841             (vnm->vlib_main, hi->tx_node_index, 
842              "mpls-post-rewrite", MPLS_GRE_OUTPUT_NEXT_POST_REWRITE);
843
844         ASSERT (slot == MPLS_GRE_OUTPUT_NEXT_POST_REWRITE);
845     }
846   
847     *tunnel_sw_if_index = hi->sw_if_index;
848     vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
849                                  VNET_SW_INTERFACE_FLAG_ADMIN_UP);      
850     vec_validate(ip4_main.fib_index_by_sw_if_index, *tunnel_sw_if_index);
851     ip4_main.fib_index_by_sw_if_index[*tunnel_sw_if_index] = outer_fib_index;
852
853     tp->hw_if_index = hw_if_index;
854
855     /* bind the MPLS and IPv4 FIBs to the interface and enable */
856     vec_validate(mm->fib_index_by_sw_if_index, hi->sw_if_index);
857     mm->fib_index_by_sw_if_index[hi->sw_if_index] = inner_fib_index;
858     mpls_sw_interface_enable_disable(mm, hi->sw_if_index, 1);
859     ip4_main.fib_index_by_sw_if_index[hi->sw_if_index] = inner_fib_index;
860     ip4_sw_interface_enable_disable(hi->sw_if_index, 1);
861
862     tp->tunnel_src.as_u32 = src->as_u32;
863     tp->tunnel_dst.as_u32 = dst->as_u32;
864     tp->intfc_address.as_u32 = intfc->as_u32;
865     tp->mask_width = mask_width;
866     tp->inner_fib_index = inner_fib_index;
867     tp->outer_fib_index = outer_fib_index;
868     tp->encap_index = e - mm->encaps;
869     tp->l2_only = l2_only;
870
871     /* Add the tunnel to the hash table of all GRE tunnels */
872     u64 key = (u64)src->as_u32 << 32 | (u64)dst->as_u32;
873
874     ASSERT(NULL == hash_get (gm->tunnel_by_key, key));
875     hash_set (gm->tunnel_by_key, key, tp - mm->gre_tunnels);
876
877     /* Create the adjacency and add to v4 fib */
878     memset(&adj, 0, sizeof (adj));
879     adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
880     
881     rewrite_data = mpls_gre_rewrite (mm, tp);
882     if (rewrite_data == 0)
883     {
884         if (*tunnel_sw_if_index != ~0)
885         {
886             hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
887             vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
888                                          0 /* admin down */);
889             vec_add1 (mm->free_gre_sw_if_indices, tp->hw_if_index);
890         }
891         pool_put (mm->gre_tunnels, tp);
892         return VNET_API_ERROR_NO_SUCH_LABEL;
893     }
894
895     /* Save a copy of the rewrite data for L2 x-connect */
896     vec_free (tp->rewrite_data);
897
898     tp->rewrite_data = rewrite_data;
899   
900     if (!l2_only)
901     {
902         /*
903          * source the FIB entry for the tunnel's destination
904          * and become a child thereof. The tunnel will then get poked
905          * when the forwarding for the entry updates, and the tunnel can
906          * re-stack accordingly
907          */
908         const fib_prefix_t tun_dst_pfx = {
909             .fp_len = 32,
910             .fp_proto = FIB_PROTOCOL_IP4,
911             .fp_addr = {
912                 .ip4 = *dst,
913             }
914         };
915
916         tp->fei = fib_table_entry_special_add(outer_fib_index,
917                                               &tun_dst_pfx,
918                                               FIB_SOURCE_RR,
919                                               FIB_ENTRY_FLAG_NONE,
920                                               ADJ_INDEX_INVALID);
921         tp->sibling_index = fib_entry_child_add(tp->fei,
922                                                 FIB_NODE_TYPE_MPLS_GRE_TUNNEL,
923                                                 tp - mm->gre_tunnels);
924
925         /*
926          * create and update the midchain adj this tunnel sources.
927          * This is the adj the route we add below will resolve to.
928          */
929         tp->adj_index = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
930                                             FIB_LINK_IP4,
931                                             &zero_nh,
932                                             hi->sw_if_index);
933
934         adj_nbr_midchain_update_rewrite(tp->adj_index,
935                                         mpls_gre_fixup,
936                                         ADJ_MIDCHAIN_FLAG_NONE,
937                                         rewrite_data);
938         mpls_gre_tunnel_stack(tp);
939
940         /*
941          * Update the route for the tunnel's subnet to point through the tunnel
942          */
943         const fib_prefix_t tun_sub_net_pfx = {
944             .fp_len = tp->mask_width,
945             .fp_proto = FIB_PROTOCOL_IP4,
946             .fp_addr = {
947                 .ip4 = tp->intfc_address,
948             },
949         };
950
951         fib_table_entry_update_one_path(inner_fib_index,
952                                         &tun_sub_net_pfx,
953                                         FIB_SOURCE_INTERFACE,
954                                         (FIB_ENTRY_FLAG_CONNECTED |
955                                          FIB_ENTRY_FLAG_ATTACHED),
956                                         FIB_PROTOCOL_IP4,
957                                         &zero_nh,
958                                         hi->sw_if_index,
959                                         ~0, // invalid fib index
960                                         1,
961                                         MPLS_LABEL_INVALID,
962                                         FIB_ROUTE_PATH_FLAG_NONE);
963     }
964
965     return 0;
966 }
967
968 static int
969 mpls_gre_tunnel_del (ip4_address_t *src,
970                      ip4_address_t *dst,
971                      ip4_address_t *intfc,
972                      u32 mask_width,
973                      u32 inner_fib_index,
974                      u32 outer_fib_index,
975                      u32 * tunnel_sw_if_index,
976                      u8 l2_only)
977 {
978     mpls_main_t * mm = &mpls_main;
979     vnet_main_t * vnm = vnet_get_main();
980     gre_main_t * gm = &gre_main;
981     mpls_gre_tunnel_t *tp;
982     vnet_hw_interface_t * hi;
983   
984     tp = mpls_gre_tunnel_find(src,dst,intfc,inner_fib_index);
985
986     /* Delete, and we can't find the tunnel */
987     if (NULL == tp)
988         return VNET_API_ERROR_NO_SUCH_ENTRY;
989
990     hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
991
992     if (!l2_only)
993     {
994         /*
995          * unsource the FIB entry for the tunnel's destination
996          */
997         const fib_prefix_t tun_dst_pfx = {
998             .fp_len = 32,
999             .fp_proto = FIB_PROTOCOL_IP4,
1000             .fp_addr = {
1001                 .ip4 = *dst,
1002             }
1003         };
1004
1005         fib_entry_child_remove(tp->fei,
1006                                tp->sibling_index);
1007         fib_table_entry_special_remove(outer_fib_index,
1008                                        &tun_dst_pfx,
1009                                        FIB_SOURCE_RR);
1010         tp->fei = FIB_NODE_INDEX_INVALID;
1011         adj_unlock(tp->adj_index);
1012  
1013         /*
1014          * unsource the route for the tunnel's subnet
1015          */
1016         const fib_prefix_t tun_sub_net_pfx = {
1017             .fp_len = tp->mask_width,
1018             .fp_proto = FIB_PROTOCOL_IP4,
1019             .fp_addr = {
1020                 .ip4 = tp->intfc_address,
1021             },
1022         };
1023
1024         fib_table_entry_delete(inner_fib_index,
1025                                &tun_sub_net_pfx,
1026                                FIB_SOURCE_INTERFACE);
1027     }
1028
1029     u64 key = ((u64)tp->tunnel_src.as_u32 << 32 |
1030                (u64)tp->tunnel_src.as_u32);
1031
1032     hash_unset (gm->tunnel_by_key, key);
1033     mpls_sw_interface_enable_disable(mm, hi->sw_if_index, 0);
1034     ip4_sw_interface_enable_disable(hi->sw_if_index, 0);
1035
1036     vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1037                                  0 /* admin down */);
1038     vec_add1 (mm->free_gre_sw_if_indices, tp->hw_if_index);
1039     vec_free (tp->rewrite_data);
1040     fib_node_deinit(&tp->mgt_node);
1041     pool_put (mm->gre_tunnels, tp);
1042
1043     return 0;
1044 }
1045
1046 int
1047 vnet_mpls_gre_add_del_tunnel (ip4_address_t *src,
1048                               ip4_address_t *dst,
1049                               ip4_address_t *intfc,
1050                               u32 mask_width,
1051                               u32 inner_fib_id, u32 outer_fib_id,
1052                               u32 * tunnel_sw_if_index,
1053                               u8 l2_only,
1054                               u8 is_add)
1055 {
1056     u32 inner_fib_index = 0;
1057     u32 outer_fib_index = 0;
1058     u32 dummy;
1059     ip4_main_t * im = &ip4_main;
1060   
1061     /* No questions, no answers */
1062     if (NULL == tunnel_sw_if_index)
1063         tunnel_sw_if_index = &dummy;
1064
1065     *tunnel_sw_if_index = ~0;
1066
1067     if (inner_fib_id != (u32)~0)
1068     {
1069         uword * p;
1070       
1071         p = hash_get (im->fib_index_by_table_id, inner_fib_id);
1072         if (! p)
1073             return VNET_API_ERROR_NO_SUCH_INNER_FIB;
1074         inner_fib_index = p[0];
1075     }
1076
1077     if (outer_fib_id != 0)
1078     {
1079         uword * p;
1080       
1081         p = hash_get (im->fib_index_by_table_id, outer_fib_id);
1082         if (! p)
1083             return VNET_API_ERROR_NO_SUCH_FIB;
1084         outer_fib_index = p[0];
1085     }
1086
1087     if (is_add)
1088     {
1089         return (mpls_gre_tunnel_add(src,dst,intfc, mask_width,
1090                                     inner_fib_index,
1091                                     outer_fib_index,
1092                                     tunnel_sw_if_index,
1093                                     l2_only));
1094     }
1095     else
1096     {
1097         return (mpls_gre_tunnel_del(src,dst,intfc, mask_width,
1098                                     inner_fib_index,
1099                                     outer_fib_index,
1100                                     tunnel_sw_if_index,
1101                                     l2_only));
1102     }
1103 }
1104
1105 /*
1106  * Remove all mpls tunnels in the specified fib
1107  */
1108 int vnet_mpls_gre_delete_fib_tunnels (u32 fib_id)
1109 {
1110   mpls_main_t * mm = &mpls_main;
1111   vnet_main_t * vnm = mm->vnet_main;
1112   mpls_gre_tunnel_t *tp;
1113   u32 fib_index = 0;
1114   u32 * tunnels_to_delete = 0;
1115   vnet_hw_interface_t * hi;
1116   int i;
1117
1118   fib_index = ip4_fib_index_from_table_id(fib_id);
1119   if (~0 == fib_index)
1120       return VNET_API_ERROR_NO_SUCH_INNER_FIB;
1121
1122   pool_foreach (tp, mm->gre_tunnels, 
1123     ({
1124       if (tp->inner_fib_index == fib_index) 
1125         vec_add1 (tunnels_to_delete, tp - mm->gre_tunnels);
1126     }));
1127   
1128   for (i = 0; i < vec_len(tunnels_to_delete); i++) {
1129       tp = pool_elt_at_index (mm->gre_tunnels, tunnels_to_delete[i]);
1130
1131       /* Delete, the route if not already gone */
1132       if (FIB_NODE_INDEX_INVALID != tp->fei && !tp->l2_only) 
1133       {
1134           const fib_prefix_t tun_dst_pfx = {
1135               .fp_len = 32,
1136               .fp_proto = FIB_PROTOCOL_IP4,
1137               .fp_addr = {
1138                   .ip4 = tp->tunnel_dst,
1139               }
1140           };
1141
1142           fib_entry_child_remove(tp->fei,
1143                                  tp->sibling_index);
1144           fib_table_entry_special_remove(tp->outer_fib_index,
1145                                          &tun_dst_pfx,
1146                                          FIB_SOURCE_RR);
1147           tp->fei = FIB_NODE_INDEX_INVALID;
1148           adj_unlock(tp->adj_index);
1149  
1150           const fib_prefix_t tun_sub_net_pfx = {
1151               .fp_len = tp->mask_width,
1152               .fp_proto = FIB_PROTOCOL_IP4,
1153               .fp_addr = {
1154                   .ip4 = tp->intfc_address,
1155               },
1156           };
1157
1158           fib_table_entry_delete(tp->inner_fib_index,
1159                                  &tun_sub_net_pfx,
1160                                  FIB_SOURCE_INTERFACE);
1161       }
1162       
1163       hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1164       vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1165                                    0 /* admin down */);
1166       vec_add1 (mm->free_gre_sw_if_indices, tp->hw_if_index);
1167       vec_free (tp->rewrite_data);
1168       pool_put (mm->gre_tunnels, tp);
1169   }
1170   
1171   vec_free(tunnels_to_delete);
1172   
1173   return (0);
1174 }
1175
1176 static clib_error_t *
1177 create_mpls_gre_tunnel_command_fn (vlib_main_t * vm,
1178                  unformat_input_t * input,
1179                  vlib_cli_command_t * cmd)
1180 {
1181   unformat_input_t _line_input, * line_input = &_line_input;
1182   ip4_address_t src, dst, intfc;
1183   int src_set = 0, dst_set = 0, intfc_set = 0;
1184   u32 mask_width;
1185   u32 inner_fib_id = (u32)~0;
1186   u32 outer_fib_id = 0;
1187   int rv;
1188   u8 is_del = 0;
1189   u8 l2_only = 0;
1190   u32 tunnel_intfc_sw_if_index = ~0;
1191   
1192   /* Get a line of input. */
1193   if (! unformat_user (input, unformat_line_input, line_input))
1194     return 0;
1195
1196   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1197     {
1198       if (unformat (line_input, "src %U", 
1199                     unformat_ip4_address, &src))
1200         src_set = 1;
1201       else if (unformat (line_input, "dst %U", 
1202                          unformat_ip4_address, &dst))
1203         dst_set = 1;
1204       else if (unformat (line_input, "intfc %U/%d", 
1205                          unformat_ip4_address, &intfc, &mask_width))
1206         intfc_set = 1;
1207       else if (unformat (line_input, "inner-fib-id %d", &inner_fib_id))
1208         ;
1209       else if (unformat (line_input, "outer-fib-id %d", &outer_fib_id))
1210         ;
1211       else if (unformat (line_input, "del"))
1212         is_del = 1;
1213       else if (unformat (line_input, "l2-only"))
1214         l2_only = 1;
1215       else
1216         return clib_error_return (0, "unknown input '%U'",
1217                                   format_unformat_error, line_input);
1218     }
1219
1220   if (!src_set)
1221     return clib_error_return (0, "missing: src <ip-address>");
1222           
1223   if (!dst_set)
1224     return clib_error_return (0, "missing: dst <ip-address>");
1225           
1226   if (!intfc_set)
1227     return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
1228           
1229
1230   rv = vnet_mpls_gre_add_del_tunnel (&src, &dst, &intfc, mask_width, 
1231                                      inner_fib_id, outer_fib_id, 
1232                                      &tunnel_intfc_sw_if_index, 
1233                                      l2_only, !is_del);
1234
1235   switch (rv) 
1236     {
1237     case 0:
1238       if (!is_del)
1239         vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), tunnel_intfc_sw_if_index);
1240       break;
1241
1242     case VNET_API_ERROR_NO_SUCH_INNER_FIB:
1243       return clib_error_return (0, "inner fib ID %d doesn't exist\n",
1244                                 inner_fib_id);
1245     case VNET_API_ERROR_NO_SUCH_FIB:
1246       return clib_error_return (0, "outer fib ID %d doesn't exist\n",
1247                                 outer_fib_id);
1248
1249     case VNET_API_ERROR_NO_SUCH_ENTRY:
1250       return clib_error_return (0, "tunnel not found\n");
1251
1252     case VNET_API_ERROR_NO_SUCH_LABEL:
1253       /* 
1254        * This happens when there's no MPLS label for the dst address
1255        * no need for two error messages.
1256        */
1257       break;
1258       
1259     default:
1260       return clib_error_return (0, "vnet_mpls_gre_add_del_tunnel returned %d",
1261                                 rv);
1262     }
1263   return 0;
1264 }
1265
1266 VLIB_CLI_COMMAND (create_mpls_tunnel_command, static) = {
1267   .path = "create mpls gre tunnel",
1268   .short_help = 
1269   "create mpls gre tunnel [del] src <addr> dst <addr> intfc <addr>/<mw>",
1270   .function = create_mpls_gre_tunnel_command_fn,
1271 };
1272
1273 u8 * format_mpls_encap_index (u8 * s, va_list * args)
1274 {
1275   mpls_main_t * mm = va_arg (*args, mpls_main_t *);
1276   u32 entry_index = va_arg (*args, u32);
1277   mpls_encap_t * e;
1278   int i;
1279
1280   e = pool_elt_at_index (mm->encaps, entry_index);
1281   
1282   for (i = 0; i < vec_len (e->labels); i++)
1283     s = format 
1284         (s, "%d ", vnet_mpls_uc_get_label(clib_net_to_host_u32 
1285                                           (e->labels[i].label_exp_s_ttl)));
1286
1287   return s;
1288 }
1289
1290 u8 * format_mpls_gre_tunnel (u8 * s, va_list * args)
1291 {
1292   mpls_gre_tunnel_t * t = va_arg (*args, mpls_gre_tunnel_t *);
1293   mpls_main_t * mm = &mpls_main;
1294   
1295   if (t->l2_only == 0)
1296     {
1297       s = format (s, "[%d]: src %U, dst %U, adj %U/%d, labels %U\n",
1298                   t - mm->gre_tunnels, 
1299                   format_ip4_address, &t->tunnel_src,
1300                   format_ip4_address, &t->tunnel_dst,
1301                   format_ip4_address, &t->intfc_address,
1302                   t->mask_width, 
1303                   format_mpls_encap_index, mm, t->encap_index);
1304
1305       s = format (s, "      inner fib index %d, outer fib index %d",
1306                   t->inner_fib_index, t->outer_fib_index);
1307     }
1308   else
1309     {
1310       s = format (s, "[%d]: src %U, dst %U, key %U, labels %U\n",
1311                   t - mm->gre_tunnels, 
1312                   format_ip4_address, &t->tunnel_src,
1313                   format_ip4_address, &t->tunnel_dst,
1314                   format_ip4_address, &t->intfc_address,
1315                   format_mpls_encap_index, mm, t->encap_index);
1316
1317       s = format (s, "      l2 interface %d, outer fib index %d",
1318                   t->hw_if_index, t->outer_fib_index);
1319     }
1320
1321   return s;
1322 }
1323
1324 u8 * format_mpls_ethernet_tunnel (u8 * s, va_list * args)
1325 {
1326   mpls_eth_tunnel_t * t = va_arg (*args, mpls_eth_tunnel_t *);
1327   mpls_main_t * mm = &mpls_main;
1328   
1329   s = format (s, "[%d]: dst %U, adj %U/%d, labels %U\n",
1330               t - mm->eth_tunnels, 
1331               format_ethernet_address, &t->tunnel_dst,
1332               format_ip4_address, &t->intfc_address,
1333               t->mask_width, 
1334               format_mpls_encap_index, mm, t->encap_index);
1335
1336
1337   s = format (s, "      tx on %U, rx fib index %d", 
1338               format_vnet_sw_if_index_name, mm->vnet_main, t->tx_sw_if_index,
1339               t->inner_fib_index);
1340
1341   return s;
1342 }
1343
1344 static clib_error_t *
1345 show_mpls_tunnel_command_fn (vlib_main_t * vm,
1346                              unformat_input_t * input,
1347                              vlib_cli_command_t * cmd)
1348 {
1349   mpls_main_t * mm = &mpls_main;
1350   mpls_gre_tunnel_t * gt;
1351   mpls_eth_tunnel_t * et;
1352
1353   if (pool_elts (mm->gre_tunnels))
1354     {
1355       vlib_cli_output (vm, "MPLS-GRE tunnels");
1356       pool_foreach (gt, mm->gre_tunnels,
1357       ({
1358         vlib_cli_output (vm, "%U", format_mpls_gre_tunnel, gt);
1359       }));
1360     }
1361   else
1362     vlib_cli_output (vm, "No MPLS-GRE tunnels");
1363
1364   if (pool_elts (mm->eth_tunnels))
1365     {
1366       vlib_cli_output (vm, "MPLS-Ethernet tunnels");
1367       pool_foreach (et, mm->eth_tunnels,
1368       ({
1369         vlib_cli_output (vm, "%U", format_mpls_ethernet_tunnel, et);
1370       }));
1371     }
1372   else
1373     vlib_cli_output (vm, "No MPLS-Ethernet tunnels");
1374
1375   return 0;
1376 }
1377
1378 VLIB_CLI_COMMAND (show_mpls_tunnel_command, static) = {
1379     .path = "show mpls tunnel",
1380     .short_help = "show mpls tunnel",
1381     .function = show_mpls_tunnel_command_fn,
1382 };
1383
1384
1385 /* force inclusion from application's main.c */
1386 clib_error_t *mpls_interface_init (vlib_main_t *vm)
1387 {
1388   clib_error_t * error;
1389
1390   fib_node_register_type(FIB_NODE_TYPE_MPLS_GRE_TUNNEL,
1391                          &mpls_gre_vft);
1392
1393   if ((error = vlib_call_init_function (vm, mpls_policy_encap_init)))
1394       return error;
1395
1396   return 0;
1397 }
1398 VLIB_INIT_FUNCTION(mpls_interface_init);
1399
1400
1401 static u8 * mpls_ethernet_rewrite (mpls_main_t *mm, mpls_eth_tunnel_t * t)
1402 {
1403   u8 * rewrite_data = 0;
1404   mpls_encap_t * e;
1405   mpls_unicast_header_t *lp0;
1406   int i;
1407
1408  /* look up the encap label stack using the RX FIB and adjacency address*/
1409   e = mpls_encap_by_fib_and_dest (mm, t->inner_fib_index, 
1410                                   t->intfc_address.as_u32);
1411
1412   if (e == 0)
1413     {
1414       clib_warning ("no label for inner fib index %d, dst %U",
1415                     t->inner_fib_index, format_ip4_address, 
1416                     &t->intfc_address);
1417       return 0;
1418     }
1419  
1420   vec_validate (rewrite_data, 
1421                 sizeof (mpls_unicast_header_t) * vec_len(e->labels) -1);
1422
1423   /* Copy the encap label stack */
1424   lp0 = (mpls_unicast_header_t *) rewrite_data;
1425   
1426   for (i = 0; i < vec_len(e->labels); i++)
1427     lp0[i] = e->labels[i];
1428   
1429   return (rewrite_data);
1430 }
1431
1432 int vnet_mpls_ethernet_add_del_tunnel (u8 *dst,
1433                                        ip4_address_t *intfc,
1434                                        u32 mask_width,
1435                                        u32 inner_fib_id, 
1436                                        u32 tx_sw_if_index,
1437                                        u32 * tunnel_sw_if_index,
1438                                        u8 l2_only,
1439                                        u8 is_add)
1440 {
1441   ip4_main_t * im = &ip4_main;
1442   ip_lookup_main_t * lm = &im->lookup_main;
1443   mpls_main_t * mm = &mpls_main;
1444   vnet_main_t * vnm = vnet_get_main();
1445   mpls_eth_tunnel_t *tp;
1446   u32 inner_fib_index = 0;
1447   ip_adjacency_t adj;
1448   u32 adj_index;
1449   u8 * rewrite_data;
1450   int found_tunnel = 0;
1451   mpls_encap_t * e = 0;
1452   u32 hw_if_index = ~0;
1453   vnet_hw_interface_t * hi;
1454   u32 slot;
1455   u32 dummy;
1456   
1457   if (tunnel_sw_if_index == 0)
1458     tunnel_sw_if_index = &dummy;
1459
1460   *tunnel_sw_if_index = ~0;
1461
1462   if (inner_fib_id != (u32)~0)
1463     {
1464       uword * p;
1465       
1466       p = hash_get (im->fib_index_by_table_id, inner_fib_id);
1467       if (! p)
1468         return VNET_API_ERROR_NO_SUCH_FIB;
1469       inner_fib_index = p[0];
1470     }
1471
1472   /* suppress duplicate mpls interface generation. */
1473   pool_foreach (tp, mm->eth_tunnels, 
1474   ({
1475     /* 
1476      * If we have a tunnel which matches (src, dst, intfc/mask)
1477      * AND the expected route is in the FIB, it's a dup 
1478      */
1479     if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
1480         && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
1481         && tp->inner_fib_index == inner_fib_index
1482         && FIB_NODE_INDEX_INVALID != tp->fei)
1483       {
1484         found_tunnel = 1;
1485
1486         if (is_add)
1487           {
1488             if (l2_only)
1489               return 1;
1490             else
1491               {
1492                 e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, 
1493                                                 intfc->as_u32);
1494                 if (e == 0)
1495                   return VNET_API_ERROR_NO_SUCH_LABEL;
1496                 
1497                 goto reinstall_it;
1498               }
1499           }
1500         else
1501           {
1502             /* Delete */
1503             goto add_del_route;
1504           }
1505
1506       }
1507   }));
1508     
1509   /* Delete, and we can't find the tunnel */
1510   if (is_add == 0 && found_tunnel == 0)
1511     return VNET_API_ERROR_NO_SUCH_ENTRY;
1512
1513   e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, intfc->as_u32);
1514   if (e == 0)
1515     return VNET_API_ERROR_NO_SUCH_LABEL;
1516
1517   pool_get(mm->eth_tunnels, tp);
1518   memset (tp, 0, sizeof (*tp));
1519     
1520   if (vec_len (mm->free_eth_sw_if_indices) > 0)
1521     {
1522       hw_if_index = 
1523         mm->free_eth_sw_if_indices[vec_len(mm->free_eth_sw_if_indices)-1];
1524       _vec_len (mm->free_eth_sw_if_indices) -= 1;
1525       hi = vnet_get_hw_interface (vnm, hw_if_index);
1526       hi->dev_instance = tp - mm->eth_tunnels;
1527       hi->hw_instance = tp - mm->eth_tunnels;
1528     }
1529   else 
1530     {
1531       hw_if_index = vnet_register_interface
1532         (vnm, mpls_eth_device_class.index, tp - mm->eth_tunnels,
1533          mpls_eth_hw_interface_class.index,
1534          tp - mm->eth_tunnels);
1535       hi = vnet_get_hw_interface (vnm, hw_if_index);
1536
1537       /* ... to make the IP and L2 x-connect cases identical */
1538       slot = vlib_node_add_named_next_with_slot
1539         (vnm->vlib_main, hi->tx_node_index, 
1540          "interface-output", MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1541
1542       ASSERT (slot == MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1543     }
1544   
1545   *tunnel_sw_if_index = hi->sw_if_index;
1546   vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1547                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);      
1548
1549   tp->hw_if_index = hw_if_index;
1550
1551  reinstall_it:
1552   clib_memcpy(tp->tunnel_dst, dst, sizeof (tp->tunnel_dst));
1553   tp->intfc_address.as_u32 = intfc->as_u32;
1554   tp->mask_width = mask_width;
1555   tp->inner_fib_index = inner_fib_index;
1556   tp->encap_index = e - mm->encaps;
1557   tp->tx_sw_if_index = tx_sw_if_index;
1558   tp->l2_only = l2_only;
1559
1560   /* Create the adjacency and add to v4 fib */
1561   memset(&adj, 0, sizeof (adj));
1562   adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
1563     
1564   rewrite_data = mpls_ethernet_rewrite (mm, tp);
1565   if (rewrite_data == 0)
1566     {
1567       if (*tunnel_sw_if_index != ~0)
1568         {
1569           hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1570           vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1571                                        0 /* admin down */);
1572           vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
1573       }
1574
1575       pool_put (mm->eth_tunnels, tp);
1576       return VNET_API_ERROR_NO_SUCH_LABEL;
1577     }
1578   
1579   vnet_rewrite_for_sw_interface
1580     (vnm,
1581      VNET_L3_PACKET_TYPE_MPLS_UNICAST, 
1582      tx_sw_if_index,
1583      ip4_rewrite_node.index,
1584      tp->tunnel_dst,
1585      &adj.rewrite_header,
1586      sizeof (adj.rewrite_data));
1587   
1588   /* 
1589    * Prepend the (0,1,2) VLAN tag ethernet header 
1590    * we just built to the mpls header stack
1591    */
1592   vec_insert (rewrite_data, adj.rewrite_header.data_bytes, 0);
1593   clib_memcpy(rewrite_data, 
1594          vnet_rewrite_get_data_internal(&adj.rewrite_header, 
1595                                         sizeof (adj.rewrite_data)),
1596          adj.rewrite_header.data_bytes);
1597
1598   vnet_rewrite_set_data_internal (&adj.rewrite_header, 
1599                                   sizeof(adj.rewrite_data),
1600                                   rewrite_data, 
1601                                   vec_len(rewrite_data));
1602   
1603   vec_free (tp->rewrite_data);
1604   
1605   tp->rewrite_data = rewrite_data;
1606
1607   if (!l2_only)
1608     ip_add_adjacency (lm, &adj, 1 /* one adj */,
1609                       &adj_index);
1610   
1611  add_del_route:
1612
1613   if (!l2_only)
1614     {
1615       const fib_prefix_t pfx = {
1616           .fp_addr = {
1617               .ip4 = tp->intfc_address,
1618           },
1619           .fp_len = tp->mask_width,
1620           .fp_proto = FIB_PROTOCOL_IP4,
1621       };
1622       if (is_add)
1623           tp->fei = fib_table_entry_special_add(tp->inner_fib_index,
1624                                                 &pfx,
1625                                                 FIB_SOURCE_API,
1626                                                 FIB_ENTRY_FLAG_NONE,
1627                                                 adj_index);
1628       else
1629         {
1630           fib_table_entry_delete(tp->inner_fib_index, &pfx, FIB_SOURCE_API);
1631           tp->fei = FIB_NODE_INDEX_INVALID;
1632         }
1633     }
1634   if (is_add == 0 && found_tunnel)
1635     {
1636       hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1637       vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1638                                    0 /* admin down */);
1639       vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
1640       vec_free (tp->rewrite_data);
1641       pool_put (mm->eth_tunnels, tp);
1642     }
1643
1644   return 0;
1645 }
1646
1647 static clib_error_t *
1648 create_mpls_ethernet_tunnel_command_fn (vlib_main_t * vm,
1649                                         unformat_input_t * input,
1650                                         vlib_cli_command_t * cmd)
1651 {
1652   unformat_input_t _line_input, * line_input = &_line_input;
1653   vnet_main_t * vnm = vnet_get_main();
1654   ip4_address_t intfc;
1655   int adj_set = 0;
1656   u8 dst[6];
1657   int dst_set = 0, intfc_set = 0;
1658   u32 mask_width;
1659   u32 inner_fib_id = (u32)~0;
1660   int rv;
1661   u8 is_del = 0;
1662   u8 l2_only = 0;
1663   u32 tx_sw_if_index;
1664   u32 sw_if_index = ~0;
1665   
1666   /* Get a line of input. */
1667   if (! unformat_user (input, unformat_line_input, line_input))
1668     return 0;
1669
1670   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1671     {
1672       if (unformat (line_input, "dst %U", 
1673                     unformat_ethernet_address, &dst))
1674         dst_set = 1;
1675       else if (unformat (line_input, "adj %U/%d", 
1676                          unformat_ip4_address, &intfc, &mask_width))
1677         adj_set = 1;
1678       else if (unformat (line_input, "tx-intfc %U", 
1679                          unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
1680         intfc_set = 1;
1681       else if (unformat (line_input, "fib-id %d", &inner_fib_id))
1682         ;
1683       else if (unformat (line_input, "l2-only"))
1684         l2_only = 1;
1685       else if (unformat (line_input, "del"))
1686         is_del = 1;
1687       else
1688         return clib_error_return (0, "unknown input '%U'",
1689                                   format_unformat_error, line_input);
1690     }
1691
1692   if (!intfc_set)
1693     return clib_error_return (0, "missing tx-intfc");
1694
1695   if (!dst_set)
1696     return clib_error_return (0, "missing: dst <ethernet-address>");
1697           
1698   if (!adj_set)
1699     return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
1700   
1701
1702   rv = vnet_mpls_ethernet_add_del_tunnel (dst, &intfc, mask_width, 
1703                                           inner_fib_id, tx_sw_if_index, 
1704                                           &sw_if_index,
1705                                           l2_only, !is_del);
1706
1707   switch (rv) 
1708     {
1709     case 0:
1710       if (!is_del)
1711         vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), sw_if_index);
1712       break;
1713     case VNET_API_ERROR_NO_SUCH_FIB:
1714       return clib_error_return (0, "rx fib ID %d doesn't exist\n",
1715                                 inner_fib_id);
1716
1717     case VNET_API_ERROR_NO_SUCH_ENTRY:
1718       return clib_error_return (0, "tunnel not found\n");
1719
1720     case VNET_API_ERROR_NO_SUCH_LABEL:
1721       /* 
1722        * This happens when there's no MPLS label for the dst address
1723        * no need for two error messages.
1724        */
1725         return clib_error_return (0, "no label for %U in fib %d", 
1726                                   format_ip4_address, &intfc, inner_fib_id);
1727       break;
1728       
1729     default:
1730       return clib_error_return (0, "vnet_mpls_ethernet_add_del_tunnel returned %d", rv);
1731       break;
1732     }
1733   return 0;
1734 }
1735
1736
1737 VLIB_CLI_COMMAND (create_mpls_ethernet_tunnel_command, static) = {
1738   .path = "create mpls ethernet tunnel",
1739   .short_help = 
1740   "create mpls ethernet tunnel [del] dst <mac-addr> intfc <addr>/<mw>",
1741   .function = create_mpls_ethernet_tunnel_command_fn,
1742 };
1743
1744
1745 int vnet_mpls_policy_tunnel_add_rewrite (mpls_main_t * mm, 
1746                                          mpls_encap_t * e, 
1747                                          u32 policy_tunnel_index)
1748 {
1749   mpls_eth_tunnel_t * t;
1750   ip_adjacency_t adj;
1751   u8 * rewrite_data = 0;
1752   u8 * label_start;
1753   mpls_unicast_header_t *lp;
1754   int i;
1755
1756   if (pool_is_free_index (mm->eth_tunnels, policy_tunnel_index))
1757     return VNET_API_ERROR_NO_SUCH_ENTRY;
1758
1759   t = pool_elt_at_index (mm->eth_tunnels, policy_tunnel_index);
1760
1761   memset (&adj, 0, sizeof (adj));
1762
1763   /* Build L2 encap */
1764   vnet_rewrite_for_sw_interface
1765     (mm->vnet_main, 
1766      VNET_L3_PACKET_TYPE_MPLS_UNICAST, 
1767      t->tx_sw_if_index,
1768      mpls_policy_encap_node.index,
1769      t->tunnel_dst,
1770      &adj.rewrite_header,
1771      sizeof (adj.rewrite_data));
1772   
1773   vec_validate (rewrite_data, adj.rewrite_header.data_bytes -1);
1774
1775   clib_memcpy(rewrite_data, 
1776          vnet_rewrite_get_data_internal(&adj.rewrite_header, 
1777                                         sizeof (adj.rewrite_data)),
1778          adj.rewrite_header.data_bytes);
1779
1780   /* Append the label stack */
1781
1782   vec_add2 (rewrite_data, label_start, vec_len(e->labels) * sizeof (u32));
1783
1784   lp = (mpls_unicast_header_t *) label_start;
1785
1786   for (i = 0; i < vec_len(e->labels); i++)
1787     lp[i] = e->labels[i];
1788   
1789   /* Remember the rewrite data */
1790   e->rewrite = rewrite_data;
1791   e->output_next_index = adj.rewrite_header.next_index;
1792
1793   return 0;
1794 }
1795
1796 int vnet_mpls_ethernet_add_del_policy_tunnel (u8 *dst,
1797                                               ip4_address_t *intfc,
1798                                               u32 mask_width,
1799                                               u32 inner_fib_id, 
1800                                               u32 tx_sw_if_index,
1801                                               u32 * tunnel_sw_if_index,
1802                                               u32 classify_table_index,
1803                                               u32 * new_tunnel_index,
1804                                               u8 l2_only,
1805                                               u8 is_add)
1806 {
1807   ip4_main_t * im = &ip4_main;
1808   mpls_main_t * mm = &mpls_main;
1809   vnet_main_t * vnm = vnet_get_main();
1810   mpls_eth_tunnel_t *tp;
1811   u32 inner_fib_index = 0;
1812   int found_tunnel = 0;
1813   mpls_encap_t * e = 0;
1814   u32 hw_if_index = ~0;
1815   vnet_hw_interface_t * hi;
1816   u32 slot;
1817   u32 dummy;
1818   
1819   if (tunnel_sw_if_index == 0)
1820     tunnel_sw_if_index = &dummy;
1821
1822   *tunnel_sw_if_index = ~0;
1823
1824   if (inner_fib_id != (u32)~0)
1825     {
1826       uword * p;
1827       
1828       p = hash_get (im->fib_index_by_table_id, inner_fib_id);
1829       if (! p)
1830         return VNET_API_ERROR_NO_SUCH_FIB;
1831       inner_fib_index = p[0];
1832     }
1833
1834   /* suppress duplicate mpls interface generation. */
1835   pool_foreach (tp, mm->eth_tunnels, 
1836   ({
1837     /* 
1838      * If we have a tunnel which matches (src, dst, intfc/mask)
1839      * AND the expected route is in the FIB, it's a dup 
1840      */
1841     if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
1842         && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
1843         && tp->inner_fib_index == inner_fib_index
1844         && FIB_NODE_INDEX_INVALID != tp->fei)
1845       {
1846         found_tunnel = 1;
1847
1848         if (is_add)
1849           {
1850             if (l2_only)
1851               return 1;
1852             else
1853               {
1854                 goto reinstall_it;
1855               }
1856           }
1857         else
1858           {
1859             /* Delete */
1860             goto add_del_route;
1861           }
1862
1863       }
1864   }));
1865     
1866   /* Delete, and we can't find the tunnel */
1867   if (is_add == 0 && found_tunnel == 0)
1868     return VNET_API_ERROR_NO_SUCH_ENTRY;
1869
1870   pool_get(mm->eth_tunnels, tp);
1871   memset (tp, 0, sizeof (*tp));
1872     
1873   if (vec_len (mm->free_eth_sw_if_indices) > 0)
1874     {
1875       hw_if_index = 
1876         mm->free_eth_sw_if_indices[vec_len(mm->free_eth_sw_if_indices)-1];
1877       _vec_len (mm->free_eth_sw_if_indices) -= 1;
1878       hi = vnet_get_hw_interface (vnm, hw_if_index);
1879       hi->dev_instance = tp - mm->eth_tunnels;
1880       hi->hw_instance = tp - mm->eth_tunnels;
1881     }
1882   else 
1883     {
1884       hw_if_index = vnet_register_interface
1885         (vnm, mpls_eth_device_class.index, tp - mm->eth_tunnels,
1886          mpls_eth_hw_interface_class.index,
1887          tp - mm->eth_tunnels);
1888       hi = vnet_get_hw_interface (vnm, hw_if_index);
1889
1890       /* ... to make the IP and L2 x-connect cases identical */
1891       slot = vlib_node_add_named_next_with_slot
1892         (vnm->vlib_main, hi->tx_node_index, 
1893          "interface-output", MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1894
1895       ASSERT (slot == MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1896     }
1897   
1898   *tunnel_sw_if_index = hi->sw_if_index;
1899   vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1900                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);      
1901
1902   tp->hw_if_index = hw_if_index;
1903
1904  reinstall_it:
1905   clib_memcpy(tp->tunnel_dst, dst, sizeof (tp->tunnel_dst));
1906   tp->intfc_address.as_u32 = intfc->as_u32;
1907   tp->mask_width = mask_width;
1908   tp->inner_fib_index = inner_fib_index;
1909   tp->encap_index = e - mm->encaps;
1910   tp->tx_sw_if_index = tx_sw_if_index;
1911   tp->l2_only = l2_only;
1912   tp->fei = FIB_NODE_INDEX_INVALID;
1913
1914   if (new_tunnel_index)
1915     *new_tunnel_index = tp - mm->eth_tunnels;
1916
1917  add_del_route:
1918
1919   if (!l2_only)
1920     {
1921       const fib_prefix_t pfx = {
1922           .fp_addr = {
1923               .ip4 = tp->intfc_address,
1924           },
1925           .fp_len = tp->mask_width,
1926           .fp_proto = FIB_PROTOCOL_IP4,
1927       };
1928       dpo_id_t dpo = DPO_NULL;
1929
1930       if (is_add)
1931         {
1932           dpo_set(&dpo,
1933                   DPO_CLASSIFY,
1934                   DPO_PROTO_IP4,
1935                   classify_dpo_create(FIB_PROTOCOL_IP4,
1936                                       classify_table_index));
1937
1938           tp->fei = fib_table_entry_special_dpo_add(tp->inner_fib_index,
1939                                                     &pfx,
1940                                                     FIB_SOURCE_API,
1941                                                     FIB_ENTRY_FLAG_EXCLUSIVE,
1942                                                     &dpo);
1943           dpo_reset(&dpo);
1944         }
1945       else
1946         {
1947           fib_table_entry_delete(tp->inner_fib_index, &pfx, FIB_SOURCE_API);
1948           tp->fei = FIB_NODE_INDEX_INVALID;
1949         }
1950     }
1951   if (is_add == 0 && found_tunnel)
1952     {
1953       hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1954       vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1955                                    0 /* admin down */);
1956       vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
1957       pool_put (mm->eth_tunnels, tp);
1958     }
1959
1960   return 0;
1961 }
1962
1963 static clib_error_t *
1964 create_mpls_ethernet_policy_tunnel_command_fn (vlib_main_t * vm,
1965                                                unformat_input_t * input,
1966                                                vlib_cli_command_t * cmd)
1967 {
1968   unformat_input_t _line_input, * line_input = &_line_input;
1969   vnet_main_t * vnm = vnet_get_main();
1970   ip4_address_t intfc;
1971   int adj_set = 0;
1972   u8 dst[6];
1973   int dst_set = 0, intfc_set = 0;
1974   u32 mask_width;
1975   u32 inner_fib_id = (u32)~0;
1976   u32 classify_table_index = (u32)~0;
1977   u32 new_tunnel_index;
1978   int rv;
1979   u8 is_del = 0;
1980   u8 l2_only = 0;
1981   u32 tx_sw_if_index;
1982   
1983   /* Get a line of input. */
1984   if (! unformat_user (input, unformat_line_input, line_input))
1985     return 0;
1986
1987   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1988     {
1989       if (unformat (line_input, "dst %U", 
1990                     unformat_ethernet_address, &dst))
1991         dst_set = 1;
1992       else if (unformat (line_input, "adj %U/%d", 
1993                          unformat_ip4_address, &intfc, &mask_width))
1994         adj_set = 1;
1995       else if (unformat (line_input, "tx-intfc %U", 
1996                          unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
1997         intfc_set = 1;
1998       else if (unformat (line_input, "classify-table-index %d",
1999                          &classify_table_index))
2000         ;
2001       else if (unformat (line_input, "fib-id %d", &inner_fib_id))
2002         ;
2003       else if (unformat (line_input, "l2-only"))
2004         l2_only = 1;
2005       else if (unformat (line_input, "del"))
2006         is_del = 1;
2007       else
2008         return clib_error_return (0, "unknown input '%U'",
2009                                   format_unformat_error, line_input);
2010     }
2011
2012   if (classify_table_index == ~0)
2013     return clib_error_return (0, "missing classify_table_index");
2014
2015   if (!intfc_set)
2016     return clib_error_return (0, "missing tx-intfc");
2017
2018   if (!dst_set)
2019     return clib_error_return (0, "missing: dst <ethernet-address>");
2020           
2021   if (!adj_set)
2022     return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
2023   
2024
2025   rv = vnet_mpls_ethernet_add_del_policy_tunnel (dst, &intfc, mask_width, 
2026                                                  inner_fib_id, tx_sw_if_index, 
2027                                                  0 /* tunnel sw_if_index */, 
2028                                                  classify_table_index,
2029                                                  &new_tunnel_index,
2030                                                  l2_only, !is_del);
2031   switch (rv) 
2032     {
2033     case 0:
2034       if (!is_del)
2035         vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), new_tunnel_index);
2036       break;
2037     case VNET_API_ERROR_NO_SUCH_FIB:
2038       return clib_error_return (0, "rx fib ID %d doesn't exist\n",
2039                                 inner_fib_id);
2040
2041     case VNET_API_ERROR_NO_SUCH_ENTRY:
2042       return clib_error_return (0, "tunnel not found\n");
2043
2044     case VNET_API_ERROR_NO_SUCH_LABEL:
2045       /* 
2046        * This happens when there's no MPLS label for the dst address
2047        * no need for two error messages.
2048        */
2049         return clib_error_return (0, "no label for %U in fib %d", 
2050                                   format_ip4_address, &intfc, inner_fib_id);
2051       break;
2052       
2053     default:
2054       return clib_error_return (0, "vnet_mpls_ethernet_add_del_policy_tunnel returned %d", rv);
2055       break;
2056     }
2057
2058   return 0;
2059 }
2060
2061 VLIB_CLI_COMMAND (create_mpls_ethernet_policy_tunnel_command, static) = {
2062   .path = "create mpls ethernet policy tunnel",
2063   .short_help = 
2064   "create mpls ethernet policy tunnel [del] dst <mac-addr> intfc <addr>/<mw>\n"
2065   " classify-table-index <nn>",
2066   .function = create_mpls_ethernet_policy_tunnel_command_fn,
2067 };
2068
2069 static clib_error_t *
2070 mpls_interface_enable_disable (vlib_main_t * vm,
2071                                unformat_input_t * input,
2072                                vlib_cli_command_t * cmd)
2073 {
2074   vnet_main_t * vnm = vnet_get_main();
2075   clib_error_t * error = 0;
2076   u32 sw_if_index, enable;
2077
2078   sw_if_index = ~0;
2079
2080   if (! unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
2081     {
2082       error = clib_error_return (0, "unknown interface `%U'",
2083                                  format_unformat_error, input);
2084       goto done;
2085     }
2086
2087   if (unformat (input, "enable"))
2088       enable = 1;
2089   else if (unformat (input, "disable"))
2090       enable = 0;
2091   else
2092     {
2093       error = clib_error_return (0, "expected 'enable' or 'disable'",
2094                                  format_unformat_error, input);
2095       goto done;
2096     }
2097
2098   mpls_sw_interface_enable_disable(&mpls_main, sw_if_index, enable);
2099
2100  done:
2101   return error;
2102 }
2103
2104 VLIB_CLI_COMMAND (set_interface_ip_table_command, static) = {
2105   .path = "set interface mpls",
2106   .function = mpls_interface_enable_disable,
2107   .short_help = "Enable/Disable an interface for MPLS forwarding",
2108 };