Merge pull request #9981 from pfl/dhcp6_pd_other_information_quirk

DHCPv6 PD other information quirk
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2018-09-24 13:02:24 +02:00 committed by GitHub
commit 54e6f0a38f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 355 additions and 135 deletions

View File

@ -1372,6 +1372,22 @@
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ForceDHCPv6PDOtherInformation=</varname></term>
<listitem>
<para>A boolean that enforces DHCPv6 stateful mode when the 'Other information' bit is set in
Router Advertisement messages. By default setting only the 'O' bit in Router Advertisements
makes DHCPv6 request network information in a stateless manner using a two-message Information
Request and Information Reply message exchange.
<ulink url="https://tools.ietf.org/html/rfc7084">RFC 7084</ulink>, requirement WPD-4, updates
this behavior for a Customer Edge router so that stateful DHCPv6 Prefix Delegation is also
requested when only the 'O' bit is set in Router Advertisements. This option enables such a CE
behavior as it is impossible to automatically distinguish the intention of the 'O' bit otherwise.
By default this option is set to 'false', enable it if no prefixes are delegated when the device
should be acting as a CE router.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -73,8 +73,6 @@ struct DHCP6IA {
struct ia_pd ia_pd;
struct ia_ta ia_ta;
};
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
LIST_HEAD(DHCP6Address, addresses);
};

View File

@ -37,7 +37,6 @@ struct sd_dhcp6_lease {
size_t ntp_fqdn_count;
};
int dhcp6_lease_clear_timers(DHCP6IA *ia);
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire);
DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia);
@ -50,6 +49,7 @@ int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease);
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit);
int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,

View File

@ -27,6 +27,13 @@
#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
enum {
DHCP6_REQUEST_IA_NA = 1,
DHCP6_REQUEST_IA_TA = 2, /* currently not used */
DHCP6_REQUEST_IA_PD = 4,
};
struct sd_dhcp6_client {
unsigned n_ref;
@ -40,7 +47,9 @@ struct sd_dhcp6_client {
uint16_t arp_type;
DHCP6IA ia_na;
DHCP6IA ia_pd;
bool prefix_delegation;
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
int request;
be32_t transaction_id;
usec_t transaction_start;
struct sd_dhcp6_lease *lease;
@ -325,10 +334,36 @@ 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) {
int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
assert_return(client, -EINVAL);
assert_return(delegation, -EINVAL);
*delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD);
return 0;
}
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
assert_return(client, -EINVAL);
client->prefix_delegation = delegation;
SET_FLAG(client->request, DHCP6_REQUEST_IA_PD, delegation);
return 0;
}
int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
assert_return(client, -EINVAL);
assert_return(request, -EINVAL);
*request = FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA);
return 0;
}
int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
assert_return(client, -EINVAL);
SET_FLAG(client->request, DHCP6_REQUEST_IA_NA, request);
return 0;
}
@ -355,12 +390,8 @@ static void client_notify(sd_dhcp6_client *client, int event) {
static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) {
assert(client);
if (client->lease) {
dhcp6_lease_clear_timers(&client->lease->ia);
sd_dhcp6_lease_unref(client->lease);
}
client->lease = lease;
(void) sd_dhcp6_lease_unref(client->lease);
client->lease = sd_dhcp6_lease_ref(lease);
}
static int client_reset(sd_dhcp6_client *client) {
@ -374,11 +405,6 @@ static int client_reset(sd_dhcp6_client *client) {
client->transaction_id = 0;
client->transaction_start = 0;
client->ia_na.timeout_t1 =
sd_event_source_unref(client->ia_na.timeout_t1);
client->ia_na.timeout_t2 =
sd_event_source_unref(client->ia_na.timeout_t2);
client->retransmit_time = 0;
client->retransmit_count = 0;
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
@ -436,9 +462,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (r < 0)
return r;
r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
if (r < 0)
return r;
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
r = dhcp6_option_append_ia(&opt, &optlen,
&client->ia_na);
if (r < 0)
return r;
}
if (client->fqdn) {
r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
@ -446,7 +475,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
if (client->prefix_delegation) {
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd);
if (r < 0)
return r;
@ -471,9 +500,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (r < 0)
return r;
r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
if (r < 0)
return r;
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
r = dhcp6_option_append_ia(&opt, &optlen,
&client->lease->ia);
if (r < 0)
return r;
}
if (client->fqdn) {
r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
@ -481,7 +513,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
if (client->prefix_delegation) {
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
if (r < 0)
return r;
@ -495,9 +527,11 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
case DHCP6_STATE_REBIND:
message->type = DHCP6_REBIND;
r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
if (r < 0)
return r;
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
if (r < 0)
return r;
}
if (client->fqdn) {
r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
@ -505,7 +539,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
if (client->prefix_delegation) {
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
if (r < 0)
return r;
@ -562,8 +596,8 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
assert(client);
assert(client->lease);
client->lease->ia.timeout_t2 =
sd_event_source_unref(client->lease->ia.timeout_t2);
client->timeout_t2 =
sd_event_source_unref(client->timeout_t2);
log_dhcp6_client(client, "Timeout T2");
@ -579,8 +613,8 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
assert(client);
assert(client->lease);
client->lease->ia.timeout_t1 =
sd_event_source_unref(client->lease->ia.timeout_t1);
client->timeout_t1 =
sd_event_source_unref(client->timeout_t1);
log_dhcp6_client(client, "Timeout T1");
@ -914,7 +948,7 @@ static int client_parse_message(
if (r < 0 && r != -ENOMSG)
return r;
r = dhcp6_lease_get_iaid(lease, &iaid_lease);
r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
if (r < 0)
return r;
@ -1188,10 +1222,33 @@ static int client_receive_message(
return 0;
}
static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
uint32_t *lifetime_t2) {
assert_return(client, -EINVAL);
assert_return(client->lease, -EINVAL);
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
*lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
*lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
return 0;
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
*lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
*lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
return 0;
}
return -ENOMSG;
}
static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
int r;
usec_t timeout, time_now;
char time_string[FORMAT_TIMESPAN_MAX];
uint32_t lifetime_t1, lifetime_t2;
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
@ -1251,57 +1308,58 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
case DHCP6_STATE_BOUND:
if (client->lease->ia.ia_na.lifetime_t1 == 0xffffffff ||
client->lease->ia.ia_na.lifetime_t2 == 0xffffffff) {
r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2);
if (r < 0)
goto error;
if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) {
log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
be32toh(client->lease->ia.ia_na.lifetime_t1),
be32toh(client->lease->ia.ia_na.lifetime_t2));
lifetime_t1, lifetime_t2);
return 0;
}
timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t1) * USEC_PER_SEC);
timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC);
log_dhcp6_client(client, "T1 expires in %s",
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
r = sd_event_add_time(client->event,
&client->lease->ia.timeout_t1,
&client->timeout_t1,
clock_boottime_or_monotonic(), time_now + timeout,
10 * USEC_PER_SEC, client_timeout_t1,
client);
if (r < 0)
goto error;
r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
r = sd_event_source_set_priority(client->timeout_t1,
client->event_priority);
if (r < 0)
goto error;
r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
r = sd_event_source_set_description(client->timeout_t1, "dhcp6-t1-timeout");
if (r < 0)
goto error;
timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t2) * USEC_PER_SEC);
timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC);
log_dhcp6_client(client, "T2 expires in %s",
format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
r = sd_event_add_time(client->event,
&client->lease->ia.timeout_t2,
&client->timeout_t2,
clock_boottime_or_monotonic(), time_now + timeout,
10 * USEC_PER_SEC, client_timeout_t2,
client);
if (r < 0)
goto error;
r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
r = sd_event_source_set_priority(client->timeout_t2,
client->event_priority);
if (r < 0)
goto error;
r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
r = sd_event_source_set_description(client->timeout_t2, "dhcp6-t2-timeout");
if (r < 0)
goto error;
@ -1363,6 +1421,9 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
return -EBUSY;
if (!client->information_request && !client->request)
return -EINVAL;
r = client_reset(client);
if (r < 0)
return r;
@ -1461,6 +1522,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
client->ia_pd.type = SD_DHCP6_OPTION_IA_PD;
client->ifindex = -1;
client->request = DHCP6_REQUEST_IA_NA;
client->fd = -1;
client->req_opts_len = ELEMENTSOF(default_req_opts);

View File

@ -11,15 +11,6 @@
#include "strv.h"
#include "util.h"
int dhcp6_lease_clear_timers(DHCP6IA *ia) {
assert_return(ia, -EINVAL);
ia->timeout_t1 = sd_event_source_unref(ia->timeout_t1);
ia->timeout_t2 = sd_event_source_unref(ia->timeout_t2);
return 0;
}
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
DHCP6Address *addr;
uint32_t valid = 0, t;
@ -48,8 +39,6 @@ DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) {
if (!ia)
return NULL;
dhcp6_lease_clear_timers(ia);
while (ia->addresses) {
address = ia->addresses;
@ -136,6 +125,15 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
return 0;
}
int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
assert_return(lease, -EINVAL);
assert_return(iaid, -EINVAL);
*iaid = lease->pd.ia_pd.id;
return 0;
}
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid) {

View File

@ -37,6 +37,7 @@ static uint8_t test_duid[14] = { };
static int test_client_basic(sd_event *e) {
sd_dhcp6_client *client;
int v;
if (verbose)
printf("* %s\n", __FUNCTION__);
@ -68,6 +69,36 @@ static int test_client_basic(sd_event *e) {
assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN_LIST) == -EEXIST);
assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL);
assert_se(sd_dhcp6_client_set_information_request(client, 1) >= 0);
v = 0;
assert_se(sd_dhcp6_client_get_information_request(client, &v) >= 0);
assert_se(v);
assert_se(sd_dhcp6_client_set_information_request(client, 0) >= 0);
v = 42;
assert_se(sd_dhcp6_client_get_information_request(client, &v) >= 0);
assert_se(v == 0);
v = 0;
assert_se(sd_dhcp6_client_get_address_request(client, &v) >= 0);
assert_se(v);
v = 0;
assert_se(sd_dhcp6_client_set_address_request(client, 1) >= 0);
assert_se(sd_dhcp6_client_get_address_request(client, &v) >= 0);
assert_se(v);
v = 42;
assert_se(sd_dhcp6_client_set_address_request(client, 1) >= 0);
assert_se(sd_dhcp6_client_get_address_request(client, &v) >= 0);
assert_se(v);
assert_se(sd_dhcp6_client_set_address_request(client, 1) >= 0);
assert_se(sd_dhcp6_client_set_prefix_delegation(client, 1) >= 0);
v = 0;
assert_se(sd_dhcp6_client_get_address_request(client, &v) >= 0);
assert_se(v);
v = 0;
assert_se(sd_dhcp6_client_get_prefix_delegation(client, &v) >= 0);
assert_se(v);
assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
assert_se(sd_dhcp6_client_detach_event(client) >= 0);
@ -862,7 +893,7 @@ static int test_client_solicit(sd_event *e) {
sd_dhcp6_client *client;
usec_t time_now = now(clock_boottime_or_monotonic());
struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
int val = true;
int val;
if (verbose)
printf("* %s\n", __FUNCTION__);
@ -879,10 +910,10 @@ static int test_client_solicit(sd_event *e) {
assert_se(sd_dhcp6_client_set_fqdn(client, "host.lab.intra") == 1);
assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0);
assert_se(val == false);
assert_se(sd_dhcp6_client_set_information_request(client, true) >= 0);
assert_se(val == 0);
assert_se(sd_dhcp6_client_set_information_request(client, 42) >= 0);
assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0);
assert_se(val == true);
assert_se(val);
assert_se(sd_dhcp6_client_set_callback(client,
test_client_information_cb, e) >= 0);

View File

@ -103,12 +103,77 @@ static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
return sd_radv_start(radv);
}
static Network *dhcp6_reset_pd_prefix_network(Link *link) {
assert(link);
assert(link->manager);
assert(link->manager->networks);
static int dhcp6_route_remove_cb(sd_netlink *nl, sd_netlink_message *m,
void *userdata) {
Link *l = userdata;
int r;
return link->manager->networks;
r = sd_netlink_message_get_errno(m);
if (r < 0)
log_link_debug_errno(l, r, "Received error on unreachable route removal for DHCPv6 delegated subnetl: %m");
l = link_unref(l);
return 0;
}
int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
int r;
sd_dhcp6_lease *lease;
union in_addr_union pd_prefix;
uint8_t pd_prefix_len;
uint32_t lifetime_preferred, lifetime_valid;
r = sd_dhcp6_client_get_lease(client, &lease);
if (r < 0)
return r;
sd_dhcp6_lease_reset_pd_prefix_iter(lease);
while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
&lifetime_preferred,
&lifetime_valid) >= 0) {
_cleanup_free_ char *buf = NULL;
_cleanup_free_ Route *route;
if (pd_prefix_len > 64)
continue;
(void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
if (pd_prefix_len < 64) {
r = route_new(&route);
if (r < 0) {
log_link_warning_errno(link, r, "Cannot create unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m",
strnull(buf),
pd_prefix_len);
continue;
}
route_add(link, AF_INET6, &pd_prefix, pd_prefix_len,
0, 0, 0, &route);
route_update(route, NULL, 0, NULL, NULL, 0, 0,
RTN_UNREACHABLE);
r = route_remove(route, link, dhcp6_route_remove_cb);
if (r < 0) {
(void) in_addr_to_string(AF_INET6,
&pd_prefix, &buf);
log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
strnull(buf),
pd_prefix_len);
route_free(route);
continue;
}
link = link_ref(link);
log_link_debug(link, "Removing unreachable route %s/%u",
strnull(buf), pd_prefix_len);
}
}
return 0;
}
static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
@ -184,39 +249,28 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
return r;
}
if (n_used < n_prefixes) {
Route *route;
uint64_t n = n_used;
r = route_new(&route);
if (r < 0)
return r;
route->family = AF_INET6;
while (n < n_prefixes) {
route_update(route, &prefix, pd_prefix_len, NULL, NULL,
0, 0, RTN_UNREACHABLE);
r = route_configure(route, dhcp6_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;
return 0;
}
static int dhcp6_route_add_cb(sd_netlink *nl, sd_netlink_message *m,
void *userdata) {
Link *l = userdata;
int r;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST)
log_link_debug_errno(l, r, "Received error when adding unreachable route for DHCPv6 delegated subnet: %m");
l = link_unref(l);
return 0;
}
static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
int r;
sd_dhcp6_lease *lease;
struct in6_addr pd_prefix;
union in_addr_union pd_prefix;
uint8_t pd_prefix_len;
uint32_t lifetime_preferred, lifetime_valid;
_cleanup_free_ char *buf = NULL;
@ -226,27 +280,62 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
if (r < 0)
return r;
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,
while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
&lifetime_preferred,
&lifetime_valid) >= 0) {
if (pd_prefix_len > 64) {
(void) in_addr_to_string(AF_INET6, (union in_addr_union*) &pd_prefix, &buf);
(void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
strnull(buf), pd_prefix_len);
continue;
}
if (pd_prefix_len < 48) {
(void) in_addr_to_string(AF_INET6, (union in_addr_union*) &pd_prefix, &buf);
(void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
strnull(buf), pd_prefix_len);
}
r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix,
if (pd_prefix_len < 64) {
Route *route = NULL;
(void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
r = route_new(&route);
if (r < 0) {
log_link_warning_errno(link, r, "Cannot create unreachable route for DHCPv6 delegated subnet %s/%u: %m",
strnull(buf),
pd_prefix_len);
continue;
}
route_add(link, AF_INET6, &pd_prefix, pd_prefix_len,
0, 0, 0, &route);
route_update(route, NULL, 0, NULL, NULL, 0, 0,
RTN_UNREACHABLE);
r = route_configure(route, link, dhcp6_route_add_cb);
if (r < 0) {
log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m",
strnull(buf),
pd_prefix_len);
route_free(route);
continue;
}
link = link_ref(link);
route_free(route);
log_link_debug(link, "Configuring unreachable route for %s/%u",
strnull(buf), pd_prefix_len);
} else
log_link_debug(link, "Not adding a blocking route since distributed prefix is /64");
r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix.in6,
pd_prefix_len,
lifetime_preferred,
lifetime_valid);
@ -364,6 +453,7 @@ 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) dhcp6_lease_pd_prefix_lost(client, link);
(void) manager_dhcp6_prefix_remove_all(link->manager, link);
link->dhcp6_configured = false;
@ -403,11 +493,12 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
}
int dhcp6_request_address(Link *link, int ir) {
int r, inf_req;
int r, inf_req, pd;
bool running;
assert(link);
assert(link->dhcp6_client);
assert(link->network);
assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
r = sd_dhcp6_client_is_running(link->dhcp6_client);
@ -416,6 +507,21 @@ int dhcp6_request_address(Link *link, int ir) {
else
running = r;
r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd);
if (r < 0)
return r;
if (pd && ir && link->network->dhcp6_force_pd_other_information) {
log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set");
r = sd_dhcp6_client_set_address_request(link->dhcp6_client,
false);
if (r < 0 )
return r;
ir = false;
}
if (running) {
r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
if (r < 0)

View File

@ -165,6 +165,7 @@ int dhcp4_set_client_identifier(Link *link);
int dhcp4_set_promote_secondaries(Link *link);
int dhcp6_configure(Link *link);
int dhcp6_request_address(Link *link, int ir);
int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link);
const char* link_state_to_string(LinkState s) _const_;
LinkState link_state_from_string(const char *s) _pure_;

View File

@ -1240,27 +1240,15 @@ Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr) {
}
static int dhcp6_route_add_callback(sd_netlink *nl, sd_netlink_message *m,
void *userdata) {
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) {
if (r < 0 && r != -EEXIST)
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));
l = link_unref(l);
return 0;
}
@ -1268,6 +1256,7 @@ static int dhcp6_route_add_callback(sd_netlink *nl, sd_netlink_message *m,
int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
int r;
Route *route;
_cleanup_free_ char *buf = NULL;
assert_return(m, -EINVAL);
assert_return(m->dhcp6_prefixes, -ENODATA);
@ -1282,6 +1271,11 @@ int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
if (r < 0)
return r;
(void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
log_link_debug(link, "Adding prefix route %s/64", strnull(buf));
link = link_ref(link);
return hashmap_put(m->dhcp6_prefixes, addr, link);
}
@ -1289,24 +1283,12 @@ 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) {
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));
l = link_unref(l);
return 0;
}
@ -1315,6 +1297,7 @@ int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
Link *l;
int r;
Route *route;
_cleanup_free_ char *buf = NULL;
assert_return(m, -EINVAL);
assert_return(m->dhcp6_prefixes, -ENODATA);
@ -1327,8 +1310,17 @@ int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
(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);
if (r < 0)
return r;
r = route_remove(route, l, dhcp6_route_remove_callback);
if (r < 0)
return r;
(void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
log_link_debug(l, "Removing prefix route %s/64", strnull(buf));
l = link_ref(l);
return 0;
}
@ -1452,11 +1444,18 @@ void manager_free(Manager *m) {
network_free(network);
while ((link = hashmap_first(m->dhcp6_prefixes)))
link_unref(link);
manager_dhcp6_prefix_remove_all(m, link);
hashmap_free(m->dhcp6_prefixes);
while ((link = hashmap_first(m->links)))
while ((link = hashmap_first(m->links))) {
if (link->dhcp6_client)
(void) dhcp6_lease_pd_prefix_lost(link->dhcp6_client,
link);
hashmap_remove(m->links, INT_TO_PTR(link->ifindex));
link_unref(link);
}
hashmap_free(m->links);
set_free(m->links_requesting_uuid);

View File

@ -138,6 +138,7 @@ DHCP.UseTimezone, config_parse_bool,
DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid)
DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)
DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
IPv6AcceptRA.RouteTable, config_parse_uint32, 0, offsetof(Network, ipv6_accept_ra_route_table)

View File

@ -171,6 +171,9 @@ struct Network {
struct in6_addr *router_dns;
unsigned n_router_dns;
char **router_search_domains;
bool dhcp6_force_pd_other_information; /* Start DHCPv6 PD also when 'O'
RA flag is set, see RFC 7084,
WPD-4 */
/* Bridge Support */
int use_bpdu;

View File

@ -21,7 +21,6 @@
#include <inttypes.h>
#include <net/ethernet.h>
#include <stdbool.h>
#include <sys/types.h>
#include "sd-dhcp6-lease.h"
@ -120,8 +119,14 @@ 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_get_prefix_delegation(sd_dhcp6_client *client,
int *delegation);
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
bool delegation);
int delegation);
int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client,
int *request);
int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
int request);
int sd_dhcp6_client_get_lease(
sd_dhcp6_client *client,