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 ++++++ 3 files changed, 27 insertions(+), 11 deletions(-) (limited to 'src/libsystemd-network') 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); -- cgit v1.2.3-54-g00ecf