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