532308cb94468baf054c018781a8b3af699b8828
[project/netifd.git] / bonding.c
1 /*
2 * netifd - network interface daemon
3 * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2
7 * as published by the Free Software Foundation
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14 #include <string.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17
18 #include "netifd.h"
19 #include "device.h"
20 #include "system.h"
21
22 struct bonding_device {
23 struct device dev;
24 device_state_cb set_state;
25
26 struct blob_attr *port_list;
27 struct vlist_tree ports;
28 int n_present;
29 int n_failed;
30
31 struct bonding_port *primary_port;
32 struct uloop_timeout retry;
33
34 struct bonding_config config;
35 struct blob_attr *config_data;
36 bool has_macaddr;
37 bool force_active;
38 bool active;
39 };
40
41 struct bonding_port {
42 struct vlist_node node;
43 struct bonding_device *bdev;
44 struct device_user dev;
45 bool set_primary;
46 bool present;
47 bool active;
48 char name[];
49 };
50
51 enum {
52 BOND_ATTR_PORTS,
53
54 BOND_ATTR_POLICY,
55 BOND_ATTR_XMIT_HASH_POLICY,
56 BOND_ATTR_ALL_PORTS_ACTIVE,
57
58 BOND_ATTR_MIN_LINKS,
59 BOND_ATTR_AD_ACTOR_SYSTEM,
60 BOND_ATTR_AD_ACTOR_SYS_PRIO,
61 BOND_ATTR_AD_SELECT,
62 BOND_ATTR_LACP_RATE,
63
64 BOND_ATTR_PACKETS_PER_PORT,
65 BOND_ATTR_LP_INTERVAL,
66 BOND_ATTR_DYNAMIC_LB,
67 BOND_ATTR_RESEND_IGMP,
68
69 BOND_ATTR_NUM_PEER_NOTIF,
70 BOND_ATTR_PRIMARY,
71 BOND_ATTR_PRIMARY_RESELECT,
72 BOND_ATTR_FAILOVER_MAC,
73
74 BOND_ATTR_MON_MODE,
75 BOND_ATTR_MON_INTERVAL,
76 BOND_ATTR_ARP_TARGET,
77 BOND_ATTR_ARP_ALL_TARGETS,
78 BOND_ATTR_ARP_VALIDATE,
79 BOND_ATTR_USE_CARRIER,
80 BOND_ATTR_UPDELAY,
81 BOND_ATTR_DOWNDELAY,
82
83 __BOND_ATTR_MAX,
84 };
85
86 static const struct blobmsg_policy bonding_attrs[__BOND_ATTR_MAX] = {
87 [BOND_ATTR_PORTS] = { "ports", BLOBMSG_TYPE_ARRAY },
88 [BOND_ATTR_POLICY] = { "policy", BLOBMSG_TYPE_STRING },
89 [BOND_ATTR_XMIT_HASH_POLICY] = { "xmit_hash_policy", BLOBMSG_TYPE_STRING },
90 [BOND_ATTR_ALL_PORTS_ACTIVE] = { "all_ports_active", BLOBMSG_TYPE_BOOL },
91 [BOND_ATTR_MIN_LINKS] = { "min_links", BLOBMSG_TYPE_INT32 },
92 [BOND_ATTR_AD_ACTOR_SYSTEM] = { "ad_actor_system", BLOBMSG_TYPE_STRING },
93 [BOND_ATTR_AD_ACTOR_SYS_PRIO] = { "ad_actor_sys_prio", BLOBMSG_TYPE_INT32 },
94 [BOND_ATTR_AD_SELECT] = { "ad_select", BLOBMSG_TYPE_STRING },
95 [BOND_ATTR_LACP_RATE] = { "lacp_rate", BLOBMSG_TYPE_STRING },
96 [BOND_ATTR_PACKETS_PER_PORT] = { "packets_per_port", BLOBMSG_TYPE_INT32 },
97 [BOND_ATTR_LP_INTERVAL] = { "lp_interval", BLOBMSG_TYPE_INT32 },
98 [BOND_ATTR_DYNAMIC_LB] = { "dynamic_lb", BLOBMSG_TYPE_BOOL },
99 [BOND_ATTR_RESEND_IGMP] = { "resend_igmp", BLOBMSG_TYPE_INT32 },
100 [BOND_ATTR_NUM_PEER_NOTIF] = { "num_peer_notif", BLOBMSG_TYPE_INT32 },
101 [BOND_ATTR_PRIMARY] = { "primary", BLOBMSG_TYPE_STRING },
102 [BOND_ATTR_PRIMARY_RESELECT] = { "primary_reselect", BLOBMSG_TYPE_STRING },
103 [BOND_ATTR_FAILOVER_MAC] = { "failover_mac", BLOBMSG_TYPE_STRING },
104 [BOND_ATTR_MON_MODE] = { "monitor_mode", BLOBMSG_TYPE_STRING },
105 [BOND_ATTR_MON_INTERVAL] = { "monitor_interval", BLOBMSG_TYPE_INT32 },
106 [BOND_ATTR_ARP_TARGET] = { "arp_target", BLOBMSG_TYPE_ARRAY },
107 [BOND_ATTR_ARP_ALL_TARGETS] = { "arp_all_targets", BLOBMSG_TYPE_BOOL },
108 [BOND_ATTR_ARP_VALIDATE] = { "arp_validate", BLOBMSG_TYPE_STRING },
109 [BOND_ATTR_USE_CARRIER] = { "use_carrier", BLOBMSG_TYPE_BOOL },
110 [BOND_ATTR_UPDELAY] = { "updelay", BLOBMSG_TYPE_INT32 },
111 [BOND_ATTR_DOWNDELAY] = { "downdelay", BLOBMSG_TYPE_INT32 },
112 };
113
114 static const struct uci_blob_param_info bonding_attr_info[__BOND_ATTR_MAX] = {
115 [BOND_ATTR_PORTS] = { .type = BLOBMSG_TYPE_STRING },
116 [BOND_ATTR_ARP_TARGET] = { .type = BLOBMSG_TYPE_STRING },
117 };
118
119 static const struct uci_blob_param_list bonding_attr_list = {
120 .n_params = __BOND_ATTR_MAX,
121 .params = bonding_attrs,
122 .info = bonding_attr_info,
123
124 .n_next = 1,
125 .next = { &device_attr_list },
126 };
127
128 static void
129 bonding_reset_primary(struct bonding_device *bdev)
130 {
131 struct bonding_port *bp;
132
133 bdev->primary_port = NULL;
134 if (!bdev->has_macaddr)
135 bdev->dev.settings.flags &= ~DEV_OPT_MACADDR;
136
137 vlist_for_each_element(&bdev->ports, bp, node) {
138 uint8_t *macaddr;
139
140 if (!bp->present)
141 continue;
142
143 if (bdev->primary_port && !bp->set_primary)
144 continue;
145
146 bdev->primary_port = bp;
147 if (bdev->has_macaddr)
148 continue;
149
150 if (bp->dev.dev->settings.flags & DEV_OPT_MACADDR)
151 macaddr = bp->dev.dev->settings.macaddr;
152 else
153 macaddr = bp->dev.dev->orig_settings.macaddr;
154 memcpy(bdev->dev.settings.macaddr, macaddr, 6);
155 bdev->dev.settings.flags |= DEV_OPT_MACADDR;
156 }
157 }
158
159 static int
160 bonding_disable_port(struct bonding_port *bp, bool keep_dev)
161 {
162 struct bonding_device *bdev = bp->bdev;
163
164 if (!bp->present || !bp->active)
165 return 0;
166
167 bp->active = false;
168
169 system_bonding_set_port(&bdev->dev, bp->dev.dev, false, bp->set_primary);
170 if (!keep_dev)
171 device_release(&bp->dev);
172
173 return 0;
174 }
175
176 static void
177 bonding_remove_port(struct bonding_port *bp)
178 {
179 struct bonding_device *bdev = bp->bdev;
180
181 if (!bp->present)
182 return;
183
184 if (bdev->dev.active)
185 bonding_disable_port(bp, false);
186
187 bp->present = false;
188 bp->bdev->n_present--;
189
190 if (bp == bdev->primary_port)
191 bonding_reset_primary(bdev);
192
193 bdev->force_active = false;
194 if (bdev->n_present == 0)
195 device_set_present(&bdev->dev, false);
196 }
197
198 static int
199 bonding_set_active(struct bonding_device *bdev, bool active)
200 {
201 int ret;
202
203 if (bdev->active == active)
204 return 0;
205
206 ret = system_bonding_set_device(&bdev->dev, active ? &bdev->config : NULL);
207 if (ret < 0)
208 return ret;
209
210 bdev->active = active;
211 return 0;
212 }
213
214 static int
215 bonding_enable_port(struct bonding_port *bp)
216 {
217 struct bonding_device *bdev = bp->bdev;
218 struct device *dev;
219 int ret;
220
221 if (!bp->present)
222 return 0;
223
224 /* Disable IPv6 for bonding ports */
225 if (!(bp->dev.dev->settings.flags & DEV_OPT_IPV6)) {
226 bp->dev.dev->settings.ipv6 = 0;
227 bp->dev.dev->settings.flags |= DEV_OPT_IPV6;
228 }
229
230 ret = device_claim(&bp->dev);
231 if (ret < 0)
232 return ret;
233
234 ret = bonding_set_active(bdev, true);
235 if (ret)
236 goto release;
237
238 dev = bp->dev.dev;
239 if (dev->settings.auth && !dev->auth_status)
240 return -1;
241
242 if (bp->active)
243 return 0;
244
245 ret = system_bonding_set_port(&bdev->dev, bp->dev.dev, true, bp->set_primary);
246 if (ret < 0) {
247 D(DEVICE, "Bonding port %s could not be added\n", bp->dev.dev->ifname);
248 goto error;
249 }
250
251 bp->active = true;
252 device_set_present(&bdev->dev, true);
253
254 return 0;
255
256 error:
257 bdev->n_failed++;
258 bp->present = false;
259 bdev->n_present--;
260 release:
261 device_release(&bp->dev);
262
263 return ret;
264 }
265
266 static void
267 bonding_port_cb(struct device_user *dep, enum device_event ev)
268 {
269 struct bonding_port *bp = container_of(dep, struct bonding_port, dev);
270 struct bonding_device *bdev = bp->bdev;
271 struct device *dev = dep->dev;
272
273 switch (ev) {
274 case DEV_EVENT_ADD:
275 if (bp->present)
276 break;
277
278 bp->present = true;
279 bdev->n_present++;
280
281 if (bdev->n_present == 1)
282 device_set_present(&bdev->dev, true);
283 fallthrough;
284 case DEV_EVENT_AUTH_UP:
285 if (!bdev->dev.active)
286 break;
287
288 if (bonding_enable_port(bp))
289 break;
290
291 /*
292 * Adding a bonding port can overwrite the bonding device mtu
293 * in the kernel, apply the bonding settings in case the
294 * bonding device mtu is set
295 */
296 system_if_apply_settings(&bdev->dev, &bdev->dev.settings,
297 DEV_OPT_MTU | DEV_OPT_MTU6);
298 break;
299 case DEV_EVENT_LINK_DOWN:
300 if (!dev->settings.auth)
301 break;
302
303 bonding_disable_port(bp, true);
304 break;
305 case DEV_EVENT_REMOVE:
306 if (dep->hotplug && !dev->sys_present) {
307 vlist_delete(&bdev->ports, &bp->node);
308 return;
309 }
310
311 if (bp->present)
312 bonding_remove_port(bp);
313
314 break;
315 default:
316 return;
317 }
318 }
319
320 static struct bonding_port *
321 bonding_create_port(struct bonding_device *bdev, const char *name,
322 struct device *dev, bool hotplug)
323 {
324 struct bonding_port *bp;
325
326 bp = calloc(1, sizeof(*bp) + strlen(name) + 1);
327 if (!bp)
328 return NULL;
329
330 bp->bdev = bdev;
331 bp->dev.cb = bonding_port_cb;
332 bp->dev.hotplug = hotplug;
333 strcpy(bp->name, name);
334 bp->dev.dev = dev;
335 vlist_add(&bdev->ports, &bp->node, bp->name);
336 /*
337 * Need to look up the bonding port again as the above
338 * created pointer will be freed in case the bonding port
339 * already existed
340 */
341 if (!hotplug)
342 return bp;
343
344 bp = vlist_find(&bdev->ports, name, bp, node);
345 if (bp)
346 bp->node.version = -1;
347
348 return bp;
349 }
350
351 static void
352 bonding_config_init(struct device *dev)
353 {
354 struct bonding_device *bdev;
355 struct blob_attr *cur;
356 size_t rem;
357
358 bdev = container_of(dev, struct bonding_device, dev);
359
360 bdev->n_failed = 0;
361
362 vlist_update(&bdev->ports);
363 blobmsg_for_each_attr(cur, bdev->port_list, rem) {
364 const char *name = blobmsg_get_string(cur);
365
366 dev = device_get(name, true);
367 if (!dev)
368 continue;
369
370 bonding_create_port(bdev, name, dev, false);
371 }
372 vlist_flush(&bdev->ports);
373
374 if (bdev->n_failed)
375 uloop_timeout_set(&bdev->retry, 100);
376 }
377
378 static void
379 bonding_apply_settings(struct bonding_device *bdev, struct blob_attr **tb)
380 {
381 struct bonding_config *cfg = &bdev->config;
382 struct blob_attr *cur;
383
384 /* defaults */
385 memset(cfg, 0, sizeof(*cfg));
386 cfg->resend_igmp = 1;
387 cfg->ad_actor_sys_prio = 65535;
388 cfg->lp_interval = 1;
389 cfg->num_peer_notif = 1;
390
391 #define cfg_item(_type, _field, _attr) \
392 do { \
393 if ((cur = tb[BOND_ATTR_##_attr]) != NULL) \
394 cfg->_field = blobmsg_get_##_type(cur); \
395 } while (0)
396
397 if ((cur = tb[BOND_ATTR_POLICY]) != NULL) {
398 const char *policy = blobmsg_get_string(cur);
399 size_t i;
400
401 for (i = 0; i < ARRAY_SIZE(bonding_policy_str); i++) {
402 if (strcmp(policy, bonding_policy_str[i]) != 0)
403 continue;
404
405 cfg->policy = i;
406 break;
407 }
408 }
409
410 cfg_item(string, xmit_hash_policy, XMIT_HASH_POLICY);
411 cfg_item(bool, all_ports_active, ALL_PORTS_ACTIVE);
412 cfg_item(u32, min_links, MIN_LINKS);
413 cfg_item(string, ad_actor_system, AD_ACTOR_SYSTEM);
414 cfg_item(u32, ad_actor_sys_prio, AD_ACTOR_SYS_PRIO);
415 cfg_item(string, ad_select, AD_SELECT);
416 cfg_item(string, lacp_rate, LACP_RATE);
417 cfg_item(u32, packets_per_port, PACKETS_PER_PORT);
418 cfg_item(u32, lp_interval, LP_INTERVAL);
419 cfg_item(bool, dynamic_lb, DYNAMIC_LB);
420 cfg_item(u32, resend_igmp, RESEND_IGMP);
421 cfg_item(u32, num_peer_notif, NUM_PEER_NOTIF);
422 cfg_item(string, primary, PRIMARY);
423 cfg_item(string, primary_reselect, PRIMARY_RESELECT);
424 cfg_item(string, failover_mac, FAILOVER_MAC);
425 cfg_item(u32, monitor_interval, MON_INTERVAL);
426 cfg_item(bool, arp_all_targets, ARP_ALL_TARGETS);
427 cfg_item(string, arp_validate, ARP_VALIDATE);
428 cfg_item(bool, use_carrier, USE_CARRIER);
429 cfg_item(u32, updelay, UPDELAY);
430 cfg_item(u32, downdelay, DOWNDELAY);
431
432 if ((cur = tb[BOND_ATTR_MON_MODE]) != NULL &&
433 !strcmp(blobmsg_get_string(cur), "arp"))
434 cfg->monitor_arp = true;
435 cfg->arp_target = tb[BOND_ATTR_ARP_TARGET];
436 #undef cfg_item
437 }
438
439 static enum dev_change_type
440 bonding_reload(struct device *dev, struct blob_attr *attr)
441 {
442 struct blob_attr *tb_dev[__DEV_ATTR_MAX];
443 struct blob_attr *tb_b[__BOND_ATTR_MAX];
444 enum dev_change_type ret = DEV_CONFIG_APPLIED;
445 unsigned long diff[2] = {};
446 struct bonding_device *bdev;
447
448 BUILD_BUG_ON(sizeof(diff[0]) < __BOND_ATTR_MAX / 8);
449 BUILD_BUG_ON(sizeof(diff) < __DEV_ATTR_MAX / 8);
450
451 bdev = container_of(dev, struct bonding_device, dev);
452 attr = blob_memdup(attr);
453
454 blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, tb_dev,
455 blob_data(attr), blob_len(attr));
456 blobmsg_parse(bonding_attrs, __BOND_ATTR_MAX, tb_b,
457 blob_data(attr), blob_len(attr));
458
459 bdev->has_macaddr = tb_dev[DEV_ATTR_MACADDR];
460 if (bdev->primary_port && !bdev->primary_port->set_primary &&
461 tb_dev[DEV_ATTR_MACADDR])
462 bdev->primary_port = NULL;
463
464 bdev->port_list = tb_b[BOND_ATTR_PORTS];
465 device_init_settings(dev, tb_dev);
466 bonding_apply_settings(bdev, tb_b);
467
468 if (bdev->config_data) {
469 struct blob_attr *otb_dev[__DEV_ATTR_MAX];
470 struct blob_attr *otb_b[__BOND_ATTR_MAX];
471
472 blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, otb_dev,
473 blob_data(bdev->config_data), blob_len(bdev->config_data));
474
475 uci_blob_diff(tb_dev, otb_dev, &device_attr_list, diff);
476 if (diff[0] | diff[1])
477 ret = DEV_CONFIG_RESTART;
478
479 blobmsg_parse(bonding_attrs, __BOND_ATTR_MAX, otb_b,
480 blob_data(bdev->config_data), blob_len(bdev->config_data));
481
482 diff[0] = 0;
483 uci_blob_diff(tb_b, otb_b, &bonding_attr_list, diff);
484 if (diff[0] & ~(1 << BOND_ATTR_PORTS))
485 ret = DEV_CONFIG_RESTART;
486
487 bonding_config_init(dev);
488 }
489
490 free(bdev->config_data);
491 bdev->config_data = attr;
492
493 return ret;
494 }
495
496 static int
497 bonding_hotplug_add(struct device *dev, struct device *port, struct blob_attr *vlan)
498 {
499 struct bonding_device *bdev = container_of(dev, struct bonding_device, dev);
500 struct bonding_port *bp;
501
502 bp = vlist_find(&bdev->ports, port->ifname, bp, node);
503 if (!bp)
504 bonding_create_port(bdev, port->ifname, port, true);
505
506 return 0;
507 }
508
509 static int
510 bonding_hotplug_del(struct device *dev, struct device *port, struct blob_attr *vlan)
511 {
512 struct bonding_device *bdev = container_of(dev, struct bonding_device, dev);
513 struct bonding_port *bp;
514
515 bp = vlist_find(&bdev->ports, port->ifname, bp, node);
516 if (!bp)
517 return UBUS_STATUS_NOT_FOUND;
518
519 if (bp->dev.hotplug)
520 vlist_delete(&bdev->ports, &bp->node);
521
522 return 0;
523 }
524
525 static int
526 bonding_hotplug_prepare(struct device *dev, struct device **bonding_dev)
527 {
528 struct bonding_device *bdev;
529
530 if (bonding_dev)
531 *bonding_dev = dev;
532
533 bdev = container_of(dev, struct bonding_device, dev);
534 bdev->force_active = true;
535 device_set_present(&bdev->dev, true);
536
537 return 0;
538 }
539
540 static void
541 bonding_retry_ports(struct uloop_timeout *timeout)
542 {
543 struct bonding_device *bdev = container_of(timeout, struct bonding_device, retry);
544 struct bonding_port *bp;
545
546 bdev->n_failed = 0;
547 vlist_for_each_element(&bdev->ports, bp, node) {
548 if (bp->present)
549 continue;
550
551 if (!bp->dev.dev->present)
552 continue;
553
554 bp->present = true;
555 bdev->n_present++;
556 bonding_enable_port(bp);
557 }
558 }
559
560
561 static void
562 bonding_free_port(struct bonding_port *bp)
563 {
564 struct device *dev = bp->dev.dev;
565
566 bonding_remove_port(bp);
567
568 device_remove_user(&bp->dev);
569
570 /*
571 * When reloading the config and moving a device from one master to
572 * another, the other master may have tried to claim this device
573 * before it was removed here.
574 * Ensure that claiming the device is retried by toggling its present
575 * state
576 */
577 if (dev->present) {
578 device_set_present(dev, false);
579 device_set_present(dev, true);
580 }
581
582 free(bp);
583 }
584
585 static void
586 bonding_port_update(struct vlist_tree *tree, struct vlist_node *node_new,
587 struct vlist_node *node_old)
588 {
589 struct bonding_port *bp;
590 struct device *dev;
591
592 if (node_new) {
593 bp = container_of(node_new, struct bonding_port, node);
594
595 if (node_old) {
596 free(bp);
597 return;
598 }
599
600 dev = bp->dev.dev;
601 bp->dev.dev = NULL;
602 device_add_user(&bp->dev, dev);
603 }
604
605
606 if (node_old) {
607 bp = container_of(node_old, struct bonding_port, node);
608 bonding_free_port(bp);
609 }
610 }
611
612 static int
613 bonding_set_down(struct bonding_device *bdev)
614 {
615 struct bonding_port *bp;
616
617 bdev->set_state(&bdev->dev, false);
618
619 vlist_for_each_element(&bdev->ports, bp, node)
620 bonding_disable_port(bp, false);
621
622 bonding_set_active(bdev, false);
623
624 return 0;
625 }
626
627 static int
628 bonding_set_up(struct bonding_device *bdev)
629 {
630 struct bonding_port *bp;
631 int ret;
632
633 if (!bdev->n_present) {
634 if (!bdev->force_active)
635 return -ENOENT;
636
637 ret = bonding_set_active(bdev, true);
638 if (ret)
639 return ret;
640 }
641
642 bdev->n_failed = 0;
643 vlist_for_each_element(&bdev->ports, bp, node)
644 bonding_enable_port(bp);
645 if (bdev->n_failed)
646 uloop_timeout_set(&bdev->retry, 100);
647
648 if (!bdev->force_active && !bdev->n_present) {
649 /* initialization of all port interfaces failed */
650 bonding_set_active(bdev, false);
651 device_set_present(&bdev->dev, false);
652 return -ENOENT;
653 }
654
655 bonding_reset_primary(bdev);
656 ret = bdev->set_state(&bdev->dev, true);
657 if (ret < 0)
658 bonding_set_down(bdev);
659
660 return ret;
661 }
662
663 static int
664 bonding_set_state(struct device *dev, bool up)
665 {
666 struct bonding_device *bdev;
667
668 bdev = container_of(dev, struct bonding_device, dev);
669
670 if (up)
671 return bonding_set_up(bdev);
672 else
673 return bonding_set_down(bdev);
674 }
675
676 static struct device *
677 bonding_create(const char *name, struct device_type *devtype,
678 struct blob_attr *attr)
679 {
680 static const struct device_hotplug_ops bonding_ops = {
681 .prepare = bonding_hotplug_prepare,
682 .add = bonding_hotplug_add,
683 .del = bonding_hotplug_del
684 };
685 struct bonding_device *bdev;
686 struct device *dev = NULL;
687
688 bdev = calloc(1, sizeof(*bdev));
689 if (!bdev)
690 return NULL;
691
692 dev = &bdev->dev;
693
694 if (device_init(dev, devtype, name) < 0) {
695 device_cleanup(dev);
696 free(bdev);
697 return NULL;
698 }
699
700 dev->config_pending = true;
701 bdev->retry.cb = bonding_retry_ports;
702
703 bdev->set_state = dev->set_state;
704 dev->set_state = bonding_set_state;
705
706 dev->hotplug_ops = &bonding_ops;
707
708 vlist_init(&bdev->ports, avl_strcmp, bonding_port_update);
709 bdev->ports.keep_old = true;
710
711 bonding_reload(dev, attr);
712
713 return dev;
714 }
715
716 static void
717 bonding_free(struct device *dev)
718 {
719 struct bonding_device *bdev;
720
721 bdev = container_of(dev, struct bonding_device, dev);
722 vlist_flush_all(&bdev->ports);
723 free(bdev->config_data);
724 free(bdev);
725 }
726
727 static struct device_type bonding_device_type = {
728 .name = "bonding",
729 .config_params = &bonding_attr_list,
730
731 .bridge_capability = true,
732
733 .create = bonding_create,
734 .config_init = bonding_config_init,
735 .reload = bonding_reload,
736 .free = bonding_free,
737 };
738
739 static void __init bonding_device_type_init(void)
740 {
741 device_type_add(&bonding_device_type);
742 }