From 63348d13fae61fefcb29133bfae8371b33cf4b6d Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Mon, 16 Nov 2015 16:46:14 +0100 Subject: networkd: ndisc/dhcpv6 - handle starting running clients The clients may be triggered to be started repeatedly without being stopped first, simply swallow the error rather than failing the link. --- src/libsystemd-network/sd-dhcp6-client.c | 2 +- src/libsystemd-network/sd-ndisc.c | 2 +- src/network/networkd-link.c | 4 ++-- src/network/networkd-ndisc.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index a443bb3a7a..a9518791b3 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1137,7 +1137,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { assert_return(client->index > 0, -EINVAL); if (!IN_SET(client->state, DHCP6_STATE_STOPPED)) - return -EALREADY; + return -EBUSY; r = client_reset(client); if (r < 0) diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index 0881973e7f..6703d87bc4 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -628,7 +628,7 @@ int sd_ndisc_router_discovery_start(sd_ndisc *nd) { assert(nd->event); if (nd->state != NDISC_STATE_IDLE) - return -EINVAL; + return -EBUSY; if (nd->index < 1) return -EINVAL; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index a415035887..f97600e2b5 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1264,7 +1264,7 @@ static int link_acquire_ipv6_conf(Link *link) { log_link_debug(link, "Acquiring DHCPv6 lease"); r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0) + if (r < 0 && r != -EBUSY) return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m"); } @@ -1274,7 +1274,7 @@ static int link_acquire_ipv6_conf(Link *link) { log_link_debug(link, "Discovering IPv6 routers"); r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery); - if (r < 0) + if (r < 0 && r != -EBUSY) return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m"); } diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 126f9c0fe9..37c6ec2049 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -159,7 +159,7 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a dhcp6_request_address(link); r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0 && r != -EALREADY) + if (r < 0 && r != -EBUSY) log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m"); } @@ -205,7 +205,7 @@ static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { dhcp6_request_address(link); r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0 && r != -EALREADY) + if (r < 0 && r != -EBUSY) log_link_warning_errno(link, r, "Starting DHCPv6 client after NDisc timeout failed: %m"); break; case SD_NDISC_EVENT_STOP: -- cgit v1.2.3-54-g00ecf From 62379e884e8a30cabbad994095c76e2f44b8f425 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Mon, 16 Nov 2015 16:47:18 +0100 Subject: networkd: ndisc - always configure dhcp6 client The ndisc client may trigger the dhcpv6 client to be started (this is the common case), so we should allocate the dhcpv6 client whenever we allocate the ndisc one. --- src/network/networkd-dhcp6.c | 3 +++ src/network/networkd-link.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index f83ff54369..e67e51f7ef 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -211,6 +211,9 @@ int dhcp6_configure(Link *link) { assert(link); + if (link->dhcp6_client) + return 0; + r = sd_dhcp6_client_new(&client); if (r < 0) return r; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index f97600e2b5..ba64473cd3 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2089,7 +2089,8 @@ static int link_configure(Link *link) { return r; } - if (link_dhcp6_enabled(link)) { + if (link_dhcp6_enabled(link) || + link_ipv6_accept_ra_enabled(link)) { r = dhcp6_configure(link); if (r < 0) return r; -- cgit v1.2.3-54-g00ecf From 6506063f8f68f589040cfcc138d3f61269fb2a63 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Mon, 16 Nov 2015 19:05:27 +0100 Subject: sd-dhcp6-client: allow multiple clients on host We need to enable SO_REUSEADDR in order for several sockets to be allowed to bind to the same port (even on different links). --- src/libsystemd-network/dhcp6-network.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c index 318ee9c4b4..ec601cbc6a 100644 --- a/src/libsystemd-network/dhcp6-network.c +++ b/src/libsystemd-network/dhcp6-network.c @@ -45,16 +45,13 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { int r, off = 0, on = 1; if (local_address) - memcpy(&src.in6.sin6_addr, local_address, - sizeof(src.in6.sin6_addr)); + src.in6.sin6_addr = *local_address; - s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, - IPPROTO_UDP); + s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP); if (s < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &pktinfo, - sizeof(pktinfo)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &pktinfo, sizeof(pktinfo)); if (r < 0) return -errno; @@ -66,6 +63,10 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { if (r < 0) return -errno; + r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (r < 0) + return -errno; + r = bind(s, &src.sa, sizeof(src.in6)); if (r < 0) return -errno; -- cgit v1.2.3-54-g00ecf From fb84d8966e5ce49b0c705768bb4da4956c4e529a Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 17 Nov 2015 13:14:36 +0100 Subject: networkd: ndisc - fix token support Fixes CID#1338680. Thanks to Thomas Andersen. --- src/network/networkd-ndisc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 37c6ec2049..a2efac78f1 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -75,7 +75,7 @@ static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr address->family = AF_INET6; address->in_addr.in6 = *prefix; if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0) - memcpy(&address->in_addr.in6 + 8, &link->network->ipv6_token + 8, 8); + memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8); else { /* see RFC4291 section 2.5.1 */ address->in_addr.in6.__in6_u.__u6_addr8[8] = link->mac.ether_addr_octet[0]; -- cgit v1.2.3-54-g00ecf From c601ebf79f0c54be14d3c16f0f484c0335cdeec4 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Mon, 16 Nov 2015 17:43:08 +0100 Subject: sd-dhcp6-client: bind to link-local address This ensures that several DHCPv6 clients can run on separate interfaces simultaneously. --- src/libsystemd-network/dhcp6-network.c | 15 +++++---------- src/libsystemd-network/sd-dhcp6-client.c | 17 ++++++++++++++++- src/libsystemd-network/test-dhcp6-client.c | 6 ++++++ src/network/networkd-address.c | 6 +++--- src/network/networkd-link.c | 13 +++++++++---- src/network/networkd-link.h | 4 ++-- src/systemd/sd-dhcp6-client.h | 1 + 7 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c index ec601cbc6a..fd2d60c9d5 100644 --- a/src/libsystemd-network/dhcp6-network.c +++ b/src/libsystemd-network/dhcp6-network.c @@ -33,28 +33,23 @@ #include "socket-util.h" int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { - struct in6_pktinfo pktinfo = { - .ipi6_ifindex = index, - }; union sockaddr_union src = { .in6.sin6_family = AF_INET6, .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT), - .in6.sin6_addr = IN6ADDR_ANY_INIT, + .in6.sin6_scope_id = index, }; _cleanup_close_ int s = -1; int r, off = 0, on = 1; - if (local_address) - src.in6.sin6_addr = *local_address; + assert(index > 0); + assert(local_address); + + src.in6.sin6_addr = *local_address; s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP); if (s < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &pktinfo, sizeof(pktinfo)); - if (r < 0) - return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); if (r < 0) return -errno; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index a9518791b3..801331d270 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -32,6 +32,7 @@ #include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" #include "fd-util.h" +#include "in-addr-util.h" #include "network-internal.h" #include "random-util.h" #include "string-table.h" @@ -46,6 +47,7 @@ struct sd_dhcp6_client { sd_event *event; int event_priority; int index; + struct in6_addr local_address; uint8_t mac_addr[MAX_MAC_ADDR_LEN]; size_t mac_addr_len; uint16_t arp_type; @@ -133,6 +135,18 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) { return 0; } +int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address) { + assert_return(client, -EINVAL); + assert_return(local_address, -EINVAL); + assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL); + + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + + client->local_address = *local_address; + + return 0; +} + int sd_dhcp6_client_set_mac( sd_dhcp6_client *client, const uint8_t *addr, size_t addr_len, @@ -1135,6 +1149,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { assert_return(client, -EINVAL); assert_return(client->event, -EINVAL); assert_return(client->index > 0, -EINVAL); + assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL); if (!IN_SET(client->state, DHCP6_STATE_STOPPED)) return -EBUSY; @@ -1151,7 +1166,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { if (r < 0) return r; - r = dhcp6_network_bind_udp_socket(client->index, NULL); + r = dhcp6_network_bind_udp_socket(client->index, &client->local_address); if (r < 0) return r; diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 17ed6d58f3..9e05fde8f6 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -562,6 +562,7 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event, sd_event *e = userdata; sd_dhcp6_lease *lease; struct in6_addr *addrs; + struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; char **domains; assert_se(e); @@ -590,6 +591,8 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event, assert_se(sd_dhcp6_client_set_callback(client, test_client_solicit_cb, e) >= 0); + assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); + assert_se(sd_dhcp6_client_start(client) >= 0); } @@ -701,6 +704,7 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { 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; if (verbose) @@ -729,6 +733,8 @@ static int test_client_solicit(sd_event *e) { time_now + 2 * USEC_PER_SEC, 0, test_hangcheck, NULL) >= 0); + assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); + assert_se(sd_dhcp6_client_start(client) >= 0); sd_event_loop(e); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index c0562e5788..1ce1f4d8d6 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -347,9 +347,9 @@ int address_update(Address *address, unsigned char flags, unsigned char scope, s link_check_ready(address->link); if (address->family == AF_INET6 && - in_addr_is_link_local(AF_INET6, &address->in_addr) && - !address->link->ipv6ll_address) { - r = link_ipv6ll_gained(address->link); + in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 && + in_addr_is_null(AF_INET6, (const union in_addr_union*) &address->link->ipv6ll_address) > 0) { + r = link_ipv6ll_gained(address->link, &address->in_addr.in6); if (r < 0) return r; } diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index ba64473cd3..01d5942ce5 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -615,7 +615,7 @@ void link_check_ready(Link *link) { return; if (link_ipv6ll_enabled(link)) - if (!link->ipv6ll_address) + if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) > 0) return; if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) && @@ -1260,9 +1260,14 @@ static int link_acquire_ipv6_conf(Link *link) { if (link_dhcp6_enabled(link)) { assert(link->dhcp6_client); + assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0); log_link_debug(link, "Acquiring DHCPv6 lease"); + r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address); + if (r < 0 && r != -EBUSY) + return log_link_warning_errno(link, r, "Could not set IPv6LL address in DHCP client: %m"); + r = sd_dhcp6_client_start(link->dhcp6_client); if (r < 0 && r != -EBUSY) return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m"); @@ -2122,7 +2127,7 @@ static int link_configure(Link *link) { if (r < 0) return r; - if (link->ipv6ll_address) { + if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) { r = link_acquire_ipv6_conf(link); if (r < 0) return r; @@ -2473,14 +2478,14 @@ failed: return r; } -int link_ipv6ll_gained(Link *link) { +int link_ipv6ll_gained(Link *link, const struct in6_addr *address) { int r; assert(link); log_link_info(link, "Gained IPv6LL"); - link->ipv6ll_address = true; + link->ipv6ll_address = *address; link_check_ready(link); if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) { diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index b564bcbca0..aa2235b11d 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -69,6 +69,7 @@ struct Link { char *ifname; char *state_file; struct ether_addr mac; + struct in6_addr ipv6ll_address; uint32_t mtu; struct udev_device *udev_device; @@ -101,7 +102,6 @@ struct Link { sd_ipv4ll *ipv4ll; bool ipv4ll_address:1; bool ipv4ll_route:1; - bool ipv6ll_address:1; bool static_configured; @@ -144,7 +144,7 @@ int link_save(Link *link); int link_carrier_reset(Link *link); bool link_has_carrier(Link *link); -int link_ipv6ll_gained(Link *link); +int link_ipv6ll_gained(Link *link, const struct in6_addr *address); int link_set_mtu(Link *link, uint32_t mtu); int link_set_hostname(Link *link, const char *hostname); diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 9f0e92806e..0ca6c07de4 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -49,6 +49,7 @@ int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, sd_dhcp6_client_cb_t cb, void *userdata); int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index); +int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address); int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr, size_t addr_len, uint16_t arp_type); int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid, -- cgit v1.2.3-54-g00ecf