wireless: fix index for stations
[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) {
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 int 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 int 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;
446 struct bonding_device *bdev;
447
448 BUILD_BUG_ON(sizeof(diff) < __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 diff = 0;
476 uci_blob_diff(tb_dev, otb_dev, &device_attr_list, &diff);
477 if (diff)
478 ret = DEV_CONFIG_RESTART;
479
480 blobmsg_parse(bonding_attrs, __BOND_ATTR_MAX, otb_b,
481 blob_data(bdev->config_data), blob_len(bdev->config_data));
482
483 diff = 0;
484 uci_blob_diff(tb_b, otb_b, &bonding_attr_list, &diff);
485 if (diff & ~(1 << BOND_ATTR_PORTS))
486 ret = DEV_CONFIG_RESTART;
487
488 bonding_config_init(dev);
489 }
490
491 free(bdev->config_data);
492 bdev->config_data = attr;
493
494 return ret;
495 }
496
497 static int
498 bonding_hotplug_add(struct device *dev, struct device *port, struct blob_attr *vlan)
499 {
500 struct bonding_device *bdev = container_of(dev, struct bonding_device, dev);
501 struct bonding_port *bp;
502
503 bp = vlist_find(&bdev->ports, port->ifname, bp, node);
504 if (!bp)
505 bonding_create_port(bdev, port->ifname, port, true);
506
507 return 0;
508 }
509
510 static int
511 bonding_hotplug_del(struct device *dev, struct device *port, struct blob_attr *vlan)
512 {
513 struct bonding_device *bdev = container_of(dev, struct bonding_device, dev);
514 struct bonding_port *bp;
515
516 bp = vlist_find(&bdev->ports, port->ifname, bp, node);
517 if (!bp)
518 return UBUS_STATUS_NOT_FOUND;
519
520 if (bp->dev.hotplug)
521 vlist_delete(&bdev->ports, &bp->node);
522
523 return 0;
524 }
525
526 static int
527 bonding_hotplug_prepare(struct device *dev, struct device **bonding_dev)
528 {
529 struct bonding_device *bdev;
530
531 if (bonding_dev)
532 *bonding_dev = dev;
533
534 bdev = container_of(dev, struct bonding_device, dev);
535 bdev->force_active = true;
536 device_set_present(&bdev->dev, true);
537
538 return 0;
539 }
540
541 static void
542 bonding_retry_ports(struct uloop_timeout *timeout)
543 {
544 struct bonding_device *bdev = container_of(timeout, struct bonding_device, retry);
545 struct bonding_port *bp;
546
547 bdev->n_failed = 0;
548 vlist_for_each_element(&bdev->ports, bp, node) {
549 if (bp->present)
550 continue;
551
552 if (!bp->dev.dev->present)
553 continue;
554
555 bp->present = true;
556 bdev->n_present++;
557 bonding_enable_port(bp);
558 }
559 }
560
561
562 static void
563 bonding_free_port(struct bonding_port *bp)
564 {
565 struct device *dev = bp->dev.dev;
566
567 bonding_remove_port(bp);
568
569 device_remove_user(&bp->dev);
570
571 /*
572 * When reloading the config and moving a device from one master to
573 * another, the other master may have tried to claim this device
574 * before it was removed here.
575 * Ensure that claiming the device is retried by toggling its present
576 * state
577 */
578 if (dev->present) {
579 device_set_present(dev, false);
580 device_set_present(dev, true);
581 }
582
583 free(bp);
584 }
585
586 static void
587 bonding_port_update(struct vlist_tree *tree, struct vlist_node *node_new,
588 struct vlist_node *node_old)
589 {
590 struct bonding_port *bp;
591 struct device *dev;
592
593 if (node_new) {
594 bp = container_of(node_new, struct bonding_port, node);
595
596 if (node_old) {
597 free(bp);
598 return;
599 }
600
601 dev = bp->dev.dev;
602 bp->dev.dev = NULL;
603 device_add_user(&bp->dev, dev);
604 }
605
606
607 if (node_old) {
608 bp = container_of(node_old, struct bonding_port, node);
609 bonding_free_port(bp);
610 }
611 }
612
613 static int
614 bonding_set_down(struct bonding_device *bdev)
615 {
616 struct bonding_port *bp;
617
618 bdev->set_state(&bdev->dev, false);
619
620 vlist_for_each_element(&bdev->ports, bp, node)
621 bonding_disable_port(bp, false);
622
623 bonding_set_active(bdev, false);
624
625 return 0;
626 }
627
628 static int
629 bonding_set_up(struct bonding_device *bdev)
630 {
631 struct bonding_port *bp;
632 int ret;
633
634 if (!bdev->n_present) {
635 if (!bdev->force_active)
636 return -ENOENT;
637
638 ret = bonding_set_active(bdev, true);
639 if (ret)
640 return ret;
641 }
642
643 bdev->n_failed = 0;
644 vlist_for_each_element(&bdev->ports, bp, node)
645 bonding_enable_port(bp);
646 if (bdev->n_failed)
647 uloop_timeout_set(&bdev->retry, 100);
648
649 if (!bdev->force_active && !bdev->n_present) {
650 /* initialization of all port interfaces failed */
651 bonding_set_active(bdev, false);
652 device_set_present(&bdev->dev, false);
653 return -ENOENT;
654 }
655
656 bonding_reset_primary(bdev);
657 ret = bdev->set_state(&bdev->dev, true);
658 if (ret < 0)
659 bonding_set_down(bdev);
660
661 return ret;
662 }
663
664 static int
665 bonding_set_state(struct device *dev, bool up)
666 {
667 struct bonding_device *bdev;
668
669 bdev = container_of(dev, struct bonding_device, dev);
670
671 if (up)
672 return bonding_set_up(bdev);
673 else
674 return bonding_set_down(bdev);
675 }
676
677 static struct device *
678 bonding_create(const char *name, struct device_type *devtype,
679 struct blob_attr *attr)
680 {
681 static const struct device_hotplug_ops bonding_ops = {
682 .prepare = bonding_hotplug_prepare,
683 .add = bonding_hotplug_add,
684 .del = bonding_hotplug_del
685 };
686 struct bonding_device *bdev;
687 struct device *dev = NULL;
688
689 bdev = calloc(1, sizeof(*bdev));
690 if (!bdev)
691 return NULL;
692
693 dev = &bdev->dev;
694
695 if (device_init(dev, devtype, name) < 0) {
696 device_cleanup(dev);
697 free(bdev);
698 return NULL;
699 }
700
701 dev->config_pending = true;
702 bdev->retry.cb = bonding_retry_ports;
703
704 bdev->set_state = dev->set_state;
705 dev->set_state = bonding_set_state;
706
707 dev->hotplug_ops = &bonding_ops;
708
709 vlist_init(&bdev->ports, avl_strcmp, bonding_port_update);
710 bdev->ports.keep_old = true;
711
712 bonding_reload(dev, attr);
713
714 return dev;
715 }
716
717 static void
718 bonding_free(struct device *dev)
719 {
720 struct bonding_device *bdev;
721
722 bdev = container_of(dev, struct bonding_device, dev);
723 vlist_flush_all(&bdev->ports);
724 free(bdev->config_data);
725 free(bdev);
726 }
727
728 static struct device_type bonding_device_type = {
729 .name = "bonding",
730 .config_params = &bonding_attr_list,
731
732 .bridge_capability = true,
733
734 .create = bonding_create,
735 .config_init = bonding_config_init,
736 .reload = bonding_reload,
737 .free = bonding_free,
738 };
739
740 static void __init bonding_device_type_init(void)
741 {
742 device_type_add(&bonding_device_type);
743 }