dhcp6: Parse IA PD and PD Prefix options

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.
This commit is contained in:
Patrik Flykt 2018-01-04 15:11:46 +02:00
parent 831ad96445
commit f8ad4dd45d
3 changed files with 144 additions and 11 deletions

View 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;

View file

@ -36,6 +36,7 @@ struct sd_dhcp6_lease {
bool rapid_commit;
DHCP6IA ia;
DHCP6IA pd;
DHCP6Address *addr_iter;

View 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 T1 %ds and T2 %ds as both were zero",
lt_t1, 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);
}
break;
default:
break;
}
error: