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