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", 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 } 743
This page was automatically generated by LXR 0.3.1. • OpenWrt