* RA_RETRANSMIT ND Retransmit time
* AFTR The DS-Lite AFTR domain name
* MAPE / MAPT / LW4O6 Softwire rules for MAPE, MAPT and LW4O6
+
+** Ubus Integration **
+
+Build with ENABLE_UBUS flag to connect odhcp6c to ubus. Object is registered at : "odhcp6c.{ifname}."
+
+Events are emitted whenever the DHCPv6 state changes and can replace the use of a state script. The variables are the same as those defined in the State Script section.
+
+The following RPC methods are available:
+
+* *get_state()* : Returns the DHCPv6 state
+ - OUT : see State Script section
+* *get_statistics()* : Returns the packet statistics
+ - OUT :
+ - *dhcp_solicit* : Total number of SOLICIT messages sent
+ - *dhcp_advertise* : Total number of ADVERTISE messages received
+ - *dhcp_request* : Total number of REQUEST messages sent
+ - *dhcp_confirm* : Total number of CONFIRM messages sent
+ - *dhcp_renew* : Total number of RENEW messages sent
+ - *dhcp_rebind* : Total number of REBIND messages sent
+ - *dhcp_reply* : Total number of REPLY messages received
+ - *dhcp_release* : Total number of RELEASE messages sent
+ - *dhcp_decline* : Total number of DECLINE messages sent
+ - *dhcp_reconfigure* : Total number of RECONFIGURE messages received
+ - *dhcp_information_request* : Total number of INFORMATION-REQUEST messages sent
+ - *dhcp_discarded_packets* : Total number of discarded DHCP packets
+ - *dhcp_transmit_failures* : Total number of DHCP messages that failed to be transmitted
+* *reset_statistics()* : Reset packet statistics
+* *reconfigure_dhcp(IN)* : Reconfigure DHCP settings
+ - IN (optional):
+ - *dscp* (int) : DSCP value used for DHCP packets
+ - *release* (bool) : Send a RELEASE message on exit/reset
+ - *sol_timeout* (int) : Maximum timeout for DHCPv6-SOLICIT
+ - *sk_prio* (int) : Packet kernel priority
+ - *opt_requested* (int[]) : Options to be requested
+ - *opt_strict* (bool) : Do not request any options except those specified
+ - *opt_reconfigure* (bool) : Send Accept Reconfigure option
+ - *opt_fqdn* (bool) : Send Client FQDN option
+ - *opt_unicast* (bool) : Ignore Server Unicast option
+ - *opt_send* (string[]) : Options to be sent
+ - *req_addresses* (string{try|force|none}) : Request addresses
+ - *req_prefixes* (int) : Request Prefixes (0 = auto)
+ - *stateful_only* (bool) : Discard advertisements without any address or prefix proposed
static int dhcpv6_commit_advert(void);
// RFC 3315 - 5.5 Timeout and Delay values
-static struct dhcpv6_retx dhcpv6_retx[_DHCPV6_MSG_MAX] = {
+static const struct dhcpv6_retx dhcpv6_retx_default[_DHCPV6_MSG_MAX] = {
[DHCPV6_MSG_UNKNOWN] = {false, 1, 120, 0, "<POLL>",
dhcpv6_handle_reconfigure, NULL, false, 0, 0, 0, {0, 0, 0}, 0, 0, 0, -1, 0},
[DHCPV6_MSG_SOLICIT] = {true, 1, DHCPV6_SOL_MAX_RT, 0, "SOLICIT",
[DHCPV6_MSG_INFO_REQ] = {true, 1, DHCPV6_INF_MAX_RT, 0, "INFOREQ",
dhcpv6_handle_reply, NULL, false, 0, 0, 0, {0, 0, 0}, 0, 0, 0, -1, 0},
};
+static struct dhcpv6_retx dhcpv6_retx[_DHCPV6_MSG_MAX] = {0};
// Sockets
static int sock = -1;
int init_dhcpv6(const char *ifname, unsigned int options, int sk_prio, int sol_timeout, unsigned int dscp)
{
+ memcpy(dhcpv6_retx, dhcpv6_retx_default, sizeof(dhcpv6_retx));
client_options = options;
dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = sol_timeout;
#include "ra.h"
#include "ubus.h"
+#define DHCPV6_FD_INDEX 0
+#define UBUS_FD_INDEX 1
+
#ifndef IN6_IS_ADDR_UNIQUELOCAL
#define IN6_IS_ADDR_UNIQUELOCAL(a) \
((((__const uint32_t *) (a))[0] & htonl (0xfe000000)) \
static bool bound = false, release = true, 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;
static unsigned int script_sync_delay = 10;
static unsigned int script_accu_delay = 1;
uint8_t buf[134], *o_data;
char *optpos;
uint16_t opttype;
- enum odhcp6c_ia_mode ia_na_mode = IA_MODE_TRY;
- enum odhcp6c_ia_mode ia_pd_mode = IA_MODE_NONE;
- bool stateful_only_mode = 0;
struct odhcp6c_opt *opt;
int ia_pd_iaid_index = 0;
- int sk_prio = 0;
- int sol_timeout = DHCPV6_SOL_MAX_RT;
int verbosity = 0;
bool help = false, daemonize = false;
int logopt = LOG_PID;
int c;
int res = -1;
- unsigned int client_options = DHCPV6_CLIENT_FQDN | DHCPV6_ACCEPT_RECONFIGURE;
unsigned int ra_options = RA_RDNSS_DEFAULT_LIFETIME;
unsigned int ra_holdoff_interval = RA_MIN_ADV_INTERVAL;
- unsigned int dscp = 0;
bool terminate = false;
while ((c = getopt(argc, argv, "S::DN:V:P:FB:c:i:r:Ru:Ux:s:kK:t:C:m:Lhedp:fav")) != -1) {
}
if ((urandom_fd = open("/dev/urandom", O_CLOEXEC | O_RDONLY)) < 0 ||
- init_dhcpv6(ifname, client_options, sk_prio, sol_timeout, dscp) ||
ra_init(ifname, &ifid, ra_options, ra_holdoff_interval) ||
script_init(script, ifname)) {
syslog(LOG_ERR, "failed to initialize: %s", strerror(errno));
struct pollfd fds[2] = {0};
int nfds = 0;
- int dhcpv6_socket = dhcpv6_get_socket();
int mode = DHCPV6_UNKNOWN;
enum dhcpv6_msg msg_type = DHCPV6_MSG_UNKNOWN;
- if (dhcpv6_socket < 0) {
- syslog(LOG_ERR, "Invalid dhcpv6 file descriptor");
- return 1;
- }
-
- fds[nfds].fd = dhcpv6_socket;
- fds[nfds].events = POLLIN;
+ fds[DHCPV6_FD_INDEX].fd = -1;
+ fds[DHCPV6_FD_INDEX].events = POLLIN;
nfds++;
#ifdef WITH_UBUS
syslog(LOG_ERR, "Invalid ubus file descriptor");
return 1;
}
- fds[nfds].fd = ubus_socket;
- fds[nfds].events = POLLIN;
+ fds[UBUS_FD_INDEX].fd = ubus_socket;
+ fds[UBUS_FD_INDEX].events = POLLIN;
nfds++;
#endif /* WITH_UBUS */
odhcp6c_clear_state(STATE_SIP_FQDN);
bound = false;
+ 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);
+
+ if (init_dhcpv6(ifname, client_options, sk_prio, sol_timeout, dscp)) {
+ syslog(LOG_ERR, "failed to initialize: %s", strerror(errno));
+ return 1;
+ }
+
+ fds[DHCPV6_FD_INDEX].fd = dhcpv6_get_socket();
+
syslog(LOG_NOTICE, "(re)starting transaction on %s", ifname);
signal_usr1 = signal_usr2 = false;
dhcpv6_set_state(DHCPV6_REQUEST);
break;
}
-
+
msg_type = DHCPV6_MSG_SOLICIT;
dhcpv6_send_request(msg_type);
break;
terminate = true;
} else {
signal_usr2 = false;
- dhcpv6_set_state(DHCPV6_INIT);
+ dhcpv6_set_state(DHCPV6_RESET);
}
break;
+ case DHCPV6_RESET:
+ odhcp6c_clear_state(STATE_CLIENT_ID);
+
+ 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);
+ odhcp6c_remove_state(STATE_ORO, oro_user_len, oro_total_len - oro_user_len);
+
+ close(dhcpv6_get_socket());
+ fds[DHCPV6_FD_INDEX].fd = -1;
+
+ dhcpv6_set_state(DHCPV6_INIT);
+ break;
+
default:
break;
}
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);
+}
DHCPV6_INFO,
DHCPV6_INFO_PROCESSING,
DHCPV6_INFO_REPLY,
- DHCPV6_EXIT
+ DHCPV6_EXIT,
+ DHCPV6_RESET
};
enum dhcpv6_status {
void notify_state_change(const char *status, int delay, bool resume);
+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 value);
+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_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);
+
int script_init(const char *path, const char *ifname);
ssize_t script_unhexlify(uint8_t *dst, size_t len, const char *src);
void script_hexlify(char *dst, const uint8_t *src, size_t len);
ENTRY_PREFIX
};
+enum {
+ RECONFIGURE_DHCP_ATTR_DSCP,
+ RECONFIGURE_DHCP_ATTR_RELEASE,
+ RECONFIGURE_DHCP_ATTR_SOL_TIMEOUT,
+ RECONFIGURE_DHCP_ATTR_SK_PRIORITY,
+ RECONFIGURE_DHCP_ATTR_OPT_REQUESTED,
+ RECONFIGURE_DHCP_ATTR_OPT_STRICT,
+ RECONFIGURE_DHCP_ATTR_OPT_RECONFIGURE,
+ RECONFIGURE_DHCP_ATTR_OPT_FQDN,
+ RECONFIGURE_DHCP_ATTR_OPT_UNICAST,
+ RECONFIGURE_DHCP_ATTR_OPT_SEND,
+ RECONFIGURE_DHCP_ATTR_REQ_ADDRESSES,
+ RECONFIGURE_DHCP_ATTR_REQ_PREFIXES,
+ RECONFIGURE_DHCP_ATTR_STATEFUL,
+ RECONFIGURE_DHCP_ATTR_MAX,
+};
+
struct ubus_context *ubus = NULL;
static struct blob_buf b;
static char ubus_name[24];
struct ubus_request_data *req, const char *method, struct blob_attr *msg);
static int ubus_handle_reset_stats(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method, struct blob_attr *msg);
+static int ubus_handle_reconfigure_dhcp(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method, struct blob_attr *msg);
+
+static const struct blobmsg_policy reconfigure_dhcp_policy[RECONFIGURE_DHCP_ATTR_MAX] = {
+ [RECONFIGURE_DHCP_ATTR_DSCP] = { .name = "dscp", .type = BLOBMSG_TYPE_INT32},
+ [RECONFIGURE_DHCP_ATTR_RELEASE] = { .name = "release", .type = BLOBMSG_TYPE_BOOL},
+ [RECONFIGURE_DHCP_ATTR_SOL_TIMEOUT] = { .name = "sol_timeout", .type = BLOBMSG_TYPE_INT32},
+ [RECONFIGURE_DHCP_ATTR_SK_PRIORITY] = { .name = "sk_prio", .type = BLOBMSG_TYPE_INT32},
+ [RECONFIGURE_DHCP_ATTR_OPT_REQUESTED] = { .name = "opt_requested", .type = BLOBMSG_TYPE_ARRAY},
+ [RECONFIGURE_DHCP_ATTR_OPT_STRICT] = { .name = "opt_strict", .type = BLOBMSG_TYPE_BOOL},
+ [RECONFIGURE_DHCP_ATTR_OPT_RECONFIGURE] = { .name = "opt_reconfigure", .type = BLOBMSG_TYPE_BOOL},
+ [RECONFIGURE_DHCP_ATTR_OPT_FQDN] = { .name = "opt_fqdn", .type = BLOBMSG_TYPE_BOOL},
+ [RECONFIGURE_DHCP_ATTR_OPT_UNICAST] = { .name = "opt_unicast", .type = BLOBMSG_TYPE_BOOL},
+ [RECONFIGURE_DHCP_ATTR_OPT_SEND] = { .name = "opt_send", .type = BLOBMSG_TYPE_ARRAY},
+ [RECONFIGURE_DHCP_ATTR_REQ_ADDRESSES] = { .name = "req_addresses", .type = BLOBMSG_TYPE_STRING},
+ [RECONFIGURE_DHCP_ATTR_REQ_PREFIXES] = { .name = "req_prefixes", .type = BLOBMSG_TYPE_INT32},
+ [RECONFIGURE_DHCP_ATTR_STATEFUL] = { .name = "stateful_only", .type = BLOBMSG_TYPE_BOOL},
+};
static struct ubus_method odhcp6c_object_methods[] = {
UBUS_METHOD_NOARG("get_state", ubus_handle_get_state),
UBUS_METHOD_NOARG("get_statistics", ubus_handle_get_stats),
UBUS_METHOD_NOARG("reset_statistics", ubus_handle_reset_stats),
+ UBUS_METHOD("reconfigure_dhcp", ubus_handle_reconfigure_dhcp, reconfigure_dhcp_policy),
};
static struct ubus_object_type odhcp6c_object_type =
return UBUS_STATUS_OK;
}
+static int ubus_handle_reconfigure_dhcp(_unused struct ubus_context *ctx, _unused struct ubus_object *obj,
+ _unused struct ubus_request_data *req, _unused const char *method,
+ struct blob_attr *msg)
+{
+ const struct blobmsg_policy *policy = reconfigure_dhcp_policy;
+ struct blob_attr *tb[RECONFIGURE_DHCP_ATTR_MAX];
+ struct blob_attr *cur = NULL;
+ struct blob_attr *elem = NULL;
+ char *string = NULL;
+ uint32_t value = 0;
+ uint32_t index = 0;
+ bool enabled = false;
+ bool valid_args = false;
+ bool need_reinit = false;
+
+ if (blobmsg_parse(policy, RECONFIGURE_DHCP_ATTR_MAX, tb, blob_data(msg), blob_len(msg)))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ if ((cur = tb[RECONFIGURE_DHCP_ATTR_DSCP])) {
+ value = blobmsg_get_u32(cur);
+ if (!config_set_dscp(value))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ need_reinit = true;
+ valid_args = true;
+ }
+
+ if ((cur = tb[RECONFIGURE_DHCP_ATTR_RELEASE])) {
+ enabled = blobmsg_get_bool(cur);
+ config_set_release(enabled);
+ valid_args = true;
+ }
+
+ if ((cur = tb[RECONFIGURE_DHCP_ATTR_SOL_TIMEOUT])) {
+ value = blobmsg_get_u32(cur);
+ if (!config_set_solicit_timeout(value))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ need_reinit = true;
+ valid_args = true;
+ }
+
+ if ((cur = tb[RECONFIGURE_DHCP_ATTR_SK_PRIORITY])) {
+ value = blobmsg_get_u32(cur);
+ if (!config_set_sk_priority(value))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ need_reinit = true;
+ valid_args = true;
+ }
+
+ if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_REQUESTED])) {
+ if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, false))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ config_clear_requested_options();
+
+ blobmsg_for_each_attr(elem, cur, index) {
+ if (blobmsg_type(elem) != BLOBMSG_TYPE_INT32)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ value = blobmsg_get_u32(elem);
+ if (!config_add_requested_options(value))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ }
+
+ need_reinit = true;
+ valid_args = true;
+ }
+
+ if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_STRICT])) {
+ enabled = blobmsg_get_bool(cur);
+ config_set_client_options(DHCPV6_STRICT_OPTIONS, enabled);
+ need_reinit = true;
+ valid_args = true;
+ }
+
+ if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_RECONFIGURE])) {
+ enabled = blobmsg_get_bool(cur);
+ config_set_client_options(DHCPV6_ACCEPT_RECONFIGURE, enabled);
+ need_reinit = true;
+ valid_args = true;
+ }
+
+ if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_FQDN])) {
+ enabled = blobmsg_get_bool(cur);
+ config_set_client_options(DHCPV6_CLIENT_FQDN, enabled);
+ need_reinit = true;
+ valid_args = true;
+ }
+
+ if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_UNICAST])) {
+ enabled = blobmsg_get_bool(cur);
+ config_set_client_options(DHCPV6_IGNORE_OPT_UNICAST, enabled);
+ need_reinit = true;
+ valid_args = true;
+ }
+
+ if ((cur = tb[RECONFIGURE_DHCP_ATTR_OPT_SEND])) {
+ if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, false))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ config_clear_send_options();
+
+ blobmsg_for_each_attr(elem, cur, index) {
+ string = blobmsg_get_string(elem);
+ if (string == NULL || !config_add_send_options(string))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ }
+
+ need_reinit = true;
+ valid_args = true;
+ }
+
+ if ((cur = tb[RECONFIGURE_DHCP_ATTR_REQ_ADDRESSES])) {
+ string = blobmsg_get_string(cur);
+ if (string == NULL || !config_set_request_addresses(string))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ need_reinit = true;
+ valid_args = true;
+ }
+
+ if ((cur = tb[RECONFIGURE_DHCP_ATTR_REQ_PREFIXES])) {
+ value = blobmsg_get_u32(cur);
+
+ if (!config_set_request_prefix(value, 1))
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ need_reinit = true;
+ valid_args = true;
+ }
+
+ if ((cur = tb[RECONFIGURE_DHCP_ATTR_STATEFUL])) {
+ enabled = blobmsg_get_bool(cur);
+ config_set_stateful_only(enabled);
+ need_reinit = true;
+ valid_args = true;
+ }
+
+ if (need_reinit)
+ raise(SIGUSR2);
+
+ return valid_args ? UBUS_STATUS_OK : UBUS_STATUS_INVALID_ARGUMENT;
+}
+
int ubus_dhcp_event(const char *status)
{
if (!ubus || !odhcp6c_object.has_subscribers)