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 16 #include <arpa/inet.h> 17 #include <string.h> 18 #include <stdio.h> 19 20 #include "netifd.h" 21 #include "interface.h" 22 #include "proto.h" 23 #include "ubus.h" 24 #include "system.h" 25 26 struct ubus_context *ubus_ctx = NULL; 27 static struct blob_buf b; 28 static const char *ubus_path; 29 static struct udebug_ubus udebug; 30 31 /* global object */ 32 33 static int 34 netifd_handle_restart(struct ubus_context *ctx, struct ubus_object *obj, 35 struct ubus_request_data *req, const char *method, 36 struct blob_attr *msg) 37 { 38 netifd_restart(); 39 return 0; 40 } 41 42 static int 43 netifd_handle_reload(struct ubus_context *ctx, struct ubus_object *obj, 44 struct ubus_request_data *req, const char *method, 45 struct blob_attr *msg) 46 { 47 if (netifd_reload()) 48 return UBUS_STATUS_NOT_FOUND; 49 50 return UBUS_STATUS_OK; 51 } 52 53 enum { 54 HR_TARGET, 55 HR_V6, 56 HR_INTERFACE, 57 HR_EXCLUDE, 58 __HR_MAX 59 }; 60 61 static const struct blobmsg_policy route_policy[__HR_MAX] = { 62 [HR_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING }, 63 [HR_V6] = { .name = "v6", .type = BLOBMSG_TYPE_BOOL }, 64 [HR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING }, 65 [HR_EXCLUDE] = { .name = "exclude", .type = BLOBMSG_TYPE_BOOL }, 66 }; 67 68 static int 69 netifd_add_host_route(struct ubus_context *ctx, struct ubus_object *obj, 70 struct ubus_request_data *req, const char *method, 71 struct blob_attr *msg) 72 { 73 struct blob_attr *tb[__HR_MAX]; 74 struct interface *iface = NULL; 75 union if_addr a; 76 bool v6 = false; 77 bool exclude = false; 78 79 blobmsg_parse_attr(route_policy, __HR_MAX, tb, msg); 80 if (!tb[HR_TARGET]) 81 return UBUS_STATUS_INVALID_ARGUMENT; 82 83 if (tb[HR_V6]) 84 v6 = blobmsg_get_bool(tb[HR_V6]); 85 86 if (tb[HR_EXCLUDE]) 87 exclude = blobmsg_get_bool(tb[HR_EXCLUDE]); 88 89 if (tb[HR_INTERFACE]) 90 iface = vlist_find(&interfaces, blobmsg_data(tb[HR_INTERFACE]), iface, node); 91 92 memset(&a, 0, sizeof(a)); 93 if (!inet_pton(v6 ? AF_INET6 : AF_INET, blobmsg_data(tb[HR_TARGET]), &a)) 94 return UBUS_STATUS_INVALID_ARGUMENT; 95 96 iface = interface_ip_add_target_route(&a, v6, iface, exclude); 97 if (!iface) 98 return UBUS_STATUS_NOT_FOUND; 99 100 blob_buf_init(&b, 0); 101 blobmsg_add_string(&b, "interface", iface->name); 102 ubus_send_reply(ctx, req, b.head); 103 104 return 0; 105 } 106 107 static int 108 netifd_get_proto_handlers(struct ubus_context *ctx, struct ubus_object *obj, 109 struct ubus_request_data *req, const char *method, 110 struct blob_attr *msg) 111 { 112 blob_buf_init(&b, 0); 113 proto_dump_handlers(&b); 114 ubus_send_reply(ctx, req, b.head); 115 116 return 0; 117 } 118 119 120 enum { 121 DI_NAME, 122 __DI_MAX 123 }; 124 125 static const struct blobmsg_policy dynamic_policy[__DI_MAX] = { 126 [DI_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, 127 }; 128 129 static int 130 netifd_add_dynamic(struct ubus_context *ctx, struct ubus_object *obj, 131 struct ubus_request_data *req, const char *method, 132 struct blob_attr *msg) 133 { 134 struct blob_attr *tb[__DI_MAX]; 135 struct interface *iface; 136 struct blob_attr *config; 137 138 blobmsg_parse_attr(dynamic_policy, __DI_MAX, tb, msg); 139 140 if (!tb[DI_NAME]) 141 return UBUS_STATUS_INVALID_ARGUMENT; 142 143 const char *name = blobmsg_get_string(tb[DI_NAME]); 144 145 iface = interface_alloc(name, msg, true); 146 if (!iface) 147 return UBUS_STATUS_UNKNOWN_ERROR; 148 149 config = blob_memdup(msg); 150 if (!config) 151 goto error; 152 153 if (!interface_add(iface, config)) 154 goto error_free_config; 155 156 return UBUS_STATUS_OK; 157 158 error_free_config: 159 free(config); 160 error: 161 interface_free(iface); 162 return UBUS_STATUS_UNKNOWN_ERROR; 163 } 164 165 enum { 166 NETNS_UPDOWN_JAIL, 167 NETNS_UPDOWN_START, 168 __NETNS_UPDOWN_MAX 169 }; 170 171 static const struct blobmsg_policy netns_updown_policy[__NETNS_UPDOWN_MAX] = { 172 [NETNS_UPDOWN_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_STRING }, 173 [NETNS_UPDOWN_START] = { .name = "start", .type = BLOBMSG_TYPE_BOOL }, 174 }; 175 176 static int 177 netifd_netns_updown(struct ubus_context *ctx, struct ubus_object *obj, 178 struct ubus_request_data *req, const char *method, 179 struct blob_attr *msg) 180 { 181 int target_netns_fd = ubus_request_get_caller_fd(req); 182 struct blob_attr *tb[__NETNS_UPDOWN_MAX]; 183 bool start; 184 185 if (target_netns_fd < 0) 186 return UBUS_STATUS_INVALID_ARGUMENT; 187 188 blobmsg_parse_attr(netns_updown_policy, __NETNS_UPDOWN_MAX, tb, msg); 189 190 start = tb[NETNS_UPDOWN_START] && blobmsg_get_bool(tb[NETNS_UPDOWN_START]); 191 192 if (start) { 193 if (!tb[NETNS_UPDOWN_JAIL]) { 194 close(target_netns_fd); 195 return UBUS_STATUS_INVALID_ARGUMENT; 196 } 197 198 interface_start_jail(target_netns_fd, blobmsg_get_string(tb[NETNS_UPDOWN_JAIL])); 199 } else { 200 interface_stop_jail(target_netns_fd); 201 } 202 close(target_netns_fd); 203 204 return UBUS_STATUS_OK; 205 } 206 207 static struct ubus_method main_object_methods[] = { 208 { .name = "restart", .handler = netifd_handle_restart }, 209 { .name = "reload", .handler = netifd_handle_reload }, 210 UBUS_METHOD("add_host_route", netifd_add_host_route, route_policy), 211 { .name = "get_proto_handlers", .handler = netifd_get_proto_handlers }, 212 UBUS_METHOD("add_dynamic", netifd_add_dynamic, dynamic_policy), 213 UBUS_METHOD("netns_updown", netifd_netns_updown, netns_updown_policy), 214 }; 215 216 static struct ubus_object_type main_object_type = 217 UBUS_OBJECT_TYPE("netifd", main_object_methods); 218 219 static struct ubus_object main_object = { 220 .name = "network", 221 .type = &main_object_type, 222 .methods = main_object_methods, 223 .n_methods = ARRAY_SIZE(main_object_methods), 224 }; 225 226 enum { 227 DEV_NAME, 228 __DEV_MAX, 229 }; 230 231 static const struct blobmsg_policy dev_policy[__DEV_MAX] = { 232 [DEV_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, 233 }; 234 235 static int 236 netifd_dev_status(struct ubus_context *ctx, struct ubus_object *obj, 237 struct ubus_request_data *req, const char *method, 238 struct blob_attr *msg) 239 { 240 struct device *dev = NULL; 241 struct blob_attr *tb[__DEV_MAX]; 242 243 blobmsg_parse_attr(dev_policy, __DEV_MAX, tb, msg); 244 245 if (tb[DEV_NAME]) { 246 dev = device_find(blobmsg_data(tb[DEV_NAME])); 247 if (!dev) 248 return UBUS_STATUS_INVALID_ARGUMENT; 249 } 250 251 blob_buf_init(&b, 0); 252 device_dump_status(&b, dev); 253 ubus_send_reply(ctx, req, b.head); 254 255 return 0; 256 } 257 258 enum { 259 ALIAS_ATTR_ALIAS, 260 ALIAS_ATTR_DEV, 261 __ALIAS_ATTR_MAX, 262 }; 263 264 static const struct blobmsg_policy alias_attrs[__ALIAS_ATTR_MAX] = { 265 [ALIAS_ATTR_ALIAS] = { "alias", BLOBMSG_TYPE_ARRAY }, 266 [ALIAS_ATTR_DEV] = { "device", BLOBMSG_TYPE_STRING }, 267 }; 268 269 static int 270 netifd_handle_alias(struct ubus_context *ctx, struct ubus_object *obj, 271 struct ubus_request_data *req, const char *method, 272 struct blob_attr *msg) 273 { 274 struct device *dev = NULL; 275 struct blob_attr *tb[__ALIAS_ATTR_MAX]; 276 struct blob_attr *cur; 277 size_t rem; 278 279 blobmsg_parse_attr(alias_attrs, __ALIAS_ATTR_MAX, tb, msg); 280 281 if (!tb[ALIAS_ATTR_ALIAS]) 282 return UBUS_STATUS_INVALID_ARGUMENT; 283 284 if ((cur = tb[ALIAS_ATTR_DEV]) != NULL) { 285 dev = device_get(blobmsg_data(cur), true); 286 if (!dev) 287 return UBUS_STATUS_NOT_FOUND; 288 } 289 290 blobmsg_for_each_attr(cur, tb[ALIAS_ATTR_ALIAS], rem) { 291 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) 292 goto error; 293 294 if (!blobmsg_check_attr(cur, false)) 295 goto error; 296 297 alias_notify_device(blobmsg_data(cur), dev); 298 } 299 return 0; 300 301 error: 302 device_free_unused(); 303 return UBUS_STATUS_INVALID_ARGUMENT; 304 } 305 306 enum { 307 DEV_STATE_NAME, 308 DEV_STATE_DEFER, 309 DEV_STATE_AUTH_STATUS, 310 DEV_STATE_AUTH_VLANS, 311 __DEV_STATE_MAX, 312 }; 313 314 static const struct blobmsg_policy dev_state_policy[__DEV_STATE_MAX] = { 315 [DEV_STATE_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, 316 [DEV_STATE_DEFER] = { .name = "defer", .type = BLOBMSG_TYPE_BOOL }, 317 [DEV_STATE_AUTH_STATUS] = { .name = "auth_status", .type = BLOBMSG_TYPE_BOOL }, 318 [DEV_STATE_AUTH_VLANS] = { .name = "auth_vlans", BLOBMSG_TYPE_ARRAY }, 319 }; 320 321 static int 322 netifd_handle_set_state(struct ubus_context *ctx, struct ubus_object *obj, 323 struct ubus_request_data *req, const char *method, 324 struct blob_attr *msg) 325 { 326 struct device *dev = NULL; 327 struct blob_attr *tb[__DEV_STATE_MAX]; 328 struct blob_attr *cur; 329 bool auth_status; 330 331 blobmsg_parse_attr(dev_state_policy, __DEV_STATE_MAX, tb, msg); 332 333 cur = tb[DEV_STATE_NAME]; 334 if (!cur) 335 return UBUS_STATUS_INVALID_ARGUMENT; 336 337 dev = device_find(blobmsg_data(cur)); 338 if (!dev) 339 return UBUS_STATUS_NOT_FOUND; 340 341 cur = tb[DEV_STATE_DEFER]; 342 if (cur) 343 device_set_deferred(dev, !!blobmsg_get_u8(cur)); 344 345 if ((cur = tb[DEV_STATE_AUTH_STATUS]) != NULL) 346 auth_status = blobmsg_get_bool(cur); 347 else 348 auth_status = dev->auth_status; 349 if (tb[DEV_STATE_AUTH_STATUS] || tb[DEV_STATE_AUTH_VLANS]) 350 device_set_auth_status(dev, auth_status, tb[DEV_STATE_AUTH_VLANS]); 351 352 return 0; 353 } 354 355 #ifdef DUMMY_MODE 356 enum { 357 DEV_HOTPLUG_ATTR_NAME, 358 DEV_HOTPLUG_ATTR_ADD, 359 __DEV_HOTPLUG_ATTR_MAX, 360 }; 361 362 static const struct blobmsg_policy dev_hotplug_policy[__DEV_HOTPLUG_ATTR_MAX] = { 363 [DEV_HOTPLUG_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING }, 364 [DEV_HOTPLUG_ATTR_ADD] = { "add", BLOBMSG_TYPE_BOOL }, 365 }; 366 367 static int 368 netifd_handle_dev_hotplug(struct ubus_context *ctx, struct ubus_object *obj, 369 struct ubus_request_data *req, const char *method, 370 struct blob_attr *msg) 371 { 372 struct blob_attr *tb[__DEV_HOTPLUG_ATTR_MAX]; 373 const char *name; 374 375 blobmsg_parse_attr(dev_hotplug_policy, __DEV_HOTPLUG_ATTR_MAX, tb, msg); 376 377 if (!tb[DEV_HOTPLUG_ATTR_NAME] || !tb[DEV_HOTPLUG_ATTR_ADD]) 378 return UBUS_STATUS_INVALID_ARGUMENT; 379 380 name = blobmsg_get_string(tb[DEV_HOTPLUG_ATTR_NAME]); 381 device_hotplug_event(name, blobmsg_get_bool(tb[DEV_HOTPLUG_ATTR_ADD])); 382 383 return 0; 384 } 385 #endif 386 387 static int 388 netifd_handle_stp_init(struct ubus_context *ctx, struct ubus_object *obj, 389 struct ubus_request_data *req, const char *method, 390 struct blob_attr *msg) 391 { 392 device_stp_init(); 393 394 return 0; 395 } 396 397 static struct ubus_method dev_object_methods[] = { 398 UBUS_METHOD("status", netifd_dev_status, dev_policy), 399 UBUS_METHOD("set_alias", netifd_handle_alias, alias_attrs), 400 UBUS_METHOD("set_state", netifd_handle_set_state, dev_state_policy), 401 #ifdef DUMMY_MODE 402 UBUS_METHOD("hotplug_event", netifd_handle_dev_hotplug, dev_hotplug_policy), 403 #endif 404 UBUS_METHOD_NOARG("stp_init", netifd_handle_stp_init) 405 }; 406 407 static struct ubus_object_type dev_object_type = 408 UBUS_OBJECT_TYPE("device", dev_object_methods); 409 410 static struct ubus_object dev_object = { 411 .name = "network.device", 412 .type = &dev_object_type, 413 .methods = dev_object_methods, 414 .n_methods = ARRAY_SIZE(dev_object_methods), 415 }; 416 417 static void 418 netifd_ubus_add_fd(void) 419 { 420 ubus_add_uloop(ubus_ctx); 421 system_fd_set_cloexec(ubus_ctx->sock.fd); 422 } 423 424 void netifd_ubus_device_notify(const char *event, struct blob_attr *data, int timeout) 425 { 426 ubus_notify(ubus_ctx, &dev_object, event, data, timeout); 427 } 428 429 static void 430 netifd_ubus_reconnect_timer(struct uloop_timeout *timeout) 431 { 432 static struct uloop_timeout retry = { 433 .cb = netifd_ubus_reconnect_timer, 434 }; 435 int t = 2; 436 437 if (ubus_reconnect(ubus_ctx, ubus_path) != 0) { 438 D(SYSTEM, "failed to reconnect, trying again in %d seconds", t); 439 uloop_timeout_set(&retry, t * 1000); 440 return; 441 } 442 443 D(SYSTEM, "reconnected to ubus, new id: %08x", ubus_ctx->local_id); 444 netifd_ubus_add_fd(); 445 } 446 447 static void 448 netifd_ubus_connection_lost(struct ubus_context *ctx) 449 { 450 netifd_ubus_reconnect_timer(NULL); 451 } 452 453 /* per-interface object */ 454 455 static int 456 netifd_handle_up(struct ubus_context *ctx, struct ubus_object *obj, 457 struct ubus_request_data *req, const char *method, 458 struct blob_attr *msg) 459 { 460 struct interface *iface; 461 462 iface = container_of(obj, struct interface, ubus); 463 interface_set_up(iface); 464 465 return 0; 466 } 467 468 static int 469 netifd_handle_down(struct ubus_context *ctx, struct ubus_object *obj, 470 struct ubus_request_data *req, const char *method, 471 struct blob_attr *msg) 472 { 473 struct interface *iface; 474 475 iface = container_of(obj, struct interface, ubus); 476 interface_set_down(iface); 477 478 return 0; 479 } 480 481 static int 482 netifd_handle_renew(struct ubus_context *ctx, struct ubus_object *obj, 483 struct ubus_request_data *req, const char *method, 484 struct blob_attr *msg) 485 { 486 struct interface *iface; 487 488 iface = container_of(obj, struct interface, ubus); 489 interface_renew(iface); 490 491 return 0; 492 } 493 494 static int 495 netifd_handle_iface_restart(struct ubus_context *ctx, struct ubus_object *obj, 496 struct ubus_request_data *req, const char *method, 497 struct blob_attr *msg) 498 { 499 struct interface *iface; 500 501 iface = container_of(obj, struct interface, ubus); 502 if (interface_restart(iface)) 503 return UBUS_STATUS_UNKNOWN_ERROR; 504 505 return 0; 506 } 507 508 static void 509 netifd_add_interface_errors(struct blob_buf *b, struct interface *iface) 510 { 511 struct interface_error *error; 512 void *e, *e2, *e3; 513 int i; 514 515 e = blobmsg_open_array(b, "errors"); 516 list_for_each_entry(error, &iface->errors, list) { 517 e2 = blobmsg_open_table(b, NULL); 518 519 blobmsg_add_string(b, "subsystem", error->subsystem); 520 blobmsg_add_string(b, "code", error->code); 521 if (error->data[0]) { 522 e3 = blobmsg_open_array(b, "data"); 523 for (i = 0; error->data[i]; i++) 524 blobmsg_add_string(b, NULL, error->data[i]); 525 blobmsg_close_array(b, e3); 526 } 527 528 blobmsg_close_table(b, e2); 529 } 530 blobmsg_close_array(b, e); 531 } 532 533 static void 534 interface_ip_dump_address_list(struct interface_ip_settings *ip, bool v6, bool enabled) 535 { 536 struct device_addr *addr; 537 char *buf; 538 void *a; 539 int buflen = 128; 540 int af; 541 542 time_t now = system_get_rtime(); 543 vlist_for_each_element(&ip->addr, addr, node) { 544 if (addr->enabled != enabled) 545 continue; 546 547 if ((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET4) 548 af = AF_INET; 549 else 550 af = AF_INET6; 551 552 if (af != (v6 ? AF_INET6 : AF_INET)) 553 continue; 554 555 a = blobmsg_open_table(&b, NULL); 556 557 buf = blobmsg_alloc_string_buffer(&b, "address", buflen); 558 inet_ntop(af, &addr->addr, buf, buflen); 559 blobmsg_add_string_buffer(&b); 560 561 blobmsg_add_u32(&b, "mask", addr->mask); 562 563 if (addr->point_to_point) { 564 buf = blobmsg_alloc_string_buffer(&b, "ptpaddress", buflen); 565 inet_ntop(af, &addr->point_to_point, buf, buflen); 566 blobmsg_add_string_buffer(&b); 567 } 568 569 if (addr->preferred_until) { 570 int preferred = addr->preferred_until - now; 571 if (preferred < 0) 572 preferred = 0; 573 blobmsg_add_u32(&b, "preferred", preferred); 574 } 575 576 if (addr->valid_until) 577 blobmsg_add_u32(&b, "valid", addr->valid_until - now); 578 579 if (addr->pclass) 580 blobmsg_add_string(&b, "class", addr->pclass); 581 582 blobmsg_close_table(&b, a); 583 } 584 } 585 586 static void 587 interface_ip_dump_neighbor_list(struct interface_ip_settings *ip, bool enabled) 588 { 589 struct device_neighbor *neighbor; 590 int buflen = 128; 591 char *buf; 592 void *r; 593 int af; 594 595 vlist_for_each_element(&ip->neighbor, neighbor, node) { 596 if (neighbor->enabled != enabled) 597 continue; 598 599 if ((neighbor->flags & DEVADDR_FAMILY) == DEVADDR_INET4) 600 af = AF_INET; 601 else 602 af = AF_INET6; 603 604 r = blobmsg_open_table(&b, NULL); 605 606 if (neighbor->flags & DEVNEIGH_MAC) 607 blobmsg_add_string(&b, "mac", format_macaddr(neighbor->macaddr)); 608 609 buf = blobmsg_alloc_string_buffer(&b , "address", buflen); 610 inet_ntop(af, &neighbor->addr, buf, buflen); 611 blobmsg_add_string_buffer(&b); 612 613 if (neighbor->proxy) 614 blobmsg_add_u32(&b, "proxy", neighbor->proxy); 615 616 if (neighbor->router) 617 blobmsg_add_u32(&b, "router", neighbor->router); 618 619 blobmsg_close_table(&b, r); 620 } 621 } 622 623 static void 624 interface_ip_dump_route_list(struct interface_ip_settings *ip, bool enabled) 625 { 626 struct device_route *route; 627 int buflen = 128; 628 char *buf; 629 void *r; 630 int af; 631 632 time_t now = system_get_rtime(); 633 vlist_for_each_element(&ip->route, route, node) { 634 if (route->enabled != enabled) 635 continue; 636 637 if ((ip->no_defaultroute == enabled) && !route->mask) 638 continue; 639 640 if ((route->flags & DEVADDR_FAMILY) == DEVADDR_INET4) 641 af = AF_INET; 642 else 643 af = AF_INET6; 644 645 r = blobmsg_open_table(&b, NULL); 646 647 buf = blobmsg_alloc_string_buffer(&b, "target", buflen); 648 inet_ntop(af, &route->addr, buf, buflen); 649 blobmsg_add_string_buffer(&b); 650 651 blobmsg_add_u32(&b, "mask", route->mask); 652 653 buf = blobmsg_alloc_string_buffer(&b, "nexthop", buflen); 654 inet_ntop(af, &route->nexthop, buf, buflen); 655 blobmsg_add_string_buffer(&b); 656 657 if (route->flags & DEVROUTE_TYPE) 658 blobmsg_add_u32(&b, "type", route->type); 659 660 if (route->flags & DEVROUTE_PROTO) 661 blobmsg_add_u32(&b, "proto", route->proto); 662 663 if (route->flags & DEVROUTE_MTU) 664 blobmsg_add_u32(&b, "mtu", route->mtu); 665 666 if (route->flags & DEVROUTE_METRIC) 667 blobmsg_add_u32(&b, "metric", route->metric); 668 669 if (route->flags & DEVROUTE_TABLE) 670 blobmsg_add_u32(&b, "table", route->table); 671 672 if (route->valid_until) 673 blobmsg_add_u32(&b, "valid", route->valid_until - now); 674 675 buf = blobmsg_alloc_string_buffer(&b, "source", buflen); 676 inet_ntop(af, &route->source, buf, buflen); 677 snprintf(buf + strlen(buf), buflen - strlen(buf), "/%u", route->sourcemask); 678 blobmsg_add_string_buffer(&b); 679 680 blobmsg_close_table(&b, r); 681 } 682 } 683 684 685 static void 686 interface_ip_dump_prefix_list(struct interface_ip_settings *ip) 687 { 688 struct device_prefix *prefix; 689 char *buf; 690 void *a, *c; 691 const int buflen = INET6_ADDRSTRLEN; 692 693 time_t now = system_get_rtime(); 694 vlist_for_each_element(&ip->prefix, prefix, node) { 695 a = blobmsg_open_table(&b, NULL); 696 697 buf = blobmsg_alloc_string_buffer(&b, "address", buflen); 698 inet_ntop(AF_INET6, &prefix->addr, buf, buflen); 699 blobmsg_add_string_buffer(&b); 700 701 blobmsg_add_u32(&b, "mask", prefix->length); 702 703 if (prefix->preferred_until) { 704 int preferred = prefix->preferred_until - now; 705 if (preferred < 0) 706 preferred = 0; 707 blobmsg_add_u32(&b, "preferred", preferred); 708 } 709 710 if (prefix->valid_until) 711 blobmsg_add_u32(&b, "valid", prefix->valid_until - now); 712 713 blobmsg_add_string(&b, "class", prefix->pclass); 714 715 c = blobmsg_open_table(&b, "assigned"); 716 struct device_prefix_assignment *assign; 717 list_for_each_entry(assign, &prefix->assignments, head) { 718 if (!assign->name[0]) 719 continue; 720 721 struct in6_addr addr = prefix->addr; 722 addr.s6_addr32[1] |= htonl(assign->assigned); 723 724 void *d = blobmsg_open_table(&b, assign->name); 725 726 buf = blobmsg_alloc_string_buffer(&b, "address", buflen); 727 inet_ntop(AF_INET6, &addr, buf, buflen); 728 blobmsg_add_string_buffer(&b); 729 730 blobmsg_add_u32(&b, "mask", assign->length); 731 732 blobmsg_close_table(&b, d); 733 } 734 blobmsg_close_table(&b, c); 735 736 blobmsg_close_table(&b, a); 737 } 738 } 739 740 741 static void 742 interface_ip_dump_prefix_assignment_list(struct interface *iface) 743 { 744 void *a; 745 char *buf; 746 const int buflen = INET6_ADDRSTRLEN; 747 time_t now = system_get_rtime(); 748 749 struct device_prefix *prefix; 750 list_for_each_entry(prefix, &prefixes, head) { 751 struct device_prefix_assignment *assign; 752 list_for_each_entry(assign, &prefix->assignments, head) { 753 if (strcmp(assign->name, iface->name)) 754 continue; 755 756 struct in6_addr addr = prefix->addr; 757 addr.s6_addr32[1] |= htonl(assign->assigned); 758 759 a = blobmsg_open_table(&b, NULL); 760 761 buf = blobmsg_alloc_string_buffer(&b, "address", buflen); 762 inet_ntop(AF_INET6, &addr, buf, buflen); 763 blobmsg_add_string_buffer(&b); 764 765 blobmsg_add_u32(&b, "mask", assign->length); 766 767 if (prefix->preferred_until) { 768 int preferred = prefix->preferred_until - now; 769 if (preferred < 0) 770 preferred = 0; 771 blobmsg_add_u32(&b, "preferred", preferred); 772 } 773 774 if (prefix->valid_until) 775 blobmsg_add_u32(&b, "valid", prefix->valid_until - now); 776 777 void *c = blobmsg_open_table(&b, "local-address"); 778 if (assign->enabled) { 779 buf = blobmsg_alloc_string_buffer(&b, "address", buflen); 780 inet_ntop(AF_INET6, &assign->addr, buf, buflen); 781 blobmsg_add_string_buffer(&b); 782 783 blobmsg_add_u32(&b, "mask", assign->length); 784 } 785 blobmsg_close_table(&b, c); 786 787 blobmsg_close_table(&b, a); 788 } 789 } 790 } 791 792 static void 793 interface_ip_dump_dns_server_list(struct interface_ip_settings *ip, bool enabled) 794 { 795 struct dns_server *dns; 796 int buflen = 128; 797 char *buf; 798 799 vlist_simple_for_each_element(&ip->dns_servers, dns, node) { 800 if (ip->no_dns == enabled) 801 continue; 802 803 buf = blobmsg_alloc_string_buffer(&b, NULL, buflen); 804 inet_ntop(dns->af, &dns->addr, buf, buflen); 805 blobmsg_add_string_buffer(&b); 806 } 807 } 808 809 static void 810 interface_ip_dump_dns_search_list(struct interface_ip_settings *ip, bool enabled) 811 { 812 struct dns_search_domain *dns; 813 814 vlist_simple_for_each_element(&ip->dns_search, dns, node) { 815 if (ip->no_dns == enabled) 816 continue; 817 818 blobmsg_add_string(&b, NULL, dns->name); 819 } 820 } 821 822 static void 823 netifd_dump_status(struct interface *iface) 824 { 825 struct interface_data *data; 826 struct device *dev; 827 void *a, *inactive; 828 829 blobmsg_add_u8(&b, "up", iface->state == IFS_UP); 830 blobmsg_add_u8(&b, "pending", iface->state == IFS_SETUP); 831 blobmsg_add_u8(&b, "available", iface->available); 832 blobmsg_add_u8(&b, "autostart", iface->autostart); 833 blobmsg_add_u8(&b, "dynamic", iface->dynamic); 834 835 if (iface->state == IFS_UP) { 836 time_t cur = system_get_rtime(); 837 blobmsg_add_u32(&b, "uptime", cur - iface->start_time); 838 if (iface->l3_dev.dev) 839 blobmsg_add_string(&b, "l3_device", iface->l3_dev.dev->ifname); 840 } 841 842 if (iface->proto_handler) 843 blobmsg_add_string(&b, "proto", iface->proto_handler->name); 844 845 dev = iface->main_dev.dev; 846 if (dev && !dev->hidden && iface->proto_handler && 847 !(iface->proto_handler->flags & PROTO_FLAG_NODEV)) 848 blobmsg_add_string(&b, "device", dev->ifname); 849 850 if (iface->jail) 851 blobmsg_add_string(&b, "jail", iface->jail); 852 853 if (iface->jail_device) 854 blobmsg_add_string(&b, "jail_device", iface->jail_device); 855 if (iface->tags) 856 blobmsg_add_blob(&b, iface->tags); 857 858 if (iface->state == IFS_UP) { 859 if (iface->updated) { 860 a = blobmsg_open_array(&b, "updated"); 861 862 if (iface->updated & IUF_ADDRESS) 863 blobmsg_add_string(&b, NULL, "addresses"); 864 if (iface->updated & IUF_ROUTE) 865 blobmsg_add_string(&b, NULL, "routes"); 866 if (iface->updated & IUF_PREFIX) 867 blobmsg_add_string(&b, NULL, "prefixes"); 868 if (iface->updated & IUF_DATA) 869 blobmsg_add_string(&b, NULL, "data"); 870 871 blobmsg_close_array(&b, a); 872 } 873 874 if (iface->ip4table) 875 blobmsg_add_u32(&b, "ip4table", iface->ip4table); 876 if (iface->ip6table) 877 blobmsg_add_u32(&b, "ip6table", iface->ip6table); 878 blobmsg_add_u32(&b, "metric", iface->metric); 879 blobmsg_add_u32(&b, "dns_metric", iface->dns_metric); 880 blobmsg_add_u8(&b, "delegation", !iface->proto_ip.no_delegation); 881 if (iface->assignment_weight) 882 blobmsg_add_u32(&b, "ip6weight", iface->assignment_weight); 883 a = blobmsg_open_array(&b, "ipv4-address"); 884 interface_ip_dump_address_list(&iface->config_ip, false, true); 885 interface_ip_dump_address_list(&iface->proto_ip, false, true); 886 blobmsg_close_array(&b, a); 887 a = blobmsg_open_array(&b, "ipv6-address"); 888 interface_ip_dump_address_list(&iface->config_ip, true, true); 889 interface_ip_dump_address_list(&iface->proto_ip, true, true); 890 blobmsg_close_array(&b, a); 891 a = blobmsg_open_array(&b, "ipv6-prefix"); 892 interface_ip_dump_prefix_list(&iface->config_ip); 893 interface_ip_dump_prefix_list(&iface->proto_ip); 894 blobmsg_close_array(&b, a); 895 a = blobmsg_open_array(&b, "ipv6-prefix-assignment"); 896 interface_ip_dump_prefix_assignment_list(iface); 897 blobmsg_close_array(&b, a); 898 a = blobmsg_open_array(&b, "route"); 899 interface_ip_dump_route_list(&iface->config_ip, true); 900 interface_ip_dump_route_list(&iface->proto_ip, true); 901 blobmsg_close_array(&b, a); 902 a = blobmsg_open_array(&b, "dns-server"); 903 interface_ip_dump_dns_server_list(&iface->config_ip, true); 904 interface_ip_dump_dns_server_list(&iface->proto_ip, true); 905 blobmsg_close_array(&b, a); 906 a = blobmsg_open_array(&b, "dns-search"); 907 interface_ip_dump_dns_search_list(&iface->config_ip, true); 908 interface_ip_dump_dns_search_list(&iface->proto_ip, true); 909 blobmsg_close_array(&b, a); 910 a = blobmsg_open_array(&b, "neighbors"); 911 interface_ip_dump_neighbor_list(&iface->config_ip, true); 912 interface_ip_dump_neighbor_list(&iface->proto_ip, true); 913 blobmsg_close_array(&b, a); 914 915 inactive = blobmsg_open_table(&b, "inactive"); 916 a = blobmsg_open_array(&b, "ipv4-address"); 917 interface_ip_dump_address_list(&iface->config_ip, false, false); 918 interface_ip_dump_address_list(&iface->proto_ip, false, false); 919 blobmsg_close_array(&b, a); 920 a = blobmsg_open_array(&b, "ipv6-address"); 921 interface_ip_dump_address_list(&iface->config_ip, true, false); 922 interface_ip_dump_address_list(&iface->proto_ip, true, false); 923 blobmsg_close_array(&b, a); 924 a = blobmsg_open_array(&b, "route"); 925 interface_ip_dump_route_list(&iface->config_ip, false); 926 interface_ip_dump_route_list(&iface->proto_ip, false); 927 blobmsg_close_array(&b, a); 928 a = blobmsg_open_array(&b, "dns-server"); 929 interface_ip_dump_dns_server_list(&iface->config_ip, false); 930 interface_ip_dump_dns_server_list(&iface->proto_ip, false); 931 blobmsg_close_array(&b, a); 932 a = blobmsg_open_array(&b, "dns-search"); 933 interface_ip_dump_dns_search_list(&iface->config_ip, false); 934 interface_ip_dump_dns_search_list(&iface->proto_ip, false); 935 blobmsg_close_array(&b, a); 936 a = blobmsg_open_array(&b, "neighbors"); 937 interface_ip_dump_neighbor_list(&iface->config_ip, false); 938 interface_ip_dump_neighbor_list(&iface->proto_ip, false); 939 blobmsg_close_array(&b, a); 940 blobmsg_close_table(&b, inactive); 941 } 942 943 a = blobmsg_open_table(&b, "data"); 944 945 if (iface->zone) 946 blobmsg_add_string(&b, "zone", iface->zone); 947 avl_for_each_element(&iface->data, data, node) 948 blobmsg_add_blob(&b, data->data); 949 950 blobmsg_close_table(&b, a); 951 952 if (!list_empty(&iface->errors)) 953 netifd_add_interface_errors(&b, iface); 954 } 955 956 static int 957 netifd_handle_status(struct ubus_context *ctx, struct ubus_object *obj, 958 struct ubus_request_data *req, const char *method, 959 struct blob_attr *msg) 960 { 961 struct interface *iface = container_of(obj, struct interface, ubus); 962 963 blob_buf_init(&b, 0); 964 netifd_dump_status(iface); 965 ubus_send_reply(ctx, req, b.head); 966 967 return 0; 968 } 969 970 971 static int 972 netifd_handle_dump(struct ubus_context *ctx, struct ubus_object *obj, 973 struct ubus_request_data *req, const char *method, 974 struct blob_attr *msg) 975 { 976 blob_buf_init(&b, 0); 977 void *a = blobmsg_open_array(&b, "interface"); 978 979 struct interface *iface; 980 vlist_for_each_element(&interfaces, iface, node) { 981 void *i = blobmsg_open_table(&b, NULL); 982 blobmsg_add_string(&b, "interface", iface->name); 983 netifd_dump_status(iface); 984 blobmsg_close_table(&b, i); 985 } 986 987 blobmsg_close_array(&b, a); 988 ubus_send_reply(ctx, req, b.head); 989 990 return 0; 991 } 992 993 enum { 994 DEV_LINK_NAME, 995 DEV_LINK_EXT, 996 DEV_LINK_VLAN, 997 __DEV_LINK_MAX, 998 }; 999 1000 static const struct blobmsg_policy dev_link_policy[__DEV_LINK_MAX] = { 1001 [DEV_LINK_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, 1002 [DEV_LINK_EXT] = { .name = "link-ext", .type = BLOBMSG_TYPE_BOOL }, 1003 [DEV_LINK_VLAN] = { .name = "vlan", .type = BLOBMSG_TYPE_ARRAY }, 1004 }; 1005 1006 static int 1007 netifd_iface_handle_device(struct ubus_context *ctx, struct ubus_object *obj, 1008 struct ubus_request_data *req, const char *method, 1009 struct blob_attr *msg) 1010 { 1011 struct blob_attr *tb[__DEV_LINK_MAX]; 1012 struct blob_attr *cur; 1013 struct interface *iface; 1014 bool add = !strncmp(method, "add", 3); 1015 bool link_ext = true; 1016 1017 iface = container_of(obj, struct interface, ubus); 1018 1019 blobmsg_parse_attr(dev_link_policy, __DEV_LINK_MAX, tb, msg); 1020 1021 if (!tb[DEV_LINK_NAME]) 1022 return UBUS_STATUS_INVALID_ARGUMENT; 1023 1024 cur = tb[DEV_LINK_EXT]; 1025 if (cur) 1026 link_ext = blobmsg_get_bool(cur); 1027 1028 return interface_handle_link(iface, blobmsg_data(tb[DEV_LINK_NAME]), 1029 tb[DEV_LINK_VLAN], add, link_ext); 1030 } 1031 1032 1033 static int 1034 netifd_iface_notify_proto(struct ubus_context *ctx, struct ubus_object *obj, 1035 struct ubus_request_data *req, const char *method, 1036 struct blob_attr *msg) 1037 { 1038 struct interface *iface; 1039 1040 iface = container_of(obj, struct interface, ubus); 1041 1042 if (!iface->proto || !iface->proto->notify) 1043 return UBUS_STATUS_NOT_SUPPORTED; 1044 1045 return iface->proto->notify(iface->proto, msg); 1046 } 1047 1048 static void 1049 netifd_iface_do_remove(struct uloop_timeout *timeout) 1050 { 1051 struct interface *iface; 1052 1053 iface = container_of(timeout, struct interface, remove_timer); 1054 vlist_delete(&interfaces, &iface->node); 1055 } 1056 1057 static int 1058 netifd_iface_remove(struct ubus_context *ctx, struct ubus_object *obj, 1059 struct ubus_request_data *req, const char *method, 1060 struct blob_attr *msg) 1061 { 1062 struct interface *iface; 1063 1064 iface = container_of(obj, struct interface, ubus); 1065 if (iface->remove_timer.cb) 1066 return UBUS_STATUS_INVALID_ARGUMENT; 1067 1068 iface->remove_timer.cb = netifd_iface_do_remove; 1069 uloop_timeout_set(&iface->remove_timer, 100); 1070 return 0; 1071 } 1072 1073 static int 1074 netifd_handle_iface_prepare(struct ubus_context *ctx, struct ubus_object *obj, 1075 struct ubus_request_data *req, const char *method, 1076 struct blob_attr *msg) 1077 { 1078 struct interface *iface; 1079 struct device *dev, *bridge_dev = NULL; 1080 const struct device_hotplug_ops *ops; 1081 1082 iface = container_of(obj, struct interface, ubus); 1083 dev = iface->main_dev.dev; 1084 if (!dev) 1085 goto out; 1086 1087 ops = dev->hotplug_ops; 1088 if (!ops) 1089 goto out; 1090 1091 ops->prepare(dev, &bridge_dev); 1092 1093 out: 1094 blob_buf_init(&b, 0); 1095 if (bridge_dev) 1096 blobmsg_add_string(&b, "bridge", bridge_dev->ifname); 1097 ubus_send_reply(ctx, req, b.head); 1098 1099 return 0; 1100 } 1101 1102 static int 1103 netifd_handle_set_data(struct ubus_context *ctx, struct ubus_object *obj, 1104 struct ubus_request_data *req, const char *method, 1105 struct blob_attr *msg) 1106 { 1107 struct interface *iface; 1108 1109 iface = container_of(obj, struct interface, ubus); 1110 1111 return interface_parse_data(iface, msg); 1112 } 1113 1114 static struct ubus_method iface_object_methods[] = { 1115 { .name = "up", .handler = netifd_handle_up }, 1116 { .name = "down", .handler = netifd_handle_down }, 1117 { .name = "renew", .handler = netifd_handle_renew }, 1118 { .name = "restart", .handler = netifd_handle_iface_restart }, 1119 { .name = "status", .handler = netifd_handle_status }, 1120 { .name = "prepare", .handler = netifd_handle_iface_prepare }, 1121 { .name = "dump", .handler = netifd_handle_dump }, 1122 UBUS_METHOD("add_device", netifd_iface_handle_device, dev_link_policy ), 1123 UBUS_METHOD("remove_device", netifd_iface_handle_device, dev_link_policy ), 1124 { .name = "notify_proto", .handler = netifd_iface_notify_proto }, 1125 { .name = "remove", .handler = netifd_iface_remove }, 1126 { .name = "set_data", .handler = netifd_handle_set_data }, 1127 }; 1128 1129 static struct ubus_object_type iface_object_type = 1130 UBUS_OBJECT_TYPE("netifd_iface", iface_object_methods); 1131 1132 1133 static struct ubus_object iface_object = { 1134 .name = "network.interface", 1135 .type = &iface_object_type, 1136 .n_methods = ARRAY_SIZE(iface_object_methods), 1137 }; 1138 1139 static void netifd_add_object(struct ubus_object *obj) 1140 { 1141 int ret = ubus_add_object(ubus_ctx, obj); 1142 1143 if (ret != 0) 1144 fprintf(stderr, "Failed to publish object '%s': %s\n", obj->name, ubus_strerror(ret)); 1145 } 1146 1147 static const struct blobmsg_policy iface_policy = { 1148 .name = "interface", 1149 .type = BLOBMSG_TYPE_STRING, 1150 }; 1151 1152 static int 1153 netifd_handle_iface(struct ubus_context *ctx, struct ubus_object *obj, 1154 struct ubus_request_data *req, const char *method, 1155 struct blob_attr *msg) 1156 { 1157 struct interface *iface; 1158 struct blob_attr *tb; 1159 size_t i; 1160 1161 blobmsg_parse_attr(&iface_policy, 1, &tb, msg); 1162 if (!tb) 1163 return UBUS_STATUS_INVALID_ARGUMENT; 1164 1165 iface = vlist_find(&interfaces, blobmsg_data(tb), iface, node); 1166 if (!iface) 1167 return UBUS_STATUS_NOT_FOUND; 1168 1169 for (i = 0; i < ARRAY_SIZE(iface_object_methods); i++) { 1170 ubus_handler_t cb; 1171 1172 if (strcmp(method, iface_object_methods[i].name) != 0) 1173 continue; 1174 1175 cb = iface_object_methods[i].handler; 1176 return cb(ctx, &iface->ubus, req, method, msg); 1177 } 1178 1179 return UBUS_STATUS_INVALID_ARGUMENT; 1180 } 1181 1182 static void netifd_add_iface_object(void) 1183 { 1184 struct ubus_method *methods; 1185 size_t i; 1186 1187 methods = calloc(1, sizeof(iface_object_methods)); 1188 if (!methods) 1189 return; 1190 1191 memcpy(methods, iface_object_methods, sizeof(iface_object_methods)); 1192 iface_object.methods = methods; 1193 1194 for (i = 0; i < ARRAY_SIZE(iface_object_methods); i++) { 1195 if (methods[i].handler == netifd_handle_dump) 1196 continue; 1197 1198 methods[i].handler = netifd_handle_iface; 1199 methods[i].policy = &iface_policy; 1200 methods[i].n_policy = 1; 1201 } 1202 netifd_add_object(&iface_object); 1203 } 1204 1205 int 1206 netifd_extdev_invoke(uint32_t id, const char *method, struct blob_attr *msg, 1207 ubus_data_handler_t data_cb, void *data) 1208 { 1209 return ubus_invoke(ubus_ctx, id, method, msg, data_cb, data, 3000); 1210 } 1211 1212 int 1213 netifd_ubus_init(const char *path) 1214 { 1215 ubus_path = path; 1216 1217 ubus_ctx = ubus_connect(path); 1218 if (!ubus_ctx) 1219 return -EIO; 1220 1221 D(SYSTEM, "connected as %08x", ubus_ctx->local_id); 1222 ubus_ctx->connection_lost = netifd_ubus_connection_lost; 1223 netifd_ubus_add_fd(); 1224 1225 netifd_add_object(&main_object); 1226 netifd_add_object(&dev_object); 1227 netifd_add_iface_object(); 1228 1229 udebug_ubus_init(&udebug, ubus_ctx, "netifd", netifd_udebug_config); 1230 1231 return 0; 1232 } 1233 1234 void 1235 netifd_ubus_done(void) 1236 { 1237 udebug_ubus_free(&udebug); 1238 ubus_free(ubus_ctx); 1239 } 1240 1241 void 1242 netifd_ubus_interface_event(struct interface *iface, bool up) 1243 { 1244 blob_buf_init(&b, 0); 1245 blobmsg_add_string(&b, "action", up ? "ifup" : "ifdown"); 1246 blobmsg_add_string(&b, "interface", iface->name); 1247 ubus_send_event(ubus_ctx, "network.interface", b.head); 1248 } 1249 1250 void 1251 netifd_ubus_interface_notify(struct interface *iface, bool up) 1252 { 1253 const char *event = (up) ? "interface.update" : "interface.down"; 1254 blob_buf_init(&b, 0); 1255 blobmsg_add_string(&b, "interface", iface->name); 1256 netifd_dump_status(iface); 1257 ubus_notify(ubus_ctx, &iface_object, event, b.head, -1); 1258 ubus_notify(ubus_ctx, &iface->ubus, event, b.head, -1); 1259 } 1260 1261 void 1262 netifd_ubus_add_interface(struct interface *iface) 1263 { 1264 struct ubus_object *obj = &iface->ubus; 1265 char *name = NULL; 1266 1267 if (asprintf(&name, "%s.interface.%s", main_object.name, iface->name) == -1) 1268 return; 1269 1270 obj->name = name; 1271 obj->type = &iface_object_type; 1272 obj->methods = iface_object_methods; 1273 obj->n_methods = ARRAY_SIZE(iface_object_methods); 1274 if (ubus_add_object(ubus_ctx, &iface->ubus)) { 1275 D(SYSTEM, "failed to publish ubus object for interface '%s'", iface->name); 1276 free(name); 1277 obj->name = NULL; 1278 } 1279 } 1280 1281 void 1282 netifd_ubus_remove_interface(struct interface *iface) 1283 { 1284 if (!iface->ubus.name) 1285 return; 1286 1287 ubus_remove_object(ubus_ctx, &iface->ubus); 1288 free((void *) iface->ubus.name); 1289 } 1290 1291 static void 1292 netifd_ubus_data_cb(struct ubus_request *req, int type, struct blob_attr *msg) 1293 { 1294 struct blob_attr *srv, *in, *t, *data; 1295 procd_data_cb cb = req->priv; 1296 size_t rem, rem2, rem3, rem4; 1297 1298 blobmsg_for_each_attr(srv, msg, rem) { 1299 if (!blobmsg_check_attr(srv, true) || 1300 blobmsg_type(srv) != BLOBMSG_TYPE_TABLE) 1301 continue; 1302 blobmsg_for_each_attr(in, srv, rem2) { 1303 if (!blobmsg_check_attr(in , true) || 1304 blobmsg_type(in) != BLOBMSG_TYPE_TABLE) 1305 continue; 1306 blobmsg_for_each_attr(t, in, rem3) { 1307 if (!blobmsg_check_attr(t, true) || 1308 blobmsg_type(t) != BLOBMSG_TYPE_TABLE) 1309 continue; 1310 blobmsg_for_each_attr(data, t, rem4) { 1311 if (!blobmsg_check_attr(data, true) || 1312 blobmsg_type(data) != BLOBMSG_TYPE_TABLE) 1313 continue; 1314 cb(data); 1315 } 1316 } 1317 } 1318 } 1319 } 1320 1321 void netifd_ubus_get_procd_data(const char *type, procd_data_cb cb) 1322 { 1323 uint32_t id; 1324 1325 if (ubus_lookup_id(ubus_ctx, "service", &id)) 1326 return; 1327 1328 blob_buf_init(&b, 0); 1329 blobmsg_add_string(&b, "type", type); 1330 ubus_invoke(ubus_ctx, id, "get_data", b.head, netifd_ubus_data_cb, cb, 30000); 1331 } 1332
This page was automatically generated by LXR 0.3.1. • OpenWrt