Pipes
[vpp.git] / src / vnet / devices / pipe / pipe.c
1 /*
2  * Copyright (c) 2018 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/devices/pipe/pipe.h>
17
18 #include <vppinfra/sparse_vec.h>
19
20 /**
21  * @file
22  * @brief Pipe Interfaces.
23  *
24  * A pipe interface, like the UNIX pipe, is a pair of interfaces
25  * that are joined.
26  */
27 const static pipe_t PIPE_INVALID = {
28   .sw_if_index = ~0,
29   .subint = {0},
30 };
31
32 /**
33  * Various 'module' lavel variables
34  */
35 typedef struct pipe_main_t_
36 {
37   /**
38    * Allocated pipe instances
39    */
40   uword *instances;
41
42   /**
43    * the per-swif-index array of pipes. Each end of the pipe is stored againt
44    * its respective sw_if_index
45    */
46   pipe_t *pipes;
47 } pipe_main_t;
48
49 static pipe_main_t pipe_main;
50
51 /*
52  * The pipe rewrite is the same size as an ethernet header (since it
53  * is an ethernet interface and the DP is optimised for writing
54  * sizeof(ethernet_header_t) rewirtes. Hwoever, there are no MAC addresses
55  * since pipes don't have them.
56  */
57 static u8 *
58 pipe_build_rewrite (vnet_main_t * vnm,
59                     u32 sw_if_index,
60                     vnet_link_t link_type, const void *dst_address)
61 {
62   ethernet_header_t *h;
63   ethernet_type_t type;
64   u8 *rewrite = NULL;
65
66   switch (link_type)
67     {
68 #define _(a,b) case VNET_LINK_##a: type = ETHERNET_TYPE_##b; break
69       _(IP4, IP4);
70       _(IP6, IP6);
71       _(MPLS, MPLS);
72       _(ARP, ARP);
73 #undef _
74     default:
75       return NULL;
76     }
77
78   vec_validate (rewrite, sizeof (ethernet_header_t));
79
80   h = (ethernet_header_t *) rewrite;
81   h->type = clib_host_to_net_u16 (type);
82
83   return (rewrite);
84 }
85
86 /* *INDENT-OFF* */
87 VNET_HW_INTERFACE_CLASS (pipe_hw_interface_class) = {
88   .name = "Pipe",
89   .build_rewrite = pipe_build_rewrite,
90   .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
91 };
92 /* *INDENT-ON* */
93
94 pipe_t *
95 pipe_get (u32 sw_if_index)
96 {
97   vec_validate_init_empty (pipe_main.pipes, sw_if_index, PIPE_INVALID);
98
99   return (&pipe_main.pipes[sw_if_index]);
100 }
101
102 uword
103 unformat_pipe_interface (unformat_input_t * input, va_list * args)
104 {
105   vnet_main_t *vnm = va_arg (*args, vnet_main_t *);
106   u32 *result = va_arg (*args, u32 *);
107   u32 hw_if_index;
108   ethernet_main_t *em = &ethernet_main;
109   ethernet_interface_t *eif;
110
111   if (!unformat_user (input, unformat_vnet_hw_interface, vnm, &hw_if_index))
112     return 0;
113
114   eif = ethernet_get_interface (em, hw_if_index);
115   if (eif)
116     {
117       *result = hw_if_index;
118       return 1;
119     }
120   return 0;
121 }
122
123 #define VNET_PIPE_TX_NEXT_ETHERNET_INPUT VNET_INTERFACE_TX_N_NEXT
124
125 /*
126  * The TX function bounces the packets back to pipe-rx with the TX interface
127  * swapped to the RX.
128  */
129 static uword
130 pipe_tx (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
131 {
132   u32 n_left_from, n_left_to_next, n_copy, *from, *to_next;
133   u32 next_index = VNET_PIPE_TX_NEXT_ETHERNET_INPUT;
134   u32 i, sw_if_index = 0;
135   u32 n_pkts = 0, n_bytes = 0;
136   u32 thread_index = vm->thread_index;
137   vnet_main_t *vnm = vnet_get_main ();
138   vnet_interface_main_t *im = &vnm->interface_main;
139   vlib_buffer_t *b;
140   pipe_t *pipe;
141
142   n_left_from = frame->n_vectors;
143   from = vlib_frame_args (frame);
144
145   while (n_left_from > 0)
146     {
147       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
148
149       n_copy = clib_min (n_left_from, n_left_to_next);
150
151       clib_memcpy (to_next, from, n_copy * sizeof (from[0]));
152       n_left_to_next -= n_copy;
153       n_left_from -= n_copy;
154       i = 0;
155       while (i < n_copy)
156         {
157           b = vlib_get_buffer (vm, from[i]);
158           sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX];
159
160           pipe = &pipe_main.pipes[sw_if_index];
161           // Set up RX index to be recv'd by the other end of the pipe
162           vnet_buffer (b)->sw_if_index[VLIB_RX] = pipe->sw_if_index;
163           vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0;
164
165           i++;
166           n_pkts++;
167           n_bytes += vlib_buffer_length_in_chain (vm, b);
168         }
169       from += n_copy;
170
171       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
172
173       /* increment TX interface stat */
174       vlib_increment_combined_counter (im->combined_sw_if_counters +
175                                        VNET_INTERFACE_COUNTER_TX,
176                                        thread_index, sw_if_index, n_pkts,
177                                        n_bytes);
178     }
179
180   return n_left_from;
181 }
182
183 static u8 *
184 format_pipe_name (u8 * s, va_list * args)
185 {
186   u32 dev_instance = va_arg (*args, u32);
187   return format (s, "pipe%d", dev_instance);
188 }
189
190 static clib_error_t *
191 pipe_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
192 {
193   vnet_hw_interface_t *hi;
194   u32 id, sw_if_index;
195
196   u32 hw_flags = ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
197                   VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
198   vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
199
200   /* *INDENT-OFF* */
201   hi = vnet_get_hw_interface (vnm, hw_if_index);
202   hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
203   ({
204     vnet_sw_interface_set_flags (vnm, sw_if_index, flags);
205   }));
206   /* *INDENT-ON* */
207
208   return (NULL);
209 }
210
211 /* *INDENT-OFF* */
212 VNET_DEVICE_CLASS (pipe_device_class) = {
213   .name = "Pipe",
214   .format_device_name = format_pipe_name,
215   .tx_function = pipe_tx,
216   .admin_up_down_function = pipe_admin_up_down,
217 };
218 /* *INDENT-ON* */
219
220 #define foreach_pipe_rx_next                    \
221   _ (DROP, "error-drop")
222
223 typedef enum pipe_rx_next_t_
224 {
225 #define _(s,n) PIPE_RX_NEXT_##s,
226   foreach_pipe_rx_next
227 #undef _
228     PIPE_RX_N_NEXT,
229 } pipe_rx_next_t;
230
231 typedef struct pipe_rx_trace_t_
232 {
233   u8 packet_data[32];
234 } pipe_rx_trace_t;
235
236 static u8 *
237 format_pipe_rx_trace (u8 * s, va_list * va)
238 {
239   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
240   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
241   pipe_rx_trace_t *t = va_arg (*va, pipe_rx_trace_t *);
242
243   s = format (s, "%U", format_ethernet_header, t->packet_data);
244
245   return s;
246 }
247
248 /*
249  * The pipe-rx node is a sibling of ethernet-input so steal it's
250  * next node mechanism
251  */
252 static_always_inline void
253 pipe_determine_next_node (ethernet_main_t * em,
254                           u32 is_l20,
255                           u32 type0,
256                           vlib_buffer_t * b0, pipe_rx_next_t * next0)
257 {
258   if (is_l20)
259     {
260       *next0 = em->l2_next;
261     }
262   else if (type0 == ETHERNET_TYPE_IP4)
263     {
264       *next0 = em->l3_next.input_next_ip4;
265     }
266   else if (type0 == ETHERNET_TYPE_IP6)
267     {
268       *next0 = em->l3_next.input_next_ip6;
269     }
270   else if (type0 == ETHERNET_TYPE_MPLS)
271     {
272       *next0 = em->l3_next.input_next_mpls;
273
274     }
275   else if (em->redirect_l3)
276     {
277       // L3 Redirect is on, the cached common next nodes will be
278       // pointing to the redirect node, catch the uncommon types here
279       *next0 = em->redirect_l3_next;
280     }
281   else
282     {
283       // uncommon ethertype, check table
284       u32 i0;
285       i0 = sparse_vec_index (em->l3_next.input_next_by_type, type0);
286       *next0 = vec_elt (em->l3_next.input_next_by_type, i0);
287
288       // The table is not populated with LLC values, so check that now.
289       if (type0 < 0x600)
290         {
291           *next0 = PIPE_RX_NEXT_DROP;
292         }
293     }
294 }
295
296 static_always_inline uword
297 pipe_rx (vlib_main_t * vm,
298          vlib_node_runtime_t * node, vlib_frame_t * from_frame)
299 {
300   u32 n_left_from, next_index, *from, *to_next;
301   u32 n_left_to_next;
302
303   from = vlib_frame_vector_args (from_frame);
304   n_left_from = from_frame->n_vectors;
305
306   if (node->flags & VLIB_NODE_FLAG_TRACE)
307     vlib_trace_frame_buffers_only (vm, node,
308                                    from,
309                                    n_left_from,
310                                    sizeof (from[0]),
311                                    sizeof (pipe_rx_trace_t));
312
313   next_index = node->cached_next_index;
314
315   while (n_left_from > 0)
316     {
317       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
318
319       while (n_left_from >= 4 && n_left_to_next >= 2)
320         {
321           u32 bi0, sw_if_index0, bi1, sw_if_index1;
322           pipe_rx_next_t next0, next1;
323           ethernet_header_t *e0, *e1;
324           vlib_buffer_t *b0, *b1;
325           pipe_t *pipe0, *pipe1;
326           u8 is_l20, is_l21;
327           u16 type0, type1;
328
329           // Prefetch next iteration
330           {
331             vlib_buffer_t *p2, *p3;
332
333             p2 = vlib_get_buffer (vm, from[2]);
334             p3 = vlib_get_buffer (vm, from[3]);
335             vlib_prefetch_buffer_header (p2, STORE);
336             vlib_prefetch_buffer_header (p3, STORE);
337             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD);
338             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, LOAD);
339           }
340
341           bi0 = from[0];
342           to_next[0] = bi0;
343           bi1 = from[1];
344           to_next[1] = bi1;
345           from += 2;
346           to_next += 2;
347           n_left_from -= 2;
348           n_left_to_next -= 2;
349
350           b0 = vlib_get_buffer (vm, bi0);
351           b1 = vlib_get_buffer (vm, bi1);
352
353           e0 = vlib_buffer_get_current (b0);
354           e1 = vlib_buffer_get_current (b1);
355           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
356           sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
357           type0 = clib_net_to_host_u16 (e0->type);
358           type1 = clib_net_to_host_u16 (e1->type);
359           pipe0 = &pipe_main.pipes[sw_if_index0];
360           pipe1 = &pipe_main.pipes[sw_if_index1];
361
362           vnet_buffer (b0)->l3_hdr_offset =
363             vnet_buffer (b0)->l2_hdr_offset + vnet_buffer (b0)->l2.l2_len;
364           vnet_buffer (b1)->l3_hdr_offset =
365             vnet_buffer (b1)->l2_hdr_offset + vnet_buffer (b1)->l2.l2_len;
366           b0->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
367           b1->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
368
369           is_l20 = pipe0->subint.flags & SUBINT_CONFIG_L2;
370           is_l21 = pipe1->subint.flags & SUBINT_CONFIG_L2;
371           pipe_determine_next_node (&ethernet_main, is_l20, type0, b0,
372                                     &next0);
373           pipe_determine_next_node (&ethernet_main, is_l21, type1, b1,
374                                     &next1);
375
376           if (!is_l20)
377             vlib_buffer_advance (b0, sizeof (ethernet_header_t));
378           else
379             {
380               u32 eth_start = vnet_buffer (b0)->l2_hdr_offset;
381               vnet_buffer (b0)->l2.l2_len = b0->current_data - eth_start;
382             }
383           if (!is_l21)
384             vlib_buffer_advance (b1, sizeof (ethernet_header_t));
385           else
386             {
387               u32 eth_start = vnet_buffer (b1)->l2_hdr_offset;
388               vnet_buffer (b1)->l2.l2_len = b1->current_data - eth_start;
389             }
390
391           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
392                                            to_next, n_left_to_next,
393                                            bi0, bi1, next0, next0);
394         }
395       while (n_left_from > 0 && n_left_to_next > 0)
396         {
397           u32 bi0, sw_if_index0;
398           vlib_buffer_t *b0;
399           pipe_rx_next_t next0;
400           ethernet_header_t *e0;
401           pipe_t *pipe0;
402           u16 type0;
403           u8 is_l20;
404
405           bi0 = from[0];
406           to_next[0] = bi0;
407           from += 1;
408           to_next += 1;
409           n_left_from -= 1;
410           n_left_to_next -= 1;
411
412           b0 = vlib_get_buffer (vm, bi0);
413
414           e0 = vlib_buffer_get_current (b0);
415           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
416           type0 = clib_net_to_host_u16 (e0->type);
417           pipe0 = &pipe_main.pipes[sw_if_index0];
418
419           vnet_buffer (b0)->l3_hdr_offset =
420             vnet_buffer (b0)->l2_hdr_offset + vnet_buffer (b0)->l2.l2_len;
421           b0->flags |= VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
422
423           is_l20 = pipe0->subint.flags & SUBINT_CONFIG_L2;
424           pipe_determine_next_node (&ethernet_main, is_l20, type0, b0,
425                                     &next0);
426
427           if (!is_l20)
428             vlib_buffer_advance (b0, sizeof (ethernet_header_t));
429           else
430             {
431               u32 eth_start = vnet_buffer (b0)->l2_hdr_offset;
432               vnet_buffer (b0)->l2.l2_len = b0->current_data - eth_start;
433             }
434
435           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
436                                            to_next, n_left_to_next,
437                                            bi0, next0);
438         }
439
440       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
441     }
442
443   return from_frame->n_vectors;
444 }
445
446 /* *INDENT-OFF* */
447 VLIB_REGISTER_NODE (pipe_rx_node) = {
448   .function = pipe_rx,
449   .name = "pipe-rx",
450   /* Takes a vector of packets. */
451   .vector_size = sizeof (u32),
452   .format_trace = format_pipe_rx_trace,
453
454   .sibling_of = "ethernet-input",
455 };
456 /* *INDENT-ON* */
457
458 /*
459  * Maintain a bitmap of allocated pipe instance numbers.
460  */
461 #define PIPE_MAX_INSTANCE               (16 * 1024)
462
463 static u32
464 pipe_instance_alloc (u8 is_specified, u32 want)
465 {
466   /*
467    * Check for dynamically allocaetd instance number.
468    */
469   if (!is_specified)
470     {
471       u32 bit;
472
473       bit = clib_bitmap_first_clear (pipe_main.instances);
474       if (bit >= PIPE_MAX_INSTANCE)
475         {
476           return ~0;
477         }
478       pipe_main.instances = clib_bitmap_set (pipe_main.instances, bit, 1);
479       return bit;
480     }
481
482   /*
483    * In range?
484    */
485   if (want >= PIPE_MAX_INSTANCE)
486     {
487       return ~0;
488     }
489
490   /*
491    * Already in use?
492    */
493   if (clib_bitmap_get (pipe_main.instances, want))
494     {
495       return ~0;
496     }
497
498   /*
499    * Grant allocation request.
500    */
501   pipe_main.instances = clib_bitmap_set (pipe_main.instances, want, 1);
502
503   return want;
504 }
505
506 static int
507 pipe_instance_free (u32 instance)
508 {
509   if (instance >= PIPE_MAX_INSTANCE)
510     {
511       return -1;
512     }
513
514   if (clib_bitmap_get (pipe_main.instances, instance) == 0)
515     {
516       return -1;
517     }
518
519   pipe_main.instances = clib_bitmap_set (pipe_main.instances, instance, 0);
520   return 0;
521 }
522
523 static clib_error_t *
524 pipe_create_sub_interface (vnet_hw_interface_t * hi,
525                            uint32_t sub_id, u32 * sw_if_index)
526 {
527   vnet_sw_interface_t template;
528
529   memset (&template, 0, sizeof (template));
530   template.type = VNET_SW_INTERFACE_TYPE_PIPE;
531   template.flood_class = VNET_FLOOD_CLASS_NORMAL;
532   template.sup_sw_if_index = hi->sw_if_index;
533   template.sub.id = sub_id;
534
535   return (vnet_create_sw_interface (vnet_get_main (),
536                                     &template, sw_if_index));
537 }
538
539 int
540 vnet_create_pipe_interface (u8 is_specified,
541                             u32 user_instance,
542                             u32 * parent_sw_if_index, u32 pipe_sw_if_index[2])
543 {
544   vnet_main_t *vnm = vnet_get_main ();
545   vlib_main_t *vm = vlib_get_main ();
546   uint8_t address[6] = {
547     [0] = 0x22,
548     [1] = 0x22,
549   };
550   vnet_hw_interface_t *hi;
551   clib_error_t *error;
552   u32 hw_if_index;
553   u32 instance;
554   u32 slot;
555   int rv = 0;
556
557   ASSERT (parent_sw_if_index);
558
559   memset (address, 0, sizeof (address));
560
561   /*
562    * Allocate a pipe instance.  Either select one dynamically
563    * or try to use the desired user_instance number.
564    */
565   instance = pipe_instance_alloc (is_specified, user_instance);
566   if (instance == ~0)
567     {
568       return VNET_API_ERROR_INVALID_REGISTRATION;
569     }
570
571   /*
572    * Default MAC address (0000:0000:0000 + instance) is allocated
573    */
574   address[5] = instance;
575
576   error = ethernet_register_interface (vnm, pipe_device_class.index,
577                                        instance, address, &hw_if_index,
578                                        /* flag change */ 0);
579
580   if (error)
581     {
582       rv = VNET_API_ERROR_INVALID_REGISTRATION;
583       goto oops;
584     }
585
586   hi = vnet_get_hw_interface (vnm, hw_if_index);
587   *parent_sw_if_index = hi->sw_if_index;
588   slot = vlib_node_add_named_next_with_slot (vm, hi->tx_node_index,
589                                              "pipe-rx",
590                                              VNET_PIPE_TX_NEXT_ETHERNET_INPUT);
591   ASSERT (slot == VNET_PIPE_TX_NEXT_ETHERNET_INPUT);
592
593   /*
594    * create two sub-interfaces, one for each end of the pipe.
595    */
596   error = pipe_create_sub_interface (hi, 0, &pipe_sw_if_index[0]);
597
598   if (error)
599     goto oops;
600
601   error = pipe_create_sub_interface (hi, 1, &pipe_sw_if_index[1]);
602
603   if (error)
604     goto oops;
605
606   hash_set (hi->sub_interface_sw_if_index_by_id, 0, pipe_sw_if_index[0]);
607   hash_set (hi->sub_interface_sw_if_index_by_id, 1, pipe_sw_if_index[1]);
608
609   vec_validate_init_empty (pipe_main.pipes, pipe_sw_if_index[0],
610                            PIPE_INVALID);
611   vec_validate_init_empty (pipe_main.pipes, pipe_sw_if_index[1],
612                            PIPE_INVALID);
613
614   pipe_main.pipes[pipe_sw_if_index[0]].sw_if_index = pipe_sw_if_index[1];
615   pipe_main.pipes[pipe_sw_if_index[1]].sw_if_index = pipe_sw_if_index[0];
616
617   return 0;
618
619 oops:
620   clib_error_report (error);
621   return rv;
622 }
623
624 typedef struct pipe_hw_walk_ctx_t_
625 {
626   pipe_cb_fn_t cb;
627   void *ctx;
628 } pipe_hw_walk_ctx_t;
629
630 static walk_rc_t
631 pipe_hw_walk (vnet_main_t * vnm, u32 hw_if_index, void *args)
632 {
633   vnet_hw_interface_t *hi;
634   pipe_hw_walk_ctx_t *ctx;
635
636   ctx = args;
637   hi = vnet_get_hw_interface (vnm, hw_if_index);
638
639   if (hi->dev_class_index == pipe_device_class.index)
640     {
641       u32 pipe_sw_if_index[2], id, sw_if_index;
642
643       /* *INDENT-OFF* */
644       hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
645       ({
646         ASSERT(id < 2);
647         pipe_sw_if_index[id] = sw_if_index;
648       }));
649       /* *INDENT-ON* */
650
651       ctx->cb (hi->sw_if_index, pipe_sw_if_index, hi->dev_instance, ctx->ctx);
652     }
653
654   return (WALK_CONTINUE);
655 }
656
657 void
658 pipe_walk (pipe_cb_fn_t fn, void *ctx)
659 {
660   pipe_hw_walk_ctx_t wctx = {
661     .cb = fn,
662     .ctx = ctx,
663   };
664
665   ASSERT (fn);
666
667   vnet_hw_interface_walk (vnet_get_main (), pipe_hw_walk, &wctx);
668 }
669
670 static clib_error_t *
671 create_pipe_interfaces (vlib_main_t * vm,
672                         unformat_input_t * input, vlib_cli_command_t * cmd)
673 {
674   int rv;
675   u32 sw_if_index;
676   u32 pipe_sw_if_index[2];
677   u8 is_specified = 0;
678   u32 user_instance = 0;
679
680   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
681     {
682       if (unformat (input, "instance %d", &user_instance))
683         is_specified = 1;
684       else
685         break;
686     }
687
688   rv = vnet_create_pipe_interface (is_specified, user_instance,
689                                    &sw_if_index, pipe_sw_if_index);
690
691   if (rv)
692     return clib_error_return (0, "vnet_create_pipe_interface failed");
693
694   vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
695                    vnet_get_main (), sw_if_index);
696   return 0;
697 }
698
699 /*?
700  * Create a pipe interface.
701  *
702  * @cliexpar
703  * The following two command syntaxes are equivalent:
704  * @cliexcmd{pipe create-interface [mac <mac-addr>] [instance <instance>]}
705  * Example of how to create a pipe interface:
706  * @cliexcmd{pipe create}
707  ?*/
708 /* *INDENT-OFF* */
709 VLIB_CLI_COMMAND (pipe_create_interface_command, static) = {
710   .path = "pipe create",
711   .short_help = "pipe create [instance <instance>]",
712   .function = create_pipe_interfaces,
713 };
714 /* *INDENT-ON* */
715
716 int
717 vnet_delete_pipe_interface (u32 sw_if_index)
718 {
719   vnet_main_t *vnm = vnet_get_main ();
720   vnet_sw_interface_t *si;
721   vnet_hw_interface_t *hi;
722   u32 instance, id;
723   u32 hw_if_index;
724
725   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
726     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
727
728   si = vnet_get_sw_interface (vnm, sw_if_index);
729   hw_if_index = si->hw_if_index;
730   hi = vnet_get_hw_interface (vnm, hw_if_index);
731   instance = hi->dev_instance;
732
733   if (pipe_instance_free (instance) < 0)
734     {
735       return VNET_API_ERROR_INVALID_SW_IF_INDEX;
736     }
737
738   /* *INDENT-OFF* */
739   hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
740   ({
741     vnet_delete_sub_interface(sw_if_index);
742     pipe_main.pipes[sw_if_index] = PIPE_INVALID;
743   }));
744   /* *INDENT-ON* */
745
746   ethernet_delete_interface (vnm, hw_if_index);
747
748   return 0;
749 }
750
751 static clib_error_t *
752 delete_pipe_interfaces (vlib_main_t * vm,
753                         unformat_input_t * input, vlib_cli_command_t * cmd)
754 {
755   vnet_main_t *vnm = vnet_get_main ();
756   u32 sw_if_index = ~0;
757   int rv;
758
759   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
760     {
761       if (unformat (input, "%U",
762                     unformat_vnet_sw_interface, vnm, &sw_if_index))
763         ;
764       else
765         break;
766     }
767
768   if (sw_if_index == ~0)
769     return clib_error_return (0, "interface not specified");
770
771   rv = vnet_delete_pipe_interface (sw_if_index);
772
773   if (rv)
774     return clib_error_return (0, "vnet_delete_pipe_interface failed");
775
776   return 0;
777 }
778
779 /*?
780  * Delete a pipe interface.
781  *
782  * @cliexpar
783  * The following two command syntaxes are equivalent:
784  * @cliexcmd{pipe delete intfc <interface>}
785  * Example of how to delete a pipe interface:
786  * @cliexcmd{pipe delete-interface intfc loop0}
787  ?*/
788 /* *INDENT-OFF* */
789 VLIB_CLI_COMMAND (pipe_delete_interface_command, static) = {
790   .path = "pipe delete",
791   .short_help = "pipe delete <interface>",
792   .function = delete_pipe_interfaces,
793 };
794 /* *INDENT-ON* */
795
796 /*
797  * fd.io coding-style-patch-verification: ON
798  *
799  * Local Variables:
800  * eval: (c-set-style "gnu")
801  * End:
802  */