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