]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-dhcp6-client: ignore IAs whose IAID do not match client's IAID
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 22 Sep 2021 07:35:56 +0000 (16:35 +0900)
committerLuca Boccassi <luca.boccassi@gmail.com>
Wed, 22 Sep 2021 20:19:54 +0000 (21:19 +0100)
But do not refuse whole message.

Fixes #20803.

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 f0f814957f2b661b88ccb08c4e6379ff481b92e2..35cafc96ec9cd93f66b33100f8b0cde2a530879e 100644 (file)
@@ -104,7 +104,7 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash
 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(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code);
+int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, be32_t iaid, 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);
 int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen,
index 97ef03a2d2469f02a0cab49161df97faae699f15..34d7e997dd51c2e0b9533d187db58fca43f0ff63 100644 (file)
@@ -509,7 +509,13 @@ static int dhcp6_option_parse_pdprefix(sd_dhcp6_client *client, DHCP6Option *opt
         return 0;
 }
 
-int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code) {
+int dhcp6_option_parse_ia(
+                sd_dhcp6_client *client,
+                DHCP6Option *iaoption,
+                be32_t iaid,
+                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 iaaddr_offset;
@@ -529,6 +535,14 @@ int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6I
                 if (len < DHCP6_OPTION_IA_NA_LEN)
                         return -ENOBUFS;
 
+                /* According to RFC8415, IAs which do not match the client's IAID should be ignored,
+                 * but not necessary to ignore or refuse the whole message. */
+                if (((const struct ia_na*) iaoption->data)->id != iaid)
+                        /* ENOANO indicates the option should be ignored. */
+                        return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
+                                                      "Received an IA_NA option with a different IAID "
+                                                      "from the one chosen by the client, ignoring.");
+
                 iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
                 memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));
 
@@ -547,6 +561,14 @@ int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6I
                 if (len < sizeof(ia->ia_pd))
                         return -ENOBUFS;
 
+                /* According to RFC8415, IAs which do not match the client's IAID should be ignored,
+                 * but not necessary to ignore or refuse the whole message. */
+                if (((const struct ia_pd*) iaoption->data)->id != iaid)
+                        /* ENOANO indicates the option should be ignored. */
+                        return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
+                                                      "Received an IA_PD option with a different IAID "
+                                                      "from the one chosen by the client, ignoring.");
+
                 iaaddr_offset = sizeof(ia->ia_pd);
                 memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));
 
@@ -564,13 +586,21 @@ int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6I
                 if (len < DHCP6_OPTION_IA_TA_LEN)
                         return -ENOBUFS;
 
+                /* According to RFC8415, IAs which do not match the client's IAID should be ignored,
+                 * but not necessary to ignore or refuse the whole message. */
+                if (((const struct ia_ta*) iaoption->data)->id != iaid)
+                        /* ENOANO indicates the option should be ignored. */
+                        return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
+                                                      "Received an IA_TA option with a different IAID "
+                                                      "from the one chosen by the client, ignoring.");
+
                 iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
-                memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta));
+                memcpy(&ia->ia_ta, iaoption->data, sizeof(ia->ia_ta));
 
                 break;
 
         default:
-                return -ENOMSG;
+                return -EINVAL;
         }
 
         ia->type = iatype;
index d4ab9df992b5dadaf558839c8bc599d56c54e61d..adc65cd70e4299a547ef4f08c479d22084216bed 100644 (file)
@@ -1118,7 +1118,6 @@ static int client_parse_message(
         while (pos < len) {
                 DHCP6Option *option = (DHCP6Option *) &message->options[pos];
                 uint16_t optcode, optlen;
-                be32_t iaid_lease;
                 int  status;
                 uint8_t *optval;
 
@@ -1187,8 +1186,8 @@ static int client_parse_message(
                                 break;
                         }
 
-                        r = dhcp6_option_parse_ia(client, option, &lease->ia, &ia_na_status);
-                        if (r < 0 && r != -ENOMSG)
+                        r = dhcp6_option_parse_ia(client, option, client->ia_pd.ia_na.id, &lease->ia, &ia_na_status);
+                        if (r < 0 && r != -ENOANO)
                                 return r;
 
                         if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) {
@@ -1196,14 +1195,6 @@ static int client_parse_message(
                                 continue;
                         }
 
-                        r = dhcp6_lease_get_iaid(lease, &iaid_lease);
-                        if (r < 0)
-                                return r;
-
-                        if (client->ia_na.ia_na.id != iaid_lease)
-                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has wrong IAID for IA NA",
-                                                              dhcp6_message_type_to_string(message->type));
-
                         if (lease->ia.addresses) {
                                 lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
                                 lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2));
@@ -1217,8 +1208,8 @@ static int client_parse_message(
                                 break;
                         }
 
-                        r = dhcp6_option_parse_ia(client, option, &lease->pd, &ia_pd_status);
-                        if (r < 0 && r != -ENOMSG)
+                        r = dhcp6_option_parse_ia(client, option, client->ia_pd.ia_pd.id, &lease->pd, &ia_pd_status);
+                        if (r < 0 && r != -ENOANO)
                                 return r;
 
                         if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) {
@@ -1226,14 +1217,6 @@ static int client_parse_message(
                                 continue;
                         }
 
-                        r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
-                        if (r < 0)
-                                return r;
-
-                        if (client->ia_pd.ia_pd.id != iaid_lease)
-                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has wrong IAID for IA PD",
-                                                              dhcp6_message_type_to_string(message->type));
-
                         if (lease->pd.addresses) {
                                 lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
                                 lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
index 5511350489deed5a5115ac8d90d0e7f4be010086..22ab25b2b0614badea1fe4ac776b8e010f91f0e7 100644 (file)
@@ -287,25 +287,31 @@ static int test_option_status(sd_event *e) {
         };
         DHCP6Option *option;
         DHCP6IA ia, pd;
+        be32_t iaid;
         int r = 0;
 
         log_debug("/* %s */", __func__);
 
+        memcpy(&iaid, option1 + 4, sizeof(iaid));
+
         zero(ia);
         option = (DHCP6Option *)option1;
         assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len));
 
-        r = dhcp6_option_parse_ia(NULL, option, &ia, NULL);
+        r = dhcp6_option_parse_ia(NULL, option, 0, &ia, NULL);
+        assert_se(r == -ENOANO);
+
+        r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL);
         assert_se(r == 0);
         assert_se(ia.addresses == NULL);
 
         option->len = htobe16(17);
-        r = dhcp6_option_parse_ia(NULL, option, &ia, NULL);
+        r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL);
         assert_se(r == -ENOBUFS);
         assert_se(ia.addresses == NULL);
 
         option->len = htobe16(sizeof(DHCP6Option));
-        r = dhcp6_option_parse_ia(NULL, option, &ia, NULL);
+        r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL);
         assert_se(r == -ENOBUFS);
         assert_se(ia.addresses == NULL);
 
@@ -313,7 +319,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(NULL, option, &ia, NULL);
+        r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL);
         assert_se(r >= 0);
         assert_se(ia.addresses == NULL);
 
@@ -321,7 +327,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(NULL, option, &ia, NULL);
+        r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL);
         assert_se(r >= 0);
         assert_se(ia.addresses != NULL);
         dhcp6_lease_free_ia(&ia);
@@ -330,7 +336,7 @@ 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(NULL, option, &pd, NULL);
+        r = dhcp6_option_parse_ia(NULL, option, iaid, &pd, NULL);
         assert_se(r >= 0);
         assert_se(pd.addresses != NULL);
         assert_se(memcmp(&pd.ia_pd.id, &option4[4], 4) == 0);
@@ -342,7 +348,7 @@ 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(NULL, option, &pd, NULL);
+        r = dhcp6_option_parse_ia(NULL, option, iaid, &pd, NULL);
         assert_se(r >= 0);
         assert_se(pd.addresses != NULL);
         dhcp6_lease_free_ia(&pd);
@@ -447,13 +453,14 @@ static int test_advertise_option(sd_event *e) {
                         opt_clientid = true;
                         break;
 
-                case SD_DHCP6_OPTION_IA_NA:
+                case SD_DHCP6_OPTION_IA_NA: {
+                        be32_t iaid = htobe32(0x0ecfa37d);
+
                         assert_se(optlen == 94);
                         assert_se(optval == &msg_advertise[26]);
                         assert_se(!memcmp(optval, &msg_advertise[26], optlen));
 
-                        val = htobe32(0x0ecfa37d);
-                        assert_se(!memcmp(optval, &val, sizeof(val)));
+                        assert_se(!memcmp(optval, &iaid, sizeof(val)));
 
                         val = htobe32(80);
                         assert_se(!memcmp(optval + 4, &val, sizeof(val)));
@@ -461,10 +468,10 @@ 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(NULL, option, &lease->ia, NULL) >= 0);
+                        assert_se(dhcp6_option_parse_ia(NULL, option, iaid, &lease->ia, NULL) >= 0);
 
                         break;
-
+                }
                 case SD_DHCP6_OPTION_SERVERID:
                         assert_se(optlen == 14);
                         assert_se(optval == &msg_advertise[179]);
@@ -596,6 +603,8 @@ static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
 static int test_client_send_reply(DHCP6Message *request) {
         DHCP6Message reply;
 
+        log_debug("/* %s */", __func__);
+
         reply.transaction_id = request->transaction_id;
         reply.type = DHCP6_REPLY;
 
@@ -656,7 +665,7 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) {
                         assert_se(!memcmp(optval + 8, &val, sizeof(val)));
 
                         /* Then, this should refuse all addresses. */
-                        assert_se(dhcp6_option_parse_ia(NULL, option, &lease->ia, NULL) >= 0);
+                        assert_se(dhcp6_option_parse_ia(NULL, option, test_iaid, &lease->ia, NULL) >= 0);
 
                         break;
 
@@ -702,6 +711,8 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) {
 static int test_client_send_advertise(DHCP6Message *solicit) {
         DHCP6Message advertise;
 
+        log_debug("/* %s */", __func__);
+
         advertise.transaction_id = solicit->transaction_id;
         advertise.type = DHCP6_ADVERTISE;
 
@@ -891,6 +902,8 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
                 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
         DHCP6Message *message;
 
+        log_debug("/* %s */", __func__);
+
         assert_se(s == test_dhcp_fd[0]);
         assert_se(server_address);
         assert_se(packet);