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