From 7d21e8d8ecd6d776f64b6928f62893c0f5c9747a Mon Sep 17 00:00:00 2001 From: "viktor.iarmola" Date: Fri, 21 Jan 2022 14:33:02 +0200 Subject: [PATCH] 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 --- src/dhcpv6.c | 7 +++++-- src/odhcp6c.c | 10 ++++++++-- src/odhcp6c.h | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 9411dd0..01bda16 100644 --- a/src/dhcpv6.c +++ b/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 diff --git a/src/odhcp6c.c b/src/odhcp6c.c index 0f58945..e713da9 100644 --- a/src/odhcp6c.c +++ b/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] \n" "\nFeature options:\n" " -S