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(char* option) { 224 return (config_parse_opt(option) == 0); 225 } 226 227 bool config_set_rtx_delay_max(enum config_dhcp_msg msg, unsigned int value) 228 { 229 if (msg >= CONFIG_DHCP_MAX || value > UINT8_MAX) { 230 error("Invalid retransmission Maximum Delay value"); 231 return false; 232 } 233 config_dhcp.message_rtx[msg].delay_max = value; 234 return true; 235 } 236 237 bool config_set_rtx_timeout_init(enum config_dhcp_msg msg, unsigned int value) 238 { 239 if (msg >= CONFIG_DHCP_MAX || value > UINT8_MAX || value == 0) { 240 error("Invalid retransmission Initial Timeout value"); 241 return false; 242 } 243 config_dhcp.message_rtx[msg].timeout_init = value; 244 return true; 245 } 246 247 bool config_set_rtx_timeout_max(enum config_dhcp_msg msg, unsigned int value) 248 { 249 if (msg >= CONFIG_DHCP_MAX || value > UINT16_MAX) { 250 error("Invalid retransmission Maximum Timeout value"); 251 return false; 252 } 253 config_dhcp.message_rtx[msg].timeout_max = value; 254 return true; 255 } 256 257 bool config_set_rtx_rc_max(enum config_dhcp_msg msg, unsigned int value) 258 { 259 if (msg >= CONFIG_DHCP_MAX || value > UINT8_MAX) { 260 error("Invalid retransmission Retry Attempt value"); 261 return false; 262 } 263 config_dhcp.message_rtx[msg].rc_max = value; 264 return true; 265 } 266 267 bool config_set_irt_default(unsigned int value) 268 { 269 if (value == 0) { 270 error("Invalid Default Information Refresh Time value"); 271 return false; 272 } 273 config_dhcp.irt_default = value; 274 return true; 275 } 276 277 bool config_set_irt_min(unsigned int value) 278 { 279 if (value == 0) { 280 error("Invalid Minimum Information Refresh Time value"); 281 return false; 282 } 283 config_dhcp.irt_min = value; 284 return true; 285 } 286 287 bool config_set_rand_factor(unsigned int value) 288 { 289 if (value > 999 || value < 10) { 290 error("Invalid Random Factor value"); 291 return false; 292 } 293 config_dhcp.rand_factor = value; 294 return true; 295 } 296 297 bool config_set_auth_protocol(const char* protocol) 298 { 299 if (!strcmp(protocol, "None")) { 300 config_dhcp.auth_protocol = AUTH_PROT_NONE; 301 } else if (!strcmp(protocol, "ConfigurationToken")) { 302 config_dhcp.auth_protocol = AUTH_PROT_TOKEN; 303 } else if (!strcmp(protocol, "ReconfigureKeyAuthentication")) { 304 config_dhcp.auth_protocol = AUTH_PROT_RKAP; 305 } else { 306 error("Invalid Authentication protocol"); 307 return false; 308 } 309 310 return true; 311 } 312 313 bool config_set_auth_token(const char* token) 314 { 315 free(config_dhcp.auth_token); 316 317 config_dhcp.auth_token = strdup(token); 318 return config_dhcp.auth_token != NULL; 319 } 320 321 static int config_parse_opt_u8(const char *src, uint8_t **dst) 322 { 323 int len = strlen(src); 324 325 uint8_t *tmp = realloc(*dst, len/2); 326 if (!tmp) 327 return -1; 328 *dst = tmp; 329 330 return script_unhexlify(*dst, len, src); 331 } 332 333 static int config_parse_opt_string(const char *src, uint8_t **dst, const bool array) 334 { 335 int o_len = 0; 336 char *sep = strpbrk(src, ARRAY_SEP); 337 338 if (sep && !array) 339 return -1; 340 341 do { 342 if (sep) { 343 *sep = 0; 344 sep++; 345 } 346 347 int len = strlen(src); 348 349 uint8_t *tmp = realloc(*dst, o_len + len); 350 if (!tmp) 351 return -1; 352 *dst = tmp; 353 354 memcpy(&((*dst)[o_len]), src, len); 355 356 o_len += len; 357 src = sep; 358 359 if (sep) 360 sep = strpbrk(src, ARRAY_SEP); 361 } while (src); 362 363 return o_len; 364 } 365 366 static int config_parse_opt_dns_string(const char *src, uint8_t **dst, const bool array) 367 { 368 int o_len = 0; 369 char *sep = strpbrk(src, ARRAY_SEP); 370 371 if (sep && !array) 372 return -1; 373 374 do { 375 uint8_t tmp[256]; 376 377 if (sep) { 378 *sep = 0; 379 sep++; 380 } 381 382 int len = dn_comp(src, tmp, sizeof(tmp), NULL, NULL); 383 if (len < 0) 384 return -1; 385 386 uint8_t *dst_tmp = realloc(*dst, o_len + len); 387 if (!dst_tmp) 388 return -1; 389 *dst = dst_tmp; 390 391 memcpy(&((*dst)[o_len]), tmp, len); 392 393 o_len += len; 394 src = sep; 395 396 if (sep) 397 sep = strpbrk(src, ARRAY_SEP); 398 } while (src); 399 400 return o_len; 401 } 402 403 static int config_parse_opt_ip6(const char *src, uint8_t **dst, const bool array) 404 { 405 int o_len = 0; 406 char *sep = strpbrk(src, ARRAY_SEP); 407 408 if (sep && !array) 409 return -1; 410 411 do { 412 int len = sizeof(struct in6_addr); 413 414 if (sep) { 415 *sep = 0; 416 sep++; 417 } 418 419 uint8_t *tmp = realloc(*dst, o_len + len); 420 if (!tmp) 421 return -1; 422 *dst = tmp; 423 424 if (inet_pton(AF_INET6, src, &((*dst)[o_len])) < 1) 425 return -1; 426 427 o_len += len; 428 src = sep; 429 430 if (sep) 431 sep = strpbrk(src, ARRAY_SEP); 432 } while (src); 433 434 return o_len; 435 } 436 437 static int config_parse_opt_user_class(const char *src, uint8_t **dst, const bool array) 438 { 439 int o_len = 0; 440 char *sep = strpbrk(src, ARRAY_SEP); 441 442 if (sep && !array) 443 return -1; 444 445 do { 446 if (sep) { 447 *sep = 0; 448 sep++; 449 } 450 uint16_t str_len = strlen(src); 451 452 uint8_t *tmp = realloc(*dst, o_len + str_len + 2); 453 if (!tmp) 454 return -1; 455 *dst = tmp; 456 457 struct user_class { 458 uint16_t len; 459 uint8_t data[]; 460 } *e = (struct user_class *)&((*dst)[o_len]); 461 462 e->len = ntohs(str_len); 463 memcpy(e->data, src, str_len); 464 465 o_len += str_len + 2; 466 src = sep; 467 468 if (sep) 469 sep = strpbrk(src, ARRAY_SEP); 470 } while (src); 471 472 return o_len; 473 } 474 475 static uint8_t *config_state_find_opt(const uint16_t code) 476 { 477 size_t opts_len; 478 uint8_t *odata, *opts = odhcp6c_get_state(STATE_OPTS, &opts_len); 479 uint16_t otype, olen; 480 481 dhcpv6_for_each_option(opts, &opts[opts_len], otype, olen, odata) { 482 if (otype == code) 483 return &odata[-4]; 484 } 485 486 return NULL; 487 } 488 489 int config_add_opt(const uint16_t code, const uint8_t *data, const uint16_t len) 490 { 491 struct { 492 uint16_t code; 493 uint16_t len; 494 } opt_hdr = { htons(code), htons(len) }; 495 496 if (config_state_find_opt(code)) 497 return -1; 498 499 if (odhcp6c_add_state(STATE_OPTS, &opt_hdr, sizeof(opt_hdr)) || 500 odhcp6c_add_state(STATE_OPTS, data, len)) { 501 error("Failed to add option %hu", code); 502 return 1; 503 } 504 505 return 0; 506 } 507 508 int config_parse_opt_data(const char *data, uint8_t **dst, const unsigned int type, 509 const bool array) 510 { 511 int ret = 0; 512 513 switch (type) { 514 case OPT_U8: 515 ret = config_parse_opt_u8(data, dst); 516 break; 517 518 case OPT_STR: 519 ret = config_parse_opt_string(data, dst, array); 520 break; 521 522 case OPT_DNS_STR: 523 ret = config_parse_opt_dns_string(data, dst, array); 524 break; 525 526 case OPT_IP6: 527 ret = config_parse_opt_ip6(data, dst, array); 528 break; 529 530 case OPT_USER_CLASS: 531 ret = config_parse_opt_user_class(data, dst, array); 532 break; 533 534 default: 535 ret = -1; 536 break; 537 } 538 539 return ret; 540 } 541 542 int config_parse_opt(const char *opt) 543 { 544 uint32_t optn; 545 char *data; 546 uint8_t *payload = NULL; 547 int payload_len; 548 unsigned int type = OPT_U8; 549 bool array = false; 550 struct odhcp6c_opt *dopt = NULL; 551 int ret = -1; 552 553 data = strpbrk(opt, ":"); 554 if (!data) 555 return -1; 556 557 *data = '\0'; 558 data++; 559 560 if (strlen(opt) == 0 || strlen(data) == 0) 561 return -1; 562 563 dopt = odhcp6c_find_opt_by_name(opt); 564 if (!dopt) { 565 char *e; 566 optn = strtoul(opt, &e, 0); 567 if (*e || e == opt || optn > USHRT_MAX) 568 return -1; 569 570 dopt = odhcp6c_find_opt(optn); 571 } else { 572 optn = dopt->code; 573 } 574 575 /* Check if the type for the content is well-known */ 576 if (dopt) { 577 /* Refuse internal options */ 578 if (dopt->flags & OPT_INTERNAL) 579 return -1; 580 581 type = dopt->flags & OPT_MASK_SIZE; 582 array = ((dopt->flags & OPT_ARRAY) == OPT_ARRAY) ? true : false; 583 } else if (data[0] == '"' || data[0] == '\'') { 584 char *end = strrchr(data + 1, data[0]); 585 586 if (end && (end == (data + strlen(data) - 1))) { 587 /* Raw option is specified as a string */ 588 type = OPT_STR; 589 data++; 590 *end = '\0'; 591 } 592 } 593 594 payload_len = config_parse_opt_data(data, &payload, type, array); 595 if (payload_len > 0) 596 ret = config_add_opt(optn, payload, payload_len); 597 598 free(payload); 599 600 return ret; 601 } 602 603 void config_apply_dhcp_rtx(struct dhcpv6_retx* dhcpv6_retx) 604 { 605 dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_delay = config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].delay_max; 606 dhcpv6_retx[DHCPV6_MSG_SOLICIT].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].timeout_init; 607 dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_SOLICIT].timeout_max; 608 dhcpv6_retx[DHCPV6_MSG_REQUEST].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].timeout_init; 609 dhcpv6_retx[DHCPV6_MSG_REQUEST].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].timeout_max; 610 dhcpv6_retx[DHCPV6_MSG_REQUEST].max_rc = config_dhcp.message_rtx[CONFIG_DHCP_REQUEST].rc_max; 611 dhcpv6_retx[DHCPV6_MSG_RENEW].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_RENEW].timeout_init; 612 dhcpv6_retx[DHCPV6_MSG_RENEW].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_RENEW].timeout_max; 613 dhcpv6_retx[DHCPV6_MSG_REBIND].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REBIND].timeout_init; 614 dhcpv6_retx[DHCPV6_MSG_REBIND].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_REBIND].timeout_max; 615 dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_delay = config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].delay_max; 616 dhcpv6_retx[DHCPV6_MSG_INFO_REQ].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].timeout_init; 617 dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_timeo = config_dhcp.message_rtx[CONFIG_DHCP_INFO_REQ].timeout_max; 618 dhcpv6_retx[DHCPV6_MSG_RELEASE].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_RELEASE].timeout_init; 619 dhcpv6_retx[DHCPV6_MSG_RELEASE].max_rc = config_dhcp.message_rtx[CONFIG_DHCP_RELEASE].rc_max; 620 dhcpv6_retx[DHCPV6_MSG_DECLINE].init_timeo = config_dhcp.message_rtx[CONFIG_DHCP_DECLINE].timeout_init; 621 dhcpv6_retx[DHCPV6_MSG_DECLINE].max_rc = config_dhcp.message_rtx[CONFIG_DHCP_DECLINE].rc_max; 622 } 623
This page was automatically generated by LXR 0.3.1. • OpenWrt