badvpn/dhcpclient/BDHCPClientCore.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);
}