Browse Source

dhcpv6: add option to ignore stateless advertise

This change adds a new option to `odhcp6c` that makes it ignore any
advertisement without *both* IA_NA and IA_PD option.
Note that there is already an way to ignore advertisements without IA_NA
specifically (`-N force`) or without IA_PD specifically (`-F`), but there
is no way to express "advertisement MUST have either IA_NA or IA_PD
to be considered" - which is addressed by this change.

There are two primary use-cases for that.
First is to fix an issue with `odhcp6c` behavior when it encounters
a setup with both "stateful" (with IA_NA or IA_PD) and "stateless"
advertisements - and both coming with the same server DUID.
In that case, when the "stateless" advertisement comes last
during the RT window - it will overwrite the advertise entry for that
server DUID and effectively make it seem like we only received
one "stateless" advertisement on the link.
This, in turn, makes `odhcp6c` go into stateless mode
unless there was `-N force` or `-F` involved.

Second use case is as described in the initial part:
when we want to run in "stateful" mode, but would like to accept either
of IA_NA or IA_PD or both of them - and discard other advertisements.

Signed-off-by: Viktor Iarmola <viktor.iarmola@ui.com>
viktor.iarmola 2 years ago
parent
commit
7d21e8d8ec
3 changed files with 14 additions and 5 deletions
  1. 5 2
      src/dhcpv6.c
  2. 8 2
      src/odhcp6c.c
  3. 1 1
      src/odhcp6c.h

+ 5 - 2
src/dhcpv6.c

@@ -104,6 +104,7 @@ static int64_t t1 = 0, t2 = 0, t3 = 0;
 
 // IA states
 static enum odhcp6c_ia_mode na_mode = IA_MODE_NONE, pd_mode = IA_MODE_NONE;
+static bool stateful_only_mode = false;
 static bool accept_reconfig = false;
 // Server unicast address
 static struct in6_addr server_addr = IN6ADDR_ANY_INIT;
@@ -317,12 +318,13 @@ enum {
 	IOV_TOTAL
 };
 
-int dhcpv6_set_ia_mode(enum odhcp6c_ia_mode na, enum odhcp6c_ia_mode pd)
+int dhcpv6_set_ia_mode(enum odhcp6c_ia_mode na, enum odhcp6c_ia_mode pd, bool stateful_only)
 {
 	int mode = DHCPV6_UNKNOWN;
 
 	na_mode = na;
 	pd_mode = pd;
+	stateful_only_mode = stateful_only;
 
 	if (na_mode == IA_MODE_NONE && pd_mode == IA_MODE_NONE)
 		mode = DHCPV6_STATELESS;
@@ -1004,7 +1006,8 @@ static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc,
 		}
 	}
 
-	if ((!have_na && na_mode == IA_MODE_FORCE) ||
+	if ((stateful_only_mode && !have_na && !have_pd) ||
+			(!have_na && na_mode == IA_MODE_FORCE) ||
 			(!have_pd && pd_mode == IA_MODE_FORCE)) {
 		/*
 		 * RFC7083 states to process the SOL_MAX_RT and

+ 8 - 2
src/odhcp6c.c

@@ -175,6 +175,7 @@ int main(_unused int argc, char* const argv[])
 	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 sol_timeout = DHCPV6_SOL_MAX_RT;
@@ -186,12 +187,16 @@ 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;
 
-	while ((c = getopt(argc, argv, "S::N:V:P:FB:c:i:r:Ru:Ux:s:kt:m:Lhedp:fav")) != -1) {
+	while ((c = getopt(argc, argv, "S::DN:V:P:FB:c:i:r:Ru:Ux:s:kt:m:Lhedp:fav")) != -1) {
 		switch (c) {
 		case 'S':
 			allow_slaac_only = (optarg) ? atoi(optarg) : -1;
 			break;
 
+		case 'D':
+			stateful_only_mode = 1;
+			break;
+
 		case 'N':
 			if (!strcmp(optarg, "force")) {
 				ia_na_mode = IA_MODE_FORCE;
@@ -456,7 +461,7 @@ int main(_unused int argc, char* const argv[])
 		syslog(LOG_NOTICE, "(re)starting transaction on %s", ifname);
 
 		signal_usr1 = signal_usr2 = false;
-		int mode = dhcpv6_set_ia_mode(ia_na_mode, ia_pd_mode);
+		int mode = dhcpv6_set_ia_mode(ia_na_mode, ia_pd_mode, stateful_only_mode);
 		if (mode != DHCPV6_STATELESS)
 			mode = dhcpv6_request(DHCPV6_MSG_SOLICIT);
 
@@ -602,6 +607,7 @@ static int usage(void)
 	"Usage: odhcp6c [options] <interface>\n"
 	"\nFeature options:\n"
 	"	-S <time>	Wait at least <time> sec for a DHCP-server (0)\n"
+	"	-D		Discard advertisements without any address or prefix proposed\n"
 	"	-N <mode>	Mode for requesting addresses [try|force|none]\n"
 	"	-P <length>	Request IPv6-Prefix (0 = auto)\n"
 	"	-F		Force IPv6-Prefix\n"

+ 1 - 1
src/odhcp6c.h

@@ -393,7 +393,7 @@ struct odhcp6c_opt {
 };
 
 int init_dhcpv6(const char *ifname, unsigned int client_options, int sol_timeout);
-int dhcpv6_set_ia_mode(enum odhcp6c_ia_mode na, enum odhcp6c_ia_mode pd);
+int dhcpv6_set_ia_mode(enum odhcp6c_ia_mode na, enum odhcp6c_ia_mode pd, bool stateful_only);
 int dhcpv6_request(enum dhcpv6_msg type);
 int dhcpv6_poll_reconfigure(void);
 int dhcpv6_promote_server_cand(void);