From 288648df6fa33c974824bd023a6baba11d6eefd8 Mon Sep 17 00:00:00 2001
From: "Luke T. Shumaker" <lukeshu@lukeshu.com>
Date: Mon, 11 Nov 2024 19:27:08 -0700
Subject: libdhcp: Move files around

---
 libdhcp/CMakeLists.txt           |   2 +-
 libdhcp/dhcp.c                   | 710 ---------------------------------------
 libdhcp/dhcp_client.c            | 573 +++++++++++++++++++++++++++++++
 libdhcp/dhcp_common.h            | 207 ++++++++++++
 libdhcp/include/libdhcp/client.h | 158 +++++++++
 libdhcp/include/libdhcp/dhcp.h   | 158 ---------
 6 files changed, 939 insertions(+), 869 deletions(-)
 delete mode 100644 libdhcp/dhcp.c
 create mode 100644 libdhcp/dhcp_client.c
 create mode 100644 libdhcp/dhcp_common.h
 create mode 100644 libdhcp/include/libdhcp/client.h
 delete mode 100644 libdhcp/include/libdhcp/dhcp.h

diff --git a/libdhcp/CMakeLists.txt b/libdhcp/CMakeLists.txt
index a14fd50..2ded1f4 100644
--- a/libdhcp/CMakeLists.txt
+++ b/libdhcp/CMakeLists.txt
@@ -6,7 +6,7 @@
 add_library(libdhcp INTERFACE)
 target_include_directories(libdhcp SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
 target_sources(libdhcp INTERFACE
-	dhcp.c
+	dhcp_client.c
 )
 target_link_libraries(libdhcp INTERFACE
 	libmisc
diff --git a/libdhcp/dhcp.c b/libdhcp/dhcp.c
deleted file mode 100644
index 9786018..0000000
--- a/libdhcp/dhcp.c
+++ /dev/null
@@ -1,710 +0,0 @@
-/* libdhcp/dhcp.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>
-
-/* 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
-#ifndef CONFIG_DHCP_OPT_SIZE
-	#define CONFIG_DHCP_OPT_SIZE            312
-#endif
-
-/* RFC 2131 definitions *******************************************************/
-
-/** https://datatracker.ietf.org/doc/html/rfc2131#page-9 */
-struct dhcp_msg {
-	uint8_t                 op;         /* DHCP_OP_{x} */
-	uint8_t                 htype;      /* DHCP_HTYPE_{x} */
-	uint8_t                 hlen;       /* length of ->chaddr (`sizeof(struct net_eth_addr)`) */
-	uint8_t                 hops;
-
-	uint32be_t              xid;        /* transaction ID */
-
-	uint16be_t              secs;
-	uint16be_t              flags;      /* DHCP_FLAG_{x} */
-
-	struct net_ip4_addr     ciaddr;     /* Request IP to DHCP sever */
-	struct net_ip4_addr     yiaddr;     /* Offered IP from DHCP server */
-	struct net_ip4_addr     siaddr;     /* next-server IP address (not used) */
-	struct net_ip4_addr     giaddr;     /* relay-agent IP address (not used) */
-	uint8_t                 chaddr[16]; /* client hardware (MAC) address */
-	uint8_t                 sname[64];  /* server name (not used) */
-	uint8_t                 file[128];  /* boot file name (not used) */
-
-	uint8_t                 options[CONFIG_DHCP_OPT_SIZE];
-};
-static_assert(offsetof(struct dhcp_msg, options) == 236);
-#define DHCP_MSG_BASE_SIZE       offsetof(struct dhcp_msg, options)
-
-/** https://datatracker.ietf.org/doc/html/rfc2131#page-10 */
-#define DHCP_OP_BOOTREQUEST      1
-#define DHCP_OP_BOOTREPLY        2
-
-/** https://datatracker.ietf.org/doc/html/rfc2131#page-11 */
-#define DHCP_FLAG_BROADCAST      0x8000
-
-/** https://datatracker.ietf.org/doc/html/rfc2131#section-3 */
-static const uint8_t dhcp_magic_cookie[] = {99, 130, 83, 99};
-
-/** https://datatracker.ietf.org/doc/html/rfc2131#section-3.3 */
-#define DHCP_INFINITY            0xffffffff
-
-/* IANA assignments ***********************************************************/
-
-/**
- * Port Numbers
- * https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
- */
-#define DHCP_PORT_SERVER                ((uint16_t)67)
-#define DHCP_PORT_CLIENT                ((uint16_t)68)
-
-/**
- * Hardware Types
- * https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml#arp-parameters-2
- */
-#define DHCP_HTYPE_ETHERNET             ((uint8_t)  1)
-
-/**
- * DHCP Options
- * https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#options
- */
-#define DHCP_OPT_PAD                    ((uint8_t)  0) /* RFC2132: length: 0; meaning: None */
-#define DHCP_OPT_SUBNET_MASK            ((uint8_t)  1) /* RFC2132: length: 4; meaning: Subnet Mask Value */
-#define DHCP_OPT_TIME_OFFSET            ((uint8_t)  2) /* RFC2132: length: 4; meaning: Time Offset in Seconds from UTC (note: deprecated by 100 and 101 */
-#define DHCP_OPT_ROUTER                 ((uint8_t)  3) /* RFC2132: length: N; meaning: N/4 Router addresses */
-#define DHCP_OPT_TIME_SERVER            ((uint8_t)  4) /* RFC2132: length: N; meaning: N/4 Timeserver addresses */
-#define DHCP_OPT_NAME_SERVER            ((uint8_t)  5) /* RFC2132: length: N; meaning: N/4 IEN-116 Server addresses */
-#define DHCP_OPT_DOMAIN_SERVER          ((uint8_t)  6) /* RFC2132: length: N; meaning: N/4 DNS Server addresses */
-#define DHCP_OPT_LOG_SERVER             ((uint8_t)  7) /* RFC2132: length: N; meaning: N/4 Logging Server addresses */
-#define DHCP_OPT_QUOTES_SERVER          ((uint8_t)  8) /* RFC2132: length: N; meaning: N/4 Quotes Server addresses */
-#define DHCP_OPT_LPR_SERVER             ((uint8_t)  9) /* RFC2132: length: N; meaning: N/4 Printer Server addresses */
-#define DHCP_OPT_IMPRESS_SERVER         ((uint8_t) 10) /* RFC2132: length: N; meaning: N/4 Impress Server addresses */
-#define DHCP_OPT_RLP_SERVER             ((uint8_t) 11) /* RFC2132: length: N; meaning: N/4 RLP Server addresses */
-#define DHCP_OPT_HOSTNAME               ((uint8_t) 12) /* RFC2132: length: N; meaning: Hostname string */
-#define DHCP_OPT_BOOT_FILE_SIZE         ((uint8_t) 13) /* RFC2132: length: 2; meaning: Size of boot file in 512 byte chunks */
-#define DHCP_OPT_MERIT_DUMP_FILE        ((uint8_t) 14) /* RFC2132: length: N; meaning: Client to dump and name the file to dump it to */
-#define DHCP_OPT_DOMAIN_NAME            ((uint8_t) 15) /* RFC2132: length: N; meaning: The DNS domain name of the client */
-#define DHCP_OPT_SWAP_SERVER            ((uint8_t) 16) /* RFC2132: length: N; meaning: Swap Server address */
-#define DHCP_OPT_ROOT_PATH              ((uint8_t) 17) /* RFC2132: length: N; meaning: Path name for root disk */
-#define DHCP_OPT_EXTENSION_FILE         ((uint8_t) 18) /* RFC2132: length: N; meaning: Path name for more BOOTP info */
-#define DHCP_OPT_FORWARD_ONOFF          ((uint8_t) 19) /* RFC2132: length: 1; meaning: Enable/Disable IP Forwarding */
-#define DHCP_OPT_SRCRTE_ONOFF           ((uint8_t) 20) /* RFC2132: length: 1; meaning: Enable/Disable Source Routing */
-#define DHCP_OPT_POLICY_FILTER          ((uint8_t) 21) /* RFC2132: length: N; meaning: Routing Policy Filters */
-#define DHCP_OPT_MAX_DG_ASSEMBLY        ((uint8_t) 22) /* RFC2132: length: 2; meaning: Max Datagram Reassembly Size */
-#define DHCP_OPT_DEFAULT_IP_TTL         ((uint8_t) 23) /* RFC2132: length: 1; meaning: Default IP Time to Live */
-#define DHCP_OPT_MTU_TIMEOUT            ((uint8_t) 24) /* RFC2132: length: 4; meaning: Path MTU Aging Timeout */
-#define DHCP_OPT_MTU_PLATEAU            ((uint8_t) 25) /* RFC2132: length: N; meaning: Path MTU Plateau Table */
-#define DHCP_OPT_MTU_INTERFACE          ((uint8_t) 26) /* RFC2132: length: 2; meaning: Interface MTU Size */
-#define DHCP_OPT_MTU_SUBNET             ((uint8_t) 27) /* RFC2132: length: 1; meaning: All Subnets are Local */
-#define DHCP_OPT_BROADCAST_ADDRESS      ((uint8_t) 28) /* RFC2132: length: 4; meaning: Broadcast Address */
-#define DHCP_OPT_MASK_DISCOVERY         ((uint8_t) 29) /* RFC2132: length: 1; meaning: Perform Mask Discovery */
-#define DHCP_OPT_MASK_SUPPLIER          ((uint8_t) 30) /* RFC2132: length: 1; meaning: Provide Mask to Others */
-#define DHCP_OPT_ROUTER_DISCOVERY       ((uint8_t) 31) /* RFC2132: length: 1; meaning: Perform Router Discovery */
-#define DHCP_OPT_ROUTER_REQUEST         ((uint8_t) 32) /* RFC2132: length: 4; meaning: Router Solicitation Address */
-#define DHCP_OPT_STATIC_ROUTE           ((uint8_t) 33) /* RFC2132: length: N; meaning: Static Routing Table */
-#define DHCP_OPT_TRAILERS               ((uint8_t) 34) /* RFC2132: length: 1; meaning: Trailer Encapsulation */
-#define DHCP_OPT_ARP_TIMEOUT            ((uint8_t) 35) /* RFC2132: length: 4; meaning: ARP Cache Timeout */
-#define DHCP_OPT_ETHERNET               ((uint8_t) 36) /* RFC2132: length: 1; meaning: Ethernet Encapsulation */
-#define DHCP_OPT_DEFAULT_TCP_TTL        ((uint8_t) 37) /* RFC2132: length: 1; meaning: Default TCP Time to Live */
-#define DHCP_OPT_KEEPALIVE_TIME         ((uint8_t) 38) /* RFC2132: length: 4; meaning: TCP Keepalive Interval */
-#define DHCP_OPT_KEEPALIVE_DATA         ((uint8_t) 39) /* RFC2132: length: 1; meaning: TCP Keepalive Garbage */
-#define DHCP_OPT_NIS_DOMAIN             ((uint8_t) 40) /* RFC2132: length: N; meaning: NIS Domain Name */
-#define DHCP_OPT_NIS_SERVERS            ((uint8_t) 41) /* RFC2132: length: N; meaning: NIS Server Addresses */
-#define DHCP_OPT_NTP_SERVERS            ((uint8_t) 42) /* RFC2132: length: N; meaning: NTP Server Addresses */
-#define DHCP_OPT_VENDOR_SPECIFIC        ((uint8_t) 43) /* RFC2132: length: N; meaning: Vendor Specific Information */
-#define DHCP_OPT_NETBIOS_NAME_SRV       ((uint8_t) 44) /* RFC2132: length: N; meaning: NETBIOS Name Servers */
-#define DHCP_OPT_NETBIOS_DIST_SRV       ((uint8_t) 45) /* RFC2132: length: N; meaning: NETBIOS Datagram Distribution */
-#define DHCP_OPT_NETBIOS_NODE_TYPE      ((uint8_t) 46) /* RFC2132: length: 1; meaning: NETBIOS Node Type */
-#define DHCP_OPT_NETBIOS_SCOPE          ((uint8_t) 47) /* RFC2132: length: N; meaning: NETBIOS Scope */
-#define DHCP_OPT_X_WINDOW_FONT          ((uint8_t) 48) /* RFC2132: length: N; meaning: X Window Font Server */
-#define DHCP_OPT_X_WINDOW_MANAGER       ((uint8_t) 49) /* RFC2132: length: N; meaning: X Window Display Manager */
-#define DHCP_OPT_ADDRESS_REQUEST        ((uint8_t) 50) /* RFC2132: length: 4; meaning: Requested IP Address */
-#define DHCP_OPT_ADDRESS_TIME           ((uint8_t) 51) /* RFC2132: length: 4; meaning: IP Address Lease Time */
-#define DHCP_OPT_OVERLOAD               ((uint8_t) 52) /* RFC2132: length: 1; meaning: Overload "sname" or "file */
-#define DHCP_OPT_DHCP_MSG_TYPE          ((uint8_t) 53) /* RFC2132: length: 1; meaning: DHCP Message Type */
-#define DHCP_OPT_DHCP_SERVER_ID         ((uint8_t) 54) /* RFC2132: length: 4; meaning: DHCP Server Identification */
-#define DHCP_OPT_PARAMETER_LIST         ((uint8_t) 55) /* RFC2132: length: N; meaning: Parameter Request List */
-#define DHCP_OPT_DHCP_MESSAGE           ((uint8_t) 56) /* RFC2132: length: N; meaning: DHCP Error Message */
-#define DHCP_OPT_DHCP_MAX_MSG_SIZE      ((uint8_t) 57) /* RFC2132: length: 2; meaning: DHCP Maximum Message Size */
-#define DHCP_OPT_RENEWAL_TIME           ((uint8_t) 58) /* RFC2132: length: 4; meaning: DHCP Renewal (T1) Time */
-#define DHCP_OPT_REBINDING_TIME         ((uint8_t) 59) /* RFC2132: length: 4; meaning: DHCP Rebinding (T2) Time */
-#define DHCP_OPT_CLASS_ID               ((uint8_t) 60) /* RFC2132: length: N; meaning: Class Identifier */
-#define DHCP_OPT_CLIENT_ID              ((uint8_t) 61) /* RFC2132: length: N; meaning: Client Identifier */
-#define DHCP_OPT_END                    ((uint8_t)255) /* RFC2132: length: 0; meaning: None */
-
-/**
- * DHCP Message Type 53 Values
- * https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#message-type-53
- */
-#define DHCP_MSGTYP_DISCOVER            ((uint8_t)  1) /* RFC2132 */
-#define DHCP_MSGTYP_OFFER               ((uint8_t)  2) /* RFC2132 */
-#define DHCP_MSGTYP_REQUEST             ((uint8_t)  3) /* RFC2132 */
-#define DHCP_MSGTYP_DECLINE             ((uint8_t)  4) /* RFC2132 */
-#define DHCP_MSGTYP_ACK                 ((uint8_t)  5) /* RFC2132 */
-#define DHCP_MSGTYP_NAK                 ((uint8_t)  6) /* RFC2132 */
-#define DHCP_MSGTYP_RELEASE             ((uint8_t)  7) /* RFC2132 */
-#define DHCP_MSGTYP_INFORM              ((uint8_t)  8) /* RFC2132 */
-
-/* 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;
-}
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;
+}
diff --git a/libdhcp/dhcp_common.h b/libdhcp/dhcp_common.h
new file mode 100644
index 0000000..2bde7a4
--- /dev/null
+++ b/libdhcp/dhcp_common.h
@@ -0,0 +1,207 @@
+/* libdhcp/dhcp_common.c - TODO
+ *
+ * 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
+ */
+
+/* Config *********************************************************************/
+
+#ifndef CONFIG_DHCP_OPT_SIZE
+	#define CONFIG_DHCP_OPT_SIZE            312
+#endif
+
+/* RFC 2131 definitions *******************************************************/
+
+/** https://datatracker.ietf.org/doc/html/rfc2131#page-9 */
+struct dhcp_msg {
+	uint8_t                 op;         /* DHCP_OP_{x} */
+	uint8_t                 htype;      /* DHCP_HTYPE_{x} */
+	uint8_t                 hlen;       /* length of ->chaddr (`sizeof(struct net_eth_addr)`) */
+	uint8_t                 hops;
+
+	uint32be_t              xid;        /* transaction ID */
+
+	uint16be_t              secs;
+	uint16be_t              flags;      /* DHCP_FLAG_{x} */
+
+	struct net_ip4_addr     ciaddr;     /* Request IP to DHCP sever */
+	struct net_ip4_addr     yiaddr;     /* Offered IP from DHCP server */
+	struct net_ip4_addr     siaddr;     /* next-server IP address (not used) */
+	struct net_ip4_addr     giaddr;     /* relay-agent IP address (not used) */
+	uint8_t                 chaddr[16]; /* client hardware (MAC) address */
+	uint8_t                 sname[64];  /* server name (not used) */
+	uint8_t                 file[128];  /* boot file name (not used) */
+
+	uint8_t                 options[CONFIG_DHCP_OPT_SIZE];
+};
+static_assert(offsetof(struct dhcp_msg, options) == 236);
+#define DHCP_MSG_BASE_SIZE       offsetof(struct dhcp_msg, options)
+
+/** https://datatracker.ietf.org/doc/html/rfc2131#page-10 */
+#define DHCP_OP_BOOTREQUEST      1
+#define DHCP_OP_BOOTREPLY        2
+
+/** https://datatracker.ietf.org/doc/html/rfc2131#page-11 */
+#define DHCP_FLAG_BROADCAST      0x8000
+
+/** https://datatracker.ietf.org/doc/html/rfc2131#section-3 */
+static const uint8_t dhcp_magic_cookie[] = {99, 130, 83, 99};
+
+/** https://datatracker.ietf.org/doc/html/rfc2131#section-3.3 */
+#define DHCP_INFINITY            0xffffffff
+
+/* IANA assignments ***********************************************************/
+
+/**
+ * Port Numbers
+ * https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
+ */
+#define DHCP_PORT_SERVER                ((uint16_t)67)
+#define DHCP_PORT_CLIENT                ((uint16_t)68)
+
+/**
+ * Hardware Types
+ * https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml#arp-parameters-2
+ */
+#define DHCP_HTYPE_ETHERNET             ((uint8_t)  1)
+
+/**
+ * DHCP Options
+ * https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#options
+ */
+#define DHCP_OPT_PAD                    ((uint8_t)  0) /* RFC2132: length: 0; meaning: None */
+#define DHCP_OPT_SUBNET_MASK            ((uint8_t)  1) /* RFC2132: length: 4; meaning: Subnet Mask Value */
+#define DHCP_OPT_TIME_OFFSET            ((uint8_t)  2) /* RFC2132: length: 4; meaning: Time Offset in Seconds from UTC (note: deprecated by 100 and 101 */
+#define DHCP_OPT_ROUTER                 ((uint8_t)  3) /* RFC2132: length: N; meaning: N/4 Router addresses */
+#define DHCP_OPT_TIME_SERVER            ((uint8_t)  4) /* RFC2132: length: N; meaning: N/4 Timeserver addresses */
+#define DHCP_OPT_NAME_SERVER            ((uint8_t)  5) /* RFC2132: length: N; meaning: N/4 IEN-116 Server addresses */
+#define DHCP_OPT_DOMAIN_SERVER          ((uint8_t)  6) /* RFC2132: length: N; meaning: N/4 DNS Server addresses */
+#define DHCP_OPT_LOG_SERVER             ((uint8_t)  7) /* RFC2132: length: N; meaning: N/4 Logging Server addresses */
+#define DHCP_OPT_QUOTES_SERVER          ((uint8_t)  8) /* RFC2132: length: N; meaning: N/4 Quotes Server addresses */
+#define DHCP_OPT_LPR_SERVER             ((uint8_t)  9) /* RFC2132: length: N; meaning: N/4 Printer Server addresses */
+#define DHCP_OPT_IMPRESS_SERVER         ((uint8_t) 10) /* RFC2132: length: N; meaning: N/4 Impress Server addresses */
+#define DHCP_OPT_RLP_SERVER             ((uint8_t) 11) /* RFC2132: length: N; meaning: N/4 RLP Server addresses */
+#define DHCP_OPT_HOSTNAME               ((uint8_t) 12) /* RFC2132: length: N; meaning: Hostname string */
+#define DHCP_OPT_BOOT_FILE_SIZE         ((uint8_t) 13) /* RFC2132: length: 2; meaning: Size of boot file in 512 byte chunks */
+#define DHCP_OPT_MERIT_DUMP_FILE        ((uint8_t) 14) /* RFC2132: length: N; meaning: Client to dump and name the file to dump it to */
+#define DHCP_OPT_DOMAIN_NAME            ((uint8_t) 15) /* RFC2132: length: N; meaning: The DNS domain name of the client */
+#define DHCP_OPT_SWAP_SERVER            ((uint8_t) 16) /* RFC2132: length: N; meaning: Swap Server address */
+#define DHCP_OPT_ROOT_PATH              ((uint8_t) 17) /* RFC2132: length: N; meaning: Path name for root disk */
+#define DHCP_OPT_EXTENSION_FILE         ((uint8_t) 18) /* RFC2132: length: N; meaning: Path name for more BOOTP info */
+#define DHCP_OPT_FORWARD_ONOFF          ((uint8_t) 19) /* RFC2132: length: 1; meaning: Enable/Disable IP Forwarding */
+#define DHCP_OPT_SRCRTE_ONOFF           ((uint8_t) 20) /* RFC2132: length: 1; meaning: Enable/Disable Source Routing */
+#define DHCP_OPT_POLICY_FILTER          ((uint8_t) 21) /* RFC2132: length: N; meaning: Routing Policy Filters */
+#define DHCP_OPT_MAX_DG_ASSEMBLY        ((uint8_t) 22) /* RFC2132: length: 2; meaning: Max Datagram Reassembly Size */
+#define DHCP_OPT_DEFAULT_IP_TTL         ((uint8_t) 23) /* RFC2132: length: 1; meaning: Default IP Time to Live */
+#define DHCP_OPT_MTU_TIMEOUT            ((uint8_t) 24) /* RFC2132: length: 4; meaning: Path MTU Aging Timeout */
+#define DHCP_OPT_MTU_PLATEAU            ((uint8_t) 25) /* RFC2132: length: N; meaning: Path MTU Plateau Table */
+#define DHCP_OPT_MTU_INTERFACE          ((uint8_t) 26) /* RFC2132: length: 2; meaning: Interface MTU Size */
+#define DHCP_OPT_MTU_SUBNET             ((uint8_t) 27) /* RFC2132: length: 1; meaning: All Subnets are Local */
+#define DHCP_OPT_BROADCAST_ADDRESS      ((uint8_t) 28) /* RFC2132: length: 4; meaning: Broadcast Address */
+#define DHCP_OPT_MASK_DISCOVERY         ((uint8_t) 29) /* RFC2132: length: 1; meaning: Perform Mask Discovery */
+#define DHCP_OPT_MASK_SUPPLIER          ((uint8_t) 30) /* RFC2132: length: 1; meaning: Provide Mask to Others */
+#define DHCP_OPT_ROUTER_DISCOVERY       ((uint8_t) 31) /* RFC2132: length: 1; meaning: Perform Router Discovery */
+#define DHCP_OPT_ROUTER_REQUEST         ((uint8_t) 32) /* RFC2132: length: 4; meaning: Router Solicitation Address */
+#define DHCP_OPT_STATIC_ROUTE           ((uint8_t) 33) /* RFC2132: length: N; meaning: Static Routing Table */
+#define DHCP_OPT_TRAILERS               ((uint8_t) 34) /* RFC2132: length: 1; meaning: Trailer Encapsulation */
+#define DHCP_OPT_ARP_TIMEOUT            ((uint8_t) 35) /* RFC2132: length: 4; meaning: ARP Cache Timeout */
+#define DHCP_OPT_ETHERNET               ((uint8_t) 36) /* RFC2132: length: 1; meaning: Ethernet Encapsulation */
+#define DHCP_OPT_DEFAULT_TCP_TTL        ((uint8_t) 37) /* RFC2132: length: 1; meaning: Default TCP Time to Live */
+#define DHCP_OPT_KEEPALIVE_TIME         ((uint8_t) 38) /* RFC2132: length: 4; meaning: TCP Keepalive Interval */
+#define DHCP_OPT_KEEPALIVE_DATA         ((uint8_t) 39) /* RFC2132: length: 1; meaning: TCP Keepalive Garbage */
+#define DHCP_OPT_NIS_DOMAIN             ((uint8_t) 40) /* RFC2132: length: N; meaning: NIS Domain Name */
+#define DHCP_OPT_NIS_SERVERS            ((uint8_t) 41) /* RFC2132: length: N; meaning: NIS Server Addresses */
+#define DHCP_OPT_NTP_SERVERS            ((uint8_t) 42) /* RFC2132: length: N; meaning: NTP Server Addresses */
+#define DHCP_OPT_VENDOR_SPECIFIC        ((uint8_t) 43) /* RFC2132: length: N; meaning: Vendor Specific Information */
+#define DHCP_OPT_NETBIOS_NAME_SRV       ((uint8_t) 44) /* RFC2132: length: N; meaning: NETBIOS Name Servers */
+#define DHCP_OPT_NETBIOS_DIST_SRV       ((uint8_t) 45) /* RFC2132: length: N; meaning: NETBIOS Datagram Distribution */
+#define DHCP_OPT_NETBIOS_NODE_TYPE      ((uint8_t) 46) /* RFC2132: length: 1; meaning: NETBIOS Node Type */
+#define DHCP_OPT_NETBIOS_SCOPE          ((uint8_t) 47) /* RFC2132: length: N; meaning: NETBIOS Scope */
+#define DHCP_OPT_X_WINDOW_FONT          ((uint8_t) 48) /* RFC2132: length: N; meaning: X Window Font Server */
+#define DHCP_OPT_X_WINDOW_MANAGER       ((uint8_t) 49) /* RFC2132: length: N; meaning: X Window Display Manager */
+#define DHCP_OPT_ADDRESS_REQUEST        ((uint8_t) 50) /* RFC2132: length: 4; meaning: Requested IP Address */
+#define DHCP_OPT_ADDRESS_TIME           ((uint8_t) 51) /* RFC2132: length: 4; meaning: IP Address Lease Time */
+#define DHCP_OPT_OVERLOAD               ((uint8_t) 52) /* RFC2132: length: 1; meaning: Overload "sname" or "file */
+#define DHCP_OPT_DHCP_MSG_TYPE          ((uint8_t) 53) /* RFC2132: length: 1; meaning: DHCP Message Type */
+#define DHCP_OPT_DHCP_SERVER_ID         ((uint8_t) 54) /* RFC2132: length: 4; meaning: DHCP Server Identification */
+#define DHCP_OPT_PARAMETER_LIST         ((uint8_t) 55) /* RFC2132: length: N; meaning: Parameter Request List */
+#define DHCP_OPT_DHCP_MESSAGE           ((uint8_t) 56) /* RFC2132: length: N; meaning: DHCP Error Message */
+#define DHCP_OPT_DHCP_MAX_MSG_SIZE      ((uint8_t) 57) /* RFC2132: length: 2; meaning: DHCP Maximum Message Size */
+#define DHCP_OPT_RENEWAL_TIME           ((uint8_t) 58) /* RFC2132: length: 4; meaning: DHCP Renewal (T1) Time */
+#define DHCP_OPT_REBINDING_TIME         ((uint8_t) 59) /* RFC2132: length: 4; meaning: DHCP Rebinding (T2) Time */
+#define DHCP_OPT_CLASS_ID               ((uint8_t) 60) /* RFC2132: length: N; meaning: Class Identifier */
+#define DHCP_OPT_CLIENT_ID              ((uint8_t) 61) /* RFC2132: length: N; meaning: Client Identifier */
+#define DHCP_OPT_END                    ((uint8_t)255) /* RFC2132: length: 0; meaning: None */
+
+/**
+ * DHCP Message Type 53 Values
+ * https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#message-type-53
+ */
+#define DHCP_MSGTYP_DISCOVER            ((uint8_t)  1) /* RFC2132 */
+#define DHCP_MSGTYP_OFFER               ((uint8_t)  2) /* RFC2132 */
+#define DHCP_MSGTYP_REQUEST             ((uint8_t)  3) /* RFC2132 */
+#define DHCP_MSGTYP_DECLINE             ((uint8_t)  4) /* RFC2132 */
+#define DHCP_MSGTYP_ACK                 ((uint8_t)  5) /* RFC2132 */
+#define DHCP_MSGTYP_NAK                 ((uint8_t)  6) /* RFC2132 */
+#define DHCP_MSGTYP_RELEASE             ((uint8_t)  7) /* RFC2132 */
+#define DHCP_MSGTYP_INFORM              ((uint8_t)  8) /* RFC2132 */
diff --git a/libdhcp/include/libdhcp/client.h b/libdhcp/include/libdhcp/client.h
new file mode 100644
index 0000000..6037a3b
--- /dev/null
+++ b/libdhcp/include/libdhcp/client.h
@@ -0,0 +1,158 @@
+/* libdhcp/client.h - 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.h
+ *
+ * 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
+ */
+
+#ifndef _LIBDHCP_DHCP_H_
+#define _LIBDHCP_DHCP_H_
+
+#include <libhw/generic/net.h>
+#include "hw/w5500.h"
+
+enum dhcp_event {
+	DHCP_ASSIGN,
+	DHCP_UPDATE,
+	DHCP_CONFLICT,
+};
+
+typedef void (*dhcp_callback_t)(enum dhcp_event, struct dhcp_lease);
+
+/* Retry to processing DHCP */
+#define MAX_DHCP_RETRY           2        ///< Maximum retry count
+#define DHCP_WAIT_TIME           10       ///< Wait Time 10s
+
+/*
+ * @brief return value of @ref DHCP_run()
+ */
+enum {
+	DHCP_RET_FAILED = 0,  // Processing Fail
+	DHCP_RET_RUNNING,     // Processing DHCP protocol
+	DHCP_RET_IP_ASSIGN,   // First Occupy IP from DHPC server      (if cbfunc == null, act as default default_ip_assign)
+	DHCP_RET_IP_CHANGED,  // Change IP address by new ip from DHCP (if cbfunc == null, act as default default_ip_update)
+	DHCP_RET_IP_LEASED,   // Stand by
+	DHCP_RET_STOPPED      // Stop processing DHCP protocol
+};
+
+/*
+ * @brief DHCP client initialization (outside of the main loop)
+ * @param buf - buffer for processing DHCP message
+ */
+void DHCP_init(void *buf);
+
+/*
+ * @brief DHCP 1s Tick Timer handler
+ * @note SHOULD BE register to your system 1s Tick timer handler
+ */
+void DHCP_time_handler(void);
+
+/*
+ * @brief DHCP client in the main loop
+ * @return    The value is as the follow \n
+ *            @ref DHCP_FAILED     \n
+ *            @ref DHCP_RUNNING    \n
+ *            @ref DHCP_IP_ASSIGN  \n
+ *            @ref DHCP_IP_CHANGED \n
+ * 			  @ref DHCP_IP_LEASED  \n
+ *            @ref DHCP_STOPPED    \n
+ *
+ * @note This function is always called by you main task.
+ */
+uint8_t DHCP_run(struct w5500 *chip, uint8_t socknum, dhcp_callback_t cb);
+//uint8_t DHCP_run(implements_net_packet_conn *sock, dhcp_callback_t cb);
+
+/*
+ * @brief Stop DHCP processing
+ * @note If you want to restart. call DHCP_init() and DHCP_run()
+ */
+void    DHCP_stop(implements_net_packet_conn *sock);
+
+void xhandle(uint8_t opt_typ, uint8_t opt_len, uint8_t *opt_dat) {
+	switch (opt_typ) {
+	case DHCP_OPT_SUBNET_MASK:
+		if (opt_len != 4)
+			return;
+		global_lease.subnet_mask.octets[0] = opt_dat[0];
+		global_lease.subnet_mask.octets[1] = opt_dat[1];
+		global_lease.subnet_mask.octets[2] = opt_dat[2];
+		global_lease.subnet_mask.octets[3] = opt_dat[3];
+	case DHCP_OPT_ROUTER :
+		if (opt_len < 4)
+			return;
+		global_lease.gateway.octets[0] = opt_dat[0];
+		global_lease.gateway.octets[1] = opt_dat[1];
+		global_lease.gateway.octets[2] = opt_dat[2];
+		global_lease.gateway.octets[3] = opt_dat[3];
+		break;
+	case DHCP_OPT_DOMAIN_SERVER :
+		if (opt_len < 4)
+			return;
+		global_lease.dns.octets[0] = opt_dat[0];
+		global_lease.dns.octets[1] = opt_dat[1];
+		global_lease.dns.octets[2] = opt_dat[2];
+		global_lease.dns.octets[3] = opt_dat[3];
+		break;
+	}
+}
+
+#endif /* _LIBDHCP_DHCP_H_ */
diff --git a/libdhcp/include/libdhcp/dhcp.h b/libdhcp/include/libdhcp/dhcp.h
deleted file mode 100644
index 0b4c06f..0000000
--- a/libdhcp/include/libdhcp/dhcp.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/* libdhcp/dhcp.h - 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.h
- *
- * 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
- */
-
-#ifndef _LIBDHCP_DHCP_H_
-#define _LIBDHCP_DHCP_H_
-
-#include <libhw/generic/net.h>
-#include "hw/w5500.h"
-
-enum dhcp_event {
-	DHCP_ASSIGN,
-	DHCP_UPDATE,
-	DHCP_CONFLICT,
-};
-
-typedef void (*dhcp_callback_t)(enum dhcp_event, struct dhcp_lease);
-
-/* Retry to processing DHCP */
-#define MAX_DHCP_RETRY           2        ///< Maximum retry count
-#define DHCP_WAIT_TIME           10       ///< Wait Time 10s
-
-/*
- * @brief return value of @ref DHCP_run()
- */
-enum {
-	DHCP_RET_FAILED = 0,  // Processing Fail
-	DHCP_RET_RUNNING,     // Processing DHCP protocol
-	DHCP_RET_IP_ASSIGN,   // First Occupy IP from DHPC server      (if cbfunc == null, act as default default_ip_assign)
-	DHCP_RET_IP_CHANGED,  // Change IP address by new ip from DHCP (if cbfunc == null, act as default default_ip_update)
-	DHCP_RET_IP_LEASED,   // Stand by
-	DHCP_RET_STOPPED      // Stop processing DHCP protocol
-};
-
-/*
- * @brief DHCP client initialization (outside of the main loop)
- * @param buf - buffer for processing DHCP message
- */
-void DHCP_init(void *buf);
-
-/*
- * @brief DHCP 1s Tick Timer handler
- * @note SHOULD BE register to your system 1s Tick timer handler
- */
-void DHCP_time_handler(void);
-
-/*
- * @brief DHCP client in the main loop
- * @return    The value is as the follow \n
- *            @ref DHCP_FAILED     \n
- *            @ref DHCP_RUNNING    \n
- *            @ref DHCP_IP_ASSIGN  \n
- *            @ref DHCP_IP_CHANGED \n
- * 			  @ref DHCP_IP_LEASED  \n
- *            @ref DHCP_STOPPED    \n
- *
- * @note This function is always called by you main task.
- */
-uint8_t DHCP_run(struct w5500 *chip, uint8_t socknum, dhcp_callback_t cb);
-//uint8_t DHCP_run(implements_net_packet_conn *sock, dhcp_callback_t cb);
-
-/*
- * @brief Stop DHCP processing
- * @note If you want to restart. call DHCP_init() and DHCP_run()
- */
-void    DHCP_stop(implements_net_packet_conn *sock);
-
-void xhandle(uint8_t opt_typ, uint8_t opt_len, uint8_t *opt_dat) {
-	switch (opt_typ) {
-	case DHCP_OPT_SUBNET_MASK:
-		if (opt_len != 4)
-			return;
-		global_lease.subnet_mask.octets[0] = opt_dat[0];
-		global_lease.subnet_mask.octets[1] = opt_dat[1];
-		global_lease.subnet_mask.octets[2] = opt_dat[2];
-		global_lease.subnet_mask.octets[3] = opt_dat[3];
-	case DHCP_OPT_ROUTER :
-		if (opt_len < 4)
-			return;
-		global_lease.gateway.octets[0] = opt_dat[0];
-		global_lease.gateway.octets[1] = opt_dat[1];
-		global_lease.gateway.octets[2] = opt_dat[2];
-		global_lease.gateway.octets[3] = opt_dat[3];
-		break;
-	case DHCP_OPT_DOMAIN_SERVER :
-		if (opt_len < 4)
-			return;
-		global_lease.dns.octets[0] = opt_dat[0];
-		global_lease.dns.octets[1] = opt_dat[1];
-		global_lease.dns.octets[2] = opt_dat[2];
-		global_lease.dns.octets[3] = opt_dat[3];
-		break;
-	}
-}
-
-#endif /* _LIBDHCP_DHCP_H_ */
-- 
cgit v1.2.3-2-g168b