]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-network: Rectify Advertise Message Processing by a Client
authorSusant Sahani <ssahani@vmware.com>
Thu, 16 Apr 2020 15:00:29 +0000 (17:00 +0200)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 29 May 2020 07:17:36 +0000 (16:17 +0900)
We need to fix RCC 2215 behaviour with rfc7550 errata
and https://tools.ietf.org/html/rfc8415.

[RFC3315] specifies that a client must ignore an Advertise message if
   a server will not assign any addresses to a client, and [RFC3633]
   specifies that a client must ignore an Advertise message if a server
   returns the NoPrefixAvail status to a requesting router.  Thus, a
   client requesting both IA_NA and IA_PD, with a server that only
   offers either addresses or delegated prefixes, is not supported by
   the current protocol specifications.

   Solution: a client SHOULD accept Advertise messages, even when not
   all IA option types are being offered.  And, in this case, the client
   SHOULD include the not offered IA option types in its Request.  A
   client SHOULD only ignore an Advertise message when none of the
   requested IA options include offered addresses or delegated prefixes.
   Note that ignored messages MUST still be processed for SOL_MAX_RT and
   INF_MAX_RT options as specified in [RFC7083].

   Replace Section 17.1.3 of RFC 3315: (existing errata)

     The client MUST ignore any Advertise message that includes a Status
     Code option containing the value NoAddrsAvail, with the exception
     that the client MAY display the associated status message(s) to the
     user.

   With the following text (which addresses the existing erratum
   [Err2471] and includes the changes made by [RFC7083]):

     The client MUST ignore any Advertise message that contains no
     addresses (IAADDR options encapsulated in IA_NA or IA_TA options)
     and no delegated prefixes (IAPREFIX options encapsulated in IA_PD
     options; see RFC 3633) with the exception that the client:

       - MUST process an included SOL_MAX_RT option (RFC 7083) and
       - MUST process an included INF_MAX_RT option (RFC 7083).

     A client can display any associated status message(s) to the user
     or activity log.

     The client ignoring this Advertise message MUST NOT restart the
     Solicit retransmission timer.

src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/dhcp6-option.c
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/test-dhcp6-client.c

index ced85fd3afe7a0a26b3baeb68a81654cc4774eb3..2880bc4770f62b0f54e6886bb5c8a2329b9e04a9 100644 (file)
@@ -102,7 +102,7 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_
 int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
                        size_t *optlen, uint8_t **optvalue);
 int dhcp6_option_parse_status(DHCP6Option *option, size_t len);
-int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia);
+int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code);
 int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
                                 struct in6_addr **addrs, size_t count,
                                 size_t *allocated);
index 0ec36507cd46cae97004c42c83e56bb86613f6e9..f763474e6ec58b4f94a2d4c86be9109f30193ca4 100644 (file)
@@ -417,13 +417,13 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
         return 0;
 }
 
-int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
+int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code) {
+        uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
         uint16_t iatype, optlen;
-        size_t i, len;
+        size_t iaaddr_offset;
         int r = 0, status;
+        size_t i, len;
         uint16_t opt;
-        size_t iaaddr_offset;
-        uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
 
         assert_return(ia, -EINVAL);
         assert_return(!ia->addresses, -EINVAL);
@@ -533,11 +533,15 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
                         status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data));
                         if (status < 0)
                                 return status;
+
                         if (status > 0) {
+                                if (ret_status_code)
+                                        *ret_status_code = status;
+
                                 log_dhcp6_client(client, "IA status %s",
                                                  dhcp6_message_status_to_string(status));
 
-                                return -EINVAL;
+                                return 0;
                         }
 
                         break;
@@ -581,7 +585,10 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
                 break;
         }
 
-        return 0;
+        if (ret_status_code)
+                *ret_status_code = 0;
+
+        return 1;
 }
 
 int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
index 13d8f652888a1ce9c116bf4231eae31f31b6fa8b..76a16a18c10d793975ea925789934e9c7696d4bd 100644 (file)
@@ -998,10 +998,11 @@ static int client_parse_message(
                 size_t len,
                 sd_dhcp6_lease *lease) {
 
+        uint16_t ia_na_status = 0, ia_pd_status = 0;
         uint32_t lt_t1 = ~0, lt_t2 = ~0;
+        usec_t irt = IRT_DEFAULT;
         bool clientid = false;
         size_t pos = 0;
-        usec_t irt = IRT_DEFAULT;
         int r;
 
         assert(client);
@@ -1015,8 +1016,8 @@ static int client_parse_message(
                 DHCP6Option *option = (DHCP6Option *) &message->options[pos];
                 uint16_t optcode, optlen;
                 be32_t iaid_lease;
+                int  status;
                 uint8_t *optval;
-                int status;
 
                 if (len < pos + offsetof(DHCP6Option, data))
                         return -ENOBUFS;
@@ -1093,10 +1094,15 @@ static int client_parse_message(
                                 break;
                         }
 
-                        r = dhcp6_option_parse_ia(option, &lease->ia);
+                        r = dhcp6_option_parse_ia(option, &lease->ia, &ia_na_status);
                         if (r < 0 && r != -ENOMSG)
                                 return r;
 
+                        if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) {
+                                pos += offsetof(DHCP6Option, data) + optlen;
+                                continue;
+                        }
+
                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
                         if (r < 0)
                                 return r;
@@ -1121,10 +1127,15 @@ static int client_parse_message(
                                 break;
                         }
 
-                        r = dhcp6_option_parse_ia(option, &lease->pd);
+                        r = dhcp6_option_parse_ia(option, &lease->pd, &ia_pd_status);
                         if (r < 0 && r != -ENOMSG)
                                 return r;
 
+                        if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) {
+                                pos += offsetof(DHCP6Option, data) + optlen;
+                                continue;
+                        }
+
                         r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
                         if (r < 0)
                                 return r;
@@ -1188,6 +1199,11 @@ static int client_parse_message(
                 pos += offsetof(DHCP6Option, data) + optlen;
         }
 
+        if (ia_na_status > 0 && ia_pd_status > 0) {
+                log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring.");
+                return -EINVAL;
+        }
+
         if (!clientid) {
                 log_dhcp6_client(client, "%s has incomplete options",
                                  dhcp6_message_type_to_string(message->type));
index 4b40e31c1249ac4dff65d3f5ec7df91a229ca1f5..56c8c978e5817f2ca207de2906ea138a66237b82 100644 (file)
@@ -247,17 +247,17 @@ static int test_option_status(sd_event *e) {
         option = (DHCP6Option *)option1;
         assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len));
 
-        r = dhcp6_option_parse_ia(option, &ia);
-        assert_se(r == -EINVAL);
+        r = dhcp6_option_parse_ia(option, &ia, NULL);
+        assert_se(r == 0);
         assert_se(ia.addresses == NULL);
 
         option->len = htobe16(17);
-        r = dhcp6_option_parse_ia(option, &ia);
+        r = dhcp6_option_parse_ia(option, &ia, NULL);
         assert_se(r == -ENOBUFS);
         assert_se(ia.addresses == NULL);
 
         option->len = htobe16(sizeof(DHCP6Option));
-        r = dhcp6_option_parse_ia(option, &ia);
+        r = dhcp6_option_parse_ia(option, &ia, NULL);
         assert_se(r == -ENOBUFS);
         assert_se(ia.addresses == NULL);
 
@@ -265,7 +265,7 @@ static int test_option_status(sd_event *e) {
         option = (DHCP6Option *)option2;
         assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len));
 
-        r = dhcp6_option_parse_ia(option, &ia);
+        r = dhcp6_option_parse_ia(option, &ia, NULL);
         assert_se(r >= 0);
         assert_se(ia.addresses == NULL);
 
@@ -273,7 +273,7 @@ static int test_option_status(sd_event *e) {
         option = (DHCP6Option *)option3;
         assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len));
 
-        r = dhcp6_option_parse_ia(option, &ia);
+        r = dhcp6_option_parse_ia(option, &ia, NULL);
         assert_se(r >= 0);
         assert_se(ia.addresses != NULL);
         dhcp6_lease_free_ia(&ia);
@@ -282,8 +282,8 @@ static int test_option_status(sd_event *e) {
         option = (DHCP6Option *)option4;
         assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len));
 
-        r = dhcp6_option_parse_ia(option, &pd);
-        assert_se(r == 0);
+        r = dhcp6_option_parse_ia(option, &pd, NULL);
+        assert_se(r >= 0);
         assert_se(pd.addresses != NULL);
         assert_se(memcmp(&pd.ia_pd.id, &option4[4], 4) == 0);
         assert_se(memcmp(&pd.ia_pd.lifetime_t1, &option4[8], 4) == 0);
@@ -294,8 +294,8 @@ static int test_option_status(sd_event *e) {
         option = (DHCP6Option *)option5;
         assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len));
 
-        r = dhcp6_option_parse_ia(option, &pd);
-        assert_se(r == 0);
+        r = dhcp6_option_parse_ia(option, &pd, NULL);
+        assert_se(r >= 0);
         assert_se(pd.addresses != NULL);
         dhcp6_lease_free_ia(&pd);
 
@@ -364,15 +364,15 @@ static int test_advertise_option(sd_event *e) {
         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
         DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
         size_t len = sizeof(msg_advertise) - sizeof(DHCP6Message), pos = 0;
-        be32_t val;
-        uint8_t preference = 255;
-        struct in6_addr addr;
         uint32_t lt_pref, lt_valid;
-        int r;
-        uint8_t *opt;
         bool opt_clientid = false;
         const struct in6_addr *addrs;
+        uint8_t preference = 255;
+        struct in6_addr addr;
         char **domains;
+        uint8_t *opt;
+        int r;
+        be32_t val;
 
         log_debug("/* %s */", __func__);
 
@@ -410,7 +410,7 @@ static int test_advertise_option(sd_event *e) {
                         val = htobe32(120);
                         assert_se(!memcmp(optval + 8, &val, sizeof(val)));
 
-                        assert_se(dhcp6_option_parse_ia(option, &lease->ia) >= 0);
+                        assert_se(dhcp6_option_parse_ia(option, &lease->ia, NULL) >= 0);
 
                         break;
 
@@ -563,12 +563,12 @@ static int test_client_send_reply(DHCP6Message *request) {
 
 static int test_client_verify_request(DHCP6Message *request, size_t len) {
         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
-        size_t pos = 0;
         bool found_clientid = false, found_iana = false, found_serverid = false,
                 found_elapsed_time = false, found_fqdn = false;
+        uint32_t lt_pref, lt_valid;
         struct in6_addr addr;
+        size_t pos = 0;
         be32_t val;
-        uint32_t lt_pref, lt_valid;
 
         log_debug("/* %s */", __func__);
 
@@ -606,7 +606,7 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) {
                         val = htobe32(120);
                         assert_se(!memcmp(optval + 8, &val, sizeof(val)));
 
-                        assert_se(!dhcp6_option_parse_ia(option, &lease->ia));
+                        assert_se(dhcp6_option_parse_ia(option, &lease->ia, NULL) >= 0);
 
                         break;