X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-network%2Fsd-dhcp6-client.c;h=15d174beee2c256561a3d60a5899a65254c227b3;hb=f12abb48fc510b8b349c05e35ba048134debaf25;hp=5063d4a4c5b9759add5dbac27ba78bbde4ac12e1;hpb=139b011ab81ccea1d51f09e0261a1c390115c6ff;p=elogind.git diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 5063d4a4c..15d174bee 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -22,11 +22,20 @@ #include #include +#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) struct sd_dhcp6_client { RefCount n_ref; @@ -36,8 +45,15 @@ 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; + + struct duid_en { + uint16_t type; /* DHCP6_DUID_EN */ + uint32_t pen; + uint8_t id[8]; + } _packed_ duid; }; int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, @@ -88,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; @@ -103,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); @@ -118,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, @@ -166,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); @@ -185,6 +268,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref); int sd_dhcp6_client_new(sd_dhcp6_client **ret) { _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL; + sd_id128_t machine_id; + int r; assert_return(ret, -EINVAL); @@ -194,8 +279,23 @@ 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 */ + client->duid.type = htobe16(DHCP6_DUID_EN); + client->duid.pen = htobe32(SYSTEMD_PEN); + + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + + /* a bit of snake-oil perhaps, but no need to expose the machine-id + directly */ + siphash24(client->duid.id, &machine_id, sizeof(machine_id), + HASH_KEY.bytes); + *ret = client; client = NULL;