From d8c51121bb3fe7ee17a06419c91854abd3f7941f Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Thu, 6 Sep 2018 11:31:48 -0600 Subject: [PATCH 01/12] dhcp6_client: Add helper for fetching Prefix Delegation information Add helper function for fetching enabled/disabled state of Prefix Delegation for a DHCPv6 client. Update function setting prefix delegation to use an int instead of a boolean. --- src/libsystemd-network/sd-dhcp6-client.c | 11 ++++++++++- src/systemd/sd-dhcp6-client.h | 5 +++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index a99ddaae9d..20e19be3bc 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -325,7 +325,16 @@ 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 = client->prefix_delegation; + + return 0; +} + +int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) { assert_return(client, -EINVAL); client->prefix_delegation = delegation; diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index fa36dca909..b7e0db1d6c 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -21,7 +21,6 @@ #include #include -#include #include #include "sd-dhcp6-lease.h" @@ -120,8 +119,10 @@ 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_lease( sd_dhcp6_client *client, From f311a62befe70805e890723a4c21c4f070a64723 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Fri, 7 Sep 2018 14:00:10 -0600 Subject: [PATCH 02/12] dhcp6-client: Function for enabling/disabling IA_NA request Add function to enable/disable IA_NA address requests. Internally handle the request as a bit mask and add IA_PD prefix delegation to the same bit mask instead of having a separate boolean. Thus the calling code can set requests for prefix and address delegation separately. This is handy when supporting RFC 7084. Add a check in the code that at least something is requested from the server in Managed mode. By default request IA_NA addresses from the DHCPv6 server. Although a value has been defined for IA_TA, temporay IA_TA addresses are not yet requested. --- src/libsystemd-network/sd-dhcp6-client.c | 66 ++++++++++++++++++------ src/systemd/sd-dhcp6-client.h | 4 ++ 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 20e19be3bc..f857c793e5 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -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,7 @@ struct sd_dhcp6_client { uint16_t arp_type; DHCP6IA ia_na; DHCP6IA ia_pd; - bool prefix_delegation; + int request; be32_t transaction_id; usec_t transaction_start; struct sd_dhcp6_lease *lease; @@ -329,7 +336,7 @@ int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegati assert_return(client, -EINVAL); assert_return(delegation, -EINVAL); - *delegation = client->prefix_delegation; + *delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD); return 0; } @@ -337,7 +344,24 @@ int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegati 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; } @@ -445,9 +469,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); @@ -455,7 +482,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; @@ -480,9 +507,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); @@ -490,7 +520,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; @@ -504,9 +534,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); @@ -514,7 +546,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; @@ -1372,6 +1404,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; @@ -1470,6 +1505,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); diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index b7e0db1d6c..7024ad84d6 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -123,6 +123,10 @@ int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation); int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, 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, From 7776f2aeff2607603132122e382e5cc847b0b943 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Fri, 7 Sep 2018 09:24:15 -0600 Subject: [PATCH 03/12] dhcp6-client: Add tests for address, information and prefix requests Add simple test cases to ensure the request for addresses, prefixes and information request are handled as expected. --- src/libsystemd-network/test-dhcp6-client.c | 39 +++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 5e20580783..5ec8403df0 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -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); From 125f20b4de130a31ff9cf0cdcc579b99d7822a04 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Fri, 7 Sep 2018 14:15:55 -0600 Subject: [PATCH 04/12] networkd-network: Introduce DHCPv6 PD knob for RFC 7084 WPD-4 RFC 7084, WPD-4, requires Customer Edge end routers to behave according to the following: "WPD-4: By default, the IPv6 CE router MUST initiate DHCPv6 prefix delegation when either the M or O flags are set to 1 in a received Router Advertisement (RA) message. Behavior of the CE router to use DHCPv6 prefix delegation when the CE router has not received any RA or received an RA with the M and the O bits set to zero is out of scope for this document." Since it cannot be automatically detected whether DHCPv6 is to be operated as an CE end router or whether to initiate an Informational exchange to obtain other useful network information via DHCPv6 when the Router Advertisement 'O' bit is set, a 'ForceDHCPv6PDOtherInformation' boolean network configuration option in the '[DHCP]' section of a is introduced. Setting this option causes DHCPv6 to be started in stateful mode, although only the 'O' bit is seen in the Router Advertisement. When 'ForceDHCPv6PDOtherInformation' is set and the Router Advertisement has only the Other information 'O' bit set, disable requests for IA_NA addresses. Fixes #9745. --- man/systemd.network.xml | 16 ++++++++++++++++ src/network/networkd-dhcp6.c | 18 +++++++++++++++++- src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.h | 3 +++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 31af7cfa98..2c564d26b4 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1372,6 +1372,22 @@ + + ForceDHCPv6PDOtherInformation= + + 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. + RFC 7084, 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. + + + diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 0aa7a190c4..f9b97d17dd 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -403,11 +403,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 +417,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) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 357231152e..a7c94d1dc4 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -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) diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 6bfd8d5dd5..1be7d46735 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -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; From 49228c7520b55edbeb6f8ccc469ae6309363d876 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Fri, 7 Sep 2018 14:37:56 -0600 Subject: [PATCH 05/12] dhcp6-lease: Add function to fetch the IAID for the prefix Add function to fetch the IAID for the delegated IA_PD prefix. In order to keep things simple in the implemntation, the same IAID is used with IA_NA addresses and IA_PD prefixes. But the DHCPv6 server can choose to return only IA_PD prefixes, and the client can nowadays omit requesting of IA_NA addresses. Since the function fetching said IAID from the lease looks only for IA_NA ones, it will return an empty IAID, which of course does not match the one set for prefixes. Fix this by adding a function returning the IAID for the prefix. --- src/libsystemd-network/dhcp6-lease-internal.h | 1 + src/libsystemd-network/sd-dhcp6-client.c | 2 +- src/libsystemd-network/sd-dhcp6-lease.c | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index ff0b0f00ce..c05c32d5b1 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -50,6 +50,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, diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index f857c793e5..a803097d0f 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -955,7 +955,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; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 54283133cb..971d963c47 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -136,6 +136,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) { From 134ebaa45ed454be9282659c4a8e4aaeba14b035 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Tue, 11 Sep 2018 11:55:38 -0600 Subject: [PATCH 06/12] dhcp6-client: Select T1 and T2 timeouts from IA_NA and IA_PD Select T1 and T2 timeouts based on whether addresses or prefixes were requested and what the server offered. The address and prefix timeouts values have been computed earlier when the relevant DHCPv6 options were parsed. --- src/libsystemd-network/sd-dhcp6-client.c | 36 ++++++++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index a803097d0f..3562ccd182 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1229,10 +1229,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); @@ -1292,17 +1315,18 @@ 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)); @@ -1324,7 +1348,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.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)); From 9a7225de67ddfb48a2f75f125c16bfe76421ae9c Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Tue, 11 Sep 2018 16:26:01 -0600 Subject: [PATCH 07/12] dhcp6-client: Store lease timeouts T1 and T1 in client struct Since we now have the possibility to request prefixes to be delegated without corresponding IPv6 addresses, it does not make sense to store lease T1 and T2 timeouts in the otherwise unused IA_NA structure. Therefore lease timeouts T1 and T2 are moved to the DHCPv6 client structure, as there will be only one set of stateful timeouts required by RFC 7550, Section 4.3. --- src/libsystemd-network/dhcp6-internal.h | 2 -- src/libsystemd-network/dhcp6-lease-internal.h | 1 - src/libsystemd-network/sd-dhcp6-client.c | 32 +++++++------------ src/libsystemd-network/sd-dhcp6-lease.c | 11 ------- 4 files changed, 12 insertions(+), 34 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index f1cbd6a4f1..c45b068fd7 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -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); }; diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index c05c32d5b1..e004f48b4e 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -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); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 3562ccd182..9969b01ab9 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -47,6 +47,8 @@ struct sd_dhcp6_client { uint16_t arp_type; DHCP6IA ia_na; DHCP6IA ia_pd; + sd_event_source *timeout_t1; + sd_event_source *timeout_t2; int request; be32_t transaction_id; usec_t transaction_start; @@ -388,11 +390,6 @@ 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; } @@ -407,11 +404,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); @@ -603,8 +595,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"); @@ -620,8 +612,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"); @@ -1332,19 +1324,19 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { 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; @@ -1354,19 +1346,19 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { 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; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 971d963c47..15fec2d851 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -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; From 494c868d1fcdb72e0aed575ff0da36138b5af4b7 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Tue, 18 Sep 2018 18:32:19 -0600 Subject: [PATCH 08/12] networkd-dhcp6: Set one unreachable route per DHCPv6 delegated prefix Instead of setting many small unreachable routes for each of the /64 subnets that were not distributed between the links requesting delegated prefixes, set one unreachable route for the size of the delegated prefix. Each subnet asssigned to a downstream link will add a routable subnet for that link, and as the subnet assigned to the downstream link has a longer prefix than the whole delegated prefix, the downstream link subnet routes are preferred over the unroutable delegated one. The unreachable route is not added when the delegated prefix is exactly a /64 as the prefix size cannot be used to sort out the order of routing into a bigger blocking subnet with the smaller /64 punching routable "holes" into it. When stopping the DHCPv6 client, the unroutable delegated prefix is removed before the downstream link prefixes are unassigned. --- src/network/networkd-dhcp6.c | 164 ++++++++++++++++++++++++++++------- 1 file changed, 132 insertions(+), 32 deletions(-) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index f9b97d17dd..c0572add0a 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -111,6 +111,80 @@ static Network *dhcp6_reset_pd_prefix_network(Link *link) { return link->manager->networks; } +static int dhcp6_route_remove_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) + log_link_debug_errno(l, r, "Received error on unreachable route removal for DHCPv6 delegated subnetl: %m"); + + l = link_unref(l); + + return 0; +} + +static 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; + + dhcp6_reset_pd_prefix_network(link); + 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, struct in6_addr *pd_prefix, uint8_t pd_prefix_len, @@ -184,39 +258,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; @@ -229,24 +292,60 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { 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 +463,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; From e9c96052939105a6ff5a87d3e3707524a040b2eb Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Tue, 18 Sep 2018 18:32:23 -0600 Subject: [PATCH 09/12] networkd-dhcp6: Remove functions whose output is not used Remove dhcp6_reset_pd_prefix_network() that returns the network, but whose output is not used anywhere. --- src/network/networkd-dhcp6.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index c0572add0a..c044c02528 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -103,14 +103,6 @@ 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); - - return link->manager->networks; -} - static int dhcp6_route_remove_cb(sd_netlink *nl, sd_netlink_message *m, void *userdata) { Link *l = userdata; @@ -136,7 +128,6 @@ static int dhcp6_lease_pd_prefix_lost(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.in6, &pd_prefix_len, @@ -289,7 +280,6 @@ 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.in6, &pd_prefix_len, From 7c881490c1265d31dcdd8b1ab42af5bbf22a3811 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Tue, 18 Sep 2018 18:32:26 -0600 Subject: [PATCH 10/12] sd-dhcp6-client: Reference and unreference a stored lease In order to be able to properly free a DHCPv6 lease, unreference the previous lease and reference the new one. --- src/libsystemd-network/sd-dhcp6-client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 9969b01ab9..b306f07eb9 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -390,7 +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); - 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) { From e1d737ef9d68287e396bb0756cdd5abca34850a1 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Tue, 18 Sep 2018 18:32:28 -0600 Subject: [PATCH 11/12] networkd-manager: Update logging of route additions and removals Log route additions and removals when the action is to be done, as the reply rtnl message may contain only a success or failure. --- src/network/networkd-manager.c | 48 ++++++++++++---------------------- 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 27a4f6db61..73c4cd95a7 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -1240,27 +1240,14 @@ 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)); return 0; } @@ -1268,6 +1255,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 +1270,9 @@ 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)); + return hashmap_put(m->dhcp6_prefixes, addr, link); } @@ -1289,24 +1280,11 @@ 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)); return 0; } @@ -1315,6 +1293,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 +1306,15 @@ 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)); return 0; } From 65dd5e310582954be0fd99edb5aabaac38e6c1ef Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Tue, 18 Sep 2018 18:32:30 -0600 Subject: [PATCH 12/12] networkd-manager: Fix route removals on shutdown In order to shut down networkd properly, the delegated routes added need to be removed properly, and as error reporting is wanted, the network link is needed in the debug output. Solve this by calling manager_dhcp6_prefix_remove_all(), which will remove each prefix stored in the Manager structure, and while doing that reference each link so that it isn't freed before the route removal callback is called. This in turn causes the network link to be referenced once more, and an explicit hashmap_remove() must be called to remove the network link from the m->links hashmap. Also, since the registered callback is not called when the DHCPv6 client is stopped with sd_dhcp6_client_stop(), an explicit call to dhcp6_lease_pd_prefix_lost() needs to be made to clean up any unreachable routes set up for the delegated prefixes. --- src/network/networkd-dhcp6.c | 2 +- src/network/networkd-link.h | 1 + src/network/networkd-manager.c | 17 +++++++++++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index c044c02528..518d3e8f1f 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -117,7 +117,7 @@ static int dhcp6_route_remove_cb(sd_netlink *nl, sd_netlink_message *m, return 0; } -static int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) { +int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) { int r; sd_dhcp6_lease *lease; union in_addr_union pd_prefix; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index dcf722a63d..b686011da4 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -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_; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 73c4cd95a7..bdb1aacf7f 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -1248,6 +1248,7 @@ static int dhcp6_route_add_callback(sd_netlink *nl, sd_netlink_message *m, if (r < 0 && r != -EEXIST) log_link_debug_errno(l, r, "Received error adding DHCPv6 Prefix Delegation route: %m"); + l = link_unref(l); return 0; } @@ -1273,6 +1274,8 @@ int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) { (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); } @@ -1285,6 +1288,7 @@ static int dhcp6_route_remove_callback(sd_netlink *nl, sd_netlink_message *m, if (r < 0) log_link_debug_errno(l, r, "Received error on DHCPv6 Prefix Delegation route removal: %m"); + l = link_unref(l); return 0; } @@ -1316,6 +1320,8 @@ int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) { (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; } @@ -1438,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);