dhcpv6: add option to ignore stateless advertise
authorviktor.iarmola <viktor.iarmola@ui.com>
Fri, 21 Jan 2022 12:33:02 +0000 (14:33 +0200)
committerHans Dedecker <dedeckeh@gmail.com>
Mon, 1 Aug 2022 19:27:51 +0000 (21:27 +0200)
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>
src/dhcpv6.c
src/odhcp6c.c
src/odhcp6c.h

index 9411dd0abbbb9262519353cad8ecbc13d6b62ffa..01bda16b0b8b0f9d27612ba1070205b7af12db2d 100644 (file)
@@ -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
index 0f58945246abf423ab444cb323bca1850f4a14f0..e713da9f5a9c4c4942b733ec5de3c01261df3d82 100644 (file)
@@ -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"
index d27cdda35f247d9d996126155920da5dbe19f49c..0831775f8bcc5ce58cf87fc68a5ffff13f55ea8a 100644 (file)
@@ -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);