diff options
Diffstat (limited to 'src/libsystemd-network/sd-dhcp6-client.c')
-rw-r--r-- | src/libsystemd-network/sd-dhcp6-client.c | 79 |
1 files changed, 77 insertions, 2 deletions
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 8718324c33..15d174beee 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -22,12 +22,17 @@ #include <errno.h> #include <string.h> +#include "udev.h" +#include "udev-util.h" +#include "virt.h" #include "siphash24.h" #include "util.h" #include "refcnt.h" +#include "network-internal.h" #include "sd-dhcp6-client.h" #include "dhcp6-protocol.h" +#include "dhcp6-internal.h" #define SYSTEMD_PEN 43793 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) @@ -40,6 +45,7 @@ struct sd_dhcp6_client { int event_priority; int index; struct ether_addr mac_addr; + DHCP6IA ia_na; sd_dhcp6_client_cb_t cb; void *userdata; @@ -98,6 +104,11 @@ static int client_initialize(sd_dhcp6_client *client) { assert_return(client, -EINVAL); + 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->state = DHCP6_STATE_STOPPED; return 0; @@ -113,6 +124,65 @@ static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) { return client; } +static int client_ensure_iaid(sd_dhcp6_client *client) { + const char *name = NULL; + uint64_t id; + + assert(client); + + if (client->ia_na.id) + return 0; + + if (detect_container(NULL) <= 0) { + /* not in a container, udev will be around */ + _cleanup_udev_unref_ struct udev *udev; + _cleanup_udev_device_unref_ struct udev_device *device; + char ifindex_str[2 + DECIMAL_STR_MAX(int)]; + + udev = udev_new(); + if (!udev) + return -ENOMEM; + + sprintf(ifindex_str, "n%d", client->index); + device = udev_device_new_from_device_id(udev, ifindex_str); + if (!device) + return -errno; + + if (udev_device_get_is_initialized(device) <= 0) + /* not yet ready */ + return -EBUSY; + + name = net_get_name(device); + } + + if (name) + siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes); + else + /* fall back to mac address if no predictable name available */ + siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN, + HASH_KEY.bytes); + + /* fold into 32 bits */ + client->ia_na.id = (id & 0xffffffff) ^ (id >> 32); + + return 0; +} + +static int client_start(sd_dhcp6_client *client) +{ + int r; + + assert_return(client, -EINVAL); + assert_return(client->event, -EINVAL); + assert_return(client->index > 0, -EINVAL); + + r = client_ensure_iaid(client); + if (r < 0) + return r; + + return 0; +} + int sd_dhcp6_client_stop(sd_dhcp6_client *client) { client_stop(client, DHCP6_EVENT_STOP); @@ -128,7 +198,11 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) assert_return(client->event, -EINVAL); assert_return(client->index > 0, -EINVAL); - return r; + r = client_initialize(client); + if (r < 0) + return r; + + return client_start(client); } int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, @@ -176,7 +250,6 @@ sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) { sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { if (client && REFCNT_DEC(client->n_ref) <= 0) { - client_initialize(client); sd_dhcp6_client_detach_event(client); @@ -206,6 +279,8 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) client->n_ref = REFCNT_INIT; + client->ia_na.type = DHCP6_OPTION_IA_NA; + client->index = -1; /* initialize DUID */ |