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 <limits.h> 64 #include <resolv.h> 65 #include <stdlib.h> 66 #include <string.h> 67 68 #include "config.h" 69 #include "odhcp6c.h" 70 71 #define ARRAY_SEP " ,\t" 72 73 static struct config_dhcp config_dhcp; 74 75 struct config_dhcp *config_dhcp_get(void) { 76 return &config_dhcp; 77 } 78 79 void config_dhcp_reset(void) { 80 config_dhcp.log_level = LOG_WARNING; 81 config_dhcp.release = true; 82 config_dhcp.dscp = 0; 83 config_dhcp.sk_prio = 0; 84 config_dhcp.stateful_only_mode = false; 85 config_dhcp.ia_na_mode = IA_MODE_TRY; 86 config_dhcp.ia_pd_mode = IA_MODE_NONE; 87 config_dhcp.client_options = DHCPV6_CLIENT_FQDN | DHCPV6_ACCEPT_RECONFIGURE; 88 config_dhcp.allow_slaac_only = true; 89 config_dhcp.oro_user_cnt = 0; 90 memset(config_dhcp.message_rtx, 0, sizeof(config_dhcp.message_rtx)); 91 config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].delay_max = DHCPV6_MAX_DELAY; 92 config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].timeout_init = DHCPV6_SOL_INIT_RT; 93 config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].timeout_max = DHCPV6_SOL_MAX_RT; 94 config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].timeout_init = DHCPV6_REQ_INIT_RT; 95 config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].timeout_max = DHCPV6_REQ_MAX_RT; 96 config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].rc_max = DHCPV6_REQ_MAX_RC; 97 config_dhcp.message_rtx[CONFIG_DHCP_RENEW].timeout_init = DHCPV6_REN_INIT_RT; 98 config_dhcp.message_rtx[CONFIG_DHCP_RENEW].timeout_max = DHCPV6_REN_MAX_RT; 99 config_dhcp.message_rtx[CONFIG_DHCP_REBIND].timeout_init = DHCPV6_REB_INIT_RT; 100 config_dhcp.message_rtx[CONFIG_DHCP_REBIND].timeout_max = DHCPV6_REB_MAX_RT; 101 config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].delay_max = DHCPV6_MAX_DELAY; 102 config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].timeout_init = DHCPV6_INF_INIT_RT; 103 config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].timeout_max = DHCPV6_INF_MAX_RT; 104 config_dhcp.message_rtx[CONFIG_DHCP_RELEASE].timeout_init = DHCPV6_REL_INIT_RT; 105 config_dhcp.message_rtx[CONFIG_DHCP_RELEASE].rc_max = DHCPV6_REL_MAX_RC; 106 config_dhcp.message_rtx[CONFIG_DHCP_DECLINE].timeout_init = DHCPV6_DEC_INIT_RT; 107 config_dhcp.message_rtx[CONFIG_DHCP_DECLINE].rc_max = DHCPV6_DEC_MAX_RC; 108 config_dhcp.irt_default = DHCPV6_IRT_DEFAULT; 109 config_dhcp.irt_min = DHCPV6_IRT_MIN; 110 config_dhcp.rand_factor = DHCPV6_RAND_FACTOR; 111 config_dhcp.auth_protocol = AUTH_PROT_RKAP; 112 free(config_dhcp.auth_token); 113 config_dhcp.auth_token = NULL; 114 } 115 116 void config_set_release(bool enable) { 117 config_dhcp.release = enable; 118 } 119 120 bool config_set_dscp(unsigned int value) { 121 if (value > 63) { 122 error("Invalid DSCP value"); 123 return false; 124 } 125 config_dhcp.dscp = value; 126 return true; 127 } 128 129 bool config_set_sk_priority(unsigned int priority) { 130 if (priority > 6) { 131 error("Invalid SK priority value"); 132 return false; 133 } 134 config_dhcp.sk_prio = priority; 135 return true; 136 } 137 138 void config_set_client_options(enum dhcpv6_config option, bool enable) { 139 if (enable) { 140 config_dhcp.client_options |= option; 141 } else { 142 config_dhcp.client_options &= ~option; 143 } 144 } 145 146 bool config_set_request_addresses(char* mode) { 147 if (!strcmp(mode, "force")) { 148 config_dhcp.ia_na_mode = IA_MODE_FORCE; 149 config_dhcp.allow_slaac_only = false; 150 } else if (!strcmp(mode, "none")) { 151 config_dhcp.ia_na_mode = IA_MODE_NONE; 152 } else if (!strcmp(mode, "try")) { 153 config_dhcp.ia_na_mode = IA_MODE_TRY; 154 } else { 155 error("Invalid IA_NA Request Addresses mode"); 156 return false; 157 } 158 159 return true; 160 } 161 162 bool config_set_request_prefix(unsigned int length, unsigned int id) { 163 struct odhcp6c_request_prefix prefix = {0}; 164 165 odhcp6c_clear_state(STATE_IA_PD_INIT); 166 167 if (config_dhcp.ia_pd_mode != IA_MODE_FORCE) 168 config_dhcp.ia_pd_mode = length > 128 ? IA_MODE_NONE : IA_MODE_TRY; 169 170 if (length <= 128) { 171 prefix.length = length; 172 prefix.iaid = htonl(id); 173 174 if (odhcp6c_add_state(STATE_IA_PD_INIT, &prefix, sizeof(prefix))) { 175 error("Failed to set request IPv6-Prefix"); 176 return false; 177 } 178 } 179 180 return true; 181 } 182 183 void config_set_force_prefix(bool enable) { 184 if (enable) { 185 config_dhcp.allow_slaac_only = false; 186 config_dhcp.ia_pd_mode = IA_MODE_FORCE; 187 } else { 188 config_dhcp.ia_pd_mode = IA_MODE_NONE; 189 } 190 } 191 192 void config_set_stateful_only(bool enable) { 193 config_dhcp.stateful_only_mode = enable; 194 } 195 196 void config_set_allow_slaac_only(bool value) { 197 config_dhcp.allow_slaac_only = value; 198 } 199 200 void config_clear_requested_options(void) { 201 config_dhcp.oro_user_cnt = 0; 202 } 203 204 bool config_add_requested_options(unsigned int option) { 205 if (option > UINT16_MAX) { 206 error("Invalid requested option"); 207 return false; 208 } 209 210 option = htons(option); 211 if (odhcp6c_insert_state(STATE_ORO, 0, &option, 2)) { 212 error("Failed to set requested option"); 213 return false; 214 } 215 config_dhcp.oro_user_cnt++; 216 return true; 217 } 218 219 void config_clear_send_options(void) { 220 odhcp6c_clear_state(STATE_OPTS); 221 } 222 223 bool config_add_send_options(const char *option) { 224 char *dup = strdup(option); 225 bool ok; 226 227 if (!dup) 228 return false; 229 230 ok = (config_parse_opt(dup) == 0); 231 free(dup); 232 233 return ok; 234 } 235 236 bool config_set_rtx_delay_max(enum config_dhcp_msg msg, unsigned int value) 237 { 238 if (msg >= CONFIG_DHCP_MAX || value > UINT8_MAX) { 239 error("Invalid retransmission Maximum Delay value"); 240 return false; 241 } 242 config_dhcp.message_rtx[msg].delay_max = value; 243 return true; 244 } 245 246 bool config_set_rtx_timeout_init(enum config_dhcp_msg msg, unsigned int value) 247 { 248 if (msg >= CONFIG_DHCP_MAX || value > UINT8_MAX || value == 0) { 249 error("Invalid retransmission Initial Timeout value"); 250 return false; 251 } 252 config_dhcp.message_rtx[msg].timeout_init = value; 253 return true; 254 } 255 256 bool config_set_rtx_timeout_max(enum config_dhcp_msg msg, unsigned int value) 257 { 258 if (msg >= CONFIG_DHCP_MAX || value > UINT16_MAX) { 259 error("Invalid retransmission Maximum Timeout value"); 260 return false; 261 } 262 config_dhcp.message_rtx[msg].timeout_max = value; 263 return true; 264 } 265 266 bool config_set_rtx_rc_max(enum config_dhcp_msg msg, unsigned int value) 267 { 268 if (msg >= CONFIG_DHCP_MAX || value > UINT8_MAX) { 269 error("Invalid retransmission Retry Attempt value"); 270 return false; 271 } 272 config_dhcp.message_rtx[msg].rc_max = value; 273 return true; 274 } 275 276 bool config_set_irt_default(unsigned int value) 277 { 278 if (value == 0) { 279 error("Invalid Default Information Refresh Time value"); 280 return false; 281 } 282 config_dhcp.irt_default = value; 283 return true; 284 } 285 286 bool config_set_irt_min(unsigned int value) 287 { 288 if (value == 0) { 289 error("Invalid Minimum Information Refresh Time value"); 290 return false; 291 } 292 config_dhcp.irt_min = value; 293 return true; 294 } 295 296 bool config_set_rand_factor(unsigned int value) 297 { 298 if (value > 999 || value < 10) { 299 error("Invalid Random Factor value"); 300 return false; 301 } 302 config_dhcp.rand_factor = value; 303 return true; 304 } 305 306 bool config_set_auth_protocol(const char* protocol) 307 { 308 if (!strcmp(protocol, "None")) { 309 config_dhcp.auth_protocol = AUTH_PROT_NONE; 310 } else if (!strcmp(protocol, "ConfigurationToken")) { 311 config_dhcp.auth_protocol = AUTH_PROT_TOKEN; 312 } else if (!strcmp(protocol, "ReconfigureKeyAuthentication")) { 313 config_dhcp.auth_protocol = AUTH_PROT_RKAP; 314 } else { 315 error("Invalid Authentication protocol"); 316 return false; 317 } 318 319 return true; 320 } 321 322 bool config_set_auth_token(const char* token) 323 { 324 free(config_dhcp.auth_token); 325 326 config_dhcp.auth_token = strdup(token); 327 return config_dhcp.auth_token != NULL; 328 } 329 330 void config_set_client_opt_cfg(struct odhcp6c_opt_cfg *opt_cfg) 331 { 332 config_dhcp.strict_rfc7550 = opt_cfg->strict_rfc7550 != 0; 333 } 334 335 static int config_parse_opt_u8(const char *src, uint8_t **dst) 336 { 337 int len = strlen(src); 338 339 uint8_t *tmp = realloc(*dst, len/2); 340 if (!tmp) 341 return -1; 342 *dst = tmp; 343 344 return script_unhexlify(*dst, len, src); 345 } 346 347 static int config_parse_opt_string(char *src, uint8_t **dst, const bool array) 348 { 349 int o_len = 0; 350 char *sep = strpbrk(src, ARRAY_SEP); 351 352 if (sep && !array) 353 return -1; 354 355 do { 356 if (sep) { 357 *sep = 0; 358 sep++; 359 } 360 361 int len = strlen(src); 362 363 uint8_t *tmp = realloc(*dst, o_len + len); 364 if (!tmp) 365 return -1; 366 *dst = tmp; 367 368 memcpy(&((*dst)[o_len]), src, len); 369 370 o_len += len; 371 src = sep; 372 373 if (sep) 374 sep = strpbrk(src, ARRAY_SEP); 375 } while (src); 376 377 return o_len; 378 } 379 380 static int config_parse_opt_dns_string(char *src, uint8_t **dst, const bool array) 381 { 382 int o_len = 0; 383 char *sep = strpbrk(src, ARRAY_SEP); 384 385 if (sep && !array) 386 return -1; 387 388 do { 389 uint8_t tmp[256]; 390 391 if (sep) { 392 *sep = 0; 393 sep++; 394 } 395 396 int len = dn_comp(src, tmp, sizeof(tmp), NULL, NULL); 397 if (len < 0) 398 return -1; 399 400 uint8_t *dst_tmp = realloc(*dst, o_len + len); 401 if (!dst_tmp) 402 return -1; 403 *dst = dst_tmp; 404 405 memcpy(&((*dst)[o_len]), tmp, len); 406 407 o_len += len; 408 src = sep; 409 410 if (sep) 411 sep = strpbrk(src, ARRAY_SEP); 412 } while (src); 413 414 return o_len; 415 } 416 417 static int config_parse_opt_ip6(char *src, uint8_t **dst, const bool array) 418 { 419 int o_len = 0; 420 char *sep = strpbrk(src, ARRAY_SEP); 421 422 if (sep && !array) 423 return -1; 424 425 do { 426 int len = sizeof(struct in6_addr); 427 428 if (sep) { 429 *sep = 0; 430 sep++; 431 } 432 433 uint8_t *tmp = realloc(*dst, o_len + len); 434 if (!tmp) 435 return -1; 436 *dst = tmp; 437 438 if (inet_pton(AF_INET6, src, &((*dst)[o_len])) < 1) 439 return -1; 440 441 o_len += len; 442 src = sep; 443 444 if (sep) 445 sep = strpbrk(src, ARRAY_SEP); 446 } while (src); 447 448 return o_len; 449 } 450 451 static int config_parse_opt_user_class(char *src, uint8_t **dst, const bool array) 452 { 453 int o_len = 0; 454 char *sep = strpbrk(src, ARRAY_SEP); 455 456 if (sep && !array) 457 return -1; 458 459 do { 460 if (sep) { 461 *sep = 0; 462 sep++; 463 } 464 uint16_t str_len = strlen(src); 465 466 uint8_t *tmp = realloc(*dst, o_len + str_len + 2); 467 if (!tmp) 468 return -1; 469 *dst = tmp; 470 471 struct user_class { 472 uint16_t len; 473 uint8_t data[]; 474 } *e = (struct user_class *)&((*dst)[o_len]); 475 476 e->len = ntohs(str_len); 477 memcpy(e->data, src, str_len); 478 479 o_len += str_len + 2; 480 src = sep; 481 482 if (sep) 483 sep = strpbrk(src, ARRAY_SEP); 484 } while (src); 485 486 return o_len; 487 } 488 489 static uint8_t *config_state_find_opt(const uint16_t code) 490 { 491 size_t opts_len; 492 uint8_t *odata, *opts = odhcp6c_get_state(STATE_OPTS, &opts_len); 493 uint16_t otype, olen; 494 495 dhcpv6_for_each_option(opts, &opts[opts_len], otype, olen, odata) { 496 if (otype == code) 497 return &odata[-4]; 498 } 499 500 return NULL; 501 } 502 503 int config_add_opt(const uint16_t code, const uint8_t *data, const uint16_t len) 504 { 505 struct { 506 uint16_t code; 507 uint16_t len; 508 } opt_hdr = { htons(code), htons(len) }; 509 510 if (config_state_find_opt(code)) 511 return -1; 512 513 if (odhcp6c_add_state(STATE_OPTS, &opt_hdr, sizeof(opt_hdr)) || 514 odhcp6c_add_state(STATE_OPTS, data, len)) { 515 error("Failed to add option %hu", code); 516 return 1; 517 } 518 519 return 0; 520 } 521 522 int config_parse_opt_data(char *data, uint8_t **dst, const unsigned int type, 523 const bool array) 524 { 525 int ret = 0; 526 527 switch (type) { 528 case OPT_U8: 529 ret = config_parse_opt_u8(data, dst); 530 break; 531 532 case OPT_STR: 533 ret = config_parse_opt_string(data, dst, array); 534 break; 535 536 case OPT_DNS_STR: 537 ret = config_parse_opt_dns_string(data, dst, array); 538 break; 539 540 case OPT_IP6: 541 ret = config_parse_opt_ip6(data, dst, array); 542 break; 543 544 case OPT_USER_CLASS: 545 ret = config_parse_opt_user_class(data, dst, array); 546 break; 547 548 default: 549 ret = -1; 550 break; 551 } 552 553 return ret; 554 } 555 556 int config_parse_opt(char *opt) 557 { 558 uint32_t optn; 559 char *data; 560 uint8_t *payload = NULL; 561 int payload_len; 562 unsigned int type = OPT_U8; 563 bool array = false; 564 struct odhcp6c_opt *dopt = NULL; 565 int ret = -1; 566 567 data = strpbrk(opt, ":"); 568 if (!data) 569 return -1; 570 571 *data = '\0'; 572 data++; 573 574 if (strlen(opt) == 0 || strlen(data) == 0) 575 return -1; 576 577 dopt = odhcp6c_find_opt_by_name(opt); 578 if (!dopt) { 579 char *e; 580 optn = strtoul(opt, &e, 0); 581 if (*e || e == opt || optn > USHRT_MAX) 582 return -1; 583 584 dopt = odhcp6c_find_opt(optn); 585 } else { 586 optn = dopt->code; 587 } 588 589 /* Check if the type for the content is well-known */ 590 if (dopt) { 591 /* Refuse internal options */ 592 if (dopt->flags & OPT_INTERNAL) 593 return -1; 594 595 type = dopt->flags & OPT_MASK_SIZE; 596 array = ((dopt->flags & OPT_ARRAY) == OPT_ARRAY) ? true : false; 597 } else if (data[0] == '"' || data[0] == '\'') { 598 char *end = strrchr(data + 1, data[0]); 599 600 if (end && (end == (data + strlen(data) - 1))) { 601 /* Raw option is specified as a string */ 602 type = OPT_STR; 603 data++; 604 *end = '\0'; 605 } 606 } 607 608 payload_len = config_parse_opt_data(data, &payload, type, array); 609 if (payload_len > 0) 610 ret = config_add_opt(optn, payload, payload_len); 611 612 free(payload); 613 614 return ret; 615 } 616 617 void config_apply_dhcp_rtx(struct dhcpv6_retx* dhcpv6_retx) 618 { 619 dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_delay = config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].delay_max; 620 dhcpv6_retx[DHCPV6_MSG_SOLICIT].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].timeout_init; 621 dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].timeout_max; 622 dhcpv6_retx[DHCPV6_MSG_REQUEST].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].timeout_init; 623 dhcpv6_retx[DHCPV6_MSG_REQUEST].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].timeout_max; 624 dhcpv6_retx[DHCPV6_MSG_REQUEST].max_rc = config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].rc_max; 625 dhcpv6_retx[DHCPV6_MSG_RENEW].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_RENEW].timeout_init; 626 dhcpv6_retx[DHCPV6_MSG_RENEW].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_RENEW].timeout_max; 627 dhcpv6_retx[DHCPV6_MSG_REBIND].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REBIND].timeout_init; 628 dhcpv6_retx[DHCPV6_MSG_REBIND].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REBIND].timeout_max; 629 dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_delay = config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].delay_max; 630 dhcpv6_retx[DHCPV6_MSG_INFO_REQ].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].timeout_init; 631 dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].timeout_max; 632 dhcpv6_retx[DHCPV6_MSG_RELEASE].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_RELEASE].timeout_init; 633 dhcpv6_retx[DHCPV6_MSG_RELEASE].max_rc = config_dhcp.message_rtx[CONFIG_DHCP_RELEASE].rc_max; 634 dhcpv6_retx[DHCPV6_MSG_DECLINE].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_DECLINE].timeout_init; 635 dhcpv6_retx[DHCPV6_MSG_DECLINE].max_rc = config_dhcp.message_rtx[CONFIG_DHCP_DECLINE].rc_max; 636 } 637
This page was automatically generated by LXR 0.3.1. • OpenWrt