From d7c9c21f18704580f66a1ce73fb6b506fdf40732 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Wed, 23 Sep 2015 13:51:53 +0300 Subject: sd-dhcp6-client: Prevent setting and restarting of DHCPv6 client Prevent modifications to index, MAC address, DUID and Information Request while the DHCPv6 client is running. Require the DHCPv6 client to be stopped first instead of always unconditionally restarting it if the caller calls sd_dhcp6_client_start() more than once. With this change, handling of for example incoming Router Advertisments becomes much easier. --- src/libsystemd-network/sd-dhcp6-client.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 2367509693..1ab3640c6b 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -125,6 +125,8 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) { assert_return(client, -EINVAL); assert_return(interface_index >= -1, -EINVAL); + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + client->index = interface_index; return 0; @@ -140,6 +142,8 @@ int sd_dhcp6_client_set_mac( assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); assert_return(arp_type > 0, -EINVAL); + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); else if (arp_type == ARPHRD_INFINIBAND) @@ -173,6 +177,8 @@ int sd_dhcp6_client_set_duid( assert_return(duid, -EINVAL); assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL); + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + switch (type) { case DHCP6_DUID_LLT: if (duid_len <= sizeof(client->duid.llt)) @@ -205,6 +211,8 @@ int sd_dhcp6_client_set_duid( int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, bool enabled) { assert_return(client, -EINVAL); + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + client->information_request = enabled; return 0; @@ -1126,6 +1134,9 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { assert_return(client->event, -EINVAL); assert_return(client->index > 0, -EINVAL); + if (!IN_SET(client->state, DHCP6_STATE_STOPPED)) + return -EALREADY; + r = client_reset(client); if (r < 0) return r; -- cgit v1.2.3 From 44598572da523f66872f593be7139b9a0f97deee Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Wed, 23 Sep 2015 14:00:03 +0300 Subject: test-dhcp6-client: Update test case due to changed semantics Update the test case to stop the ongoing Information Request exchange before unsetting its state. To keep the test case callback verification simpler, temporarily unset the callback function before stopping. --- src/libsystemd-network/test-dhcp6-client.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 178f536337..0c131a9897 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -581,7 +581,11 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event, if (verbose) printf(" got DHCPv6 event %d\n", event); + assert_se(sd_dhcp6_client_set_information_request(client, false) == -EBUSY); + assert_se(sd_dhcp6_client_set_callback(client, NULL, e) >= 0); + assert_se(sd_dhcp6_client_stop(client) >= 0); assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0); + assert_se(sd_dhcp6_client_set_callback(client, test_client_solicit_cb, e) >= 0); -- cgit v1.2.3 From e66040417b52be98d41ba1230f25dea65147e8ee Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Wed, 23 Sep 2015 14:10:26 +0300 Subject: sd-dhcp6-client: Properly handle DHCPv6 client restart after resume Whenever a Router Advertisement is received, dhcp6_configure() will be called. A Router Advertisment can also instruct DHCPv6 to start acquiring IPv6 addresses in manged mode, if it previously was handling only other information. As an Router Advertisment is also received after the DHCPv6 client has resumed from a suspend, fix the function not to assume DHCPv6 is currently running, but instead try to restart it. Handle sd_dhcp6_start() returning -EALREADY indicating that the DHCPv6 client was already running. Collect all client unrefs in one place to unclutter the error handling. Fixes https://github.com/systemd/systemd/issues/963 --- src/network/networkd-dhcp6.c | 86 +++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index fca73b3154..1d85fef907 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -183,41 +183,42 @@ static int dhcp6_configure(Link *link, int event) { bool information_request; assert_return(link, -EINVAL); + assert_return(IN_SET(event, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT, + SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER, + SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED), -EINVAL); if (link->dhcp6_client) { - if (event != SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED) - return 0; - r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &information_request); if (r < 0) { log_link_warning(link, "Could not get DHCPv6 Information request setting: %s", - strerror(-r)); - link->dhcp6_client = - sd_dhcp6_client_unref(link->dhcp6_client); - return r; + strerror(-r)); + goto error; } - if (!information_request) - return r; + if (information_request && event != SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER) { + r = sd_dhcp6_client_stop(link->dhcp6_client); + if (r < 0) { + log_link_warning(link, "Could not stop DHCPv6 while setting Managed mode %s", + strerror(-r)); + goto error; + } + + r = sd_dhcp6_client_set_information_request(link->dhcp6_client, + false); + if (r < 0) { + log_link_warning(link, "Could not unset DHCPv6 Information request: %s", + strerror(-r)); + goto error; + } - r = sd_dhcp6_client_set_information_request(link->dhcp6_client, - false); - if (r < 0) { - log_link_warning(link, "Could not unset DHCPv6 Information request: %s", - strerror(-r)); - link->dhcp6_client = - sd_dhcp6_client_unref(link->dhcp6_client); - return r; } r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0) { - log_link_warning(link, "Could not restart DHCPv6 after enabling Information request: %s", - strerror(-r)); - link->dhcp6_client = - sd_dhcp6_client_unref(link->dhcp6_client); - return r; + if (r < 0 && r != -EALREADY) { + log_link_warning(link, "Could not restart DHCPv6: %s", + strerror(-r)); + goto error; } return r; @@ -225,49 +226,42 @@ static int dhcp6_configure(Link *link, int event) { r = sd_dhcp6_client_new(&link->dhcp6_client); if (r < 0) - return r; + goto error; r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0); - if (r < 0) { - link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); - return r; - } + if (r < 0) + goto error; r = sd_dhcp6_client_set_mac(link->dhcp6_client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER); - if (r < 0) { - link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); - return r; - } + if (r < 0) + goto error; r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex); - if (r < 0) { - link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); - return r; - } + if (r < 0) + goto error; r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler, link); - if (r < 0) { - link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); - return r; - } + if (r < 0) + goto error; if (event == SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER) { r = sd_dhcp6_client_set_information_request(link->dhcp6_client, true); - if (r < 0) { - link->dhcp6_client = - sd_dhcp6_client_unref(link->dhcp6_client); - return r; - } + if (r < 0) + goto error; } r = sd_dhcp6_client_start(link->dhcp6_client); if (r < 0) - link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); + goto error; + + return r; + error: + link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); return r; } -- cgit v1.2.3 From 18d29550b5fbc4b0de334b8212d05decdd131f1b Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Wed, 23 Sep 2015 14:52:03 +0300 Subject: networkd: Wait for DHCPv6 before announcing link configured Wait until DHCPv6 has acquired an address before announcing the link to be configured. Log the DHCPv6 lease lost event. --- src/network/networkd-dhcp6.c | 12 +++++++++++- src/network/networkd-link.c | 3 +++ src/network/networkd-link.h | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 1d85fef907..13105c7865 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -147,7 +147,9 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { case SD_DHCP6_CLIENT_EVENT_STOP: case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE: case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX: - log_link_debug(link, "DHCPv6 event %d", event); + log_link_warning(link, "DHCPv6 lease lost"); + + link->dhcp6_configured = false; break; case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE: @@ -165,6 +167,7 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { return; } + link->dhcp6_configured = true; break; default: @@ -176,6 +179,8 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { event); return; } + + link_client_handler(link); } static int dhcp6_configure(Link *link, int event) { @@ -187,6 +192,8 @@ static int dhcp6_configure(Link *link, int event) { SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED), -EINVAL); + link->dhcp6_configured = false; + if (link->dhcp6_client) { r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &information_request); @@ -221,6 +228,9 @@ static int dhcp6_configure(Link *link, int event) { goto error; } + if (r == -EALREADY) + link->dhcp6_configured = true; + return r; } diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 86f1c3bb1a..0a7e75c89c 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -504,6 +504,9 @@ void link_client_handler(Link *link) { if (link_dhcp4_enabled(link) && !link->dhcp4_configured) return; + if (link_dhcp6_enabled(link) && !link->dhcp6_configured) + return; + if (link->state != LINK_STATE_CONFIGURED) link_enter_configured(link); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index f588faf209..7b219c6854 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -91,6 +91,7 @@ struct Link { uint16_t original_mtu; unsigned dhcp4_messages; bool dhcp4_configured; + bool dhcp6_configured; sd_ipv4ll *ipv4ll; bool ipv4ll_address; -- cgit v1.2.3