726e6720bf2a9f23b44371647128d9402e43b99e
[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   vlib_main_t * vm = vlib_get_main();
280   ip_config_main_t * cm = &mm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
281   vnet_config_main_t * vcm = &cm->config_main;
282   u32 lookup_feature_index;
283   fib_node_index_t lfib_index;
284   u32 ci;
285
286   vec_validate_init_empty (mm->mpls_enabled_by_sw_if_index, sw_if_index, 0);
287
288   /*
289    * enable/disable only on the 1<->0 transition
290    */
291   if (is_enable)
292     {
293       if (1 != ++mm->mpls_enabled_by_sw_if_index[sw_if_index])
294         return;
295
296       lfib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_MPLS,
297                                                      MPLS_FIB_DEFAULT_TABLE_ID);
298       vec_validate(mm->fib_index_by_sw_if_index, 0);
299       mm->fib_index_by_sw_if_index[sw_if_index] = lfib_index;
300     }
301   else
302     {
303       ASSERT(mm->mpls_enabled_by_sw_if_index[sw_if_index] > 0);
304       if (0 != --mm->mpls_enabled_by_sw_if_index[sw_if_index])
305         return;
306
307       fib_table_unlock(mm->fib_index_by_sw_if_index[sw_if_index],
308                        FIB_PROTOCOL_MPLS);
309     }
310
311   vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0);
312   ci = cm->config_index_by_sw_if_index[sw_if_index];
313
314   lookup_feature_index = mm->mpls_rx_feature_lookup;
315
316   if (is_enable)
317     ci = vnet_config_add_feature (vm, vcm,
318                                   ci,
319                                   lookup_feature_index,
320                                   /* config data */ 0,
321                                   /* # bytes of config data */ 0);
322   else
323     ci = vnet_config_del_feature (vm, vcm, ci,
324                                   lookup_feature_index,
325                                   /* config data */ 0,
326                                   /* # bytes of config data */ 0);
327
328   cm->config_index_by_sw_if_index[sw_if_index] = ci;
329 }
330
331 u8 * format_mpls_encap_index (u8 * s, va_list * args)
332 {
333   mpls_main_t * mm = va_arg (*args, mpls_main_t *);
334   u32 entry_index = va_arg (*args, u32);
335   mpls_encap_t * e;
336   int i;
337
338   e = pool_elt_at_index (mm->encaps, entry_index);
339   
340   for (i = 0; i < vec_len (e->labels); i++)
341     s = format 
342         (s, "%d ", vnet_mpls_uc_get_label(clib_net_to_host_u32 
343                                           (e->labels[i].label_exp_s_ttl)));
344
345   return s;
346 }
347
348 u8 * format_mpls_ethernet_tunnel (u8 * s, va_list * args)
349 {
350   mpls_eth_tunnel_t * t = va_arg (*args, mpls_eth_tunnel_t *);
351   mpls_main_t * mm = &mpls_main;
352   
353   s = format (s, "[%d]: dst %U, adj %U/%d, labels %U\n",
354               t - mm->eth_tunnels, 
355               format_ethernet_address, &t->tunnel_dst,
356               format_ip4_address, &t->intfc_address,
357               t->mask_width, 
358               format_mpls_encap_index, mm, t->encap_index);
359
360
361   s = format (s, "      tx on %U, rx fib index %d", 
362               format_vnet_sw_if_index_name, mm->vnet_main, t->tx_sw_if_index,
363               t->inner_fib_index);
364
365   return s;
366 }
367
368 static clib_error_t *
369 show_mpls_tunnel_command_fn (vlib_main_t * vm,
370                              unformat_input_t * input,
371                              vlib_cli_command_t * cmd)
372 {
373   mpls_main_t * mm = &mpls_main;
374   mpls_eth_tunnel_t * et;
375
376   if (pool_elts (mm->eth_tunnels))
377     {
378       vlib_cli_output (vm, "MPLS-Ethernet tunnels");
379       pool_foreach (et, mm->eth_tunnels,
380       ({
381         vlib_cli_output (vm, "%U", format_mpls_ethernet_tunnel, et);
382       }));
383     }
384   else
385     vlib_cli_output (vm, "No MPLS-Ethernet tunnels");
386
387   return 0;
388 }
389
390 VLIB_CLI_COMMAND (show_mpls_tunnel_command, static) = {
391     .path = "show mpls tunnel",
392     .short_help = "show mpls tunnel",
393     .function = show_mpls_tunnel_command_fn,
394 };
395
396
397 /* force inclusion from application's main.c */
398 clib_error_t *mpls_interface_init (vlib_main_t *vm)
399 {
400   clib_error_t * error;
401
402   if ((error = vlib_call_init_function (vm, mpls_policy_encap_init)))
403       return error;
404
405   return 0;
406 }
407 VLIB_INIT_FUNCTION(mpls_interface_init);
408
409
410 static u8 * mpls_ethernet_rewrite (mpls_main_t *mm, mpls_eth_tunnel_t * t)
411 {
412   u8 * rewrite_data = 0;
413   mpls_encap_t * e;
414   mpls_unicast_header_t *lp0;
415   int i;
416
417  /* look up the encap label stack using the RX FIB and adjacency address*/
418   e = mpls_encap_by_fib_and_dest (mm, t->inner_fib_index, 
419                                   t->intfc_address.as_u32);
420
421   if (e == 0)
422     {
423       clib_warning ("no label for inner fib index %d, dst %U",
424                     t->inner_fib_index, format_ip4_address, 
425                     &t->intfc_address);
426       return 0;
427     }
428  
429   vec_validate (rewrite_data, 
430                 sizeof (mpls_unicast_header_t) * vec_len(e->labels) -1);
431
432   /* Copy the encap label stack */
433   lp0 = (mpls_unicast_header_t *) rewrite_data;
434   
435   for (i = 0; i < vec_len(e->labels); i++)
436     lp0[i] = e->labels[i];
437   
438   return (rewrite_data);
439 }
440
441 int vnet_mpls_ethernet_add_del_tunnel (u8 *dst,
442                                        ip4_address_t *intfc,
443                                        u32 mask_width,
444                                        u32 inner_fib_id, 
445                                        u32 tx_sw_if_index,
446                                        u32 * tunnel_sw_if_index,
447                                        u8 l2_only,
448                                        u8 is_add)
449 {
450   ip4_main_t * im = &ip4_main;
451   ip_lookup_main_t * lm = &im->lookup_main;
452   mpls_main_t * mm = &mpls_main;
453   vnet_main_t * vnm = vnet_get_main();
454   mpls_eth_tunnel_t *tp;
455   u32 inner_fib_index = 0;
456   ip_adjacency_t adj;
457   u32 adj_index;
458   u8 * rewrite_data;
459   int found_tunnel = 0;
460   mpls_encap_t * e = 0;
461   u32 hw_if_index = ~0;
462   vnet_hw_interface_t * hi;
463   u32 slot;
464   u32 dummy;
465   
466   if (tunnel_sw_if_index == 0)
467     tunnel_sw_if_index = &dummy;
468
469   *tunnel_sw_if_index = ~0;
470
471   if (inner_fib_id != (u32)~0)
472     {
473       uword * p;
474       
475       p = hash_get (im->fib_index_by_table_id, inner_fib_id);
476       if (! p)
477         return VNET_API_ERROR_NO_SUCH_FIB;
478       inner_fib_index = p[0];
479     }
480
481   /* suppress duplicate mpls interface generation. */
482   pool_foreach (tp, mm->eth_tunnels, 
483   ({
484     /* 
485      * If we have a tunnel which matches (src, dst, intfc/mask)
486      * AND the expected route is in the FIB, it's a dup 
487      */
488     if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
489         && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
490         && tp->inner_fib_index == inner_fib_index
491         && FIB_NODE_INDEX_INVALID != tp->fei)
492       {
493         found_tunnel = 1;
494
495         if (is_add)
496           {
497             if (l2_only)
498               return 1;
499             else
500               {
501                 e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, 
502                                                 intfc->as_u32);
503                 if (e == 0)
504                   return VNET_API_ERROR_NO_SUCH_LABEL;
505                 
506                 goto reinstall_it;
507               }
508           }
509         else
510           {
511             /* Delete */
512             goto add_del_route;
513           }
514
515       }
516   }));
517     
518   /* Delete, and we can't find the tunnel */
519   if (is_add == 0 && found_tunnel == 0)
520     return VNET_API_ERROR_NO_SUCH_ENTRY;
521
522   e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, intfc->as_u32);
523   if (e == 0)
524     return VNET_API_ERROR_NO_SUCH_LABEL;
525
526   pool_get(mm->eth_tunnels, tp);
527   memset (tp, 0, sizeof (*tp));
528     
529   if (vec_len (mm->free_eth_sw_if_indices) > 0)
530     {
531       hw_if_index = 
532         mm->free_eth_sw_if_indices[vec_len(mm->free_eth_sw_if_indices)-1];
533       _vec_len (mm->free_eth_sw_if_indices) -= 1;
534       hi = vnet_get_hw_interface (vnm, hw_if_index);
535       hi->dev_instance = tp - mm->eth_tunnels;
536       hi->hw_instance = tp - mm->eth_tunnels;
537     }
538   else 
539     {
540       hw_if_index = vnet_register_interface
541         (vnm, mpls_eth_device_class.index, tp - mm->eth_tunnels,
542          mpls_eth_hw_interface_class.index,
543          tp - mm->eth_tunnels);
544       hi = vnet_get_hw_interface (vnm, hw_if_index);
545
546       /* ... to make the IP and L2 x-connect cases identical */
547       slot = vlib_node_add_named_next_with_slot
548         (vnm->vlib_main, hi->tx_node_index, 
549          "interface-output", MPLS_ETH_OUTPUT_NEXT_OUTPUT);
550
551       ASSERT (slot == MPLS_ETH_OUTPUT_NEXT_OUTPUT);
552     }
553   
554   *tunnel_sw_if_index = hi->sw_if_index;
555   vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
556                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);      
557
558   tp->hw_if_index = hw_if_index;
559
560  reinstall_it:
561   clib_memcpy(tp->tunnel_dst, dst, sizeof (tp->tunnel_dst));
562   tp->intfc_address.as_u32 = intfc->as_u32;
563   tp->mask_width = mask_width;
564   tp->inner_fib_index = inner_fib_index;
565   tp->encap_index = e - mm->encaps;
566   tp->tx_sw_if_index = tx_sw_if_index;
567   tp->l2_only = l2_only;
568
569   /* Create the adjacency and add to v4 fib */
570   memset(&adj, 0, sizeof (adj));
571   adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
572     
573   rewrite_data = mpls_ethernet_rewrite (mm, tp);
574   if (rewrite_data == 0)
575     {
576       if (*tunnel_sw_if_index != ~0)
577         {
578           hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
579           vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
580                                        0 /* admin down */);
581           vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
582       }
583
584       pool_put (mm->eth_tunnels, tp);
585       return VNET_API_ERROR_NO_SUCH_LABEL;
586     }
587   
588   vnet_rewrite_for_sw_interface
589     (vnm,
590      VNET_LINK_MPLS, 
591      tx_sw_if_index,
592      ip4_rewrite_node.index,
593      tp->tunnel_dst,
594      &adj.rewrite_header,
595      sizeof (adj.rewrite_data));
596   
597   /* 
598    * Prepend the (0,1,2) VLAN tag ethernet header 
599    * we just built to the mpls header stack
600    */
601   vec_insert (rewrite_data, adj.rewrite_header.data_bytes, 0);
602   clib_memcpy(rewrite_data, 
603          vnet_rewrite_get_data_internal(&adj.rewrite_header, 
604                                         sizeof (adj.rewrite_data)),
605          adj.rewrite_header.data_bytes);
606
607   vnet_rewrite_set_data_internal (&adj.rewrite_header, 
608                                   sizeof(adj.rewrite_data),
609                                   rewrite_data, 
610                                   vec_len(rewrite_data));
611   
612   vec_free (tp->rewrite_data);
613   
614   tp->rewrite_data = rewrite_data;
615
616   if (!l2_only)
617     ip_add_adjacency (lm, &adj, 1 /* one adj */,
618                       &adj_index);
619   
620  add_del_route:
621
622   if (!l2_only)
623     {
624       const fib_prefix_t pfx = {
625           .fp_addr = {
626               .ip4 = tp->intfc_address,
627           },
628           .fp_len = tp->mask_width,
629           .fp_proto = FIB_PROTOCOL_IP4,
630       };
631       if (is_add)
632           tp->fei = fib_table_entry_special_add(tp->inner_fib_index,
633                                                 &pfx,
634                                                 FIB_SOURCE_API,
635                                                 FIB_ENTRY_FLAG_NONE,
636                                                 adj_index);
637       else
638         {
639           fib_table_entry_delete(tp->inner_fib_index, &pfx, FIB_SOURCE_API);
640           tp->fei = FIB_NODE_INDEX_INVALID;
641         }
642     }
643   if (is_add == 0 && found_tunnel)
644     {
645       hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
646       vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
647                                    0 /* admin down */);
648       vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
649       vec_free (tp->rewrite_data);
650       pool_put (mm->eth_tunnels, tp);
651     }
652
653   return 0;
654 }
655
656 static clib_error_t *
657 create_mpls_ethernet_tunnel_command_fn (vlib_main_t * vm,
658                                         unformat_input_t * input,
659                                         vlib_cli_command_t * cmd)
660 {
661   unformat_input_t _line_input, * line_input = &_line_input;
662   vnet_main_t * vnm = vnet_get_main();
663   ip4_address_t intfc;
664   int adj_set = 0;
665   u8 dst[6];
666   int dst_set = 0, intfc_set = 0;
667   u32 mask_width;
668   u32 inner_fib_id = (u32)~0;
669   int rv;
670   u8 is_del = 0;
671   u8 l2_only = 0;
672   u32 tx_sw_if_index;
673   u32 sw_if_index = ~0;
674   
675   /* Get a line of input. */
676   if (! unformat_user (input, unformat_line_input, line_input))
677     return 0;
678
679   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
680     {
681       if (unformat (line_input, "dst %U", 
682                     unformat_ethernet_address, &dst))
683         dst_set = 1;
684       else if (unformat (line_input, "adj %U/%d", 
685                          unformat_ip4_address, &intfc, &mask_width))
686         adj_set = 1;
687       else if (unformat (line_input, "tx-intfc %U", 
688                          unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
689         intfc_set = 1;
690       else if (unformat (line_input, "fib-id %d", &inner_fib_id))
691         ;
692       else if (unformat (line_input, "l2-only"))
693         l2_only = 1;
694       else if (unformat (line_input, "del"))
695         is_del = 1;
696       else
697         return clib_error_return (0, "unknown input '%U'",
698                                   format_unformat_error, line_input);
699     }
700
701   if (!intfc_set)
702     return clib_error_return (0, "missing tx-intfc");
703
704   if (!dst_set)
705     return clib_error_return (0, "missing: dst <ethernet-address>");
706           
707   if (!adj_set)
708     return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
709   
710
711   rv = vnet_mpls_ethernet_add_del_tunnel (dst, &intfc, mask_width, 
712                                           inner_fib_id, tx_sw_if_index, 
713                                           &sw_if_index,
714                                           l2_only, !is_del);
715
716   switch (rv) 
717     {
718     case 0:
719       if (!is_del)
720         vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), sw_if_index);
721       break;
722     case VNET_API_ERROR_NO_SUCH_FIB:
723       return clib_error_return (0, "rx fib ID %d doesn't exist\n",
724                                 inner_fib_id);
725
726     case VNET_API_ERROR_NO_SUCH_ENTRY:
727       return clib_error_return (0, "tunnel not found\n");
728
729     case VNET_API_ERROR_NO_SUCH_LABEL:
730       /* 
731        * This happens when there's no MPLS label for the dst address
732        * no need for two error messages.
733        */
734         return clib_error_return (0, "no label for %U in fib %d", 
735                                   format_ip4_address, &intfc, inner_fib_id);
736       break;
737       
738     default:
739       return clib_error_return (0, "vnet_mpls_ethernet_add_del_tunnel returned %d", rv);
740       break;
741     }
742   return 0;
743 }
744
745
746 VLIB_CLI_COMMAND (create_mpls_ethernet_tunnel_command, static) = {
747   .path = "create mpls ethernet tunnel",
748   .short_help = 
749   "create mpls ethernet tunnel [del] dst <mac-addr> intfc <addr>/<mw>",
750   .function = create_mpls_ethernet_tunnel_command_fn,
751 };
752
753
754 int vnet_mpls_policy_tunnel_add_rewrite (mpls_main_t * mm, 
755                                          mpls_encap_t * e, 
756                                          u32 policy_tunnel_index)
757 {
758   mpls_eth_tunnel_t * t;
759   ip_adjacency_t adj;
760   u8 * rewrite_data = 0;
761   u8 * label_start;
762   mpls_unicast_header_t *lp;
763   int i;
764
765   if (pool_is_free_index (mm->eth_tunnels, policy_tunnel_index))
766     return VNET_API_ERROR_NO_SUCH_ENTRY;
767
768   t = pool_elt_at_index (mm->eth_tunnels, policy_tunnel_index);
769
770   memset (&adj, 0, sizeof (adj));
771
772   /* Build L2 encap */
773   vnet_rewrite_for_sw_interface
774     (mm->vnet_main, 
775      VNET_LINK_MPLS, 
776      t->tx_sw_if_index,
777      mpls_policy_encap_node.index,
778      t->tunnel_dst,
779      &adj.rewrite_header,
780      sizeof (adj.rewrite_data));
781   
782   vec_validate (rewrite_data, adj.rewrite_header.data_bytes -1);
783
784   clib_memcpy(rewrite_data, 
785          vnet_rewrite_get_data_internal(&adj.rewrite_header, 
786                                         sizeof (adj.rewrite_data)),
787          adj.rewrite_header.data_bytes);
788
789   /* Append the label stack */
790
791   vec_add2 (rewrite_data, label_start, vec_len(e->labels) * sizeof (u32));
792
793   lp = (mpls_unicast_header_t *) label_start;
794
795   for (i = 0; i < vec_len(e->labels); i++)
796     lp[i] = e->labels[i];
797   
798   /* Remember the rewrite data */
799   e->rewrite = rewrite_data;
800   e->output_next_index = adj.rewrite_header.next_index;
801
802   return 0;
803 }
804
805 int vnet_mpls_ethernet_add_del_policy_tunnel (u8 *dst,
806                                               ip4_address_t *intfc,
807                                               u32 mask_width,
808                                               u32 inner_fib_id, 
809                                               u32 tx_sw_if_index,
810                                               u32 * tunnel_sw_if_index,
811                                               u32 classify_table_index,
812                                               u32 * new_tunnel_index,
813                                               u8 l2_only,
814                                               u8 is_add)
815 {
816   ip4_main_t * im = &ip4_main;
817   mpls_main_t * mm = &mpls_main;
818   vnet_main_t * vnm = vnet_get_main();
819   mpls_eth_tunnel_t *tp;
820   u32 inner_fib_index = 0;
821   int found_tunnel = 0;
822   mpls_encap_t * e = 0;
823   u32 hw_if_index = ~0;
824   vnet_hw_interface_t * hi;
825   u32 slot;
826   u32 dummy;
827   
828   if (tunnel_sw_if_index == 0)
829     tunnel_sw_if_index = &dummy;
830
831   *tunnel_sw_if_index = ~0;
832
833   if (inner_fib_id != (u32)~0)
834     {
835       uword * p;
836       
837       p = hash_get (im->fib_index_by_table_id, inner_fib_id);
838       if (! p)
839         return VNET_API_ERROR_NO_SUCH_FIB;
840       inner_fib_index = p[0];
841     }
842
843   /* suppress duplicate mpls interface generation. */
844   pool_foreach (tp, mm->eth_tunnels, 
845   ({
846     /* 
847      * If we have a tunnel which matches (src, dst, intfc/mask)
848      * AND the expected route is in the FIB, it's a dup 
849      */
850     if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
851         && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
852         && tp->inner_fib_index == inner_fib_index
853         && FIB_NODE_INDEX_INVALID != tp->fei)
854       {
855         found_tunnel = 1;
856
857         if (is_add)
858           {
859             if (l2_only)
860               return 1;
861             else
862               {
863                 goto reinstall_it;
864               }
865           }
866         else
867           {
868             /* Delete */
869             goto add_del_route;
870           }
871
872       }
873   }));
874     
875   /* Delete, and we can't find the tunnel */
876   if (is_add == 0 && found_tunnel == 0)
877     return VNET_API_ERROR_NO_SUCH_ENTRY;
878
879   pool_get(mm->eth_tunnels, tp);
880   memset (tp, 0, sizeof (*tp));
881     
882   if (vec_len (mm->free_eth_sw_if_indices) > 0)
883     {
884       hw_if_index = 
885         mm->free_eth_sw_if_indices[vec_len(mm->free_eth_sw_if_indices)-1];
886       _vec_len (mm->free_eth_sw_if_indices) -= 1;
887       hi = vnet_get_hw_interface (vnm, hw_if_index);
888       hi->dev_instance = tp - mm->eth_tunnels;
889       hi->hw_instance = tp - mm->eth_tunnels;
890     }
891   else 
892     {
893       hw_if_index = vnet_register_interface
894         (vnm, mpls_eth_device_class.index, tp - mm->eth_tunnels,
895          mpls_eth_hw_interface_class.index,
896          tp - mm->eth_tunnels);
897       hi = vnet_get_hw_interface (vnm, hw_if_index);
898
899       /* ... to make the IP and L2 x-connect cases identical */
900       slot = vlib_node_add_named_next_with_slot
901         (vnm->vlib_main, hi->tx_node_index, 
902          "interface-output", MPLS_ETH_OUTPUT_NEXT_OUTPUT);
903
904       ASSERT (slot == MPLS_ETH_OUTPUT_NEXT_OUTPUT);
905     }
906   
907   *tunnel_sw_if_index = hi->sw_if_index;
908   vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
909                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);      
910
911   tp->hw_if_index = hw_if_index;
912
913  reinstall_it:
914   clib_memcpy(tp->tunnel_dst, dst, sizeof (tp->tunnel_dst));
915   tp->intfc_address.as_u32 = intfc->as_u32;
916   tp->mask_width = mask_width;
917   tp->inner_fib_index = inner_fib_index;
918   tp->encap_index = e - mm->encaps;
919   tp->tx_sw_if_index = tx_sw_if_index;
920   tp->l2_only = l2_only;
921   tp->fei = FIB_NODE_INDEX_INVALID;
922
923   if (new_tunnel_index)
924     *new_tunnel_index = tp - mm->eth_tunnels;
925
926  add_del_route:
927
928   if (!l2_only)
929     {
930       const fib_prefix_t pfx = {
931           .fp_addr = {
932               .ip4 = tp->intfc_address,
933           },
934           .fp_len = tp->mask_width,
935           .fp_proto = FIB_PROTOCOL_IP4,
936       };
937       dpo_id_t dpo = DPO_INVALID;
938
939       if (is_add)
940         {
941           dpo_set(&dpo,
942                   DPO_CLASSIFY,
943                   DPO_PROTO_IP4,
944                   classify_dpo_create(FIB_PROTOCOL_IP4,
945                                       classify_table_index));
946
947           tp->fei = fib_table_entry_special_dpo_add(tp->inner_fib_index,
948                                                     &pfx,
949                                                     FIB_SOURCE_API,
950                                                     FIB_ENTRY_FLAG_EXCLUSIVE,
951                                                     &dpo);
952           dpo_reset(&dpo);
953         }
954       else
955         {
956           fib_table_entry_delete(tp->inner_fib_index, &pfx, FIB_SOURCE_API);
957           tp->fei = FIB_NODE_INDEX_INVALID;
958         }
959     }
960   if (is_add == 0 && found_tunnel)
961     {
962       hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
963       vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
964                                    0 /* admin down */);
965       vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
966       pool_put (mm->eth_tunnels, tp);
967     }
968
969   return 0;
970 }
971
972 static clib_error_t *
973 create_mpls_ethernet_policy_tunnel_command_fn (vlib_main_t * vm,
974                                                unformat_input_t * input,
975                                                vlib_cli_command_t * cmd)
976 {
977   unformat_input_t _line_input, * line_input = &_line_input;
978   vnet_main_t * vnm = vnet_get_main();
979   ip4_address_t intfc;
980   int adj_set = 0;
981   u8 dst[6];
982   int dst_set = 0, intfc_set = 0;
983   u32 mask_width;
984   u32 inner_fib_id = (u32)~0;
985   u32 classify_table_index = (u32)~0;
986   u32 new_tunnel_index;
987   int rv;
988   u8 is_del = 0;
989   u8 l2_only = 0;
990   u32 tx_sw_if_index;
991   
992   /* Get a line of input. */
993   if (! unformat_user (input, unformat_line_input, line_input))
994     return 0;
995
996   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
997     {
998       if (unformat (line_input, "dst %U", 
999                     unformat_ethernet_address, &dst))
1000         dst_set = 1;
1001       else if (unformat (line_input, "adj %U/%d", 
1002                          unformat_ip4_address, &intfc, &mask_width))
1003         adj_set = 1;
1004       else if (unformat (line_input, "tx-intfc %U", 
1005                          unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
1006         intfc_set = 1;
1007       else if (unformat (line_input, "classify-table-index %d",
1008                          &classify_table_index))
1009         ;
1010       else if (unformat (line_input, "fib-id %d", &inner_fib_id))
1011         ;
1012       else if (unformat (line_input, "l2-only"))
1013         l2_only = 1;
1014       else if (unformat (line_input, "del"))
1015         is_del = 1;
1016       else
1017         return clib_error_return (0, "unknown input '%U'",
1018                                   format_unformat_error, line_input);
1019     }
1020
1021   if (classify_table_index == ~0)
1022     return clib_error_return (0, "missing classify_table_index");
1023
1024   if (!intfc_set)
1025     return clib_error_return (0, "missing tx-intfc");
1026
1027   if (!dst_set)
1028     return clib_error_return (0, "missing: dst <ethernet-address>");
1029           
1030   if (!adj_set)
1031     return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
1032   
1033
1034   rv = vnet_mpls_ethernet_add_del_policy_tunnel (dst, &intfc, mask_width, 
1035                                                  inner_fib_id, tx_sw_if_index, 
1036                                                  0 /* tunnel sw_if_index */, 
1037                                                  classify_table_index,
1038                                                  &new_tunnel_index,
1039                                                  l2_only, !is_del);
1040   switch (rv) 
1041     {
1042     case 0:
1043       if (!is_del)
1044         vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), new_tunnel_index);
1045       break;
1046     case VNET_API_ERROR_NO_SUCH_FIB:
1047       return clib_error_return (0, "rx fib ID %d doesn't exist\n",
1048                                 inner_fib_id);
1049
1050     case VNET_API_ERROR_NO_SUCH_ENTRY:
1051       return clib_error_return (0, "tunnel not found\n");
1052
1053     case VNET_API_ERROR_NO_SUCH_LABEL:
1054       /* 
1055        * This happens when there's no MPLS label for the dst address
1056        * no need for two error messages.
1057        */
1058         return clib_error_return (0, "no label for %U in fib %d", 
1059                                   format_ip4_address, &intfc, inner_fib_id);
1060       break;
1061       
1062     default:
1063       return clib_error_return (0, "vnet_mpls_ethernet_add_del_policy_tunnel returned %d", rv);
1064       break;
1065     }
1066
1067   return 0;
1068 }
1069
1070 VLIB_CLI_COMMAND (create_mpls_ethernet_policy_tunnel_command, static) = {
1071   .path = "create mpls ethernet policy tunnel",
1072   .short_help = 
1073   "create mpls ethernet policy tunnel [del] dst <mac-addr> intfc <addr>/<mw>\n"
1074   " classify-table-index <nn>",
1075   .function = create_mpls_ethernet_policy_tunnel_command_fn,
1076 };
1077
1078 static clib_error_t *
1079 mpls_interface_enable_disable (vlib_main_t * vm,
1080                                unformat_input_t * input,
1081                                vlib_cli_command_t * cmd)
1082 {
1083   vnet_main_t * vnm = vnet_get_main();
1084   clib_error_t * error = 0;
1085   u32 sw_if_index, enable;
1086
1087   sw_if_index = ~0;
1088
1089   if (! unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
1090     {
1091       error = clib_error_return (0, "unknown interface `%U'",
1092                                  format_unformat_error, input);
1093       goto done;
1094     }
1095
1096   if (unformat (input, "enable"))
1097       enable = 1;
1098   else if (unformat (input, "disable"))
1099       enable = 0;
1100   else
1101     {
1102       error = clib_error_return (0, "expected 'enable' or 'disable'",
1103                                  format_unformat_error, input);
1104       goto done;
1105     }
1106
1107   mpls_sw_interface_enable_disable(&mpls_main, sw_if_index, enable);
1108
1109  done:
1110   return error;
1111 }
1112
1113 VLIB_CLI_COMMAND (set_interface_ip_table_command, static) = {
1114   .path = "set interface mpls",
1115   .function = mpls_interface_enable_disable,
1116   .short_help = "Enable/Disable an interface for MPLS forwarding",
1117 };