diff --git a/.clang-format b/.clang-format
index e7a61da..f12263a 100644
--- a/.clang-format
+++ b/.clang-format
@@ -72,7 +72,7 @@ BreakConstructorInitializersBeforeComma: false
 BreakConstructorInitializers: BeforeColon
 BreakAfterJavaFieldAnnotations: false
 BreakStringLiterals: true
-ColumnLimit: 140
+ColumnLimit: 120
 CommentPragmas: '^ IWYU pragma:'
 CompactNamespaces: true
 ConstructorInitializerAllOnOneLineOrOnePerLine: true
diff --git a/srcs/libs/include/misc.h b/srcs/libs/include/misc.h
index 902237b..6bd4c66 100644
--- a/srcs/libs/include/misc.h
+++ b/srcs/libs/include/misc.h
@@ -42,6 +42,8 @@ int                process_lock_pidfile(char *pFilePath);
 void               process_unlock_pidfile();
 int                get_all_network_info(PSYS_NIC_INFO pInfo);
 const char        *u32_to_str_ip(unsigned int ip);
+unsigned short     udp_checksum(unsigned int saddr, unsigned int daddr, unsigned char *pUdp);
+unsigned short     ip_checksum(unsigned char *pIp);
 #ifdef __cplusplus
 }
 #endif
diff --git a/srcs/libs/include/user_errno.h b/srcs/libs/include/user_errno.h
index 2caf25d..ae5e5fd 100644
--- a/srcs/libs/include/user_errno.h
+++ b/srcs/libs/include/user_errno.h
@@ -70,6 +70,7 @@ extern "C" {
     ERR_CODE(ERR_DHCP_NO_POOL, "找不到可用地址池")              \
     ERR_CODE(ERR_DHCP_NO_ADDR, "找不到可用IP地址")              \
     ERR_CODE(ERR_SOCK_CREATE, "创建套接字失败")                 \
+    ERR_CODE(ERR_SOCK_SEND, "套接字发送数据失败")               \
     ERR_CODE(ERR_SOCK_SETOPT, "设置套接字参数失败")
 
 #define GENERATE_ENUM(ENUM, x) ENUM,
diff --git a/srcs/libs/misc/misc.c b/srcs/libs/misc/misc.c
index 78fb9a7..303e269 100644
--- a/srcs/libs/misc/misc.c
+++ b/srcs/libs/misc/misc.c
@@ -11,12 +11,13 @@
 #include <sys/ioctl.h>
 #include <linux/if_ether.h>
 #include <sys/time.h>
+#include <netinet/udp.h>
+#include <netinet/ip.h>
 
 #include "user_errno.h"
 #include "misc.h"
 #include "zlog_module.h"
 #include "common.h"
-#include "uthash/uthash.h"
 
 PSYS_NIC_INFO g_sysNicInfo = NULL;
 
@@ -364,4 +365,54 @@ const char *get_cur_process_name() {
     }
 
     return basename_v2(g_exeName);
+}
+
+unsigned short calc_checksum(unsigned short *buffer, int size, U32 sum) {
+    unsigned long cksum = ((sum & 0xFFFF0000) >> 16) + (sum & 0xFFFF);
+
+    while (size > 1) {
+        cksum += *buffer++;
+        size  -= sizeof(unsigned short);
+    }
+
+    if (size) {
+        cksum += *(unsigned short *)buffer;
+    }
+
+    cksum  = (cksum >> 16) + (cksum & 0xffff);
+    cksum += (cksum >> 16);
+
+    return cksum;
+    //return (unsigned short)(~cksum);
+}
+
+unsigned short ip_checksum(unsigned char *pIp) {
+    unsigned int  csum;
+    struct iphdr *pIph = (struct iphdr *)pIp;
+    pIph->check        = 0;
+
+    csum = calc_checksum((unsigned short *)pIp, 20, 0);
+
+    csum = ((csum & 0xFFFF0000) >> 16) + (csum & 0xFFFF);
+    return (unsigned short)(~csum);
+}
+
+unsigned short udp_checksum(unsigned int saddr, unsigned int daddr, unsigned char *pUdp) {
+    unsigned char  padBuf[12];
+    unsigned int   csum;
+    struct udphdr *pUhd = (struct udphdr *)pUdp;
+
+    memcpy(&padBuf[0], &saddr, 4);
+    memcpy(&padBuf[4], &daddr, 4);
+    padBuf[8]  = 0;
+    padBuf[9]  = 17;
+    padBuf[10] = pUdp[4];
+    padBuf[11] = pUdp[5];
+
+    pUhd->check = 0;
+    csum        = calc_checksum((unsigned short *)pUdp, ntohs(pUhd->len), 0);
+    csum        = calc_checksum((unsigned short *)padBuf, 12, csum);
+
+    csum = ((csum & 0xFFFF0000) >> 16) + (csum & 0xFFFF);
+    return (unsigned short)(~csum);
 }
\ No newline at end of file
diff --git a/srcs/service/dhcpd/dhcp_option.c b/srcs/service/dhcpd/dhcp_option.c
index 497a541..6c24088 100644
--- a/srcs/service/dhcpd/dhcp_option.c
+++ b/srcs/service/dhcpd/dhcp_option.c
@@ -141,22 +141,39 @@ void dhcp_option_cfg_init() {
     }
 }
 
-void dhcp_add_number_option(U8 *pBegin, int opt, U8 value, U8 nSize) {
+int dhcp_add_u8_option(U8 *pBegin, int opt, U8 value) {
     *pBegin = opt;
     pBegin++;
-    *pBegin = nSize;
+    *pBegin = 1;
     pBegin++;
-    memcpy(pBegin, &value, nSize);
-    pBegin += nSize;
+    *pBegin = value;
+
+    return 3;
 }
 
-void dhcp_add_buf_option(U8 *pBegin, int opt, U8 *buf, U8 nSize) {
+int dhcp_add_u32_option(U8 *pBegin, int opt, U32 value) {
+    *pBegin = opt;
+    pBegin++;
+    *pBegin = 4;
+    pBegin++;
+    memcpy(pBegin, &value, 4);
+    return 4 + 2;
+}
+
+int dhcp_add_buf_option(U8 *pBegin, int opt, U8 *buf, U8 nSize) {
     *pBegin = opt;
     pBegin++;
     *pBegin = nSize;
     pBegin++;
     memcpy(pBegin, buf, nSize);
     pBegin += nSize;
+
+    return nSize + 2;
+}
+
+int dhcp_add_string_option(U8 *pBegin, int opt, const char *buf) {
+    dhcp_add_buf_option(pBegin, opt, (U8 *)buf, strlen(buf) + 1);
+    return (int)(strlen(buf) + 1 + 2);
 }
 
 int dhcp_get_option(int opt, U8 *pOptData, U32 nSize, PDHCP_OPT pVal) {
@@ -177,8 +194,6 @@ int dhcp_get_option(int opt, U8 *pOptData, U32 nSize, PDHCP_OPT pVal) {
         if (g_optRuntime[id].enable && id == opt) {
             pVal->len    = len;
             pVal->pValue = p + 2;
-
-            printf("++++ %d size %d\n", id, len);
             return ERR_SUCCESS;
         }
 
diff --git a/srcs/service/dhcpd/dhcpd_network.c b/srcs/service/dhcpd/dhcpd_network.c
index 3eae98f..c9dc1ef 100644
--- a/srcs/service/dhcpd/dhcpd_network.c
+++ b/srcs/service/dhcpd/dhcpd_network.c
@@ -21,12 +21,16 @@
 #include "misc.h"
 #include "user_mgr.h"
 
-#define PKG_MMAP_BLOCKSIZ    (1 << 22)
-#define PKG_MMAP_FRAMESIZ    (1 << 11)
-#define PKG_MMAP_BLOCKNUM    (64)
-#define MAX_DHCP_PKG_SIZE    (512)
+#define DHCP_COOKIE_VAL   (0x63825363)
+#define PKG_MMAP_BLOCKSIZ (1 << 22)
+#define PKG_MMAP_FRAMESIZ (1 << 11)
+#define PKG_MMAP_BLOCKNUM (64)
+#define MAX_DHCP_PKG_SIZE (512)
+#define VLAN_VNI_ID(x)    ntohs((x))
+#define DHCP_XID(x)       ntohl((x))
 
-#define MAC_TO_STR(mac, str) sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])
+#define MAC_TO_STR(mac, str) \
+    sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])
 
 typedef struct {
     U8   unicast;
@@ -40,9 +44,15 @@ typedef struct {
 } DHCP_REQ, *PDHCP_REQ;
 
 typedef struct {
-    struct iovec       *rd;
-    uint8_t            *map;
-    struct tpacket_req3 req;
+    int                 sock;
+    struct sockaddr_ll  addr;
+    struct iovec       *rx;
+    struct iovec       *tx;
+    uint8_t            *map_recv;
+    uint8_t            *map_send;
+    struct tpacket_req3 recv;
+    struct tpacket_req3 send;
+    unsigned int        index;
 } PACKET_MMAP_RING, *PPACKET_MMAP_RING;
 
 typedef struct {
@@ -201,16 +211,39 @@ static struct sock_filter g_filterCode[] = {
 
 static PACKET_MMAP_RING g_pkgRing;
 static NIC_INFO         g_nicInfo;
-
-#define VLAN_VNI_ID(x) ntohs((x))
-#define DHCP_XID(x)    ntohl((x))
+static uv_udp_t         g_uvRawScokReq;
 
 static struct sock_fprog bpf = {
     .len    = sizeof(g_filterCode) / (sizeof(struct sock_filter)),
     .filter = g_filterCode,
 };
 
+U32 pkg_mmap_tx(U8 *pData, U32 nBytes) {
+    int                  i;
+    ssize_t              ret;
+    struct tpacket3_hdr *hdr;
+
+    for (i = 0; i < g_pkgRing.send.tp_frame_nr; i++) {
+        hdr = (struct tpacket3_hdr *)(g_pkgRing.tx[0].iov_base + (g_pkgRing.send.tp_frame_size * g_pkgRing.index));
+        g_pkgRing.index = (g_pkgRing.index + 1) % g_pkgRing.send.tp_frame_nr;
+
+        if (!(hdr->tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING))) {
+            U8 *p = (U8 *)hdr + TPACKET3_HDRLEN - sizeof(struct sockaddr_ll);
+            memcpy(p, pData, nBytes);
+            hdr->tp_next_offset = 0;
+            hdr->tp_len         = nBytes;
+            hdr->tp_snaplen     = nBytes;
+            hdr->tp_status      = TP_STATUS_SEND_REQUEST;
+            return hdr->tp_len;
+        }
+    }
+
+    return 0;
+}
+
 static int dhcp_resp_offer(PDHCP_PACKAGE pReq, PIPPOOL_INFO pIpInfo, U32 ip) {
+    U16           csum;
+    int           tolSize;
     PDHCP_PACKAGE pRsp = (PDHCP_PACKAGE)malloc(MAX_DHCP_PKG_SIZE);
     U8           *pOpt = pRsp->dhcp.options;
 
@@ -236,8 +269,10 @@ static int dhcp_resp_offer(PDHCP_PACKAGE pReq, PIPPOOL_INFO pIpInfo, U32 ip) {
 
     // IP 头
     memcpy(&pRsp->vlan_hdr.ip, &pReq->vlan_hdr.ip, sizeof(struct iphdr));
+    // TOS
+    pRsp->vlan_hdr.ip.tos   = 0;
     // 更新源IP
-    pRsp->vlan_hdr.ip.saddr = htonl(g_nicInfo.ipAddr);
+    pRsp->vlan_hdr.ip.saddr = g_nicInfo.ipAddr;
     // 更新目的IP地址,广播255.255.255.255
     pRsp->vlan_hdr.ip.daddr = 0xFFFFFFFF;
 
@@ -263,7 +298,10 @@ static int dhcp_resp_offer(PDHCP_PACKAGE pReq, PIPPOOL_INFO pIpInfo, U32 ip) {
     // DHCP服务端主机名
     memcpy(pRsp->dhcp.sname, g_nicInfo.hostname, MIN(64, strlen(g_nicInfo.hostname)));
     // Magic Cookie: DHCP
-    memcpy(&pRsp->dhcp.cookie, "DHCP", 4);
+    //
+    // memcpy(&pRsp->dhcp.cookie, DHCP_COOKIE_VAL, 4);
+    pRsp->dhcp.cookie = htonl(DHCP_COOKIE_VAL);
+
     pRsp->dhcp.hops   = 0;
     pRsp->dhcp.secs   = 0;
     pRsp->dhcp.flags  = 0;
@@ -273,39 +311,57 @@ static int dhcp_resp_offer(PDHCP_PACKAGE pReq, PIPPOOL_INFO pIpInfo, U32 ip) {
 
     // DHCP Options
     // DHCP 消息类型
-    dhcp_add_number_option(pOpt, OPT_MESSAGETYPE, DHCP_MSG_OFFER, 1);
-    pOpt += 3;
+    pOpt += dhcp_add_u8_option(pOpt, OPT_MESSAGETYPE, DHCP_MSG_OFFER);
     // 子网掩码
-    dhcp_add_number_option(pOpt, OPT_NETMASK, pIpInfo->netMask, 4);
-    pOpt += 6;
+    pOpt += dhcp_add_u32_option(pOpt, OPT_NETMASK, htonl(pIpInfo->netMask));
     // 租约
-    dhcp_add_number_option(pOpt, OPT_IPADDRLEASE, htonl(pIpInfo->leaseTime), 4);
-    pOpt += 6;
+    pOpt += dhcp_add_u32_option(pOpt, OPT_IPADDRLEASE, htonl(pIpInfo->leaseTime));
     // DHCP Server
-    dhcp_add_number_option(pOpt, OPT_SERVERID, g_nicInfo.ipAddr, 4);
-    pOpt += 6;
+    pOpt += dhcp_add_u32_option(pOpt, OPT_SERVERID, g_nicInfo.ipAddr);
     // DNS
     if (pIpInfo->primeDNS != 0) {
         if (pIpInfo->salveDNS == 0) {
-            dhcp_add_number_option(pOpt, OPT_DNS, pIpInfo->primeDNS, 4);
-            pOpt += 6;
+            pOpt += dhcp_add_u32_option(pOpt, OPT_DNS, htonl(pIpInfo->primeDNS));
         } else {
-            dhcp_add_buf_option(pOpt, OPT_DNS, (U8 *)&pIpInfo->primeDNS, 8);
-            pOpt += 10;
+            U8   buf[8]  = {0};
+            U32 *pVal    = (U32 *)buf;
+            *pVal++      = htonl(pIpInfo->primeDNS);
+            *pVal        = htonl(pIpInfo->salveDNS);
+            pOpt        += dhcp_add_buf_option(pOpt, OPT_DNS, buf, 8);
         }
     }
     // 网关
     if (pIpInfo->gwAddr != 0) {
-        dhcp_add_number_option(pOpt, OPT_ROUTER, pIpInfo->gwAddr, 4);
-        pOpt += 6;
+        pOpt += dhcp_add_u32_option(pOpt, OPT_ROUTER, htonl(pIpInfo->gwAddr));
     }
     // DHCP 主机名
-    dhcp_add_buf_option(pOpt, OPT_DOMAINNAME, (U8 *)g_nicInfo.hostname, strlen(g_nicInfo.hostname));
-    pOpt += strlen(g_nicInfo.hostname) + 2;
+    pOpt  += dhcp_add_string_option(pOpt, OPT_DOMAINNAME, "workgroup");
+    *pOpt  = OPT_END;
 
-    *pOpt = OPT_END;
+    // 计算包总长度
+    tolSize = (int)((pOpt - pRsp->dhcp.options) + 1 + sizeof(DHCP_PACKAGE));
 
-    LOG_MSG_HEX(debug, pRsp, 256);
+    // 计算 IP 数据长度
+    pRsp->vlan_hdr.ip.tot_len = htons(tolSize - sizeof(struct ethhdr) - sizeof(struct vlan_hdr));
+    // 计算 UDP 数据长度
+    pRsp->vlan_hdr.udp.len    = htons(tolSize - sizeof(struct ethhdr) - sizeof(struct vlan_hdr) - sizeof(struct iphdr));
+
+    // 计算 IP 校验和
+    csum                    = htons(ip_checksum((unsigned char *)&pRsp->vlan_hdr.ip));
+    pRsp->vlan_hdr.ip.check = htons(csum);
+
+    // 计算 UDP 校验和
+    csum = htons(udp_checksum(pRsp->vlan_hdr.ip.saddr, pRsp->vlan_hdr.ip.daddr, (unsigned char *)&pRsp->vlan_hdr.udp));
+    pRsp->vlan_hdr.udp.check = htons(csum);
+
+    LOG_MOD(trace, ZLOG_MOD_DHCPD, "OPTIONS size: %ld\n", (intptr_t)(pOpt - pRsp->dhcp.options) + 1);
+    LOG_MOD(trace, ZLOG_MOD_DHCPD, "Total size: %d\n", tolSize);
+
+    // 发送数据
+    if (pkg_mmap_tx((U8 *)pRsp, tolSize) != tolSize) {
+        LOG_MOD(error, ZLOG_MOD_DHCPD, "Send package(%u bytes) error\n", tolSize);
+        return -ERR_SOCK_SEND;
+    }
 
     return ERR_SUCCESS;
 }
@@ -380,8 +436,16 @@ static void on_sock_recv(uv_work_t *req) {
                         u32_to_str_ip(ntohl(ip)));
 
                 ret = dhcp_resp_offer(pkg, pIpInfo, ip);
+
+                if (ret != ERR_SUCCESS) {
+                    LOG_MOD(error, ZLOG_MOD_DHCPD, "Send Offer error: %d\n", ret);
+                }
             } else {
-                //LOG_MOD(error, ZLOG_MOD_DHCPD, );
+                LOG_MOD(error,
+                        ZLOG_MOD_DHCPD,
+                        "DHCP prepare assign ipaddress error: [%s(%s)]\n",
+                        macStr,
+                        reqDhcp.hostName);
             }
             break;
         case DHCP_MSG_REQUEST:
@@ -440,8 +504,8 @@ static void after_msg_recv(uv_work_t *req, int status) {
 void raw_sock_recv_cb(uv_poll_t *handle, int status, int events) {
     static unsigned int block_num = 0;
 
-    if (status >= 0) {
-        struct block_desc *pbd = (struct block_desc *)g_pkgRing.rd[block_num].iov_base;
+    if (status >= 0 && (events & UV_READABLE)) {
+        struct block_desc *pbd = (struct block_desc *)g_pkgRing.rx[block_num].iov_base;
 
         if ((pbd->h1.block_status & TP_STATUS_USER) && (pbd->h1.num_pkts > 0)) {
             int                  i;
@@ -486,14 +550,14 @@ void raw_sock_recv_cb(uv_poll_t *handle, int status, int events) {
 }
 
 static int create_udp_socket() {
-    int i;
-    int err;
-    int v = TPACKET_V3;
-
     struct sockaddr_ll addr;
+    unsigned int       size;
+    int                i;
+    int                err;
+    int                v = TPACKET_V3;
 
     // 1. create socket
-    int sock_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+    int sock_fd = socket(PF_PACKET, SOCK_RAW, 0);
     if (sock_fd < 0) {
         LOG_MOD(error, ZLOG_MOD_DHCPD, "Socket created failure\n");
         return -ERR_SOCK_CREATE;
@@ -504,74 +568,130 @@ static int create_udp_socket() {
         LOG_MOD(error, ZLOG_MOD_DHCPD, "Attaching filter failed: %d\n", err);
         return -ERR_SOCK_SETOPT;
     }
-#if 1
+
     // 3. set PACKET_MMAP version
     if ((err = setsockopt(sock_fd, SOL_PACKET, PACKET_VERSION, &v, sizeof(v))) < 0) {
         LOG_MOD(error, ZLOG_MOD_DHCPD, "Set PACKET_VERSION option failed: %d\n", err);
         return -ERR_SOCK_SETOPT;
     }
 
-    // 4. setup PAKCET_MMAP ring
-    memset(&g_pkgRing.req, 0, sizeof(g_pkgRing.req));
-    g_pkgRing.req.tp_block_size       = PKG_MMAP_BLOCKSIZ;
-    g_pkgRing.req.tp_frame_size       = PKG_MMAP_FRAMESIZ;
-    g_pkgRing.req.tp_block_nr         = PKG_MMAP_BLOCKNUM;
-    g_pkgRing.req.tp_frame_nr         = (PKG_MMAP_BLOCKSIZ * PKG_MMAP_BLOCKNUM) / PKG_MMAP_FRAMESIZ;
-    g_pkgRing.req.tp_retire_blk_tov   = 60;
-    g_pkgRing.req.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH;
+    // 4. setup PAKCET_MMAP rx ring
+    memset(&g_pkgRing.recv, 0, sizeof(g_pkgRing.recv));
+    g_pkgRing.recv.tp_block_size       = PKG_MMAP_BLOCKSIZ;
+    g_pkgRing.recv.tp_frame_size       = PKG_MMAP_FRAMESIZ;
+    g_pkgRing.recv.tp_block_nr         = PKG_MMAP_BLOCKNUM;
+    g_pkgRing.recv.tp_frame_nr         = (PKG_MMAP_BLOCKSIZ * PKG_MMAP_BLOCKNUM) / PKG_MMAP_FRAMESIZ;
+    g_pkgRing.recv.tp_retire_blk_tov   = 60;
+    g_pkgRing.recv.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH;
 
-    if ((err = setsockopt(sock_fd, SOL_PACKET, PACKET_RX_RING, &g_pkgRing.req, sizeof(g_pkgRing.req))) < 0) {
+    if ((err = setsockopt(sock_fd, SOL_PACKET, PACKET_RX_RING, &g_pkgRing.recv, sizeof(g_pkgRing.recv))) < 0) {
         LOG_MOD(error, ZLOG_MOD_DHCPD, "Set PACKET_RX_RING option failed: %d\n", err);
         return -ERR_SOCK_SETOPT;
     }
 
-    g_pkgRing.map = mmap(NULL,
-                         g_pkgRing.req.tp_block_size * g_pkgRing.req.tp_block_nr,
-                         PROT_READ | PROT_WRITE,
-                         MAP_SHARED | MAP_LOCKED,
-                         sock_fd,
-                         0);
+    // 5. setup PACKET_MMAP tx ring
+    memset(&g_pkgRing.send, 0, sizeof(g_pkgRing.send));
+    g_pkgRing.send.tp_block_size = PKG_MMAP_BLOCKSIZ;
+    g_pkgRing.send.tp_frame_size = PKG_MMAP_FRAMESIZ;
+    g_pkgRing.send.tp_block_nr   = PKG_MMAP_BLOCKNUM;
+    g_pkgRing.send.tp_frame_nr   = (PKG_MMAP_BLOCKSIZ * PKG_MMAP_BLOCKNUM) / PKG_MMAP_FRAMESIZ;
 
-    if (g_pkgRing.map == MAP_FAILED) {
+    if ((err = setsockopt(sock_fd, SOL_PACKET, PACKET_TX_RING, &g_pkgRing.send, sizeof(g_pkgRing.recv))) < 0) {
+        LOG_MOD(error, ZLOG_MOD_DHCPD, "Set PACKET_TX_RING option failed: %d\n", err);
+        return -ERR_SOCK_SETOPT;
+    }
+
+    // 6. mmap RX/TX ring
+    size = g_pkgRing.recv.tp_block_size * g_pkgRing.recv.tp_block_nr +
+        g_pkgRing.send.tp_block_size * g_pkgRing.send.tp_block_nr;
+
+    g_pkgRing.map_recv = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED | MAP_POPULATE, sock_fd, 0);
+    g_pkgRing.map_send = g_pkgRing.map_recv + g_pkgRing.recv.tp_block_size * g_pkgRing.recv.tp_block_nr;
+
+    if (g_pkgRing.map_recv == MAP_FAILED) {
         LOG_MOD(error, ZLOG_MOD_DHCPD, "MMAP socket ring failed\n");
+        perror("title");
         return -ERR_MMAP_MEMORY;
     }
 
-    // 5. malloc read buffer
-    g_pkgRing.rd = malloc(g_pkgRing.req.tp_block_nr * sizeof(struct iovec));
+    LOG_MOD(trace, ZLOG_MOD_DHCPD, "size = %u\n", size);
+    LOG_MOD(trace, ZLOG_MOD_DHCPD, "MMAP address recv = %p, send = %p\n", g_pkgRing.map_recv, g_pkgRing.map_send);
 
-    if (g_pkgRing.rd == NULL) {
-        LOG_MOD(error, ZLOG_MOD_DHCPD, "Malloc memory failed: %lu\n", g_pkgRing.req.tp_block_nr * sizeof(struct iovec));
+    // 7. malloc read buffer
+    g_pkgRing.rx = malloc(g_pkgRing.recv.tp_block_nr * sizeof(struct iovec));
+    if (g_pkgRing.rx == NULL) {
+        LOG_MOD(error,
+                ZLOG_MOD_DHCPD,
+                "Malloc memory failed: %lu\n",
+                g_pkgRing.recv.tp_block_nr * sizeof(struct iovec));
         return -ERR_MMAP_MEMORY;
     }
 
-    for (i = 0; i < g_pkgRing.req.tp_block_nr; ++i) {
-        g_pkgRing.rd[i].iov_base = g_pkgRing.map + (i * g_pkgRing.req.tp_block_size);
-        g_pkgRing.rd[i].iov_len  = g_pkgRing.req.tp_block_size;
+    memset(g_pkgRing.rx, 0, g_pkgRing.recv.tp_block_nr * sizeof(struct iovec));
+    for (i = 0; i < g_pkgRing.recv.tp_block_nr; ++i) {
+        g_pkgRing.rx[i].iov_base = g_pkgRing.map_recv + (i * g_pkgRing.recv.tp_block_size);
+        g_pkgRing.rx[i].iov_len  = g_pkgRing.recv.tp_block_size;
     }
-#endif
-    // 6. bind socket
-    memset(&addr, 0, sizeof(addr));
+
+    // 8. malloc send buffer
+    g_pkgRing.tx = malloc(g_pkgRing.send.tp_block_nr * sizeof(struct iovec));
+    if (g_pkgRing.tx == NULL) {
+        LOG_MOD(error,
+                ZLOG_MOD_DHCPD,
+                "Malloc memory failed: %lu\n",
+                g_pkgRing.send.tp_block_nr * sizeof(struct iovec));
+
+        munmap(g_pkgRing.map_recv, size * 2);
+        return -ERR_MMAP_MEMORY;
+    }
+
+    memset(g_pkgRing.tx, 0, g_pkgRing.send.tp_block_nr * sizeof(struct iovec));
+    for (i = 0; i < g_pkgRing.send.tp_block_nr; ++i) {
+        g_pkgRing.tx[i].iov_base = g_pkgRing.map_send + (i * g_pkgRing.send.tp_block_size);
+        g_pkgRing.tx[i].iov_len  = g_pkgRing.send.tp_block_size;
+    }
+
+    // 9. bind socket
+    memset(&addr, 0, sizeof(struct sockaddr_ll));
     addr.sll_ifindex  = (int)if_nametoindex(g_nicInfo.pIfName);
-    addr.sll_family   = AF_PACKET;
+    addr.sll_family   = PF_PACKET;
     addr.sll_protocol = htons(ETH_P_ALL);
     addr.sll_hatype   = 0;
     addr.sll_pkttype  = 0;
     addr.sll_halen    = 0;
 
-    if ((err = bind(sock_fd, (struct sockaddr *)&addr, sizeof(addr))) < 0) {
+    if ((err = bind(sock_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_ll))) < 0) {
         LOG_MOD(error, ZLOG_MOD_DHCPD, "Bind raw socket failed: %d\n", err);
         return -ERR_SOCK_SETOPT;
     }
 
-    return sock_fd;
+    g_pkgRing.sock = sock_fd;
+
+    return ERR_SUCCESS;
+}
+
+static void socket_send_task(uv_timer_t *UNUSED(pArg)) {
+    int                  i;
+    struct tpacket3_hdr *hdr;
+
+    for (i = 0; i < g_pkgRing.send.tp_frame_nr && g_pkgRing.sock != 0; i++) {
+        hdr = (struct tpacket3_hdr *)(g_pkgRing.tx[0].iov_base + (g_pkgRing.send.tp_frame_size * i));
+        if ((hdr->tp_status & TP_STATUS_SEND_REQUEST) || (hdr->tp_status & TP_STATUS_SENDING)) {
+            ssize_t ret = sendto(g_pkgRing.sock, NULL, 0, MSG_DONTWAIT, NULL, sizeof(struct sockaddr_ll));
+
+            if (ret == -1) {
+                LOG_MOD(error, ZLOG_MOD_DHCPD, "Send pakcet error\n");
+            }
+            break;
+        }
+    }
 }
 
 int dhcpd_init() {
-    static uv_udp_t  uvRaw;
-    static uv_poll_t uvSocket;
-    int              sock;
-    size_t           size = MAX_PATH;
+    static uv_poll_t  uvSocket;
+    static uv_timer_t uvTm;
+    int               ret;
+    size_t            size = MAX_PATH;
 
     memset(&g_nicInfo, 0, sizeof(NIC_INFO));
     g_nicInfo.pIfName = (char *)config_get_dhcp_nic_name();
@@ -579,21 +699,24 @@ int dhcpd_init() {
 
     get_nic_info(g_nicInfo.pIfName, &g_nicInfo.ipAddr, &g_nicInfo.netmask, NULL, g_nicInfo.macAddr);
 
-    sock = create_udp_socket();
-
-    if (sock <= 0) {
-        return sock;
+    ret = create_udp_socket();
+    if (ret != ERR_SUCCESS) {
+        LOG_MOD(error, ZLOG_MOD_DHCPD, "Create receive RAW Socket Error\n");
+        return ret;
     }
 
     dhcp_user_mgr_init();
     dhcp_option_cfg_init();
     dhcp_lease_init();
 
-    uv_udp_init(get_task_manager(), &uvRaw);
-    uv_udp_open(&uvRaw, sock);
+    uv_udp_init(get_task_manager(), &g_uvRawScokReq);
+    uv_udp_open(&g_uvRawScokReq, g_pkgRing.sock);
 
-    uv_poll_init_socket(get_task_manager(), &uvSocket, sock);
+    uv_poll_init_socket(get_task_manager(), &uvSocket, g_pkgRing.sock);
     uv_poll_start(&uvSocket, UV_READABLE, raw_sock_recv_cb);
 
+    uv_timer_init(get_task_manager(), &uvTm);
+    uv_timer_start(&uvTm, socket_send_task, 3000, 100);
+
     return ERR_SUCCESS;
 }
\ No newline at end of file
diff --git a/srcs/service/dhcpd/include/dhcp_options.h b/srcs/service/dhcpd/include/dhcp_options.h
index d7e446e..d5457ce 100644
--- a/srcs/service/dhcpd/include/dhcp_options.h
+++ b/srcs/service/dhcpd/include/dhcp_options.h
@@ -141,8 +141,10 @@ typedef struct {
     U8  len;
 } DHCP_OPT, *PDHCP_OPT;
 
-void dhcp_add_number_option(U8 *pBegin, int opt, U8 value, U8 nSize);
-void dhcp_add_buf_option(U8 *pBegin, int opt, U8 *buf, U8 nSize);
+int  dhcp_add_u8_option(U8 *pBegin, int opt, U8 value);
+int  dhcp_add_u32_option(U8 *pBegin, int opt, U32 value);
+int  dhcp_add_buf_option(U8 *pBegin, int opt, U8 *buf, U8 nSize);
+int  dhcp_add_string_option(U8 *pBegin, int opt, const char *buf);
 int  dhcp_get_option(int opt, U8 *pOptData, U32 nSize, PDHCP_OPT pVal);
 void dhcp_option_cfg_init();
 #ifdef __cplusplus