Skip to content

Commit 24485bb

Browse files
committed
dhcpv6: add config for strict RFC7550
Some ISPs don't comply with RFC7550 and need workarounds to get proper IPv6 connectivity. Link: #152 Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
1 parent 591ce40 commit 24485bb

File tree

5 files changed

+72
-19
lines changed

5 files changed

+72
-19
lines changed

src/config.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,11 @@ bool config_set_auth_token(const char* token)
318318
return config_dhcp.auth_token != NULL;
319319
}
320320

321+
void config_set_client_opt_cfg(struct odhcp6c_opt_cfg *opt_cfg)
322+
{
323+
config_dhcp.strict_rfc7550 = opt_cfg->strict_rfc7550 != 0;
324+
}
325+
321326
static int config_parse_opt_u8(const char *src, uint8_t **dst)
322327
{
323328
int len = strlen(src);

src/config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ struct config_dhcp {
100100
uint16_t rand_factor;
101101
enum odhcp6c_auth_protocol auth_protocol;
102102
char* auth_token;
103+
bool strict_rfc7550;
103104
};
104105

105106
struct config_dhcp *config_dhcp_get(void);
@@ -126,6 +127,7 @@ bool config_set_irt_min(unsigned int value);
126127
bool config_set_rand_factor(unsigned int value);
127128
bool config_set_auth_protocol(const char* protocol);
128129
bool config_set_auth_token(const char* token);
130+
void config_set_client_opt_cfg(struct odhcp6c_opt_cfg *opt_cfg);
129131

130132
int config_add_opt(const uint16_t code, const uint8_t *data, const uint16_t len);
131133
int config_parse_opt_data(const char *data, uint8_t **dst, const unsigned int type, const bool array);

src/dhcpv6.c

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -985,13 +985,15 @@ static void dhcpv6_send(enum dhcpv6_msg req_msg_type, uint8_t trid[3], uint32_t
985985

986986
switch (req_msg_type) {
987987
case DHCPV6_MSG_REQUEST:
988-
/* Some broken ISPs won't behave properly if IA_NA is
989-
* sent on Requests when they have provided an empty
990-
* IA_NA on Advertise.
991-
* Therefore we don't comply with RFC7550 and omit
992-
* IA_NA as a workaround.
993-
*/
994-
iov[IOV_HDR_IA_NA].iov_len = 0;
988+
if (!config_dhcp->strict_rfc7550) {
989+
/* Some broken ISPs won't behave properly if IA_NA is
990+
* sent on Requests when they have provided an empty
991+
* IA_NA on Advertise.
992+
* Therefore we don't comply with RFC7550 and omit
993+
* IA_NA as a workaround.
994+
*/
995+
iov[IOV_HDR_IA_NA].iov_len = 0;
996+
}
995997
break;
996998
case DHCPV6_MSG_SOLICIT:
997999
break;
@@ -2134,6 +2136,7 @@ int dhcpv6_promote_server_cand(void)
21342136
struct dhcpv6_server_cand *cand = odhcp6c_get_state(STATE_SERVER_CAND, &cand_len);
21352137
uint16_t hdr[2];
21362138
int ret = DHCPV6_STATELESS;
2139+
bool override_ia = false;
21372140

21382141
// Clear lingering candidate state info
21392142
odhcp6c_clear_state(STATE_SERVER_ID);
@@ -2143,25 +2146,47 @@ int dhcpv6_promote_server_cand(void)
21432146
if (!cand_len)
21442147
return -1;
21452148

2146-
if (!cand->ia_pd_len && cand->has_noaddravail) {
2147-
bool override = false;
2149+
if (config_dhcp->strict_rfc7550) {
2150+
if (!cand->ia_pd_len && cand->has_noaddravail) {
2151+
/* Some ISPs provide neither IA_NA nor IA_PD, so we
2152+
* should fallback to SLAAC.
2153+
*/
2154+
2155+
if (na_mode == IA_MODE_TRY) {
2156+
na_mode = IA_MODE_NONE;
2157+
override_ia = true;
2158+
}
21482159

2149-
if (na_mode == IA_MODE_TRY) {
2160+
if (pd_mode == IA_MODE_TRY) {
2161+
pd_mode = IA_MODE_NONE;
2162+
override_ia = true;
2163+
}
2164+
}
2165+
} else {
2166+
if (cand->has_noaddravail && na_mode == IA_MODE_TRY) {
2167+
/* Some broken ISPs require a new Solicit message
2168+
* without IA_NA if they haven't provided an address
2169+
* on the Advertise message.
2170+
*/
21502171
na_mode = IA_MODE_NONE;
2151-
override = true;
2172+
override_ia = true;
21522173
}
21532174

2154-
if (pd_mode == IA_MODE_TRY) {
2175+
if (!cand->ia_pd_len && pd_mode == IA_MODE_TRY) {
2176+
/* Some broken ISPs require a new Solicit message
2177+
* without IA_PD if they haven't provided a prefix
2178+
* on the Advertise message.
2179+
*/
21552180
pd_mode = IA_MODE_NONE;
2156-
override = true;
2181+
override_ia = true;
21572182
}
2183+
}
21582184

2159-
if (override) {
2160-
dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = cand->sol_max_rt;
2161-
dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_timeo = cand->inf_max_rt;
2185+
if (override_ia) {
2186+
dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = cand->sol_max_rt;
2187+
dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_timeo = cand->inf_max_rt;
21622188

2163-
return -1;
2164-
}
2189+
return -1;
21652190
}
21662191

21672192
hdr[0] = htons(DHCPV6_OPT_SERVERID);

src/odhcp6c.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <ctype.h>
1818
#include <errno.h>
1919
#include <fcntl.h>
20+
#include <getopt.h>
2021
#include <limits.h>
2122
#include <linux/if_addr.h>
2223
#include <net/if.h>
@@ -201,6 +202,15 @@ static struct odhcp6c_opt opts[] = {
201202
{ .code = 0, .flags = 0, .str = NULL },
202203
};
203204

205+
static struct odhcp6c_opt_cfg opt_cfg = {
206+
.strict_rfc7550 = 0,
207+
};
208+
209+
static struct option opt_long[] = {
210+
{ "strict-rfc7550", no_argument, &opt_cfg.strict_rfc7550, 1 },
211+
{ NULL, 0, NULL, 0 },
212+
};
213+
204214
int main(_o_unused int argc, char* const argv[])
205215
{
206216
static struct in6_addr ifid = IN6ADDR_ANY_INIT;
@@ -209,6 +219,7 @@ int main(_o_unused int argc, char* const argv[])
209219
const char *script = "/lib/netifd/dhcpv6.script";
210220
ssize_t l;
211221
uint8_t buf[134], *o_data;
222+
int optidx;
212223
char *optpos;
213224
uint16_t opttype;
214225
struct odhcp6c_opt *opt;
@@ -228,8 +239,11 @@ int main(_o_unused int argc, char* const argv[])
228239

229240
atexit(odhcp6c_cleanup);
230241

231-
while ((c = getopt(argc, argv, "SDN:V:P:FB:c:i:r:Ru:Ux:s:EkK:t:C:m:Lhedp:favl:")) != -1) {
242+
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) {
232243
switch (c) {
244+
case 0:
245+
break;
246+
233247
case 'S':
234248
config_set_allow_slaac_only(false);
235249
break;
@@ -477,6 +491,8 @@ int main(_o_unused int argc, char* const argv[])
477491
}
478492
}
479493

494+
config_set_client_opt_cfg(&opt_cfg);
495+
480496
openlog("odhcp6c", logopt, LOG_DAEMON);
481497
setlogmask(LOG_UPTO(config_dhcp->log_level));
482498

@@ -858,6 +874,7 @@ static int usage(void)
858874
" -m <seconds> Minimum time between accepting RA updates (3)\n"
859875
" -L Ignore default lifetime for RDNSS records\n"
860876
" -U Ignore Server Unicast option\n"
877+
" --strict-rfc7550 Enforce RFC7550 compliance\n"
861878
"\nInvocation options:\n"
862879
" -p <pidfile> Set pidfile (/var/run/odhcp6c.pid)\n"
863880
" -d Daemonize\n"

src/odhcp6c.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,10 @@ struct odhcp6c_opt {
552552
const char *str;
553553
};
554554

555+
struct odhcp6c_opt_cfg {
556+
int strict_rfc7550;
557+
};
558+
555559
uint32_t hash_ifname(const char *s);
556560
int init_dhcpv6(const char *ifname);
557561
int dhcpv6_get_ia_mode(void);

0 commit comments

Comments
 (0)