1 /** 2 * SPDX-License-Identifier: BSD-2-Clause-Patent 3 * 4 * SPDX-FileCopyrightText: Copyright (c) 2024 SoftAtHome 5 * 6 * Redistribution and use in source and binary forms, with or 7 * without modification, are permitted provided that the following 8 * conditions are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above 14 * copyright notice, this list of conditions and the following 15 * disclaimer in the documentation and/or other materials provided 16 * with the distribution. 17 * 18 * Subject to the terms and conditions of this license, each 19 * copyright holder and contributor hereby grants to those receiving 20 * rights under this license a perpetual, worldwide, non-exclusive, 21 * no-charge, royalty-free, irrevocable (except for failure to 22 * satisfy the conditions of this license) patent license to make, 23 * have made, use, offer to sell, sell, import, and otherwise 24 * transfer this software, where such license applies only to those 25 * patent claims, already acquired or hereafter acquired, licensable 26 * by such copyright holder or contributor that are necessarily 27 * infringed by: 28 * 29 * (a) their Contribution(s) (the licensed copyrights of copyright 30 * holders and non-copyrightable additions of contributors, in 31 * source or binary form) alone; or 32 * 33 * (b) combination of their Contribution(s) with the work of 34 * authorship to which such Contribution(s) was added by such 35 * copyright holder or contributor, if, at the time the Contribution 36 * is added, such addition causes such combination to be necessarily 37 * infringed. The patent license shall not apply to any other 38 * combinations which include the Contribution. 39 * 40 * Except as expressly stated above, no rights or licenses from any 41 * copyright holder or contributor is granted under this license, 42 * whether expressly, by implication, estoppel or otherwise. 43 * 44 * DISCLAIMER 45 * 46 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 47 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 48 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 49 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 50 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR 51 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 52 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 53 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 54 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 55 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 57 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 58 * POSSIBILITY OF SUCH DAMAGE. 59 * 60 */ 61 62 #include <arpa/inet.h> 63 #include <inttypes.h> 64 #include <libubox/blobmsg.h> 65 #include <resolv.h> 66 #include <stdio.h> 67 #include <sys/types.h> 68 69 #include "config.h" 70 #include "odhcp6c.h" 71 #include "ubus.h" 72 73 #define CHECK(stmt) \ 74 do { \ 75 int ret = (stmt); \ 76 if (ret != UBUS_STATUS_OK) \ 77 { \ 78 error("%s failed: %s (%d)", #stmt, ubus_strerror(ret), ret); \ 79 return ret; \ 80 } \ 81 } while (0) 82 83 #define CHECK_ALLOC(buf) \ 84 do { \ 85 if (buf == NULL) \ 86 { \ 87 return UBUS_STATUS_NO_MEMORY; \ 88 } \ 89 } while (0) 90 91 enum entry_type { 92 ENTRY_ADDRESS, 93 ENTRY_HOST, 94 ENTRY_ROUTE, 95 ENTRY_PREFIX 96 }; 97 98 enum { 99 RECONFIGURE_DHCP_ATTR_DSCP, 100 RECONFIGURE_DHCP_ATTR_RELEASE, 101 RECONFIGURE_DHCP_ATTR_SOL_TIMEOUT, 102 RECONFIGURE_DHCP_ATTR_SK_PRIORITY, 103 RECONFIGURE_DHCP_ATTR_OPT_REQUESTED, 104 RECONFIGURE_DHCP_ATTR_OPT_STRICT, 105 RECONFIGURE_DHCP_ATTR_OPT_RECONFIGURE, 106 RECONFIGURE_DHCP_ATTR_OPT_FQDN, 107 RECONFIGURE_DHCP_ATTR_OPT_UNICAST, 108 RECONFIGURE_DHCP_ATTR_OPT_SEND, 109 RECONFIGURE_DHCP_ATTR_REQ_ADDRESSES, 110 RECONFIGURE_DHCP_ATTR_REQ_PREFIXES, 111 RECONFIGURE_DHCP_ATTR_STATEFUL, 112 RECONFIGURE_DHCP_ATTR_MSG_SOLICIT, 113 RECONFIGURE_DHCP_ATTR_MSG_REQUEST, 114 RECONFIGURE_DHCP_ATTR_MSG_RENEW, 115 RECONFIGURE_DHCP_ATTR_MSG_REBIND, 116 RECONFIGURE_DHCP_ATTR_MSG_RELEASE, 117 RECONFIGURE_DHCP_ATTR_MSG_DECLINE, 118 RECONFIGURE_DHCP_ATTR_MSG_INFO_REQ, 119 RECONFIGURE_DHCP_ATTR_IRT_DEFAULT, 120 RECONFIGURE_DHCP_ATTR_IRT_MIN, 121 RECONFIGURE_DHCP_ATTR_RAND_FACTOR, 122 RECONFIGURE_DHCP_ATTR_AUTH_PROTO, 123 RECONFIGURE_DHCP_ATTR_AUTH_TOKEN, 124 RECONFIGURE_DHCP_ATTR_MAX, 125 }; 126 127 struct ubus_context *ubus = NULL; 128 static struct blob_buf b; 129 static char ubus_name[24]; 130 131 static int ubus_handle_get_state(struct ubus_context *ctx, struct ubus_object *obj, 132 struct ubus_request_data *req, const char *method, struct blob_attr *msg); 133 static int ubus_handle_get_stats(struct ubus_context *ctx, struct ubus_object *obj, 134 struct ubus_request_data *req, const char *method, struct blob_attr *msg); 135 static int ubus_handle_reset_stats(struct ubus_context *ctx, struct ubus_object *obj, 136 struct ubus_request_data *req, const char *method, struct blob_attr *msg); 137 static int ubus_handle_reconfigure_dhcp(struct ubus_context *ctx, struct ubus_object *obj, 138 struct ubus_request_data *req, const char *method, struct blob_attr *msg); 139 static int ubus_handle_renew(struct ubus_context *ctx, struct ubus_object *obj, 140 struct ubus_request_data *req, const char *method, struct blob_attr *msg); 141 static int ubus_handle_release(struct ubus_context *ctx, struct ubus_object *obj, 142 struct ubus_request_data *req, const char *method, struct blob_attr *msg); 143 144 static const struct blobmsg_policy reconfigure_dhcp_policy[RECONFIGURE_DHCP_ATTR_MAX] = { 145 [RECONFIGURE_DHCP_ATTR_DSCP] = { .name = "dscp", .type = BLOBMSG_TYPE_INT32}, 146 [RECONFIGURE_DHCP_ATTR_RELEASE] = { .name = "release", .type = BLOBMSG_TYPE_BOOL}, 147 [RECONFIGURE_DHCP_ATTR_SOL_TIMEOUT] = { .name = "sol_timeout", .type = BLOBMSG_TYPE_INT32}, 148 [RECONFIGURE_DHCP_ATTR_SK_PRIORITY] = { .name = "sk_prio", .type = BLOBMSG_TYPE_INT32}, 149 [RECONFIGURE_DHCP_ATTR_OPT_REQUESTED] = { .name = "opt_requested", .type = BLOBMSG_TYPE_ARRAY}, 150 [RECONFIGURE_DHCP_ATTR_OPT_STRICT] = { .name = "opt_strict", .type = BLOBMSG_TYPE_BOOL}, 151 [RECONFIGURE_DHCP_ATTR_OPT_RECONFIGURE] = { .name = "opt_reconfigure", .type = BLOBMSG_TYPE_BOOL}, 152 [RECONFIGURE_DHCP_ATTR_OPT_FQDN] = { .name = "opt_fqdn", .type = BLOBMSG_TYPE_BOOL}, 153 [RECONFIGURE_DHCP_ATTR_OPT_UNICAST] = { .name = "opt_unicast", .type = BLOBMSG_TYPE_BOOL}, 154 [RECONFIGURE_DHCP_ATTR_OPT_SEND] = { .name = "opt_send", .type = BLOBMSG_TYPE_ARRAY}, 155 [RECONFIGURE_DHCP_ATTR_REQ_ADDRESSES] = { .name = "req_addresses", .type = BLOBMSG_TYPE_STRING}, 156 [RECONFIGURE_DHCP_ATTR_REQ_PREFIXES] = { .name = "req_prefixes", .type = BLOBMSG_TYPE_INT32}, 157 [RECONFIGURE_DHCP_ATTR_STATEFUL] = { .name = "stateful_only", .type = BLOBMSG_TYPE_BOOL}, 158 [RECONFIGURE_DHCP_ATTR_MSG_SOLICIT] = { .name = "msg_solicit", .type = BLOBMSG_TYPE_TABLE}, 159 [RECONFIGURE_DHCP_ATTR_MSG_REQUEST] = { .name = "msg_request", .type = BLOBMSG_TYPE_TABLE}, 160 [RECONFIGURE_DHCP_ATTR_MSG_RENEW] = { .name = "msg_renew", .type = BLOBMSG_TYPE_TABLE}, 161 [RECONFIGURE_DHCP_ATTR_MSG_REBIND] = { .name = "msg_rebind", .type = BLOBMSG_TYPE_TABLE}, 162 [RECONFIGURE_DHCP_ATTR_MSG_RELEASE] = { .name = "msg_release", .type = BLOBMSG_TYPE_TABLE}, 163 [RECONFIGURE_DHCP_ATTR_MSG_DECLINE] = { .name = "msg_decline", .type = BLOBMSG_TYPE_TABLE}, 164 [RECONFIGURE_DHCP_ATTR_MSG_INFO_REQ] = { .name = "msg_inforeq", .type = BLOBMSG_TYPE_TABLE}, 165 [RECONFIGURE_DHCP_ATTR_IRT_DEFAULT] = { .name = "irt_default", .type = BLOBMSG_TYPE_INT32}, 166 [RECONFIGURE_DHCP_ATTR_IRT_MIN] = { .name = "irt_min", .type = BLOBMSG_TYPE_INT32}, 167 [RECONFIGURE_DHCP_ATTR_RAND_FACTOR] = { .name = "rand_factor", .type = BLOBMSG_TYPE_INT32}, 168 [RECONFIGURE_DHCP_ATTR_AUTH_PROTO] = { .name = "auth_protocol", .type = BLOBMSG_TYPE_STRING}, 169 [RECONFIGURE_DHCP_ATTR_AUTH_TOKEN] = { .name = "auth_token", .type = BLOBMSG_TYPE_STRING}, 170 }; 171 172 static struct ubus_method odhcp6c_object_methods[] = { 173 UBUS_METHOD_NOARG("get_state", ubus_handle_get_state), 174 UBUS_METHOD_NOARG("get_statistics", ubus_handle_get_stats), 175 UBUS_METHOD_NOARG("reset_statistics", ubus_handle_reset_stats), 176 UBUS_METHOD("reconfigure_dhcp", ubus_handle_reconfigure_dhcp, reconfigure_dhcp_policy), 177 UBUS_METHOD_NOARG("renew", ubus_handle_renew), 178 UBUS_METHOD_NOARG("release", ubus_handle_release), 179 }; 180 181 static struct ubus_object_type odhcp6c_object_type = 182 UBUS_OBJECT_TYPE("odhcp6c", odhcp6c_object_methods); 183 184 static struct ubus_object odhcp6c_object = { 185 .name = NULL, 186 .type = &odhcp6c_object_type, 187 .methods = odhcp6c_object_methods, 188 .n_methods = ARRAY_SIZE(odhcp6c_object_methods), 189 }; 190 191 static void ubus_disconnect_cb(struct ubus_context *ubus) 192 { 193 int ret; 194 195 ret = ubus_reconnect(ubus, NULL); 196 if (ret) { 197 error("Cannot reconnect to ubus: %s", ubus_strerror(ret)); 198 ubus_destroy(ubus); 199 } 200 } 201 202 char *ubus_init(const char* interface) 203 { 204 int ret = 0; 205 206 if (!(ubus = ubus_connect(NULL))) 207 return NULL; 208 209 snprintf(ubus_name, 24, "odhcp6c.%s", interface); 210 odhcp6c_object.name = ubus_name; 211 212 ret = ubus_add_object(ubus, &odhcp6c_object); 213 if (ret) { 214 ubus_destroy(ubus); 215 return (char *)ubus_strerror(ret); 216 } 217 218 ubus->connection_lost = ubus_disconnect_cb; 219 return NULL; 220 } 221 222 struct ubus_context *ubus_get_ctx(void) 223 { 224 return ubus; 225 } 226 227 void ubus_destroy(struct ubus_context *ubus) 228 { 229 notice("Disconnecting from ubus"); 230 231 if (ubus != NULL) 232 ubus_free(ubus); 233 ubus = NULL; 234 235 /* Forces re-initialization when we're reusing the same definitions later on. */ 236 odhcp6c_object.id = 0; 237 odhcp6c_object_type.id = 0; 238 } 239 240 static int ipv6_to_blob(const char *name, 241 const struct in6_addr *addr, size_t cnt) 242 { 243 void *arr = blobmsg_open_array(&b, name); 244 245 for (size_t i = 0; i < cnt; ++i) { 246 char *buf = blobmsg_alloc_string_buffer(&b, NULL, INET6_ADDRSTRLEN); 247 CHECK_ALLOC(buf); 248 inet_ntop(AF_INET6, &addr[i], buf, INET6_ADDRSTRLEN); 249 blobmsg_add_string_buffer(&b); 250 } 251 252 blobmsg_close_array(&b, arr); 253 return UBUS_STATUS_OK; 254 } 255 256 static int fqdn_to_blob(const char *name, const uint8_t *fqdn, size_t len) 257 { 258 size_t buf_size = len > 255 ? 256 : (len + 1); 259 const uint8_t *fqdn_end = fqdn + len; 260 char *buf = NULL; 261 262 void *arr = blobmsg_open_array(&b, name); 263 264 while (fqdn < fqdn_end) { 265 buf = blobmsg_alloc_string_buffer(&b, name, buf_size); 266 CHECK_ALLOC(buf); 267 int l = dn_expand(fqdn, fqdn_end, fqdn, buf, buf_size); 268 if (l < 1) { 269 buf[0] = '\0'; 270 blobmsg_add_string_buffer(&b); 271 break; 272 } 273 buf[l] = '\0'; 274 blobmsg_add_string_buffer(&b); 275 fqdn += l; 276 } 277 278 blobmsg_close_array(&b, arr); 279 return UBUS_STATUS_OK; 280 } 281 282 static int bin_to_blob(uint8_t *opts, size_t len) 283 { 284 uint8_t *oend = opts + len, *odata; 285 uint16_t otype, olen; 286 287 dhcpv6_for_each_option(opts, oend, otype, olen, odata) { 288 char name[14]; 289 char *buf; 290 291 snprintf(name, 14, "OPTION_%hu", otype); 292 buf = blobmsg_alloc_string_buffer(&b, name, olen * 2); 293 CHECK_ALLOC(buf); 294 script_hexlify(buf, odata, olen); 295 blobmsg_add_string_buffer(&b); 296 } 297 return UBUS_STATUS_OK; 298 } 299 300 static int entry_to_blob(const char *name, const void *data, size_t len, enum entry_type type) 301 { 302 const uint8_t *start = data; 303 304 void *arr = blobmsg_open_array(&b, name); 305 306 for (const struct odhcp6c_entry *e = (const struct odhcp6c_entry *)start; 307 (const uint8_t *)e < start + len && 308 (const uint8_t *)odhcp6c_next_entry(e) <= start + len; 309 e = odhcp6c_next_entry(e)) { 310 /* 311 * The only invalid entries allowed to be passed to the script are prefix entries. 312 * This will allow immediate removal of the old ipv6-prefix-assignment that might 313 * otherwise be kept for up to 2 hours (see L-13 requirement of RFC 7084). 314 * 315 * Skip before opening the table: a "continue" after 316 * blobmsg_open_table() would leave an unclosed nested table and 317 * corrupt the emitted blob. 318 */ 319 if (!e->valid && type != ENTRY_PREFIX) 320 continue; 321 322 void *entry = blobmsg_open_table(&b, name); 323 324 char *buf = blobmsg_alloc_string_buffer(&b, "target", INET6_ADDRSTRLEN); 325 CHECK_ALLOC(buf); 326 inet_ntop(AF_INET6, &e->target, buf, INET6_ADDRSTRLEN); 327 blobmsg_add_string_buffer(&b); 328 329 if (type != ENTRY_HOST) { 330 blobmsg_add_u32(&b, "length", e->length); 331 if (type == ENTRY_ROUTE) { 332 if (!IN6_IS_ADDR_UNSPECIFIED(&e->router)) { 333 buf = blobmsg_alloc_string_buffer(&b, "router", INET6_ADDRSTRLEN); 334 CHECK_ALLOC(buf); 335 inet_ntop(AF_INET6, &e->router, buf, INET6_ADDRSTRLEN); 336 blobmsg_add_string_buffer(&b); 337 } 338 339 blobmsg_add_u32(&b, "valid", e->valid); 340 blobmsg_add_u32(&b, "priority", e->priority); 341 } else { 342 blobmsg_add_u32(&b, "valid", e->valid); 343 blobmsg_add_u32(&b, "preferred", e->preferred); 344 blobmsg_add_u32(&b, "t1", e->t1); 345 blobmsg_add_u32(&b, "t2", e->t2); 346 } 347 348 if (type == ENTRY_PREFIX && ntohl(e->iaid) != 1) { 349 blobmsg_add_u32(&b, "iaid", ntohl(e->iaid)); 350 } 351 352 if (type == ENTRY_PREFIX && e->exclusion_length) { 353 buf = blobmsg_alloc_string_buffer(&b, "excluded", INET6_ADDRSTRLEN); 354 CHECK_ALLOC(buf); 355 // '.router' is dual-used: for prefixes it contains the prefix 356 inet_ntop(AF_INET6, &e->router, buf, INET6_ADDRSTRLEN); 357 blobmsg_add_string_buffer(&b); 358 blobmsg_add_u32(&b, "excluded_length", e->exclusion_length); 359 } 360 } 361 362 blobmsg_close_table(&b, entry); 363 } 364 365 blobmsg_close_array(&b, arr); 366 return UBUS_STATUS_OK; 367 } 368 369 static int search_to_blob(const char *name, const uint8_t *start, size_t len) 370 { 371 void *arr = blobmsg_open_array(&b, name); 372 char *buf = NULL; 373 374 for (struct odhcp6c_entry *e = (struct odhcp6c_entry*)start; 375 (uint8_t*)e < &start[len] && 376 (uint8_t*)odhcp6c_next_entry(e) <= &start[len]; 377 e = odhcp6c_next_entry(e)) { 378 if (!e->valid) 379 continue; 380 381 buf = blobmsg_alloc_string_buffer(&b, NULL, e->auxlen+1); 382 CHECK_ALLOC(buf); 383 buf = mempcpy(buf, e->auxtarget, e->auxlen); 384 *buf = '\0'; 385 blobmsg_add_string_buffer(&b); 386 } 387 388 blobmsg_close_array(&b, arr); 389 return UBUS_STATUS_OK; 390 } 391 392 static int s46_to_blob_portparams(const uint8_t *data, size_t len) 393 { 394 uint8_t *odata; 395 uint16_t otype, olen; 396 397 dhcpv6_for_each_option(data, &data[len], otype, olen, odata) { 398 if (otype == DHCPV6_OPT_S46_PORTPARAMS && 399 olen == sizeof(struct dhcpv6_s46_portparams)) { 400 struct dhcpv6_s46_portparams *params = (void*)odata; 401 blobmsg_add_u32(&b, "offset", params->offset); 402 blobmsg_add_u32(&b, "psidlen", params->psid_len); 403 blobmsg_add_u32(&b, "psid", ntohs(params->psid)); 404 } 405 } 406 return UBUS_STATUS_OK; 407 } 408 409 static int s46_to_blob(enum odhcp6c_state state, const uint8_t *data, size_t len) 410 { 411 const char *name = (state == STATE_S46_MAPE) ? "MAPE" : 412 (state == STATE_S46_MAPT) ? "MAPT" : "LW4O6"; 413 414 if (len == 0) 415 return UBUS_STATUS_OK; 416 417 char *buf = NULL; 418 void *arr = blobmsg_open_array(&b, name); 419 420 const char *type = (state == STATE_S46_MAPE) ? "map-e" : 421 (state == STATE_S46_MAPT) ? "map-t" : "lw4o6"; 422 423 uint8_t *odata; 424 uint16_t otype, olen; 425 426 dhcpv6_for_each_option(data, &data[len], otype, olen, odata) { 427 struct dhcpv6_s46_rule *rule = (struct dhcpv6_s46_rule*)odata; 428 struct dhcpv6_s46_v4v6bind *bind = (struct dhcpv6_s46_v4v6bind*)odata; 429 430 void *option = blobmsg_open_table(&b, NULL); 431 432 if (state != STATE_S46_LW && otype == DHCPV6_OPT_S46_RULE && 433 olen >= sizeof(struct dhcpv6_s46_rule)) { 434 struct in6_addr in6 = IN6ADDR_ANY_INIT; 435 436 size_t prefix6len = rule->prefix6_len; 437 prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1; 438 439 if (prefix6len > sizeof(in6) || 440 olen < sizeof(struct dhcpv6_s46_rule) + prefix6len) { 441 blobmsg_close_table(&b, option); 442 continue; 443 } 444 445 memcpy(&in6, rule->ipv6_prefix, prefix6len); 446 447 buf = blobmsg_alloc_string_buffer(&b, "ipv4prefix", INET_ADDRSTRLEN); 448 CHECK_ALLOC(buf); 449 inet_ntop(AF_INET, &rule->ipv4_prefix, buf, INET_ADDRSTRLEN); 450 blobmsg_add_string_buffer(&b); 451 452 buf = blobmsg_alloc_string_buffer(&b, "ipv6prefix", INET6_ADDRSTRLEN); 453 CHECK_ALLOC(buf); 454 inet_ntop(AF_INET6, &in6, buf, INET6_ADDRSTRLEN); 455 blobmsg_add_string_buffer(&b); 456 457 blobmsg_add_u32(&b, "fmr", rule->flags); 458 blobmsg_add_string(&b, "type", type); 459 blobmsg_add_u32(&b, "ealen", rule->ea_len); 460 blobmsg_add_u32(&b, "prefix4len", rule->prefix4_len); 461 blobmsg_add_u32(&b, "prefix6len", rule->prefix6_len); 462 463 s46_to_blob_portparams(&rule->ipv6_prefix[prefix6len], 464 olen - sizeof(*rule) - prefix6len); 465 466 dhcpv6_for_each_option(data, &data[len], otype, olen, odata) { 467 if (state != STATE_S46_MAPT && otype == DHCPV6_OPT_S46_BR && 468 olen == sizeof(struct in6_addr)) { 469 buf = blobmsg_alloc_string_buffer(&b, "br", INET6_ADDRSTRLEN); 470 CHECK_ALLOC(buf); 471 inet_ntop(AF_INET6, odata, buf, INET6_ADDRSTRLEN); 472 blobmsg_add_string_buffer(&b); 473 } else if (state == STATE_S46_MAPT && otype == DHCPV6_OPT_S46_DMR && 474 olen >= sizeof(struct dhcpv6_s46_dmr)) { 475 struct dhcpv6_s46_dmr *dmr = (struct dhcpv6_s46_dmr*)odata; 476 memset(&in6, 0, sizeof(in6)); 477 size_t prefix6len = dmr->dmr_prefix6_len; 478 prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1; 479 480 if (prefix6len > sizeof(in6) || 481 olen < sizeof(struct dhcpv6_s46_dmr) + prefix6len) 482 continue; 483 484 buf = blobmsg_alloc_string_buffer(&b, "dmr", INET6_ADDRSTRLEN); 485 CHECK_ALLOC(buf); 486 inet_ntop(AF_INET6, &in6, buf, INET6_ADDRSTRLEN); 487 blobmsg_add_string_buffer(&b); 488 blobmsg_add_u32(&b, "dmrprefix6len", dmr->dmr_prefix6_len); 489 } 490 } 491 } else if (state == STATE_S46_LW && otype == DHCPV6_OPT_S46_V4V6BIND && 492 olen >= sizeof(struct dhcpv6_s46_v4v6bind)) { 493 struct in6_addr in6 = IN6ADDR_ANY_INIT; 494 495 size_t prefix6len = bind->bindprefix6_len; 496 prefix6len = (prefix6len % 8 == 0) ? prefix6len / 8 : prefix6len / 8 + 1; 497 498 if (prefix6len > sizeof(in6) || 499 olen < sizeof(struct dhcpv6_s46_v4v6bind) + prefix6len) { 500 blobmsg_close_table(&b, option); 501 continue; 502 } 503 504 memcpy(&in6, bind->bind_ipv6_prefix, prefix6len); 505 506 buf = blobmsg_alloc_string_buffer(&b, "ipv4prefix", INET_ADDRSTRLEN); 507 CHECK_ALLOC(buf); 508 inet_ntop(AF_INET, &bind->ipv4_address, buf, INET_ADDRSTRLEN); 509 blobmsg_add_string_buffer(&b); 510 511 buf = blobmsg_alloc_string_buffer(&b, "ipv6prefix", INET6_ADDRSTRLEN); 512 CHECK_ALLOC(buf); 513 inet_ntop(AF_INET6, &in6, buf, INET6_ADDRSTRLEN); 514 blobmsg_add_string_buffer(&b); 515 516 blobmsg_add_string(&b, "type", type); 517 blobmsg_add_u32(&b, "prefix4len", 32); 518 blobmsg_add_u32(&b, "prefix6len", bind->bindprefix6_len); 519 520 s46_to_blob_portparams(&bind->bind_ipv6_prefix[prefix6len], 521 olen - sizeof(*bind) - prefix6len); 522 523 dhcpv6_for_each_option(data, &data[len], otype, olen, odata) { 524 if (otype == DHCPV6_OPT_S46_BR && olen == sizeof(struct in6_addr)) { 525 buf = blobmsg_alloc_string_buffer(&b, "br", INET6_ADDRSTRLEN); 526 CHECK_ALLOC(buf); 527 inet_ntop(AF_INET6, odata, buf, INET6_ADDRSTRLEN); 528 blobmsg_add_string_buffer(&b); 529 } 530 } 531 } 532 blobmsg_close_table(&b, option); 533 } 534 535 blobmsg_close_array(&b, arr); 536 return UBUS_STATUS_OK; 537 } 538 539 static int states_to_blob(void) 540 { 541 char *buf = NULL; 542 size_t dns_len, search_len, custom_len, sntp_ip_len, ntp_ip_len, ntp_dns_len; 543 size_t sip_ip_len, sip_fqdn_len, aftr_name_len, addr_len; 544 size_t s46_mapt_len, s46_mape_len, s46_lw_len, passthru_len; 545 size_t capt_port_ra_len, capt_port_dhcpv6_len; 546 struct in6_addr *addr = odhcp6c_get_state(STATE_SERVER_ADDR, &addr_len); 547 struct in6_addr *dns = odhcp6c_get_state(STATE_DNS, &dns_len); 548 uint8_t *search = odhcp6c_get_state(STATE_SEARCH, &search_len); 549 uint8_t *custom = odhcp6c_get_state(STATE_CUSTOM_OPTS, &custom_len); 550 struct in6_addr *sntp = odhcp6c_get_state(STATE_SNTP_IP, &sntp_ip_len); 551 struct in6_addr *ntp = odhcp6c_get_state(STATE_NTP_IP, &ntp_ip_len); 552 uint8_t *ntp_dns = odhcp6c_get_state(STATE_NTP_FQDN, &ntp_dns_len); 553 struct in6_addr *sip = odhcp6c_get_state(STATE_SIP_IP, &sip_ip_len); 554 uint8_t *sip_fqdn = odhcp6c_get_state(STATE_SIP_FQDN, &sip_fqdn_len); 555 uint8_t *aftr_name = odhcp6c_get_state(STATE_AFTR_NAME, &aftr_name_len); 556 uint8_t *s46_mapt = odhcp6c_get_state(STATE_S46_MAPT, &s46_mapt_len); 557 uint8_t *s46_mape = odhcp6c_get_state(STATE_S46_MAPE, &s46_mape_len); 558 uint8_t *s46_lw = odhcp6c_get_state(STATE_S46_LW, &s46_lw_len); 559 uint8_t *capt_port_ra = odhcp6c_get_state(STATE_CAPT_PORT_RA, &capt_port_ra_len); 560 uint8_t *capt_port_dhcpv6 = odhcp6c_get_state(STATE_CAPT_PORT_DHCPV6, &capt_port_dhcpv6_len); 561 uint8_t *passthru = odhcp6c_get_state(STATE_PASSTHRU, &passthru_len); 562 563 size_t prefix_len, address_len, ra_pref_len, 564 ra_route_len, ra_dns_len, ra_search_len; 565 uint8_t *prefix = odhcp6c_get_state(STATE_IA_PD, &prefix_len); 566 uint8_t *address = odhcp6c_get_state(STATE_IA_NA, &address_len); 567 uint8_t *ra_pref = odhcp6c_get_state(STATE_RA_PREFIX, &ra_pref_len); 568 uint8_t *ra_route = odhcp6c_get_state(STATE_RA_ROUTE, &ra_route_len); 569 uint8_t *ra_dns = odhcp6c_get_state(STATE_RA_DNS, &ra_dns_len); 570 uint8_t *ra_search = odhcp6c_get_state(STATE_RA_SEARCH, &ra_search_len); 571 572 blob_buf_init(&b, BLOBMSG_TYPE_TABLE); 573 574 /* RFC8910 ยง3 */ 575 if (capt_port_ra_len > 0 && capt_port_dhcpv6_len > 0) { 576 if (capt_port_ra_len != capt_port_dhcpv6_len || 577 memcmp(capt_port_dhcpv6, capt_port_ra, capt_port_dhcpv6_len)) 578 error( 579 "%s received via different vectors differ: preferring URI from DHCPv6", 580 CAPT_PORT_URI_STR); 581 } 582 583 blobmsg_add_string(&b, "DHCPV6_STATE", dhcpv6_state_to_str(dhcpv6_get_state())); 584 585 CHECK(ipv6_to_blob("SERVER", addr, addr_len / sizeof(*addr))); 586 CHECK(ipv6_to_blob("RDNSS", dns, dns_len / sizeof(*dns))); 587 CHECK(ipv6_to_blob("SNTP_IP", sntp, sntp_ip_len / sizeof(*sntp))); 588 CHECK(ipv6_to_blob("NTP_IP", ntp, ntp_ip_len / sizeof(*ntp))); 589 CHECK(fqdn_to_blob("NTP_FQDN", ntp_dns, ntp_dns_len)); 590 CHECK(ipv6_to_blob("SIP_IP", sip, sip_ip_len / sizeof(*sip))); 591 CHECK(fqdn_to_blob("DOMAINS", search, search_len)); 592 CHECK(fqdn_to_blob("SIP_DOMAIN", sip_fqdn, sip_fqdn_len)); 593 CHECK(fqdn_to_blob("AFTR", aftr_name, aftr_name_len)); 594 CHECK(s46_to_blob(STATE_S46_MAPE, s46_mape, s46_mape_len)); 595 CHECK(s46_to_blob(STATE_S46_MAPT, s46_mapt, s46_mapt_len)); 596 CHECK(s46_to_blob(STATE_S46_LW, s46_lw, s46_lw_len)); 597 if (capt_port_dhcpv6_len > 0 || capt_port_ra_len > 0) { 598 const uint8_t *uri = capt_port_dhcpv6_len > 0 ? capt_port_dhcpv6 : capt_port_ra; 599 size_t uri_len = capt_port_dhcpv6_len > 0 ? capt_port_dhcpv6_len : capt_port_ra_len; 600 601 /* The captive-portal URI is stored unterminated: it is copied 602 * verbatim from the DHCPv6 option / RA option payload, which is 603 * not guaranteed to be NUL-terminated. It must therefore not be 604 * passed to blobmsg_add_string(), which would call strlen() and 605 * read past the end of the (attacker-controlled) state buffer. */ 606 buf = blobmsg_alloc_string_buffer(&b, CAPT_PORT_URI_STR, uri_len + 1); 607 CHECK_ALLOC(buf); 608 memcpy(buf, uri, uri_len); 609 buf[uri_len] = '\0'; 610 blobmsg_add_string_buffer(&b); 611 } 612 CHECK(bin_to_blob(custom, custom_len)); 613 614 if (odhcp6c_is_bound()) { 615 CHECK(entry_to_blob("PREFIXES", prefix, prefix_len, ENTRY_PREFIX)); 616 CHECK(entry_to_blob("ADDRESSES", address, address_len, ENTRY_ADDRESS)); 617 } 618 619 CHECK(entry_to_blob("RA_ADDRESSES", ra_pref, ra_pref_len, ENTRY_ADDRESS)); 620 CHECK(entry_to_blob("RA_ROUTES", ra_route, ra_route_len, ENTRY_ROUTE)); 621 CHECK(entry_to_blob("RA_DNS", ra_dns, ra_dns_len, ENTRY_HOST)); 622 CHECK(search_to_blob("RA_DOMAINS", ra_search, ra_search_len)); 623 624 blobmsg_add_u32(&b, "RA_HOPLIMIT", ra_get_hoplimit()); 625 blobmsg_add_u32(&b, "RA_MTU", ra_get_mtu()); 626 blobmsg_add_u32(&b, "RA_REACHABLE", ra_get_reachable()); 627 blobmsg_add_u32(&b, "RA_RETRANSMIT", ra_get_retransmit()); 628 629 buf = blobmsg_alloc_string_buffer(&b, "PASSTHRU", passthru_len * 2); 630 CHECK_ALLOC(buf); 631 script_hexlify(buf, passthru, passthru_len); 632 blobmsg_add_string_buffer(&b); 633 634 return UBUS_STATUS_OK; 635 } 636 637 static int ubus_handle_get_stats(struct ubus_context *ctx, _o_unused struct ubus_object *obj, 638 struct ubus_request_data *req, _o_unused const char *method, 639 _o_unused struct blob_attr *msg) 640 { 641 struct dhcpv6_stats stats = dhcpv6_get_stats(); 642 643 blob_buf_init(&b, BLOBMSG_TYPE_TABLE); 644 blobmsg_add_u64(&b, "dhcp_solicit", stats.solicit); 645 blobmsg_add_u64(&b, "dhcp_advertise", stats.advertise); 646 blobmsg_add_u64(&b, "dhcp_request", stats.request); 647 blobmsg_add_u64(&b, "dhcp_confirm", stats.confirm); 648 blobmsg_add_u64(&b, "dhcp_renew", stats.renew); 649 blobmsg_add_u64(&b, "dhcp_rebind", stats.rebind); 650 blobmsg_add_u64(&b, "dhcp_reply", stats.reply); 651 blobmsg_add_u64(&b, "dhcp_release", stats.release); 652 blobmsg_add_u64(&b, "dhcp_decline", stats.decline); 653 blobmsg_add_u64(&b, "dhcp_reconfigure", stats.reconfigure); 654 blobmsg_add_u64(&b, "dhcp_information_request", stats.information_request); 655 blobmsg_add_u64(&b, "dhcp_discarded_packets", stats.discarded_packets); 656 blobmsg_add_u64(&b, "dhcp_transmit_failures", stats.transmit_failures); 657 658 CHECK(ubus_send_reply(ctx, req, b.head)); 659 blob_buf_free(&b); 660 661 return UBUS_STATUS_OK; 662 } 663 664 static int ubus_handle_reset_stats(_o_unused struct ubus_context *ctx, _o_unused struct ubus_object *obj, 665 _o_unused struct ubus_request_data *req, _o_unused const char *method, 666 _o_unused struct blob_attr *msg) 667 { 668 dhcpv6_reset_stats(); 669 670 return UBUS_STATUS_OK; 671 } 672 673 static int ubus_handle_get_state(struct ubus_context *ctx, _o_unused struct ubus_object *obj, 674 struct ubus_request_data *req, _o_unused const char *method, 675 _o_unused struct blob_attr *msg) 676 { 677 CHECK(states_to_blob()); 678 CHECK(ubus_send_reply(ctx, req, b.head)); 679 blob_buf_free(&b); 680 681 return UBUS_STATUS_OK; 682 } 683 684 static int ubus_handle_reconfigure_dhcp_rtx(enum config_dhcp_msg msg, struct blob_attr* table) 685 { 686 struct blob_attr *cur = NULL; 687 uint32_t value = 0; 688 size_t rem = 0; 689 690 if (msg >= CONFIG_DHCP_MAX || blobmsg_data_len(table) == 0) 691 return UBUS_STATUS_INVALID_ARGUMENT; 692 693 blobmsg_for_each_attr(cur, table, rem) { 694 if (!blobmsg_check_attr(cur, true) || blobmsg_type(cur) != BLOBMSG_TYPE_INT32) 695 return UBUS_STATUS_INVALID_ARGUMENT; 696 697 const char* name = blobmsg_name(cur); 698 if (strcmp("delay_max", name) == 0) { 699 value = blobmsg_get_u32(cur); 700 if (!config_set_rtx_delay_max(msg, value)) 701 return UBUS_STATUS_INVALID_ARGUMENT; 702 } else if (strcmp("timeout_init", name) == 0 ) { 703 value = blobmsg_get_u32(cur); 704 if (!config_set_rtx_timeout_init(msg, value)) 705 return UBUS_STATUS_INVALID_ARGUMENT; 706 } else if (strcmp("timeout_max", name) == 0 ) { 707 value = blobmsg_get_u32(cur); 708 if (!config_set_rtx_timeout_max(msg, value)) 709 return UBUS_STATUS_INVALID_ARGUMENT; 710 } else if (strcmp("rc_max", name) == 0) { 711 value = blobmsg_get_u32(cur); 712 if (!config_set_rtx_rc_max(msg, value)) 713 return UBUS_STATUS_INVALID_ARGUMENT; 714 } else { 715 return UBUS_STATUS_INVALID_ARGUMENT; 716 } 717 } 718 719 return UBUS_STATUS_OK; 720 } 721 722 static int ubus_handle_reconfigure_dhcp(_o_unused struct ubus_context *ctx, _o_unused struct ubus_object *obj, 723 _o_unused struct ubus_request_data *req, _o_unused const char *method, 724 struct blob_attr *msg) 725 { 726 const struct blobmsg_policy *policy = reconfigure_dhcp_policy; 727 struct blob_attr *tb[RECONFIGURE_DHCP_ATTR_MAX]; 728 struct blob_attr *cur = NULL; 729 struct blob_attr *elem = NULL; 730 char *string = NULL; 731 uint32_t value = 0; 732 uint32_t index = 0; 733 bool enabled = false; 734 bool valid_args = false; 735 bool need_reinit = false; 736 737 if (blobmsg_parse(policy, RECONFIGURE_DHCP_ATTR_MAX, tb, blob_data(msg), blob_len(msg))) 738 return UBUS_STATUS_INVALID_ARGUMENT; 739 740 if ((cur = tb[RECONFIGURE_DHCP_ATTR_DSCP])) { 741 value = blobmsg_get_u32(cur); 742 if (!config_set_dscp(value)) 743 return UBUS_STATUS_INVALID_ARGUMENT; 744 need_reinit = true; 745 valid_args = true; 746 } 747 748 if ((cur = tb[RECONFIGURE_DHCP_ATTR_RELEASE])) { 749 enabled = blobmsg_get_bool(cur); 750 config_set_release(enabled); 751 valid_args = true; 752 } 753 754 if ((cur = tb[RECONFIGURE_DHCP_ATTR_SOL_TIMEOUT])) { 755 value = blobmsg_get_u32(cur); 756 if (!config_set_rtx_timeout_max(CONFIG_DHCP_SOLICIT, value)) 757 return UBUS_STATUS_INVALID_ARGUMENT; 758 need_reinit = true; 759 valid_args = true; 760 } 761 762 if ((cur = tb[RECONFIGURE_DHCP_ATTR_SK_PRIORITY])) { 763 value = blobmsg_get_u32(cur); 764 if (!config_set_sk_priority(value)) 765 return UBUS_STATUS_INVALID_ARGUMENT; 766 need_reinit = true; 767 valid_args = true; 768 } 769 770 if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_REQUESTED])) { 771 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, false)) 772 return UBUS_STATUS_INVALID_ARGUMENT; 773 774 config_clear_requested_options(); 775 776 blobmsg_for_each_attr(elem, cur, index) { 777 if (blobmsg_type(elem) != BLOBMSG_TYPE_INT32) 778 return UBUS_STATUS_INVALID_ARGUMENT; 779 780 value = blobmsg_get_u32(elem); 781 if (!config_add_requested_options(value)) 782 return UBUS_STATUS_INVALID_ARGUMENT; 783 } 784 785 need_reinit = true; 786 valid_args = true; 787 } 788 789 if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_STRICT])) { 790 enabled = blobmsg_get_bool(cur); 791 config_set_client_options(DHCPV6_STRICT_OPTIONS, enabled); 792 need_reinit = true; 793 valid_args = true; 794 } 795 796 if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_RECONFIGURE])) { 797 enabled = blobmsg_get_bool(cur); 798 config_set_client_options(DHCPV6_ACCEPT_RECONFIGURE, enabled); 799 need_reinit = true; 800 valid_args = true; 801 } 802 803 if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_FQDN])) { 804 enabled = blobmsg_get_bool(cur); 805 config_set_client_options(DHCPV6_CLIENT_FQDN, enabled); 806 need_reinit = true; 807 valid_args = true; 808 } 809 810 if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_UNICAST])) { 811 enabled = blobmsg_get_bool(cur); 812 config_set_client_options(DHCPV6_IGNORE_OPT_UNICAST, enabled); 813 need_reinit = true; 814 valid_args = true; 815 } 816 817 if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_SEND])) { 818 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, false)) 819 return UBUS_STATUS_INVALID_ARGUMENT; 820 821 config_clear_send_options(); 822 823 blobmsg_for_each_attr(elem, cur, index) { 824 if (blobmsg_type(elem) != BLOBMSG_TYPE_STRING) 825 return UBUS_STATUS_INVALID_ARGUMENT; 826 827 string = blobmsg_get_string(elem); 828 if (string == NULL || !config_add_send_options(string)) 829 return UBUS_STATUS_INVALID_ARGUMENT; 830 } 831 832 need_reinit = true; 833 valid_args = true; 834 } 835 836 if ((cur = tb[RECONFIGURE_DHCP_ATTR_REQ_ADDRESSES])) { 837 string = blobmsg_get_string(cur); 838 if (string == NULL || !config_set_request_addresses(string)) 839 return UBUS_STATUS_INVALID_ARGUMENT; 840 841 need_reinit = true; 842 valid_args = true; 843 } 844 845 if ((cur = tb[RECONFIGURE_DHCP_ATTR_REQ_PREFIXES])) { 846 value = blobmsg_get_u32(cur); 847 848 if (!config_set_request_prefix(value, 1)) 849 return UBUS_STATUS_INVALID_ARGUMENT; 850 851 need_reinit = true; 852 valid_args = true; 853 } 854 855 if ((cur = tb[RECONFIGURE_DHCP_ATTR_STATEFUL])) { 856 enabled = blobmsg_get_bool(cur); 857 config_set_stateful_only(enabled); 858 need_reinit = true; 859 valid_args = true; 860 } 861 862 if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_SOLICIT])) { 863 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_SOLICIT, cur)) 864 return UBUS_STATUS_INVALID_ARGUMENT; 865 866 need_reinit = true; 867 valid_args = true; 868 } 869 870 if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_REQUEST])) { 871 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_REQUEST, cur)) 872 return UBUS_STATUS_INVALID_ARGUMENT; 873 874 need_reinit = true; 875 valid_args = true; 876 } 877 878 if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_RENEW])) { 879 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_RENEW, cur)) 880 return UBUS_STATUS_INVALID_ARGUMENT; 881 882 need_reinit = true; 883 valid_args = true; 884 } 885 886 if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_REBIND])) { 887 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_REBIND, cur)) 888 return UBUS_STATUS_INVALID_ARGUMENT; 889 890 need_reinit = true; 891 valid_args = true; 892 } 893 894 if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_RELEASE])) { 895 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_RELEASE, cur)) 896 return UBUS_STATUS_INVALID_ARGUMENT; 897 898 need_reinit = true; 899 valid_args = true; 900 } 901 902 if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_DECLINE])) { 903 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_DECLINE, cur)) 904 return UBUS_STATUS_INVALID_ARGUMENT; 905 906 need_reinit = true; 907 valid_args = true; 908 } 909 910 if ((cur = tb[RECONFIGURE_DHCP_ATTR_MSG_INFO_REQ])) { 911 if (ubus_handle_reconfigure_dhcp_rtx(CONFIG_DHCP_INFO_REQ, cur)) 912 return UBUS_STATUS_INVALID_ARGUMENT; 913 914 need_reinit = true; 915 valid_args = true; 916 } 917 918 if ((cur = tb[RECONFIGURE_DHCP_ATTR_IRT_MIN])) { 919 value = blobmsg_get_u32(cur); 920 921 if (!config_set_irt_min(value)) 922 return UBUS_STATUS_INVALID_ARGUMENT; 923 924 need_reinit = true; 925 valid_args = true; 926 } 927 928 if ((cur = tb[RECONFIGURE_DHCP_ATTR_IRT_DEFAULT])) { 929 value = blobmsg_get_u32(cur); 930 931 if (!config_set_irt_default(value)) 932 return UBUS_STATUS_INVALID_ARGUMENT; 933 934 need_reinit = true; 935 valid_args = true; 936 } 937 938 if ((cur = tb[RECONFIGURE_DHCP_ATTR_RAND_FACTOR])) { 939 value = blobmsg_get_u32(cur); 940 941 if (!config_set_rand_factor(value)) 942 return UBUS_STATUS_INVALID_ARGUMENT; 943 944 need_reinit = true; 945 valid_args = true; 946 } 947 948 if ((cur = tb[RECONFIGURE_DHCP_ATTR_AUTH_PROTO])) { 949 string = blobmsg_get_string(cur); 950 951 if (string == NULL || !config_set_auth_protocol(string)) 952 return UBUS_STATUS_INVALID_ARGUMENT; 953 954 need_reinit = true; 955 valid_args = true; 956 } 957 958 if ((cur = tb[RECONFIGURE_DHCP_ATTR_AUTH_TOKEN])) { 959 string = blobmsg_get_string(cur); 960 961 if (string == NULL || !config_set_auth_token(string)) 962 return UBUS_STATUS_INVALID_ARGUMENT; 963 964 need_reinit = true; 965 valid_args = true; 966 } 967 968 if (need_reinit) 969 raise(SIGUSR2); 970 971 return valid_args ? UBUS_STATUS_OK : UBUS_STATUS_INVALID_ARGUMENT; 972 } 973 974 static int ubus_handle_renew(_o_unused struct ubus_context *ctx, _o_unused struct ubus_object *obj, 975 _o_unused struct ubus_request_data *req, _o_unused const char *method, 976 _o_unused struct blob_attr *msg) 977 { 978 raise(SIGUSR1); 979 return UBUS_STATUS_OK; 980 } 981 982 static int ubus_handle_release(_o_unused struct ubus_context *ctx, _o_unused struct ubus_object *obj, 983 _o_unused struct ubus_request_data *req, _o_unused const char *method, 984 _o_unused struct blob_attr *msg) 985 { 986 raise(SIGUSR2); 987 return UBUS_STATUS_OK; 988 } 989 990 int ubus_dhcp_event(const char *status) 991 { 992 if (!ubus || !odhcp6c_object.has_subscribers) 993 return UBUS_STATUS_UNKNOWN_ERROR; 994 995 CHECK(states_to_blob()); 996 CHECK(ubus_notify(ubus, &odhcp6c_object, status, b.head, -1)); 997 blob_buf_free(&b); 998 999 return UBUS_STATUS_OK; 1000 } 1001
This page was automatically generated by LXR 0.3.1. • OpenWrt