summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorÁlvaro Fernández Rojas2026-01-23 16:29:24 +0000
committerÁlvaro Fernández Rojas2026-01-25 13:54:38 +0000
commit24485bb4b35ab84c17c2e87bd561d026d4c15c00 (patch)
tree8a8e9e8cf8bd96b2af28e606e286ff5c56ed2815
parent591ce406c70b9b6916a29d30ea8279d57f53a82b (diff)
downloadodhcp6c-master.tar.gz
dhcpv6: add config for strict RFC7550HEADmaster
Some ISPs don't comply with RFC7550 and need workarounds to get proper IPv6 connectivity. Link: https://github.com/openwrt/odhcp6c/pull/152 Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
-rw-r--r--src/config.c5
-rw-r--r--src/config.h2
-rw-r--r--src/dhcpv6.c61
-rw-r--r--src/odhcp6c.c19
-rw-r--r--src/odhcp6c.h4
5 files changed, 72 insertions, 19 deletions
diff --git a/src/config.c b/src/config.c
index 4a1b6d9..54e2d35 100644
--- a/src/config.c
+++ b/src/config.c
@@ -318,6 +318,11 @@ bool config_set_auth_token(const char* token)
return config_dhcp.auth_token != NULL;
}
+void config_set_client_opt_cfg(struct odhcp6c_opt_cfg *opt_cfg)
+{
+ config_dhcp.strict_rfc7550 = opt_cfg->strict_rfc7550 != 0;
+}
+
static int config_parse_opt_u8(const char *src, uint8_t **dst)
{
int len = strlen(src);
diff --git a/src/config.h b/src/config.h
index cce1c04..91713de 100644
--- a/src/config.h
+++ b/src/config.h
@@ -100,6 +100,7 @@ struct config_dhcp {
uint16_t rand_factor;
enum odhcp6c_auth_protocol auth_protocol;
char* auth_token;
+ bool strict_rfc7550;
};
struct config_dhcp *config_dhcp_get(void);
@@ -126,6 +127,7 @@ bool config_set_irt_min(unsigned int value);
bool config_set_rand_factor(unsigned int value);
bool config_set_auth_protocol(const char* protocol);
bool config_set_auth_token(const char* token);
+void config_set_client_opt_cfg(struct odhcp6c_opt_cfg *opt_cfg);
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);
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index 05d55d3..b82dc30 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -985,13 +985,15 @@ static void dhcpv6_send(enum dhcpv6_msg req_msg_type, uint8_t trid[3], uint32_t
switch (req_msg_type) {
case DHCPV6_MSG_REQUEST:
- /* Some broken ISPs won't behave properly if IA_NA is
- * sent on Requests when they have provided an empty
- * IA_NA on Advertise.
- * Therefore we don't comply with RFC7550 and omit
- * IA_NA as a workaround.
- */
- iov[IOV_HDR_IA_NA].iov_len = 0;
+ if (!config_dhcp->strict_rfc7550) {
+ /* Some broken ISPs won't behave properly if IA_NA is
+ * sent on Requests when they have provided an empty
+ * IA_NA on Advertise.
+ * Therefore we don't comply with RFC7550 and omit
+ * IA_NA as a workaround.
+ */
+ iov[IOV_HDR_IA_NA].iov_len = 0;
+ }
break;
case DHCPV6_MSG_SOLICIT:
break;
@@ -2134,6 +2136,7 @@ int dhcpv6_promote_server_cand(void)
struct dhcpv6_server_cand *cand = odhcp6c_get_state(STATE_SERVER_CAND, &cand_len);
uint16_t hdr[2];
int ret = DHCPV6_STATELESS;
+ bool override_ia = false;
// Clear lingering candidate state info
odhcp6c_clear_state(STATE_SERVER_ID);
@@ -2143,25 +2146,47 @@ int dhcpv6_promote_server_cand(void)
if (!cand_len)
return -1;
- if (!cand->ia_pd_len && cand->has_noaddravail) {
- bool override = false;
+ if (config_dhcp->strict_rfc7550) {
+ if (!cand->ia_pd_len && cand->has_noaddravail) {
+ /* Some ISPs provide neither IA_NA nor IA_PD, so we
+ * should fallback to SLAAC.
+ */
+
+ if (na_mode == IA_MODE_TRY) {
+ na_mode = IA_MODE_NONE;
+ override_ia = true;
+ }
- if (na_mode == IA_MODE_TRY) {
+ if (pd_mode == IA_MODE_TRY) {
+ pd_mode = IA_MODE_NONE;
+ override_ia = true;
+ }
+ }
+ } else {
+ if (cand->has_noaddravail && na_mode == IA_MODE_TRY) {
+ /* Some broken ISPs require a new Solicit message
+ * without IA_NA if they haven't provided an address
+ * on the Advertise message.
+ */
na_mode = IA_MODE_NONE;
- override = true;
+ override_ia = true;
}
- if (pd_mode == IA_MODE_TRY) {
+ if (!cand->ia_pd_len && pd_mode == IA_MODE_TRY) {
+ /* Some broken ISPs require a new Solicit message
+ * without IA_PD if they haven't provided a prefix
+ * on the Advertise message.
+ */
pd_mode = IA_MODE_NONE;
- override = true;
+ override_ia = true;
}
+ }
- if (override) {
- dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = cand->sol_max_rt;
- dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_timeo = cand->inf_max_rt;
+ if (override_ia) {
+ dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = cand->sol_max_rt;
+ dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_timeo = cand->inf_max_rt;
- return -1;
- }
+ return -1;
}
hdr[0] = htons(DHCPV6_OPT_SERVERID);
diff --git a/src/odhcp6c.c b/src/odhcp6c.c
index 1966d27..235632d 100644
--- a/src/odhcp6c.c
+++ b/src/odhcp6c.c
@@ -17,6 +17,7 @@
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
+#include <getopt.h>
#include <limits.h>
#include <linux/if_addr.h>
#include <net/if.h>
@@ -201,6 +202,15 @@ static struct odhcp6c_opt opts[] = {
{ .code = 0, .flags = 0, .str = NULL },
};
+static struct odhcp6c_opt_cfg opt_cfg = {
+ .strict_rfc7550 = 0,
+};
+
+static struct option opt_long[] = {
+ { "strict-rfc7550", no_argument, &opt_cfg.strict_rfc7550, 1 },
+ { NULL, 0, NULL, 0 },
+};
+
int main(_o_unused int argc, char* const argv[])
{
static struct in6_addr ifid = IN6ADDR_ANY_INIT;
@@ -209,6 +219,7 @@ int main(_o_unused int argc, char* const argv[])
const char *script = "/lib/netifd/dhcpv6.script";
ssize_t l;
uint8_t buf[134], *o_data;
+ int optidx;
char *optpos;
uint16_t opttype;
struct odhcp6c_opt *opt;
@@ -228,8 +239,11 @@ int main(_o_unused int argc, char* const argv[])
atexit(odhcp6c_cleanup);
- while ((c = getopt(argc, argv, "SDN:V:P:FB:c:i:r:Ru:Ux:s:EkK:t:C:m:Lhedp:favl:")) != -1) {
+ while ((c = getopt_long(argc, argv, "SDN:V:P:FB:c:i:r:Ru:Ux:s:EkK:t:C:m:Lhedp:favl:", opt_long, &optidx)) != -1) {
switch (c) {
+ case 0:
+ break;
+
case 'S':
config_set_allow_slaac_only(false);
break;
@@ -477,6 +491,8 @@ int main(_o_unused int argc, char* const argv[])
}
}
+ config_set_client_opt_cfg(&opt_cfg);
+
openlog("odhcp6c", logopt, LOG_DAEMON);
setlogmask(LOG_UPTO(config_dhcp->log_level));
@@ -858,6 +874,7 @@ static int usage(void)
" -m <seconds> Minimum time between accepting RA updates (3)\n"
" -L Ignore default lifetime for RDNSS records\n"
" -U Ignore Server Unicast option\n"
+ " --strict-rfc7550 Enforce RFC7550 compliance\n"
"\nInvocation options:\n"
" -p <pidfile> Set pidfile (/var/run/odhcp6c.pid)\n"
" -d Daemonize\n"
diff --git a/src/odhcp6c.h b/src/odhcp6c.h
index bd3c515..a626c52 100644
--- a/src/odhcp6c.h
+++ b/src/odhcp6c.h
@@ -552,6 +552,10 @@ struct odhcp6c_opt {
const char *str;
};
+struct odhcp6c_opt_cfg {
+ int strict_rfc7550;
+};
+
uint32_t hash_ifname(const char *s);
int init_dhcpv6(const char *ifname);
int dhcpv6_get_ia_mode(void);