mirror of
https://github.com/systemd/systemd
synced 2024-10-07 08:40:44 +00:00
Merge pull request #7582 from pfl/dhcp6_prefix_delegation
DHCPv6 prefix delegation
This commit is contained in:
commit
38edb7674b
|
@ -667,8 +667,16 @@
|
|||
<varlistentry>
|
||||
<term><varname>IPv6PrefixDelegation=</varname></term>
|
||||
<listitem><para>Whether to enable or disable Router Advertisement sending on a link.
|
||||
Defaults to <literal>false</literal>. See the <literal>[IPv6PrefixDelegation]</literal>
|
||||
and the <literal>[IPv6Prefix]</literal> sections for configuration options.
|
||||
Allowed values are <literal>static</literal> which distributes prefixes as defined in
|
||||
the <literal>[IPv6PrefixDelegation]</literal> and any <literal>[IPv6Prefix]</literal>
|
||||
sections, <literal>dhcpv6</literal> which requests prefixes using a DHCPv6 client
|
||||
configured for another link and any values configured in the
|
||||
<literal>[IPv6PrefixDelegation]</literal> section while ignoring all static prefix
|
||||
configuration sections, <literal>yes</literal> which uses both static configuration
|
||||
and DHCPv6, and <literal>false</literal> which turns off IPv6 prefix delegation
|
||||
altogether. Defaults to <literal>false</literal>. See the
|
||||
<literal>[IPv6PrefixDelegation]</literal> and the <literal>[IPv6Prefix]</literal>
|
||||
sections for more configuration options.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
|
|
|
@ -29,25 +29,65 @@
|
|||
#include "macro.h"
|
||||
#include "sparse-endian.h"
|
||||
|
||||
/* Common option header */
|
||||
typedef struct DHCP6Option {
|
||||
be16_t code;
|
||||
be16_t len;
|
||||
uint8_t data[];
|
||||
} _packed_ DHCP6Option;
|
||||
|
||||
/* Address option */
|
||||
struct iaaddr {
|
||||
struct in6_addr address;
|
||||
be32_t lifetime_preferred;
|
||||
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 {
|
||||
struct in6_addr address;
|
||||
be32_t lifetime_preferred;
|
||||
be32_t lifetime_valid;
|
||||
} iaaddr _packed_;
|
||||
union {
|
||||
struct iaaddr iaaddr;
|
||||
struct iapdprefix iapdprefix;
|
||||
};
|
||||
};
|
||||
|
||||
/* Non-temporary Address option */
|
||||
struct ia_na {
|
||||
be32_t id;
|
||||
be32_t lifetime_t1;
|
||||
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;
|
||||
} _packed_;
|
||||
|
||||
struct DHCP6IA {
|
||||
uint16_t type;
|
||||
struct {
|
||||
be32_t id;
|
||||
be32_t lifetime_t1;
|
||||
be32_t lifetime_t2;
|
||||
} _packed_;
|
||||
union {
|
||||
struct ia_na ia_na;
|
||||
struct ia_pd ia_pd;
|
||||
struct ia_ta ia_ta;
|
||||
};
|
||||
sd_event_source *timeout_t1;
|
||||
sd_event_source *timeout_t2;
|
||||
|
||||
|
@ -62,11 +102,12 @@ typedef struct DHCP6IA DHCP6IA;
|
|||
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
|
||||
size_t optlen, const void *optval);
|
||||
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia);
|
||||
int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd);
|
||||
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
|
||||
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
|
||||
size_t *optlen, uint8_t **optvalue);
|
||||
int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
|
||||
DHCP6IA *ia);
|
||||
int dhcp6_option_parse_status(DHCP6Option *option);
|
||||
int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia);
|
||||
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
|
||||
struct in6_addr **addrs, size_t count,
|
||||
size_t *allocated);
|
||||
|
|
|
@ -36,8 +36,10 @@ struct sd_dhcp6_lease {
|
|||
bool rapid_commit;
|
||||
|
||||
DHCP6IA ia;
|
||||
DHCP6IA pd;
|
||||
|
||||
DHCP6Address *addr_iter;
|
||||
DHCP6Address *prefix_iter;
|
||||
|
||||
struct in6_addr *dns;
|
||||
size_t dns_count;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "alloc-util.h"
|
||||
#include "dhcp6-internal.h"
|
||||
#include "dhcp6-lease-internal.h"
|
||||
#include "dhcp6-protocol.h"
|
||||
#include "dns-domain.h"
|
||||
#include "sparse-endian.h"
|
||||
|
@ -33,14 +34,27 @@
|
|||
#include "unaligned.h"
|
||||
#include "util.h"
|
||||
|
||||
#define DHCP6_OPTION_IA_NA_LEN 12
|
||||
#define DHCP6_OPTION_IA_TA_LEN 4
|
||||
typedef struct DHCP6StatusOption {
|
||||
struct DHCP6Option option;
|
||||
be16_t status;
|
||||
char msg[];
|
||||
} _packed_ DHCP6StatusOption;
|
||||
|
||||
typedef struct DHCP6Option {
|
||||
be16_t code;
|
||||
be16_t len;
|
||||
uint8_t data[];
|
||||
} _packed_ DHCP6Option;
|
||||
typedef struct DHCP6AddressOption {
|
||||
struct DHCP6Option option;
|
||||
struct iaaddr iaaddr;
|
||||
uint8_t options[];
|
||||
} _packed_ DHCP6AddressOption;
|
||||
|
||||
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) {
|
||||
|
@ -82,6 +96,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
|
|||
|
||||
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
|
||||
uint16_t len;
|
||||
be32_t *iaid;
|
||||
uint8_t *ia_hdr;
|
||||
size_t ia_buflen, ia_addrlen = 0;
|
||||
DHCP6Address *addr;
|
||||
|
@ -92,10 +107,12 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
|
|||
switch (ia->type) {
|
||||
case SD_DHCP6_OPTION_IA_NA:
|
||||
len = DHCP6_OPTION_IA_NA_LEN;
|
||||
iaid = &ia->ia_na.id;
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_TA:
|
||||
len = DHCP6_OPTION_IA_TA_LEN;
|
||||
iaid = &ia->ia_ta.id;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -111,7 +128,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
|
|||
*buf += sizeof(DHCP6Option);
|
||||
*buflen -= sizeof(DHCP6Option);
|
||||
|
||||
memcpy(*buf, &ia->id, len);
|
||||
memcpy(*buf, iaid, len);
|
||||
|
||||
*buf += len;
|
||||
*buflen -= len;
|
||||
|
@ -164,6 +181,42 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
|
|||
return r;
|
||||
}
|
||||
|
||||
int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd) {
|
||||
DHCP6Option *option = (DHCP6Option *)buf;
|
||||
size_t i = sizeof(*option) + sizeof(pd->ia_pd);
|
||||
DHCP6Address *prefix;
|
||||
|
||||
assert_return(buf, -EINVAL);
|
||||
assert_return(pd, -EINVAL);
|
||||
assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
|
||||
|
||||
if (len < i)
|
||||
return -ENOBUFS;
|
||||
|
||||
option->code = htobe16(SD_DHCP6_OPTION_IA_PD);
|
||||
|
||||
memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd));
|
||||
|
||||
LIST_FOREACH(addresses, prefix, pd->addresses) {
|
||||
DHCP6PDPrefixOption *prefix_opt;
|
||||
|
||||
if (len < i + sizeof(*prefix_opt))
|
||||
return -ENOBUFS;
|
||||
|
||||
prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
|
||||
prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
|
||||
prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
|
||||
|
||||
memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix,
|
||||
sizeof(struct iapdprefix));
|
||||
|
||||
i += sizeof(*prefix_opt);
|
||||
}
|
||||
|
||||
option->len = htobe16(i - sizeof(*option));
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
|
||||
DHCP6Option *option = (DHCP6Option*) *buf;
|
||||
|
@ -210,35 +263,147 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
|
||||
DHCP6IA *ia) {
|
||||
int r;
|
||||
uint16_t opt, status;
|
||||
size_t optlen;
|
||||
size_t iaaddr_offset;
|
||||
int dhcp6_option_parse_status(DHCP6Option *option) {
|
||||
DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
|
||||
|
||||
if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*statusopt))
|
||||
return -ENOBUFS;
|
||||
|
||||
return be16toh(statusopt->status);
|
||||
}
|
||||
|
||||
static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
|
||||
uint32_t *lifetime_valid) {
|
||||
DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option;
|
||||
DHCP6Address *addr;
|
||||
uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0;
|
||||
uint32_t lt_valid, lt_pref;
|
||||
int r;
|
||||
|
||||
if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*addr_option))
|
||||
return -ENOBUFS;
|
||||
|
||||
lt_valid = be32toh(addr_option->iaaddr.lifetime_valid);
|
||||
lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
|
||||
|
||||
if (lt_valid == 0 || lt_pref > lt_valid) {
|
||||
log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d",
|
||||
lt_pref, lt_valid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*addr_option)) {
|
||||
r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options);
|
||||
if (r != 0)
|
||||
return r < 0 ? r: 0;
|
||||
}
|
||||
|
||||
addr = new0(DHCP6Address, 1);
|
||||
if (!addr)
|
||||
return -ENOMEM;
|
||||
|
||||
LIST_INIT(addresses, addr);
|
||||
memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr));
|
||||
|
||||
LIST_PREPEND(addresses, ia->addresses, addr);
|
||||
|
||||
*lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
|
||||
|
||||
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;
|
||||
int r = 0, status;
|
||||
uint16_t opt;
|
||||
size_t iaaddr_offset;
|
||||
uint32_t lt_t1, lt_t2, lt_valid, lt_min = ~0;
|
||||
|
||||
assert_return(ia, -EINVAL);
|
||||
assert_return(!ia->addresses, -EINVAL);
|
||||
|
||||
iatype = be16toh(iaoption->code);
|
||||
len = be16toh(iaoption->len);
|
||||
|
||||
switch (iatype) {
|
||||
case SD_DHCP6_OPTION_IA_NA:
|
||||
|
||||
if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) +
|
||||
sizeof(addr->iaaddr)) {
|
||||
if (len < DHCP6_OPTION_IA_NA_LEN) {
|
||||
r = -ENOBUFS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
|
||||
memcpy(&ia->id, *buf, iaaddr_offset);
|
||||
memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));
|
||||
|
||||
lt_t1 = be32toh(ia->lifetime_t1);
|
||||
lt_t2 = be32toh(ia->lifetime_t2);
|
||||
lt_t1 = be32toh(ia->ia_na.lifetime_t1);
|
||||
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;
|
||||
|
@ -247,17 +412,13 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
|
|||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_TA:
|
||||
if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) +
|
||||
sizeof(addr->iaaddr)) {
|
||||
if (len < DHCP6_OPTION_IA_TA_LEN) {
|
||||
r = -ENOBUFS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
|
||||
memcpy(&ia->id, *buf, iaaddr_offset);
|
||||
|
||||
ia->lifetime_t1 = 0;
|
||||
ia->lifetime_t2 = 0;
|
||||
memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta));
|
||||
|
||||
break;
|
||||
|
||||
|
@ -267,48 +428,63 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
|
|||
}
|
||||
|
||||
ia->type = iatype;
|
||||
i = iaaddr_offset;
|
||||
|
||||
*buflen -= iaaddr_offset;
|
||||
*buf += iaaddr_offset;
|
||||
while (i < len) {
|
||||
DHCP6Option *option = (DHCP6Option *)&iaoption->data[i];
|
||||
|
||||
while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) {
|
||||
if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) {
|
||||
r = -ENOBUFS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
opt = be16toh(option->code);
|
||||
optlen = be16toh(option->len);
|
||||
|
||||
switch (opt) {
|
||||
case SD_DHCP6_OPTION_IAADDR:
|
||||
|
||||
addr = new0(DHCP6Address, 1);
|
||||
if (!addr) {
|
||||
r = -ENOMEM;
|
||||
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;
|
||||
}
|
||||
|
||||
LIST_INIT(addresses, addr);
|
||||
r = dhcp6_option_parse_address(option, ia, <_valid);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr));
|
||||
if (lt_valid < lt_min)
|
||||
lt_min = lt_valid;
|
||||
|
||||
lt_valid = be32toh(addr->iaaddr.lifetime_valid);
|
||||
lt_pref = be32toh(addr->iaaddr.lifetime_valid);
|
||||
break;
|
||||
|
||||
if (!lt_valid || lt_pref > lt_valid) {
|
||||
log_dhcp6_client(client, "IA preferred %ds > valid %ds",
|
||||
lt_pref, lt_valid);
|
||||
free(addr);
|
||||
} else {
|
||||
LIST_PREPEND(addresses, ia->addresses, addr);
|
||||
if (lt_valid < lt_min)
|
||||
lt_min = lt_valid;
|
||||
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:
|
||||
if (optlen < sizeof(status))
|
||||
break;
|
||||
|
||||
status = (*buf)[0] << 8 | (*buf)[1];
|
||||
status = dhcp6_option_parse_status(option);
|
||||
if (status) {
|
||||
log_dhcp6_client(client, "IA status %d",
|
||||
status);
|
||||
|
||||
dhcp6_lease_free_ia(ia);
|
||||
|
||||
r = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
@ -320,30 +496,41 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
|
|||
break;
|
||||
}
|
||||
|
||||
*buflen -= optlen;
|
||||
*buf += optlen;
|
||||
i += sizeof(*option) + optlen;
|
||||
}
|
||||
|
||||
if (r == -ENOMSG)
|
||||
r = 0;
|
||||
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);
|
||||
|
||||
if (!ia->lifetime_t1 && !ia->lifetime_t2) {
|
||||
lt_t1 = lt_min / 2;
|
||||
lt_t2 = lt_min / 10 * 8;
|
||||
ia->lifetime_t1 = htobe32(lt_t1);
|
||||
ia->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);
|
||||
}
|
||||
|
||||
log_dhcp6_client(client, "Computed IA 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;
|
||||
}
|
||||
|
||||
if (*buflen)
|
||||
r = -ENOMSG;
|
||||
|
||||
error:
|
||||
*buf += *buflen;
|
||||
*buflen = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ struct DHCP6Message {
|
|||
} _packed_;
|
||||
be32_t transaction_id;
|
||||
};
|
||||
uint8_t options[];
|
||||
} _packed_;
|
||||
|
||||
typedef struct DHCP6Message DHCP6Message;
|
||||
|
|
|
@ -93,6 +93,9 @@ struct sd_radv_prefix {
|
|||
} _packed_ opt;
|
||||
|
||||
LIST_FIELDS(struct sd_radv_prefix, prefix);
|
||||
|
||||
usec_t valid_until;
|
||||
usec_t preferred_until;
|
||||
};
|
||||
|
||||
#define log_radv_full(level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "RADV: " fmt, ##__VA_ARGS__)
|
||||
|
|
|
@ -54,6 +54,8 @@ struct sd_dhcp6_client {
|
|||
size_t mac_addr_len;
|
||||
uint16_t arp_type;
|
||||
DHCP6IA ia_na;
|
||||
DHCP6IA ia_pd;
|
||||
bool prefix_delegation;
|
||||
be32_t transaction_id;
|
||||
usec_t transaction_start;
|
||||
struct sd_dhcp6_lease *lease;
|
||||
|
@ -230,7 +232,8 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
|
|||
assert_return(client, -EINVAL);
|
||||
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
||||
|
||||
client->ia_na.id = htobe32(iaid);
|
||||
client->ia_na.ia_na.id = htobe32(iaid);
|
||||
client->ia_pd.ia_pd.id = htobe32(iaid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -298,6 +301,14 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, bool delegation) {
|
||||
assert_return(client, -EINVAL);
|
||||
|
||||
client->prefix_delegation = delegation;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
|
||||
assert_return(client, -EINVAL);
|
||||
|
||||
|
@ -411,6 +422,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
|
|||
return r;
|
||||
}
|
||||
|
||||
if (client->prefix_delegation) {
|
||||
r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
opt += r;
|
||||
optlen -= r;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DHCP6_STATE_REQUEST:
|
||||
|
@ -437,6 +457,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
|
|||
return r;
|
||||
}
|
||||
|
||||
if (client->prefix_delegation) {
|
||||
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
opt += r;
|
||||
optlen -= r;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DHCP6_STATE_REBIND:
|
||||
|
@ -452,6 +481,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
|
|||
return r;
|
||||
}
|
||||
|
||||
if (client->prefix_delegation) {
|
||||
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
opt += r;
|
||||
optlen -= r;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DHCP6_STATE_STOPPED:
|
||||
|
@ -707,16 +745,20 @@ error:
|
|||
|
||||
static int client_ensure_iaid(sd_dhcp6_client *client) {
|
||||
int r;
|
||||
be32_t iaid;
|
||||
|
||||
assert(client);
|
||||
|
||||
if (client->ia_na.id)
|
||||
if (client->ia_na.ia_na.id)
|
||||
return 0;
|
||||
|
||||
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
|
||||
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &iaid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
client->ia_na.ia_na.id = iaid;
|
||||
client->ia_pd.ia_pd.id = iaid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -725,23 +767,34 @@ static int client_parse_message(
|
|||
DHCP6Message *message,
|
||||
size_t len,
|
||||
sd_dhcp6_lease *lease) {
|
||||
size_t pos = 0;
|
||||
int r;
|
||||
uint8_t *optval, *option, *id = NULL;
|
||||
uint16_t optcode, status;
|
||||
size_t optlen, id_len;
|
||||
bool clientid = false;
|
||||
be32_t iaid_lease;
|
||||
uint8_t *id = NULL;
|
||||
size_t id_len;
|
||||
uint32_t lt_t1 = ~0, lt_t2 = ~0;
|
||||
|
||||
assert(client);
|
||||
assert(message);
|
||||
assert(len >= sizeof(DHCP6Message));
|
||||
assert(lease);
|
||||
|
||||
option = (uint8_t *)message + sizeof(DHCP6Message);
|
||||
len -= sizeof(DHCP6Message);
|
||||
|
||||
while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
|
||||
&optval)) >= 0) {
|
||||
while (pos < len) {
|
||||
DHCP6Option *option = (DHCP6Option *)&message->options[pos];
|
||||
uint16_t optcode, optlen;
|
||||
int status;
|
||||
uint8_t *optval;
|
||||
be32_t iaid_lease;
|
||||
|
||||
if (len < sizeof(DHCP6Option) || len < sizeof(DHCP6Option) + be16toh(option->len))
|
||||
return -ENOBUFS;
|
||||
|
||||
optcode = be16toh(option->code);
|
||||
optlen = be16toh(option->len);
|
||||
optval = option->data;
|
||||
|
||||
switch (optcode) {
|
||||
case SD_DHCP6_OPTION_CLIENTID:
|
||||
if (clientid) {
|
||||
|
@ -779,21 +832,21 @@ static int client_parse_message(
|
|||
if (optlen != 1)
|
||||
return -EINVAL;
|
||||
|
||||
r = dhcp6_lease_set_preference(lease, *optval);
|
||||
r = dhcp6_lease_set_preference(lease, optval[0]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_STATUS_CODE:
|
||||
if (optlen < 2)
|
||||
return -EINVAL;
|
||||
|
||||
status = optval[0] << 8 | optval[1];
|
||||
status = dhcp6_option_parse_status(option);
|
||||
if (status) {
|
||||
log_dhcp6_client(client, "%s Status %s",
|
||||
dhcp6_message_type_to_string(message->type),
|
||||
dhcp6_message_status_to_string(status));
|
||||
dhcp6_lease_free_ia(&lease->ia);
|
||||
dhcp6_lease_free_ia(&lease->pd);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -806,8 +859,7 @@ static int client_parse_message(
|
|||
break;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
|
||||
&lease->ia);
|
||||
r = dhcp6_option_parse_ia(option, &lease->ia);
|
||||
if (r < 0 && r != -ENOMSG)
|
||||
return r;
|
||||
|
||||
|
@ -815,12 +867,45 @@ static int client_parse_message(
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (client->ia_na.id != iaid_lease) {
|
||||
log_dhcp6_client(client, "%s has wrong IAID",
|
||||
if (client->ia_na.ia_na.id != iaid_lease) {
|
||||
log_dhcp6_client(client, "%s has wrong IAID for IA NA",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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_t1));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_PD:
|
||||
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
||||
log_dhcp6_client(client, "Information request ignoring IA PD option");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia(option, &lease->pd);
|
||||
if (r < 0 && r != -ENOMSG)
|
||||
return r;
|
||||
|
||||
r = dhcp6_lease_get_iaid(lease, &iaid_lease);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (client->ia_pd.ia_pd.id != iaid_lease) {
|
||||
log_dhcp6_client(client, "%s has wrong IAID for IA PD",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_RAPID_COMMIT:
|
||||
|
@ -859,11 +944,9 @@ static int client_parse_message(
|
|||
break;
|
||||
}
|
||||
|
||||
pos += sizeof(*option) + optlen;
|
||||
}
|
||||
|
||||
if (r == -ENOMSG)
|
||||
r = 0;
|
||||
|
||||
if (r < 0 || !clientid) {
|
||||
log_dhcp6_client(client, "%s has incomplete options",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
|
@ -875,6 +958,17 @@ static int client_parse_message(
|
|||
if (r < 0)
|
||||
log_dhcp6_client(client, "%s has no server id",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
return r;
|
||||
}
|
||||
|
||||
if (lease->ia.addresses) {
|
||||
lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
|
||||
lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
|
||||
}
|
||||
|
||||
if (lease->pd.addresses) {
|
||||
lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
|
||||
lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
|
||||
}
|
||||
|
||||
return r;
|
||||
|
@ -1133,17 +1227,17 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
|
|||
|
||||
case DHCP6_STATE_BOUND:
|
||||
|
||||
if (client->lease->ia.lifetime_t1 == 0xffffffff ||
|
||||
client->lease->ia.lifetime_t2 == 0xffffffff) {
|
||||
if (client->lease->ia.ia_na.lifetime_t1 == 0xffffffff ||
|
||||
client->lease->ia.ia_na.lifetime_t2 == 0xffffffff) {
|
||||
|
||||
log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
|
||||
be32toh(client->lease->ia.lifetime_t1),
|
||||
be32toh(client->lease->ia.lifetime_t2));
|
||||
be32toh(client->lease->ia.ia_na.lifetime_t1),
|
||||
be32toh(client->lease->ia.ia_na.lifetime_t2));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
|
||||
timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t1) * USEC_PER_SEC);
|
||||
|
||||
log_dhcp6_client(client, "T1 expires in %s",
|
||||
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
|
||||
|
@ -1165,7 +1259,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
|
|||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
|
||||
timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t2) * USEC_PER_SEC);
|
||||
|
||||
log_dhcp6_client(client, "T2 expires in %s",
|
||||
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
|
||||
|
@ -1354,6 +1448,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
|
|||
|
||||
client->n_ref = 1;
|
||||
client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
|
||||
client->ia_pd.type = SD_DHCP6_OPTION_IA_PD;
|
||||
client->ifindex = -1;
|
||||
client->fd = -1;
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
|
|||
valid = t;
|
||||
}
|
||||
|
||||
t = be32toh(ia->lifetime_t2);
|
||||
t = be32toh(ia->ia_na.lifetime_t2);
|
||||
if (t > valid)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -144,7 +144,7 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
|
|||
assert_return(lease, -EINVAL);
|
||||
assert_return(iaid, -EINVAL);
|
||||
|
||||
*iaid = lease->ia.id;
|
||||
*iaid = lease->ia.ia_na.id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -176,6 +176,37 @@ void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
|
|||
lease->addr_iter = lease->ia.addresses;
|
||||
}
|
||||
|
||||
int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
|
||||
uint8_t *prefix_len,
|
||||
uint32_t *lifetime_preferred,
|
||||
uint32_t *lifetime_valid) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(prefix, -EINVAL);
|
||||
assert_return(prefix_len, -EINVAL);
|
||||
assert_return(lifetime_preferred, -EINVAL);
|
||||
assert_return(lifetime_valid, -EINVAL);
|
||||
|
||||
if (!lease->prefix_iter)
|
||||
return -ENOMSG;
|
||||
|
||||
memcpy(prefix, &lease->prefix_iter->iapdprefix.address,
|
||||
sizeof(struct in6_addr));
|
||||
*prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
|
||||
*lifetime_preferred =
|
||||
be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
|
||||
*lifetime_valid =
|
||||
be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
|
||||
|
||||
lease->prefix_iter = lease->prefix_iter->addresses_next;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
|
||||
if (lease)
|
||||
lease->prefix_iter = lease->pd.addresses;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
|
||||
int r;
|
||||
|
||||
|
@ -382,6 +413,7 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
|
|||
|
||||
free(lease->serverid);
|
||||
dhcp6_lease_free_ia(&lease->ia);
|
||||
dhcp6_lease_free_ia(&lease->pd);
|
||||
|
||||
free(lease->dns);
|
||||
|
||||
|
|
|
@ -168,6 +168,12 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst,
|
|||
.msg_namelen = sizeof(dst_addr),
|
||||
.msg_iov = iov,
|
||||
};
|
||||
usec_t time_now;
|
||||
int r;
|
||||
|
||||
r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (dst && !in_addr_is_null(AF_INET6, (union in_addr_union*) dst))
|
||||
dst_addr.sin6_addr = *dst;
|
||||
|
@ -197,6 +203,18 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst,
|
|||
}
|
||||
|
||||
LIST_FOREACH(prefix, p, ra->prefixes) {
|
||||
if (p->valid_until) {
|
||||
|
||||
if (time_now > p->valid_until)
|
||||
p->opt.valid_lifetime = 0;
|
||||
else
|
||||
p->opt.valid_lifetime = htobe32((p->valid_until - time_now) / USEC_PER_SEC);
|
||||
|
||||
if (time_now > p->preferred_until)
|
||||
p->opt.preferred_lifetime = 0;
|
||||
else
|
||||
p->opt.preferred_lifetime = htobe32((p->preferred_until - time_now) / USEC_PER_SEC);
|
||||
}
|
||||
iov[msg.msg_iovlen].iov_base = &p->opt;
|
||||
iov[msg.msg_iovlen].iov_len = sizeof(p->opt);
|
||||
msg.msg_iovlen++;
|
||||
|
@ -445,9 +463,6 @@ _public_ int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
|
|||
assert_return(ra, -EINVAL);
|
||||
assert_return(mtu >= 1280, -EINVAL);
|
||||
|
||||
if (ra->state != SD_RADV_STATE_IDLE)
|
||||
return -EBUSY;
|
||||
|
||||
ra->mtu = mtu;
|
||||
|
||||
return 0;
|
||||
|
@ -517,9 +532,13 @@ _public_ int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
|
|||
return r;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
|
||||
_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, bool dynamic) {
|
||||
sd_radv_prefix *cur;
|
||||
int r;
|
||||
_cleanup_free_ char *addr_p = NULL;
|
||||
char time_string_preferred[FORMAT_TIMESPAN_MAX];
|
||||
char time_string_valid[FORMAT_TIMESPAN_MAX];
|
||||
usec_t time_now, valid, preferred, valid_until, preferred_until;
|
||||
|
||||
assert_return(ra, -EINVAL);
|
||||
|
||||
|
@ -527,7 +546,6 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
|
|||
return -EINVAL;
|
||||
|
||||
LIST_FOREACH(prefix, cur, ra->prefixes) {
|
||||
int r;
|
||||
|
||||
r = in_addr_prefix_intersect(AF_INET6,
|
||||
(union in_addr_union*) &cur->opt.in6_addr,
|
||||
|
@ -537,13 +555,16 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
|
|||
if (r > 0) {
|
||||
_cleanup_free_ char *addr_cur = NULL;
|
||||
|
||||
(void) in_addr_to_string(AF_INET6,
|
||||
(union in_addr_union*) &cur->opt.in6_addr,
|
||||
&addr_cur);
|
||||
(void) in_addr_to_string(AF_INET6,
|
||||
(union in_addr_union*) &p->opt.in6_addr,
|
||||
&addr_p);
|
||||
|
||||
if (dynamic && cur->opt.prefixlen == p->opt.prefixlen)
|
||||
goto update;
|
||||
|
||||
(void) in_addr_to_string(AF_INET6,
|
||||
(union in_addr_union*) &cur->opt.in6_addr,
|
||||
&addr_cur);
|
||||
log_radv("IPv6 prefix %s/%u already configured, ignoring %s/%u",
|
||||
addr_cur, cur->opt.prefixlen,
|
||||
addr_p, p->opt.prefixlen);
|
||||
|
@ -559,11 +580,69 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
|
|||
ra->n_prefixes++;
|
||||
|
||||
(void) in_addr_to_string(AF_INET6, (union in_addr_union*) &p->opt.in6_addr, &addr_p);
|
||||
log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen);
|
||||
|
||||
if (!dynamic) {
|
||||
log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cur = p;
|
||||
|
||||
update:
|
||||
r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
valid = be32toh(p->opt.valid_lifetime) * USEC_PER_SEC;
|
||||
valid_until = usec_add(valid, time_now);
|
||||
if (valid_until == USEC_INFINITY)
|
||||
return -EOVERFLOW;
|
||||
|
||||
preferred = be32toh(p->opt.preferred_lifetime) * USEC_PER_SEC;
|
||||
preferred_until = usec_add(preferred, time_now);
|
||||
if (preferred_until == USEC_INFINITY)
|
||||
return -EOVERFLOW;
|
||||
|
||||
cur->valid_until = valid_until;
|
||||
cur->preferred_until = preferred_until;
|
||||
|
||||
log_radv("%s prefix %s/%u preferred %s valid %s",
|
||||
cur? "Updated": "Added",
|
||||
addr_p, p->opt.prefixlen,
|
||||
format_timespan(time_string_preferred, FORMAT_TIMESPAN_MAX,
|
||||
preferred, USEC_PER_SEC),
|
||||
format_timespan(time_string_valid, FORMAT_TIMESPAN_MAX,
|
||||
valid, USEC_PER_SEC));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra,
|
||||
struct in6_addr *prefix,
|
||||
uint8_t prefixlen) {
|
||||
sd_radv_prefix *cur, *next;
|
||||
|
||||
assert_return(ra, NULL);
|
||||
assert_return(prefix, NULL);
|
||||
|
||||
LIST_FOREACH_SAFE(prefix, cur, next, ra->prefixes) {
|
||||
if (prefixlen != cur->opt.prefixlen)
|
||||
continue;
|
||||
|
||||
if (!in_addr_equal(AF_INET6,
|
||||
(union in_addr_union *)prefix,
|
||||
(union in_addr_union *)&cur->opt.in6_addr))
|
||||
continue;
|
||||
|
||||
LIST_REMOVE(prefix, ra->prefixes, cur);
|
||||
ra->n_prefixes--;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
_public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
|
||||
const struct in6_addr *dns, size_t n_dns) {
|
||||
_cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL;
|
||||
|
|
|
@ -156,6 +156,135 @@ static int test_option(sd_event *e) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int test_option_status(sd_event *e) {
|
||||
uint8_t option1[] = {
|
||||
/* IA NA */
|
||||
0x00, 0x03, 0x00, 0x12, 0x1a, 0x1d, 0x1a, 0x1d,
|
||||
0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02,
|
||||
/* status option */
|
||||
0x00, 0x0d, 0x00, 0x02, 0x00, 0x01,
|
||||
};
|
||||
static const uint8_t option2[] = {
|
||||
/* IA NA */
|
||||
0x00, 0x03, 0x00, 0x2e, 0x1a, 0x1d, 0x1a, 0x1d,
|
||||
0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02,
|
||||
/* IA Addr */
|
||||
0x00, 0x05, 0x00, 0x1e,
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
/* status option */
|
||||
0x00, 0x0d, 0x00, 0x02, 0x00, 0x01,
|
||||
};
|
||||
static const uint8_t option3[] = {
|
||||
/* IA NA */
|
||||
0x00, 0x03, 0x00, 0x34, 0x1a, 0x1d, 0x1a, 0x1d,
|
||||
0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02,
|
||||
/* IA Addr */
|
||||
0x00, 0x05, 0x00, 0x24,
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
/* status option */
|
||||
0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 'f', 'o',
|
||||
'o', 'b', 'a', 'r',
|
||||
};
|
||||
static const uint8_t option4[] = {
|
||||
/* IA PD */
|
||||
0x00, 0x19, 0x00, 0x2f, 0x1a, 0x1d, 0x1a, 0x1d,
|
||||
0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02,
|
||||
/* IA PD Prefix */
|
||||
0x00, 0x1a, 0x00, 0x1f,
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe,
|
||||
0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00,
|
||||
/* status option */
|
||||
0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
|
||||
};
|
||||
static const uint8_t option5[] = {
|
||||
/* IA PD */
|
||||
0x00, 0x19, 0x00, 0x52, 0x1a, 0x1d, 0x1a, 0x1d,
|
||||
0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02,
|
||||
/* IA PD Prefix #1 */
|
||||
0x00, 0x1a, 0x00, 0x1f,
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe,
|
||||
0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00,
|
||||
/* status option */
|
||||
0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
|
||||
/* IA PD Prefix #2 */
|
||||
0x00, 0x1a, 0x00, 0x1f,
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x80, 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x0l, 0xd0,
|
||||
0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00,
|
||||
0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
|
||||
};
|
||||
DHCP6Option *option;
|
||||
DHCP6IA ia, pd;
|
||||
int r = 0;
|
||||
|
||||
if (verbose)
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
|
||||
zero(ia);
|
||||
option = (DHCP6Option *)option1;
|
||||
assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len));
|
||||
|
||||
r = dhcp6_option_parse_ia(option, &ia);
|
||||
assert_se(r == -EINVAL);
|
||||
assert_se(ia.addresses == NULL);
|
||||
|
||||
option->len = htobe16(17);
|
||||
r = dhcp6_option_parse_ia(option, &ia);
|
||||
assert_se(r == -ENOBUFS);
|
||||
assert_se(ia.addresses == NULL);
|
||||
|
||||
option->len = htobe16(sizeof(DHCP6Option));
|
||||
r = dhcp6_option_parse_ia(option, &ia);
|
||||
assert_se(r == -ENOBUFS);
|
||||
assert_se(ia.addresses == NULL);
|
||||
|
||||
zero(ia);
|
||||
option = (DHCP6Option *)option2;
|
||||
assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len));
|
||||
|
||||
r = dhcp6_option_parse_ia(option, &ia);
|
||||
assert_se(r >= 0);
|
||||
assert_se(ia.addresses == NULL);
|
||||
|
||||
zero(ia);
|
||||
option = (DHCP6Option *)option3;
|
||||
assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len));
|
||||
|
||||
r = dhcp6_option_parse_ia(option, &ia);
|
||||
assert_se(r >= 0);
|
||||
assert_se(ia.addresses != NULL);
|
||||
|
||||
zero(pd);
|
||||
option = (DHCP6Option *)option4;
|
||||
assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len));
|
||||
|
||||
r = dhcp6_option_parse_ia(option, &pd);
|
||||
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);
|
||||
assert_se(memcmp(&pd.ia_pd.lifetime_t2, &option4[12], 4) == 0);
|
||||
|
||||
zero(pd);
|
||||
option = (DHCP6Option *)option5;
|
||||
assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len));
|
||||
|
||||
r = dhcp6_option_parse_ia(option, &pd);
|
||||
assert_se(r == 0);
|
||||
assert_se(pd.addresses != NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t msg_advertise[198] = {
|
||||
0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e,
|
||||
0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30,
|
||||
|
@ -217,14 +346,13 @@ static uint8_t fqdn_wire[16] = {
|
|||
static int test_advertise_option(sd_event *e) {
|
||||
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
|
||||
DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
|
||||
uint8_t *optval, *opt = msg_advertise + sizeof(DHCP6Message);
|
||||
uint16_t optcode;
|
||||
size_t optlen, len = sizeof(msg_advertise) - sizeof(DHCP6Message);
|
||||
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;
|
||||
struct in6_addr *addrs;
|
||||
char **domains;
|
||||
|
@ -232,14 +360,19 @@ static int test_advertise_option(sd_event *e) {
|
|||
if (verbose)
|
||||
printf("* %s\n", __FUNCTION__);
|
||||
|
||||
assert_se(len >= sizeof(DHCP6Message));
|
||||
|
||||
assert_se(dhcp6_lease_new(&lease) >= 0);
|
||||
|
||||
assert_se(advertise->type == DHCP6_ADVERTISE);
|
||||
assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) ==
|
||||
0x0fb4e5);
|
||||
|
||||
while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen,
|
||||
&optval)) >= 0) {
|
||||
while (pos < len) {
|
||||
DHCP6Option *option = (DHCP6Option *)&advertise->options[pos];
|
||||
const uint16_t optcode = be16toh(option->code);
|
||||
const uint16_t optlen = be16toh(option->len);
|
||||
uint8_t *optval = option->data;
|
||||
|
||||
switch(optcode) {
|
||||
case SD_DHCP6_OPTION_CLIENTID:
|
||||
|
@ -261,9 +394,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(&optval, &optlen,
|
||||
optcode,
|
||||
&lease->ia) >= 0);
|
||||
assert_se(dhcp6_option_parse_ia(option, &lease->ia) >= 0);
|
||||
|
||||
break;
|
||||
|
||||
|
@ -309,11 +440,11 @@ static int test_advertise_option(sd_event *e) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pos += sizeof(*option) + optlen;
|
||||
}
|
||||
|
||||
|
||||
assert_se(r == -ENOMSG);
|
||||
|
||||
assert_se(pos == len);
|
||||
assert_se(opt_clientid);
|
||||
|
||||
sd_dhcp6_lease_reset_address_iter(lease);
|
||||
|
@ -415,15 +546,11 @@ static int test_client_send_reply(DHCP6Message *request) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
|
||||
size_t len) {
|
||||
static int test_client_verify_request(DHCP6Message *request, size_t len) {
|
||||
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
|
||||
uint8_t *optval;
|
||||
uint16_t optcode;
|
||||
size_t optlen;
|
||||
size_t pos = 0;
|
||||
bool found_clientid = false, found_iana = false, found_serverid = false,
|
||||
found_elapsed_time = false, found_fqdn = false;
|
||||
int r;
|
||||
struct in6_addr addr;
|
||||
be32_t val;
|
||||
uint32_t lt_pref, lt_valid;
|
||||
|
@ -432,8 +559,14 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
|
|||
|
||||
assert_se(dhcp6_lease_new(&lease) >= 0);
|
||||
|
||||
while ((r = dhcp6_option_parse(&option, &len,
|
||||
&optcode, &optlen, &optval)) >= 0) {
|
||||
len -= sizeof(DHCP6Message);
|
||||
|
||||
while (pos < len) {
|
||||
DHCP6Option *option = (DHCP6Option *)&request->options[pos];
|
||||
uint16_t optcode = be16toh(option->code);
|
||||
uint16_t optlen = be16toh(option->len);
|
||||
uint8_t *optval = option->data;
|
||||
|
||||
switch(optcode) {
|
||||
case SD_DHCP6_OPTION_CLIENTID:
|
||||
assert_se(!found_clientid);
|
||||
|
@ -458,8 +591,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
|
|||
val = htobe32(120);
|
||||
assert_se(!memcmp(optval + 8, &val, sizeof(val)));
|
||||
|
||||
assert_se(!dhcp6_option_parse_ia(&optval, &optlen,
|
||||
optcode, &lease->ia));
|
||||
assert_se(!dhcp6_option_parse_ia(option, &lease->ia));
|
||||
|
||||
break;
|
||||
|
||||
|
@ -489,9 +621,10 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
|
|||
assert_se(!memcmp(optval + 1, fqdn_wire, sizeof(fqdn_wire)));
|
||||
break;
|
||||
}
|
||||
|
||||
pos += sizeof(*option) + optlen;
|
||||
}
|
||||
|
||||
assert_se(r == -ENOMSG);
|
||||
assert_se(found_clientid && found_iana && found_serverid &&
|
||||
found_elapsed_time);
|
||||
|
||||
|
@ -526,19 +659,21 @@ static int test_client_send_advertise(DHCP6Message *solicit) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
|
||||
size_t len) {
|
||||
uint8_t *optval;
|
||||
uint16_t optcode;
|
||||
size_t optlen;
|
||||
static int test_client_verify_solicit(DHCP6Message *solicit, size_t len) {
|
||||
bool found_clientid = false, found_iana = false,
|
||||
found_elapsed_time = false, found_fqdn = false;
|
||||
int r;
|
||||
size_t pos = 0;
|
||||
|
||||
assert_se(solicit->type == DHCP6_SOLICIT);
|
||||
|
||||
while ((r = dhcp6_option_parse(&option, &len,
|
||||
&optcode, &optlen, &optval)) >= 0) {
|
||||
len -= sizeof(DHCP6Message);
|
||||
|
||||
while (pos < len) {
|
||||
DHCP6Option *option = (DHCP6Option *)&solicit->options[pos];
|
||||
uint16_t optcode = be16toh(option->code);
|
||||
uint16_t optlen = be16toh(option->len);
|
||||
uint8_t *optval = option->data;
|
||||
|
||||
switch(optcode) {
|
||||
case SD_DHCP6_OPTION_CLIENTID:
|
||||
assert_se(!found_clientid);
|
||||
|
@ -578,9 +713,11 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
pos += sizeof(*option) + optlen;
|
||||
}
|
||||
|
||||
assert_se(r == -ENOMSG);
|
||||
assert_se(pos == len);
|
||||
assert_se(found_clientid && found_iana && found_elapsed_time);
|
||||
|
||||
return 0;
|
||||
|
@ -623,17 +760,15 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event,
|
|||
assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
|
||||
|
||||
assert_se(sd_dhcp6_client_start(client) >= 0);
|
||||
|
||||
}
|
||||
|
||||
static int test_client_verify_information_request(DHCP6Message *information_request,
|
||||
uint8_t *option, size_t len) {
|
||||
size_t len) {
|
||||
|
||||
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
|
||||
uint8_t *optval;
|
||||
uint16_t optcode;
|
||||
size_t optlen;
|
||||
size_t pos = 0;
|
||||
bool found_clientid = false, found_elapsed_time = false;
|
||||
int r;
|
||||
struct in6_addr addr;
|
||||
uint32_t lt_pref, lt_valid;
|
||||
|
||||
|
@ -641,8 +776,14 @@ static int test_client_verify_information_request(DHCP6Message *information_requ
|
|||
|
||||
assert_se(dhcp6_lease_new(&lease) >= 0);
|
||||
|
||||
while ((r = dhcp6_option_parse(&option, &len,
|
||||
&optcode, &optlen, &optval)) >= 0) {
|
||||
len -= sizeof(DHCP6Message);
|
||||
|
||||
while (pos < len) {
|
||||
DHCP6Option *option = (DHCP6Option *)&information_request->options[pos];
|
||||
uint16_t optcode = be16toh(option->code);
|
||||
uint16_t optlen = be16toh(option->len);
|
||||
uint8_t *optval = option->data;
|
||||
|
||||
switch(optcode) {
|
||||
case SD_DHCP6_OPTION_CLIENTID:
|
||||
assert_se(!found_clientid);
|
||||
|
@ -671,9 +812,11 @@ static int test_client_verify_information_request(DHCP6Message *information_requ
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
pos += sizeof(*option) + optlen;
|
||||
}
|
||||
|
||||
assert_se(r == -ENOMSG);
|
||||
assert_se(pos == len);
|
||||
assert_se(found_clientid && found_elapsed_time);
|
||||
|
||||
sd_dhcp6_lease_reset_address_iter(lease);
|
||||
|
@ -689,7 +832,6 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
|
|||
struct in6_addr mcast =
|
||||
IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
|
||||
DHCP6Message *message;
|
||||
uint8_t *option;
|
||||
|
||||
assert_se(s == test_dhcp_fd[0]);
|
||||
assert_se(server_address);
|
||||
|
@ -699,21 +841,19 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
|
|||
assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
|
||||
|
||||
message = (DHCP6Message *)packet;
|
||||
option = (uint8_t *)(message + 1);
|
||||
len -= sizeof(DHCP6Message);
|
||||
|
||||
assert_se(message->transaction_id & 0x00ffffff);
|
||||
|
||||
if (test_client_message_num == 0) {
|
||||
test_client_verify_information_request(message, option, len);
|
||||
test_client_verify_information_request(message, len);
|
||||
test_client_send_reply(message);
|
||||
test_client_message_num++;
|
||||
} else if (test_client_message_num == 1) {
|
||||
test_client_verify_solicit(message, option, len);
|
||||
test_client_verify_solicit(message, len);
|
||||
test_client_send_advertise(message);
|
||||
test_client_message_num++;
|
||||
} else if (test_client_message_num == 2) {
|
||||
test_client_verify_request(message, option, len);
|
||||
test_client_verify_request(message, len);
|
||||
test_client_send_reply(message);
|
||||
test_client_message_num++;
|
||||
}
|
||||
|
@ -789,6 +929,7 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
test_client_basic(e);
|
||||
test_option(e);
|
||||
test_option_status(e);
|
||||
test_advertise_option(e);
|
||||
test_client_solicit(e);
|
||||
|
||||
|
|
|
@ -342,8 +342,8 @@ static void test_ra(void) {
|
|||
if (prefix[i].preferred)
|
||||
assert_se(sd_radv_prefix_set_preferred_lifetime(p, prefix[i].preferred) >= 0);
|
||||
|
||||
assert_se((sd_radv_add_prefix(ra, p) >= 0) == prefix[i].succesful);
|
||||
assert_se(sd_radv_add_prefix(ra, p) < 0);
|
||||
assert_se((sd_radv_add_prefix(ra, p, false) >= 0) == prefix[i].succesful);
|
||||
assert_se(sd_radv_add_prefix(ra, p, false) < 0);
|
||||
|
||||
p = sd_radv_prefix_unref(p);
|
||||
assert_se(!p);
|
||||
|
|
|
@ -984,251 +984,3 @@ bool address_is_ready(const Address *a) {
|
|||
else
|
||||
return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED));
|
||||
}
|
||||
|
||||
int config_parse_router_preference(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
Network *network = userdata;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (streq(rvalue, "high"))
|
||||
network->router_preference = SD_NDISC_PREFERENCE_HIGH;
|
||||
else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
|
||||
network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
|
||||
else if (streq(rvalue, "low"))
|
||||
network->router_preference = SD_NDISC_PREFERENCE_LOW;
|
||||
else
|
||||
log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void prefix_free(Prefix *prefix) {
|
||||
if (!prefix)
|
||||
return;
|
||||
|
||||
if (prefix->network) {
|
||||
LIST_REMOVE(prefixes, prefix->network->static_prefixes, prefix);
|
||||
assert(prefix->network->n_static_prefixes > 0);
|
||||
prefix->network->n_static_prefixes--;
|
||||
|
||||
if (prefix->section)
|
||||
hashmap_remove(prefix->network->prefixes_by_section,
|
||||
prefix->section);
|
||||
}
|
||||
|
||||
prefix->radv_prefix = sd_radv_prefix_unref(prefix->radv_prefix);
|
||||
|
||||
free(prefix);
|
||||
}
|
||||
|
||||
int prefix_new(Prefix **ret) {
|
||||
_cleanup_prefix_free_ Prefix *prefix = NULL;
|
||||
|
||||
prefix = new0(Prefix, 1);
|
||||
if (!prefix)
|
||||
return -ENOMEM;
|
||||
|
||||
if (sd_radv_prefix_new(&prefix->radv_prefix) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = prefix;
|
||||
prefix = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prefix_new_static(Network *network, const char *filename,
|
||||
unsigned section_line, Prefix **ret) {
|
||||
_cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
|
||||
_cleanup_prefix_free_ Prefix *prefix = NULL;
|
||||
int r;
|
||||
|
||||
assert(network);
|
||||
assert(ret);
|
||||
assert(!!filename == (section_line > 0));
|
||||
|
||||
if (filename) {
|
||||
r = network_config_section_new(filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (section_line) {
|
||||
prefix = hashmap_get(network->prefixes_by_section, n);
|
||||
if (prefix) {
|
||||
*ret = prefix;
|
||||
prefix = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r = prefix_new(&prefix);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (filename) {
|
||||
prefix->section = n;
|
||||
n = NULL;
|
||||
|
||||
r = hashmap_put(network->prefixes_by_section, prefix->section,
|
||||
prefix);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
prefix->network = network;
|
||||
LIST_APPEND(prefixes, network->static_prefixes, prefix);
|
||||
network->n_static_prefixes++;
|
||||
|
||||
*ret = prefix;
|
||||
prefix = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_prefix(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Network *network = userdata;
|
||||
_cleanup_prefix_free_ Prefix *p = NULL;
|
||||
uint8_t prefixlen = 64;
|
||||
union in_addr_union in6addr;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = prefix_new_static(network, filename, section_line, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen) < 0)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
log_syntax(unit, LOG_INFO, filename, line, r, "Found prefix %s", rvalue);
|
||||
|
||||
p = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_prefix_flags(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
Network *network = userdata;
|
||||
_cleanup_prefix_free_ Prefix *p = NULL;
|
||||
int r, val;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = prefix_new_static(network, filename, section_line, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = parse_boolean(rvalue);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
val = r;
|
||||
|
||||
if (streq(lvalue, "OnLink"))
|
||||
r = sd_radv_prefix_set_onlink(p->radv_prefix, val);
|
||||
else if (streq(lvalue, "AddressAutoconfiguration"))
|
||||
r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_prefix_lifetime(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
Network *network = userdata;
|
||||
_cleanup_prefix_free_ Prefix *p = NULL;
|
||||
usec_t usec;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = prefix_new_static(network, filename, section_line, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = parse_sec(rvalue, &usec);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* a value of 0xffffffff represents infinity */
|
||||
if (streq(lvalue, "PreferredLifetimeSec"))
|
||||
r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix,
|
||||
DIV_ROUND_UP(usec, USEC_PER_SEC));
|
||||
else if (streq(lvalue, "ValidLifetimeSec"))
|
||||
r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix,
|
||||
DIV_ROUND_UP(usec, USEC_PER_SEC));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "in-addr-util.h"
|
||||
|
||||
typedef struct Address Address;
|
||||
typedef struct Prefix Prefix;
|
||||
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-network.h"
|
||||
|
@ -37,15 +36,6 @@ typedef struct Network Network;
|
|||
typedef struct Link Link;
|
||||
typedef struct NetworkConfigSection NetworkConfigSection;
|
||||
|
||||
struct Prefix {
|
||||
Network *network;
|
||||
NetworkConfigSection *section;
|
||||
|
||||
sd_radv_prefix *radv_prefix;
|
||||
|
||||
LIST_FIELDS(Prefix, prefixes);
|
||||
};
|
||||
|
||||
struct Address {
|
||||
Network *network;
|
||||
NetworkConfigSection *section;
|
||||
|
@ -90,21 +80,9 @@ bool address_is_ready(const Address *a);
|
|||
DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free);
|
||||
#define _cleanup_address_free_ _cleanup_(address_freep)
|
||||
|
||||
int prefix_new(Prefix **ret);
|
||||
void prefix_free(Prefix *prefix);
|
||||
int prefix_new_static(Network *network, const char *filename, unsigned section,
|
||||
Prefix **ret);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Prefix*, prefix_free);
|
||||
#define _cleanup_prefix_free_ _cleanup_(prefix_freep)
|
||||
|
||||
int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_broadcast(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_address_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_address_scope(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_router_preference(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_prefix(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_prefix_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_prefix_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
|
|
|
@ -20,21 +20,249 @@
|
|||
|
||||
#include <netinet/ether.h>
|
||||
#include <linux/if.h>
|
||||
#include "sd-radv.h"
|
||||
|
||||
#include "sd-dhcp6-client.h"
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "hostname-util.h"
|
||||
#include "network-internal.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "siphash24.h"
|
||||
#include "string-util.h"
|
||||
#include "radv-internal.h"
|
||||
|
||||
static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
|
||||
|
||||
static bool dhcp6_verify_link(Link *link) {
|
||||
if (!link->network) {
|
||||
log_link_info(link, "Link is not managed by us");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IN_SET(link->network->router_prefix_delegation,
|
||||
RADV_PREFIX_DELEGATION_DHCP6,
|
||||
RADV_PREFIX_DELEGATION_BOTH)) {
|
||||
log_link_debug(link, "Link does not request DHCPv6 prefix delegation");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
|
||||
Manager *manager;
|
||||
Link *l;
|
||||
Iterator i;
|
||||
|
||||
assert(dhcp6_link);
|
||||
|
||||
manager = dhcp6_link->manager;
|
||||
assert(manager);
|
||||
|
||||
HASHMAP_FOREACH(l, manager->links, i) {
|
||||
if (l == dhcp6_link)
|
||||
continue;
|
||||
|
||||
if (!dhcp6_verify_link(l))
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
|
||||
Link *link) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
|
||||
uint8_t prefix_len,
|
||||
uint32_t lifetime_preferred,
|
||||
uint32_t lifetime_valid) {
|
||||
sd_radv *radv = link->radv;
|
||||
int r;
|
||||
_cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
|
||||
|
||||
r = sd_radv_prefix_new(&p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_radv_stop(radv);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_radv_add_prefix(radv, p, true);
|
||||
if (r < 0 && r != -EEXIST)
|
||||
return r;
|
||||
|
||||
r = manager_dhcp6_prefix_add(link->manager, &p->opt.in6_addr, link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_radv_start(radv);
|
||||
}
|
||||
|
||||
static Network *dhcp6_reset_pd_prefix_network(Link *link) {
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
assert(link->manager->networks);
|
||||
|
||||
return link->manager->networks;
|
||||
}
|
||||
|
||||
static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
|
||||
struct in6_addr *pd_prefix,
|
||||
uint8_t pd_prefix_len,
|
||||
uint32_t lifetime_preferred,
|
||||
uint32_t lifetime_valid) {
|
||||
Link *link;
|
||||
Manager *manager = dhcp6_link->manager;
|
||||
union in_addr_union prefix;
|
||||
uint8_t n_prefixes, n_used = 0;
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
assert(pd_prefix_len <= 64);
|
||||
|
||||
prefix.in6 = *pd_prefix;
|
||||
|
||||
r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
n_prefixes = 1 << (64 - pd_prefix_len);
|
||||
|
||||
(void) in_addr_to_string(AF_INET6, &prefix, &buf);
|
||||
log_link_debug(dhcp6_link, "Assigning up to %u prefixes from %s/%u",
|
||||
n_prefixes, strnull(buf), pd_prefix_len);
|
||||
|
||||
while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
|
||||
Link *assigned_link;
|
||||
|
||||
if (n_used == n_prefixes) {
|
||||
log_link_debug(dhcp6_link, "Assigned %u/%u prefixes from %s/%u",
|
||||
n_used, n_prefixes, strnull(buf), pd_prefix_len);
|
||||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (link == dhcp6_link)
|
||||
continue;
|
||||
|
||||
if (!dhcp6_verify_link(link))
|
||||
continue;
|
||||
|
||||
assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6);
|
||||
if (assigned_link != NULL && assigned_link != link)
|
||||
continue;
|
||||
|
||||
r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
|
||||
lifetime_preferred, lifetime_valid);
|
||||
if (r < 0) {
|
||||
log_link_error_errno(link, r, "Unable to %s prefix %s/%u for link: %m",
|
||||
assigned_link ? "update": "assign",
|
||||
strnull(buf), pd_prefix_len);
|
||||
|
||||
if (assigned_link == NULL)
|
||||
continue;
|
||||
|
||||
} else
|
||||
log_link_debug(link, "Assigned prefix %u/%u %s/64 to link",
|
||||
n_used + 1, n_prefixes, strnull(buf));
|
||||
|
||||
n_used++;
|
||||
|
||||
r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len);
|
||||
if (r < 0 && n_used < n_prefixes)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (n_used < n_prefixes) {
|
||||
Route *route;
|
||||
int n = n_used;
|
||||
|
||||
r = route_new(&route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
while (n < n_prefixes) {
|
||||
route_update(route, &prefix, pd_prefix_len, NULL, NULL,
|
||||
0, 0, RTN_UNREACHABLE);
|
||||
|
||||
r = route_configure(route, link, NULL);
|
||||
if (r < 0) {
|
||||
route_free(route);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return n_used;
|
||||
}
|
||||
|
||||
static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
|
||||
int r;
|
||||
sd_dhcp6_lease *lease;
|
||||
struct in6_addr pd_prefix;
|
||||
uint8_t pd_prefix_len;
|
||||
uint32_t lifetime_preferred, lifetime_valid;
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
Iterator i = ITERATOR_FIRST;
|
||||
|
||||
r = sd_dhcp6_client_get_lease(client, &lease);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) in_addr_to_string(AF_INET6, (union in_addr_union*) &pd_prefix, &buf);
|
||||
|
||||
dhcp6_reset_pd_prefix_network(link);
|
||||
sd_dhcp6_lease_reset_pd_prefix_iter(lease);
|
||||
|
||||
while (sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len,
|
||||
&lifetime_preferred,
|
||||
&lifetime_valid) >= 0) {
|
||||
|
||||
if (pd_prefix_len > 64) {
|
||||
log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
|
||||
strnull(buf), pd_prefix_len);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix,
|
||||
pd_prefix_len,
|
||||
lifetime_preferred,
|
||||
lifetime_valid);
|
||||
if (r < 0 && r != -EAGAIN)
|
||||
return r;
|
||||
|
||||
if (r >= 0)
|
||||
i = ITERATOR_FIRST;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
|
||||
void *userdata) {
|
||||
_cleanup_link_unref_ Link *link = userdata;
|
||||
|
@ -139,6 +367,8 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
|
|||
if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
|
||||
log_link_warning(link, "DHCPv6 lease lost");
|
||||
|
||||
(void) manager_dhcp6_prefix_remove_all(link->manager, link);
|
||||
|
||||
link->dhcp6_configured = false;
|
||||
break;
|
||||
|
||||
|
@ -149,6 +379,10 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
|
|||
return;
|
||||
}
|
||||
|
||||
r = dhcp6_lease_pd_prefix_acquired(client, link);
|
||||
if (r < 0)
|
||||
log_link_debug(link, "DHCPv6 did not receive prefixes to delegate");
|
||||
|
||||
_fallthrough_;
|
||||
case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
|
||||
r = dhcp6_lease_information_acquired(client, link);
|
||||
|
@ -283,6 +517,12 @@ int dhcp6_configure(Link *link) {
|
|||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
if (dhcp6_enable_prefix_delegation(link)) {
|
||||
r = sd_dhcp6_client_set_prefix_delegation(client, true);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
link->dhcp6_client = client;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -129,7 +129,7 @@ static bool link_radv_enabled(Link *link) {
|
|||
if (!link_ipv6ll_enabled(link))
|
||||
return false;
|
||||
|
||||
return link->network->router_prefix_delegation;
|
||||
return (link->network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE);
|
||||
}
|
||||
|
||||
static bool link_lldp_rx_enabled(Link *link) {
|
||||
|
|
|
@ -1249,6 +1249,145 @@ static int manager_dirty_handler(sd_event_source *s, void *userdata) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr) {
|
||||
assert_return(m, NULL);
|
||||
assert_return(m->dhcp6_prefixes, NULL);
|
||||
assert_return(addr, NULL);
|
||||
|
||||
return hashmap_get(m->dhcp6_prefixes, addr);
|
||||
}
|
||||
|
||||
static int dhcp6_route_add_callback(sd_netlink *nl, sd_netlink_message *m,
|
||||
void *userdata) {
|
||||
Link *l = userdata;
|
||||
int r;
|
||||
union in_addr_union prefix;
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r != 0) {
|
||||
log_link_debug_errno(l, r, "Received error adding DHCPv6 Prefix Delegation route: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = sd_netlink_message_read_in6_addr(m, RTA_DST, &prefix.in6);
|
||||
if (r < 0) {
|
||||
log_link_debug_errno(l, r, "Could not read IPv6 address from DHCPv6 Prefix Delegation while adding route: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
(void) in_addr_to_string(AF_INET6, &prefix, &buf);
|
||||
log_link_debug(l, "Added DHCPv6 Prefix Deleagtion route %s/64",
|
||||
strnull(buf));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
|
||||
int r;
|
||||
Route *route;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->dhcp6_prefixes, -ENODATA);
|
||||
assert_return(addr, -EINVAL);
|
||||
|
||||
r = route_add(link, AF_INET6, (union in_addr_union *) addr, 64,
|
||||
0, 0, 0, &route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_configure(route, link, dhcp6_route_add_callback);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return hashmap_put(m->dhcp6_prefixes, addr, link);
|
||||
}
|
||||
|
||||
static int dhcp6_route_remove_callback(sd_netlink *nl, sd_netlink_message *m,
|
||||
void *userdata) {
|
||||
Link *l = userdata;
|
||||
int r;
|
||||
union in_addr_union prefix;
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r != 0) {
|
||||
log_link_debug_errno(l, r, "Received error on DHCPv6 Prefix Delegation route removal: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = sd_netlink_message_read_in6_addr(m, RTA_DST, &prefix.in6);
|
||||
if (r < 0) {
|
||||
log_link_debug_errno(l, r, "Could not read IPv6 address from DHCPv6 Prefix Delegation while removing route: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
(void) in_addr_to_string(AF_INET6, &prefix, &buf);
|
||||
log_link_debug(l, "Removed DHCPv6 Prefix Delegation route %s/64",
|
||||
strnull(buf));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
|
||||
Link *l;
|
||||
int r;
|
||||
Route *route;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->dhcp6_prefixes, -ENODATA);
|
||||
assert_return(addr, -EINVAL);
|
||||
|
||||
l = hashmap_remove(m->dhcp6_prefixes, addr);
|
||||
if (!l)
|
||||
return -EINVAL;
|
||||
|
||||
(void) sd_radv_remove_prefix(l->radv, addr, 64);
|
||||
r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64,
|
||||
0, 0, 0, &route);
|
||||
if (r >= 0)
|
||||
(void) route_remove(route, l, dhcp6_route_remove_callback);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_dhcp6_prefix_remove_all(Manager *m, Link *link) {
|
||||
Iterator i;
|
||||
Link *l;
|
||||
struct in6_addr *addr;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(l, -EINVAL);
|
||||
|
||||
HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i) {
|
||||
if (l != link)
|
||||
continue;
|
||||
|
||||
(void) manager_dhcp6_prefix_remove(m, addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dhcp6_prefixes_hash_func(const void *p, struct siphash *state) {
|
||||
const struct in6_addr *addr = p;
|
||||
|
||||
assert(p);
|
||||
|
||||
siphash24_compress(addr, sizeof(*addr), state);
|
||||
}
|
||||
|
||||
static int dhcp6_prefixes_compare_func(const void *_a, const void *_b) {
|
||||
const struct in6_addr *a = _a, *b = _b;
|
||||
|
||||
return memcmp(&a, &b, sizeof(*a));
|
||||
}
|
||||
|
||||
static const struct hash_ops dhcp6_prefixes_hash_ops = {
|
||||
.hash = dhcp6_prefixes_hash_func,
|
||||
.compare = dhcp6_prefixes_compare_func,
|
||||
};
|
||||
|
||||
int manager_new(Manager **ret, sd_event *event) {
|
||||
_cleanup_manager_free_ Manager *m = NULL;
|
||||
int r;
|
||||
|
@ -1297,6 +1436,10 @@ int manager_new(Manager **ret, sd_event *event) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
m->dhcp6_prefixes = hashmap_new(&dhcp6_prefixes_hash_ops);
|
||||
if (!m->dhcp6_prefixes)
|
||||
return -ENOMEM;
|
||||
|
||||
m->duid.type = DUID_TYPE_EN;
|
||||
|
||||
(void) routing_policy_load_rules(m->state_file, &m->rules_saved);
|
||||
|
@ -1321,6 +1464,10 @@ void manager_free(Manager *m) {
|
|||
while ((network = m->networks))
|
||||
network_free(network);
|
||||
|
||||
while ((link = hashmap_first(m->dhcp6_prefixes)))
|
||||
link_unref(link);
|
||||
hashmap_free(m->dhcp6_prefixes);
|
||||
|
||||
while ((link = hashmap_first(m->links)))
|
||||
link_unref(link);
|
||||
hashmap_free(m->links);
|
||||
|
|
|
@ -63,6 +63,7 @@ struct Manager {
|
|||
Hashmap *links;
|
||||
Hashmap *netdevs;
|
||||
Hashmap *networks_by_name;
|
||||
Hashmap *dhcp6_prefixes;
|
||||
LIST_HEAD(Network, networks);
|
||||
LIST_HEAD(AddressPool, address_pools);
|
||||
|
||||
|
@ -114,5 +115,10 @@ Link* manager_find_uplink(Manager *m, Link *exclude);
|
|||
int manager_set_hostname(Manager *m, const char *hostname);
|
||||
int manager_set_timezone(Manager *m, const char *timezone);
|
||||
|
||||
Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr);
|
||||
int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link);
|
||||
int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr);
|
||||
int manager_dhcp6_prefix_remove_all(Manager *m, Link *link);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
||||
#define _cleanup_manager_free_ _cleanup_(manager_freep)
|
||||
|
|
|
@ -156,7 +156,7 @@ BridgeFDB.VLANId, config_parse_fdb_vlan_id,
|
|||
BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0
|
||||
BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0
|
||||
BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0
|
||||
Network.IPv6PrefixDelegation, config_parse_bool, 0, offsetof(Network, router_prefix_delegation)
|
||||
Network.IPv6PrefixDelegation, config_parse_router_prefix_delegation, 0, 0
|
||||
IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec)
|
||||
IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
|
||||
IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information)
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "networkd-fdb.h"
|
||||
#include "networkd-lldp-tx.h"
|
||||
#include "networkd-ipv6-proxy-ndp.h"
|
||||
#include "networkd-radv.h"
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-routing-policy-rule.h"
|
||||
#include "networkd-util.h"
|
||||
|
@ -85,6 +86,13 @@ typedef struct DUID {
|
|||
uint8_t raw_data[MAX_DUID_LEN];
|
||||
} DUID;
|
||||
|
||||
typedef enum RADVPrefixDelegation {
|
||||
RADV_PREFIX_DELEGATION_NONE,
|
||||
RADV_PREFIX_DELEGATION_STATIC,
|
||||
RADV_PREFIX_DELEGATION_DHCP6,
|
||||
RADV_PREFIX_DELEGATION_BOTH,
|
||||
} RADVPrefixDelegation;
|
||||
|
||||
typedef struct NetworkConfigSection {
|
||||
unsigned line;
|
||||
char filename[];
|
||||
|
@ -164,7 +172,7 @@ struct Network {
|
|||
bool ipv4ll_route;
|
||||
|
||||
/* IPv6 prefix delegation support */
|
||||
bool router_prefix_delegation;
|
||||
RADVPrefixDelegation router_prefix_delegation;
|
||||
usec_t router_lifetime_usec;
|
||||
uint8_t router_preference;
|
||||
bool router_managed;
|
||||
|
|
|
@ -24,7 +24,294 @@
|
|||
#include "networkd-address.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-radv.h"
|
||||
#include "parse-util.h"
|
||||
#include "sd-radv.h"
|
||||
#include "string-util.h"
|
||||
|
||||
int config_parse_router_prefix_delegation(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
Network *network = userdata;
|
||||
int d;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (streq(rvalue, "static"))
|
||||
network->router_prefix_delegation = RADV_PREFIX_DELEGATION_STATIC;
|
||||
else if (streq(rvalue, "dhcpv6"))
|
||||
network->router_prefix_delegation = RADV_PREFIX_DELEGATION_DHCP6;
|
||||
else {
|
||||
d = parse_boolean(rvalue);
|
||||
if (d > 0)
|
||||
network->router_prefix_delegation = RADV_PREFIX_DELEGATION_BOTH;
|
||||
else
|
||||
network->router_prefix_delegation = RADV_PREFIX_DELEGATION_NONE;
|
||||
|
||||
if (d < 0)
|
||||
log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router prefix delegation '%s' is invalid, ignoring assignment: %m", rvalue);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_router_preference(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
Network *network = userdata;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (streq(rvalue, "high"))
|
||||
network->router_preference = SD_NDISC_PREFERENCE_HIGH;
|
||||
else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
|
||||
network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
|
||||
else if (streq(rvalue, "low"))
|
||||
network->router_preference = SD_NDISC_PREFERENCE_LOW;
|
||||
else
|
||||
log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void prefix_free(Prefix *prefix) {
|
||||
if (!prefix)
|
||||
return;
|
||||
|
||||
if (prefix->network) {
|
||||
LIST_REMOVE(prefixes, prefix->network->static_prefixes, prefix);
|
||||
assert(prefix->network->n_static_prefixes > 0);
|
||||
prefix->network->n_static_prefixes--;
|
||||
|
||||
if (prefix->section)
|
||||
hashmap_remove(prefix->network->prefixes_by_section,
|
||||
prefix->section);
|
||||
}
|
||||
|
||||
prefix->radv_prefix = sd_radv_prefix_unref(prefix->radv_prefix);
|
||||
|
||||
free(prefix);
|
||||
}
|
||||
|
||||
int prefix_new(Prefix **ret) {
|
||||
Prefix *prefix = NULL;
|
||||
|
||||
prefix = new0(Prefix, 1);
|
||||
if (!prefix)
|
||||
return -ENOMEM;
|
||||
|
||||
if (sd_radv_prefix_new(&prefix->radv_prefix) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = prefix;
|
||||
prefix = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prefix_new_static(Network *network, const char *filename,
|
||||
unsigned section_line, Prefix **ret) {
|
||||
_cleanup_network_config_section_free_ NetworkConfigSection *n = NULL;
|
||||
_cleanup_prefix_free_ Prefix *prefix = NULL;
|
||||
int r;
|
||||
|
||||
assert(network);
|
||||
assert(ret);
|
||||
assert(!!filename == (section_line > 0));
|
||||
|
||||
if (filename) {
|
||||
r = network_config_section_new(filename, section_line, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (section_line) {
|
||||
prefix = hashmap_get(network->prefixes_by_section, n);
|
||||
if (prefix) {
|
||||
*ret = prefix;
|
||||
prefix = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r = prefix_new(&prefix);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (filename) {
|
||||
prefix->section = n;
|
||||
n = NULL;
|
||||
|
||||
r = hashmap_put(network->prefixes_by_section, prefix->section,
|
||||
prefix);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
prefix->network = network;
|
||||
LIST_APPEND(prefixes, network->static_prefixes, prefix);
|
||||
network->n_static_prefixes++;
|
||||
|
||||
*ret = prefix;
|
||||
prefix = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_prefix(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Network *network = userdata;
|
||||
_cleanup_prefix_free_ Prefix *p = NULL;
|
||||
uint8_t prefixlen = 64;
|
||||
union in_addr_union in6addr;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = prefix_new_static(network, filename, section_line, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen) < 0)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
log_syntax(unit, LOG_INFO, filename, line, r, "Found prefix %s", rvalue);
|
||||
|
||||
p = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_prefix_flags(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
Network *network = userdata;
|
||||
_cleanup_prefix_free_ Prefix *p = NULL;
|
||||
int r, val;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = prefix_new_static(network, filename, section_line, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = parse_boolean(rvalue);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
val = r;
|
||||
|
||||
if (streq(lvalue, "OnLink"))
|
||||
r = sd_radv_prefix_set_onlink(p->radv_prefix, val);
|
||||
else if (streq(lvalue, "AddressAutoconfiguration"))
|
||||
r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_prefix_lifetime(const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
Network *network = userdata;
|
||||
_cleanup_prefix_free_ Prefix *p = NULL;
|
||||
usec_t usec;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(section);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
r = prefix_new_static(network, filename, section_line, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = parse_sec(rvalue, &usec);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* a value of 0xffffffff represents infinity */
|
||||
if (streq(lvalue, "PreferredLifetimeSec"))
|
||||
r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix,
|
||||
DIV_ROUND_UP(usec, USEC_PER_SEC));
|
||||
else if (streq(lvalue, "ValidLifetimeSec"))
|
||||
r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix,
|
||||
DIV_ROUND_UP(usec, USEC_PER_SEC));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int radv_get_ip6dns(Network *network, struct in6_addr **dns,
|
||||
size_t *n_dns) {
|
||||
|
@ -211,10 +498,14 @@ int radv_configure(Link *link) {
|
|||
return r;
|
||||
}
|
||||
|
||||
LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
|
||||
r = sd_radv_add_prefix(link->radv, p->radv_prefix);
|
||||
if (r != -EEXIST && r < 0)
|
||||
return r;
|
||||
if (IN_SET(link->network->router_prefix_delegation,
|
||||
RADV_PREFIX_DELEGATION_STATIC,
|
||||
RADV_PREFIX_DELEGATION_BOTH)) {
|
||||
LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
|
||||
r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
|
||||
if (r != -EEXIST && r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return radv_emit_dns(link);
|
||||
|
|
|
@ -20,7 +20,33 @@
|
|||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-link.h"
|
||||
|
||||
typedef struct Prefix Prefix;
|
||||
|
||||
struct Prefix {
|
||||
Network *network;
|
||||
NetworkConfigSection *section;
|
||||
|
||||
sd_radv_prefix *radv_prefix;
|
||||
|
||||
LIST_FIELDS(Prefix, prefixes);
|
||||
};
|
||||
|
||||
int prefix_new(Prefix **ret);
|
||||
void prefix_free(Prefix *prefix);
|
||||
int prefix_new_static(Network *network, const char *filename, unsigned section,
|
||||
Prefix **ret);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Prefix*, prefix_free);
|
||||
#define _cleanup_prefix_free_ _cleanup_(prefix_freep)
|
||||
|
||||
int config_parse_router_prefix_delegation(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_router_preference(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_prefix(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_prefix_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_prefix_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
|
||||
int radv_emit_dns(Link *link);
|
||||
int radv_configure(Link *link);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-dhcp6-lease.h"
|
||||
|
@ -64,6 +65,8 @@ enum {
|
|||
|
||||
SD_DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */
|
||||
SD_DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */
|
||||
SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, prefix delegation */
|
||||
SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, prefix delegation */
|
||||
|
||||
SD_DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */
|
||||
|
||||
|
@ -116,6 +119,8 @@ int sd_dhcp6_client_get_information_request(
|
|||
int sd_dhcp6_client_set_request_option(
|
||||
sd_dhcp6_client *client,
|
||||
uint16_t option);
|
||||
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
|
||||
bool delegation);
|
||||
|
||||
int sd_dhcp6_client_get_lease(
|
||||
sd_dhcp6_client *client,
|
||||
|
|
|
@ -36,6 +36,11 @@ int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
|
|||
struct in6_addr *addr,
|
||||
uint32_t *lifetime_preferred,
|
||||
uint32_t *lifetime_valid);
|
||||
void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease);
|
||||
int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
|
||||
uint8_t *prefix_len,
|
||||
uint32_t *lifetime_preferred,
|
||||
uint32_t *lifetime_valid);
|
||||
|
||||
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs);
|
||||
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <inttypes.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-ndisc.h"
|
||||
|
@ -62,7 +63,9 @@ int sd_radv_set_router_lifetime(sd_radv *ra, uint32_t router_lifetime);
|
|||
int sd_radv_set_managed_information(sd_radv *ra, int managed);
|
||||
int sd_radv_set_other_information(sd_radv *ra, int other);
|
||||
int sd_radv_set_preference(sd_radv *ra, unsigned preference);
|
||||
int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p);
|
||||
int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, bool dynamic);
|
||||
sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, struct in6_addr *prefix,
|
||||
uint8_t prefixlen);
|
||||
int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
|
||||
const struct in6_addr *dns, size_t n_dns);
|
||||
int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime, char **search_list);
|
||||
|
|
Loading…
Reference in a new issue