861 lines
26 KiB
C
861 lines
26 KiB
C
/**
|
|
* @file BDHCPClientCore.c
|
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
|
*
|
|
* @section LICENSE
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the author nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <misc/byteorder.h>
|
|
#include <misc/minmax.h>
|
|
#include <misc/balloc.h>
|
|
#include <misc/bsize.h>
|
|
#include <misc/dhcp_proto.h>
|
|
#include <base/BLog.h>
|
|
|
|
#include <dhcpclient/BDHCPClientCore.h>
|
|
|
|
#include <generated/blog_channel_BDHCPClientCore.h>
|
|
|
|
#define RESET_TIMEOUT 4000
|
|
#define REQUEST_TIMEOUT 3000
|
|
#define RENEW_REQUEST_TIMEOUT 20000
|
|
#define MAX_REQUESTS 4
|
|
#define RENEW_TIMEOUT(lease) ((btime_t)500 * (lease))
|
|
#define XID_REUSE_MAX 8
|
|
|
|
#define LEASE_TIMEOUT(lease) ((btime_t)1000 * (lease) - RENEW_TIMEOUT(lease))
|
|
|
|
#define STATE_RESETTING 1
|
|
#define STATE_SENT_DISCOVER 2
|
|
#define STATE_SENT_REQUEST 3
|
|
#define STATE_FINISHED 4
|
|
#define STATE_RENEWING 5
|
|
|
|
#define IP_UDP_HEADERS_SIZE 28
|
|
|
|
static void report_up (BDHCPClientCore *o)
|
|
{
|
|
o->handler(o->user, BDHCPCLIENTCORE_EVENT_UP);
|
|
return;
|
|
}
|
|
|
|
static void report_down (BDHCPClientCore *o)
|
|
{
|
|
o->handler(o->user, BDHCPCLIENTCORE_EVENT_DOWN);
|
|
return;
|
|
}
|
|
|
|
static void send_message (
|
|
BDHCPClientCore *o,
|
|
int type,
|
|
uint32_t xid,
|
|
int have_requested_ip_address, uint32_t requested_ip_address,
|
|
int have_dhcp_server_identifier, uint32_t dhcp_server_identifier
|
|
)
|
|
{
|
|
ASSERT(type == DHCP_MESSAGE_TYPE_DISCOVER || type == DHCP_MESSAGE_TYPE_REQUEST)
|
|
|
|
if (o->sending) {
|
|
BLog(BLOG_ERROR, "already sending");
|
|
return;
|
|
}
|
|
|
|
// write header
|
|
struct dhcp_header header;
|
|
memset(&header, 0, sizeof(header));
|
|
header.op = hton8(DHCP_OP_BOOTREQUEST);
|
|
header.htype = hton8(DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET);
|
|
header.hlen = hton8(6);
|
|
header.xid = xid;
|
|
header.secs = hton16(0);
|
|
memcpy(header.chaddr, o->client_mac_addr, sizeof(o->client_mac_addr));
|
|
header.magic = hton32(DHCP_MAGIC);
|
|
memcpy(o->send_buf, &header, sizeof(header));
|
|
|
|
// write options
|
|
|
|
char *out = o->send_buf + sizeof(header);
|
|
struct dhcp_option_header oh;
|
|
|
|
// DHCP message type
|
|
{
|
|
oh.type = hton8(DHCP_OPTION_DHCP_MESSAGE_TYPE);
|
|
oh.len = hton8(sizeof(struct dhcp_option_dhcp_message_type));
|
|
struct dhcp_option_dhcp_message_type opt;
|
|
opt.type = hton8(type);
|
|
memcpy(out, &oh, sizeof(oh));
|
|
memcpy(out + sizeof(oh), &opt, sizeof(opt));
|
|
out += sizeof(oh) + sizeof(opt);
|
|
}
|
|
|
|
if (have_requested_ip_address) {
|
|
// requested IP address
|
|
oh.type = hton8(DHCP_OPTION_REQUESTED_IP_ADDRESS);
|
|
oh.len = hton8(sizeof(struct dhcp_option_addr));
|
|
struct dhcp_option_addr opt;
|
|
opt.addr = requested_ip_address;
|
|
memcpy(out, &oh, sizeof(oh));
|
|
memcpy(out + sizeof(oh), &opt, sizeof(opt));
|
|
out += sizeof(oh) + sizeof(opt);
|
|
}
|
|
|
|
if (have_dhcp_server_identifier) {
|
|
// DHCP server identifier
|
|
oh.type = hton8(DHCP_OPTION_DHCP_SERVER_IDENTIFIER);
|
|
oh.len = hton8(sizeof(struct dhcp_option_dhcp_server_identifier));
|
|
struct dhcp_option_dhcp_server_identifier opt;
|
|
opt.id = dhcp_server_identifier;
|
|
memcpy(out, &oh, sizeof(oh));
|
|
memcpy(out + sizeof(oh), &opt, sizeof(opt));
|
|
out += sizeof(oh) + sizeof(opt);
|
|
}
|
|
|
|
// maximum message size
|
|
{
|
|
oh.type = hton8(DHCP_OPTION_MAXIMUM_MESSAGE_SIZE);
|
|
oh.len = hton8(sizeof(struct dhcp_option_maximum_message_size));
|
|
struct dhcp_option_maximum_message_size opt;
|
|
opt.size = hton16(IP_UDP_HEADERS_SIZE + PacketRecvInterface_GetMTU(o->recv_if));
|
|
memcpy(out, &oh, sizeof(oh));
|
|
memcpy(out + sizeof(oh), &opt, sizeof(opt));
|
|
out += sizeof(oh) + sizeof(opt);
|
|
}
|
|
|
|
// parameter request list
|
|
{
|
|
oh.type = hton8(DHCP_OPTION_PARAMETER_REQUEST_LIST);
|
|
oh.len = hton8(4);
|
|
uint8_t opt[4];
|
|
opt[0] = DHCP_OPTION_SUBNET_MASK;
|
|
opt[1] = DHCP_OPTION_ROUTER;
|
|
opt[2] = DHCP_OPTION_DOMAIN_NAME_SERVER;
|
|
opt[3] = DHCP_OPTION_IP_ADDRESS_LEASE_TIME;
|
|
memcpy(out, &oh, sizeof(oh));
|
|
memcpy(out + sizeof(oh), &opt, sizeof(opt));
|
|
out += sizeof(oh) + sizeof(opt);
|
|
}
|
|
|
|
if (o->hostname) {
|
|
// host name
|
|
oh.type = hton8(DHCP_OPTION_HOST_NAME);
|
|
oh.len = hton8(strlen(o->hostname));
|
|
memcpy(out, &oh, sizeof(oh));
|
|
memcpy(out + sizeof(oh), o->hostname, strlen(o->hostname));
|
|
out += sizeof(oh) + strlen(o->hostname);
|
|
}
|
|
|
|
if (o->vendorclassid) {
|
|
// vendor class identifier
|
|
oh.type = hton8(DHCP_OPTION_VENDOR_CLASS_IDENTIFIER);
|
|
oh.len = hton8(strlen(o->vendorclassid));
|
|
memcpy(out, &oh, sizeof(oh));
|
|
memcpy(out + sizeof(oh), o->vendorclassid, strlen(o->vendorclassid));
|
|
out += sizeof(oh) + strlen(o->vendorclassid);
|
|
}
|
|
|
|
if (o->clientid) {
|
|
// client identifier
|
|
oh.type = hton8(DHCP_OPTION_CLIENT_IDENTIFIER);
|
|
oh.len = hton8(o->clientid_len);
|
|
memcpy(out, &oh, sizeof(oh));
|
|
memcpy(out + sizeof(oh), o->clientid, o->clientid_len);
|
|
out += sizeof(oh) + o->clientid_len;
|
|
}
|
|
|
|
// end option
|
|
uint8_t end = 0xFF;
|
|
memcpy(out, &end, sizeof(end));
|
|
out += sizeof(end);
|
|
|
|
// send it
|
|
PacketPassInterface_Sender_Send(o->send_if, (uint8_t *)o->send_buf, out - o->send_buf);
|
|
o->sending = 1;
|
|
}
|
|
|
|
static void send_handler_done (BDHCPClientCore *o)
|
|
{
|
|
ASSERT(o->sending)
|
|
DebugObject_Access(&o->d_obj);
|
|
|
|
o->sending = 0;
|
|
}
|
|
|
|
static void recv_handler_done (BDHCPClientCore *o, int data_len)
|
|
{
|
|
ASSERT(data_len >= 0)
|
|
DebugObject_Access(&o->d_obj);
|
|
|
|
// receive more packets
|
|
PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)o->recv_buf);
|
|
|
|
if (o->state == STATE_RESETTING) {
|
|
return;
|
|
}
|
|
|
|
// check header
|
|
|
|
if (data_len < sizeof(struct dhcp_header)) {
|
|
return;
|
|
}
|
|
|
|
struct dhcp_header header;
|
|
memcpy(&header, o->recv_buf, sizeof(header));
|
|
|
|
if (ntoh8(header.op) != DHCP_OP_BOOTREPLY) {
|
|
return;
|
|
}
|
|
|
|
if (ntoh8(header.htype) != DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET) {
|
|
return;
|
|
}
|
|
|
|
if (ntoh8(header.hlen) != 6) {
|
|
return;
|
|
}
|
|
|
|
if (header.xid != o->xid) {
|
|
return;
|
|
}
|
|
|
|
if (memcmp(header.chaddr, o->client_mac_addr, sizeof(o->client_mac_addr))) {
|
|
return;
|
|
}
|
|
|
|
if (ntoh32(header.magic) != DHCP_MAGIC) {
|
|
return;
|
|
}
|
|
|
|
// parse and check options
|
|
|
|
uint8_t *pos = (uint8_t *)o->recv_buf + sizeof(header);
|
|
int len = data_len - sizeof(header);
|
|
|
|
int have_end = 0;
|
|
|
|
int dhcp_message_type = -1;
|
|
|
|
int have_dhcp_server_identifier = 0;
|
|
uint32_t dhcp_server_identifier = 0; // to remove warning
|
|
|
|
int have_ip_address_lease_time = 0;
|
|
uint32_t ip_address_lease_time = 0; // to remove warning
|
|
|
|
int have_subnet_mask = 0;
|
|
uint32_t subnet_mask = 0; // to remove warning
|
|
|
|
int have_router = 0;
|
|
uint32_t router = 0; // to remove warning
|
|
|
|
int domain_name_servers_count = 0;
|
|
uint32_t domain_name_servers[BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS];
|
|
|
|
while (len > 0) {
|
|
// padding option ?
|
|
if (*pos == 0) {
|
|
pos++;
|
|
len--;
|
|
continue;
|
|
}
|
|
|
|
if (have_end) {
|
|
return;
|
|
}
|
|
|
|
// end option ?
|
|
if (*pos == 0xff) {
|
|
pos++;
|
|
len--;
|
|
have_end = 1;
|
|
continue;
|
|
}
|
|
|
|
// check option header
|
|
if (len < sizeof(struct dhcp_option_header)) {
|
|
return;
|
|
}
|
|
struct dhcp_option_header opt;
|
|
memcpy(&opt, pos, sizeof(opt));
|
|
pos += sizeof(opt);
|
|
len -= sizeof(opt);
|
|
int opt_type = ntoh8(opt.type);
|
|
int opt_len = ntoh8(opt.len);
|
|
|
|
// check option payload
|
|
if (opt_len > len) {
|
|
return;
|
|
}
|
|
uint8_t *optval = pos;
|
|
pos += opt_len;
|
|
len -= opt_len;
|
|
|
|
switch (opt_type) {
|
|
case DHCP_OPTION_DHCP_MESSAGE_TYPE: {
|
|
if (opt_len != sizeof(struct dhcp_option_dhcp_message_type)) {
|
|
return;
|
|
}
|
|
struct dhcp_option_dhcp_message_type val;
|
|
memcpy(&val, optval, sizeof(val));
|
|
|
|
dhcp_message_type = ntoh8(val.type);
|
|
} break;
|
|
|
|
case DHCP_OPTION_DHCP_SERVER_IDENTIFIER: {
|
|
if (opt_len != sizeof(struct dhcp_option_dhcp_server_identifier)) {
|
|
return;
|
|
}
|
|
struct dhcp_option_dhcp_server_identifier val;
|
|
memcpy(&val, optval, sizeof(val));
|
|
|
|
dhcp_server_identifier = val.id;
|
|
have_dhcp_server_identifier = 1;
|
|
} break;
|
|
|
|
case DHCP_OPTION_IP_ADDRESS_LEASE_TIME: {
|
|
if (opt_len != sizeof(struct dhcp_option_time)) {
|
|
return;
|
|
}
|
|
struct dhcp_option_time val;
|
|
memcpy(&val, optval, sizeof(val));
|
|
|
|
ip_address_lease_time = ntoh32(val.time);
|
|
have_ip_address_lease_time = 1;
|
|
} break;
|
|
|
|
case DHCP_OPTION_SUBNET_MASK: {
|
|
if (opt_len != sizeof(struct dhcp_option_addr)) {
|
|
return;
|
|
}
|
|
struct dhcp_option_addr val;
|
|
memcpy(&val, optval, sizeof(val));
|
|
|
|
subnet_mask = val.addr;
|
|
have_subnet_mask = 1;
|
|
} break;
|
|
|
|
case DHCP_OPTION_ROUTER: {
|
|
if (opt_len != sizeof(struct dhcp_option_addr)) {
|
|
return;
|
|
}
|
|
struct dhcp_option_addr val;
|
|
memcpy(&val, optval, sizeof(val));
|
|
|
|
router = val.addr;
|
|
have_router = 1;
|
|
} break;
|
|
|
|
case DHCP_OPTION_DOMAIN_NAME_SERVER: {
|
|
if (opt_len % sizeof(struct dhcp_option_addr)) {
|
|
return;
|
|
}
|
|
|
|
int num_servers = opt_len / sizeof(struct dhcp_option_addr);
|
|
|
|
int i;
|
|
for (i = 0; i < num_servers && i < BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS; i++) {
|
|
struct dhcp_option_addr addr;
|
|
memcpy(&addr, optval + i * sizeof(addr), sizeof(addr));
|
|
domain_name_servers[i] = addr.addr;
|
|
}
|
|
|
|
domain_name_servers_count = i;
|
|
} break;
|
|
}
|
|
}
|
|
|
|
if (!have_end) {
|
|
return;
|
|
}
|
|
|
|
if (dhcp_message_type == -1) {
|
|
return;
|
|
}
|
|
|
|
if (dhcp_message_type != DHCP_MESSAGE_TYPE_OFFER && dhcp_message_type != DHCP_MESSAGE_TYPE_ACK && dhcp_message_type != DHCP_MESSAGE_TYPE_NAK) {
|
|
return;
|
|
}
|
|
|
|
if (!have_dhcp_server_identifier) {
|
|
return;
|
|
}
|
|
|
|
if (dhcp_message_type == DHCP_MESSAGE_TYPE_NAK) {
|
|
if (o->state != STATE_SENT_REQUEST && o->state != STATE_FINISHED && o->state != STATE_RENEWING) {
|
|
return;
|
|
}
|
|
|
|
if (dhcp_server_identifier != o->offered.dhcp_server_identifier) {
|
|
return;
|
|
}
|
|
|
|
if (o->state == STATE_SENT_REQUEST) {
|
|
BLog(BLOG_INFO, "received NAK (in sent request)");
|
|
|
|
// stop request timer
|
|
BReactor_RemoveTimer(o->reactor, &o->request_timer);
|
|
|
|
// start reset timer
|
|
BReactor_SetTimer(o->reactor, &o->reset_timer);
|
|
|
|
// set state
|
|
o->state = STATE_RESETTING;
|
|
}
|
|
else if (o->state == STATE_FINISHED) {
|
|
BLog(BLOG_INFO, "received NAK (in finished)");
|
|
|
|
// stop renew timer
|
|
BReactor_RemoveTimer(o->reactor, &o->renew_timer);
|
|
|
|
// start reset timer
|
|
BReactor_SetTimer(o->reactor, &o->reset_timer);
|
|
|
|
// set state
|
|
o->state = STATE_RESETTING;
|
|
|
|
// report to user
|
|
report_down(o);
|
|
return;
|
|
}
|
|
else { // STATE_RENEWING
|
|
BLog(BLOG_INFO, "received NAK (in renewing)");
|
|
|
|
// stop renew request timer
|
|
BReactor_RemoveTimer(o->reactor, &o->renew_request_timer);
|
|
|
|
// stop lease timer
|
|
BReactor_RemoveTimer(o->reactor, &o->lease_timer);
|
|
|
|
// start reset timer
|
|
BReactor_SetTimer(o->reactor, &o->reset_timer);
|
|
|
|
// set state
|
|
o->state = STATE_RESETTING;
|
|
|
|
// report to user
|
|
report_down(o);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (ntoh32(header.yiaddr) == 0) {
|
|
return;
|
|
}
|
|
|
|
if (!have_ip_address_lease_time) {
|
|
return;
|
|
}
|
|
|
|
if (!have_subnet_mask) {
|
|
return;
|
|
}
|
|
|
|
if (o->state == STATE_SENT_DISCOVER && dhcp_message_type == DHCP_MESSAGE_TYPE_OFFER) {
|
|
BLog(BLOG_INFO, "received OFFER");
|
|
|
|
// remember offer
|
|
o->offered.yiaddr = header.yiaddr;
|
|
o->offered.dhcp_server_identifier = dhcp_server_identifier;
|
|
|
|
// send request
|
|
send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 1, o->offered.dhcp_server_identifier);
|
|
|
|
// stop reset timer
|
|
BReactor_RemoveTimer(o->reactor, &o->reset_timer);
|
|
|
|
// start request timer
|
|
BReactor_SetTimer(o->reactor, &o->request_timer);
|
|
|
|
// set state
|
|
o->state = STATE_SENT_REQUEST;
|
|
|
|
// set request count
|
|
o->request_count = 1;
|
|
}
|
|
else if (o->state == STATE_SENT_REQUEST && dhcp_message_type == DHCP_MESSAGE_TYPE_ACK) {
|
|
if (header.yiaddr != o->offered.yiaddr) {
|
|
return;
|
|
}
|
|
|
|
if (dhcp_server_identifier != o->offered.dhcp_server_identifier) {
|
|
return;
|
|
}
|
|
|
|
BLog(BLOG_INFO, "received ACK (in sent request)");
|
|
|
|
// remember stuff
|
|
o->acked.ip_address_lease_time = ip_address_lease_time;
|
|
o->acked.subnet_mask = subnet_mask;
|
|
o->acked.have_router = have_router;
|
|
if (have_router) {
|
|
o->acked.router = router;
|
|
}
|
|
o->acked.domain_name_servers_count = domain_name_servers_count;
|
|
memcpy(o->acked.domain_name_servers, domain_name_servers, domain_name_servers_count * sizeof(uint32_t));
|
|
o->func_getsendermac(o->user, o->acked.server_mac);
|
|
|
|
// stop request timer
|
|
BReactor_RemoveTimer(o->reactor, &o->request_timer);
|
|
|
|
// start renew timer
|
|
BReactor_SetTimerAfter(o->reactor, &o->renew_timer, RENEW_TIMEOUT(o->acked.ip_address_lease_time));
|
|
|
|
// set state
|
|
o->state = STATE_FINISHED;
|
|
|
|
// report to user
|
|
report_up(o);
|
|
return;
|
|
}
|
|
else if (o->state == STATE_RENEWING && dhcp_message_type == DHCP_MESSAGE_TYPE_ACK) {
|
|
if (header.yiaddr != o->offered.yiaddr) {
|
|
return;
|
|
}
|
|
|
|
if (dhcp_server_identifier != o->offered.dhcp_server_identifier) {
|
|
return;
|
|
}
|
|
|
|
// TODO: check parameters?
|
|
|
|
BLog(BLOG_INFO, "received ACK (in renewing)");
|
|
|
|
// remember stuff
|
|
o->acked.ip_address_lease_time = ip_address_lease_time;
|
|
|
|
// stop renew request timer
|
|
BReactor_RemoveTimer(o->reactor, &o->renew_request_timer);
|
|
|
|
// stop lease timer
|
|
BReactor_RemoveTimer(o->reactor, &o->lease_timer);
|
|
|
|
// start renew timer
|
|
BReactor_SetTimerAfter(o->reactor, &o->renew_timer, RENEW_TIMEOUT(o->acked.ip_address_lease_time));
|
|
|
|
// set state
|
|
o->state = STATE_FINISHED;
|
|
}
|
|
}
|
|
|
|
static void start_process (BDHCPClientCore *o, int force_new_xid)
|
|
{
|
|
if (force_new_xid || o->xid_reuse_counter == XID_REUSE_MAX) {
|
|
// generate xid
|
|
if (!BRandom2_GenBytes(o->random2, &o->xid, sizeof(o->xid))) {
|
|
BLog(BLOG_ERROR, "BRandom2_GenBytes failed");
|
|
o->xid = UINT32_C(3416960072);
|
|
}
|
|
|
|
// reset counter
|
|
o->xid_reuse_counter = 0;
|
|
}
|
|
|
|
// increment counter
|
|
o->xid_reuse_counter++;
|
|
|
|
// send discover
|
|
send_message(o, DHCP_MESSAGE_TYPE_DISCOVER, o->xid, 0, 0, 0, 0);
|
|
|
|
// set timer
|
|
BReactor_SetTimer(o->reactor, &o->reset_timer);
|
|
|
|
// set state
|
|
o->state = STATE_SENT_DISCOVER;
|
|
}
|
|
|
|
static void reset_timer_handler (BDHCPClientCore *o)
|
|
{
|
|
ASSERT(o->state == STATE_RESETTING || o->state == STATE_SENT_DISCOVER)
|
|
DebugObject_Access(&o->d_obj);
|
|
|
|
BLog(BLOG_INFO, "reset timer");
|
|
|
|
start_process(o, (o->state == STATE_RESETTING));
|
|
}
|
|
|
|
static void request_timer_handler (BDHCPClientCore *o)
|
|
{
|
|
ASSERT(o->state == STATE_SENT_REQUEST)
|
|
ASSERT(o->request_count >= 1)
|
|
ASSERT(o->request_count <= MAX_REQUESTS)
|
|
DebugObject_Access(&o->d_obj);
|
|
|
|
// if we have sent enough requests, start again
|
|
if (o->request_count == MAX_REQUESTS) {
|
|
BLog(BLOG_INFO, "request timer, aborting");
|
|
|
|
start_process(o, 0);
|
|
return;
|
|
}
|
|
|
|
BLog(BLOG_INFO, "request timer, retrying");
|
|
|
|
// send request
|
|
send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 1, o->offered.dhcp_server_identifier);
|
|
|
|
// start request timer
|
|
BReactor_SetTimer(o->reactor, &o->request_timer);
|
|
|
|
// increment request count
|
|
o->request_count++;
|
|
}
|
|
|
|
static void renew_timer_handler (BDHCPClientCore *o)
|
|
{
|
|
ASSERT(o->state == STATE_FINISHED)
|
|
DebugObject_Access(&o->d_obj);
|
|
|
|
BLog(BLOG_INFO, "renew timer");
|
|
|
|
// send request
|
|
send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 0, 0);
|
|
|
|
// start renew request timer
|
|
BReactor_SetTimer(o->reactor, &o->renew_request_timer);
|
|
|
|
// start lease timer
|
|
BReactor_SetTimerAfter(o->reactor, &o->lease_timer, LEASE_TIMEOUT(o->acked.ip_address_lease_time));
|
|
|
|
// set state
|
|
o->state = STATE_RENEWING;
|
|
}
|
|
|
|
static void renew_request_timer_handler (BDHCPClientCore *o)
|
|
{
|
|
ASSERT(o->state == STATE_RENEWING)
|
|
DebugObject_Access(&o->d_obj);
|
|
|
|
BLog(BLOG_INFO, "renew request timer");
|
|
|
|
// send request
|
|
send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 0, 0);
|
|
|
|
// start renew request timer
|
|
BReactor_SetTimer(o->reactor, &o->renew_request_timer);
|
|
}
|
|
|
|
static void lease_timer_handler (BDHCPClientCore *o)
|
|
{
|
|
ASSERT(o->state == STATE_RENEWING)
|
|
DebugObject_Access(&o->d_obj);
|
|
|
|
BLog(BLOG_INFO, "lease timer");
|
|
|
|
// stop renew request timer
|
|
BReactor_RemoveTimer(o->reactor, &o->renew_request_timer);
|
|
|
|
// start again now
|
|
start_process(o, 1);
|
|
|
|
// report to user
|
|
report_down(o);
|
|
return;
|
|
}
|
|
|
|
static bsize_t maybe_len (const char *str)
|
|
{
|
|
return bsize_fromsize(str ? strlen(str) : 0);
|
|
}
|
|
|
|
int BDHCPClientCore_Init (BDHCPClientCore *o, PacketPassInterface *send_if, PacketRecvInterface *recv_if,
|
|
uint8_t *client_mac_addr, struct BDHCPClientCore_opts opts, BReactor *reactor,
|
|
BRandom2 *random2, void *user,
|
|
BDHCPClientCore_func_getsendermac func_getsendermac,
|
|
BDHCPClientCore_handler handler)
|
|
{
|
|
ASSERT(PacketPassInterface_GetMTU(send_if) == PacketRecvInterface_GetMTU(recv_if))
|
|
ASSERT(PacketPassInterface_GetMTU(send_if) >= 576 - IP_UDP_HEADERS_SIZE)
|
|
ASSERT(func_getsendermac)
|
|
ASSERT(handler)
|
|
|
|
// init arguments
|
|
o->send_if = send_if;
|
|
o->recv_if = recv_if;
|
|
memcpy(o->client_mac_addr, client_mac_addr, sizeof(o->client_mac_addr));
|
|
o->reactor = reactor;
|
|
o->random2 = random2;
|
|
o->user = user;
|
|
o->func_getsendermac = func_getsendermac;
|
|
o->handler = handler;
|
|
|
|
o->hostname = NULL;
|
|
o->vendorclassid = NULL;
|
|
o->clientid = NULL;
|
|
o->clientid_len = 0;
|
|
|
|
// copy options
|
|
if (opts.hostname && !(o->hostname = strdup(opts.hostname))) {
|
|
BLog(BLOG_ERROR, "strdup failed");
|
|
goto fail0;
|
|
}
|
|
if (opts.vendorclassid && !(o->vendorclassid = strdup(opts.vendorclassid))) {
|
|
BLog(BLOG_ERROR, "strdup failed");
|
|
goto fail0;
|
|
}
|
|
if (opts.clientid) {
|
|
if (!(o->clientid = BAlloc(opts.clientid_len))) {
|
|
BLog(BLOG_ERROR, "BAlloc failed");
|
|
goto fail0;
|
|
}
|
|
memcpy(o->clientid, opts.clientid, opts.clientid_len);
|
|
o->clientid_len = opts.clientid_len;
|
|
}
|
|
|
|
// make sure options aren't too long
|
|
bsize_t opts_size = bsize_add(maybe_len(o->hostname), bsize_add(maybe_len(o->vendorclassid), bsize_fromsize(o->clientid_len)));
|
|
if (opts_size.is_overflow || opts_size.value > 100) {
|
|
BLog(BLOG_ERROR, "options too long together");
|
|
goto fail0;
|
|
}
|
|
if (o->hostname && strlen(o->hostname) > 255) {
|
|
BLog(BLOG_ERROR, "hostname too long");
|
|
goto fail0;
|
|
}
|
|
if (o->vendorclassid && strlen(o->vendorclassid) > 255) {
|
|
BLog(BLOG_ERROR, "vendorclassid too long");
|
|
goto fail0;
|
|
}
|
|
if (o->clientid && o->clientid_len > 255) {
|
|
BLog(BLOG_ERROR, "clientid too long");
|
|
goto fail0;
|
|
}
|
|
|
|
// allocate buffers
|
|
if (!(o->send_buf = BAlloc(PacketPassInterface_GetMTU(send_if)))) {
|
|
BLog(BLOG_ERROR, "BAlloc send buf failed");
|
|
goto fail0;
|
|
}
|
|
if (!(o->recv_buf = BAlloc(PacketRecvInterface_GetMTU(recv_if)))) {
|
|
BLog(BLOG_ERROR, "BAlloc recv buf failed");
|
|
goto fail1;
|
|
}
|
|
|
|
// init send interface
|
|
PacketPassInterface_Sender_Init(o->send_if, (PacketPassInterface_handler_done)send_handler_done, o);
|
|
|
|
// init receive interface
|
|
PacketRecvInterface_Receiver_Init(o->recv_if, (PacketRecvInterface_handler_done)recv_handler_done, o);
|
|
|
|
// set not sending
|
|
o->sending = 0;
|
|
|
|
// init timers
|
|
BTimer_Init(&o->reset_timer, RESET_TIMEOUT, (BTimer_handler)reset_timer_handler, o);
|
|
BTimer_Init(&o->request_timer, REQUEST_TIMEOUT, (BTimer_handler)request_timer_handler, o);
|
|
BTimer_Init(&o->renew_timer, 0, (BTimer_handler)renew_timer_handler, o);
|
|
BTimer_Init(&o->renew_request_timer, RENEW_REQUEST_TIMEOUT, (BTimer_handler)renew_request_timer_handler, o);
|
|
BTimer_Init(&o->lease_timer, 0, (BTimer_handler)lease_timer_handler, o);
|
|
|
|
// start receving
|
|
PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)o->recv_buf);
|
|
|
|
// start
|
|
start_process(o, 1);
|
|
|
|
DebugObject_Init(&o->d_obj);
|
|
|
|
return 1;
|
|
|
|
fail1:
|
|
BFree(o->send_buf);
|
|
fail0:
|
|
BFree(o->clientid);
|
|
free(o->vendorclassid);
|
|
free(o->hostname);
|
|
return 0;
|
|
}
|
|
|
|
void BDHCPClientCore_Free (BDHCPClientCore *o)
|
|
{
|
|
DebugObject_Free(&o->d_obj);
|
|
|
|
// free timers
|
|
BReactor_RemoveTimer(o->reactor, &o->lease_timer);
|
|
BReactor_RemoveTimer(o->reactor, &o->renew_request_timer);
|
|
BReactor_RemoveTimer(o->reactor, &o->renew_timer);
|
|
BReactor_RemoveTimer(o->reactor, &o->request_timer);
|
|
BReactor_RemoveTimer(o->reactor, &o->reset_timer);
|
|
|
|
// free buffers
|
|
BFree(o->recv_buf);
|
|
BFree(o->send_buf);
|
|
|
|
// free options
|
|
BFree(o->clientid);
|
|
free(o->vendorclassid);
|
|
free(o->hostname);
|
|
}
|
|
|
|
void BDHCPClientCore_GetClientIP (BDHCPClientCore *o, uint32_t *out_ip)
|
|
{
|
|
ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING)
|
|
DebugObject_Access(&o->d_obj);
|
|
|
|
*out_ip = o->offered.yiaddr;
|
|
}
|
|
|
|
void BDHCPClientCore_GetClientMask (BDHCPClientCore *o, uint32_t *out_mask)
|
|
{
|
|
ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING)
|
|
DebugObject_Access(&o->d_obj);
|
|
|
|
*out_mask = o->acked.subnet_mask;
|
|
}
|
|
|
|
int BDHCPClientCore_GetRouter (BDHCPClientCore *o, uint32_t *out_router)
|
|
{
|
|
ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING)
|
|
DebugObject_Access(&o->d_obj);
|
|
|
|
if (!o->acked.have_router) {
|
|
return 0;
|
|
}
|
|
|
|
*out_router = o->acked.router;
|
|
return 1;
|
|
}
|
|
|
|
int BDHCPClientCore_GetDNS (BDHCPClientCore *o, uint32_t *out_dns_servers, size_t max_dns_servers)
|
|
{
|
|
ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING)
|
|
DebugObject_Access(&o->d_obj);
|
|
|
|
int num_return = bmin_int(o->acked.domain_name_servers_count, max_dns_servers);
|
|
|
|
memcpy(out_dns_servers, o->acked.domain_name_servers, num_return * sizeof(uint32_t));
|
|
return num_return;
|
|
}
|
|
|
|
void BDHCPClientCore_GetServerMAC (BDHCPClientCore *o, uint8_t *out_mac)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING)
|
|
|
|
memcpy(out_mac, o->acked.server_mac, 6);
|
|
}
|