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/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 ff0b0f00ce..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); @@ -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, diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index a99ddaae9d..b306f07eb9 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,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); diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 54283133cb..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; @@ -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) { 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); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 0aa7a190c4..518d3e8f1f 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -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) 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 27a4f6db61..bdb1aacf7f 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -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); 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; diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index fa36dca909..7024ad84d6 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,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,