bonding: add weight support for active-backup mode
[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 #include <vpp/stats/stat_segment.h>
24
25 void
26 bond_disable_collecting_distributing (vlib_main_t * vm, slave_if_t * sif)
27 {
28   bond_main_t *bm = &bond_main;
29   bond_if_t *bif;
30   int i;
31   uword p;
32   u8 switching_active = 0;
33
34   bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
35   clib_spinlock_lock_if_init (&bif->lockp);
36   vec_foreach_index (i, bif->active_slaves)
37   {
38     p = *vec_elt_at_index (bif->active_slaves, i);
39     if (p == sif->sw_if_index)
40       {
41         if ((bif->mode == BOND_MODE_ACTIVE_BACKUP) && (i == 0) &&
42             (vec_len (bif->active_slaves) > 1))
43           /* deleting the active slave for active-backup */
44           switching_active = 1;
45         vec_del1 (bif->active_slaves, i);
46         hash_unset (bif->active_slave_by_sw_if_index, sif->sw_if_index);
47         if (sif->lacp_enabled && bif->numa_only)
48           {
49             /* For lacp mode, if we check it is a slave on local numa node,
50                bif->n_numa_slaves should be decreased by 1 becasue the first
51                bif->n_numa_slaves are all slaves on local numa node */
52             if (i < bif->n_numa_slaves)
53               {
54                 bif->n_numa_slaves--;
55                 ASSERT (bif->n_numa_slaves >= 0);
56               }
57           }
58         break;
59       }
60   }
61
62   /* We get a new slave just becoming active */
63   if (switching_active)
64     vlib_process_signal_event (bm->vlib_main, bond_process_node.index,
65                                BOND_SEND_GARP_NA, bif->hw_if_index);
66   clib_spinlock_unlock_if_init (&bif->lockp);
67
68   if (bif->mode == BOND_MODE_LACP)
69     stat_segment_set_state_counter (bm->stats[bif->sw_if_index]
70                                     [sif->sw_if_index], sif->actor.state);
71 }
72
73 /*
74  * return 1 if s2 is preferred.
75  * return -1 if s1 is preferred.
76  */
77 static int
78 bond_slave_sort (void *a1, void *a2)
79 {
80   u32 *s1 = a1;
81   u32 *s2 = a2;
82   slave_if_t *sif1 = bond_get_slave_by_sw_if_index (*s1);
83   slave_if_t *sif2 = bond_get_slave_by_sw_if_index (*s2);
84   bond_if_t *bif;
85
86   ASSERT (sif1);
87   ASSERT (sif2);
88   /*
89    * sort entries according to preference rules:
90    * 1. biggest weight
91    * 2. numa-node
92    * 3. current active slave (to prevent churning)
93    * 4. lowest sw_if_index (for deterministic behavior)
94    *
95    */
96   if (sif2->weight > sif1->weight)
97     return 1;
98   if (sif2->weight < sif1->weight)
99     return -1;
100   else
101     {
102       if (sif2->is_local_numa > sif1->is_local_numa)
103         return 1;
104       if (sif2->is_local_numa < sif1->is_local_numa)
105         return -1;
106       else
107         {
108           bif = bond_get_master_by_dev_instance (sif1->bif_dev_instance);
109           /* Favor the current active slave to avoid churning */
110           if (bif->active_slaves[0] == sif2->sw_if_index)
111             return 1;
112           if (bif->active_slaves[0] == sif1->sw_if_index)
113             return -1;
114           /* go for the tiebreaker as the last resort */
115           if (sif1->sw_if_index > sif2->sw_if_index)
116             return 1;
117           if (sif1->sw_if_index < sif2->sw_if_index)
118             return -1;
119           else
120             ASSERT (0);
121         }
122     }
123   return 0;
124 }
125
126 static void
127 bond_sort_slaves (bond_if_t * bif)
128 {
129   bond_main_t *bm = &bond_main;
130   u32 old_active = bif->active_slaves[0];
131
132   vec_sort_with_function (bif->active_slaves, bond_slave_sort);
133   if (old_active != bif->active_slaves[0])
134     vlib_process_signal_event (bm->vlib_main, bond_process_node.index,
135                                BOND_SEND_GARP_NA, bif->hw_if_index);
136 }
137
138 void
139 bond_enable_collecting_distributing (vlib_main_t * vm, slave_if_t * sif)
140 {
141   bond_if_t *bif;
142   bond_main_t *bm = &bond_main;
143   vnet_main_t *vnm = vnet_get_main ();
144   vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sif->sw_if_index);
145
146   bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
147   clib_spinlock_lock_if_init (&bif->lockp);
148   if (!hash_get (bif->active_slave_by_sw_if_index, sif->sw_if_index))
149     {
150       hash_set (bif->active_slave_by_sw_if_index, sif->sw_if_index,
151                 sif->sw_if_index);
152
153       if ((sif->lacp_enabled && bif->numa_only)
154           && (vm->numa_node == hw->numa_node))
155         {
156           vec_insert_elts (bif->active_slaves, &sif->sw_if_index, 1,
157                            bif->n_numa_slaves);
158           bif->n_numa_slaves++;
159         }
160       else
161         vec_add1 (bif->active_slaves, sif->sw_if_index);
162
163       sif->is_local_numa = (vm->numa_node == hw->numa_node) ? 1 : 0;
164       if (bif->mode == BOND_MODE_ACTIVE_BACKUP)
165         {
166           if (vec_len (bif->active_slaves) == 1)
167             /* First slave becomes active? */
168             vlib_process_signal_event (bm->vlib_main, bond_process_node.index,
169                                        BOND_SEND_GARP_NA, bif->hw_if_index);
170           else
171             bond_sort_slaves (bif);
172         }
173     }
174   clib_spinlock_unlock_if_init (&bif->lockp);
175
176   if (bif->mode == BOND_MODE_LACP)
177     stat_segment_set_state_counter (bm->stats[bif->sw_if_index]
178                                     [sif->sw_if_index], sif->actor.state);
179 }
180
181 int
182 bond_dump_ifs (bond_interface_details_t ** out_bondifs)
183 {
184   vnet_main_t *vnm = vnet_get_main ();
185   bond_main_t *bm = &bond_main;
186   bond_if_t *bif;
187   vnet_hw_interface_t *hi;
188   bond_interface_details_t *r_bondifs = NULL;
189   bond_interface_details_t *bondif = NULL;
190
191   /* *INDENT-OFF* */
192   pool_foreach (bif, bm->interfaces,
193     vec_add2(r_bondifs, bondif, 1);
194     clib_memset (bondif, 0, sizeof (*bondif));
195     bondif->id = bif->id;
196     bondif->sw_if_index = bif->sw_if_index;
197     hi = vnet_get_hw_interface (vnm, bif->hw_if_index);
198     clib_memcpy(bondif->interface_name, hi->name,
199                 MIN (ARRAY_LEN (bondif->interface_name) - 1,
200                      strlen ((const char *) hi->name)));
201     bondif->mode = bif->mode;
202     bondif->lb = bif->lb;
203     bondif->numa_only = bif->numa_only;
204     bondif->active_slaves = vec_len (bif->active_slaves);
205     bondif->slaves = vec_len (bif->slaves);
206   );
207   /* *INDENT-ON* */
208
209   *out_bondifs = r_bondifs;
210
211   return 0;
212 }
213
214 int
215 bond_dump_slave_ifs (slave_interface_details_t ** out_slaveifs,
216                      u32 bond_sw_if_index)
217 {
218   vnet_main_t *vnm = vnet_get_main ();
219   bond_if_t *bif;
220   vnet_hw_interface_t *hi;
221   vnet_sw_interface_t *sw;
222   slave_interface_details_t *r_slaveifs = NULL;
223   slave_interface_details_t *slaveif = NULL;
224   u32 *sw_if_index = NULL;
225   slave_if_t *sif;
226
227   bif = bond_get_master_by_sw_if_index (bond_sw_if_index);
228   if (!bif)
229     return 1;
230
231   vec_foreach (sw_if_index, bif->slaves)
232   {
233     vec_add2 (r_slaveifs, slaveif, 1);
234     clib_memset (slaveif, 0, sizeof (*slaveif));
235     sif = bond_get_slave_by_sw_if_index (*sw_if_index);
236     if (sif)
237       {
238         sw = vnet_get_sw_interface (vnm, sif->sw_if_index);
239         hi = vnet_get_hw_interface (vnm, sw->hw_if_index);
240         clib_memcpy (slaveif->interface_name, hi->name,
241                      MIN (ARRAY_LEN (slaveif->interface_name) - 1,
242                           strlen ((const char *) hi->name)));
243         slaveif->sw_if_index = sif->sw_if_index;
244         slaveif->is_passive = sif->is_passive;
245         slaveif->is_long_timeout = sif->is_long_timeout;
246         slaveif->is_local_numa = sif->is_local_numa;
247         slaveif->weight = sif->weight;
248       }
249   }
250   *out_slaveifs = r_slaveifs;
251
252   return 0;
253 }
254
255 static void
256 bond_delete_neighbor (vlib_main_t * vm, bond_if_t * bif, slave_if_t * sif)
257 {
258   bond_main_t *bm = &bond_main;
259   vnet_main_t *vnm = vnet_get_main ();
260   int i;
261   vnet_hw_interface_t *sif_hw;
262
263   sif_hw = vnet_get_sup_hw_interface (vnm, sif->sw_if_index);
264
265   bif->port_number_bitmap =
266     clib_bitmap_set (bif->port_number_bitmap,
267                      ntohs (sif->actor_admin.port_number) - 1, 0);
268   bm->slave_by_sw_if_index[sif->sw_if_index] = 0;
269   vec_free (sif->last_marker_pkt);
270   vec_free (sif->last_rx_pkt);
271   vec_foreach_index (i, bif->slaves)
272   {
273     uword p = *vec_elt_at_index (bif->slaves, i);
274     if (p == sif->sw_if_index)
275       {
276         vec_del1 (bif->slaves, i);
277         break;
278       }
279   }
280
281   bond_disable_collecting_distributing (vm, sif);
282
283   vnet_feature_enable_disable ("device-input", "bond-input",
284                                sif_hw->hw_if_index, 0, 0, 0);
285
286   /* Put back the old mac */
287   vnet_hw_interface_change_mac_address (vnm, sif_hw->hw_if_index,
288                                         sif->persistent_hw_address);
289
290   if ((bif->mode == BOND_MODE_LACP) && bm->lacp_enable_disable)
291     (*bm->lacp_enable_disable) (vm, bif, sif, 0);
292
293   if (bif->mode == BOND_MODE_LACP)
294     stat_segment_deregister_state_counter
295       (bm->stats[bif->sw_if_index][sif->sw_if_index]);
296
297   pool_put (bm->neighbors, sif);
298 }
299
300 int
301 bond_delete_if (vlib_main_t * vm, u32 sw_if_index)
302 {
303   bond_main_t *bm = &bond_main;
304   vnet_main_t *vnm = vnet_get_main ();
305   bond_if_t *bif;
306   slave_if_t *sif;
307   vnet_hw_interface_t *hw;
308   u32 *sif_sw_if_index;
309   u32 **s_list = 0;
310   u32 i;
311
312   hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
313   if (hw == NULL || bond_dev_class.index != hw->dev_class_index)
314     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
315
316   bif = bond_get_master_by_dev_instance (hw->dev_instance);
317
318   vec_foreach (sif_sw_if_index, bif->slaves)
319   {
320     vec_add1 (s_list, sif_sw_if_index);
321   }
322
323   for (i = 0; i < vec_len (s_list); i++)
324     {
325       sif_sw_if_index = s_list[i];
326       sif = bond_get_slave_by_sw_if_index (*sif_sw_if_index);
327       if (sif)
328         bond_delete_neighbor (vm, bif, sif);
329     }
330
331   if (s_list)
332     vec_free (s_list);
333
334   /* bring down the interface */
335   vnet_hw_interface_set_flags (vnm, bif->hw_if_index, 0);
336   vnet_sw_interface_set_flags (vnm, bif->sw_if_index, 0);
337
338   ethernet_delete_interface (vnm, bif->hw_if_index);
339
340   clib_bitmap_free (bif->port_number_bitmap);
341   hash_unset (bm->bond_by_sw_if_index, bif->sw_if_index);
342   hash_unset (bm->id_used, bif->id);
343   clib_memset (bif, 0, sizeof (*bif));
344   pool_put (bm->interfaces, bif);
345
346   return 0;
347 }
348
349 void
350 bond_create_if (vlib_main_t * vm, bond_create_if_args_t * args)
351 {
352   bond_main_t *bm = &bond_main;
353   vnet_main_t *vnm = vnet_get_main ();
354   vnet_sw_interface_t *sw;
355   bond_if_t *bif;
356
357   if ((args->mode == BOND_MODE_LACP) && bm->lacp_plugin_loaded == 0)
358     {
359       args->rv = VNET_API_ERROR_FEATURE_DISABLED;
360       args->error = clib_error_return (0, "LACP plugin is not loaded");
361       return;
362     }
363   if (args->mode > BOND_MODE_LACP || args->mode < BOND_MODE_ROUND_ROBIN)
364     {
365       args->rv = VNET_API_ERROR_INVALID_ARGUMENT;
366       args->error = clib_error_return (0, "Invalid mode");
367       return;
368     }
369   if (args->lb > BOND_LB_L23)
370     {
371       args->rv = VNET_API_ERROR_INVALID_ARGUMENT;
372       args->error = clib_error_return (0, "Invalid load-balance");
373       return;
374     }
375   pool_get (bm->interfaces, bif);
376   clib_memset (bif, 0, sizeof (*bif));
377   bif->dev_instance = bif - bm->interfaces;
378   bif->id = args->id;
379   bif->lb = args->lb;
380   bif->mode = args->mode;
381
382   // Adjust requested interface id
383   if (bif->id == ~0)
384     bif->id = bif->dev_instance;
385   if (hash_get (bm->id_used, bif->id))
386     {
387       args->rv = VNET_API_ERROR_INSTANCE_IN_USE;
388       pool_put (bm->interfaces, bif);
389       return;
390     }
391   hash_set (bm->id_used, bif->id, 1);
392
393   // Special load-balance mode used for rr and bc
394   if (bif->mode == BOND_MODE_ROUND_ROBIN)
395     bif->lb = BOND_LB_RR;
396   else if (bif->mode == BOND_MODE_BROADCAST)
397     bif->lb = BOND_LB_BC;
398   else if (bif->mode == BOND_MODE_ACTIVE_BACKUP)
399     bif->lb = BOND_LB_AB;
400
401   bif->use_custom_mac = args->hw_addr_set;
402   if (!args->hw_addr_set)
403     {
404       f64 now = vlib_time_now (vm);
405       u32 rnd;
406       rnd = (u32) (now * 1e6);
407       rnd = random_u32 (&rnd);
408
409       memcpy (args->hw_addr + 2, &rnd, sizeof (rnd));
410       args->hw_addr[0] = 2;
411       args->hw_addr[1] = 0xfe;
412     }
413   memcpy (bif->hw_address, args->hw_addr, 6);
414   args->error = ethernet_register_interface
415     (vnm, bond_dev_class.index, bif->dev_instance /* device instance */ ,
416      bif->hw_address /* ethernet address */ ,
417      &bif->hw_if_index, 0 /* flag change */ );
418
419   if (args->error)
420     {
421       args->rv = VNET_API_ERROR_INVALID_REGISTRATION;
422       hash_unset (bm->id_used, bif->id);
423       pool_put (bm->interfaces, bif);
424       return;
425     }
426
427   sw = vnet_get_hw_sw_interface (vnm, bif->hw_if_index);
428   bif->sw_if_index = sw->sw_if_index;
429   bif->group = bif->sw_if_index;
430   bif->numa_only = args->numa_only;
431   if (vlib_get_thread_main ()->n_vlib_mains > 1)
432     clib_spinlock_init (&bif->lockp);
433
434   vnet_hw_interface_set_flags (vnm, bif->hw_if_index,
435                                VNET_HW_INTERFACE_FLAG_LINK_UP);
436
437   hash_set (bm->bond_by_sw_if_index, bif->sw_if_index, bif->dev_instance);
438
439   // for return
440   args->sw_if_index = bif->sw_if_index;
441   args->rv = 0;
442 }
443
444 static clib_error_t *
445 bond_create_command_fn (vlib_main_t * vm, unformat_input_t * input,
446                         vlib_cli_command_t * cmd)
447 {
448   unformat_input_t _line_input, *line_input = &_line_input;
449   bond_create_if_args_t args = { 0 };
450   u8 mode_is_set = 0;
451
452   /* Get a line of input. */
453   if (!unformat_user (input, unformat_line_input, line_input))
454     return clib_error_return (0, "Missing required arguments.");
455
456   args.id = ~0;
457   args.mode = -1;
458   args.lb = BOND_LB_L2;
459   args.rv = -1;
460   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
461     {
462       if (unformat (line_input, "mode %U", unformat_bond_mode, &args.mode))
463         mode_is_set = 1;
464       else if (((args.mode == BOND_MODE_LACP) || (args.mode == BOND_MODE_XOR))
465                && unformat (line_input, "load-balance %U",
466                             unformat_bond_load_balance, &args.lb))
467         ;
468       else if (unformat (line_input, "hw-addr %U",
469                          unformat_ethernet_address, args.hw_addr))
470         args.hw_addr_set = 1;
471       else if (unformat (line_input, "id %u", &args.id))
472         ;
473       else if (unformat (line_input, "numa-only"))
474         {
475           if (args.mode == BOND_MODE_LACP)
476             args.numa_only = 1;
477           else
478             return clib_error_return (0,
479                                       "Only lacp mode supports numa-only so far!");
480         }
481       else
482         return clib_error_return (0, "unknown input `%U'",
483                                   format_unformat_error, input);
484     }
485   unformat_free (line_input);
486
487   if (mode_is_set == 0)
488     return clib_error_return (0, "Missing bond mode");
489
490   bond_create_if (vm, &args);
491
492   if (!args.rv)
493     vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
494                      vnet_get_main (), args.sw_if_index);
495
496   return args.error;
497 }
498
499 /* *INDENT-OFF* */
500 VLIB_CLI_COMMAND (bond_create_command, static) = {
501   .path = "create bond",
502   .short_help = "create bond mode {round-robin | active-backup | broadcast | "
503     "{lacp | xor} [load-balance { l2 | l23 | l34 } [numa-only]]} [hw-addr <mac-address>] "
504     "[id <if-id>]",
505   .function = bond_create_command_fn,
506 };
507 /* *INDENT-ON* */
508
509 static clib_error_t *
510 bond_delete_command_fn (vlib_main_t * vm, unformat_input_t * input,
511                         vlib_cli_command_t * cmd)
512 {
513   unformat_input_t _line_input, *line_input = &_line_input;
514   u32 sw_if_index = ~0;
515   vnet_main_t *vnm = vnet_get_main ();
516   int rv;
517
518   /* Get a line of input. */
519   if (!unformat_user (input, unformat_line_input, line_input))
520     return clib_error_return (0, "Missing <interface>");
521
522   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
523     {
524       if (unformat (line_input, "sw_if_index %d", &sw_if_index))
525         ;
526       else if (unformat (line_input, "%U", unformat_vnet_sw_interface,
527                          vnm, &sw_if_index))
528         ;
529       else
530         return clib_error_return (0, "unknown input `%U'",
531                                   format_unformat_error, input);
532     }
533   unformat_free (line_input);
534
535   if (sw_if_index == ~0)
536     return clib_error_return (0,
537                               "please specify interface name or sw_if_index");
538
539   rv = bond_delete_if (vm, sw_if_index);
540   if (rv == VNET_API_ERROR_INVALID_SW_IF_INDEX)
541     return clib_error_return (0, "not a bond interface");
542   else if (rv != 0)
543     return clib_error_return (0, "error on deleting bond interface");
544
545   return 0;
546 }
547
548 /* *INDENT-OFF* */
549 VLIB_CLI_COMMAND (bond_delete__command, static) =
550 {
551   .path = "delete bond",
552   .short_help = "delete bond {<interface> | sw_if_index <sw_idx>}",
553   .function = bond_delete_command_fn,
554 };
555 /* *INDENT-ON* */
556
557 void
558 bond_enslave (vlib_main_t * vm, bond_enslave_args_t * args)
559 {
560   bond_main_t *bm = &bond_main;
561   vnet_main_t *vnm = vnet_get_main ();
562   bond_if_t *bif;
563   slave_if_t *sif;
564   vnet_interface_main_t *im = &vnm->interface_main;
565   vnet_hw_interface_t *bif_hw, *sif_hw;
566   vnet_sw_interface_t *sw;
567   u32 thread_index;
568   u32 sif_if_index;
569
570   bif = bond_get_master_by_sw_if_index (args->group);
571   if (!bif)
572     {
573       args->rv = VNET_API_ERROR_INVALID_INTERFACE;
574       args->error = clib_error_return (0, "bond interface not found");
575       return;
576     }
577   // make sure the interface is not already enslaved
578   if (bond_get_slave_by_sw_if_index (args->slave))
579     {
580       args->rv = VNET_API_ERROR_VALUE_EXIST;
581       args->error = clib_error_return (0, "interface was already enslaved");
582       return;
583     }
584   sif_hw = vnet_get_sup_hw_interface (vnm, args->slave);
585   if (sif_hw->dev_class_index == bond_dev_class.index)
586     {
587       args->rv = VNET_API_ERROR_INVALID_INTERFACE;
588       args->error =
589         clib_error_return (0, "bond interface cannot be enslaved");
590       return;
591     }
592   if (bif->mode == BOND_MODE_LACP)
593     {
594       u8 *name = format (0, "/if/lacp/%u/%u/state", bif->sw_if_index,
595                          args->slave);
596
597       vec_validate (bm->stats, bif->sw_if_index);
598       vec_validate (bm->stats[bif->sw_if_index], args->slave);
599
600       args->error = stat_segment_register_state_counter
601         (name, &bm->stats[bif->sw_if_index][args->slave]);
602       vec_free (name);
603       if (args->error != 0)
604         {
605           args->rv = VNET_API_ERROR_INVALID_INTERFACE;
606           return;
607         }
608     }
609
610   pool_get (bm->neighbors, sif);
611   clib_memset (sif, 0, sizeof (*sif));
612   sw = pool_elt_at_index (im->sw_interfaces, args->slave);
613   /* port_enabled is both admin up and hw link up */
614   sif->port_enabled = vnet_sw_interface_is_up (vnm, sw->sw_if_index);
615   sif->sw_if_index = sw->sw_if_index;
616   sif->hw_if_index = sw->hw_if_index;
617   sif->packet_template_index = (u8) ~ 0;
618   sif->is_passive = args->is_passive;
619   sif->group = args->group;
620   sif->bif_dev_instance = bif->dev_instance;
621   sif->mode = bif->mode;
622
623   sif->is_long_timeout = args->is_long_timeout;
624   if (args->is_long_timeout)
625     sif->ttl_in_seconds = LACP_LONG_TIMOUT_TIME;
626   else
627     sif->ttl_in_seconds = LACP_SHORT_TIMOUT_TIME;
628
629   vec_validate_aligned (bm->slave_by_sw_if_index, sif->sw_if_index,
630                         CLIB_CACHE_LINE_BYTES);
631   /*
632    * sif - bm->neighbors may be 0
633    * Left shift it by 1 bit to distinguish the valid entry that we actually
634    * store from the null entries
635    */
636   bm->slave_by_sw_if_index[sif->sw_if_index] =
637     (uword) (((sif - bm->neighbors) << 1) | 1);
638   vec_add1 (bif->slaves, sif->sw_if_index);
639
640   sif_hw = vnet_get_sup_hw_interface (vnm, sif->sw_if_index);
641
642   /* Save the old mac */
643   memcpy (sif->persistent_hw_address, sif_hw->hw_address, 6);
644   bif_hw = vnet_get_sup_hw_interface (vnm, bif->sw_if_index);
645   if (bif->use_custom_mac)
646     {
647       vnet_hw_interface_change_mac_address (vnm, sif_hw->hw_if_index,
648                                             bif->hw_address);
649     }
650   else
651     {
652       // bond interface gets the mac address from the first slave
653       if (vec_len (bif->slaves) == 1)
654         {
655           memcpy (bif->hw_address, sif_hw->hw_address, 6);
656           vnet_hw_interface_change_mac_address (vnm, bif_hw->hw_if_index,
657                                                 sif_hw->hw_address);
658         }
659       else
660         {
661           // subsequent slaves gets the mac address of the bond interface
662           vnet_hw_interface_change_mac_address (vnm, sif_hw->hw_if_index,
663                                                 bif->hw_address);
664         }
665     }
666
667   if (bif_hw->l2_if_count)
668     {
669       ethernet_set_flags (vnm, sif_hw->hw_if_index,
670                           ETHERNET_INTERFACE_FLAG_ACCEPT_ALL);
671       /* ensure all packets go to ethernet-input */
672       ethernet_set_rx_redirect (vnm, sif_hw, 1);
673     }
674
675   if (bif->mode == BOND_MODE_LACP)
676     {
677       if (bm->lacp_enable_disable)
678         (*bm->lacp_enable_disable) (vm, bif, sif, 1);
679     }
680   else if (sif->port_enabled)
681     {
682       bond_enable_collecting_distributing (vm, sif);
683     }
684
685   vec_foreach_index (thread_index, bm->per_thread_data)
686   {
687     bond_per_thread_data_t *ptd = vec_elt_at_index (bm->per_thread_data,
688                                                     thread_index);
689
690     vec_validate_aligned (ptd->per_port_queue, vec_len (bif->slaves) - 1,
691                           CLIB_CACHE_LINE_BYTES);
692
693     vec_foreach_index (sif_if_index, ptd->per_port_queue)
694     {
695       ptd->per_port_queue[sif_if_index].n_buffers = 0;
696     }
697   }
698
699   args->rv = vnet_feature_enable_disable ("device-input", "bond-input",
700                                           sif_hw->hw_if_index, 1, 0, 0);
701
702   if (args->rv)
703     {
704       args->error =
705         clib_error_return (0,
706                            "Error encountered on input feature arc enable");
707     }
708 }
709
710 static clib_error_t *
711 enslave_interface_command_fn (vlib_main_t * vm, unformat_input_t * input,
712                               vlib_cli_command_t * cmd)
713 {
714   bond_enslave_args_t args = { 0 };
715   unformat_input_t _line_input, *line_input = &_line_input;
716   vnet_main_t *vnm = vnet_get_main ();
717
718   /* Get a line of input. */
719   if (!unformat_user (input, unformat_line_input, line_input))
720     return clib_error_return (0, "Missing required arguments.");
721
722   args.slave = ~0;
723   args.group = ~0;
724   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
725     {
726       if (unformat (line_input, "%U %U",
727                     unformat_vnet_sw_interface, vnm, &args.group,
728                     unformat_vnet_sw_interface, vnm, &args.slave))
729         ;
730       else if (unformat (line_input, "passive"))
731         args.is_passive = 1;
732       else if (unformat (line_input, "long-timeout"))
733         args.is_long_timeout = 1;
734       else
735         {
736           args.error = clib_error_return (0, "unknown input `%U'",
737                                           format_unformat_error, input);
738           break;
739         }
740     }
741   unformat_free (line_input);
742
743   if (args.error)
744     return args.error;
745   if (args.group == ~0)
746     return clib_error_return (0, "Missing bond interface");
747   if (args.slave == ~0)
748     return clib_error_return (0, "please specify valid slave interface name");
749
750   bond_enslave (vm, &args);
751
752   return args.error;
753 }
754
755 /* *INDENT-OFF* */
756 VLIB_CLI_COMMAND (enslave_interface_command, static) = {
757   .path = "bond add",
758   .short_help = "bond add <BondEthernetx> <slave-interface> "
759                 "[passive] [long-timeout]",
760   .function = enslave_interface_command_fn,
761 };
762 /* *INDENT-ON* */
763
764 void
765 bond_detach_slave (vlib_main_t * vm, bond_detach_slave_args_t * args)
766 {
767   bond_if_t *bif;
768   slave_if_t *sif;
769
770   sif = bond_get_slave_by_sw_if_index (args->slave);
771   if (!sif)
772     {
773       args->rv = VNET_API_ERROR_INVALID_INTERFACE;
774       args->error = clib_error_return (0, "interface was not enslaved");
775       return;
776     }
777   bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
778   bond_delete_neighbor (vm, bif, sif);
779 }
780
781 static clib_error_t *
782 detach_interface_command_fn (vlib_main_t * vm, unformat_input_t * input,
783                              vlib_cli_command_t * cmd)
784 {
785   bond_detach_slave_args_t args = { 0 };
786   unformat_input_t _line_input, *line_input = &_line_input;
787   vnet_main_t *vnm = vnet_get_main ();
788
789   /* Get a line of input. */
790   if (!unformat_user (input, unformat_line_input, line_input))
791     return clib_error_return (0, "Missing required arguments.");
792
793   args.slave = ~0;
794   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
795     {
796       if (unformat (line_input, "%U",
797                     unformat_vnet_sw_interface, vnm, &args.slave))
798         ;
799       else
800         {
801           args.error = clib_error_return (0, "unknown input `%U'",
802                                           format_unformat_error, input);
803           break;
804         }
805     }
806   unformat_free (line_input);
807
808   if (args.error)
809     return args.error;
810   if (args.slave == ~0)
811     return clib_error_return (0, "please specify valid slave interface name");
812
813   bond_detach_slave (vm, &args);
814
815   return args.error;
816 }
817
818 /* *INDENT-OFF* */
819 VLIB_CLI_COMMAND (detach_interface_command, static) = {
820   .path = "bond del",
821   .short_help = "bond del <slave-interface>",
822   .function = detach_interface_command_fn,
823 };
824 /* *INDENT-ON* */
825
826 static void
827 show_bond (vlib_main_t * vm)
828 {
829   bond_main_t *bm = &bond_main;
830   bond_if_t *bif;
831
832   vlib_cli_output (vm, "%-16s %-12s %-13s %-13s %-14s %s",
833                    "interface name", "sw_if_index", "mode",
834                    "load balance", "active slaves", "slaves");
835
836   /* *INDENT-OFF* */
837   pool_foreach (bif, bm->interfaces,
838   ({
839     vlib_cli_output (vm, "%-16U %-12d %-13U %-13U %-14u %u",
840                      format_bond_interface_name, bif->dev_instance,
841                      bif->sw_if_index, format_bond_mode, bif->mode,
842                      format_bond_load_balance, bif->lb,
843                      vec_len (bif->active_slaves), vec_len (bif->slaves));
844   }));
845   /* *INDENT-ON* */
846 }
847
848 static void
849 show_bond_details (vlib_main_t * vm)
850 {
851   bond_main_t *bm = &bond_main;
852   bond_if_t *bif;
853   u32 *sw_if_index;
854
855   /* *INDENT-OFF* */
856   pool_foreach (bif, bm->interfaces,
857   ({
858     vlib_cli_output (vm, "%U", format_bond_interface_name, bif->dev_instance);
859     vlib_cli_output (vm, "  mode: %U",
860                      format_bond_mode, bif->mode);
861     vlib_cli_output (vm, "  load balance: %U",
862                      format_bond_load_balance, bif->lb);
863     if (bif->mode == BOND_MODE_ROUND_ROBIN)
864       vlib_cli_output (vm, "  last xmit slave index: %u",
865                        bif->lb_rr_last_index);
866     vlib_cli_output (vm, "  number of active slaves: %d",
867                      vec_len (bif->active_slaves));
868     vec_foreach (sw_if_index, bif->active_slaves)
869       {
870         vlib_cli_output (vm, "    %U", format_vnet_sw_if_index_name,
871                          vnet_get_main (), *sw_if_index);
872         if (bif->mode == BOND_MODE_ACTIVE_BACKUP)
873           {
874             slave_if_t *sif = bond_get_slave_by_sw_if_index (*sw_if_index);
875             if (sif)
876               vlib_cli_output (vm, "      weight: %u, is_local_numa: %u, "
877                                "sw_if_index: %u", sif->weight,
878                                sif->is_local_numa, sif->sw_if_index);
879           }
880       }
881     vlib_cli_output (vm, "  number of slaves: %d", vec_len (bif->slaves));
882     vec_foreach (sw_if_index, bif->slaves)
883       {
884         vlib_cli_output (vm, "    %U", format_vnet_sw_if_index_name,
885                          vnet_get_main (), *sw_if_index);
886       }
887     vlib_cli_output (vm, "  device instance: %d", bif->dev_instance);
888     vlib_cli_output (vm, "  interface id: %d", bif->id);
889     vlib_cli_output (vm, "  sw_if_index: %d", bif->sw_if_index);
890     vlib_cli_output (vm, "  hw_if_index: %d", bif->hw_if_index);
891   }));
892   /* *INDENT-ON* */
893 }
894
895 static clib_error_t *
896 show_bond_fn (vlib_main_t * vm, unformat_input_t * input,
897               vlib_cli_command_t * cmd)
898 {
899   u8 details = 0;
900
901   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
902     {
903       if (unformat (input, "details"))
904         details = 1;
905       else
906         {
907           return clib_error_return (0, "unknown input `%U'",
908                                     format_unformat_error, input);
909         }
910     }
911
912   if (details)
913     show_bond_details (vm);
914   else
915     show_bond (vm);
916
917   return 0;
918 }
919
920 /* *INDENT-OFF* */
921 VLIB_CLI_COMMAND (show_bond_command, static) = {
922   .path = "show bond",
923   .short_help = "show bond [details]",
924   .function = show_bond_fn,
925 };
926 /* *INDENT-ON* */
927
928 void
929 bond_set_intf_weight (vlib_main_t * vm, bond_set_intf_weight_args_t * args)
930 {
931   slave_if_t *sif;
932   bond_if_t *bif;
933   vnet_main_t *vnm;
934   u32 old_weight;
935
936   sif = bond_get_slave_by_sw_if_index (args->sw_if_index);
937   if (!sif)
938     {
939       args->rv = VNET_API_ERROR_INVALID_INTERFACE;
940       args->error = clib_error_return (0, "Interface not enslaved");
941       return;
942     }
943   bif = bond_get_master_by_dev_instance (sif->bif_dev_instance);
944   if (!bif)
945     {
946       args->rv = VNET_API_ERROR_INVALID_INTERFACE;
947       args->error = clib_error_return (0, "bond interface not found");
948       return;
949     }
950   if (bif->mode != BOND_MODE_ACTIVE_BACKUP)
951     {
952       args->rv = VNET_API_ERROR_INVALID_ARGUMENT;
953       args->error =
954         clib_error_return (0, "Weight valid for active-backup only");
955       return;
956     }
957
958   old_weight = sif->weight;
959   sif->weight = args->weight;
960   vnm = vnet_get_main ();
961   /*
962    * No need to sort the list if the affected slave is not up (not in active
963    * slave set), active slave count is 1, or the current slave is already the
964    * primary slave and new weight > old weight.
965    */
966   if (!vnet_sw_interface_is_up (vnm, sif->sw_if_index) ||
967       (vec_len (bif->active_slaves) == 1) ||
968       ((bif->active_slaves[0] == sif->sw_if_index) &&
969        (sif->weight >= old_weight)))
970     return;
971
972   bond_sort_slaves (bif);
973 }
974
975 static clib_error_t *
976 bond_set_intf_cmd (vlib_main_t * vm, unformat_input_t * input,
977                    vlib_cli_command_t * cmd)
978 {
979   bond_set_intf_weight_args_t args = { 0 };
980   u32 sw_if_index = (u32) ~ 0;
981   unformat_input_t _line_input, *line_input = &_line_input;
982   vnet_main_t *vnm = vnet_get_main ();
983   u8 weight_enter = 0;
984   u32 weight = 0;
985
986   /* Get a line of input. */
987   if (!unformat_user (input, unformat_line_input, line_input))
988     return clib_error_return (0, "Missing required arguments.");
989
990   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
991     {
992       if (unformat (line_input, "sw_if_index %d", &sw_if_index))
993         ;
994       else if (unformat (line_input, "%U", unformat_vnet_sw_interface, vnm,
995                          &sw_if_index))
996         ;
997       else if (unformat (line_input, "weight %u", &weight))
998         weight_enter = 1;
999       else
1000         {
1001           clib_error_return (0, "unknown input `%U'", format_unformat_error,
1002                              input);
1003           break;
1004         }
1005     }
1006
1007   unformat_free (line_input);
1008   if (sw_if_index == (u32) ~ 0)
1009     {
1010       args.rv = VNET_API_ERROR_INVALID_INTERFACE;
1011       clib_error_return (0, "Interface name is invalid!");
1012     }
1013   if (weight_enter == 0)
1014     {
1015       args.rv = VNET_API_ERROR_INVALID_ARGUMENT;
1016       clib_error_return (0, "weight missing");
1017     }
1018
1019   args.sw_if_index = sw_if_index;
1020   args.weight = weight;
1021   bond_set_intf_weight (vm, &args);
1022
1023   return args.error;
1024 }
1025
1026 /* *INDENT-OFF* */
1027 VLIB_CLI_COMMAND(set_interface_bond_cmd, static) = {
1028   .path = "set interface bond",
1029   .short_help = "set interface bond <interface> | sw_if_index <idx>"
1030                 " weight <value>",
1031   .function = bond_set_intf_cmd,
1032 };
1033 /* *INDENT-ON* */
1034
1035 clib_error_t *
1036 bond_cli_init (vlib_main_t * vm)
1037 {
1038   bond_main_t *bm = &bond_main;
1039
1040   bm->vlib_main = vm;
1041   bm->vnet_main = vnet_get_main ();
1042   vec_validate_aligned (bm->slave_by_sw_if_index, 1, CLIB_CACHE_LINE_BYTES);
1043   vec_validate_aligned (bm->per_thread_data,
1044                         vlib_get_thread_main ()->n_vlib_mains - 1,
1045                         CLIB_CACHE_LINE_BYTES);
1046
1047   return 0;
1048 }
1049
1050 VLIB_INIT_FUNCTION (bond_cli_init);
1051
1052 /*
1053  * fd.io coding-style-patch-verification: ON
1054  *
1055  * Local Variables:
1056  * eval: (c-set-style "gnu")
1057  * End:
1058  */