#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <linux/if_infiniband.h>
#include "udev.h"
#include "udev-util.h"
-#include "virt.h"
-#include "siphash24.h"
#include "util.h"
#include "refcnt.h"
#include "dhcp6-protocol.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
+#include "dhcp-identifier.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)
-
-#define MAX_DUID_LEN 32
+#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
struct sd_dhcp6_client {
RefCount n_ref;
sd_event *event;
int event_priority;
int index;
- struct ether_addr mac_addr;
+ uint8_t mac_addr[MAX_MAC_ADDR_LEN];
+ size_t mac_addr_len;
+ uint16_t arp_type;
DHCP6IA ia_na;
be32_t transaction_id;
usec_t transaction_start;
struct sd_dhcp6_lease *lease;
int fd;
+ bool information_request;
be16_t *req_opts;
size_t req_opts_allocated;
size_t req_opts_len;
sd_event_source *timeout_resend_expire;
sd_dhcp6_client_cb_t cb;
void *userdata;
- uint8_t duid[MAX_DUID_LEN];
+ struct duid duid;
size_t duid_len;
};
return 0;
}
-int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
- const struct ether_addr *mac_addr)
+int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
+ size_t addr_len, uint16_t arp_type)
{
assert_return(client, -EINVAL);
-
- if (mac_addr)
- memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
+ assert_return(addr, -EINVAL);
+ assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
+ assert_return(arp_type > 0, -EINVAL);
+
+ if (arp_type == ARPHRD_ETHER)
+ assert_return(addr_len == ETH_ALEN, -EINVAL);
+ else if (arp_type == ARPHRD_INFINIBAND)
+ assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
else
- memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
+ return -EINVAL;
+
+ if (client->mac_addr_len == addr_len &&
+ memcmp(&client->mac_addr, addr, addr_len) == 0)
+ return 0;
+
+ memcpy(&client->mac_addr, addr, addr_len);
+ client->mac_addr_len = addr_len;
+ client->arp_type = arp_type;
return 0;
}
-int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint8_t *duid,
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
size_t duid_len)
{
assert_return(client, -EINVAL);
assert_return(duid, -EINVAL);
assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
- memcpy(&client->duid, duid, duid_len);
- client->duid_len = duid_len;
+ switch (type) {
+ case DHCP6_DUID_LLT:
+ if (duid_len <= sizeof(client->duid.llt))
+ return -EINVAL;
+ break;
+ case DHCP6_DUID_EN:
+ if (duid_len != sizeof(client->duid.en))
+ return -EINVAL;
+ break;
+ case DHCP6_DUID_LL:
+ if (duid_len <= sizeof(client->duid.ll))
+ return -EINVAL;
+ break;
+ case DHCP6_DUID_UUID:
+ if (duid_len != sizeof(client->duid.uuid))
+ return -EINVAL;
+ break;
+ default:
+ /* accept unknown type in order to be forward compatible */
+ break;
+ }
+
+ client->duid.type = htobe16(type);
+ memcpy(&client->duid.raw.data, duid, duid_len);
+ client->duid_len = duid_len + sizeof(client->duid.type);
+
+ return 0;
+}
+
+int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
+ bool enabled) {
+ assert_return(client, -EINVAL);
+
+ client->information_request = enabled;
+
+ return 0;
+}
+
+int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
+ bool *enabled) {
+ assert_return(client, -EINVAL);
+ assert_return(enabled, -EINVAL);
+
+ *enabled = client->information_request;
return 0;
}
message->transaction_id = client->transaction_id;
switch(client->state) {
+ case DHCP6_STATE_INFORMATION_REQUEST:
+ message->type = DHCP6_INFORMATION_REQUEST;
+
+ break;
+
case DHCP6_STATE_SOLICITATION:
message->type = DHCP6_SOLICIT;
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
switch (client->state) {
+ case DHCP6_STATE_INFORMATION_REQUEST:
+ init_retransmit_time = DHCP6_INF_TIMEOUT;
+ max_retransmit_time = DHCP6_INF_MAX_RT;
+
+ break;
+
case DHCP6_STATE_SOLICITATION:
if (client->retransmit_count && client->lease) {
if (r < 0)
goto error;
- r = sd_event_source_set_name(client->timeout_resend,
- "dhcp6-resend-timer");
+ r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer");
if (r < 0)
goto error;
if (r < 0)
goto error;
- r = sd_event_source_set_name(client->timeout_resend_expire,
- "dhcp6-resend-expire-timer");
+ r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer");
if (r < 0)
goto error;
}
}
static int client_ensure_iaid(sd_dhcp6_client *client) {
- /* name is a pointer to memory in the udev_device struct, so must
- have the same scope */
- _cleanup_udev_device_unref_ struct udev_device *device = NULL;
- const char *name = NULL;
- uint64_t id;
+ int r;
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;
- 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);
+ r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
+ if (r < 0)
+ return r;
return 0;
}
break;
case DHCP6_OPTION_IA_NA:
+ if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
+ log_dhcp6_client(client, "Information request ignoring IA NA option");
+
+ break;
+ }
+
r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
&lease->ia);
if (r < 0 && r != -ENOMSG)
}
}
- if ((r < 0 && r != -ENOMSG) || !clientid) {
+ if (r == -ENOMSG)
+ r = 0;
+
+ if (r < 0 || !clientid) {
log_dhcp6_client(client, "%s has incomplete options",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
- r = dhcp6_lease_get_serverid(lease, &id, &id_len);
- if (r < 0)
- log_dhcp6_client(client, "%s has no server id",
- dhcp6_message_type_to_string(message->type));
+ if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
+ r = dhcp6_lease_get_serverid(lease, &id, &id_len);
+ if (r < 0)
+ log_dhcp6_client(client, "%s has no server id",
+ dhcp6_message_type_to_string(message->type));
+ }
return r;
}
return 0;
}
- if (client->lease)
+ if (client->lease) {
dhcp6_lease_clear_timers(&client->lease->ia);
+ client->lease = sd_dhcp6_lease_unref(client->lease);
+ }
- client->lease = sd_dhcp6_lease_unref(client->lease);
- client->lease = lease;
- lease = NULL;
+ if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
+ client->lease = lease;
+ lease = NULL;
+ }
return DHCP6_STATE_BOUND;
}
return r;
r = dhcp6_lease_get_preference(client->lease, &pref_lease);
- if (!client->lease || r < 0 || pref_advertise > pref_lease) {
+
+ if (r < 0 || pref_advertise > pref_lease) {
sd_dhcp6_lease_unref(client->lease);
client->lease = lease;
lease = NULL;
return 0;
switch (client->state) {
+ case DHCP6_STATE_INFORMATION_REQUEST:
+ r = client_receive_reply(client, message, len);
+ if (r < 0)
+ return 0;
+
+ client_notify(client, DHCP6_EVENT_INFORMATION_REQUEST);
+
+ client_start(client, DHCP6_STATE_STOPPED);
+
+ break;
+
case DHCP6_STATE_SOLICITATION:
r = client_receive_advertise(client, message, len);
switch (state) {
case DHCP6_STATE_STOPPED:
- case DHCP6_STATE_SOLICITATION:
-
- r = client_ensure_iaid(client);
- if (r < 0)
- return r;
-
- r = dhcp6_network_bind_udp_socket(client->index, NULL);
- if (r < 0)
- return r;
-
- client->fd = r;
-
- r = sd_event_add_io(client->event, &client->receive_message,
- client->fd, EPOLLIN, client_receive_message,
- client);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(client->receive_message,
- client->event_priority);
- if (r < 0)
- return r;
+ if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
+ client->state = DHCP6_STATE_STOPPED;
- r = sd_event_source_set_name(client->receive_message,
- "dhcp6-receive-message");
- if (r < 0)
- return r;
+ return 0;
+ }
+ /* fall through */
+ case DHCP6_STATE_SOLICITATION:
client->state = DHCP6_STATE_SOLICITATION;
break;
+ case DHCP6_STATE_INFORMATION_REQUEST:
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_RENEW:
case DHCP6_STATE_REBIND:
if (r < 0)
return r;
- r = sd_event_source_set_name(client->lease->ia.timeout_t1,
- "dhcp6-t1-timeout");
+ r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
if (r < 0)
return r;
if (r < 0)
return r;
- r = sd_event_source_set_name(client->lease->ia.timeout_t2,
- "dhcp6-t2-timeout");
+ r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
if (r < 0)
return r;
if (r < 0)
return r;
- r = sd_event_source_set_name(client->timeout_resend,
- "dhcp6-resend-timeout");
+ r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
if (r < 0)
return r;
int sd_dhcp6_client_start(sd_dhcp6_client *client)
{
int r = 0;
+ enum DHCP6State state = DHCP6_STATE_SOLICITATION;
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
if (r < 0)
return r;
- return client_start(client, DHCP6_STATE_SOLICITATION);
+ r = client_ensure_iaid(client);
+ if (r < 0)
+ return r;
+
+ r = dhcp6_network_bind_udp_socket(client->index, NULL);
+ if (r < 0)
+ return r;
+
+ client->fd = r;
+
+ r = sd_event_add_io(client->event, &client->receive_message,
+ client->fd, EPOLLIN, client_receive_message,
+ client);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_priority(client->receive_message,
+ client->event_priority);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_description(client->receive_message,
+ "dhcp6-receive-message");
+ if (r < 0)
+ goto error;
+
+ if (client->information_request)
+ state = DHCP6_STATE_INFORMATION_REQUEST;
+
+ log_dhcp6_client(client, "Started in %s mode",
+ client->information_request? "Information request":
+ "Managed");
+
+ return client_start(client, state);
+
+error:
+ client_reset(client);
+ return r;
}
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
}
sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
- if (client && REFCNT_DEC(client->n_ref) <= 0) {
+ if (client && REFCNT_DEC(client->n_ref) == 0) {
client_reset(client);
sd_dhcp6_client_detach_event(client);
return client;
}
-struct duid_en {
- uint16_t type; /* DHCP6_DUID_EN */
- uint32_t pen;
- uint8_t id[8];
-} _packed_;
-
int sd_dhcp6_client_new(sd_dhcp6_client **ret)
{
_cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
- struct duid_en *duid;
- sd_id128_t machine_id;
int r;
size_t t;
client->fd = -1;
/* initialize DUID */
- duid = (struct duid_en *) &client->duid;
- duid->type = htobe16(DHCP6_DUID_EN);
- duid->pen = htobe32(SYSTEMD_PEN);
-
- r = sd_id128_get_machine(&machine_id);
+ r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
if (r < 0)
return r;
- /* a bit of snake-oil perhaps, but no need to expose the machine-id
- directly */
- siphash24(duid->id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
- client->duid_len = sizeof (struct duid_en);
-
client->req_opts_len = ELEMENTSOF(default_req_opts);
client->req_opts = new0(be16_t, client->req_opts_len);