dhcp6: Parse IA PD and PD Prefix options
authorPatrik Flykt <patrik.flykt@linux.intel.com>
Thu, 4 Jan 2018 13:11:46 +0000 (15:11 +0200)
committerPatrik Flykt <patrik.flykt@linux.intel.com>
Thu, 4 Jan 2018 13:22:44 +0000 (15:22 +0200)
Parse IA PD options and the prefixes in one or more PD Prefix
options. As the PD option contains identical data as the IA NA
option, re-use the same general data structures and sub-option
parsing logic. Similar to IA NA addresses, PD and associated
prefixes are stored in the address list of the IA PD lease.

An IA sub-option Status code will affect the IA NA and IA PD
option in question and cause those options to be ignored. A
Status code option in an IA Address or IA PD Prefix option
affects only that IA Address or Prefix.

src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/dhcp6-lease-internal.h
src/libsystemd-network/dhcp6-option.c

index 8691c2ca028442c9d016ddf603aef55070c6e2c0..b1d7d63932b08cf9c0c3caa0743e2d567863aa41 100644 (file)
@@ -43,12 +43,23 @@ struct iaaddr {
         be32_t lifetime_valid;
 } _packed_;
 
+/* Prefix Delegation Prefix option */
+struct iapdprefix {
+        be32_t lifetime_preferred;
+        be32_t lifetime_valid;
+        uint8_t prefixlen;
+        struct in6_addr address;
+} _packed_;
+
 typedef struct DHCP6Address DHCP6Address;
 
 struct DHCP6Address {
         LIST_FIELDS(DHCP6Address, addresses);
 
-        struct iaaddr iaaddr;
+        union {
+                struct iaaddr iaaddr;
+                struct iapdprefix iapdprefix;
+        };
 };
 
 /* Non-temporary Address option */
@@ -58,6 +69,13 @@ struct ia_na {
         be32_t lifetime_t2;
 } _packed_;
 
+/* Prefix Delegation option */
+struct ia_pd {
+        be32_t id;
+        be32_t lifetime_t1;
+        be32_t lifetime_t2;
+} _packed_;
+
 /* Temporary Address option */
 struct ia_ta {
         be32_t id;
@@ -67,6 +85,7 @@ struct DHCP6IA {
         uint16_t type;
         union {
                 struct ia_na ia_na;
+                struct ia_pd ia_pd;
                 struct ia_ta ia_ta;
         };
         sd_event_source *timeout_t1;
index a3f00d4a5d864a086ac5e79e4af80b729f5f6dc4..ba9e9945dccd07958645e2baeb89d01f0ac75de6 100644 (file)
@@ -36,6 +36,7 @@ struct sd_dhcp6_lease {
         bool rapid_commit;
 
         DHCP6IA ia;
+        DHCP6IA pd;
 
         DHCP6Address *addr_iter;
 
index c196078a716dc9391760c7dcce2ee60683dada80..c894ab4b3433e63dfa0bd2eeb0bee15642eda611 100644 (file)
@@ -46,8 +46,15 @@ typedef struct DHCP6AddressOption {
         uint8_t options[];
 } _packed_ DHCP6AddressOption;
 
-#define DHCP6_OPTION_IA_NA_LEN                  12
-#define DHCP6_OPTION_IA_TA_LEN                  4
+typedef struct DHCP6PDPrefixOption {
+        struct DHCP6Option option;
+        struct iapdprefix iapdprefix;
+        uint8_t options[];
+} _packed_ DHCP6PDPrefixOption;
+
+#define DHCP6_OPTION_IA_NA_LEN                  (sizeof(struct ia_na))
+#define DHCP6_OPTION_IA_PD_LEN                  (sizeof(struct ia_pd))
+#define DHCP6_OPTION_IA_TA_LEN                  (sizeof(struct ia_ta))
 
 static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
                              size_t optlen) {
@@ -269,6 +276,46 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
         return 0;
 }
 
+static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
+                                       uint32_t *lifetime_valid) {
+        DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option;
+        DHCP6Address *prefix;
+        uint32_t lt_valid, lt_pref;
+        int r;
+
+        if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*pdprefix_option))
+                return -ENOBUFS;
+
+        lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid);
+        lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
+
+        if (lt_valid == 0 || lt_pref > lt_valid) {
+                log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d",
+                                 lt_pref, lt_valid);
+
+                return 0;
+        }
+
+        if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*pdprefix_option)) {
+                r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options);
+                if (r != 0)
+                        return r < 0 ? r: 0;
+        }
+
+        prefix = new0(DHCP6Address, 1);
+        if (!prefix)
+                return -ENOMEM;
+
+        LIST_INIT(addresses, prefix);
+        memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix));
+
+        LIST_PREPEND(addresses, ia->addresses, prefix);
+
+        *lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
+
+        return 0;
+}
+
 int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
         uint16_t iatype, optlen;
         size_t i, len;
@@ -298,7 +345,29 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
                 lt_t2 = be32toh(ia->ia_na.lifetime_t2);
 
                 if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
-                        log_dhcp6_client(client, "IA T1 %ds > T2 %ds",
+                        log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds",
+                                         lt_t1, lt_t2);
+                        r = -EINVAL;
+                        goto error;
+                }
+
+                break;
+
+        case SD_DHCP6_OPTION_IA_PD:
+
+                if (len < sizeof(ia->ia_pd)) {
+                        r = -ENOBUFS;
+                        goto error;
+                }
+
+                iaaddr_offset = sizeof(ia->ia_pd);
+                memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));
+
+                lt_t1 = be32toh(ia->ia_pd.lifetime_t1);
+                lt_t2 = be32toh(ia->ia_pd.lifetime_t2);
+
+                if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
+                        log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds",
                                          lt_t1, lt_t2);
                         r = -EINVAL;
                         goto error;
@@ -339,6 +408,12 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
                 switch (opt) {
                 case SD_DHCP6_OPTION_IAADDR:
 
+                        if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
+                                log_dhcp6_client(client, "IA Address option not in IA NA or TA option");
+                                r = -EINVAL;
+                                goto error;
+                        }
+
                         r = dhcp6_option_parse_address(option, ia, &lt_valid);
                         if (r < 0)
                                 goto error;
@@ -348,6 +423,23 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
 
                         break;
 
+                case SD_DHCP6_OPTION_IA_PD_PREFIX:
+
+                        if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) {
+                                log_dhcp6_client(client, "IA PD Prefix option not in IA PD option");
+                                r = -EINVAL;
+                                goto error;
+                        }
+
+                        r = dhcp6_option_parse_pdprefix(option, ia, &lt_valid);
+                        if (r < 0)
+                                goto error;
+
+                        if (lt_valid < lt_min)
+                                lt_min = lt_valid;
+
+                        break;
+
                 case SD_DHCP6_OPTION_STATUS_CODE:
 
                         status = dhcp6_option_parse_status(option);
@@ -371,14 +463,35 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
                 i += sizeof(*option) + optlen;
         }
 
-        if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) {
-                lt_t1 = lt_min / 2;
-                lt_t2 = lt_min / 10 * 8;
-                ia->ia_na.lifetime_t1 = htobe32(lt_t1);
-                ia->ia_na.lifetime_t2 = htobe32(lt_t2);
+        switch(iatype) {
+        case SD_DHCP6_OPTION_IA_NA:
+                if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) {
+                        lt_t1 = lt_min / 2;
+                        lt_t2 = lt_min / 10 * 8;
+                        ia->ia_na.lifetime_t1 = htobe32(lt_t1);
+                        ia->ia_na.lifetime_t2 = htobe32(lt_t2);
+
+                        log_dhcp6_client(client, "Computed IA NA T1 %ds and T2 %ds as both were zero",
+                                         lt_t1, lt_t2);
+                }
+
+                break;
+
+        case SD_DHCP6_OPTION_IA_PD:
+                if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) {
+                        lt_t1 = lt_min / 2;
+                        lt_t2 = lt_min / 10 * 8;
+                        ia->ia_pd.lifetime_t1 = htobe32(lt_t1);
+                        ia->ia_pd.lifetime_t2 = htobe32(lt_t2);
+
+                        log_dhcp6_client(client, "Computed IA PD T1 %ds and T2 %ds as both were zero",
+                                         lt_t1, lt_t2);
+                }
 
-                log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero",
-                                 lt_t1, lt_t2);
+                break;
+
+        default:
+                break;
         }
 
 error: