summaryrefslogtreecommitdiff
path: root/libdhcp/dhcp_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdhcp/dhcp_client.c')
-rw-r--r--libdhcp/dhcp_client.c573
1 files changed, 573 insertions, 0 deletions
diff --git a/libdhcp/dhcp_client.c b/libdhcp/dhcp_client.c
new file mode 100644
index 0000000..a4b015c
--- /dev/null
+++ b/libdhcp/dhcp_client.c
@@ -0,0 +1,573 @@
+/* libdhcp/dhcp_client.c - A DHCP client
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ *
+ * -----------------------------------------------------------------------------
+ * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Internet/DHCP/dhcp.c
+ *
+ * Copyright (c) 2013, WIZnet Co., LTD.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the <ORGANIZATION> 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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * -----------------------------------------------------------------------------
+ * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/license.txt
+ *
+ * Copyright (c) 2014 WIZnet Co.,Ltd.
+ * Copyright (c) WIZnet ioLibrary Project.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <string.h> /* for strlen(), memcpy(), memset() */
+
+#include <libmisc/endian.h>
+#include <libmisc/vcall.h>
+#include <libdhcp/dhcp.h>
+
+#include "dhcp_common.h"
+
+/* Config *********************************************************************/
+
+#include "config.h"
+
+#ifndef CONFIG_DHCP_DEBUG
+ #define CONFIG_DHCP_DEBUG 1
+#endif
+#ifndef CONFIG_DHCP_HOPS
+ #define CONFIG_DHCP_HOPS 0
+#endif
+#ifndef CONFIG_DHCP_SECS
+ #define CONFIG_DHCP_SECS 0
+#endif
+
+/* Implementation *************************************************************/
+
+#if CONFIG_DHCP_DEBUG
+ #include <stdio.h>
+ #define debugf(fmt, ...) printf(fmt "\n" __VA_OPT__(,) __VA_ARGS__)
+#else
+ #define debugf(fmt, ...) ((void)0)
+#endif
+
+/* DHCP state machine. */
+#define STATE_DHCP_INIT 0 /* Initialize */
+#define STATE_DHCP_DISCOVER 1 /* send DISCOVER and wait OFFER */
+#define STATE_DHCP_REQUEST 2 /* send REQEUST and wait ACK or NACK */
+#define STATE_DHCP_LEASED 3 /* ReceiveD ACK and IP leased */
+#define STATE_DHCP_REREQUEST 4 /* send REQUEST for maintaining leased IP */
+#define STATE_DHCP_RELEASE 5 /* No use */
+#define STATE_DHCP_STOP 6 /* Stop processing DHCP */
+
+/* Global state. */
+struct net_ip4_addr DHCP_SIP; /* DHCP Server IP address */
+struct net_ip4_addr DHCP_REAL_SIP; /* For extract my DHCP server in a few DHCP server */
+struct net_ip4_addr OLD_allocated_ip = {0}; /* Previous IP address */
+struct dhcp_lease global_lease = {.lifetime = DHCP_INFINITY};
+int8_t dhcp_state = STATE_DHCP_INIT; /* DHCP state */
+int8_t dhcp_retry_count = 0;
+volatile uint32_t dhcp_tick_1s = 0; /* unit 1 second */
+uint32_t dhcp_tick_next = DHCP_WAIT_TIME ;
+char global_hostname[64];
+struct net_eth_addr global_eth_addr; /* DHCP Client MAC address. */
+
+#define mem_encode(dst, obj) ({ \
+ typeof(obj) _obj = obj; \
+ memcpy(dst, &_obj, sizeof(_obj)); \
+ sizeof(_obj); \
+})
+
+static inline size_t str_encode(void *dst, char *str) {
+ size_t len = strlen(str);
+ memcpy(dst, str, len);
+ return len;
+}
+
+/* make the common DHCP message */
+static inline void dhcp_msg_init(struct dhcp_msg *msg, size_t *optlen,
+ struct net_eth_addr self_eth_addr,
+ uint16_t flags, uint32_t xid) {
+ size_t tmp;
+
+ assert(msg);
+ assert(optlen);
+
+ *msg = (struct dhcp_msg){0};
+
+ msg->op = DHCP_OP_BOOTREQUEST;
+ msg->htype = DHCP_HTYPE_ETHERNET;
+ msg->hlen = sizeof(self_eth_addr);
+ msg->hops = CONFIG_DHCP_HOPS;
+ msg->xid = uint32be_marshal(xid);
+ msg->secs = uint16be_marshal(CONFIG_DHCP_SECS);
+ msg->flags = uint16be_marshal(flags);
+ msg->ciaddr = (struct net_ip4_addr){0};
+ msg->yiaddr = (struct net_ip4_addr){0};
+ msg->siaddr = (struct net_ip4_addr){0};
+ msg->siaddr = (struct net_ip4_addr){0};
+ msg->giaddr = (struct net_ip4_addr){0};
+
+ tmp = mem_encode(msg->chaddr, self_eth_addr);
+ memset(&msg->chaddr[tmp], 0, sizeof(msg->chaddr)-tmp);
+
+ memset(msg->sname, 0, sizeof(msg->sname));
+ memset(msg->file, 0, sizeof(msg->file));
+
+ *optlen = 0;
+ msg->options[*optlen++] = dhcp_magic_cookie[0];
+ msg->options[*optlen++] = dhcp_magic_cookie[1];
+ msg->options[*optlen++] = dhcp_magic_cookie[2];
+ msg->options[*optlen++] = dhcp_magic_cookie[3];
+}
+
+static void dhcp_send_DISCOVER(implements_net_packet_conn *sock) {
+ struct dhcp_msg msg;
+ size_t k;
+
+ dhcp_msg_init(&msg, &k, DHCP_FLAG_BROADCAST);
+ DHCP_SIP = (struct net_ip4_addr){0};
+ DHCP_REAL_SIP = (struct net_ip4_addr){0};
+
+ /* Message type. */
+ msg->options[k++] = DHCP_OPT_DHCP_MSG_TYPE;
+ msg->options[k++] = 1;
+ msg->options[k++] = DHCP_MSGTYP_DISCOVER;
+
+ /* Our Client ID. */
+ msg->options[k++] = DHCP_OPT_CLIENT_ID;
+ msg->options[k++] = 1+sizeof(self_eth_addr);
+ msg->options[k++] = DHCP_HTYPE_ETHERNET;
+ k += mem_encode(&msg->options[k], self_eth_addr);
+
+ /* Our requested hostname. */
+ msg->options[k++] = DHCP_OPT_HOSTNAME;
+ msg->options[k++] = strlen(global_hostname);
+ k += str_encode(&msg->options[k], global_hostname);
+
+ /* Which parameters we want back. */
+ msg->options[k++] = DHCP_OPT_PARAMETER_LIST;
+ msg->options[k++] = 6;
+ msg->options[k++] = DHCP_OPT_SUBNET_MASK; /* 1 */
+ msg->options[k++] = DHCP_OPT_ROUTER; /* 2 */
+ msg->options[k++] = DHCP_OPT_DOMAIN_SERVER; /* 3 */
+ msg->options[k++] = DHCP_OPT_DOMAIN_NAME; /* 4 */
+ msg->options[k++] = DHCP_OPT_RENEWAL_TIME; /* 5 */
+ msg->options[k++] = DHCP_OPT_REBINDING_TIME; /* 6 */
+
+ /* End. */
+ msg->options[k++] = DHCP_OPT_END;
+ assert(k <= CONFIG_DHCP_OPT_SIZE);
+
+ /* Send. */
+ debugf("> Send DHCP_DISCOVER");
+ VCALL(sock, sendto, msg, DHCP_MSG_BASE_SIZE + k, net_ip4_addr_broadcast, DHCP_PORT_SERVER);
+}
+
+static void dhcp_send_REQUEST(implements_net_packet_conn *sock) {
+ struct net_ip4_addr ip;
+ struct dhcp_msg msg;
+ size_t k;
+
+ dhcp_msg_init(msg, &k,
+ (dhcp_state != STATE_DHCP_LEASED && dhcp_state != STATE_DHCP_REREQUEST) ? DHCP_FLAG_BROADCAST : 0);
+
+ switch (dhcp_state) {
+ case STATE_DHCP_LEASED:
+ case STATE_DHCP_REREQUEST:
+ msg->ciaddr = global_lease.addr;
+ ip = DHCP_SIP;
+ break;
+ default:
+ ip = net_ip4_addr_broadcast;
+ }
+
+ /* Message type. */
+ msg->options[k++] = DHCP_OPT_DHCP_MSG_TYPE;
+ msg->options[k++] = 1;
+ msg->options[k++] = DHCP_MSGTYP_REQUEST;
+
+ /* Our Client ID. */
+ msg->options[k++] = DHCP_OPT_CLIENT_ID;
+ msg->options[k++] = 1+sizeof(self_eth_addr);
+ msg->options[k++] = DHCP_HTYPE_ETHERNET;
+ k += mem_encode(&msg->options[k], self_eth_addr);
+
+ switch (dhcp_state) {
+ case STATE_DHCP_LEASED:
+ case STATE_DHCP_REREQUEST:
+ /* Our current IP address. */
+ msg->options[k++] = DHCP_OPT_ADDRESS_REQUEST;
+ msg->options[k++] = sizeof(global_lease.addr);
+ k += mem_encode(&msg->options[k], global_lease.addr);
+
+ /* The server we got it from. */
+ msg->options[k++] = DHCP_OPT_DHCP_SERVER_ID;
+ msg->options[k++] = sizeof(DHCP_SIP);
+ k += mem_encode(&msg->options[k], DHCP_SIP);
+ }
+
+ /* Our requested hostname. */
+ msg->options[k++] = DHCP_OPT_HOSTNAME;
+ msg->options[k++] = strlen(global_hostname);
+ k += str_encode(&msg->options[k], global_hostname);
+
+ /* Which parameters we want back. */
+ msg->options[k++] = DHCP_OPT_PARAMETER_LIST;
+ msg->options[k++] = 8;
+ msg->options[k++] = DHCP_OPT_SUBNET_MASK; /* 1 */
+ msg->options[k++] = DHCP_OPT_ROUTER; /* 2 */
+ msg->options[k++] = DHCP_OPT_DOMAIN_SERVER; /* 3 */
+ msg->options[k++] = DHCP_OPT_DOMAIN_NAME; /* 4 */
+ msg->options[k++] = DHCP_OPT_RENEWAL_TIME; /* 5 */
+ msg->options[k++] = DHCP_OPT_REBINDING_TIME; /* 6 */
+ msg->options[k++] = DHCP_OPT_ROUTER_DISCOVERY; /* 7 */
+ msg->options[k++] = DHCP_OPT_STATIC_ROUTE; /* 8 */
+
+ /* End. */
+ msg->options[k++] = DHCP_OPT_END;
+ assert(k <= CONFIG_DHCP_OPT_SIZE);
+
+ debugf("> Send DHCP_REQUEST");
+ VCALL(sock, sendto, msg, DHCP_MSG_BASE_SIZE + k, ip, DHCP_PORT_SERVER);
+}
+
+static void dhcp_send_DECLINE(implements_net_packet_conn *sock) {
+ size_t k;
+
+ dhcp_msg_init(pDHCPMSG, &k, DHCP_FLAG_BROADCAST);
+ pDHCPMSG->flags = uint16be_marshal(0);
+
+ /* Option Request Param. */
+ pDHCPMSG->options[k++] = DHCP_OPT_DHCP_MSG_TYPE;
+ pDHCPMSG->options[k++] = 1;
+ pDHCPMSG->options[k++] = DHCP_MSGTYP_DECLINE;
+
+ /* Our Client ID. */
+ pDHCPMSG->options[k++] = DHCP_OPT_CLIENT_ID;
+ pDHCPMSG->options[k++] = 1+sizeof(self_eth_addr);
+ pDHCPMSG->options[k++] = DHCP_HTYPE_ETHERNET;
+ memcpy(&pDHCPMSG->options[k], self_eth_addr.octets, sizeof(self_eth_addr));
+ k += sizeof(self_eth_addr);
+
+ /* Our current IP address. */
+ pDHCPMSG->options[k++] = DHCP_OPT_ADDRESS_REQUEST;
+ pDHCPMSG->options[k++] = sizeof(global_lease.addr);
+ k += mem_encode(&pDHCPMSG->options[k], global_lease.addr);
+
+ /* The server we got it from. */
+ pDHCPMSG->options[k++] = DHCP_OPT_DHCP_SERVER_ID;
+ pDHCPMSG->options[k++] = sizeof(DHCP_SIP);
+ k += mem_encode(&pDHCPMSG->options[k], DHCP_SIP);
+
+ /* End. */
+ pDHCPMSG->options[k++] = DHCP_OPT_END;
+
+ debugf("> Send DHCP_DECLINE");
+ assert(k <= CONFIG_DHCP_OPT_SIZE);
+ VCALL(sock, sendto, pDHCPMSG, DHCP_MSG_BASE_SIZE + k, net_ip4_addr_broadcast, DHCP_PORT_SERVER);
+}
+
+static int8_t dhcp_msg_parse(implements_net_packet_conn *sock) {
+ struct net_ip4_addr srv_addr;
+ uint16_t srv_port;
+ size_t msg_len;
+ msg_len = VCALL(sock, recvfrom, pDHCPMSG, sizeof(*pDHCPMSG), &srv_addr, &srv_port);
+ debugf("DHCP message : %d.%d.%d.%d(%d) %d received.",
+ srv_addr.octets[0], srv_addr.octets[1], srv_addr.octets[2], srv_addr.octets[3], srv_port, msg_len);
+ /* Compare server port. */
+ if (srv_port != DHCP_PORT_SERVER) {
+ return 0;
+ }
+ /* Compare our MAC address. */
+ if (memcmp(pDHCPMSG->chaddr, self_eth_addr.octets, sizeof(self_eth_addr))) {
+ debugf("Not my DHCP Message. This message is ignored.");
+ return 0;
+ }
+ /* Compare server IP address. */
+ if (memcmp(DHCP_SIP.octets, ((struct net_ip4_addr){0}).octets, sizeof(struct net_ip4_addr))) {
+ if ( memcmp(srv_addr.octets, DHCP_SIP.octets, sizeof(srv_addr)) &&
+ memcmp(srv_addr.octets, DHCP_REAL_SIP.octets, sizeof(srv_addr)) ) {
+ debugf("Another DHCP sever send a response message. This is ignored.");
+ return 0;
+ }
+ }
+
+ uint8_t msg_type = 0;
+ for (size_t k = 4, k_max = msg_len - DHCP_MSG_BASE_SIZE, opt_len; k < k_max; k += opt_len) {
+ uint8_t opt_typ;
+ opt_typ = pDHCPMSG->options[k++];
+ switch (opt_typ) {
+ case DHCP_OPT_END:
+ opt_len = k_max - k;
+ break;
+ case DHCP_OPT_PAD:
+ opt_len = 0;
+ break;
+ default:
+ opt_len = pDHCPMSG->options[k++];
+ xhandle(opt_typ, opt_len, &pDHCPMSG->options[k]);
+ }
+ switch (opt_typ) {
+ case DHCP_OPT_DHCP_MSG_TYPE:
+ if (opt_len != 1)
+ continue;
+ msg_type = pDHCPMSG->options[k];
+ break;
+ case DHCP_OPT_ADDRESS_TIME:
+ if (opt_len != 4)
+ continue;
+ global_lease.lifetime = uint32be_decode(&pDHCPMSG->options[k]);
+#if CONFIG_DHCP_DEBUG
+ global_lease.lifetime = 10;
+#endif
+ break;
+ case DHCP_OPT_DHCP_SERVER_ID:
+ if (opt_len != 4)
+ continue;
+ DHCP_SIP.octets[0] = pDHCPMSG->options[k+0];
+ DHCP_SIP.octets[1] = pDHCPMSG->options[k+1];
+ DHCP_SIP.octets[2] = pDHCPMSG->options[k+2];
+ DHCP_SIP.octets[3] = pDHCPMSG->options[k+3];
+ DHCP_REAL_SIP = srv_addr;
+ break;
+ }
+ }
+ return msg_type;
+}
+
+static void dhcp_reset_timeout(void) {
+ dhcp_tick_1s = 0;
+ dhcp_tick_next = DHCP_WAIT_TIME;
+ dhcp_retry_count = 0;
+}
+
+static uint8_t dhcp_check_timeout(implements_net_packet_conn *sock) {
+ uint8_t ret = DHCP_RET_RUNNING;
+
+ if (dhcp_retry_count < MAX_DHCP_RETRY) {
+ if (dhcp_tick_next < dhcp_tick_1s) {
+
+ switch ( dhcp_state ) {
+ case STATE_DHCP_DISCOVER :
+ /*debugf("<<timeout>> state : STATE_DHCP_DISCOVER");*/
+ dhcp_send_DISCOVER(sock);
+ break;
+
+ case STATE_DHCP_REQUEST :
+ /*debugf("<<timeout>> state : STATE_DHCP_REQUEST");*/
+ dhcp_send_REQUEST(sock);
+ break;
+
+ case STATE_DHCP_REREQUEST :
+ /*debugf("<<timeout>> state : STATE_DHCP_REREQUEST");*/
+ dhcp_send_REQUEST(sock);
+ break;
+
+ default :
+ break;
+ }
+
+ dhcp_tick_1s = 0;
+ dhcp_tick_next = dhcp_tick_1s + DHCP_WAIT_TIME;
+ dhcp_retry_count++;
+ }
+ } else { /* timeout occurred */
+
+ switch(dhcp_state) {
+ case STATE_DHCP_DISCOVER:
+ dhcp_state = STATE_DHCP_INIT;
+ ret = DHCP_RET_FAILED;
+ break;
+ case STATE_DHCP_REQUEST:
+ case STATE_DHCP_REREQUEST:
+ dhcp_send_DISCOVER(sock);
+ dhcp_state = STATE_DHCP_DISCOVER;
+ break;
+ default :
+ break;
+ }
+ dhcp_reset_timeout();
+ }
+ return ret;
+}
+
+static int8_t dhcp_check_leasedIP(implements_net_packet_conn *sock) {
+ int32_t ret;
+
+ /* IP conflict detection : ARP request - ARP reply */
+ /* Broadcasting ARP Request for check the IP conflict using UDP sendto() function */
+ ret = VCALL(sock, sendto, "CHECK_IP_CONFLICT", 17, global_lease.addr, 5000);
+
+ if (ret == NET_ETIMEDOUT) {
+ /* UDP send Timeout occurred : allocated IP address is unique, DHCP Success */
+ debugf("\r\n> Check leased IP - OK");
+ return 1;
+ }
+
+ /* Received ARP reply or etc : IP address conflict occur, DHCP Failed */
+ dhcp_send_DECLINE(sock);
+
+ ret = dhcp_tick_1s;
+ while ((dhcp_tick_1s - ret) < 2) {} /* wait for 1s over; wait to complete to send DECLINE message; */
+
+ return 0;
+}
+
+COROUTINE DHCP_cr(struct w5500 *chip, uint8_t socknum,
+ struct net_eth_addr self_eth_addr,
+ char *self_hostname,
+ dhcp_callback_t cb) {
+ uint32_t xid;
+ implements_net_packet_conn *sock;
+
+ xid = 0x12345678;
+ xid += self_eth_addr.octets[3];
+ xid += self_eth_addr.octets[4];
+ xid += self_eth_addr.octets[5];
+ xid += (self_eth_addr.octets[3] ^
+ self_eth_addr.octets[4] ^
+ self_eth_addr.octets[5]);
+
+ dhcp_tick_1s = 0;
+ dhcp_tick_next = DHCP_WAIT_TIME;
+ dhcp_retry_count = 0;
+ dhcp_state = STATE_DHCP_INIT;
+
+ sock = w5500_udp_conn(chip, socknum, DHCP_PORT_CLIENT);
+
+ ret = DHCP_RET_RUNNING;
+ msg_type = dhcp_msg_parse(sock);
+
+ for (;;) {
+ /* State transition diagram: https://datatracker.ietf.org/doc/html/rfc2131#page-35 */
+ switch (state) {
+ case STATE_DHCP_INIT:
+ dhcp_send_DISCOVER(sock);
+ dhcp_state = STATE_DHCP_DISCOVER;
+ break;
+ case STATE_DHCP_DISCOVER :
+ if (msg_type == DHCP_MSGTYP_OFFER){
+ debugf("> Receive DHCP_OFFER");
+ global_lease.addr = pDHCPMSG->yiaddr;
+
+ dhcp_send_REQUEST(sock);
+ dhcp_state = STATE_DHCP_REQUEST;
+ } else
+ ret = dhcp_check_timeout(sock);
+ break;
+
+ case STATE_DHCP_REQUEST :
+ if (msg_type == DHCP_MSGTYP_ACK) {
+ debugf("> Receive DHCP_ACK");
+ if (dhcp_check_leasedIP(sock)) {
+ /* Network info assignment from DHCP */
+ cb(DHCP_ASSIGN, global_lease);
+ dhcp_reset_timeout();
+
+ dhcp_state = STATE_DHCP_LEASED;
+ } else {
+ /* IP address conflict occurred */
+ dhcp_reset_timeout();
+ cb(DHCP_CONFLICT, global_lease);
+ dhcp_state = STATE_DHCP_INIT;
+ }
+ } else if (msg_type == DHCP_MSGTYP_NAK) {
+ debugf("> Receive DHCP_NACK");
+
+ dhcp_reset_timeout();
+
+ dhcp_state = STATE_DHCP_DISCOVER;
+ } else
+ ret = dhcp_check_timeout(sock);
+ break;
+
+ case STATE_DHCP_LEASED :
+ ret = DHCP_RET_IP_LEASED;
+ if ((global_lease.lifetime != DHCP_INFINITY) && ((global_lease.lifetime/2) < dhcp_tick_1s)) {
+ debugf("> Maintains the IP address");
+
+ msg_type = 0;
+ OLD_allocated_ip = global_lease.addr;
+
+ xid++;
+
+ dhcp_send_REQUEST(sock);
+
+ dhcp_reset_timeout();
+
+ dhcp_state = STATE_DHCP_REREQUEST;
+ }
+ break;
+
+ case STATE_DHCP_REREQUEST :
+ ret = DHCP_RET_IP_LEASED;
+ if (msg_type == DHCP_MSGTYP_ACK) {
+ dhcp_retry_count = 0;
+ if (memcmp(OLD_allocated_ip.octets, global_lease.addr.octets, sizeof(global_lease.addr))) {
+ ret = DHCP_RET_IP_CHANGED;
+ cb(DHCP_UPDATE, global_lease);
+ debugf(">IP changed.");
+ } else {
+ debugf(">IP is continued.");
+ }
+ dhcp_reset_timeout();
+ dhcp_state = STATE_DHCP_LEASED;
+ } else if (msg_type == DHCP_MSGTYP_NAK) {
+ debugf("> Receive DHCP_NACK, Failed to maintain ip");
+ dhcp_reset_timeout();
+
+ dhcp_state = STATE_DHCP_DISCOVER;
+ } else
+ ret = dhcp_check_timeout(sock);
+ break;
+ default :
+ break;
+ }
+
+ return ret;
+}