a0f6f2f2474b34a109c732abe4022bab585b78f9
[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/mpls/mpls.h>
21 #include <vnet/fib/ip4_fib.h>
22 #include <vnet/adj/adj_midchain.h>
23 #include <vnet/dpo/classify_dpo.h>
24
25 /* manually added to the interface output node */
26 #define MPLS_ETH_OUTPUT_NEXT_OUTPUT     1
27
28 static uword
29 mpls_eth_interface_tx (vlib_main_t * vm,
30                        vlib_node_runtime_t * node,
31                        vlib_frame_t * frame)
32 {
33   mpls_main_t * gm = &mpls_main;
34   vnet_main_t * vnm = gm->vnet_main;
35   u32 next_index;
36   u32 * from, * to_next, n_left_from, n_left_to_next;
37
38   /* Vector of buffer / pkt indices we're supposed to process */
39   from = vlib_frame_vector_args (frame);
40
41   /* Number of buffers / pkts */
42   n_left_from = frame->n_vectors;   
43
44   /* Speculatively send the first buffer to the last disposition we used */
45   next_index = node->cached_next_index;
46   
47   while (n_left_from > 0)
48     {
49       /* set up to enqueue to our disposition with index = next_index */
50       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
51
52       /* 
53        * As long as we have enough pkts left to process two pkts
54        * and prefetch two pkts...
55        */
56       while (n_left_from >= 4 && n_left_to_next >= 2)
57         {
58           vlib_buffer_t * b0, * b1;
59           u32 bi0, next0, bi1, next1;
60           mpls_eth_tunnel_t * t0, * t1;
61           u32 sw_if_index0, sw_if_index1;
62           vnet_hw_interface_t * hi0, * hi1;
63           u8 * dst0, * dst1;
64       
65           /* Prefetch the next iteration */
66           {
67             vlib_buffer_t * p2, * p3;
68
69             p2 = vlib_get_buffer (vm, from[2]);
70             p3 = vlib_get_buffer (vm, from[3]);
71
72             vlib_prefetch_buffer_header (p2, LOAD);
73             vlib_prefetch_buffer_header (p3, LOAD);
74
75             /* 
76              * Prefetch packet data. We expect to overwrite
77              * the inbound L2 header with an ip header and a
78              * gre header. Might want to prefetch the last line
79              * of rewrite space as well; need profile data
80              */
81             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
82             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
83           }
84
85           /* Pick up the next two buffer indices */
86           bi0 = from[0];
87           bi1 = from[1];
88
89           /* Speculatively enqueue them where we sent the last buffer */
90           to_next[0] = bi0;
91           to_next[1] = bi1;
92           from += 2;
93           to_next += 2;
94           n_left_to_next -= 2;
95           n_left_from -= 2;
96       
97           b0 = vlib_get_buffer (vm, bi0);
98           b1 = vlib_get_buffer (vm, bi1);
99
100           sw_if_index0 = vnet_buffer(b0)->sw_if_index [VLIB_TX];
101           sw_if_index1 = vnet_buffer(b1)->sw_if_index [VLIB_TX];
102
103           /* get h/w intfcs */
104           hi0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
105           hi1 = vnet_get_sup_hw_interface (vnm, sw_if_index1);
106           
107           /* hw_instance = tunnel pool index */
108           t0 = pool_elt_at_index (gm->eth_tunnels, hi0->hw_instance);
109           t1 = pool_elt_at_index (gm->eth_tunnels, hi1->hw_instance);
110
111           /* Apply rewrite - $$$$$ fixme don't use memcpy */
112           vlib_buffer_advance (b0, -(word)vec_len(t0->rewrite_data));
113           vlib_buffer_advance (b1, -(word)vec_len(t1->rewrite_data));
114
115           dst0 = vlib_buffer_get_current (b0);
116           dst1 = vlib_buffer_get_current (b1);
117
118           clib_memcpy (dst0, t0->rewrite_data, vec_len(t0->rewrite_data));
119           clib_memcpy (dst1, t1->rewrite_data, vec_len(t1->rewrite_data));
120
121           /* Fix TX fib indices */
122           vnet_buffer(b0)->sw_if_index [VLIB_TX] = t0->tx_sw_if_index;
123           vnet_buffer(b1)->sw_if_index [VLIB_TX] = t1->tx_sw_if_index;
124
125           /* mpls-post-rewrite takes it from here... */
126           next0 = MPLS_ETH_OUTPUT_NEXT_OUTPUT;
127           next1 = MPLS_ETH_OUTPUT_NEXT_OUTPUT;
128
129           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
130             {
131               mpls_eth_tx_trace_t *tr = vlib_add_trace (vm, node, 
132                                                         b0, sizeof (*tr));
133               tr->lookup_miss = 0;
134               tr->tunnel_id = t0 - gm->eth_tunnels;
135               tr->tx_sw_if_index = t0->tx_sw_if_index;
136               tr->mpls_encap_index = t0->encap_index;
137               tr->length = b0->current_length;
138               hi0 = vnet_get_sup_hw_interface (vnm, t0->tx_sw_if_index);
139               clib_memcpy (tr->dst, hi0->hw_address, sizeof (tr->dst));
140             }
141           if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) 
142             {
143               mpls_eth_tx_trace_t *tr = vlib_add_trace (vm, node, 
144                                                         b1, sizeof (*tr));
145               tr->lookup_miss = 0;
146               tr->tunnel_id = t1 - gm->eth_tunnels;
147               tr->tx_sw_if_index = t1->tx_sw_if_index;
148               tr->mpls_encap_index = t1->encap_index;
149               tr->length = b1->current_length;
150               hi1 = vnet_get_sup_hw_interface (vnm, t1->tx_sw_if_index);
151               clib_memcpy (tr->dst, hi1->hw_address, sizeof (tr->dst));
152             }
153
154           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
155                                            to_next, n_left_to_next,
156                                            bi0, bi1, next0, next1);
157         }
158       while (n_left_from > 0 && n_left_to_next > 0)
159         {
160           vlib_buffer_t * b0;
161           u32 bi0, next0;
162           mpls_eth_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->eth_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 interface */
190           vnet_buffer(b0)->sw_if_index [VLIB_TX] = t0->tx_sw_if_index;
191
192           /* Send the packet */
193           next0 = MPLS_ETH_OUTPUT_NEXT_OUTPUT;
194
195           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
196             {
197               mpls_eth_tx_trace_t *tr = vlib_add_trace (vm, node, 
198                                                         b0, sizeof (*tr));
199               tr->lookup_miss = 0;
200               tr->tunnel_id = t0 - gm->eth_tunnels;
201               tr->tx_sw_if_index = t0->tx_sw_if_index;
202               tr->mpls_encap_index = t0->encap_index;
203               tr->length = b0->current_length;
204               hi0 = vnet_get_sup_hw_interface (vnm, t0->tx_sw_if_index);
205               clib_memcpy (tr->dst, hi0->hw_address, sizeof (tr->dst));
206             }
207
208           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
209                                            to_next, n_left_to_next,
210                                            bi0, next0);
211         }
212   
213       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
214     }
215
216   vlib_node_increment_counter (vm, mpls_input_node.index,
217                                MPLS_ERROR_PKTS_ENCAP, frame->n_vectors);
218
219   return frame->n_vectors;
220 }
221
222 static u8 * format_mpls_eth_tunnel_name (u8 * s, va_list * args)
223 {
224   u32 dev_instance = va_arg (*args, u32);
225   return format (s, "mpls-eth%d", dev_instance);
226 }
227
228 static u8 * format_mpls_eth_device (u8 * s, va_list * args)
229 {
230   u32 dev_instance = va_arg (*args, u32);
231   CLIB_UNUSED (int verbose) = va_arg (*args, int);
232
233   s = format (s, "MPLS-ETH tunnel: id %d\n", dev_instance);
234   return s;
235 }
236
237 VNET_DEVICE_CLASS (mpls_eth_device_class) = {
238   .name = "MPLS-ETH tunnel device",
239   .format_device_name = format_mpls_eth_tunnel_name,
240   .format_device = format_mpls_eth_device,
241   .format_tx_trace = format_mpls_eth_tx_trace,
242   .tx_function = mpls_eth_interface_tx,
243   .no_flatten_output_chains = 1,
244 #ifdef SOON
245   .clear counter = 0;
246   .admin_up_down_function = 0;
247 #endif
248 };
249
250 VLIB_DEVICE_TX_FUNCTION_MULTIARCH (mpls_eth_device_class,
251                                    mpls_eth_interface_tx)
252
253 VNET_HW_INTERFACE_CLASS (mpls_eth_hw_interface_class) = {
254   .name = "MPLS-ETH",
255   .format_header = format_mpls_eth_header_with_length,
256 #if 0
257   .unformat_header = unformat_mpls_eth_header,
258 #endif
259   .build_rewrite = default_build_rewrite,
260   .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
261 };
262
263 u8
264 mpls_sw_interface_is_enabled (u32 sw_if_index)
265 {
266     mpls_main_t * mm = &mpls_main;
267
268     if (vec_len(mm->mpls_enabled_by_sw_if_index) < sw_if_index)
269         return (0);
270
271     return (mm->mpls_enabled_by_sw_if_index[sw_if_index]);
272 }
273
274 void
275 mpls_sw_interface_enable_disable (mpls_main_t * mm,
276                                   u32 sw_if_index,
277                                   u8 is_enable)
278 {
279   fib_node_index_t lfib_index;
280
281   vec_validate_init_empty (mm->mpls_enabled_by_sw_if_index, sw_if_index, 0);
282
283   /*
284    * enable/disable only on the 1<->0 transition
285    */
286   if (is_enable)
287     {
288       if (1 != ++mm->mpls_enabled_by_sw_if_index[sw_if_index])
289         return;
290
291       lfib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_MPLS,
292                                                      MPLS_FIB_DEFAULT_TABLE_ID);
293       vec_validate(mm->fib_index_by_sw_if_index, 0);
294       mm->fib_index_by_sw_if_index[sw_if_index] = lfib_index;
295     }
296   else
297     {
298       ASSERT(mm->mpls_enabled_by_sw_if_index[sw_if_index] > 0);
299       if (0 != --mm->mpls_enabled_by_sw_if_index[sw_if_index])
300         return;
301
302       fib_table_unlock(mm->fib_index_by_sw_if_index[sw_if_index],
303                        FIB_PROTOCOL_MPLS);
304     }
305
306   vnet_feature_enable_disable ("mpls-input", "mpls-lookup", sw_if_index,
307                                is_enable, 0, 0);
308
309 }
310
311 u8 * format_mpls_encap_index (u8 * s, va_list * args)
312 {
313   mpls_main_t * mm = va_arg (*args, mpls_main_t *);
314   u32 entry_index = va_arg (*args, u32);
315   mpls_encap_t * e;
316   int i;
317
318   e = pool_elt_at_index (mm->encaps, entry_index);
319   
320   for (i = 0; i < vec_len (e->labels); i++)
321     s = format 
322         (s, "%d ", vnet_mpls_uc_get_label(clib_net_to_host_u32 
323                                           (e->labels[i].label_exp_s_ttl)));
324
325   return s;
326 }
327
328 u8 * format_mpls_ethernet_tunnel (u8 * s, va_list * args)
329 {
330   mpls_eth_tunnel_t * t = va_arg (*args, mpls_eth_tunnel_t *);
331   mpls_main_t * mm = &mpls_main;
332   
333   s = format (s, "[%d]: dst %U, adj %U/%d, labels %U\n",
334               t - mm->eth_tunnels, 
335               format_ethernet_address, &t->tunnel_dst,
336               format_ip4_address, &t->intfc_address,
337               t->mask_width, 
338               format_mpls_encap_index, mm, t->encap_index);
339
340
341   s = format (s, "      tx on %U, rx fib index %d", 
342               format_vnet_sw_if_index_name, mm->vnet_main, t->tx_sw_if_index,
343               t->inner_fib_index);
344
345   return s;
346 }
347
348 static clib_error_t *
349 show_mpls_tunnel_command_fn (vlib_main_t * vm,
350                              unformat_input_t * input,
351                              vlib_cli_command_t * cmd)
352 {
353   mpls_main_t * mm = &mpls_main;
354   mpls_eth_tunnel_t * et;
355
356   if (pool_elts (mm->eth_tunnels))
357     {
358       vlib_cli_output (vm, "MPLS-Ethernet tunnels");
359       pool_foreach (et, mm->eth_tunnels,
360       ({
361         vlib_cli_output (vm, "%U", format_mpls_ethernet_tunnel, et);
362       }));
363     }
364   else
365     vlib_cli_output (vm, "No MPLS-Ethernet tunnels");
366
367   return 0;
368 }
369
370 VLIB_CLI_COMMAND (show_mpls_tunnel_command, static) = {
371     .path = "show mpls tunnel",
372     .short_help = "show mpls tunnel",
373     .function = show_mpls_tunnel_command_fn,
374 };
375
376
377 /* force inclusion from application's main.c */
378 clib_error_t *mpls_interface_init (vlib_main_t *vm)
379 {
380   clib_error_t * error;
381
382   if ((error = vlib_call_init_function (vm, mpls_policy_encap_init)))
383       return error;
384
385   return 0;
386 }
387 VLIB_INIT_FUNCTION(mpls_interface_init);
388
389
390 static u8 * mpls_ethernet_rewrite (mpls_main_t *mm, mpls_eth_tunnel_t * t)
391 {
392   u8 * rewrite_data = 0;
393   mpls_encap_t * e;
394   mpls_unicast_header_t *lp0;
395   int i;
396
397  /* look up the encap label stack using the RX FIB and adjacency address*/
398   e = mpls_encap_by_fib_and_dest (mm, t->inner_fib_index, 
399                                   t->intfc_address.as_u32);
400
401   if (e == 0)
402     {
403       clib_warning ("no label for inner fib index %d, dst %U",
404                     t->inner_fib_index, format_ip4_address, 
405                     &t->intfc_address);
406       return 0;
407     }
408  
409   vec_validate (rewrite_data, 
410                 sizeof (mpls_unicast_header_t) * vec_len(e->labels) -1);
411
412   /* Copy the encap label stack */
413   lp0 = (mpls_unicast_header_t *) rewrite_data;
414   
415   for (i = 0; i < vec_len(e->labels); i++)
416     lp0[i] = e->labels[i];
417   
418   return (rewrite_data);
419 }
420
421 int vnet_mpls_ethernet_add_del_tunnel (u8 *dst,
422                                        ip4_address_t *intfc,
423                                        u32 mask_width,
424                                        u32 inner_fib_id, 
425                                        u32 tx_sw_if_index,
426                                        u32 * tunnel_sw_if_index,
427                                        u8 l2_only,
428                                        u8 is_add)
429 {
430   ip4_main_t * im = &ip4_main;
431   ip_lookup_main_t * lm = &im->lookup_main;
432   mpls_main_t * mm = &mpls_main;
433   vnet_main_t * vnm = vnet_get_main();
434   mpls_eth_tunnel_t *tp;
435   u32 inner_fib_index = 0;
436   ip_adjacency_t adj;
437   u32 adj_index;
438   u8 * rewrite_data;
439   int found_tunnel = 0;
440   mpls_encap_t * e = 0;
441   u32 hw_if_index = ~0;
442   vnet_hw_interface_t * hi;
443   u32 slot;
444   u32 dummy;
445   
446   if (tunnel_sw_if_index == 0)
447     tunnel_sw_if_index = &dummy;
448
449   *tunnel_sw_if_index = ~0;
450
451   if (inner_fib_id != (u32)~0)
452     {
453       uword * p;
454       
455       p = hash_get (im->fib_index_by_table_id, inner_fib_id);
456       if (! p)
457         return VNET_API_ERROR_NO_SUCH_FIB;
458       inner_fib_index = p[0];
459     }
460
461   /* suppress duplicate mpls interface generation. */
462   pool_foreach (tp, mm->eth_tunnels, 
463   ({
464     /* 
465      * If we have a tunnel which matches (src, dst, intfc/mask)
466      * AND the expected route is in the FIB, it's a dup 
467      */
468     if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
469         && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
470         && tp->inner_fib_index == inner_fib_index
471         && FIB_NODE_INDEX_INVALID != tp->fei)
472       {
473         found_tunnel = 1;
474
475         if (is_add)
476           {
477             if (l2_only)
478               return 1;
479             else
480               {
481                 e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, 
482                                                 intfc->as_u32);
483                 if (e == 0)
484                   return VNET_API_ERROR_NO_SUCH_LABEL;
485                 
486                 goto reinstall_it;
487               }
488           }
489         else
490           {
491             /* Delete */
492             goto add_del_route;
493           }
494
495       }
496   }));
497     
498   /* Delete, and we can't find the tunnel */
499   if (is_add == 0 && found_tunnel == 0)
500     return VNET_API_ERROR_NO_SUCH_ENTRY;
501
502   e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, intfc->as_u32);
503   if (e == 0)
504     return VNET_API_ERROR_NO_SUCH_LABEL;
505
506   pool_get(mm->eth_tunnels, tp);
507   memset (tp, 0, sizeof (*tp));
508     
509   if (vec_len (mm->free_eth_sw_if_indices) > 0)
510     {
511       hw_if_index = 
512         mm->free_eth_sw_if_indices[vec_len(mm->free_eth_sw_if_indices)-1];
513       _vec_len (mm->free_eth_sw_if_indices) -= 1;
514       hi = vnet_get_hw_interface (vnm, hw_if_index);
515       hi->dev_instance = tp - mm->eth_tunnels;
516       hi->hw_instance = tp - mm->eth_tunnels;
517     }
518   else 
519     {
520       hw_if_index = vnet_register_interface
521         (vnm, mpls_eth_device_class.index, tp - mm->eth_tunnels,
522          mpls_eth_hw_interface_class.index,
523          tp - mm->eth_tunnels);
524       hi = vnet_get_hw_interface (vnm, hw_if_index);
525
526       /* ... to make the IP and L2 x-connect cases identical */
527       slot = vlib_node_add_named_next_with_slot
528         (vnm->vlib_main, hi->tx_node_index, 
529          "interface-output", MPLS_ETH_OUTPUT_NEXT_OUTPUT);
530
531       ASSERT (slot == MPLS_ETH_OUTPUT_NEXT_OUTPUT);
532     }
533   
534   *tunnel_sw_if_index = hi->sw_if_index;
535   vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
536                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);      
537
538   tp->hw_if_index = hw_if_index;
539
540  reinstall_it:
541   clib_memcpy(tp->tunnel_dst, dst, sizeof (tp->tunnel_dst));
542   tp->intfc_address.as_u32 = intfc->as_u32;
543   tp->mask_width = mask_width;
544   tp->inner_fib_index = inner_fib_index;
545   tp->encap_index = e - mm->encaps;
546   tp->tx_sw_if_index = tx_sw_if_index;
547   tp->l2_only = l2_only;
548
549   /* Create the adjacency and add to v4 fib */
550   memset(&adj, 0, sizeof (adj));
551   adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
552     
553   rewrite_data = mpls_ethernet_rewrite (mm, tp);
554   if (rewrite_data == 0)
555     {
556       if (*tunnel_sw_if_index != ~0)
557         {
558           hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
559           vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
560                                        0 /* admin down */);
561           vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
562       }
563
564       pool_put (mm->eth_tunnels, tp);
565       return VNET_API_ERROR_NO_SUCH_LABEL;
566     }
567   
568   vnet_rewrite_for_sw_interface
569     (vnm,
570      VNET_LINK_MPLS, 
571      tx_sw_if_index,
572      ip4_rewrite_node.index,
573      tp->tunnel_dst,
574      &adj.rewrite_header,
575      sizeof (adj.rewrite_data));
576   
577   /* 
578    * Prepend the (0,1,2) VLAN tag ethernet header 
579    * we just built to the mpls header stack
580    */
581   vec_insert (rewrite_data, adj.rewrite_header.data_bytes, 0);
582   clib_memcpy(rewrite_data, 
583          vnet_rewrite_get_data_internal(&adj.rewrite_header, 
584                                         sizeof (adj.rewrite_data)),
585          adj.rewrite_header.data_bytes);
586
587   vnet_rewrite_set_data_internal (&adj.rewrite_header, 
588                                   sizeof(adj.rewrite_data),
589                                   rewrite_data, 
590                                   vec_len(rewrite_data));
591   
592   vec_free (tp->rewrite_data);
593   
594   tp->rewrite_data = rewrite_data;
595
596   if (!l2_only)
597     ip_add_adjacency (lm, &adj, 1 /* one adj */,
598                       &adj_index);
599   
600  add_del_route:
601
602   if (!l2_only)
603     {
604       const fib_prefix_t pfx = {
605           .fp_addr = {
606               .ip4 = tp->intfc_address,
607           },
608           .fp_len = tp->mask_width,
609           .fp_proto = FIB_PROTOCOL_IP4,
610       };
611       if (is_add)
612           tp->fei = fib_table_entry_special_add(tp->inner_fib_index,
613                                                 &pfx,
614                                                 FIB_SOURCE_API,
615                                                 FIB_ENTRY_FLAG_NONE,
616                                                 adj_index);
617       else
618         {
619           fib_table_entry_delete(tp->inner_fib_index, &pfx, FIB_SOURCE_API);
620           tp->fei = FIB_NODE_INDEX_INVALID;
621         }
622     }
623   if (is_add == 0 && found_tunnel)
624     {
625       hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
626       vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
627                                    0 /* admin down */);
628       vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
629       vec_free (tp->rewrite_data);
630       pool_put (mm->eth_tunnels, tp);
631     }
632
633   return 0;
634 }
635
636 static clib_error_t *
637 create_mpls_ethernet_tunnel_command_fn (vlib_main_t * vm,
638                                         unformat_input_t * input,
639                                         vlib_cli_command_t * cmd)
640 {
641   unformat_input_t _line_input, * line_input = &_line_input;
642   vnet_main_t * vnm = vnet_get_main();
643   ip4_address_t intfc;
644   int adj_set = 0;
645   u8 dst[6];
646   int dst_set = 0, intfc_set = 0;
647   u32 mask_width;
648   u32 inner_fib_id = (u32)~0;
649   int rv;
650   u8 is_del = 0;
651   u8 l2_only = 0;
652   u32 tx_sw_if_index;
653   u32 sw_if_index = ~0;
654   
655   /* Get a line of input. */
656   if (! unformat_user (input, unformat_line_input, line_input))
657     return 0;
658
659   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
660     {
661       if (unformat (line_input, "dst %U", 
662                     unformat_ethernet_address, &dst))
663         dst_set = 1;
664       else if (unformat (line_input, "adj %U/%d", 
665                          unformat_ip4_address, &intfc, &mask_width))
666         adj_set = 1;
667       else if (unformat (line_input, "tx-intfc %U", 
668                          unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
669         intfc_set = 1;
670       else if (unformat (line_input, "fib-id %d", &inner_fib_id))
671         ;
672       else if (unformat (line_input, "l2-only"))
673         l2_only = 1;
674       else if (unformat (line_input, "del"))
675         is_del = 1;
676       else
677         return clib_error_return (0, "unknown input '%U'",
678                                   format_unformat_error, line_input);
679     }
680
681   if (!intfc_set)
682     return clib_error_return (0, "missing tx-intfc");
683
684   if (!dst_set)
685     return clib_error_return (0, "missing: dst <ethernet-address>");
686           
687   if (!adj_set)
688     return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
689   
690
691   rv = vnet_mpls_ethernet_add_del_tunnel (dst, &intfc, mask_width, 
692                                           inner_fib_id, tx_sw_if_index, 
693                                           &sw_if_index,
694                                           l2_only, !is_del);
695
696   switch (rv) 
697     {
698     case 0:
699       if (!is_del)
700         vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), sw_if_index);
701       break;
702     case VNET_API_ERROR_NO_SUCH_FIB:
703       return clib_error_return (0, "rx fib ID %d doesn't exist\n",
704                                 inner_fib_id);
705
706     case VNET_API_ERROR_NO_SUCH_ENTRY:
707       return clib_error_return (0, "tunnel not found\n");
708
709     case VNET_API_ERROR_NO_SUCH_LABEL:
710       /* 
711        * This happens when there's no MPLS label for the dst address
712        * no need for two error messages.
713        */
714         return clib_error_return (0, "no label for %U in fib %d", 
715                                   format_ip4_address, &intfc, inner_fib_id);
716       break;
717       
718     default:
719       return clib_error_return (0, "vnet_mpls_ethernet_add_del_tunnel returned %d", rv);
720       break;
721     }
722   return 0;
723 }
724
725
726 VLIB_CLI_COMMAND (create_mpls_ethernet_tunnel_command, static) = {
727   .path = "create mpls ethernet tunnel",
728   .short_help = 
729   "create mpls ethernet tunnel [del] dst <mac-addr> intfc <addr>/<mw>",
730   .function = create_mpls_ethernet_tunnel_command_fn,
731 };
732
733
734 int vnet_mpls_policy_tunnel_add_rewrite (mpls_main_t * mm, 
735                                          mpls_encap_t * e, 
736                                          u32 policy_tunnel_index)
737 {
738   mpls_eth_tunnel_t * t;
739   ip_adjacency_t adj;
740   u8 * rewrite_data = 0;
741   u8 * label_start;
742   mpls_unicast_header_t *lp;
743   int i;
744
745   if (pool_is_free_index (mm->eth_tunnels, policy_tunnel_index))
746     return VNET_API_ERROR_NO_SUCH_ENTRY;
747
748   t = pool_elt_at_index (mm->eth_tunnels, policy_tunnel_index);
749
750   memset (&adj, 0, sizeof (adj));
751
752   /* Build L2 encap */
753   vnet_rewrite_for_sw_interface
754     (mm->vnet_main, 
755      VNET_LINK_MPLS, 
756      t->tx_sw_if_index,
757      mpls_policy_encap_node.index,
758      t->tunnel_dst,
759      &adj.rewrite_header,
760      sizeof (adj.rewrite_data));
761   
762   vec_validate (rewrite_data, adj.rewrite_header.data_bytes -1);
763
764   clib_memcpy(rewrite_data, 
765          vnet_rewrite_get_data_internal(&adj.rewrite_header, 
766                                         sizeof (adj.rewrite_data)),
767          adj.rewrite_header.data_bytes);
768
769   /* Append the label stack */
770
771   vec_add2 (rewrite_data, label_start, vec_len(e->labels) * sizeof (u32));
772
773   lp = (mpls_unicast_header_t *) label_start;
774
775   for (i = 0; i < vec_len(e->labels); i++)
776     lp[i] = e->labels[i];
777   
778   /* Remember the rewrite data */
779   e->rewrite = rewrite_data;
780   e->output_next_index = adj.rewrite_header.next_index;
781
782   return 0;
783 }
784
785 int vnet_mpls_ethernet_add_del_policy_tunnel (u8 *dst,
786                                               ip4_address_t *intfc,
787                                               u32 mask_width,
788                                               u32 inner_fib_id, 
789                                               u32 tx_sw_if_index,
790                                               u32 * tunnel_sw_if_index,
791                                               u32 classify_table_index,
792                                               u32 * new_tunnel_index,
793                                               u8 l2_only,
794                                               u8 is_add)
795 {
796   ip4_main_t * im = &ip4_main;
797   mpls_main_t * mm = &mpls_main;
798   vnet_main_t * vnm = vnet_get_main();
799   mpls_eth_tunnel_t *tp;
800   u32 inner_fib_index = 0;
801   int found_tunnel = 0;
802   mpls_encap_t * e = 0;
803   u32 hw_if_index = ~0;
804   vnet_hw_interface_t * hi;
805   u32 slot;
806   u32 dummy;
807   
808   if (tunnel_sw_if_index == 0)
809     tunnel_sw_if_index = &dummy;
810
811   *tunnel_sw_if_index = ~0;
812
813   if (inner_fib_id != (u32)~0)
814     {
815       uword * p;
816       
817       p = hash_get (im->fib_index_by_table_id, inner_fib_id);
818       if (! p)
819         return VNET_API_ERROR_NO_SUCH_FIB;
820       inner_fib_index = p[0];
821     }
822
823   /* suppress duplicate mpls interface generation. */
824   pool_foreach (tp, mm->eth_tunnels, 
825   ({
826     /* 
827      * If we have a tunnel which matches (src, dst, intfc/mask)
828      * AND the expected route is in the FIB, it's a dup 
829      */
830     if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
831         && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
832         && tp->inner_fib_index == inner_fib_index
833         && FIB_NODE_INDEX_INVALID != tp->fei)
834       {
835         found_tunnel = 1;
836
837         if (is_add)
838           {
839             if (l2_only)
840               return 1;
841             else
842               {
843                 goto reinstall_it;
844               }
845           }
846         else
847           {
848             /* Delete */
849             goto add_del_route;
850           }
851
852       }
853   }));
854     
855   /* Delete, and we can't find the tunnel */
856   if (is_add == 0 && found_tunnel == 0)
857     return VNET_API_ERROR_NO_SUCH_ENTRY;
858
859   pool_get(mm->eth_tunnels, tp);
860   memset (tp, 0, sizeof (*tp));
861     
862   if (vec_len (mm->free_eth_sw_if_indices) > 0)
863     {
864       hw_if_index = 
865         mm->free_eth_sw_if_indices[vec_len(mm->free_eth_sw_if_indices)-1];
866       _vec_len (mm->free_eth_sw_if_indices) -= 1;
867       hi = vnet_get_hw_interface (vnm, hw_if_index);
868       hi->dev_instance = tp - mm->eth_tunnels;
869       hi->hw_instance = tp - mm->eth_tunnels;
870     }
871   else 
872     {
873       hw_if_index = vnet_register_interface
874         (vnm, mpls_eth_device_class.index, tp - mm->eth_tunnels,
875          mpls_eth_hw_interface_class.index,
876          tp - mm->eth_tunnels);
877       hi = vnet_get_hw_interface (vnm, hw_if_index);
878
879       /* ... to make the IP and L2 x-connect cases identical */
880       slot = vlib_node_add_named_next_with_slot
881         (vnm->vlib_main, hi->tx_node_index, 
882          "interface-output", MPLS_ETH_OUTPUT_NEXT_OUTPUT);
883
884       ASSERT (slot == MPLS_ETH_OUTPUT_NEXT_OUTPUT);
885     }
886   
887   *tunnel_sw_if_index = hi->sw_if_index;
888   vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
889                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);      
890
891   tp->hw_if_index = hw_if_index;
892
893  reinstall_it:
894   clib_memcpy(tp->tunnel_dst, dst, sizeof (tp->tunnel_dst));
895   tp->intfc_address.as_u32 = intfc->as_u32;
896   tp->mask_width = mask_width;
897   tp->inner_fib_index = inner_fib_index;
898   tp->encap_index = e - mm->encaps;
899   tp->tx_sw_if_index = tx_sw_if_index;
900   tp->l2_only = l2_only;
901   tp->fei = FIB_NODE_INDEX_INVALID;
902
903   if (new_tunnel_index)
904     *new_tunnel_index = tp - mm->eth_tunnels;
905
906  add_del_route:
907
908   if (!l2_only)
909     {
910       const fib_prefix_t pfx = {
911           .fp_addr = {
912               .ip4 = tp->intfc_address,
913           },
914           .fp_len = tp->mask_width,
915           .fp_proto = FIB_PROTOCOL_IP4,
916       };
917       dpo_id_t dpo = DPO_INVALID;
918
919       if (is_add)
920         {
921           dpo_set(&dpo,
922                   DPO_CLASSIFY,
923                   DPO_PROTO_IP4,
924                   classify_dpo_create(DPO_PROTO_IP4,
925                                       classify_table_index));
926
927           tp->fei = fib_table_entry_special_dpo_add(tp->inner_fib_index,
928                                                     &pfx,
929                                                     FIB_SOURCE_API,
930                                                     FIB_ENTRY_FLAG_EXCLUSIVE,
931                                                     &dpo);
932           dpo_reset(&dpo);
933         }
934       else
935         {
936           fib_table_entry_delete(tp->inner_fib_index, &pfx, FIB_SOURCE_API);
937           tp->fei = FIB_NODE_INDEX_INVALID;
938         }
939     }
940   if (is_add == 0 && found_tunnel)
941     {
942       hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
943       vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
944                                    0 /* admin down */);
945       vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
946       pool_put (mm->eth_tunnels, tp);
947     }
948
949   return 0;
950 }
951
952 static clib_error_t *
953 create_mpls_ethernet_policy_tunnel_command_fn (vlib_main_t * vm,
954                                                unformat_input_t * input,
955                                                vlib_cli_command_t * cmd)
956 {
957   unformat_input_t _line_input, * line_input = &_line_input;
958   vnet_main_t * vnm = vnet_get_main();
959   ip4_address_t intfc;
960   int adj_set = 0;
961   u8 dst[6];
962   int dst_set = 0, intfc_set = 0;
963   u32 mask_width;
964   u32 inner_fib_id = (u32)~0;
965   u32 classify_table_index = (u32)~0;
966   u32 new_tunnel_index;
967   int rv;
968   u8 is_del = 0;
969   u8 l2_only = 0;
970   u32 tx_sw_if_index;
971   
972   /* Get a line of input. */
973   if (! unformat_user (input, unformat_line_input, line_input))
974     return 0;
975
976   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
977     {
978       if (unformat (line_input, "dst %U", 
979                     unformat_ethernet_address, &dst))
980         dst_set = 1;
981       else if (unformat (line_input, "adj %U/%d", 
982                          unformat_ip4_address, &intfc, &mask_width))
983         adj_set = 1;
984       else if (unformat (line_input, "tx-intfc %U", 
985                          unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
986         intfc_set = 1;
987       else if (unformat (line_input, "classify-table-index %d",
988                          &classify_table_index))
989         ;
990       else if (unformat (line_input, "fib-id %d", &inner_fib_id))
991         ;
992       else if (unformat (line_input, "l2-only"))
993         l2_only = 1;
994       else if (unformat (line_input, "del"))
995         is_del = 1;
996       else
997         return clib_error_return (0, "unknown input '%U'",
998                                   format_unformat_error, line_input);
999     }
1000
1001   if (classify_table_index == ~0)
1002     return clib_error_return (0, "missing classify_table_index");
1003
1004   if (!intfc_set)
1005     return clib_error_return (0, "missing tx-intfc");
1006
1007   if (!dst_set)
1008     return clib_error_return (0, "missing: dst <ethernet-address>");
1009           
1010   if (!adj_set)
1011     return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
1012   
1013
1014   rv = vnet_mpls_ethernet_add_del_policy_tunnel (dst, &intfc, mask_width, 
1015                                                  inner_fib_id, tx_sw_if_index, 
1016                                                  0 /* tunnel sw_if_index */, 
1017                                                  classify_table_index,
1018                                                  &new_tunnel_index,
1019                                                  l2_only, !is_del);
1020   switch (rv) 
1021     {
1022     case 0:
1023       if (!is_del)
1024         vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), new_tunnel_index);
1025       break;
1026     case VNET_API_ERROR_NO_SUCH_FIB:
1027       return clib_error_return (0, "rx fib ID %d doesn't exist\n",
1028                                 inner_fib_id);
1029
1030     case VNET_API_ERROR_NO_SUCH_ENTRY:
1031       return clib_error_return (0, "tunnel not found\n");
1032
1033     case VNET_API_ERROR_NO_SUCH_LABEL:
1034       /* 
1035        * This happens when there's no MPLS label for the dst address
1036        * no need for two error messages.
1037        */
1038         return clib_error_return (0, "no label for %U in fib %d", 
1039                                   format_ip4_address, &intfc, inner_fib_id);
1040       break;
1041       
1042     default:
1043       return clib_error_return (0, "vnet_mpls_ethernet_add_del_policy_tunnel returned %d", rv);
1044       break;
1045     }
1046
1047   return 0;
1048 }
1049
1050 VLIB_CLI_COMMAND (create_mpls_ethernet_policy_tunnel_command, static) = {
1051   .path = "create mpls ethernet policy tunnel",
1052   .short_help = 
1053   "create mpls ethernet policy tunnel [del] dst <mac-addr> intfc <addr>/<mw>\n"
1054   " classify-table-index <nn>",
1055   .function = create_mpls_ethernet_policy_tunnel_command_fn,
1056 };
1057
1058 static clib_error_t *
1059 mpls_interface_enable_disable (vlib_main_t * vm,
1060                                unformat_input_t * input,
1061                                vlib_cli_command_t * cmd)
1062 {
1063   vnet_main_t * vnm = vnet_get_main();
1064   clib_error_t * error = 0;
1065   u32 sw_if_index, enable;
1066
1067   sw_if_index = ~0;
1068
1069   if (! unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
1070     {
1071       error = clib_error_return (0, "unknown interface `%U'",
1072                                  format_unformat_error, input);
1073       goto done;
1074     }
1075
1076   if (unformat (input, "enable"))
1077       enable = 1;
1078   else if (unformat (input, "disable"))
1079       enable = 0;
1080   else
1081     {
1082       error = clib_error_return (0, "expected 'enable' or 'disable'",
1083                                  format_unformat_error, input);
1084       goto done;
1085     }
1086
1087   mpls_sw_interface_enable_disable(&mpls_main, sw_if_index, enable);
1088
1089  done:
1090   return error;
1091 }
1092
1093 VLIB_CLI_COMMAND (set_interface_ip_table_command, static) = {
1094   .path = "set interface mpls",
1095   .function = mpls_interface_enable_disable,
1096   .short_help = "Enable/Disable an interface for MPLS forwarding",
1097 };