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
)
--- /dev/null
+/**
+ *
+ * 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 <string.h>
+#include <resolv.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#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;
+}
--- /dev/null
+/*
+ *
+ * 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_ */
#include <arpa/inet.h>
#include <linux/if_addr.h>
+#include "config.h"
#include "odhcp6c.h"
#include "ra.h"
#include "ubus.h"
((((__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};
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;
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;
}
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;
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 };
break;
case 'F':
- allow_slaac_only = -1;
- ia_pd_mode = IA_MODE_FORCE;
+ config_set_force_prefix(true);
break;
case 'c':
break;
case 'R':
- client_options |= DHCPV6_STRICT_OPTIONS;
+ config_set_client_options(DHCPV6_STRICT_OPTIONS, true);
break;
case 'u':
}
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;
break;
case 'U':
- client_options |= DHCPV6_IGNORE_OPT_UNICAST;
+ config_set_client_options(DHCPV6_IGNORE_OPT_UNICAST, true);
break;
case 's':
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;
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':
break;
case 'x':
- res = parse_opt(optarg);
+ res = config_parse_opt(optarg);
if (res) {
if (res > 0)
return res;
}
}
- 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)
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;
}
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;
dhcpv6_set_state(DHCPV6_REQUEST);
} else {
mode = DHCPV6_UNKNOWN;
- dhcpv6_set_state(DHCPV6_INIT);
+ dhcpv6_set_state(DHCPV6_RESET);
}
break;
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:
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);
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());
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;
}
}
-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;
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)
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;
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);
-}
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);
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);