From: Nicolas BESNARD Date: Fri, 6 Dec 2024 10:26:07 +0000 (+0000) Subject: reconfigure: move all configuration functions to a new file X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=1048fc4fb622baa3dfabf42d2a3248d229271c00;p=project%2Fodhcp6c.git reconfigure: move all configuration functions to a new file Tidy up. Signed-off-by: Nicolas BESNARD Signed-off-by: Paul Donald Link: https://github.com/openwrt/odhcp6c/pull/106 Signed-off-by: Álvaro Fernández Rojas --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c3d1be..fc60a84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,9 @@ cmake_policy(SET CMP0015 NEW) project(odhcp6c LANGUAGES C) add_executable(${PROJECT_NAME}) target_sources(${PROJECT_NAME} PRIVATE - src/odhcp6c.c + src/config.c src/dhcpv6.c + src/odhcp6c.c src/ra.c src/script.c ) diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..1b18730 --- /dev/null +++ b/src/config.c @@ -0,0 +1,483 @@ +/** + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + * + * SPDX-FileCopyrightText: Copyright (c) 2024 SoftAtHome + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * Subject to the terms and conditions of this license, each + * copyright holder and contributor hereby grants to those receiving + * rights under this license a perpetual, worldwide, non-exclusive, + * no-charge, royalty-free, irrevocable (except for failure to + * satisfy the conditions of this license) patent license to make, + * have made, use, offer to sell, sell, import, and otherwise + * transfer this software, where such license applies only to those + * patent claims, already acquired or hereafter acquired, licensable + * by such copyright holder or contributor that are necessarily + * infringed by: + * + * (a) their Contribution(s) (the licensed copyrights of copyright + * holders and non-copyrightable additions of contributors, in + * source or binary form) alone; or + * + * (b) combination of their Contribution(s) with the work of + * authorship to which such Contribution(s) was added by such + * copyright holder or contributor, if, at the time the Contribution + * is added, such addition causes such combination to be necessarily + * infringed. The patent license shall not apply to any other + * combinations which include the Contribution. + * + * Except as expressly stated above, no rights or licenses from any + * copyright holder or contributor is granted under this license, + * whether expressly, by implication, estoppel or otherwise. + * + * DISCLAIMER + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "odhcp6c.h" + +#define ARRAY_SEP " ,\t" + +static struct config_dhcp config_dhcp; + +struct config_dhcp *config_dhcp_get(void) { + return &config_dhcp; +} + +void config_dhcp_reset(void) { + config_dhcp.release = true; + config_dhcp.dscp = 0; + config_dhcp.sol_timeout = DHCPV6_SOL_MAX_RT; + config_dhcp.sk_prio = 0; + config_dhcp.stateful_only_mode = false; + config_dhcp.ia_na_mode = IA_MODE_TRY; + config_dhcp.ia_pd_mode = IA_MODE_NONE; + config_dhcp.client_options = DHCPV6_CLIENT_FQDN | DHCPV6_ACCEPT_RECONFIGURE; + config_dhcp.allow_slaac_only = -1; + config_dhcp.oro_user_cnt = 0; +} + +void config_set_release(bool enable) { + config_dhcp.release = enable; +} + +bool config_set_dscp(unsigned int value) { + if (value > 63) + return false; + config_dhcp.dscp = value; + return true; +} + +bool config_set_solicit_timeout(unsigned int timeout) { + if (timeout > INT32_MAX) + return false; + + config_dhcp.sol_timeout = timeout; + return true; +} + +bool config_set_sk_priority(unsigned int priority) { + if (priority > 6) + return false; + + config_dhcp.sk_prio = priority; + return true; +} + +void config_set_client_options(enum dhcpv6_config option, bool enable) { + if (enable) { + config_dhcp.client_options |= option; + } else { + config_dhcp.client_options &= ~option; + } +} + +bool config_set_request_addresses(char* mode) { + if (!strcmp(mode, "force")) { + config_dhcp.ia_na_mode = IA_MODE_FORCE; + config_dhcp.allow_slaac_only = -1; + } else if (!strcmp(mode, "none")) { + config_dhcp.ia_na_mode = IA_MODE_NONE; + } else if (!strcmp(mode, "try")) { + config_dhcp.ia_na_mode = IA_MODE_TRY; + } else { + return false; + } + + return true; +} + +bool config_set_request_prefix(unsigned int length, unsigned int id) { + struct odhcp6c_request_prefix prefix = {0}; + + odhcp6c_clear_state(STATE_IA_PD_INIT); + + if (config_dhcp.ia_pd_mode != IA_MODE_FORCE) + config_dhcp.ia_pd_mode = length > 128 ? IA_MODE_NONE : IA_MODE_TRY; + + if (length <= 128) { + if (config_dhcp.allow_slaac_only >= 0 && config_dhcp.allow_slaac_only < 10) + config_dhcp.allow_slaac_only = 10; + + prefix.length = length; + prefix.iaid = htonl(id); + + if (odhcp6c_add_state(STATE_IA_PD_INIT, &prefix, sizeof(prefix))) { + syslog(LOG_ERR, "Failed to set request IPv6-Prefix"); + return false; + } + } + + return true; +} + +void config_set_force_prefix(bool enable) { + if (enable) { + config_dhcp.allow_slaac_only = -1; + config_dhcp.ia_pd_mode = IA_MODE_FORCE; + } else { + config_dhcp.ia_pd_mode = IA_MODE_NONE; + } +} + +void config_set_stateful_only(bool enable) { + config_dhcp.stateful_only_mode = enable; +} + +void config_set_allow_slaac_only(int value) { + config_dhcp.allow_slaac_only = value; +} + +void config_clear_requested_options(void) { + config_dhcp.oro_user_cnt = 0; +} + +bool config_add_requested_options(unsigned int option) { + if (option > UINT16_MAX) + return false; + + option = htons(option); + if (odhcp6c_insert_state(STATE_ORO, 0, &option, 2)) { + return false; + } + config_dhcp.oro_user_cnt++; + return true; +} + +void config_clear_send_options(void) { + odhcp6c_clear_state(STATE_OPTS); +} + +bool config_add_send_options(char* option) { + return (config_parse_opt(option) == 0); +} + +static int config_parse_opt_u8(const char *src, uint8_t **dst) +{ + int len = strlen(src); + + *dst = realloc(*dst, len/2); + if (!*dst) + return -1; + + return script_unhexlify(*dst, len, src); +} + +static int config_parse_opt_string(const char *src, uint8_t **dst, const bool array) +{ + int o_len = 0; + char *sep = strpbrk(src, ARRAY_SEP); + + if (sep && !array) + return -1; + + do { + if (sep) { + *sep = 0; + sep++; + } + + int len = strlen(src); + + *dst = realloc(*dst, o_len + len); + if (!*dst) + return -1; + + memcpy(&((*dst)[o_len]), src, len); + + o_len += len; + src = sep; + + if (sep) + sep = strpbrk(src, ARRAY_SEP); + } while (src); + + return o_len; +} + +static int config_parse_opt_dns_string(const char *src, uint8_t **dst, const bool array) +{ + int o_len = 0; + char *sep = strpbrk(src, ARRAY_SEP); + + if (sep && !array) + return -1; + + do { + uint8_t tmp[256]; + + if (sep) { + *sep = 0; + sep++; + } + + int len = dn_comp(src, tmp, sizeof(tmp), NULL, NULL); + if (len < 0) + return -1; + + *dst = realloc(*dst, o_len + len); + if (!*dst) + return -1; + + memcpy(&((*dst)[o_len]), tmp, len); + + o_len += len; + src = sep; + + if (sep) + sep = strpbrk(src, ARRAY_SEP); + } while (src); + + return o_len; +} + +static int config_parse_opt_ip6(const char *src, uint8_t **dst, const bool array) +{ + int o_len = 0; + char *sep = strpbrk(src, ARRAY_SEP); + + if (sep && !array) + return -1; + + do { + int len = sizeof(struct in6_addr); + + if (sep) { + *sep = 0; + sep++; + } + + *dst = realloc(*dst, o_len + len); + if (!*dst) + return -1; + + if (inet_pton(AF_INET6, src, &((*dst)[o_len])) < 1) + return -1; + + o_len += len; + src = sep; + + if (sep) + sep = strpbrk(src, ARRAY_SEP); + } while (src); + + return o_len; +} + +static int config_parse_opt_user_class(const char *src, uint8_t **dst, const bool array) +{ + int o_len = 0; + char *sep = strpbrk(src, ARRAY_SEP); + + if (sep && !array) + return -1; + + do { + if (sep) { + *sep = 0; + sep++; + } + uint16_t str_len = strlen(src); + + *dst = realloc(*dst, o_len + str_len + 2); + if (!*dst) + return -1; + + struct user_class { + uint16_t len; + uint8_t data[]; + } *e = (struct user_class *)&((*dst)[o_len]); + + e->len = ntohs(str_len); + memcpy(e->data, src, str_len); + + o_len += str_len + 2; + src = sep; + + if (sep) + sep = strpbrk(src, ARRAY_SEP); + } while (src); + + return o_len; +} + +static uint8_t *config_state_find_opt(const uint16_t code) +{ + size_t opts_len; + uint8_t *odata, *opts = odhcp6c_get_state(STATE_OPTS, &opts_len); + uint16_t otype, olen; + + dhcpv6_for_each_option(opts, &opts[opts_len], otype, olen, odata) { + if (otype == code) + return &odata[-4]; + } + + return NULL; +} + +int config_add_opt(const uint16_t code, const uint8_t *data, const uint16_t len) +{ + struct { + uint16_t code; + uint16_t len; + } opt_hdr = { htons(code), htons(len) }; + + if (config_state_find_opt(code)) + return -1; + + if (odhcp6c_add_state(STATE_OPTS, &opt_hdr, sizeof(opt_hdr)) || + odhcp6c_add_state(STATE_OPTS, data, len)) { + syslog(LOG_ERR, "Failed to add option %hu", code); + return 1; + } + + return 0; +} + +int config_parse_opt_data(const char *data, uint8_t **dst, const unsigned int type, + const bool array) +{ + int ret = 0; + + switch (type) { + case OPT_U8: + ret = config_parse_opt_u8(data, dst); + break; + + case OPT_STR: + ret = config_parse_opt_string(data, dst, array); + break; + + case OPT_DNS_STR: + ret = config_parse_opt_dns_string(data, dst, array); + break; + + case OPT_IP6: + ret = config_parse_opt_ip6(data, dst, array); + break; + + case OPT_USER_CLASS: + ret = config_parse_opt_user_class(data, dst, array); + break; + + default: + ret = -1; + break; + } + + return ret; +} + +int config_parse_opt(const char *opt) +{ + uint32_t optn; + char *data; + uint8_t *payload = NULL; + int payload_len; + unsigned int type = OPT_U8; + bool array = false; + struct odhcp6c_opt *dopt = NULL; + int ret = -1; + + data = strpbrk(opt, ":"); + if (!data) + return -1; + + *data = '\0'; + data++; + + if (strlen(opt) == 0 || strlen(data) == 0) + return -1; + + dopt = odhcp6c_find_opt_by_name(opt); + if (!dopt) { + char *e; + optn = strtoul(opt, &e, 0); + if (*e || e == opt || optn > USHRT_MAX) + return -1; + + dopt = odhcp6c_find_opt(optn); + } else { + optn = dopt->code; + } + + /* Check if the type for the content is well-known */ + if (dopt) { + /* Refuse internal options */ + if (dopt->flags & OPT_INTERNAL) + return -1; + + type = dopt->flags & OPT_MASK_SIZE; + array = ((dopt->flags & OPT_ARRAY) == OPT_ARRAY) ? true : false; + } else if (data[0] == '"' || data[0] == '\'') { + char *end = strrchr(data + 1, data[0]); + + if (end && (end == (data + strlen(data) - 1))) { + /* Raw option is specified as a string */ + type = OPT_STR; + data++; + *end = '\0'; + } + } + + payload_len = config_parse_opt_data(data, &payload, type, array); + if (payload_len > 0) + ret = config_add_opt(optn, payload, payload_len); + + free(payload); + + return ret; +} diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..a664e45 --- /dev/null +++ b/src/config.h @@ -0,0 +1,102 @@ +/* + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + * + * SPDX-FileCopyrightText: Copyright (c) 2024 SoftAtHome + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * Subject to the terms and conditions of this license, each + * copyright holder and contributor hereby grants to those receiving + * rights under this license a perpetual, worldwide, non-exclusive, + * no-charge, royalty-free, irrevocable (except for failure to + * satisfy the conditions of this license) patent license to make, + * have made, use, offer to sell, sell, import, and otherwise + * transfer this software, where such license applies only to those + * patent claims, already acquired or hereafter acquired, licensable + * by such copyright holder or contributor that are necessarily + * infringed by: + * + * (a) their Contribution(s) (the licensed copyrights of copyright + * holders and non-copyrightable additions of contributors, in + * source or binary form) alone; or + * + * (b) combination of their Contribution(s) with the work of + * authorship to which such Contribution(s) was added by such + * copyright holder or contributor, if, at the time the Contribution + * is added, such addition causes such combination to be necessarily + * infringed. The patent license shall not apply to any other + * combinations which include the Contribution. + * + * Except as expressly stated above, no rights or licenses from any + * copyright holder or contributor is granted under this license, + * whether expressly, by implication, estoppel or otherwise. + * + * DISCLAIMER + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#include "odhcp6c.h" + +struct config_dhcp { + bool release; + int dscp; + int sol_timeout; + int sk_prio; + bool stateful_only_mode; + enum odhcp6c_ia_mode ia_na_mode; + enum odhcp6c_ia_mode ia_pd_mode; + unsigned int client_options; + int allow_slaac_only; + unsigned int oro_user_cnt; +}; + +struct config_dhcp *config_dhcp_get(void); +void config_dhcp_reset(void); +void config_set_release(bool enable); +bool config_set_dscp(unsigned int value) ; +bool config_set_solicit_timeout(unsigned int timeout); +bool config_set_sk_priority(unsigned int priority); +void config_set_client_options(enum dhcpv6_config option, bool enable); +bool config_set_request_addresses(char *mode); +bool config_set_request_prefix(unsigned int length, unsigned int id); +void config_set_force_prefix(bool enable); +void config_set_stateful_only(bool enable); +void config_set_allow_slaac_only(int value); +void config_clear_requested_options(void) ; +bool config_add_requested_options(unsigned int option); +void config_clear_send_options(void); +bool config_add_send_options(char *option); + +int config_add_opt(const uint16_t code, const uint8_t *data, const uint16_t len); +int config_parse_opt_data(const char *data, uint8_t **dst, const unsigned int type, const bool array); +int config_parse_opt(const char *opt); + +#endif /* _CONFIG_H_ */ diff --git a/src/odhcp6c.c b/src/odhcp6c.c index c5b8cb4..361de81 100644 --- a/src/odhcp6c.c +++ b/src/odhcp6c.c @@ -35,6 +35,7 @@ #include #include +#include "config.h" #include "odhcp6c.h" #include "ra.h" #include "ubus.h" @@ -47,15 +48,9 @@ ((((__const uint32_t *) (a))[0] & htonl (0xfe000000)) \ == htonl (0xfc000000)) #endif -#define ARRAY_SEP " ,\t" static void sighandler(int signal); static int usage(void); -static int add_opt(const uint16_t code, const uint8_t *data, - const uint16_t len); -static int parse_opt_data(const char *data, uint8_t **dst, - const unsigned int type, const bool array); -static int parse_opt(const char *opt); static uint8_t *state_data[_STATE_MAX] = {NULL}; static size_t state_len[_STATE_MAX] = {0}; @@ -65,18 +60,11 @@ static volatile bool signal_usr1 = false; static volatile bool signal_usr2 = false; static volatile bool signal_term = false; -static int urandom_fd = -1, allow_slaac_only = 0; -static bool bound = false, release = true, ra = false; +static int urandom_fd = -1; +static bool bound = false, ra = false; static time_t last_update = 0; static char *ifname = NULL; -static unsigned int dscp = 0; -static int sol_timeout = DHCPV6_SOL_MAX_RT; -static int sk_prio = 0; -bool stateful_only_mode = 0; -static enum odhcp6c_ia_mode ia_na_mode = IA_MODE_TRY; -static enum odhcp6c_ia_mode ia_pd_mode = IA_MODE_NONE; -static unsigned int client_options = DHCPV6_CLIENT_FQDN | DHCPV6_ACCEPT_RECONFIGURE; -static unsigned int oro_user_cnt = 0; +struct config_dhcp *config_dhcp = NULL; static unsigned int script_sync_delay = 10; static unsigned int script_accu_delay = 1; @@ -196,26 +184,21 @@ int main(_unused int argc, char* const argv[]) unsigned int ra_options = RA_RDNSS_DEFAULT_LIFETIME; unsigned int ra_holdoff_interval = RA_MIN_ADV_INTERVAL; bool terminate = false; + config_dhcp = config_dhcp_get(); + config_dhcp_reset(); while ((c = getopt(argc, argv, "S::DN:V:P:FB:c:i:r:Ru:Ux:s:kK:t:C:m:Lhedp:fav")) != -1) { switch (c) { case 'S': - allow_slaac_only = (optarg) ? atoi(optarg) : -1; + config_set_allow_slaac_only((optarg) ? atoi(optarg) : -1); break; case 'D': - stateful_only_mode = 1; + config_set_stateful_only(true); break; case 'N': - if (!strcmp(optarg, "force")) { - ia_na_mode = IA_MODE_FORCE; - allow_slaac_only = -1; - } else if (!strcmp(optarg, "none")) - ia_na_mode = IA_MODE_NONE; - else if (!strcmp(optarg, "try")) - ia_na_mode = IA_MODE_TRY; - else + if (!config_set_request_addresses(optarg)) help = true; break; @@ -227,10 +210,10 @@ int main(_unused int argc, char* const argv[]) } o_data = NULL; - res = parse_opt_data(optarg, &o_data, opt->flags & OPT_MASK_SIZE, + res = config_parse_opt_data(optarg, &o_data, opt->flags & OPT_MASK_SIZE, (opt->flags & OPT_ARRAY) == OPT_ARRAY); if (res > 0) { - res = add_opt(opt->code, o_data, res); + res = config_add_opt(opt->code, o_data, res); if (res) { if (res > 0) return 1; @@ -244,11 +227,11 @@ int main(_unused int argc, char* const argv[]) break; case 'P': - if (ia_pd_mode == IA_MODE_NONE) - ia_pd_mode = IA_MODE_TRY; + if (config_dhcp->ia_pd_mode == IA_MODE_NONE) + config_dhcp->ia_pd_mode = IA_MODE_TRY; - if (allow_slaac_only >= 0 && allow_slaac_only < 10) - allow_slaac_only = 10; + if (config_dhcp->allow_slaac_only >= 0 && config_dhcp->allow_slaac_only < 10) + config_dhcp->allow_slaac_only = 10; struct odhcp6c_request_prefix prefix = { 0 }; @@ -287,8 +270,7 @@ int main(_unused int argc, char* const argv[]) break; case 'F': - allow_slaac_only = -1; - ia_pd_mode = IA_MODE_FORCE; + config_set_force_prefix(true); break; case 'c': @@ -328,7 +310,7 @@ int main(_unused int argc, char* const argv[]) break; case 'R': - client_options |= DHCPV6_STRICT_OPTIONS; + config_set_client_options(DHCPV6_STRICT_OPTIONS, true); break; case 'u': @@ -339,10 +321,10 @@ int main(_unused int argc, char* const argv[]) } o_data = NULL; - res = parse_opt_data(optarg, &o_data, opt->flags & OPT_MASK_SIZE, + res = config_parse_opt_data(optarg, &o_data, opt->flags & OPT_MASK_SIZE, (opt->flags & OPT_ARRAY) == OPT_ARRAY); if (res > 0) { - res = add_opt(opt->code, o_data, res); + res = config_add_opt(opt->code, o_data, res); if (res) { if (res > 0) return 1; @@ -356,7 +338,7 @@ int main(_unused int argc, char* const argv[]) break; case 'U': - client_options |= DHCPV6_IGNORE_OPT_UNICAST; + config_set_client_options(DHCPV6_IGNORE_OPT_UNICAST, true); break; case 's': @@ -364,21 +346,19 @@ int main(_unused int argc, char* const argv[]) break; case 'k': - release = false; + config_set_release(false); break; case 'K': - sk_prio = atoi(optarg); + config_set_sk_priority(atoi(optarg)); break; case 't': - sol_timeout = atoi(optarg); + config_set_solicit_timeout(atoi(optarg)); break; case 'C': - dscp = atoi(optarg); - if (dscp > 63) { - dscp = 0; + if (!config_set_dscp(atoi(optarg))) { syslog(LOG_ERR, "Invalid DSCP value, using default (0)"); } break; @@ -404,11 +384,11 @@ int main(_unused int argc, char* const argv[]) break; case 'f': - client_options &= ~DHCPV6_CLIENT_FQDN; + config_set_client_options(DHCPV6_CLIENT_FQDN, false); break; case 'a': - client_options &= ~DHCPV6_ACCEPT_RECONFIGURE; + config_set_client_options(DHCPV6_ACCEPT_RECONFIGURE, false); break; case 'v': @@ -416,7 +396,7 @@ int main(_unused int argc, char* const argv[]) break; case 'x': - res = parse_opt(optarg); + res = config_parse_opt(optarg); if (res) { if (res > 0) return res; @@ -431,8 +411,8 @@ int main(_unused int argc, char* const argv[]) } } - if (allow_slaac_only > 0) - script_sync_delay = allow_slaac_only; + if (config_dhcp->allow_slaac_only > 0) + script_sync_delay = config_dhcp->allow_slaac_only; openlog("odhcp6c", logopt, LOG_DAEMON); if (!verbosity) @@ -526,11 +506,9 @@ int main(_unused int argc, char* const argv[]) size_t oro_len = 0; odhcp6c_get_state(STATE_ORO, &oro_len); - oro_user_cnt = oro_len / sizeof(uint16_t); - - syslog(LOG_NOTICE, "number of user requested options %u", oro_user_cnt); + config_dhcp->oro_user_cnt = oro_len / sizeof(uint16_t); - if (init_dhcpv6(ifname, client_options, sk_prio, sol_timeout, dscp)) { + if (init_dhcpv6(ifname, config_dhcp->client_options, config_dhcp->sk_prio, config_dhcp->sol_timeout, config_dhcp->dscp)) { syslog(LOG_ERR, "failed to initialize: %s", strerror(errno)); return 1; } @@ -544,7 +522,7 @@ int main(_unused int argc, char* const argv[]) break; case DHCPV6_SOLICIT: - mode = dhcpv6_set_ia_mode(ia_na_mode, ia_pd_mode, stateful_only_mode); + mode = dhcpv6_set_ia_mode(config_dhcp->ia_na_mode, config_dhcp->ia_pd_mode, config_dhcp->stateful_only_mode); if (mode == DHCPV6_STATELESS) { dhcpv6_set_state(DHCPV6_REQUEST); break; @@ -560,7 +538,7 @@ int main(_unused int argc, char* const argv[]) dhcpv6_set_state(DHCPV6_REQUEST); } else { mode = DHCPV6_UNKNOWN; - dhcpv6_set_state(DHCPV6_INIT); + dhcpv6_set_state(DHCPV6_RESET); } break; @@ -577,12 +555,12 @@ int main(_unused int argc, char* const argv[]) if ((res < 0) && signalled) { mode = DHCPV6_UNKNOWN; - dhcpv6_set_state(DHCPV6_INIT); + dhcpv6_set_state(DHCPV6_RESET); break; } mode = dhcpv6_promote_server_cand(); - dhcpv6_set_state(mode > DHCPV6_UNKNOWN ? DHCPV6_REQUEST : DHCPV6_INIT); + dhcpv6_set_state(mode > DHCPV6_UNKNOWN ? DHCPV6_REQUEST : DHCPV6_RESET); break; case DHCPV6_BOUND: @@ -717,7 +695,7 @@ int main(_unused int argc, char* const argv[]) bound = false; notify_state_change("unbound", 0, true); - if (server_id_len > 0 && (ia_pd_len > 0 || ia_na_len > 0) && release) + if (server_id_len > 0 && (ia_pd_len > 0 || ia_na_len > 0) && config_dhcp->release) dhcpv6_send_request(DHCPV6_MSG_RELEASE); odhcp6c_clear_state(STATE_IA_NA); @@ -736,7 +714,7 @@ int main(_unused int argc, char* const argv[]) size_t oro_user_len, oro_total_len; odhcp6c_get_state(STATE_ORO, &oro_total_len); - oro_user_len = oro_user_cnt * sizeof(uint16_t); + oro_user_len = config_dhcp->oro_user_cnt * sizeof(uint16_t); odhcp6c_remove_state(STATE_ORO, oro_user_len, oro_total_len - oro_user_len); close(dhcpv6_get_socket()); @@ -856,7 +834,7 @@ bool odhcp6c_signal_process(void) ra = false; } - if (ra_updated && (bound || allow_slaac_only >= 0)) { + if (ra_updated && (bound || config_dhcp->allow_slaac_only >= 0)) { notify_state_change("ra-updated", (!ra && !bound) ? script_sync_delay : script_accu_delay, false); ra = true; @@ -1013,20 +991,6 @@ static void odhcp6c_expire_list(enum odhcp6c_state state, uint32_t elapsed, bool } } -static uint8_t *odhcp6c_state_find_opt(const uint16_t code) -{ - size_t opts_len; - uint8_t *odata, *opts = odhcp6c_get_state(STATE_OPTS, &opts_len); - uint16_t otype, olen; - - dhcpv6_for_each_option(opts, &opts[opts_len], otype, olen, odata) { - if (otype == code) - return &odata[-4]; - } - - return NULL; -} - void odhcp6c_expire(bool expire_ia_pd) { time_t now = odhcp6c_get_milli_time() / 1000; @@ -1125,23 +1089,13 @@ static void sighandler(int signal) signal_term = true; } -static int add_opt(const uint16_t code, const uint8_t *data, const uint16_t len) +void notify_state_change(const char *status, int delay, bool resume) { - struct { - uint16_t code; - uint16_t len; - } opt_hdr = { htons(code), htons(len) }; - - if (odhcp6c_state_find_opt(code)) - return -1; - - if (odhcp6c_add_state(STATE_OPTS, &opt_hdr, sizeof(opt_hdr)) || - odhcp6c_add_state(STATE_OPTS, data, len)) { - syslog(LOG_ERR, "Failed to add option %hu", code); - return 1; - } + script_call(status, delay, resume); - return 0; +#ifdef WITH_UBUS + ubus_dhcp_event(status); +#endif /* WITH_UBUS */ } struct odhcp6c_opt *odhcp6c_find_opt(const uint16_t code) @@ -1158,7 +1112,7 @@ struct odhcp6c_opt *odhcp6c_find_opt(const uint16_t code) return NULL; } -static struct odhcp6c_opt *odhcp6c_find_opt_by_name(const char *name) +struct odhcp6c_opt *odhcp6c_find_opt_by_name(const char *name) { struct odhcp6c_opt *opt = opts; @@ -1170,359 +1124,3 @@ static struct odhcp6c_opt *odhcp6c_find_opt_by_name(const char *name) return (opt->code > 0 ? opt : NULL); } - -static int parse_opt_u8(const char *src, uint8_t **dst) -{ - int len = strlen(src); - - *dst = realloc(*dst, len/2); - if (!*dst) - return -1; - - return script_unhexlify(*dst, len, src); -} - -static int parse_opt_string(const char *src, uint8_t **dst, const bool array) -{ - int o_len = 0; - char *sep = strpbrk(src, ARRAY_SEP); - - if (sep && !array) - return -1; - - do { - if (sep) { - *sep = 0; - sep++; - } - - int len = strlen(src); - - *dst = realloc(*dst, o_len + len); - if (!*dst) - return -1; - - memcpy(&((*dst)[o_len]), src, len); - - o_len += len; - src = sep; - - if (sep) - sep = strpbrk(src, ARRAY_SEP); - } while (src); - - return o_len; -} - -static int parse_opt_dns_string(const char *src, uint8_t **dst, const bool array) -{ - int o_len = 0; - char *sep = strpbrk(src, ARRAY_SEP); - - if (sep && !array) - return -1; - - do { - uint8_t tmp[256]; - - if (sep) { - *sep = 0; - sep++; - } - - int len = dn_comp(src, tmp, sizeof(tmp), NULL, NULL); - if (len < 0) - return -1; - - *dst = realloc(*dst, o_len + len); - if (!*dst) - return -1; - - memcpy(&((*dst)[o_len]), tmp, len); - - o_len += len; - src = sep; - - if (sep) - sep = strpbrk(src, ARRAY_SEP); - } while (src); - - return o_len; -} - -static int parse_opt_ip6(const char *src, uint8_t **dst, const bool array) -{ - int o_len = 0; - char *sep = strpbrk(src, ARRAY_SEP); - - if (sep && !array) - return -1; - - do { - int len = sizeof(struct in6_addr); - - if (sep) { - *sep = 0; - sep++; - } - - *dst = realloc(*dst, o_len + len); - if (!*dst) - return -1; - - if (inet_pton(AF_INET6, src, &((*dst)[o_len])) < 1) - return -1; - - o_len += len; - src = sep; - - if (sep) - sep = strpbrk(src, ARRAY_SEP); - } while (src); - - return o_len; -} - -static int parse_opt_user_class(const char *src, uint8_t **dst, const bool array) -{ - int o_len = 0; - char *sep = strpbrk(src, ARRAY_SEP); - - if (sep && !array) - return -1; - - do { - if (sep) { - *sep = 0; - sep++; - } - uint16_t str_len = strlen(src); - - *dst = realloc(*dst, o_len + str_len + 2); - if (!*dst) - return -1; - - struct user_class { - uint16_t len; - uint8_t data[]; - } *e = (struct user_class *)&((*dst)[o_len]); - - e->len = ntohs(str_len); - memcpy(e->data, src, str_len); - - o_len += str_len + 2; - src = sep; - - if (sep) - sep = strpbrk(src, ARRAY_SEP); - } while (src); - - return o_len; -} - -static int parse_opt_data(const char *data, uint8_t **dst, const unsigned int type, - const bool array) -{ - int ret = 0; - - switch (type) { - case OPT_U8: - ret = parse_opt_u8(data, dst); - break; - - case OPT_STR: - ret = parse_opt_string(data, dst, array); - break; - - case OPT_DNS_STR: - ret = parse_opt_dns_string(data, dst, array); - break; - - case OPT_IP6: - ret = parse_opt_ip6(data, dst, array); - break; - - case OPT_USER_CLASS: - ret = parse_opt_user_class(data, dst, array); - break; - - default: - ret = -1; - break; - } - - return ret; -} - -static int parse_opt(const char *opt) -{ - uint32_t optn; - char *data; - uint8_t *payload = NULL; - int payload_len; - unsigned int type = OPT_U8; - bool array = false; - struct odhcp6c_opt *dopt = NULL; - int ret = -1; - - data = strpbrk(opt, ":"); - if (!data) - return -1; - - *data = '\0'; - data++; - - if (strlen(opt) == 0 || strlen(data) == 0) - return -1; - - dopt = odhcp6c_find_opt_by_name(opt); - if (!dopt) { - char *e; - optn = strtoul(opt, &e, 0); - if (*e || e == opt || optn > USHRT_MAX) - return -1; - - dopt = odhcp6c_find_opt(optn); - } else - optn = dopt->code; - - /* Check if the type for the content is well-known */ - if (dopt) { - /* Refuse internal options */ - if (dopt->flags & OPT_INTERNAL) - return -1; - - type = dopt->flags & OPT_MASK_SIZE; - array = ((dopt->flags & OPT_ARRAY) == OPT_ARRAY) ? true : false; - } else if (data[0] == '"' || data[0] == '\'') { - char *end = strrchr(data + 1, data[0]); - - if (end && (end == (data + strlen(data) - 1))) { - /* Raw option is specified as a string */ - type = OPT_STR; - data++; - *end = '\0'; - } - - } - - payload_len = parse_opt_data(data, &payload, type, array); - if (payload_len > 0) - ret = add_opt(optn, payload, payload_len); - - free(payload); - - return ret; -} - -void notify_state_change(const char *status, int delay, bool resume) -{ - script_call(status, delay, resume); - -#ifdef WITH_UBUS - ubus_dhcp_event(status); -#endif /* WITH_UBUS */ -} - -void config_set_release(bool enable) { - release = enable; -} - -bool config_set_dscp(unsigned int value) { - if (value > 63) - return false; - dscp = value; - return true; -} - -bool config_set_solicit_timeout(unsigned int timeout) { - if (timeout > INT32_MAX) - return false; - - sol_timeout = timeout; - return true; -} - -bool config_set_sk_priority(unsigned int priority) { - if (priority > 6) - return false; - - sk_prio = priority; - return true; -} - -void config_set_client_options(enum dhcpv6_config option, bool enable) { - if (enable) { - client_options |= option; - } else { - client_options &= ~option; - } -} - -bool config_set_request_addresses(char* mode) { - if (!strcmp(mode, "force")) { - ia_na_mode = IA_MODE_FORCE; - allow_slaac_only = -1; - } else if (!strcmp(mode, "none")) { - ia_na_mode = IA_MODE_NONE; - } else if (!strcmp(mode, "try")) { - ia_na_mode = IA_MODE_TRY; - } else { - return false; - } - - return true; -} - -bool config_set_request_prefix(unsigned int length, unsigned int id) { - struct odhcp6c_request_prefix prefix = {0}; - - odhcp6c_clear_state(STATE_IA_PD_INIT); - - if (ia_pd_mode != IA_MODE_FORCE) - ia_pd_mode = length > 128 ? IA_MODE_NONE : IA_MODE_TRY; - - if (length <= 128) { - if (allow_slaac_only >= 0 && allow_slaac_only < 10) - allow_slaac_only = 10; - - prefix.length = length; - prefix.iaid = htonl(id); - - if (odhcp6c_add_state(STATE_IA_PD_INIT, &prefix, sizeof(prefix))) { - syslog(LOG_ERR, "Failed to set request IPv6-Prefix"); - return false; - } - } - - return true; -} - -void config_set_stateful_only(bool enable) { - stateful_only_mode = enable; -} - -void config_clear_requested_options(void) { - oro_user_cnt = 0; -} - -bool config_add_requested_options(unsigned int option) { - if (option > UINT16_MAX) - return false; - - option = htons(option); - if (odhcp6c_insert_state(STATE_ORO, 0, &option, 2)) { - syslog(LOG_ERR, "Failed to add requested option"); - return false; - } - oro_user_cnt++; - return true; -} - -void config_clear_send_options(void) { - odhcp6c_clear_state(STATE_OPTS); -} - -bool config_add_send_options(char* option) { - return (parse_opt(option) == 0); -} diff --git a/src/odhcp6c.h b/src/odhcp6c.h index fc751c6..c398269 100644 --- a/src/odhcp6c.h +++ b/src/odhcp6c.h @@ -477,13 +477,13 @@ bool config_set_dscp(unsigned int value); bool config_set_solicit_timeout(unsigned int timeout); bool config_set_sk_priority(unsigned int value); void config_set_client_options(enum dhcpv6_config option, bool enable); -bool config_set_request_addresses(char* mode); +bool config_set_request_addresses(char *mode); bool config_set_request_prefix(unsigned int length, unsigned int id); void config_set_stateful_only(bool enable); void config_clear_requested_options(void); bool config_add_requested_options(unsigned int option); void config_clear_send_options(void); -bool config_add_send_options(char* option); +bool config_add_send_options(char *option); int script_init(const char *path, const char *ifname); ssize_t script_unhexlify(uint8_t *dst, size_t len, const char *src); @@ -512,3 +512,4 @@ bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new, void odhcp6c_expire(bool expire_ia_pd); uint32_t odhcp6c_elapsed(void); struct odhcp6c_opt *odhcp6c_find_opt(const uint16_t code); +struct odhcp6c_opt *odhcp6c_find_opt_by_name(const char *name);