Add support for multiple microarchitectures in single binary
[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 = b0->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       break;
1087
1088     case VNET_API_ERROR_NO_SUCH_INNER_FIB:
1089       return clib_error_return (0, "inner fib ID %d doesn't exist\n",
1090                                 inner_fib_id);
1091     case VNET_API_ERROR_NO_SUCH_FIB:
1092       return clib_error_return (0, "outer fib ID %d doesn't exist\n",
1093                                 outer_fib_id);
1094
1095     case VNET_API_ERROR_NO_SUCH_ENTRY:
1096       return clib_error_return (0, "tunnel not found\n");
1097
1098     case VNET_API_ERROR_NO_SUCH_LABEL:
1099       /* 
1100        * This happens when there's no MPLS label for the dst address
1101        * no need for two error messages.
1102        */
1103       break;
1104       
1105     default:
1106       return clib_error_return (0, "vnet_mpls_gre_add_del_tunnel returned %d",
1107                                 rv);
1108     }
1109   return 0;
1110 }
1111
1112 VLIB_CLI_COMMAND (create_mpls_tunnel_command, static) = {
1113   .path = "create mpls gre tunnel",
1114   .short_help = 
1115   "create mpls gre tunnel [del] src <addr> dst <addr> intfc <addr>/<mw>",
1116   .function = create_mpls_gre_tunnel_command_fn,
1117 };
1118
1119 u8 * format_mpls_encap_index (u8 * s, va_list * args)
1120 {
1121   mpls_main_t * mm = va_arg (*args, mpls_main_t *);
1122   u32 entry_index = va_arg (*args, u32);
1123   mpls_encap_t * e;
1124   int i;
1125
1126   e = pool_elt_at_index (mm->encaps, entry_index);
1127   
1128   for (i = 0; i < vec_len (e->labels); i++)
1129     s = format 
1130         (s, "%d ", vnet_mpls_uc_get_label(clib_net_to_host_u32 
1131                                           (e->labels[i].label_exp_s_ttl)));
1132
1133   return s;
1134 }
1135
1136 u8 * format_mpls_gre_tunnel (u8 * s, va_list * args)
1137 {
1138   mpls_gre_tunnel_t * t = va_arg (*args, mpls_gre_tunnel_t *);
1139   mpls_main_t * mm = &mpls_main;
1140   
1141   if (t->l2_only == 0)
1142     {
1143       s = format (s, "[%d]: src %U, dst %U, adj %U/%d, labels %U\n",
1144                   t - mm->gre_tunnels, 
1145                   format_ip4_address, &t->tunnel_src,
1146                   format_ip4_address, &t->tunnel_dst,
1147                   format_ip4_address, &t->intfc_address,
1148                   t->mask_width, 
1149                   format_mpls_encap_index, mm, t->encap_index);
1150
1151       s = format (s, "      inner fib index %d, outer fib index %d",
1152                   t->inner_fib_index, t->outer_fib_index);
1153     }
1154   else
1155     {
1156       s = format (s, "[%d]: src %U, dst %U, key %U, labels %U\n",
1157                   t - mm->gre_tunnels, 
1158                   format_ip4_address, &t->tunnel_src,
1159                   format_ip4_address, &t->tunnel_dst,
1160                   format_ip4_address, &t->intfc_address,
1161                   format_mpls_encap_index, mm, t->encap_index);
1162
1163       s = format (s, "      l2 interface %d, outer fib index %d",
1164                   t->hw_if_index, t->outer_fib_index);
1165     }
1166
1167   return s;
1168 }
1169
1170 u8 * format_mpls_ethernet_tunnel (u8 * s, va_list * args)
1171 {
1172   mpls_eth_tunnel_t * t = va_arg (*args, mpls_eth_tunnel_t *);
1173   mpls_main_t * mm = &mpls_main;
1174   
1175   s = format (s, "[%d]: dst %U, adj %U/%d, labels %U\n",
1176               t - mm->eth_tunnels, 
1177               format_ethernet_address, &t->tunnel_dst,
1178               format_ip4_address, &t->intfc_address,
1179               t->mask_width, 
1180               format_mpls_encap_index, mm, t->encap_index);
1181
1182
1183   s = format (s, "      tx on %U, rx fib index %d", 
1184               format_vnet_sw_if_index_name, mm->vnet_main, t->tx_sw_if_index,
1185               t->inner_fib_index);
1186
1187   return s;
1188 }
1189
1190 static clib_error_t *
1191 show_mpls_tunnel_command_fn (vlib_main_t * vm,
1192                              unformat_input_t * input,
1193                              vlib_cli_command_t * cmd)
1194 {
1195   mpls_main_t * mm = &mpls_main;
1196   mpls_gre_tunnel_t * gt;
1197   mpls_eth_tunnel_t * et;
1198
1199   if (pool_elts (mm->gre_tunnels))
1200     {
1201       vlib_cli_output (vm, "MPLS-GRE tunnels");
1202       pool_foreach (gt, mm->gre_tunnels,
1203       ({
1204         vlib_cli_output (vm, "%U", format_mpls_gre_tunnel, gt);
1205       }));
1206     }
1207   else
1208     vlib_cli_output (vm, "No MPLS-GRE tunnels");
1209
1210   if (pool_elts (mm->eth_tunnels))
1211     {
1212       vlib_cli_output (vm, "MPLS-Ethernet tunnels");
1213       pool_foreach (et, mm->eth_tunnels,
1214       ({
1215         vlib_cli_output (vm, "%U", format_mpls_ethernet_tunnel, et);
1216       }));
1217     }
1218   else
1219     vlib_cli_output (vm, "No MPLS-Ethernet tunnels");
1220
1221   return 0;
1222 }
1223
1224 VLIB_CLI_COMMAND (show_mpls_tunnel_command, static) = {
1225     .path = "show mpls tunnel",
1226     .short_help = "show mpls tunnel",
1227     .function = show_mpls_tunnel_command_fn,
1228 };
1229
1230 /* force inclusion from application's main.c */
1231 clib_error_t *mpls_interface_init (vlib_main_t *vm)
1232 {
1233   clib_error_t * error;
1234
1235   if ((error = vlib_call_init_function (vm, mpls_policy_encap_init)))
1236       return error;
1237
1238   return 0;
1239 }
1240 VLIB_INIT_FUNCTION(mpls_interface_init);
1241
1242
1243 static u8 * mpls_ethernet_rewrite (mpls_main_t *mm, mpls_eth_tunnel_t * t)
1244 {
1245   u8 * rewrite_data = 0;
1246   mpls_encap_t * e;
1247   mpls_unicast_header_t *lp0;
1248   int i;
1249
1250  /* look up the encap label stack using the RX FIB and adjacency address*/
1251   e = mpls_encap_by_fib_and_dest (mm, t->inner_fib_index, 
1252                                   t->intfc_address.as_u32);
1253
1254   if (e == 0)
1255     {
1256       clib_warning ("no label for inner fib index %d, dst %U",
1257                     t->inner_fib_index, format_ip4_address, 
1258                     &t->intfc_address);
1259       return 0;
1260     }
1261  
1262   vec_validate (rewrite_data, 
1263                 sizeof (mpls_unicast_header_t) * vec_len(e->labels) -1);
1264
1265   /* Copy the encap label stack */
1266   lp0 = (mpls_unicast_header_t *) rewrite_data;
1267   
1268   for (i = 0; i < vec_len(e->labels); i++)
1269     lp0[i] = e->labels[i];
1270   
1271   return (rewrite_data);
1272 }
1273
1274 int vnet_mpls_ethernet_add_del_tunnel (u8 *dst,
1275                                        ip4_address_t *intfc,
1276                                        u32 mask_width,
1277                                        u32 inner_fib_id, 
1278                                        u32 tx_sw_if_index,
1279                                        u32 * tunnel_sw_if_index,
1280                                        u8 l2_only,
1281                                        u8 is_add)
1282 {
1283   ip4_main_t * im = &ip4_main;
1284   ip_lookup_main_t * lm = &im->lookup_main;
1285   mpls_main_t * mm = &mpls_main;
1286   vnet_main_t * vnm = vnet_get_main();
1287   ip4_address_t zero;
1288   mpls_eth_tunnel_t *tp;
1289   int need_route_add_del = 1;
1290   u32 inner_fib_index = 0;
1291   ip_adjacency_t adj;
1292   u32 adj_index;
1293   u8 * rewrite_data;
1294   int found_tunnel = 0;
1295   mpls_encap_t * e = 0;
1296   u32 hw_if_index = ~0;
1297   vnet_hw_interface_t * hi;
1298   u32 slot;
1299   u32 dummy;
1300   
1301   zero.as_u32 = 0;
1302   
1303   if (tunnel_sw_if_index == 0)
1304     tunnel_sw_if_index = &dummy;
1305
1306   *tunnel_sw_if_index = ~0;
1307
1308   if (inner_fib_id != (u32)~0)
1309     {
1310       uword * p;
1311       
1312       p = hash_get (im->fib_index_by_table_id, inner_fib_id);
1313       if (! p)
1314         return VNET_API_ERROR_NO_SUCH_FIB;
1315       inner_fib_index = p[0];
1316     }
1317
1318   /* suppress duplicate mpls interface generation. */
1319   pool_foreach (tp, mm->eth_tunnels, 
1320   ({
1321     /* 
1322      * If we have a tunnel which matches (src, dst, intfc/mask)
1323      * AND the expected route is in the FIB, it's a dup 
1324      */
1325     if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
1326         && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
1327         && tp->inner_fib_index == inner_fib_index) 
1328       {
1329         ip4_fib_t * fib = vec_elt_at_index (im->fibs, inner_fib_index);
1330         uword * hash = fib->adj_index_by_dst_address[mask_width];
1331         uword key = intfc->as_u32 & im->fib_masks[mask_width];
1332         uword *p = hash_get (hash, key);
1333
1334         found_tunnel = 1;
1335
1336         if (is_add)
1337           {
1338             if (p || l2_only)
1339               return 1;
1340             else
1341               {
1342                 e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, 
1343                                                 intfc->as_u32);
1344                 if (e == 0)
1345                   return VNET_API_ERROR_NO_SUCH_LABEL;
1346                 
1347                 goto reinstall_it;
1348               }
1349           }
1350         else
1351           {
1352             /* Delete, the route is already gone? */
1353             if (!p)
1354               need_route_add_del = 0;
1355             goto add_del_route;
1356           }
1357
1358       }
1359   }));
1360     
1361   /* Delete, and we can't find the tunnel */
1362   if (is_add == 0 && found_tunnel == 0)
1363     return VNET_API_ERROR_NO_SUCH_ENTRY;
1364
1365   e = mpls_encap_by_fib_and_dest (mm, inner_fib_index, intfc->as_u32);
1366   if (e == 0)
1367     return VNET_API_ERROR_NO_SUCH_LABEL;
1368
1369   pool_get(mm->eth_tunnels, tp);
1370   memset (tp, 0, sizeof (*tp));
1371     
1372   if (vec_len (mm->free_eth_sw_if_indices) > 0)
1373     {
1374       hw_if_index = 
1375         mm->free_eth_sw_if_indices[vec_len(mm->free_eth_sw_if_indices)-1];
1376       _vec_len (mm->free_eth_sw_if_indices) -= 1;
1377       hi = vnet_get_hw_interface (vnm, hw_if_index);
1378       hi->dev_instance = tp - mm->eth_tunnels;
1379       hi->hw_instance = tp - mm->eth_tunnels;
1380     }
1381   else 
1382     {
1383       hw_if_index = vnet_register_interface
1384         (vnm, mpls_eth_device_class.index, tp - mm->eth_tunnels,
1385          mpls_eth_hw_interface_class.index,
1386          tp - mm->eth_tunnels);
1387       hi = vnet_get_hw_interface (vnm, hw_if_index);
1388
1389       /* ... to make the IP and L2 x-connect cases identical */
1390       slot = vlib_node_add_named_next_with_slot
1391         (vnm->vlib_main, hi->tx_node_index, 
1392          "interface-output", MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1393
1394       ASSERT (slot == MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1395     }
1396   
1397   *tunnel_sw_if_index = hi->sw_if_index;
1398   vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1399                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);      
1400
1401   tp->hw_if_index = hw_if_index;
1402
1403  reinstall_it:
1404   clib_memcpy(tp->tunnel_dst, dst, sizeof (tp->tunnel_dst));
1405   tp->intfc_address.as_u32 = intfc->as_u32;
1406   tp->mask_width = mask_width;
1407   tp->inner_fib_index = inner_fib_index;
1408   tp->encap_index = e - mm->encaps;
1409   tp->tx_sw_if_index = tx_sw_if_index;
1410   tp->l2_only = l2_only;
1411
1412   /* Create the adjacency and add to v4 fib */
1413   memset(&adj, 0, sizeof (adj));
1414   adj.explicit_fib_index = ~0;
1415   adj.lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
1416     
1417   rewrite_data = mpls_ethernet_rewrite (mm, tp);
1418   if (rewrite_data == 0)
1419     {
1420       if (*tunnel_sw_if_index != ~0)
1421         {
1422           hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1423           vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1424                                        0 /* admin down */);
1425           vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
1426       }
1427
1428       pool_put (mm->eth_tunnels, tp);
1429       return VNET_API_ERROR_NO_SUCH_LABEL;
1430     }
1431   
1432   vnet_rewrite_for_sw_interface
1433     (vnm,
1434      VNET_L3_PACKET_TYPE_MPLS_UNICAST, 
1435      tx_sw_if_index,
1436      ip4_rewrite_node.index,
1437      tp->tunnel_dst,
1438      &adj.rewrite_header,
1439      sizeof (adj.rewrite_data));
1440   
1441   /* 
1442    * Prepend the (0,1,2) VLAN tag ethernet header 
1443    * we just built to the mpls header stack
1444    */
1445   vec_insert (rewrite_data, adj.rewrite_header.data_bytes, 0);
1446   clib_memcpy(rewrite_data, 
1447          vnet_rewrite_get_data_internal(&adj.rewrite_header, 
1448                                         sizeof (adj.rewrite_data)),
1449          adj.rewrite_header.data_bytes);
1450
1451   vnet_rewrite_set_data_internal (&adj.rewrite_header, 
1452                                   sizeof(adj.rewrite_data),
1453                                   rewrite_data, 
1454                                   vec_len(rewrite_data));
1455   
1456   vec_free (tp->rewrite_data);
1457   
1458   tp->rewrite_data = rewrite_data;
1459
1460   if (!l2_only)
1461     ip_add_adjacency (lm, &adj, 1 /* one adj */,
1462                       &adj_index);
1463   
1464  add_del_route:
1465
1466   if (need_route_add_del && !l2_only)
1467     {
1468       if (is_add)
1469         ip4_add_del_route_next_hop (im,
1470                                     IP4_ROUTE_FLAG_ADD,
1471                                     &tp->intfc_address,
1472                                     tp->mask_width,
1473                                     &zero /* no next hop */,
1474                                     (u32)~0 /* next_hop_sw_if_index */,
1475                                     1 /* weight */, 
1476                                     adj_index,
1477                                     tp->inner_fib_index);
1478       else
1479         {
1480           ip4_add_del_route_args_t a;
1481           memset (&a, 0, sizeof (a));
1482
1483           a.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_DEL;
1484           a.table_index_or_table_id = tp->inner_fib_index;
1485           a.dst_address = tp->intfc_address;
1486           a.dst_address_length = tp->mask_width;
1487           a.adj_index = ~0;
1488
1489           ip4_add_del_route (im, &a);
1490           ip4_maybe_remap_adjacencies (im, tp->inner_fib_index, 
1491                                        IP4_ROUTE_FLAG_FIB_INDEX);
1492         }
1493     }
1494   if (is_add == 0 && found_tunnel)
1495     {
1496       hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1497       vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1498                                    0 /* admin down */);
1499       vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
1500       vec_free (tp->rewrite_data);
1501       pool_put (mm->eth_tunnels, tp);
1502     }
1503
1504   return 0;
1505 }
1506
1507 static clib_error_t *
1508 create_mpls_ethernet_tunnel_command_fn (vlib_main_t * vm,
1509                                         unformat_input_t * input,
1510                                         vlib_cli_command_t * cmd)
1511 {
1512   unformat_input_t _line_input, * line_input = &_line_input;
1513   vnet_main_t * vnm = vnet_get_main();
1514   ip4_address_t intfc;
1515   int adj_set = 0;
1516   u8 dst[6];
1517   int dst_set = 0, intfc_set = 0;
1518   u32 mask_width;
1519   u32 inner_fib_id = (u32)~0;
1520   int rv;
1521   u8 is_del = 0;
1522   u8 l2_only = 0;
1523   u32 tx_sw_if_index;
1524   
1525   /* Get a line of input. */
1526   if (! unformat_user (input, unformat_line_input, line_input))
1527     return 0;
1528
1529   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1530     {
1531       if (unformat (line_input, "dst %U", 
1532                     unformat_ethernet_address, &dst))
1533         dst_set = 1;
1534       else if (unformat (line_input, "adj %U/%d", 
1535                          unformat_ip4_address, &intfc, &mask_width))
1536         adj_set = 1;
1537       else if (unformat (line_input, "tx-intfc %U", 
1538                          unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
1539         intfc_set = 1;
1540       else if (unformat (line_input, "fib-id %d", &inner_fib_id))
1541         ;
1542       else if (unformat (line_input, "l2-only"))
1543         l2_only = 1;
1544       else if (unformat (line_input, "del"))
1545         is_del = 1;
1546       else
1547         return clib_error_return (0, "unknown input '%U'",
1548                                   format_unformat_error, line_input);
1549     }
1550
1551   if (!intfc_set)
1552     return clib_error_return (0, "missing tx-intfc");
1553
1554   if (!dst_set)
1555     return clib_error_return (0, "missing: dst <ethernet-address>");
1556           
1557   if (!adj_set)
1558     return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
1559   
1560
1561   rv = vnet_mpls_ethernet_add_del_tunnel (dst, &intfc, mask_width, 
1562                                           inner_fib_id, tx_sw_if_index, 
1563                                           0 /* tunnel sw_if_index */, 
1564                                           l2_only, !is_del);
1565
1566   switch (rv) 
1567     {
1568     case VNET_API_ERROR_NO_SUCH_FIB:
1569       return clib_error_return (0, "rx fib ID %d doesn't exist\n",
1570                                 inner_fib_id);
1571
1572     case VNET_API_ERROR_NO_SUCH_ENTRY:
1573       return clib_error_return (0, "tunnel not found\n");
1574
1575     case VNET_API_ERROR_NO_SUCH_LABEL:
1576       /* 
1577        * This happens when there's no MPLS label for the dst address
1578        * no need for two error messages.
1579        */
1580         return clib_error_return (0, "no label for %U in fib %d", 
1581                                   format_ip4_address, &intfc, inner_fib_id);
1582       break;
1583       
1584     default:
1585       break;
1586     }
1587   return 0;
1588 }
1589
1590
1591 VLIB_CLI_COMMAND (create_mpls_ethernet_tunnel_command, static) = {
1592   .path = "create mpls ethernet tunnel",
1593   .short_help = 
1594   "create mpls ethernet tunnel [del] dst <mac-addr> intfc <addr>/<mw>",
1595   .function = create_mpls_ethernet_tunnel_command_fn,
1596 };
1597
1598
1599 int vnet_mpls_policy_tunnel_add_rewrite (mpls_main_t * mm, 
1600                                          mpls_encap_t * e, 
1601                                          u32 policy_tunnel_index)
1602 {
1603   mpls_eth_tunnel_t * t;
1604   ip_adjacency_t adj;
1605   u8 * rewrite_data = 0;
1606   u8 * label_start;
1607   mpls_unicast_header_t *lp;
1608   int i;
1609
1610   if (pool_is_free_index (mm->eth_tunnels, policy_tunnel_index))
1611     return VNET_API_ERROR_NO_SUCH_ENTRY;
1612
1613   t = pool_elt_at_index (mm->eth_tunnels, policy_tunnel_index);
1614
1615   memset (&adj, 0, sizeof (adj));
1616
1617   /* Build L2 encap */
1618   vnet_rewrite_for_sw_interface
1619     (mm->vnet_main, 
1620      VNET_L3_PACKET_TYPE_MPLS_UNICAST, 
1621      t->tx_sw_if_index,
1622      mpls_policy_encap_node.index,
1623      t->tunnel_dst,
1624      &adj.rewrite_header,
1625      sizeof (adj.rewrite_data));
1626   
1627   vec_validate (rewrite_data, adj.rewrite_header.data_bytes -1);
1628
1629   clib_memcpy(rewrite_data, 
1630          vnet_rewrite_get_data_internal(&adj.rewrite_header, 
1631                                         sizeof (adj.rewrite_data)),
1632          adj.rewrite_header.data_bytes);
1633
1634   /* Append the label stack */
1635
1636   vec_add2 (rewrite_data, label_start, vec_len(e->labels) * sizeof (u32));
1637
1638   lp = (mpls_unicast_header_t *) label_start;
1639
1640   for (i = 0; i < vec_len(e->labels); i++)
1641     lp[i] = e->labels[i];
1642   
1643   /* Remember the rewrite data */
1644   e->rewrite = rewrite_data;
1645   e->output_next_index = adj.rewrite_header.next_index;
1646
1647   return 0;
1648 }
1649
1650 int vnet_mpls_ethernet_add_del_policy_tunnel (u8 *dst,
1651                                               ip4_address_t *intfc,
1652                                               u32 mask_width,
1653                                               u32 inner_fib_id, 
1654                                               u32 tx_sw_if_index,
1655                                               u32 * tunnel_sw_if_index,
1656                                               u32 classify_table_index,
1657                                               u32 * new_tunnel_index,
1658                                               u8 l2_only,
1659                                               u8 is_add)
1660 {
1661   ip4_main_t * im = &ip4_main;
1662   ip_lookup_main_t * lm = &im->lookup_main;
1663   mpls_main_t * mm = &mpls_main;
1664   vnet_main_t * vnm = vnet_get_main();
1665   ip4_address_t zero;
1666   mpls_eth_tunnel_t *tp;
1667   int need_route_add_del = 1;
1668   u32 inner_fib_index = 0;
1669   ip_adjacency_t adj;
1670   u32 adj_index;
1671   int found_tunnel = 0;
1672   mpls_encap_t * e = 0;
1673   u32 hw_if_index = ~0;
1674   vnet_hw_interface_t * hi;
1675   u32 slot;
1676   u32 dummy;
1677   
1678   zero.as_u32 = 0;
1679   
1680   if (tunnel_sw_if_index == 0)
1681     tunnel_sw_if_index = &dummy;
1682
1683   *tunnel_sw_if_index = ~0;
1684
1685   if (inner_fib_id != (u32)~0)
1686     {
1687       uword * p;
1688       
1689       p = hash_get (im->fib_index_by_table_id, inner_fib_id);
1690       if (! p)
1691         return VNET_API_ERROR_NO_SUCH_FIB;
1692       inner_fib_index = p[0];
1693     }
1694
1695   /* suppress duplicate mpls interface generation. */
1696   pool_foreach (tp, mm->eth_tunnels, 
1697   ({
1698     /* 
1699      * If we have a tunnel which matches (src, dst, intfc/mask)
1700      * AND the expected route is in the FIB, it's a dup 
1701      */
1702     if (!memcmp (&tp->tunnel_dst, dst, sizeof (*dst))
1703         && !memcmp (&tp->intfc_address, intfc, sizeof (*intfc))
1704         && tp->inner_fib_index == inner_fib_index) 
1705       {
1706         ip4_fib_t * fib = vec_elt_at_index (im->fibs, inner_fib_index);
1707         uword * hash = fib->adj_index_by_dst_address[mask_width];
1708         uword key = intfc->as_u32 & im->fib_masks[mask_width];
1709         uword *p = hash_get (hash, key);
1710
1711         found_tunnel = 1;
1712
1713         if (is_add)
1714           {
1715             if (p || l2_only)
1716               return 1;
1717             else
1718               {
1719                 goto reinstall_it;
1720               }
1721           }
1722         else
1723           {
1724             /* Delete, the route is already gone? */
1725             if (!p)
1726               need_route_add_del = 0;
1727             goto add_del_route;
1728           }
1729
1730       }
1731   }));
1732     
1733   /* Delete, and we can't find the tunnel */
1734   if (is_add == 0 && found_tunnel == 0)
1735     return VNET_API_ERROR_NO_SUCH_ENTRY;
1736
1737   pool_get(mm->eth_tunnels, tp);
1738   memset (tp, 0, sizeof (*tp));
1739     
1740   if (vec_len (mm->free_eth_sw_if_indices) > 0)
1741     {
1742       hw_if_index = 
1743         mm->free_eth_sw_if_indices[vec_len(mm->free_eth_sw_if_indices)-1];
1744       _vec_len (mm->free_eth_sw_if_indices) -= 1;
1745       hi = vnet_get_hw_interface (vnm, hw_if_index);
1746       hi->dev_instance = tp - mm->eth_tunnels;
1747       hi->hw_instance = tp - mm->eth_tunnels;
1748     }
1749   else 
1750     {
1751       hw_if_index = vnet_register_interface
1752         (vnm, mpls_eth_device_class.index, tp - mm->eth_tunnels,
1753          mpls_eth_hw_interface_class.index,
1754          tp - mm->eth_tunnels);
1755       hi = vnet_get_hw_interface (vnm, hw_if_index);
1756
1757       /* ... to make the IP and L2 x-connect cases identical */
1758       slot = vlib_node_add_named_next_with_slot
1759         (vnm->vlib_main, hi->tx_node_index, 
1760          "interface-output", MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1761
1762       ASSERT (slot == MPLS_ETH_OUTPUT_NEXT_OUTPUT);
1763     }
1764   
1765   *tunnel_sw_if_index = hi->sw_if_index;
1766   vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1767                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);      
1768
1769   tp->hw_if_index = hw_if_index;
1770
1771  reinstall_it:
1772   clib_memcpy(tp->tunnel_dst, dst, sizeof (tp->tunnel_dst));
1773   tp->intfc_address.as_u32 = intfc->as_u32;
1774   tp->mask_width = mask_width;
1775   tp->inner_fib_index = inner_fib_index;
1776   tp->encap_index = e - mm->encaps;
1777   tp->tx_sw_if_index = tx_sw_if_index;
1778   tp->l2_only = l2_only;
1779
1780   if (new_tunnel_index)
1781     *new_tunnel_index = tp - mm->eth_tunnels;
1782
1783   /* Create the classify adjacency and add to v4 fib */
1784   memset(&adj, 0, sizeof (adj));
1785   adj.explicit_fib_index = ~0;
1786   adj.lookup_next_index = IP_LOOKUP_NEXT_CLASSIFY;
1787   adj.classify.table_index = classify_table_index;
1788     
1789   if (!l2_only)
1790     ip_add_adjacency (lm, &adj, 1 /* one adj */,
1791                       &adj_index);
1792   
1793  add_del_route:
1794
1795   if (need_route_add_del && !l2_only)
1796     {
1797       if (is_add)
1798         ip4_add_del_route_next_hop (im,
1799                                     IP4_ROUTE_FLAG_ADD,
1800                                     &tp->intfc_address,
1801                                     tp->mask_width,
1802                                     &zero /* no next hop */,
1803                                     (u32)~0 /* next_hop_sw_if_index */,
1804                                     1 /* weight */, 
1805                                     adj_index,
1806                                     tp->inner_fib_index);
1807       else
1808         {
1809           ip4_add_del_route_args_t a;
1810           memset (&a, 0, sizeof (a));
1811
1812           a.flags = IP4_ROUTE_FLAG_FIB_INDEX | IP4_ROUTE_FLAG_DEL;
1813           a.table_index_or_table_id = tp->inner_fib_index;
1814           a.dst_address = tp->intfc_address;
1815           a.dst_address_length = tp->mask_width;
1816           a.adj_index = ~0;
1817
1818           ip4_add_del_route (im, &a);
1819           ip4_maybe_remap_adjacencies (im, tp->inner_fib_index, 
1820                                        IP4_ROUTE_FLAG_FIB_INDEX);
1821         }
1822     }
1823   if (is_add == 0 && found_tunnel)
1824     {
1825       hi = vnet_get_hw_interface (vnm, tp->hw_if_index);
1826       vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
1827                                    0 /* admin down */);
1828       vec_add1 (mm->free_eth_sw_if_indices, tp->hw_if_index);
1829       pool_put (mm->eth_tunnels, tp);
1830     }
1831
1832   return 0;
1833 }
1834
1835 static clib_error_t *
1836 create_mpls_ethernet_policy_tunnel_command_fn (vlib_main_t * vm,
1837                                                unformat_input_t * input,
1838                                                vlib_cli_command_t * cmd)
1839 {
1840   unformat_input_t _line_input, * line_input = &_line_input;
1841   vnet_main_t * vnm = vnet_get_main();
1842   ip4_address_t intfc;
1843   int adj_set = 0;
1844   u8 dst[6];
1845   int dst_set = 0, intfc_set = 0;
1846   u32 mask_width;
1847   u32 inner_fib_id = (u32)~0;
1848   u32 classify_table_index = (u32)~0;
1849   u32 new_tunnel_index;
1850   int rv;
1851   u8 is_del = 0;
1852   u8 l2_only = 0;
1853   u32 tx_sw_if_index;
1854   
1855   /* Get a line of input. */
1856   if (! unformat_user (input, unformat_line_input, line_input))
1857     return 0;
1858
1859   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1860     {
1861       if (unformat (line_input, "dst %U", 
1862                     unformat_ethernet_address, &dst))
1863         dst_set = 1;
1864       else if (unformat (line_input, "adj %U/%d", 
1865                          unformat_ip4_address, &intfc, &mask_width))
1866         adj_set = 1;
1867       else if (unformat (line_input, "tx-intfc %U", 
1868                          unformat_vnet_sw_interface, vnm, &tx_sw_if_index))
1869         intfc_set = 1;
1870       else if (unformat (line_input, "classify-table-index %d",
1871                          &classify_table_index))
1872         ;
1873       else if (unformat (line_input, "fib-id %d", &inner_fib_id))
1874         ;
1875       else if (unformat (line_input, "l2-only"))
1876         l2_only = 1;
1877       else if (unformat (line_input, "del"))
1878         is_del = 1;
1879       else
1880         return clib_error_return (0, "unknown input '%U'",
1881                                   format_unformat_error, line_input);
1882     }
1883
1884   if (classify_table_index == ~0)
1885     return clib_error_return (0, "missing classify_table_index");
1886
1887   if (!intfc_set)
1888     return clib_error_return (0, "missing tx-intfc");
1889
1890   if (!dst_set)
1891     return clib_error_return (0, "missing: dst <ethernet-address>");
1892           
1893   if (!adj_set)
1894     return clib_error_return (0, "missing: intfc <ip-address>/<mask-width>");
1895   
1896
1897   rv = vnet_mpls_ethernet_add_del_policy_tunnel (dst, &intfc, mask_width, 
1898                                                  inner_fib_id, tx_sw_if_index, 
1899                                                  0 /* tunnel sw_if_index */, 
1900                                                  classify_table_index,
1901                                                  &new_tunnel_index,
1902                                                  l2_only, !is_del);
1903   switch (rv) 
1904     {
1905     case VNET_API_ERROR_NO_SUCH_FIB:
1906       return clib_error_return (0, "rx fib ID %d doesn't exist\n",
1907                                 inner_fib_id);
1908
1909     case VNET_API_ERROR_NO_SUCH_ENTRY:
1910       return clib_error_return (0, "tunnel not found\n");
1911
1912     case VNET_API_ERROR_NO_SUCH_LABEL:
1913       /* 
1914        * This happens when there's no MPLS label for the dst address
1915        * no need for two error messages.
1916        */
1917         return clib_error_return (0, "no label for %U in fib %d", 
1918                                   format_ip4_address, &intfc, inner_fib_id);
1919       break;
1920       
1921     default:
1922       break;
1923     }
1924
1925   if (!is_del)
1926     vlib_cli_output (vm, "tunnel index %d", new_tunnel_index);
1927
1928   return 0;
1929 }
1930
1931 VLIB_CLI_COMMAND (create_mpls_ethernet_policy_tunnel_command, static) = {
1932   .path = "create mpls ethernet policy tunnel",
1933   .short_help = 
1934   "create mpls ethernet policy tunnel [del] dst <mac-addr> intfc <addr>/<mw>\n"
1935   " classify-table-index <nn>",
1936   .function = create_mpls_ethernet_policy_tunnel_command_fn,
1937 };