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