#include "util.h"
#include "list.h"
+#include "refcnt.h"
#include "dhcp-protocol.h"
#include "dhcp-internal.h"
#include "sd-dhcp-client.h"
struct sd_dhcp_client {
+ RefCount n_ref;
+
DHCPState state;
sd_event *event;
int event_priority;
uint32_t revents, void *userdata);
static int client_receive_message_udp(sd_event_source *s, int fd,
uint32_t revents, void *userdata);
+static sd_dhcp_client *client_stop(sd_dhcp_client *client, int error);
int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
void *userdata) {
size_t i;
assert_return(client, -EINVAL);
- assert_return (client->state == DHCP_STATE_INIT, -EBUSY);
+ assert_return (IN_SET(client->state, DHCP_STATE_INIT,
+ DHCP_STATE_STOPPED), -EBUSY);
switch(option) {
case DHCP_OPTION_PAD:
int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
const struct in_addr *last_addr) {
assert_return(client, -EINVAL);
- assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
+ assert_return (IN_SET(client->state, DHCP_STATE_INIT,
+ DHCP_STATE_STOPPED), -EBUSY);
if (last_addr)
client->last_addr = last_addr->s_addr;
int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
assert_return(client, -EINVAL);
- assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
+ assert_return (IN_SET(client->state, DHCP_STATE_INIT,
+ DHCP_STATE_STOPPED), -EBUSY);
assert_return(interface_index >= -1, -EINVAL);
client->index = interface_index;
if (memcmp(&client->client_id.mac_addr, addr, ETH_ALEN) == 0)
return 0;
- if (client->state != DHCP_STATE_INIT) {
+ if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
log_dhcp_client(client, "Changing MAC address on running DHCP "
"client, restarting");
- sd_dhcp_client_stop(client);
need_restart = true;
+ client = client_stop(client, DHCP_EVENT_STOP);
}
+ if (!client)
+ return 0;
+
memcpy(&client->client_id.mac_addr, addr, ETH_ALEN);
client->client_id.type = 0x01;
- if (need_restart)
+ if (need_restart && client->state != DHCP_STATE_STOPPED)
sd_dhcp_client_start(client);
return 0;
return 0;
}
-static int client_notify(sd_dhcp_client *client, int event) {
- if (client->cb)
+static sd_dhcp_client *client_notify(sd_dhcp_client *client, int event) {
+ if (client->cb) {
+ client = sd_dhcp_client_ref(client);
client->cb(client, event, client->userdata);
+ client = sd_dhcp_client_unref(client);
+ }
- return 0;
+ return client;
}
static int client_initialize(sd_dhcp_client *client) {
return 0;
}
-static int client_stop(sd_dhcp_client *client, int error) {
- assert_return(client, -EINVAL);
+static sd_dhcp_client *client_stop(sd_dhcp_client *client, int error) {
+ assert_return(client, NULL);
- client_notify(client, error);
+ log_dhcp_client(client, "STOPPED %d", error);
- client_initialize(client);
+ client = client_notify(client, error);
- log_dhcp_client(client, "STOPPED");
+ if (client)
+ client_initialize(client);
- return 0;
+ return client;
}
static int client_message_init(sd_dhcp_client *client, DHCPMessage *message,
case DHCP_STATE_REBINDING:
break;
+
+ case DHCP_STATE_STOPPED:
+ return -EINVAL;
}
r = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
break;
+
+ case DHCP_STATE_STOPPED:
+ r = -EINVAL;
+ goto error;
}
next_timeout += (random_u32() & 0x1fffff);
case DHCP_STATE_BOUND:
break;
+
+ case DHCP_STATE_STOPPED:
+ r = -EINVAL;
+ goto error;
}
return 0;
client->xid = random_u32();
- r = dhcp_network_bind_raw_socket(client->index, &client->link);
+ r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
if (r < 0) {
client_stop(client, r);
log_dhcp_client(client, "EXPIRED");
- client_notify(client, DHCP_EVENT_EXPIRED);
+ client = client_notify(client, DHCP_EVENT_EXPIRED);
- /* start over as the lease was lost */
- client_initialize(client);
- client_start(client);
+ /* lease was lost, start over if not freed or stopped in callback */
+ if (client && client->state != DHCP_STATE_STOPPED) {
+ client_initialize(client);
+ client_start(client);
+ }
return 0;
}
client->state = DHCP_STATE_REBINDING;
client->attempt = 1;
- r = dhcp_network_bind_raw_socket(client->index, &client->link);
+ r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
if (r < 0) {
client_stop(client, r);
return 0;
if (r < 0)
goto error;
- if (notify_event)
- client_notify(client, notify_event);
+ if (notify_event) {
+ client = client_notify(client, notify_event);
+ if (!client ||
+ client->state == DHCP_STATE_STOPPED)
+ return 0;
+ }
client->receive_message =
sd_event_source_unref(client->receive_message);
case DHCP_STATE_BOUND:
break;
+
+ case DHCP_STATE_STOPPED:
+ r = -EINVAL;
+ goto error;
}
error:
if (r < 0 || r == DHCP_EVENT_NO_LEASE)
- return client_stop(client, r);
+ client_stop(client, r);
- return 0;
+ return r;
}
static int client_receive_message_udp(sd_event_source *s, int fd,
return 0;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
- if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) {
- struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg);
+ if (cmsg->cmsg_level == SOL_PACKET &&
+ cmsg->cmsg_type == PACKET_AUXDATA &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
+ struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
break;
}
int sd_dhcp_client_stop(sd_dhcp_client *client) {
- return client_stop(client, DHCP_EVENT_STOP);
+ assert_return(client, -EINVAL);
+
+ if (client_stop(client, DHCP_EVENT_STOP))
+ client->state = DHCP_STATE_STOPPED;
+
+ return 0;
}
int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
return client->event;
}
-void sd_dhcp_client_free(sd_dhcp_client *client) {
- if (!client)
- return;
+sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
+ if (client)
+ assert_se(REFCNT_INC(client->n_ref) >= 2);
- sd_dhcp_client_stop(client);
- sd_dhcp_client_detach_event(client);
+ return client;
+}
+
+sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
+ if (client && REFCNT_DEC(client->n_ref) <= 0) {
+ log_dhcp_client(client, "UNREF");
+
+ client_initialize(client);
+
+ client->receive_message =
+ sd_event_source_unref(client->receive_message);
+
+ sd_dhcp_client_detach_event(client);
+
+ free(client->req_opts);
+ free(client);
+
+ return NULL;
+ }
- free(client->req_opts);
- free(client);
+ return client;
}
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_free);
-#define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_freep)
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref);
+#define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_unrefp)
int sd_dhcp_client_new(sd_dhcp_client **ret) {
_cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
if (!client)
return -ENOMEM;
+ client->n_ref = REFCNT_INIT;
client->state = DHCP_STATE_INIT;
client->index = -1;
client->fd = -1;