sd-dhcp6-client: bind to link-local address
authorTom Gundersen <teg@jklm.no>
Mon, 16 Nov 2015 16:43:08 +0000 (17:43 +0100)
committerTom Gundersen <teg@jklm.no>
Tue, 17 Nov 2015 13:17:41 +0000 (14:17 +0100)
This ensures that several DHCPv6 clients can run on separate interfaces
simultaneously.

src/libsystemd-network/dhcp6-network.c
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/test-dhcp6-client.c
src/network/networkd-address.c
src/network/networkd-link.c
src/network/networkd-link.h
src/systemd/sd-dhcp6-client.h

index ec601cb..fd2d60c 100644 (file)
 #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;
index a951879..801331d 100644 (file)
@@ -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;
 
index 17ed6d5..9e05fde 100644 (file)
@@ -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);
index c0562e5..1ce1f4d 100644 (file)
@@ -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;
                         }
index ba64473..01d5942 100644 (file)
@@ -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)) {
index b564bcb..aa2235b 100644 (file)
@@ -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);
index 9f0e928..0ca6c07 100644 (file)
@@ -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,