1 /* 2 * netifd - network interface daemon 3 * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org> 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 #define _GNU_SOURCE 15 #include <string.h> 16 #include <stdlib.h> 17 #include <stdio.h> 18 19 #include <uci.h> 20 21 #include <libubox/blobmsg_json.h> 22 23 #include "netifd.h" 24 #include "interface.h" 25 #include "interface-ip.h" 26 #include "iprule.h" 27 #include "proto.h" 28 #include "config.h" 29 #include "ubus.h" 30 #include "system.h" 31 #include "ucode.h" 32 33 bool config_init = false; 34 35 static struct uci_context *uci_ctx; 36 static struct uci_package *uci_network; 37 static struct blob_attr *board_netdevs; 38 static struct blob_buf b; 39 static LIST_HEAD(config_vlans); 40 static LIST_HEAD(config_ifaces); 41 42 struct vlan_config_entry { 43 struct list_head list; 44 struct blob_attr *data; 45 char name[]; 46 }; 47 48 static bool 49 config_bridge_has_vlans(const char *br_name) 50 { 51 struct vlan_config_entry *e; 52 53 list_for_each_entry(e, &config_vlans, list) 54 if (!strcmp(e->name, br_name)) 55 return true; 56 57 return false; 58 } 59 60 static void 61 config_fixup_bridge_var(struct uci_section *s, const char *name, const char *val) 62 { 63 struct uci_ptr ptr = { 64 .p = s->package, 65 .s = s, 66 .option = name, 67 .value = val, 68 }; 69 70 uci_lookup_ptr(uci_ctx, &ptr, NULL, false); 71 if (ptr.o) 72 return; 73 74 uci_set(uci_ctx, &ptr); 75 } 76 77 /** 78 * config_fixup_bridge_ports - translate deprecated configs 79 * 80 * Old configs used "ifname" option for specifying bridge ports. For backward 81 * compatibility translate it into the new "ports" option. 82 */ 83 static void config_fixup_bridge_ports(struct uci_section *s) 84 { 85 struct uci_ptr ptr = { 86 .p = s->package, 87 .s = s, 88 .option = "ifname", 89 }; 90 91 if (uci_lookup_option(uci_ctx, s, "ports")) 92 return; 93 94 uci_lookup_ptr(uci_ctx, &ptr, NULL, false); 95 if (!ptr.o) 96 return; 97 98 ptr.value = "ports"; 99 uci_rename(uci_ctx, &ptr); 100 } 101 102 static void 103 config_fixup_bridge_vlan_filtering(struct uci_section *s, const char *name) 104 { 105 bool has_vlans = config_bridge_has_vlans(name); 106 107 config_fixup_bridge_var(s, "__has_vlans", has_vlans ? "1" : ""); 108 109 if (!has_vlans) 110 return; 111 112 config_fixup_bridge_var(s, "vlan_filtering", "1"); 113 } 114 115 static int 116 config_parse_bridge_interface(struct uci_section *s, struct device_type *devtype) 117 { 118 char *name; 119 120 name = alloca(strlen(s->e.name) + strlen(devtype->name_prefix) + 2); 121 sprintf(name, "%s-%s", devtype->name_prefix, s->e.name); 122 blobmsg_add_string(&b, "name", name); 123 124 config_fixup_bridge_ports(s); 125 config_fixup_bridge_vlan_filtering(s, name); 126 uci_to_blob(&b, s, devtype->config_params); 127 if (!device_create(name, devtype, b.head)) { 128 D(INTERFACE, "Failed to create '%s' device for interface '%s'", 129 devtype->name, s->e.name); 130 } 131 132 blob_buf_init(&b, 0); 133 blobmsg_add_string(&b, "ifname", name); 134 return 0; 135 } 136 137 static void 138 config_parse_interface(struct uci_section *s, bool alias) 139 { 140 struct interface *iface; 141 const char *type = NULL, *disabled; 142 struct blob_attr *config; 143 bool bridge = false; 144 struct device_type *devtype = NULL; 145 146 disabled = uci_lookup_option_string(uci_ctx, s, "disabled"); 147 if (disabled && !strcmp(disabled, "1")) 148 return; 149 150 blob_buf_init(&b, 0); 151 152 if (!alias) 153 type = uci_lookup_option_string(uci_ctx, s, "type"); 154 155 if (type) 156 devtype = device_type_get(type); 157 158 if (devtype && devtype->bridge_capability) { 159 if (config_parse_bridge_interface(s, devtype)) 160 return; 161 162 bridge = true; 163 } 164 165 uci_to_blob(&b, s, &interface_attr_list); 166 167 iface = interface_alloc(s->e.name, b.head, false); 168 if (!iface) 169 return; 170 171 if (iface->proto_handler && iface->proto_handler->config_params) 172 uci_to_blob(&b, s, iface->proto_handler->config_params); 173 174 if (!bridge && uci_to_blob(&b, s, simple_device_type.config_params)) 175 iface->device_config = true; 176 177 config = blob_memdup(b.head); 178 if (!config) 179 goto error; 180 181 if (alias) { 182 if (!interface_add_alias(iface, config)) 183 goto error_free_config; 184 } else { 185 if (!interface_add(iface, config)) 186 goto error_free_config; 187 } 188 return; 189 190 error_free_config: 191 free(config); 192 error: 193 interface_free(iface); 194 } 195 196 static void 197 config_parse_route(struct uci_section *s, bool v6) 198 { 199 void *route; 200 201 blob_buf_init(&b, 0); 202 route = blobmsg_open_array(&b, "route"); 203 uci_to_blob(&b, s, &route_attr_list); 204 blobmsg_close_array(&b, route); 205 interface_ip_add_route(NULL, blob_data(b.head), v6); 206 } 207 208 static void 209 config_parse_neighbor(struct uci_section *s, bool v6) 210 { 211 void *neighbor; 212 blob_buf_init(&b,0); 213 neighbor = blobmsg_open_array(&b, "neighbor"); 214 uci_to_blob(&b,s, &neighbor_attr_list); 215 blobmsg_close_array(&b, neighbor); 216 interface_ip_add_neighbor(NULL, blob_data(b.head), v6); 217 } 218 219 static void 220 config_parse_rule(struct uci_section *s, bool v6) 221 { 222 void *rule; 223 224 blob_buf_init(&b, 0); 225 rule = blobmsg_open_array(&b, "rule"); 226 uci_to_blob(&b, s, &rule_attr_list); 227 blobmsg_close_array(&b, rule); 228 iprule_add(blob_data(b.head), v6); 229 } 230 231 static void 232 config_insert_vlan_entry(const char *name, struct blob_attr *data) 233 { 234 struct vlan_config_entry *e; 235 struct blob_attr *attrbuf; 236 237 e = calloc_a(sizeof(*e) + strlen(name) + 1, 238 &attrbuf, blob_pad_len(data)); 239 e->data = memcpy(attrbuf, data, blob_pad_len(data)); 240 strcpy(e->name, name); 241 list_add_tail(&e->list, &config_vlans); 242 } 243 244 static void 245 config_device_add(const char *name, struct device_type *devtype, 246 struct blob_attr *config) 247 { 248 struct device *dev; 249 250 if (devtype) { 251 dev = device_create(name, devtype, config); 252 } else { 253 dev = device_get(name, 1); 254 if (!dev) 255 return; 256 257 dev->current_config = true; 258 device_apply_config(dev, dev->type, config); 259 } 260 261 dev->default_config = false; 262 } 263 264 static void 265 config_procd_device_cb(struct blob_attr *data) 266 { 267 static const struct blobmsg_policy policy = 268 { "type", BLOBMSG_TYPE_STRING }; 269 const char *name = blobmsg_name(data); 270 struct device_type *devtype = NULL; 271 struct blob_attr *attr; 272 273 blobmsg_parse_attr(&policy, 1, &attr, data); 274 if (attr) { 275 const char *type_name = blobmsg_get_string(attr); 276 277 if (!strcmp(type_name, "bridge")) 278 return; 279 280 devtype = device_type_get(type_name); 281 if (!devtype) 282 return; 283 } 284 285 config_device_add(name, devtype, data); 286 } 287 288 static void 289 config_procd_bridge_cb(struct blob_attr *data) 290 { 291 enum { 292 PROCD_DEV_ATTR_TYPE, 293 PROCD_DEV_ATTR_VLANS, 294 __PROCD_DEV_ATTR_MAX 295 }; 296 static const struct blobmsg_policy policy[] = { 297 [PROCD_DEV_ATTR_TYPE] = { "type", BLOBMSG_TYPE_STRING }, 298 [PROCD_DEV_ATTR_VLANS] = { "vlans", BLOBMSG_TYPE_ARRAY }, 299 }; 300 const char *name = blobmsg_name(data); 301 struct blob_attr *tb[__PROCD_DEV_ATTR_MAX], *attr, *cur; 302 struct device_type *devtype; 303 struct device *dev; 304 int len = 0; 305 size_t rem; 306 307 blobmsg_parse_attr(policy, ARRAY_SIZE(policy), tb, data); 308 if (!tb[PROCD_DEV_ATTR_TYPE] || 309 strcmp(blobmsg_get_string(tb[PROCD_DEV_ATTR_TYPE]), "bridge") != 0) 310 return; 311 312 devtype = device_type_get("bridge"); 313 if (!devtype) 314 return; 315 316 attr = tb[PROCD_DEV_ATTR_VLANS]; 317 if (attr) 318 len = blobmsg_check_array(attr, BLOBMSG_TYPE_TABLE); 319 if (len < 0) 320 return; 321 322 if (len > 0 || config_bridge_has_vlans(name)) { 323 blob_buf_init(&b, 0); 324 blob_put_raw(&b, blobmsg_data(data), blobmsg_len(data)); 325 blobmsg_add_u8(&b, "vlan_filtering", 1); 326 data = b.head; 327 } 328 329 dev = device_create(name, devtype, data); 330 if (!dev || !dev->vlans.update || !len) 331 return; 332 333 blobmsg_for_each_attr(cur, attr, rem) 334 config_insert_vlan_entry(name, cur); 335 } 336 337 static void 338 config_init_devices(bool bridge) 339 { 340 struct uci_element *e; 341 342 uci_foreach_element(&uci_network->sections, e) { 343 const struct uci_blob_param_list *params = NULL; 344 struct uci_section *s = uci_to_section(e); 345 struct device_type *devtype = NULL; 346 const char *type, *name; 347 348 if (strcmp(s->type, "device") != 0) 349 continue; 350 351 name = uci_lookup_option_string(uci_ctx, s, "name"); 352 if (!name) 353 continue; 354 355 type = uci_lookup_option_string(uci_ctx, s, "type"); 356 if (type) 357 devtype = device_type_get(type); 358 359 if (bridge != (devtype && devtype->bridge_capability)) 360 continue; 361 362 if (devtype) 363 params = devtype->config_params; 364 if (!params) 365 params = simple_device_type.config_params; 366 367 if (devtype && devtype->bridge_capability) { 368 config_fixup_bridge_ports(s); 369 config_fixup_bridge_vlan_filtering(s, name); 370 } 371 372 blob_buf_init(&b, 0); 373 uci_to_blob(&b, s, params); 374 config_device_add(name, devtype, b.head); 375 } 376 377 netifd_ubus_get_procd_data("network-device", 378 bridge ? config_procd_bridge_cb : config_procd_device_cb); 379 } 380 381 enum { 382 BRVLAN_ATTR_VID, 383 BRVLAN_ATTR_LOCAL, 384 BRVLAN_ATTR_PORTS, 385 BRVLAN_ATTR_ALIAS, 386 __BRVLAN_ATTR_MAX, 387 }; 388 389 static const struct blobmsg_policy vlan_attrs[__BRVLAN_ATTR_MAX] = { 390 [BRVLAN_ATTR_VID] = { "vlan", BLOBMSG_TYPE_INT32 }, 391 [BRVLAN_ATTR_LOCAL] = { "local", BLOBMSG_TYPE_BOOL }, 392 [BRVLAN_ATTR_PORTS] = { "ports", BLOBMSG_TYPE_ARRAY }, 393 [BRVLAN_ATTR_ALIAS] = { "alias", BLOBMSG_TYPE_ARRAY }, 394 }; 395 396 static void 397 config_init_vlan_entry(struct vlan_config_entry *e) 398 { 399 struct blob_attr *tb[__BRVLAN_ATTR_MAX]; 400 struct blob_attr *cur; 401 struct bridge_vlan_port *port; 402 struct bridge_vlan *vlan; 403 struct device *dev; 404 unsigned int vid; 405 char *name_buf; 406 int name_len = 0; 407 int n_ports = 0; 408 size_t rem; 409 410 dev = device_get(e->name, 0); 411 if (!dev || !dev->vlans.update) 412 return; 413 414 blobmsg_parse_attr(vlan_attrs, __BRVLAN_ATTR_MAX, tb, e->data); 415 416 if (!tb[BRVLAN_ATTR_VID]) 417 return; 418 419 vid = blobmsg_get_u32(tb[BRVLAN_ATTR_VID]); 420 if (!vid || vid > 4095) 421 return; 422 423 blobmsg_for_each_attr(cur, tb[BRVLAN_ATTR_PORTS], rem) { 424 name_len += strlen(blobmsg_get_string(cur)) + 1; 425 n_ports++; 426 } 427 428 vlan = calloc(1, sizeof(*vlan) + n_ports * sizeof(*port) + name_len); 429 if (!vlan) 430 return; 431 432 vlan->vid = vid; 433 vlan->local = true; 434 if (tb[BRVLAN_ATTR_LOCAL]) 435 vlan->local = blobmsg_get_bool(tb[BRVLAN_ATTR_LOCAL]); 436 437 vlan->n_ports = n_ports; 438 vlan->ports = port = (struct bridge_vlan_port *)&vlan[1]; 439 INIT_LIST_HEAD(&vlan->hotplug_ports); 440 name_buf = (char *)&port[n_ports]; 441 442 blobmsg_for_each_attr(cur, tb[BRVLAN_ATTR_PORTS], rem) { 443 char *sep; 444 445 port->ifname = name_buf; 446 port->flags = BRVLAN_F_UNTAGGED; 447 strcpy(name_buf, blobmsg_get_string(cur)); 448 449 sep = strchr(name_buf, ':'); 450 if (sep) { 451 for (*sep = 0, sep++; *sep; sep++) 452 switch (*sep) { 453 case '*': 454 port->flags |= BRVLAN_F_PVID; 455 break; 456 case 't': 457 port->flags &= ~BRVLAN_F_UNTAGGED; 458 break; 459 } 460 } 461 462 name_buf += strlen(name_buf) + 1; 463 port++; 464 } 465 466 blobmsg_for_each_attr(cur, tb[BRVLAN_ATTR_ALIAS], rem) 467 kvlist_set(&dev->vlan_aliases, blobmsg_get_string(cur), &vid); 468 469 vlist_add(&dev->vlans, &vlan->node, &vlan->vid); 470 } 471 472 static void 473 config_load_vlan(const char *name, struct uci_section *s) 474 { 475 static const struct uci_blob_param_info vlan_attr_info[__BRVLAN_ATTR_MAX] = { 476 [BRVLAN_ATTR_PORTS] = { .type = BLOBMSG_TYPE_STRING }, 477 [BRVLAN_ATTR_ALIAS] = { .type = BLOBMSG_TYPE_STRING }, 478 }; 479 static const struct uci_blob_param_list vlan_attr_list = { 480 .n_params = __BRVLAN_ATTR_MAX, 481 .params = vlan_attrs, 482 .info = vlan_attr_info, 483 }; 484 485 blob_buf_init(&b, 0); 486 uci_to_blob(&b, s, &vlan_attr_list); 487 config_insert_vlan_entry(name, b.head); 488 } 489 490 static void 491 config_procd_vlan_cb(struct blob_attr *data) 492 { 493 static const struct blobmsg_policy policy = 494 { "device", BLOBMSG_TYPE_STRING }; 495 struct blob_attr *attr; 496 497 blobmsg_parse_attr(&policy, 1, &attr, data); 498 if (!attr) 499 return; 500 501 config_insert_vlan_entry(blobmsg_get_string(attr), data); 502 } 503 504 static void 505 config_load_vlans(void) 506 { 507 struct uci_element *e; 508 509 uci_foreach_element(&uci_network->sections, e) { 510 struct uci_section *s = uci_to_section(e); 511 const char *name; 512 513 if (strcmp(s->type, "bridge-vlan") != 0) 514 continue; 515 516 name = uci_lookup_option_string(uci_ctx, s, "device"); 517 if (!name) 518 continue; 519 520 config_load_vlan(name, s); 521 } 522 523 netifd_ubus_get_procd_data("bridge-vlan", config_procd_vlan_cb); 524 } 525 526 static void 527 config_init_vlans(void) 528 { 529 struct vlan_config_entry *e; 530 531 device_vlan_update(false); 532 while (!list_empty(&config_vlans)) { 533 e = list_first_entry(&config_vlans, struct vlan_config_entry, list); 534 list_del(&e->list); 535 config_init_vlan_entry(e); 536 free(e); 537 } 538 device_vlan_update(true); 539 } 540 541 static struct uci_package * 542 config_init_package(const char *config) 543 { 544 struct uci_context *ctx = uci_ctx; 545 struct uci_package *p = NULL; 546 547 if (!ctx) { 548 ctx = uci_alloc_context(); 549 uci_ctx = ctx; 550 551 ctx->flags &= ~UCI_FLAG_STRICT; 552 if (config_path) 553 uci_set_confdir(ctx, config_path); 554 555 #ifdef DUMMY_MODE 556 uci_set_savedir(ctx, "./tmp"); 557 #endif 558 } else { 559 p = uci_lookup_package(ctx, config); 560 if (p) 561 uci_unload(ctx, p); 562 } 563 564 if (uci_load(ctx, config, &p)) 565 return NULL; 566 567 return p; 568 } 569 570 static void 571 config_procd_interface_cb(struct blob_attr *data) 572 { 573 struct interface *iface; 574 const char *name = blobmsg_name(data); 575 576 iface = interface_alloc(name, data, false); 577 if (!iface) 578 return; 579 580 data = blob_memdup(data); 581 if (!data) { 582 interface_free(iface); 583 return; 584 } 585 586 iface->config = data; 587 list_add(&iface->node.avl.list, &config_ifaces); 588 } 589 590 static void 591 config_init_interfaces(void) 592 { 593 struct interface *iface; 594 struct uci_element *e; 595 596 uci_foreach_element(&uci_network->sections, e) { 597 struct uci_section *s = uci_to_section(e); 598 599 if (!strcmp(s->type, "interface")) 600 config_parse_interface(s, false); 601 } 602 603 uci_foreach_element(&uci_network->sections, e) { 604 struct uci_section *s = uci_to_section(e); 605 606 if (!strcmp(s->type, "alias")) 607 config_parse_interface(s, true); 608 } 609 610 netifd_ubus_get_procd_data("network-interface", config_procd_interface_cb); 611 while (!list_empty(&config_ifaces)) { 612 iface = list_first_entry(&config_ifaces, struct interface, node.avl.list); 613 list_del(&iface->node.avl.list); 614 615 if (!interface_add(iface, iface->config)) 616 interface_free(iface); 617 } 618 } 619 620 static void 621 config_init_ip(void) 622 { 623 struct interface *iface; 624 struct uci_element *e; 625 626 vlist_for_each_element(&interfaces, iface, node) 627 interface_ip_update_start(&iface->config_ip); 628 629 uci_foreach_element(&uci_network->sections, e) { 630 struct uci_section *s = uci_to_section(e); 631 632 if (!strcmp(s->type, "route")) 633 config_parse_route(s, false); 634 else if (!strcmp(s->type, "route6")) 635 config_parse_route(s, true); 636 if (!strcmp(s->type, "neighbor")) 637 config_parse_neighbor(s, false); 638 else if (!strcmp(s->type, "neighbor6")) 639 config_parse_neighbor(s, true); 640 } 641 642 vlist_for_each_element(&interfaces, iface, node) 643 interface_ip_update_complete(&iface->config_ip); 644 } 645 646 static void 647 config_init_rules(void) 648 { 649 struct uci_element *e; 650 651 iprule_update_start(); 652 653 uci_foreach_element(&uci_network->sections, e) { 654 struct uci_section *s = uci_to_section(e); 655 656 if (!strcmp(s->type, "rule")) 657 config_parse_rule(s, false); 658 else if (!strcmp(s->type, "rule6")) 659 config_parse_rule(s, true); 660 } 661 662 iprule_update_complete(); 663 } 664 665 static void 666 config_init_globals(void) 667 { 668 struct uci_section *globals = uci_lookup_section( 669 uci_ctx, uci_network, "globals"); 670 if (!globals) 671 return; 672 673 const char *ula_prefix = uci_lookup_option_string( 674 uci_ctx, globals, "ula_prefix"); 675 interface_ip_set_ula_prefix(ula_prefix); 676 677 const char *tcp_l3mdev = uci_lookup_option_string( 678 uci_ctx, globals, "tcp_l3mdev"); 679 if (tcp_l3mdev) 680 system_tcp_l3mdev(!strcmp(tcp_l3mdev, "1")); 681 682 const char *udp_l3mdev = uci_lookup_option_string( 683 uci_ctx, globals, "udp_l3mdev"); 684 if (udp_l3mdev) 685 system_udp_l3mdev(!strcmp(udp_l3mdev, "1")); 686 } 687 688 static struct blob_attr * 689 config_find_blobmsg_attr(struct blob_attr *attr, const char *name, int type) 690 { 691 struct blobmsg_policy policy = { .name = name, .type = type }; 692 struct blob_attr *cur; 693 694 blobmsg_parse_attr(&policy, 1, &cur, attr); 695 696 return cur; 697 } 698 699 struct ether_addr *config_get_default_macaddr(const char *ifname) 700 { 701 struct blob_attr *cur; 702 703 if (!board_netdevs) 704 return NULL; 705 706 cur = config_find_blobmsg_attr(board_netdevs, ifname, BLOBMSG_TYPE_TABLE); 707 if (!cur) 708 return NULL; 709 710 cur = config_find_blobmsg_attr(cur, "macaddr", BLOBMSG_TYPE_STRING); 711 if (!cur) 712 return NULL; 713 714 return ether_aton(blobmsg_get_string(cur)); 715 } 716 717 int config_get_default_gro(const char *ifname) 718 { 719 struct blob_attr *cur; 720 721 if (!board_netdevs) 722 return -1; 723 724 cur = config_find_blobmsg_attr(board_netdevs, ifname, BLOBMSG_TYPE_TABLE); 725 if (!cur) 726 return -1; 727 728 cur = config_find_blobmsg_attr(cur, "gro", BLOBMSG_TYPE_BOOL); 729 if (!cur) 730 return -1; 731 732 return blobmsg_get_bool(cur); 733 } 734 735 const char *config_get_default_conduit(const char *ifname) 736 { 737 struct blob_attr *cur; 738 739 if (!board_netdevs) 740 return NULL; 741 742 cur = config_find_blobmsg_attr(board_netdevs, ifname, BLOBMSG_TYPE_TABLE); 743 if (!cur) 744 return NULL; 745 746 cur = config_find_blobmsg_attr(cur, "conduit", BLOBMSG_TYPE_STRING); 747 if (!cur) 748 return NULL; 749 750 return blobmsg_get_string(cur); 751 } 752 753 static void 754 config_init_board(void) 755 { 756 struct blob_attr *cur; 757 758 blob_buf_init(&b, 0); 759 760 if (!blobmsg_add_json_from_file(&b, DEFAULT_BOARD_JSON)) 761 return; 762 763 free(board_netdevs); 764 board_netdevs = NULL; 765 766 cur = config_find_blobmsg_attr(b.head, "network_device", 767 BLOBMSG_TYPE_TABLE); 768 if (!cur) 769 return; 770 771 board_netdevs = blob_memdup(cur); 772 } 773 774 int 775 config_init_all(void) 776 { 777 int ret = 0; 778 char *err; 779 780 uci_network = config_init_package("network"); 781 if (!uci_network) { 782 uci_get_errorstr(uci_ctx, &err, NULL); 783 netifd_log_message(L_CRIT, "Failed to load network config (%s)\n", err); 784 free(err); 785 return -1; 786 } 787 788 config_init_board(); 789 790 vlist_update(&interfaces); 791 config_init = true; 792 793 device_reset_config(); 794 config_load_vlans(); 795 config_init_devices(true); 796 config_init_vlans(); 797 config_init_devices(false); 798 config_init_interfaces(); 799 config_init_ip(); 800 config_init_rules(); 801 config_init_globals(); 802 netifd_ucode_config_load(false); 803 804 config_init = false; 805 806 device_reset_old(); 807 device_init_pending(); 808 vlist_flush(&interfaces); 809 interface_refresh_assignments(false); 810 interface_start_pending(); 811 netifd_ucode_config_load(true); 812 813 return ret; 814 } 815
This page was automatically generated by LXR 0.3.1. • OpenWrt