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:
parent
831ad96445
commit
f8ad4dd45d
|
@ -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;
|
||||
|
|
|
@ -36,6 +36,7 @@ struct sd_dhcp6_lease {
|
|||
bool rapid_commit;
|
||||
|
||||
DHCP6IA ia;
|
||||
DHCP6IA pd;
|
||||
|
||||
DHCP6Address *addr_iter;
|
||||
|
||||
|
|
|
@ -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, <_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, <_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:
|
||||
|
|
Loading…
Reference in a new issue