docs: devices-- add FEATURES.yaml
[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 vpp 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' level 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 against
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) rewrites. However, 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, n_pkts = 0, n_bytes = 0;
135   vlib_buffer_t *b;
136   pipe_t *pipe;
137
138   n_left_from = frame->n_vectors;
139   from = vlib_frame_vector_args (frame);
140
141   while (n_left_from > 0)
142     {
143       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
144
145       n_copy = clib_min (n_left_from, n_left_to_next);
146
147       clib_memcpy_fast (to_next, from, n_copy * sizeof (from[0]));
148       n_left_to_next -= n_copy;
149       n_left_from -= n_copy;
150       i = 0;
151       while (i < n_copy)
152         {
153           b = vlib_get_buffer (vm, from[i]);
154           sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX];
155
156           pipe = &pipe_main.pipes[sw_if_index];
157           // Set up RX index to be recv'd by the other end of the pipe
158           vnet_buffer (b)->sw_if_index[VLIB_RX] = pipe->sw_if_index;
159           vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0;
160
161           i++;
162           n_pkts++;
163           n_bytes += vlib_buffer_length_in_chain (vm, b);
164         }
165       from += n_copy;
166
167       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
168     }
169
170   return n_left_from;
171 }
172
173 static u8 *
174 format_pipe_name (u8 * s, va_list * args)
175 {
176   u32 dev_instance = va_arg (*args, u32);
177   return format (s, "pipe%d", dev_instance);
178 }
179
180 static clib_error_t *
181 pipe_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
182 {
183   vnet_hw_interface_t *hi;
184   u32 id, sw_if_index;
185
186   u32 hw_flags = ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
187                   VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
188   vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
189
190   /* *INDENT-OFF* */
191   hi = vnet_get_hw_interface (vnm, hw_if_index);
192   hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
193   ({
194     vnet_sw_interface_set_flags (vnm, sw_if_index, flags);
195   }));
196   /* *INDENT-ON* */
197
198   return (NULL);
199 }
200
201 /* *INDENT-OFF* */
202 VNET_DEVICE_CLASS (pipe_device_class) = {
203   .name = "Pipe",
204   .format_device_name = format_pipe_name,
205   .tx_function = pipe_tx,
206   .admin_up_down_function = pipe_admin_up_down,
207 };
208 /* *INDENT-ON* */
209
210 #define foreach_pipe_rx_next                    \
211   _ (DROP, "error-drop")
212
213 typedef enum pipe_rx_next_t_
214 {
215 #define _(s,n) PIPE_RX_NEXT_##s,
216   foreach_pipe_rx_next
217 #undef _
218     PIPE_RX_N_NEXT,
219 } pipe_rx_next_t;
220
221 typedef struct pipe_rx_trace_t_
222 {
223   u8 packet_data[32];
224 } pipe_rx_trace_t;
225
226 static u8 *
227 format_pipe_rx_trace (u8 * s, va_list * va)
228 {
229   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
230   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
231   pipe_rx_trace_t *t = va_arg (*va, pipe_rx_trace_t *);
232
233   s = format (s, "%U", format_ethernet_header, t->packet_data);
234
235   return s;
236 }
237
238 /*
239  * The pipe-rx node is a sibling of ethernet-input so steal it's
240  * next node mechanism
241  */
242 static_always_inline void
243 pipe_determine_next_node (ethernet_main_t * em,
244                           u32 is_l20,
245                           u32 type0,
246                           vlib_buffer_t * b0, pipe_rx_next_t * next0)
247 {
248   if (is_l20)
249     {
250       *next0 = em->l2_next;
251     }
252   else if (type0 == ETHERNET_TYPE_IP4)
253     {
254       *next0 = em->l3_next.input_next_ip4;
255     }
256   else if (type0 == ETHERNET_TYPE_IP6)
257     {
258       *next0 = em->l3_next.input_next_ip6;
259     }
260   else if (type0 == ETHERNET_TYPE_MPLS)
261     {
262       *next0 = em->l3_next.input_next_mpls;
263
264     }
265   else if (em->redirect_l3)
266     {
267       // L3 Redirect is on, the cached common next nodes will be
268       // pointing to the redirect node, catch the uncommon types here
269       *next0 = em->redirect_l3_next;
270     }
271   else
272     {
273       // uncommon ethertype, check table
274       u32 i0;
275       i0 = sparse_vec_index (em->l3_next.input_next_by_type, type0);
276       *next0 = vec_elt (em->l3_next.input_next_by_type, i0);
277
278       // The table is not populated with LLC values, so check that now.
279       if (type0 < 0x600)
280         {
281           *next0 = PIPE_RX_NEXT_DROP;
282         }
283     }
284 }
285
286 static_always_inline uword
287 pipe_rx (vlib_main_t * vm,
288          vlib_node_runtime_t * node, vlib_frame_t * from_frame)
289 {
290   u32 n_left_from, next_index, *from, *to_next;
291   u32 n_left_to_next;
292
293   from = vlib_frame_vector_args (from_frame);
294   n_left_from = from_frame->n_vectors;
295
296   if (node->flags & VLIB_NODE_FLAG_TRACE)
297     vlib_trace_frame_buffers_only (vm, node,
298                                    from,
299                                    n_left_from,
300                                    sizeof (from[0]),
301                                    sizeof (pipe_rx_trace_t));
302
303   next_index = node->cached_next_index;
304
305   while (n_left_from > 0)
306     {
307       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
308
309       while (n_left_from >= 4 && n_left_to_next >= 2)
310         {
311           u32 bi0, sw_if_index0, bi1, sw_if_index1;
312           pipe_rx_next_t next0, next1;
313           ethernet_header_t *e0, *e1;
314           vlib_buffer_t *b0, *b1;
315           pipe_t *pipe0, *pipe1;
316           u8 is_l20, is_l21;
317           u16 type0, type1;
318
319           // Prefetch next iteration
320           {
321             vlib_buffer_t *p2, *p3;
322
323             p2 = vlib_get_buffer (vm, from[2]);
324             p3 = vlib_get_buffer (vm, from[3]);
325             vlib_prefetch_buffer_header (p2, STORE);
326             vlib_prefetch_buffer_header (p3, STORE);
327             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD);
328             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, LOAD);
329           }
330
331           bi0 = from[0];
332           to_next[0] = bi0;
333           bi1 = from[1];
334           to_next[1] = bi1;
335           from += 2;
336           to_next += 2;
337           n_left_from -= 2;
338           n_left_to_next -= 2;
339
340           b0 = vlib_get_buffer (vm, bi0);
341           b1 = vlib_get_buffer (vm, bi1);
342
343           e0 = vlib_buffer_get_current (b0);
344           e1 = vlib_buffer_get_current (b1);
345           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
346           sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
347           type0 = clib_net_to_host_u16 (e0->type);
348           type1 = clib_net_to_host_u16 (e1->type);
349           pipe0 = &pipe_main.pipes[sw_if_index0];
350           pipe1 = &pipe_main.pipes[sw_if_index1];
351
352           vnet_buffer (b0)->l2_hdr_offset = b0->current_data;
353           vnet_buffer (b1)->l2_hdr_offset = b1->current_data;
354
355           vnet_buffer (b0)->l3_hdr_offset =
356             vnet_buffer (b0)->l2_hdr_offset + sizeof (ethernet_header_t);
357           vnet_buffer (b1)->l3_hdr_offset =
358             vnet_buffer (b1)->l2_hdr_offset + sizeof (ethernet_header_t);
359           b0->flags |=
360             VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
361             VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
362           b1->flags |=
363             VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
364             VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
365
366           is_l20 = pipe0->subint.flags & SUBINT_CONFIG_L2;
367           is_l21 = pipe1->subint.flags & SUBINT_CONFIG_L2;
368
369           /*
370            * from discussion with Neale - we do not support the tagged traffic.
371            * So assume a simple ethernet header
372            */
373           vnet_buffer (b0)->l2.l2_len = sizeof (ethernet_header_t);
374           vnet_buffer (b1)->l2.l2_len = sizeof (ethernet_header_t);
375           vlib_buffer_advance (b0, is_l20 ? 0 : sizeof (ethernet_header_t));
376           vlib_buffer_advance (b1, is_l21 ? 0 : sizeof (ethernet_header_t));
377
378           pipe_determine_next_node (&ethernet_main, is_l20, type0, b0,
379                                     &next0);
380           pipe_determine_next_node (&ethernet_main, is_l21, type1, b1,
381                                     &next1);
382
383           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
384                                            to_next, n_left_to_next,
385                                            bi0, bi1, next0, next1);
386         }
387       while (n_left_from > 0 && n_left_to_next > 0)
388         {
389           u32 bi0, sw_if_index0;
390           vlib_buffer_t *b0;
391           pipe_rx_next_t next0;
392           ethernet_header_t *e0;
393           pipe_t *pipe0;
394           u16 type0;
395           u8 is_l20;
396
397           bi0 = from[0];
398           to_next[0] = bi0;
399           from += 1;
400           to_next += 1;
401           n_left_from -= 1;
402           n_left_to_next -= 1;
403
404           b0 = vlib_get_buffer (vm, bi0);
405
406           e0 = vlib_buffer_get_current (b0);
407           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
408           type0 = clib_net_to_host_u16 (e0->type);
409           pipe0 = &pipe_main.pipes[sw_if_index0];
410
411           vnet_buffer (b0)->l2_hdr_offset = b0->current_data;
412           vnet_buffer (b0)->l3_hdr_offset =
413             vnet_buffer (b0)->l2_hdr_offset + sizeof (ethernet_header_t);
414           b0->flags |=
415             VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
416             VNET_BUFFER_F_L3_HDR_OFFSET_VALID;
417
418           is_l20 = pipe0->subint.flags & SUBINT_CONFIG_L2;
419
420           vnet_buffer (b0)->l2.l2_len = sizeof (ethernet_header_t);
421           vlib_buffer_advance (b0, is_l20 ? 0 : sizeof (ethernet_header_t));
422
423           pipe_determine_next_node (&ethernet_main, is_l20, type0, b0,
424                                     &next0);
425
426           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
427                                            to_next, n_left_to_next,
428                                            bi0, next0);
429         }
430
431       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
432     }
433
434   return from_frame->n_vectors;
435 }
436
437 /* *INDENT-OFF* */
438 VLIB_REGISTER_NODE (pipe_rx_node) = {
439   .function = pipe_rx,
440   .name = "pipe-rx",
441   /* Takes a vector of packets. */
442   .vector_size = sizeof (u32),
443   .format_trace = format_pipe_rx_trace,
444
445   .sibling_of = "ethernet-input",
446 };
447 /* *INDENT-ON* */
448
449 /*
450  * Maintain a bitmap of allocated pipe instance numbers.
451  */
452 #define PIPE_MAX_INSTANCE               (16 * 1024)
453
454 static u32
455 pipe_instance_alloc (u8 is_specified, u32 want)
456 {
457   /*
458    * Check for dynamically allocaetd instance number.
459    */
460   if (!is_specified)
461     {
462       u32 bit;
463
464       bit = clib_bitmap_first_clear (pipe_main.instances);
465       if (bit >= PIPE_MAX_INSTANCE)
466         {
467           return ~0;
468         }
469       pipe_main.instances = clib_bitmap_set (pipe_main.instances, bit, 1);
470       return bit;
471     }
472
473   /*
474    * In range?
475    */
476   if (want >= PIPE_MAX_INSTANCE)
477     {
478       return ~0;
479     }
480
481   /*
482    * Already in use?
483    */
484   if (clib_bitmap_get (pipe_main.instances, want))
485     {
486       return ~0;
487     }
488
489   /*
490    * Grant allocation request.
491    */
492   pipe_main.instances = clib_bitmap_set (pipe_main.instances, want, 1);
493
494   return want;
495 }
496
497 static int
498 pipe_instance_free (u32 instance)
499 {
500   if (instance >= PIPE_MAX_INSTANCE)
501     {
502       return -1;
503     }
504
505   if (clib_bitmap_get (pipe_main.instances, instance) == 0)
506     {
507       return -1;
508     }
509
510   pipe_main.instances = clib_bitmap_set (pipe_main.instances, instance, 0);
511   return 0;
512 }
513
514 static clib_error_t *
515 pipe_create_sub_interface (vnet_hw_interface_t * hi,
516                            u32 sub_id, u32 * sw_if_index)
517 {
518   vnet_sw_interface_t template;
519
520   clib_memset (&template, 0, sizeof (template));
521   template.type = VNET_SW_INTERFACE_TYPE_PIPE;
522   template.flood_class = VNET_FLOOD_CLASS_NORMAL;
523   template.sup_sw_if_index = hi->sw_if_index;
524   template.sub.id = sub_id;
525
526   return (vnet_create_sw_interface (vnet_get_main (),
527                                     &template, sw_if_index));
528 }
529
530 int
531 vnet_create_pipe_interface (u8 is_specified,
532                             u32 user_instance,
533                             u32 * parent_sw_if_index, u32 pipe_sw_if_index[2])
534 {
535   vnet_main_t *vnm = vnet_get_main ();
536   vlib_main_t *vm = vlib_get_main ();
537   u8 address[6] = {
538     [0] = 0x22,
539     [1] = 0x22,
540   };
541   vnet_hw_interface_t *hi;
542   clib_error_t *error;
543   u32 hw_if_index;
544   u32 instance;
545   u32 slot;
546   int rv = 0;
547
548   ASSERT (parent_sw_if_index);
549
550   clib_memset (address, 0, sizeof (address));
551
552   /*
553    * Allocate a pipe instance.  Either select one dynamically
554    * or try to use the desired user_instance number.
555    */
556   instance = pipe_instance_alloc (is_specified, user_instance);
557   if (instance == ~0)
558     {
559       return VNET_API_ERROR_INVALID_REGISTRATION;
560     }
561
562   /*
563    * Default MAC address (0000:0000:0000 + instance) is allocated
564    */
565   address[5] = instance;
566
567   error = ethernet_register_interface (vnm, pipe_device_class.index,
568                                        instance, address, &hw_if_index,
569                                        /* flag change */ 0);
570
571   if (error)
572     {
573       rv = VNET_API_ERROR_INVALID_REGISTRATION;
574       goto oops;
575     }
576
577   hi = vnet_get_hw_interface (vnm, hw_if_index);
578   *parent_sw_if_index = hi->sw_if_index;
579   slot = vlib_node_add_named_next_with_slot (vm, hi->tx_node_index,
580                                              "pipe-rx",
581                                              VNET_PIPE_TX_NEXT_ETHERNET_INPUT);
582   ASSERT (slot == VNET_PIPE_TX_NEXT_ETHERNET_INPUT);
583
584   /*
585    * create two sub-interfaces, one for each end of the pipe.
586    */
587   error = pipe_create_sub_interface (hi, 0, &pipe_sw_if_index[0]);
588
589   if (error)
590     goto oops;
591
592   error = pipe_create_sub_interface (hi, 1, &pipe_sw_if_index[1]);
593
594   if (error)
595     goto oops;
596
597   hash_set (hi->sub_interface_sw_if_index_by_id, 0, pipe_sw_if_index[0]);
598   hash_set (hi->sub_interface_sw_if_index_by_id, 1, pipe_sw_if_index[1]);
599
600   vec_validate_init_empty (pipe_main.pipes, pipe_sw_if_index[0],
601                            PIPE_INVALID);
602   vec_validate_init_empty (pipe_main.pipes, pipe_sw_if_index[1],
603                            PIPE_INVALID);
604
605   pipe_main.pipes[pipe_sw_if_index[0]].sw_if_index = pipe_sw_if_index[1];
606   pipe_main.pipes[pipe_sw_if_index[1]].sw_if_index = pipe_sw_if_index[0];
607
608   return 0;
609
610 oops:
611   clib_error_report (error);
612   return rv;
613 }
614
615 typedef struct pipe_hw_walk_ctx_t_
616 {
617   pipe_cb_fn_t cb;
618   void *ctx;
619 } pipe_hw_walk_ctx_t;
620
621 static walk_rc_t
622 pipe_hw_walk (vnet_main_t * vnm, u32 hw_if_index, void *args)
623 {
624   vnet_hw_interface_t *hi;
625   pipe_hw_walk_ctx_t *ctx;
626
627   ctx = args;
628   hi = vnet_get_hw_interface (vnm, hw_if_index);
629
630   if (hi->dev_class_index == pipe_device_class.index)
631     {
632       u32 pipe_sw_if_index[2], id, sw_if_index;
633
634       /* *INDENT-OFF* */
635       hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
636       ({
637         ASSERT(id < 2);
638         pipe_sw_if_index[id] = sw_if_index;
639       }));
640       /* *INDENT-ON* */
641
642       ctx->cb (hi->sw_if_index, pipe_sw_if_index, hi->dev_instance, ctx->ctx);
643     }
644
645   return (WALK_CONTINUE);
646 }
647
648 void
649 pipe_walk (pipe_cb_fn_t fn, void *ctx)
650 {
651   pipe_hw_walk_ctx_t wctx = {
652     .cb = fn,
653     .ctx = ctx,
654   };
655
656   ASSERT (fn);
657
658   vnet_hw_interface_walk (vnet_get_main (), pipe_hw_walk, &wctx);
659 }
660
661 static clib_error_t *
662 create_pipe_interfaces (vlib_main_t * vm,
663                         unformat_input_t * input, vlib_cli_command_t * cmd)
664 {
665   int rv;
666   u32 sw_if_index;
667   u32 pipe_sw_if_index[2];
668   u8 is_specified = 0;
669   u32 user_instance = 0;
670
671   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
672     {
673       if (unformat (input, "instance %d", &user_instance))
674         is_specified = 1;
675       else
676         break;
677     }
678
679   rv = vnet_create_pipe_interface (is_specified, user_instance,
680                                    &sw_if_index, pipe_sw_if_index);
681
682   if (rv)
683     return clib_error_return (0, "vnet_create_pipe_interface failed");
684
685   vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
686                    vnet_get_main (), sw_if_index);
687   return 0;
688 }
689
690 /*?
691  * Create a pipe interface.
692  *
693  * @cliexpar
694  * The following two command syntaxes are equivalent:
695  * @cliexcmd{pipe create-interface [mac <mac-addr>] [instance <instance>]}
696  * Example of how to create a pipe interface:
697  * @cliexcmd{pipe create}
698  ?*/
699 /* *INDENT-OFF* */
700 VLIB_CLI_COMMAND (pipe_create_interface_command, static) = {
701   .path = "pipe create",
702   .short_help = "pipe create [instance <instance>]",
703   .function = create_pipe_interfaces,
704 };
705 /* *INDENT-ON* */
706
707 int
708 vnet_delete_pipe_interface (u32 sw_if_index)
709 {
710   vnet_main_t *vnm = vnet_get_main ();
711   vnet_sw_interface_t *si;
712   vnet_hw_interface_t *hi;
713   u32 instance, id;
714   u32 hw_if_index;
715
716   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
717     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
718
719   si = vnet_get_sw_interface (vnm, sw_if_index);
720   hw_if_index = si->hw_if_index;
721   hi = vnet_get_hw_interface (vnm, hw_if_index);
722   instance = hi->dev_instance;
723
724   if (pipe_instance_free (instance) < 0)
725     {
726       return VNET_API_ERROR_INVALID_SW_IF_INDEX;
727     }
728
729   /* *INDENT-OFF* */
730   hash_foreach (id, sw_if_index, hi->sub_interface_sw_if_index_by_id,
731   ({
732     vnet_delete_sub_interface(sw_if_index);
733     pipe_main.pipes[sw_if_index] = PIPE_INVALID;
734   }));
735   /* *INDENT-ON* */
736
737   ethernet_delete_interface (vnm, hw_if_index);
738
739   return 0;
740 }
741
742 static clib_error_t *
743 delete_pipe_interfaces (vlib_main_t * vm,
744                         unformat_input_t * input, vlib_cli_command_t * cmd)
745 {
746   vnet_main_t *vnm = vnet_get_main ();
747   u32 sw_if_index = ~0;
748   int rv;
749
750   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
751     {
752       if (unformat (input, "%U",
753                     unformat_vnet_sw_interface, vnm, &sw_if_index))
754         ;
755       else
756         break;
757     }
758
759   if (sw_if_index == ~0)
760     return clib_error_return (0, "interface not specified");
761
762   rv = vnet_delete_pipe_interface (sw_if_index);
763
764   if (rv)
765     return clib_error_return (0, "vnet_delete_pipe_interface failed");
766
767   return 0;
768 }
769
770 /*?
771  * Delete a pipe interface.
772  *
773  * @cliexpar
774  * The following two command syntaxes are equivalent:
775  * @cliexcmd{pipe delete intfc <interface>}
776  * Example of how to delete a pipe interface:
777  * @cliexcmd{pipe delete-interface intfc loop0}
778  ?*/
779 /* *INDENT-OFF* */
780 VLIB_CLI_COMMAND (pipe_delete_interface_command, static) = {
781   .path = "pipe delete",
782   .short_help = "pipe delete <interface>",
783   .function = delete_pipe_interfaces,
784 };
785 /* *INDENT-ON* */
786
787 /*
788  * fd.io coding-style-patch-verification: ON
789  *
790  * Local Variables:
791  * eval: (c-set-style "gnu")
792  * End:
793  */