#include <stdio.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
+#include <netinet/ether.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include "util.h"
#include "list.h"
#include "refcnt.h"
+#include "async.h"
#include "dhcp-protocol.h"
#include "dhcp-internal.h"
assert_return(client, -EINVAL);
assert_return (IN_SET(client->state, DHCP_STATE_INIT,
DHCP_STATE_STOPPED), -EBUSY);
- assert_return(interface_index >= -1, -EINVAL);
+ assert_return(interface_index > 0, -EINVAL);
client->index = interface_index;
client->receive_message =
sd_event_source_unref(client->receive_message);
- client->fd = safe_close(client->fd);
+ client->fd = asynchronous_close(client->fd);
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
static sd_dhcp_client *client_stop(sd_dhcp_client *client, int error) {
assert_return(client, NULL);
- log_dhcp_client(client, "STOPPED %d", error);
+ if (error < 0)
+ log_dhcp_client(client, "STOPPED: %s", strerror(-error));
+ else {
+ switch(error) {
+ case DHCP_EVENT_STOP:
+ log_dhcp_client(client, "STOPPED");
+ break;
+ case DHCP_EVENT_NO_LEASE:
+ log_dhcp_client(client, "STOPPED: No lease");
+ break;
+ default:
+ log_dhcp_client(client, "STOPPED: Unknown reason");
+ break;
+ }
+ }
client = client_notify(client, error);
return client;
}
-static int client_message_init(sd_dhcp_client *client, DHCPMessage *message,
- uint8_t type, uint8_t **opt, size_t *optlen) {
+static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
+ uint8_t type, size_t *_optlen, size_t *_optoffset) {
+ _cleanup_free_ DHCPPacket *packet;
+ size_t optlen, optoffset, size;
be16_t max_size;
int r;
assert(client);
assert(client->secs);
- assert(message);
- assert(opt);
- assert(optlen);
+ assert(ret);
+ assert(_optlen);
+ assert(_optoffset);
assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
- r = dhcp_message_init(message, BOOTREQUEST, client->xid, type, opt,
- optlen);
+ optlen = DHCP_MIN_OPTIONS_SIZE;
+ size = sizeof(DHCPPacket) + optlen;
+
+ packet = malloc0(size);
+ if (!packet)
+ return -ENOMEM;
+
+ r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
+ optlen, &optoffset);
if (r < 0)
return r;
/* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
refuse to issue an DHCP lease if 'secs' is set to zero */
- message->secs = htobe16(client->secs);
+ packet->dhcp.secs = htobe16(client->secs);
+
+ /* RFC2132 section 4.1
+ A client that cannot receive unicast IP datagrams until its protocol
+ software has been configured with an IP address SHOULD set the
+ BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
+ DHCPREQUEST messages that client sends. The BROADCAST bit will
+ provide a hint to the DHCP server and BOOTP relay agent to broadcast
+ any messages to the client on the client's subnet. */
+ packet->dhcp.flags = htobe16(0x8000);
/* RFC2132 section 4.1.1:
The client MUST include its hardware address in the ’chaddr’ field, if
necessary for delivery of DHCP reply messages.
*/
- memcpy(&message->chaddr, &client->client_id.mac_addr, ETH_ALEN);
+ memcpy(&packet->dhcp.chaddr, &client->client_id.mac_addr, ETH_ALEN);
/* Some DHCP servers will refuse to issue an DHCP lease if the Client
Identifier option is not set */
- r = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
+ r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
+ DHCP_OPTION_CLIENT_IDENTIFIER,
sizeof(client->client_id), &client->client_id);
if (r < 0)
return r;
it MUST include that list in any subsequent DHCPREQUEST
messages.
*/
- r = dhcp_option_append(opt, optlen,
+ r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
DHCP_OPTION_PARAMETER_REQUEST_LIST,
- client->req_opts_size,
- client->req_opts);
+ client->req_opts_size, client->req_opts);
if (r < 0)
return r;
than the defined default size unless the Maximum Messge Size option
is explicitely set
*/
- max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE +
- DHCP_MIN_OPTIONS_SIZE);
- r = dhcp_option_append(opt, optlen,
+ max_size = htobe16(size);
+ r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
2, &max_size);
if (r < 0)
return r;
+ *_optlen = optlen;
+ *_optoffset = optoffset;
+ *ret = packet;
+ packet = NULL;
+
return 0;
}
static int client_send_discover(sd_dhcp_client *client) {
_cleanup_free_ DHCPPacket *discover = NULL;
- size_t optlen, len;
- uint8_t *opt;
+ size_t optoffset, optlen;
usec_t time_now;
int r;
* must always be strictly positive to deal with broken servers */
client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
- optlen = DHCP_MIN_OPTIONS_SIZE;
- len = sizeof(DHCPPacket) + optlen;
-
- discover = malloc0(len);
- if (!discover)
- return -ENOMEM;
-
- r = client_message_init(client, &discover->dhcp, DHCP_DISCOVER,
- &opt, &optlen);
+ r = client_message_init(client, &discover, DHCP_DISCOVER,
+ &optlen, &optoffset);
if (r < 0)
return r;
option to suggest the lease time it would like.
*/
if (client->last_addr != INADDR_ANY) {
- r = dhcp_option_append(&opt, &optlen,
- DHCP_OPTION_REQUESTED_IP_ADDRESS,
- 4, &client->last_addr);
+ r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ 4, &client->last_addr);
if (r < 0)
return r;
}
- r = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
- if (r < 0)
- return r;
+ r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ DHCP_OPTION_END, 0, NULL);
/* We currently ignore:
The client SHOULD wait a random time between one and ten seconds to
desynchronize the use of DHCP at startup.
*/
- r = dhcp_client_send_raw(client, discover, len - optlen);
+ r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
if (r < 0)
return r;
static int client_send_request(sd_dhcp_client *client) {
_cleanup_free_ DHCPPacket *request;
- size_t optlen, len;
- uint8_t *opt;
+ size_t optoffset, optlen;
int r;
- optlen = DHCP_MIN_OPTIONS_SIZE;
- len = sizeof(DHCPPacket) + optlen;
-
- request = malloc0(len);
- if (!request)
- return -ENOMEM;
-
- r = client_message_init(client, &request->dhcp, DHCP_REQUEST, &opt,
- &optlen);
+ r = client_message_init(client, &request, DHCP_REQUEST,
+ &optlen, &optoffset);
if (r < 0)
return r;
filled in with the yiaddr value from the chosen DHCPOFFER.
*/
- r = dhcp_option_append(&opt, &optlen,
+ r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
DHCP_OPTION_SERVER_IDENTIFIER,
4, &client->lease->server_address);
if (r < 0)
return r;
- r = dhcp_option_append(&opt, &optlen,
+ r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
DHCP_OPTION_REQUESTED_IP_ADDRESS,
4, &client->lease->address);
if (r < 0)
option MUST be filled in with client’s notion of its previously
assigned address. ’ciaddr’ MUST be zero.
*/
- r = dhcp_option_append(&opt, &optlen,
+ r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
DHCP_OPTION_REQUESTED_IP_ADDRESS,
4, &client->last_addr);
if (r < 0)
return -EINVAL;
}
- r = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
+ r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+ DHCP_OPTION_END, 0, NULL);
if (r < 0)
return r;
client->lease->server_address,
DHCP_PORT_SERVER,
&request->dhcp,
- len - optlen - DHCP_IP_UDP_SIZE);
+ sizeof(DHCPMessage) + optoffset);
} else {
- r = dhcp_client_send_raw(client, request, len - optlen);
+ r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
}
if (r < 0)
return r;
- log_dhcp_client(client, "REQUEST");
+ switch (client->state) {
+ case DHCP_STATE_REQUESTING:
+ log_dhcp_client(client, "REQUEST (requesting)");
+ break;
+ case DHCP_STATE_INIT_REBOOT:
+ log_dhcp_client(client, "REQUEST (init-reboot)");
+ break;
+ case DHCP_STATE_RENEWING:
+ log_dhcp_client(client, "REQUEST (renewing)");
+ break;
+ case DHCP_STATE_REBINDING:
+ log_dhcp_client(client, "REQUEST (rebinding)");
+ break;
+ default:
+ log_dhcp_client(client, "REQUEST (invalid)");
+ break;
+ }
return 0;
}
if (r < 0)
goto error;
- return client_start(client);
-
- break;
+ r = client_start(client);
+ if (r < 0)
+ goto error;
+ else {
+ log_dhcp_client(client, "REBOOTED");
+ return 0;
+ }
case DHCP_STATE_INIT:
case DHCP_STATE_INIT_REBOOT:
client->secs = 0;
}
- log_dhcp_client(client, "STARTED");
-
return client_initialize_events(client, client_receive_message_raw);
}
int r;
client->receive_message = sd_event_source_unref(client->receive_message);
- client->fd = safe_close(client->fd);
+ client->fd = asynchronous_close(client->fd);
client->state = DHCP_STATE_REBINDING;
client->attempt = 1;
}
client->fd = r;
- log_dhcp_client(client, "TIMEOUT T2");
-
return client_initialize_events(client, client_receive_message_raw);
}
client->state = DHCP_STATE_RENEWING;
client->attempt = 1;
- r = dhcp_network_bind_udp_socket(client->index,
- client->lease->address,
+ r = dhcp_network_bind_udp_socket(client->lease->address,
DHCP_PORT_CLIENT);
if (r < 0) {
client_stop(client, r);
client->fd = r;
- log_dhcp_client(client, "TIMEOUT T1");
-
return client_initialize_events(client, client_receive_message_udp);
}
}
}
+ sd_dhcp_lease_unref(client->lease);
client->lease = lease;
lease = NULL;
r = client_start(client);
if (r < 0)
goto error;
+
+ log_dhcp_client(client, "REBOOTED");
}
goto error;
client->receive_message =
sd_event_source_unref(client->receive_message);
- client->fd = safe_close(client->fd);
+ client->fd = asynchronous_close(client->fd);
} else if (r == -ENOMSG)
/* invalid message, let's ignore it */
return 0;
assert(client);
r = ioctl(fd, FIONREAD, &buflen);
- if (r < 0 || buflen <= 0)
- buflen = sizeof(DHCPMessage) + DHCP_MIN_OPTIONS_SIZE;
+ if (r < 0)
+ return r;
+
+ if (buflen < 0)
+ /* this can't be right */
+ return -EIO;
message = malloc0(buflen);
if (!message)
len = read(fd, message, buflen);
if (len < 0) {
log_dhcp_client(client, "could not receive message from UDP "
- "socket: %s", strerror(errno));
+ "socket: %m");
return 0;
} else if ((size_t)len < sizeof(DHCPMessage))
return 0;
assert(client);
r = ioctl(fd, FIONREAD, &buflen);
- if (r < 0 || buflen <= 0)
- buflen = sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE;
+ if (r < 0)
+ return r;
+
+ if (buflen < 0)
+ /* this can't be right */
+ return -EIO;
packet = malloc0(buflen);
if (!packet)
len = recvmsg(fd, &msg, 0);
if (len < 0) {
log_dhcp_client(client, "could not receive message from raw "
- "socket: %s", strerror(errno));
+ "socket: %m");
return 0;
} else if ((size_t)len < sizeof(DHCPPacket))
return 0;
if (client->last_addr)
client->state = DHCP_STATE_INIT_REBOOT;
- return client_start(client);
+ r = client_start(client);
+ if (r >= 0)
+ log_dhcp_client(client, "STARTED on ifindex %u with address %s",
+ client->index,
+ ether_ntoa(&client->client_id.mac_addr));
+
+ return r;
}
int sd_dhcp_client_stop(sd_dhcp_client *client) {
sd_dhcp_client_detach_event(client);
+ sd_dhcp_lease_unref(client->lease);
+
free(client->req_opts);
free(client);