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