sd-dhcp6-client.c revision c4e8ceddccfbb14e475e74eb5c57ba32c3c6cf86
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2014 Intel Corporation. All rights reserved.
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <string.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 "network-internal.h"
#include "sd-dhcp6-client.h"
#include "dhcp6-protocol.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#define SYSTEMD_PEN 43793
/* RFC 3315 section 9.1:
* A DUID can be no more than 128 octets long (not including the type code).
*/
#define MAX_DUID_LEN 128
#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
struct sd_dhcp6_client {
enum DHCP6State state;
int event_priority;
int index;
struct sd_dhcp6_lease *lease;
int fd;
bool information_request;
void *userdata;
union {
struct {
struct {
struct {
struct {
struct {
} duid;
};
static const uint16_t default_req_opts[] = {
};
const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
[DHCP6_SOLICIT] = "SOLICIT",
[DHCP6_ADVERTISE] = "ADVERTISE",
[DHCP6_REQUEST] = "REQUEST",
[DHCP6_CONFIRM] = "CONFIRM",
[DHCP6_RENEW] = "RENEW",
[DHCP6_REBIND] = "REBIND",
[DHCP6_REPLY] = "REPLY",
[DHCP6_RELEASE] = "RELEASE",
[DHCP6_DECLINE] = "DECLINE",
[DHCP6_RECONFIGURE] = "RECONFIGURE",
[DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
[DHCP6_RELAY_FORW] = "RELAY-FORW",
[DHCP6_RELAY_REPL] = "RELAY-REPL",
};
const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
[DHCP6_STATUS_SUCCESS] = "Success",
[DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
[DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
[DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
[DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
[DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
};
#define DHCP6_CLIENT_DONT_DESTROY(client) \
_cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
{
return 0;
}
{
return 0;
}
{
if (arp_type == ARPHRD_ETHER)
else if (arp_type == ARPHRD_INFINIBAND)
else
return -EINVAL;
return 0;
return 0;
}
{
switch (type) {
case DHCP6_DUID_LLT:
return -EINVAL;
break;
case DHCP6_DUID_EN:
return -EINVAL;
break;
case DHCP6_DUID_LL:
return -EINVAL;
break;
case DHCP6_DUID_UUID:
return -EINVAL;
break;
default:
/* accept unknown type in order to be forward compatible */
break;
}
return 0;
}
bool enabled) {
return 0;
}
bool *enabled) {
return 0;
}
size_t t;
switch(option) {
case DHCP6_OPTION_DNS_SERVERS:
case DHCP6_OPTION_DOMAIN_LIST:
case DHCP6_OPTION_NTP_SERVER:
break;
default:
return -EINVAL;
}
for (t = 0; t < client->req_opts_len; t++)
return -EEXIST;
return -ENOMEM;
return 0;
}
return -ENOMSG;
return 0;
}
}
client->transaction_id = 0;
client->transaction_start = 0;
client->retransmit_time = 0;
client->retransmit_count = 0;
return 0;
}
}
struct in6_addr all_servers =
int r;
if (!message)
return -ENOMEM;
break;
case DHCP6_STATE_SOLICITATION:
if (r < 0)
return r;
if (r < 0)
return r;
break;
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_RENEW:
else
if (r < 0)
return r;
if (r < 0)
return r;
break;
case DHCP6_STATE_REBIND:
if (r < 0)
return r;
break;
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_BOUND:
return -EINVAL;
}
if (r < 0)
return r;
if (r < 0)
return r;
else
elapsed_time = 0xffff;
sizeof(elapsed_time), &elapsed_time);
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
void *userdata) {
assert_return(s, -EINVAL);
return 0;
}
void *userdata) {
assert_return(s, -EINVAL);
return 0;
}
void *userdata) {
enum DHCP6State state;
assert(s);
/* RFC 3315, section 18.1.4., says that "...the client may choose to
use a Solicit message to locate a new DHCP server..." */
if (state == DHCP6_STATE_REBIND)
return 0;
}
}
void *userdata) {
int r = 0;
char time_string[FORMAT_TIMESPAN_MAX];
assert(s);
break;
case DHCP6_STATE_SOLICITATION:
return 0;
}
break;
case DHCP6_STATE_REQUEST:
break;
case DHCP6_STATE_RENEW:
/* RFC 3315, section 18.1.3. says max retransmit duration will
be the remaining time until T2. Instead of setting MRD,
wait for T2 to trigger with the same end result */
break;
case DHCP6_STATE_REBIND:
if (!client->timeout_resend_expire) {
&expire);
if (r < 0) {
client_stop(client, r);
return 0;
}
}
break;
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_BOUND:
return 0;
}
if (max_retransmit_count &&
return 0;
}
if (r < 0)
goto error;
if (r >= 0)
if (!client->retransmit_time) {
} else {
if (max_retransmit_time &&
else
}
client->retransmit_time, 0));
client);
if (r < 0)
goto error;
if (r < 0)
goto error;
if (r < 0)
goto error;
if (r < 0)
goto error;
if (r < 0)
goto error;
if (r < 0)
goto error;
}
if (r < 0)
client_stop(client, r);
return 0;
}
/* name is a pointer to memory in the udev_device struct, so must
have the same scope */
return 0;
if (detect_container(NULL) <= 0) {
/* not in a container, udev will be around */
if (!udev)
return -ENOMEM;
if (!device)
return -errno;
if (udev_device_get_is_initialized(device) <= 0)
/* not yet ready */
return -EBUSY;
}
if (name)
else
/* fall back to mac address if no predictable name available */
/* fold into 32 bits */
return 0;
}
sd_dhcp6_lease *lease) {
int r;
bool clientid = false;
len -= sizeof(DHCP6Message);
&optval)) >= 0) {
switch (optcode) {
case DHCP6_OPTION_CLIENTID:
if (clientid) {
return -EINVAL;
}
return -EINVAL;
}
clientid = true;
break;
case DHCP6_OPTION_SERVERID:
if (r >= 0 && id) {
return -EINVAL;
}
if (r < 0)
return r;
break;
case DHCP6_OPTION_PREFERENCE:
if (optlen != 1)
return -EINVAL;
if (r < 0)
return r;
break;
case DHCP6_OPTION_STATUS_CODE:
if (optlen < 2)
return -EINVAL;
if (status) {
return -EINVAL;
}
break;
case DHCP6_OPTION_IA_NA:
break;
}
if (r < 0 && r != -ENOMSG)
return r;
if (r < 0)
return r;
return -EINVAL;
}
break;
if (r < 0)
return r;
break;
}
}
if (r == -ENOMSG)
r = 0;
if (r < 0 || !clientid) {
return -EINVAL;
}
if (r < 0)
}
return r;
}
{
int r;
bool rapid_commit;
return 0;
r = dhcp6_lease_new(&lease);
if (r < 0)
return -ENOMEM;
if (r < 0)
return r;
if (r < 0)
return r;
if (!rapid_commit)
return 0;
}
}
}
return DHCP6_STATE_BOUND;
}
int r;
return 0;
r = dhcp6_lease_new(&lease);
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0 || pref_advertise > pref_lease) {
r = 0;
}
r = DHCP6_STATE_REQUEST;
return r;
}
void *userdata) {
assert(s);
if (r < 0 || buflen <= 0)
if (!message)
return -ENOMEM;
return 0;
}
case DHCP6_SOLICIT:
case DHCP6_REQUEST:
case DHCP6_CONFIRM:
case DHCP6_RENEW:
case DHCP6_REBIND:
case DHCP6_RELEASE:
case DHCP6_DECLINE:
case DHCP6_RELAY_FORW:
case DHCP6_RELAY_REPL:
return 0;
case DHCP6_ADVERTISE:
case DHCP6_REPLY:
case DHCP6_RECONFIGURE:
break;
default:
return 0;
}
htobe32(0x00ffffff)))
return 0;
if (r < 0)
return 0;
break;
case DHCP6_STATE_SOLICITATION:
if (r == DHCP6_STATE_REQUEST) {
client_start(client, r);
break;
}
/* fall through for Soliciation Rapid Commit option check */
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_RENEW:
case DHCP6_STATE_REBIND:
if (r < 0)
return 0;
if (r == DHCP6_STATE_BOUND) {
if (r < 0) {
client_stop(client, r);
return 0;
}
}
break;
case DHCP6_STATE_BOUND:
break;
case DHCP6_STATE_STOPPED:
return 0;
}
if (r >= 0) {
}
return 0;
}
{
int r;
char time_string[FORMAT_TIMESPAN_MAX];
client->retransmit_time = 0;
client->retransmit_count = 0;
} else {
&time_now);
if (r < 0)
return r;
}
switch (state) {
case DHCP6_STATE_STOPPED:
return 0;
}
/* fall through */
case DHCP6_STATE_SOLICITATION:
break;
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_RENEW:
case DHCP6_STATE_REBIND:
break;
case DHCP6_STATE_BOUND:
return 0;
}
timeout, 0));
client);
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
timeout, 0));
client);
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
client);
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
{
return 0;
}
{
int r = 0;
r = client_reset(client);
if (r < 0)
return r;
r = client_ensure_iaid(client);
if (r < 0)
return r;
if (r < 0)
return r;
client);
if (r < 0)
goto error;
if (r < 0)
goto error;
"dhcp6-receive-message");
if (r < 0)
goto error;
if (client->information_request)
"Managed");
return r;
}
int priority)
{
int r;
if (event)
else {
if (r < 0)
return 0;
}
return 0;
}
return 0;
}
if (!client)
return NULL;
}
if (client)
return client;
}
return NULL;
}
return client;
}
{
int r;
size_t t;
if (!client)
return -ENOMEM;
/* initialize DUID */
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 */
return -ENOMEM;
for (t = 0; t < client->req_opts_len; t++)
return 0;
}