bonding: add support for numa awareness
[vpp.git] / src / vnet / bonding / cli.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2017 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #include <stdint.h>
19 #include <vlib/vlib.h>
20 #include <vlib/unix/unix.h>
21 #include <vnet/ethernet/ethernet.h>
22 #include <vnet/bonding/node.h>
23
24 void
25 bond_disable_collecting_distributing (vlib_main_t * vm, slave_if_t * sif)
26 {
27   bond_main_t *bm = &bond_main;
28   bond_if_t *bif;
29   int i;
30   uword p;
31   vnet_main_t *vnm = vnet_get_main ();
32   vnet_hw_interface_t *hw;
33   u8 switching_active = 0;
34
35   bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
36   clib_spinlock_lock_if_init (&bif->lockp);
37   vec_foreach_index (i, bif->active_slaves)
38   {
39     p = *vec_elt_at_index (bif->active_slaves, i);
40     if (p == sif->sw_if_index)
41       {
42         if (sif->sw_if_index == bif->sw_if_index_working)
43           {
44             switching_active = 1;
45             if (bif->mode == BOND_MODE_ACTIVE_BACKUP)
46               bif->is_local_numa = 0;
47           }
48         vec_del1 (bif->active_slaves, i);
49         hash_unset (bif->active_slave_by_sw_if_index, sif->sw_if_index);
50         break;
51       }
52   }
53
54   /* We get a new slave just becoming active */
55   if ((bif->mode == BOND_MODE_ACTIVE_BACKUP) && switching_active)
56     {
57       if ((vec_len (bif->active_slaves) >= 1))
58         {
59           /* scan all slaves and try to find the first slave with local numa node. */
60           vec_foreach_index (i, bif->active_slaves)
61           {
62             p = *vec_elt_at_index (bif->active_slaves, i);
63             hw = vnet_get_sup_hw_interface (vnm, p);
64             if (vm->numa_node == hw->numa_node)
65               {
66                 bif->sw_if_index_working = p;
67                 bif->is_local_numa = 1;
68                 vlib_process_signal_event (bm->vlib_main,
69                                            bond_process_node.index,
70                                            BOND_SEND_GARP_NA,
71                                            bif->hw_if_index);
72                 break;
73               }
74           }
75         }
76
77       /* No local numa node is found in the active slave set. Use the first slave */
78       if ((bif->is_local_numa == 0) && (vec_len (bif->active_slaves) >= 1))
79         {
80           p = *vec_elt_at_index (bif->active_slaves, 0);
81           bif->sw_if_index_working = p;
82           vlib_process_signal_event (bm->vlib_main, bond_process_node.index,
83                                      BOND_SEND_GARP_NA, bif->hw_if_index);
84         }
85     }
86   clib_spinlock_unlock_if_init (&bif->lockp);
87
88   return;
89 }
90
91 void
92 bond_enable_collecting_distributing (vlib_main_t * vm, slave_if_t * sif)
93 {
94   bond_if_t *bif;
95   bond_main_t *bm = &bond_main;
96   vnet_main_t *vnm = vnet_get_main ();
97   vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sif->sw_if_index);
98   int i;
99   uword p;
100
101   bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
102   clib_spinlock_lock_if_init (&bif->lockp);
103   if (!hash_get (bif->active_slave_by_sw_if_index, sif->sw_if_index))
104     {
105       hash_set (bif->active_slave_by_sw_if_index, sif->sw_if_index,
106                 sif->sw_if_index);
107       vec_add1 (bif->active_slaves, sif->sw_if_index);
108
109       /* First slave becomes active? */
110       if ((vec_len (bif->active_slaves) == 1) &&
111           (bif->mode == BOND_MODE_ACTIVE_BACKUP))
112         {
113           bif->sw_if_index_working = sif->sw_if_index;
114           bif->is_local_numa = (vm->numa_node == hw->numa_node) ? 1 : 0;
115           vlib_process_signal_event (bm->vlib_main, bond_process_node.index,
116                                      BOND_SEND_GARP_NA, bif->hw_if_index);
117         }
118       else if ((vec_len (bif->active_slaves) > 1)
119                && (bif->mode == BOND_MODE_ACTIVE_BACKUP)
120                && bif->is_local_numa == 0)
121         {
122           if (vm->numa_node == hw->numa_node)
123             {
124               vec_foreach_index (i, bif->active_slaves)
125               {
126                 p = *vec_elt_at_index (bif->active_slaves, 0);
127                 if (p == sif->sw_if_index)
128                   break;
129
130                 vec_del1 (bif->active_slaves, 0);
131                 hash_unset (bif->active_slave_by_sw_if_index, p);
132                 vec_add1 (bif->active_slaves, p);
133                 hash_set (bif->active_slave_by_sw_if_index, p, p);
134               }
135               bif->sw_if_index_working = sif->sw_if_index;
136               bif->is_local_numa = 1;
137               vlib_process_signal_event (bm->vlib_main,
138                                          bond_process_node.index,
139                                          BOND_SEND_GARP_NA, bif->hw_if_index);
140
141             }
142         }
143     }
144   clib_spinlock_unlock_if_init (&bif->lockp);
145
146   return;
147 }
148
149 int
150 bond_dump_ifs (bond_interface_details_t ** out_bondifs)
151 {
152   vnet_main_t *vnm = vnet_get_main ();
153   bond_main_t *bm = &bond_main;
154   bond_if_t *bif;
155   vnet_hw_interface_t *hi;
156   bond_interface_details_t *r_bondifs = NULL;
157   bond_interface_details_t *bondif = NULL;
158
159   /* *INDENT-OFF* */
160   pool_foreach (bif, bm->interfaces,
161     vec_add2(r_bondifs, bondif, 1);
162     clib_memset (bondif, 0, sizeof (*bondif));
163     bondif->id = bif->id;
164     bondif->sw_if_index = bif->sw_if_index;
165     hi = vnet_get_hw_interface (vnm, bif->hw_if_index);
166     clib_memcpy(bondif->interface_name, hi->name,
167                 MIN (ARRAY_LEN (bondif->interface_name) - 1,
168                      strlen ((const char *) hi->name)));
169     bondif->mode = bif->mode;
170     bondif->lb = bif->lb;
171     bondif->active_slaves = vec_len (bif->active_slaves);
172     bondif->slaves = vec_len (bif->slaves);
173   );
174   /* *INDENT-ON* */
175
176   *out_bondifs = r_bondifs;
177
178   return 0;
179 }
180
181 int
182 bond_dump_slave_ifs (slave_interface_details_t ** out_slaveifs,
183                      u32 bond_sw_if_index)
184 {
185   vnet_main_t *vnm = vnet_get_main ();
186   bond_if_t *bif;
187   vnet_hw_interface_t *hi;
188   vnet_sw_interface_t *sw;
189   slave_interface_details_t *r_slaveifs = NULL;
190   slave_interface_details_t *slaveif = NULL;
191   u32 *sw_if_index = NULL;
192   slave_if_t *sif;
193
194   bif = bond_get_master_by_sw_if_index (bond_sw_if_index);
195   if (!bif)
196     return 1;
197
198   vec_foreach (sw_if_index, bif->slaves)
199   {
200     vec_add2 (r_slaveifs, slaveif, 1);
201     clib_memset (slaveif, 0, sizeof (*slaveif));
202     sif = bond_get_slave_by_sw_if_index (*sw_if_index);
203     if (sif)
204       {
205         sw = vnet_get_sw_interface (vnm, sif->sw_if_index);
206         hi = vnet_get_hw_interface (vnm, sw->hw_if_index);
207         clib_memcpy (slaveif->interface_name, hi->name,
208                      MIN (ARRAY_LEN (slaveif->interface_name) - 1,
209                           strlen ((const char *) hi->name)));
210         slaveif->sw_if_index = sif->sw_if_index;
211         slaveif->is_passive = sif->is_passive;
212         slaveif->is_long_timeout = sif->is_long_timeout;
213       }
214   }
215   *out_slaveifs = r_slaveifs;
216
217   return 0;
218 }
219
220 static void
221 bond_delete_neighbor (vlib_main_t * vm, bond_if_t * bif, slave_if_t * sif)
222 {
223   bond_main_t *bm = &bond_main;
224   vnet_main_t *vnm = vnet_get_main ();
225   int i;
226   vnet_hw_interface_t *sif_hw;
227
228   sif_hw = vnet_get_sup_hw_interface (vnm, sif->sw_if_index);
229
230   bif->port_number_bitmap =
231     clib_bitmap_set (bif->port_number_bitmap,
232                      ntohs (sif->actor_admin.port_number) - 1, 0);
233   bm->slave_by_sw_if_index[sif->sw_if_index] = 0;
234   vec_free (sif->last_marker_pkt);
235   vec_free (sif->last_rx_pkt);
236   vec_foreach_index (i, bif->slaves)
237   {
238     uword p = *vec_elt_at_index (bif->slaves, i);
239     if (p == sif->sw_if_index)
240       {
241         vec_del1 (bif->slaves, i);
242         break;
243       }
244   }
245
246   bond_disable_collecting_distributing (vm, sif);
247
248   vnet_feature_enable_disable ("device-input", "bond-input",
249                                sif_hw->hw_if_index, 0, 0, 0);
250
251   /* Put back the old mac */
252   vnet_hw_interface_change_mac_address (vnm, sif_hw->hw_if_index,
253                                         sif->persistent_hw_address);
254
255   if ((bif->mode == BOND_MODE_LACP) && bm->lacp_enable_disable)
256     (*bm->lacp_enable_disable) (vm, bif, sif, 0);
257
258   pool_put (bm->neighbors, sif);
259 }
260
261 int
262 bond_delete_if (vlib_main_t * vm, u32 sw_if_index)
263 {
264   bond_main_t *bm = &bond_main;
265   vnet_main_t *vnm = vnet_get_main ();
266   bond_if_t *bif;
267   slave_if_t *sif;
268   vnet_hw_interface_t *hw;
269   u32 *sif_sw_if_index;
270   u32 **s_list = 0;
271   u32 i;
272
273   hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
274   if (hw == NULL || bond_dev_class.index != hw->dev_class_index)
275     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
276
277   bif = bond_get_master_by_dev_instance (hw->dev_instance);
278
279   vec_foreach (sif_sw_if_index, bif->slaves)
280   {
281     vec_add1 (s_list, sif_sw_if_index);
282   }
283
284   for (i = 0; i < vec_len (s_list); i++)
285     {
286       sif_sw_if_index = s_list[i];
287       sif = bond_get_slave_by_sw_if_index (*sif_sw_if_index);
288       if (sif)
289         bond_delete_neighbor (vm, bif, sif);
290     }
291
292   if (s_list)
293     vec_free (s_list);
294
295   /* bring down the interface */
296   vnet_hw_interface_set_flags (vnm, bif->hw_if_index, 0);
297   vnet_sw_interface_set_flags (vnm, bif->sw_if_index, 0);
298
299   ethernet_delete_interface (vnm, bif->hw_if_index);
300
301   clib_bitmap_free (bif->port_number_bitmap);
302   hash_unset (bm->bond_by_sw_if_index, bif->sw_if_index);
303   hash_unset (bm->id_used, bif->id);
304   clib_memset (bif, 0, sizeof (*bif));
305   pool_put (bm->interfaces, bif);
306
307   return 0;
308 }
309
310 void
311 bond_create_if (vlib_main_t * vm, bond_create_if_args_t * args)
312 {
313   bond_main_t *bm = &bond_main;
314   vnet_main_t *vnm = vnet_get_main ();
315   vnet_sw_interface_t *sw;
316   bond_if_t *bif;
317
318   if ((args->mode == BOND_MODE_LACP) && bm->lacp_plugin_loaded == 0)
319     {
320       args->rv = VNET_API_ERROR_FEATURE_DISABLED;
321       args->error = clib_error_return (0, "LACP plugin is not loaded");
322       return;
323     }
324   if (args->mode > BOND_MODE_LACP || args->mode < BOND_MODE_ROUND_ROBIN)
325     {
326       args->rv = VNET_API_ERROR_INVALID_ARGUMENT;
327       args->error = clib_error_return (0, "Invalid mode");
328       return;
329     }
330   if (args->lb > BOND_LB_L23)
331     {
332       args->rv = VNET_API_ERROR_INVALID_ARGUMENT;
333       args->error = clib_error_return (0, "Invalid load-balance");
334       return;
335     }
336   pool_get (bm->interfaces, bif);
337   clib_memset (bif, 0, sizeof (*bif));
338   bif->dev_instance = bif - bm->interfaces;
339   bif->id = args->id;
340   bif->lb = args->lb;
341   bif->mode = args->mode;
342
343   // Adjust requested interface id
344   if (bif->id == ~0)
345     bif->id = bif->dev_instance;
346   if (hash_get (bm->id_used, bif->id))
347     {
348       args->rv = VNET_API_ERROR_INSTANCE_IN_USE;
349       pool_put (bm->interfaces, bif);
350       return;
351     }
352   hash_set (bm->id_used, bif->id, 1);
353
354   // Special load-balance mode used for rr and bc
355   if (bif->mode == BOND_MODE_ROUND_ROBIN)
356     bif->lb = BOND_LB_RR;
357   else if (bif->mode == BOND_MODE_BROADCAST)
358     bif->lb = BOND_LB_BC;
359   else if (bif->mode == BOND_MODE_ACTIVE_BACKUP)
360     bif->lb = BOND_LB_AB;
361
362   bif->use_custom_mac = args->hw_addr_set;
363   if (!args->hw_addr_set)
364     {
365       f64 now = vlib_time_now (vm);
366       u32 rnd;
367       rnd = (u32) (now * 1e6);
368       rnd = random_u32 (&rnd);
369
370       memcpy (args->hw_addr + 2, &rnd, sizeof (rnd));
371       args->hw_addr[0] = 2;
372       args->hw_addr[1] = 0xfe;
373     }
374   memcpy (bif->hw_address, args->hw_addr, 6);
375   args->error = ethernet_register_interface
376     (vnm, bond_dev_class.index, bif->dev_instance /* device instance */ ,
377      bif->hw_address /* ethernet address */ ,
378      &bif->hw_if_index, 0 /* flag change */ );
379
380   if (args->error)
381     {
382       args->rv = VNET_API_ERROR_INVALID_REGISTRATION;
383       hash_unset (bm->id_used, bif->id);
384       pool_put (bm->interfaces, bif);
385       return;
386     }
387
388   sw = vnet_get_hw_sw_interface (vnm, bif->hw_if_index);
389   bif->sw_if_index = sw->sw_if_index;
390   bif->group = bif->sw_if_index;
391   if (vlib_get_thread_main ()->n_vlib_mains > 1)
392     clib_spinlock_init (&bif->lockp);
393
394   vnet_hw_interface_set_flags (vnm, bif->hw_if_index,
395                                VNET_HW_INTERFACE_FLAG_LINK_UP);
396
397   hash_set (bm->bond_by_sw_if_index, bif->sw_if_index, bif->dev_instance);
398
399   // for return
400   args->sw_if_index = bif->sw_if_index;
401 }
402
403 static clib_error_t *
404 bond_create_command_fn (vlib_main_t * vm, unformat_input_t * input,
405                         vlib_cli_command_t * cmd)
406 {
407   unformat_input_t _line_input, *line_input = &_line_input;
408   bond_create_if_args_t args = { 0 };
409   u8 mode_is_set = 0;
410
411   /* Get a line of input. */
412   if (!unformat_user (input, unformat_line_input, line_input))
413     return clib_error_return (0, "Missing required arguments.");
414
415   args.id = ~0;
416   args.mode = -1;
417   args.lb = BOND_LB_L2;
418   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
419     {
420       if (unformat (line_input, "mode %U", unformat_bond_mode, &args.mode))
421         mode_is_set = 1;
422       else if (((args.mode == BOND_MODE_LACP) || (args.mode == BOND_MODE_XOR))
423                && unformat (line_input, "load-balance %U",
424                             unformat_bond_load_balance, &args.lb))
425         ;
426       else if (unformat (line_input, "hw-addr %U",
427                          unformat_ethernet_address, args.hw_addr))
428         args.hw_addr_set = 1;
429       else if (unformat (line_input, "id %u", &args.id))
430         ;
431       else
432         return clib_error_return (0, "unknown input `%U'",
433                                   format_unformat_error, input);
434     }
435   unformat_free (line_input);
436
437   if (mode_is_set == 0)
438     return clib_error_return (0, "Missing bond mode");
439
440   bond_create_if (vm, &args);
441
442   return args.error;
443 }
444
445 /* *INDENT-OFF* */
446 VLIB_CLI_COMMAND (bond_create_command, static) = {
447   .path = "create bond",
448   .short_help = "create bond mode {round-robin | active-backup | broadcast | "
449     "{lacp | xor} [load-balance { l2 | l23 | l34 }]} [hw-addr <mac-address>] "
450     "[id <if-id>]",
451   .function = bond_create_command_fn,
452 };
453 /* *INDENT-ON* */
454
455 static clib_error_t *
456 bond_delete_command_fn (vlib_main_t * vm, unformat_input_t * input,
457                         vlib_cli_command_t * cmd)
458 {
459   unformat_input_t _line_input, *line_input = &_line_input;
460   u32 sw_if_index = ~0;
461   vnet_main_t *vnm = vnet_get_main ();
462   int rv;
463
464   /* Get a line of input. */
465   if (!unformat_user (input, unformat_line_input, line_input))
466     return clib_error_return (0, "Missing <interface>");
467
468   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
469     {
470       if (unformat (line_input, "sw_if_index %d", &sw_if_index))
471         ;
472       else if (unformat (line_input, "%U", unformat_vnet_sw_interface,
473                          vnm, &sw_if_index))
474         ;
475       else
476         return clib_error_return (0, "unknown input `%U'",
477                                   format_unformat_error, input);
478     }
479   unformat_free (line_input);
480
481   if (sw_if_index == ~0)
482     return clib_error_return (0,
483                               "please specify interface name or sw_if_index");
484
485   rv = bond_delete_if (vm, sw_if_index);
486   if (rv == VNET_API_ERROR_INVALID_SW_IF_INDEX)
487     return clib_error_return (0, "not a bond interface");
488   else if (rv != 0)
489     return clib_error_return (0, "error on deleting bond interface");
490
491   return 0;
492 }
493
494 /* *INDENT-OFF* */
495 VLIB_CLI_COMMAND (bond_delete__command, static) =
496 {
497   .path = "delete bond",
498   .short_help = "delete bond {<interface> | sw_if_index <sw_idx>}",
499   .function = bond_delete_command_fn,
500 };
501 /* *INDENT-ON* */
502
503 void
504 bond_enslave (vlib_main_t * vm, bond_enslave_args_t * args)
505 {
506   bond_main_t *bm = &bond_main;
507   vnet_main_t *vnm = vnet_get_main ();
508   bond_if_t *bif;
509   slave_if_t *sif;
510   vnet_interface_main_t *im = &vnm->interface_main;
511   vnet_hw_interface_t *bif_hw, *sif_hw;
512   vnet_sw_interface_t *sw;
513   u32 thread_index;
514   u32 sif_if_index;
515
516   bif = bond_get_master_by_sw_if_index (args->group);
517   if (!bif)
518     {
519       args->rv = VNET_API_ERROR_INVALID_INTERFACE;
520       args->error = clib_error_return (0, "bond interface not found");
521       return;
522     }
523   // make sure the interface is not already enslaved
524   if (bond_get_slave_by_sw_if_index (args->slave))
525     {
526       args->rv = VNET_API_ERROR_VALUE_EXIST;
527       args->error = clib_error_return (0, "interface was already enslaved");
528       return;
529     }
530   sif_hw = vnet_get_sup_hw_interface (vnm, args->slave);
531   if (sif_hw->dev_class_index == bond_dev_class.index)
532     {
533       args->rv = VNET_API_ERROR_INVALID_INTERFACE;
534       args->error =
535         clib_error_return (0, "bond interface cannot be enslaved");
536       return;
537     }
538   pool_get (bm->neighbors, sif);
539   clib_memset (sif, 0, sizeof (*sif));
540   sw = pool_elt_at_index (im->sw_interfaces, args->slave);
541   sif->port_enabled = sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP;
542   sif->sw_if_index = sw->sw_if_index;
543   sif->hw_if_index = sw->hw_if_index;
544   sif->packet_template_index = (u8) ~ 0;
545   sif->is_passive = args->is_passive;
546   sif->group = args->group;
547   sif->bif_dev_instance = bif->dev_instance;
548   sif->mode = bif->mode;
549
550   sif->is_long_timeout = args->is_long_timeout;
551   if (args->is_long_timeout)
552     sif->ttl_in_seconds = LACP_LONG_TIMOUT_TIME;
553   else
554     sif->ttl_in_seconds = LACP_SHORT_TIMOUT_TIME;
555
556   vec_validate_aligned (bm->slave_by_sw_if_index, sif->sw_if_index,
557                         CLIB_CACHE_LINE_BYTES);
558   /*
559    * sif - bm->neighbors may be 0
560    * Left shift it by 1 bit to distinguish the valid entry that we actually
561    * store from the null entries
562    */
563   bm->slave_by_sw_if_index[sif->sw_if_index] =
564     (uword) (((sif - bm->neighbors) << 1) | 1);
565   vec_add1 (bif->slaves, sif->sw_if_index);
566
567   sif_hw = vnet_get_sup_hw_interface (vnm, sif->sw_if_index);
568
569   /* Save the old mac */
570   memcpy (sif->persistent_hw_address, sif_hw->hw_address, 6);
571   bif_hw = vnet_get_sup_hw_interface (vnm, bif->sw_if_index);
572   if (bif->use_custom_mac)
573     {
574       vnet_hw_interface_change_mac_address (vnm, sif_hw->hw_if_index,
575                                             bif->hw_address);
576     }
577   else
578     {
579       // bond interface gets the mac address from the first slave
580       if (vec_len (bif->slaves) == 1)
581         {
582           memcpy (bif->hw_address, sif_hw->hw_address, 6);
583           vnet_hw_interface_change_mac_address (vnm, bif_hw->hw_if_index,
584                                                 sif_hw->hw_address);
585         }
586       else
587         {
588           // subsequent slaves gets the mac address of the bond interface
589           vnet_hw_interface_change_mac_address (vnm, sif_hw->hw_if_index,
590                                                 bif->hw_address);
591         }
592     }
593
594   if (bif_hw->l2_if_count)
595     {
596       ethernet_set_flags (vnm, sif_hw->hw_if_index,
597                           ETHERNET_INTERFACE_FLAG_ACCEPT_ALL);
598       /* ensure all packets go to ethernet-input */
599       ethernet_set_rx_redirect (vnm, sif_hw, 1);
600     }
601
602   if (bif->mode == BOND_MODE_LACP)
603     {
604       if (bm->lacp_enable_disable)
605         (*bm->lacp_enable_disable) (vm, bif, sif, 1);
606     }
607   else if (sif->port_enabled &&
608            (sif_hw->flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
609     {
610       bond_enable_collecting_distributing (vm, sif);
611     }
612
613   vec_foreach_index (thread_index, bm->per_thread_data)
614   {
615     bond_per_thread_data_t *ptd = vec_elt_at_index (bm->per_thread_data,
616                                                     thread_index);
617
618     vec_validate_aligned (ptd->per_port_queue, vec_len (bif->slaves) - 1,
619                           CLIB_CACHE_LINE_BYTES);
620
621     vec_foreach_index (sif_if_index, ptd->per_port_queue)
622     {
623       ptd->per_port_queue[sif_if_index].n_buffers = 0;
624     }
625   }
626
627   args->rv = vnet_feature_enable_disable ("device-input", "bond-input",
628                                           sif_hw->hw_if_index, 1, 0, 0);
629
630   if (args->rv)
631     {
632       args->error =
633         clib_error_return (0,
634                            "Error encountered on input feature arc enable");
635     }
636 }
637
638 static clib_error_t *
639 enslave_interface_command_fn (vlib_main_t * vm, unformat_input_t * input,
640                               vlib_cli_command_t * cmd)
641 {
642   bond_enslave_args_t args = { 0 };
643   unformat_input_t _line_input, *line_input = &_line_input;
644   vnet_main_t *vnm = vnet_get_main ();
645
646   /* Get a line of input. */
647   if (!unformat_user (input, unformat_line_input, line_input))
648     return clib_error_return (0, "Missing required arguments.");
649
650   args.slave = ~0;
651   args.group = ~0;
652   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
653     {
654       if (unformat (line_input, "%U %U",
655                     unformat_vnet_sw_interface, vnm, &args.group,
656                     unformat_vnet_sw_interface, vnm, &args.slave))
657         ;
658       else if (unformat (line_input, "passive"))
659         args.is_passive = 1;
660       else if (unformat (line_input, "long-timeout"))
661         args.is_long_timeout = 1;
662       else
663         {
664           args.error = clib_error_return (0, "unknown input `%U'",
665                                           format_unformat_error, input);
666           break;
667         }
668     }
669   unformat_free (line_input);
670
671   if (args.error)
672     return args.error;
673   if (args.group == ~0)
674     return clib_error_return (0, "Missing bond interface");
675   if (args.slave == ~0)
676     return clib_error_return (0, "please specify valid slave interface name");
677
678   bond_enslave (vm, &args);
679
680   return args.error;
681 }
682
683 /* *INDENT-OFF* */
684 VLIB_CLI_COMMAND (enslave_interface_command, static) = {
685   .path = "bond add",
686   .short_help = "bond add <BondEthernetx> <slave-interface> "
687                 "[passive] [long-timeout]",
688   .function = enslave_interface_command_fn,
689 };
690 /* *INDENT-ON* */
691
692 void
693 bond_detach_slave (vlib_main_t * vm, bond_detach_slave_args_t * args)
694 {
695   bond_if_t *bif;
696   slave_if_t *sif;
697
698   sif = bond_get_slave_by_sw_if_index (args->slave);
699   if (!sif)
700     {
701       args->rv = VNET_API_ERROR_INVALID_INTERFACE;
702       args->error = clib_error_return (0, "interface was not enslaved");
703       return;
704     }
705   bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
706   bond_delete_neighbor (vm, bif, sif);
707 }
708
709 static clib_error_t *
710 detach_interface_command_fn (vlib_main_t * vm, unformat_input_t * input,
711                              vlib_cli_command_t * cmd)
712 {
713   bond_detach_slave_args_t args = { 0 };
714   unformat_input_t _line_input, *line_input = &_line_input;
715   vnet_main_t *vnm = vnet_get_main ();
716
717   /* Get a line of input. */
718   if (!unformat_user (input, unformat_line_input, line_input))
719     return clib_error_return (0, "Missing required arguments.");
720
721   args.slave = ~0;
722   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
723     {
724       if (unformat (line_input, "%U",
725                     unformat_vnet_sw_interface, vnm, &args.slave))
726         ;
727       else
728         {
729           args.error = clib_error_return (0, "unknown input `%U'",
730                                           format_unformat_error, input);
731           break;
732         }
733     }
734   unformat_free (line_input);
735
736   if (args.error)
737     return args.error;
738   if (args.slave == ~0)
739     return clib_error_return (0, "please specify valid slave interface name");
740
741   bond_detach_slave (vm, &args);
742
743   return args.error;
744 }
745
746 /* *INDENT-OFF* */
747 VLIB_CLI_COMMAND (detach_interface_command, static) = {
748   .path = "bond del",
749   .short_help = "bond del <slave-interface>",
750   .function = detach_interface_command_fn,
751 };
752 /* *INDENT-ON* */
753
754 static void
755 show_bond (vlib_main_t * vm)
756 {
757   bond_main_t *bm = &bond_main;
758   bond_if_t *bif;
759
760   vlib_cli_output (vm, "%-16s %-12s %-13s %-13s %-14s %s",
761                    "interface name", "sw_if_index", "mode",
762                    "load balance", "active slaves", "slaves");
763
764   /* *INDENT-OFF* */
765   pool_foreach (bif, bm->interfaces,
766   ({
767     vlib_cli_output (vm, "%-16U %-12d %-13U %-13U %-14u %u",
768                      format_bond_interface_name, bif->dev_instance,
769                      bif->sw_if_index, format_bond_mode, bif->mode,
770                      format_bond_load_balance, bif->lb,
771                      vec_len (bif->active_slaves), vec_len (bif->slaves));
772   }));
773   /* *INDENT-ON* */
774 }
775
776 static void
777 show_bond_details (vlib_main_t * vm)
778 {
779   bond_main_t *bm = &bond_main;
780   bond_if_t *bif;
781   u32 *sw_if_index;
782
783   /* *INDENT-OFF* */
784   pool_foreach (bif, bm->interfaces,
785   ({
786     vlib_cli_output (vm, "%U", format_bond_interface_name, bif->dev_instance);
787     vlib_cli_output (vm, "  mode: %U",
788                      format_bond_mode, bif->mode);
789     vlib_cli_output (vm, "  load balance: %U",
790                      format_bond_load_balance, bif->lb);
791     if (bif->mode == BOND_MODE_ROUND_ROBIN)
792       vlib_cli_output (vm, "  last xmit slave index: %u",
793                        bif->lb_rr_last_index);
794     vlib_cli_output (vm, "  number of active slaves: %d",
795                      vec_len (bif->active_slaves));
796     vec_foreach (sw_if_index, bif->active_slaves)
797       {
798         vlib_cli_output (vm, "    %U", format_vnet_sw_if_index_name,
799                          vnet_get_main (), *sw_if_index);
800       }
801     vlib_cli_output (vm, "  number of slaves: %d", vec_len (bif->slaves));
802     vec_foreach (sw_if_index, bif->slaves)
803       {
804         vlib_cli_output (vm, "    %U", format_vnet_sw_if_index_name,
805                          vnet_get_main (), *sw_if_index);
806       }
807     vlib_cli_output (vm, "  device instance: %d", bif->dev_instance);
808     vlib_cli_output (vm, "  interface id: %d", bif->id);
809     vlib_cli_output (vm, "  sw_if_index: %d", bif->sw_if_index);
810     vlib_cli_output (vm, "  hw_if_index: %d", bif->hw_if_index);
811   }));
812   /* *INDENT-ON* */
813 }
814
815 static clib_error_t *
816 show_bond_fn (vlib_main_t * vm, unformat_input_t * input,
817               vlib_cli_command_t * cmd)
818 {
819   u8 details = 0;
820
821   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
822     {
823       if (unformat (input, "details"))
824         details = 1;
825       else
826         {
827           return clib_error_return (0, "unknown input `%U'",
828                                     format_unformat_error, input);
829         }
830     }
831
832   if (details)
833     show_bond_details (vm);
834   else
835     show_bond (vm);
836
837   return 0;
838 }
839
840 /* *INDENT-OFF* */
841 VLIB_CLI_COMMAND (show_bond_command, static) = {
842   .path = "show bond",
843   .short_help = "show bond [details]",
844   .function = show_bond_fn,
845 };
846 /* *INDENT-ON* */
847
848 clib_error_t *
849 bond_cli_init (vlib_main_t * vm)
850 {
851   bond_main_t *bm = &bond_main;
852
853   bm->vlib_main = vm;
854   bm->vnet_main = vnet_get_main ();
855   vec_validate_aligned (bm->slave_by_sw_if_index, 1, CLIB_CACHE_LINE_BYTES);
856   vec_validate_aligned (bm->per_thread_data,
857                         vlib_get_thread_main ()->n_vlib_mains - 1,
858                         CLIB_CACHE_LINE_BYTES);
859
860   return 0;
861 }
862
863 VLIB_INIT_FUNCTION (bond_cli_init);
864
865 /*
866  * fd.io coding-style-patch-verification: ON
867  *
868  * Local Variables:
869  * eval: (c-set-style "gnu")
870  * End:
871  */