From 44b713b6923572cd249d6291f1acb8de4abfea82 Mon Sep 17 00:00:00 2001 From: huangxin Date: Fri, 21 Apr 2023 15:01:36 +0800 Subject: [PATCH] =?UTF-8?q?OCT=201.=20=E5=A2=9E=E5=8A=A0=20DHCP=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=B7=A5=E5=85=B7=20Offer=20=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E8=A7=A3=E6=9E=90=202.=20=E5=A2=9E=E5=8A=A0=20DHCP=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=B7=A5=E5=85=B7=20Request=20=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E5=8F=91=E9=80=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dhcp_tools/main.h | 15 +- dhcp_tools/main_wnd.c | 107 ++++++++--- dhcp_tools/uv_rawsocket.c | 266 ++++++++++++++++++++------- srcs/service/dhcpd/include/rfc2131.h | 14 ++ 4 files changed, 308 insertions(+), 94 deletions(-) diff --git a/dhcp_tools/main.h b/dhcp_tools/main.h index 5a116f5..fdde175 100644 --- a/dhcp_tools/main.h +++ b/dhcp_tools/main.h @@ -6,6 +6,7 @@ #define VCPE_MAIN_H #include "uthash/uthash.h" +#include "rfc2131.h" typedef enum { COL_INDEX = 0, @@ -34,8 +35,7 @@ typedef enum { typedef enum { STA_WAIT_START = 0, STA_SEND_REQ, - STA_RECV_RSP, - STA_FINISHED, + STA_RECV_RSP } DHCP_STATUS; typedef struct { @@ -54,10 +54,15 @@ typedef struct { BUF_INFO pOfferBuf; BUF_INFO pReqBuf; BUF_INFO pAckBuf; + DHCP_RSP offerRsp; + DHCP_RSP ackRsp; UT_hash_handle hh; } DHCP_INFO, *PDHCP_INFO; -U32 rand_number(); -int dhcp_tools_init_network(const char *pNicName); -U8 *dhcp_create_discover_req(PDHCP_INFO pInfo, int *pOutSize); +U32 rand_number(); +PDHCP_INFO get_dhcp_info_by_id(U32 xid); +int dhcp_tools_init_network(const char *pNicName); +U8 *dhcp_create_discover_req(PDHCP_INFO pInfo, int *pOutSize); +U8 *dhcp_create_request_req(PDHCP_INFO pInfo, int *pOutSize); +int cacheDhcpOfferBuffer(PDHCP_INFO pInfo, U8 *pBuf, int size); #endif //VCPE_MAIN_H diff --git a/dhcp_tools/main_wnd.c b/dhcp_tools/main_wnd.c index f4d79a2..6be786b 100644 --- a/dhcp_tools/main_wnd.c +++ b/dhcp_tools/main_wnd.c @@ -25,6 +25,14 @@ U32 rand_number() { return g_rand_int(pRand); } +PDHCP_INFO get_dhcp_info_by_id(U32 xid) { + PDHCP_INFO pInfo; + U32 index = xid & 0x00FFFFFF; + + HASH_FIND_INT(g_pDhcpInfo, &index, pInfo); + return pInfo; +} + static void load_css(void) { GtkCssProvider *provider; GdkDisplay *display; @@ -55,7 +63,7 @@ static double calc_total_progress(PDHCP_INFO pInfo) { } static gboolean calc_step_progress(PDHCP_INFO pInfo, DHCP_STEP step) { - if (pInfo && pInfo->step == step) { + if (pInfo && pInfo->step >= step) { return TRUE; } @@ -182,6 +190,8 @@ static gboolean tree_view_data_store_upgade(gpointer pInfo) { -1); // clang-format on } + + return TRUE; } G_MODULE_EXPORT void __mainWnd_on_destroy(GObject *object, gpointer user_data) { @@ -190,6 +200,17 @@ G_MODULE_EXPORT void __mainWnd_on_destroy(GObject *object, gpointer user_data) { gtk_main_quit(); } +int cacheDhcpOfferBuffer(PDHCP_INFO pInfo, U8 *pBuf, int size) { + pInfo->pOfferBuf.p = (U8 *)malloc(size); + if (pInfo->pOfferBuf.p) { + memcpy(pInfo->pOfferBuf.p, pBuf, size); + pInfo->pOfferBuf.buf_size = size; + return ERR_SUCCESS; + } + + return -ERR_MALLOC_MEMORY; +} + static void *dhcpThreadCb(void *pData) { U8 *pkg; int size = 0; @@ -215,30 +236,44 @@ static void *dhcpThreadCb(void *pData) { } break; case STEP_DISCOVER: - switch (pInfo->status) { - case STA_WAIT_START: - pkg = get_pkg_free_buf(); + if (pInfo->status == STA_WAIT_START) { + pkg = get_pkg_free_buf(); - if (pkg) { - memcpy(pkg, pInfo->pDiscBuf.p, pInfo->pDiscBuf.buf_size); + if (pkg) { + memcpy(pkg, pInfo->pDiscBuf.p, pInfo->pDiscBuf.buf_size); - if (pkg_mmap_tx((U8 *)pkg, pInfo->pDiscBuf.buf_size) == pInfo->pDiscBuf.buf_size) { - pInfo->status = STA_SEND_REQ; - g_idle_add(tree_view_data_store_upgade, pInfo); - } + if (pkg_mmap_tx((U8 *)pkg, pInfo->pDiscBuf.buf_size) == pInfo->pDiscBuf.buf_size) { + pInfo->status = STA_SEND_REQ; + g_idle_add(tree_view_data_store_upgade, pInfo); } - break; - case STA_SEND_REQ: - break; - case STA_RECV_RSP: - break; - case STA_FINISHED: - break; + } } break; case STEP_OFFER: + if (pInfo->status == STA_RECV_RSP) { + pInfo->pReqBuf.p = dhcp_create_request_req(pInfo, &size); + + if (pInfo->pReqBuf.p) { + pInfo->pReqBuf.buf_size = size; + pInfo->step = STEP_REQUEST; + pInfo->status = STA_WAIT_START; + g_idle_add(tree_view_data_store_upgade, pInfo); + } + } break; case STEP_REQUEST: + if (pInfo->status == STA_WAIT_START) { + pkg = get_pkg_free_buf(); + + if (pkg) { + memcpy(pkg, pInfo->pReqBuf.p, pInfo->pReqBuf.buf_size); + + if (pkg_mmap_tx((U8 *)pkg, pInfo->pReqBuf.buf_size) == pInfo->pReqBuf.buf_size) { + pInfo->status = STA_SEND_REQ; + g_idle_add(tree_view_data_store_upgade, pInfo); + } + } + } break; case STEP_ACK: break; @@ -255,9 +290,35 @@ static void *dhcpThreadCb(void *pData) { return NULL; } +static void cleanupDHCPInfo() { + PDHCP_INFO pInfo, pTemp; + HASH_ITER(hh, g_pDhcpInfo, pInfo, pTemp) { + HASH_DEL(g_pDhcpInfo, pInfo); + if (pInfo->pOfferBuf.p) { + free(pInfo->pOfferBuf.p); + } + + if (pInfo->pDiscBuf.p) { + free(pInfo->pDiscBuf.p); + } + + if (pInfo->pReqBuf.p) { + free(pInfo->pReqBuf.p); + } + + if (pInfo->pAckBuf.p) { + free(pInfo->pAckBuf.p); + } + free(pInfo); + } +} + G_MODULE_EXPORT void __mainWnd_on_tb_start(GObject *object, gpointer user_data) { int i; - PDHCP_INFO pInfo, pTemp; + GtkTreeIter iter; + PDHCP_INFO pInfo; + GtkTreeModel *cobModel; + gchar *pCobText; unsigned char mac[6]; U32 macVal; GtkWidget *stopButton = GTK_WIDGET(gtk_builder_get_object(g_mainBuilder, "tbStop")); @@ -274,10 +335,7 @@ G_MODULE_EXPORT void __mainWnd_on_tb_start(GObject *object, gpointer user_data) string_mac_to_bytes(strMacBegin, mac); macVal = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; - HASH_ITER(hh, g_pDhcpInfo, pInfo, pTemp) { - HASH_DEL(g_pDhcpInfo, pInfo); - free(pInfo); - } + cleanupDHCPInfo(); for (i = 0; i < nRequest; i++) { char strMac[6]; @@ -311,8 +369,11 @@ G_MODULE_EXPORT void __mainWnd_on_tb_start(GObject *object, gpointer user_data) tree_view_data_store_create(); gtk_widget_set_sensitive(stopButton, TRUE); - dhcp_tools_init_network("ens192"); + cobModel = gtk_combo_box_get_model(GTK_COMBO_BOX(nicSelect)); + gtk_combo_box_get_active_iter(GTK_COMBO_BOX(nicSelect), &iter); + gtk_tree_model_get(cobModel, &iter, 0, &pCobText, -1); + dhcp_tools_init_network(pCobText); g_runTask = TRUE; } diff --git a/dhcp_tools/uv_rawsocket.c b/dhcp_tools/uv_rawsocket.c index 3213d75..5c0d616 100644 --- a/dhcp_tools/uv_rawsocket.c +++ b/dhcp_tools/uv_rawsocket.c @@ -12,7 +12,8 @@ #include "rfc2131.h" #include "misc.h" -static char *g_pNicName = NULL; +static U8 g_dhcpReqParams[] = {0x01, 0x1c, 0x02, 0x03, 0x0f, 0x06, 0x77, 0x0c, 0x2c, 0x2f, 0x1a, 0x79, 0x2a}; +static char *g_pNicName = NULL; static struct sock_filter g_filterCode[] = { // region BPF code @@ -94,62 +95,117 @@ static struct sock_fprog bpf = { .filter = g_filterCode, }; +static void pkg_init_head(PDHCP_PACKAGE p, PDHCP_INFO pInfo) { + // 目的地 MAC 地址 + memset(p->vlan_hdr.eth.h_dest, 0xFF, ETH_ALEN); + // 源 MAC 地址 + memcpy(p->vlan_hdr.eth.h_source, pInfo->mac, ETH_ALEN); + // 协议 VLAN + p->vlan_hdr.eth.h_proto = htons(ETH_P_8021Q); + + // VLAN 隧道信息 + p->vlan_hdr.vlan.id = htons(pInfo->vni); + p->vlan_hdr.vlan.type = htons(ETH_P_IP); + + // IP 头 + p->vlan_hdr.ip.version = IPVERSION; + p->vlan_hdr.ip.ihl = 5; + p->vlan_hdr.ip.tos = 0x10; + p->vlan_hdr.ip.tot_len = 0; + p->vlan_hdr.ip.id = 0; + p->vlan_hdr.ip.frag_off = 0; + p->vlan_hdr.ip.ttl = 128; + p->vlan_hdr.ip.protocol = IPPROTO_UDP; + p->vlan_hdr.ip.check = 0; + p->vlan_hdr.ip.saddr = INADDR_ANY; + p->vlan_hdr.ip.daddr = INADDR_BROADCAST; + + // UDP 头 + p->vlan_hdr.udp.source = htons(DHCP_CLI_PORT); + p->vlan_hdr.udp.dest = htons(DHCP_SVR_PORT); + p->vlan_hdr.udp.len = 0; + p->vlan_hdr.udp.check = 0; + + // DHCP 协议内容 + p->dhcp.op = BOOTP_REQUEST; + p->dhcp.htype = 0x01; + p->dhcp.hlen = ETH_ALEN; + p->dhcp.hops = 0; + p->dhcp.xid = htonl((rand_number() & 0xFF000000) + pInfo->index); + p->dhcp.secs = 0; + p->dhcp.flags = 0; + p->dhcp.ciaddr = INADDR_ANY; + p->dhcp.yiaddr = INADDR_ANY; + p->dhcp.siaddr = INADDR_ANY; + p->dhcp.giaddr = INADDR_ANY; + memcpy(p->dhcp.chaddr, pInfo->mac, ETH_ALEN); + p->dhcp.cookie = htonl(DHCP_COOKIE_VAL); +} + +static void cacl_package_checksum(PDHCP_PACKAGE p, int tolSize) { + U16 csum; + // 计算 IP 数据长度 + p->vlan_hdr.ip.tot_len = htons(tolSize - sizeof(struct ethhdr) - sizeof(struct vlan_hdr)); + // 计算 UDP 数据长度 + p->vlan_hdr.udp.len = htons(tolSize - sizeof(struct ethhdr) - sizeof(struct vlan_hdr) - sizeof(struct iphdr)); + + // 计算 IP 校验和 + csum = htons(ip_checksum((unsigned char *)&p->vlan_hdr.ip)); + p->vlan_hdr.ip.check = htons(csum); + + // 计算 UDP 校验和 + csum = htons(udp_checksum(p->vlan_hdr.ip.saddr, p->vlan_hdr.ip.daddr, (unsigned char *)&p->vlan_hdr.udp)); + p->vlan_hdr.udp.check = htons(csum); +} + +U8 *dhcp_create_request_req(PDHCP_INFO pInfo, int *pOutSize) { + U8 *pOpt; + int tolSize; + U8 *pReqData = (U8 *)malloc(MAX_DHCP_PKG_SIZE); + + if (pReqData) { + PDHCP_PACKAGE p = (PDHCP_PACKAGE)pReqData; + memset(pReqData, 0, MAX_DHCP_PKG_SIZE); + + pkg_init_head(p, pInfo); + + // DHCP Options + pOpt = p->dhcp.options; + + // DHCP 消息类型 + pOpt += dhcp_add_u8_option(pOpt, OPT_MESSAGETYPE, DHCP_MSG_REQUEST); + // DHCP 请求参数列表 + pOpt += dhcp_add_buf_option(pOpt, OPT_PARAMREQLIST, g_dhcpReqParams, 13); + // DHCP 主机名 + pOpt += dhcp_add_string_option(pOpt, OPT_HOSTNAME, pInfo->hostname); + // DHCP Server 主机 + pOpt += dhcp_add_u32_option(pOpt, OPT_SERVERID, htonl(pInfo->offerRsp.svrIp)); + // DHCP 请求分配IP + pOpt += dhcp_add_u32_option(pOpt, OPT_REQUESTEDIPADDR, htonl(pInfo->offerRsp.ipAddr)); + // 结束 + *pOpt = OPT_END; + // 计算包总长度 + tolSize = (int)((pOpt - p->dhcp.options) + 1 + sizeof(DHCP_PACKAGE)); + + cacl_package_checksum(p, tolSize); + + *pOutSize = tolSize; + } + + return pReqData; +} + U8 *dhcp_create_discover_req(PDHCP_INFO pInfo, int *pOutSize) { - static U8 reqParams[] = {0x01, 0x1c, 0x02, 0x03, 0x0f, 0x06, 0x77, 0x0c, 0x2c, 0x2f, 0x1a, 0x79, 0x2a}; - U8 *pOpt; - U16 csum; - int tolSize; - U8 *pReqData = (U8 *)malloc(MAX_DHCP_PKG_SIZE); + U8 *pOpt; + int tolSize; + U8 *pReqData = (U8 *)malloc(MAX_DHCP_PKG_SIZE); if (pReqData) { PDHCP_PACKAGE p = (PDHCP_PACKAGE)pReqData; memset(pReqData, 0, MAX_DHCP_PKG_SIZE); - // 目的地 MAC 地址 - memset(p->vlan_hdr.eth.h_dest, 0xFF, ETH_ALEN); - // 源 MAC 地址 - memcpy(p->vlan_hdr.eth.h_source, pInfo->mac, ETH_ALEN); - // 协议 VLAN - p->vlan_hdr.eth.h_proto = htons(ETH_P_8021Q); - - // VLAN 隧道信息 - p->vlan_hdr.vlan.id = htons(pInfo->vni); - p->vlan_hdr.vlan.type = htons(ETH_P_IP); - - // IP 头 - p->vlan_hdr.ip.version = IPVERSION; - p->vlan_hdr.ip.ihl = 5; - p->vlan_hdr.ip.tos = 0x10; - p->vlan_hdr.ip.tot_len = 0; - p->vlan_hdr.ip.id = 0; - p->vlan_hdr.ip.frag_off = 0; - p->vlan_hdr.ip.ttl = 128; - p->vlan_hdr.ip.protocol = IPPROTO_UDP; - p->vlan_hdr.ip.check = 0; - p->vlan_hdr.ip.saddr = INADDR_ANY; - p->vlan_hdr.ip.daddr = INADDR_BROADCAST; - - // UDP 头 - p->vlan_hdr.udp.source = htons(DHCP_CLI_PORT); - p->vlan_hdr.udp.dest = htons(DHCP_SVR_PORT); - p->vlan_hdr.udp.len = 0; - p->vlan_hdr.udp.check = 0; - - // DHCP 协议内容 - p->dhcp.op = BOOTP_REQUEST; - p->dhcp.htype = 0x01; - p->dhcp.hlen = ETH_ALEN; - p->dhcp.hops = 0; - p->dhcp.xid = htonl(rand_number()); - p->dhcp.secs = 0; - p->dhcp.flags = 0; - p->dhcp.ciaddr = INADDR_ANY; - p->dhcp.yiaddr = INADDR_ANY; - p->dhcp.siaddr = INADDR_ANY; - p->dhcp.giaddr = INADDR_ANY; - memcpy(p->dhcp.chaddr, pInfo->mac, ETH_ALEN); - p->dhcp.cookie = htonl(DHCP_COOKIE_VAL); + pkg_init_head(p, pInfo); // DHCP Options pOpt = p->dhcp.options; @@ -159,25 +215,14 @@ U8 *dhcp_create_discover_req(PDHCP_INFO pInfo, int *pOutSize) { // DHCP 主机名 pOpt += dhcp_add_string_option(pOpt, OPT_HOSTNAME, pInfo->hostname); // DHCP 请求参数列表 - pOpt += dhcp_add_buf_option(pOpt, OPT_PARAMREQLIST, reqParams, 13); + pOpt += dhcp_add_buf_option(pOpt, OPT_PARAMREQLIST, g_dhcpReqParams, 13); // 结束 *pOpt = OPT_END; // 计算包总长度 tolSize = (int)((pOpt - p->dhcp.options) + 1 + sizeof(DHCP_PACKAGE)); - // 计算 IP 数据长度 - p->vlan_hdr.ip.tot_len = htons(tolSize - sizeof(struct ethhdr) - sizeof(struct vlan_hdr)); - // 计算 UDP 数据长度 - p->vlan_hdr.udp.len = htons(tolSize - sizeof(struct ethhdr) - sizeof(struct vlan_hdr) - sizeof(struct iphdr)); - - // 计算 IP 校验和 - csum = htons(ip_checksum((unsigned char *)&p->vlan_hdr.ip)); - p->vlan_hdr.ip.check = htons(csum); - - // 计算 UDP 校验和 - csum = htons(udp_checksum(p->vlan_hdr.ip.saddr, p->vlan_hdr.ip.daddr, (unsigned char *)&p->vlan_hdr.udp)); - p->vlan_hdr.udp.check = htons(csum); + cacl_package_checksum(p, tolSize); *pOutSize = tolSize; } @@ -186,8 +231,97 @@ U8 *dhcp_create_discover_req(PDHCP_INFO pInfo, int *pOutSize) { } static void on_dhcp_recv(uv_work_t *req) { - printf("+++++++++++++recv\n"); - //LOG_MOD_HEX(debug, ZLOG_MOD_MAIN, dhcp_create_discover_req(&info), 512); + PDHCP_INFO pInfo; + U32 ip; + DHCP_RSP rspDhcp; + DHCP_OPT optMsg, opt; + int ret; + PPKG_PROCESS_INFO pWork = (PPKG_PROCESS_INFO)req->data; + PDHCP_PACKAGE pkg = (PDHCP_PACKAGE)pWork->pPkgBase; + U32 optSize = pWork->nSize - sizeof(DHCP_PACKAGE); + + // Check op flag + if (pkg->dhcp.op != BOOTP_REPLY) { + LOG_MOD(error, ZLOG_MOD_DHCPD, "Error message op code %d\n", pkg->dhcp.op); + return; + } + + // 获取消息类型 + ret = dhcp_get_option(OPT_MESSAGETYPE, pkg->dhcp.options, optSize, &optMsg); + if (ret != ERR_SUCCESS) { + LOG_MOD(error, ZLOG_MOD_DHCPD, "Get \'message type\' option error %d\n", ret); + return; + } + + pInfo = get_dhcp_info_by_id(DHCP_XID(pkg->dhcp.xid)); + + if (pInfo == NULL) { + LOG_MOD(error, ZLOG_MOD_DHCPD, "Unknown Client %d\n", DHCP_XID(pkg->dhcp.xid) & 0xFFFFFF); + return; + } + + memset(&rspDhcp, 0, sizeof(DHCP_RSP)); + rspDhcp.xid = DHCP_XID(pkg->dhcp.xid); + rspDhcp.ipAddr = ntohl(pkg->dhcp.yiaddr); + memcpy(rspDhcp.cliMac, pkg->dhcp.chaddr, ETH_ALEN); + memcpy(rspDhcp.svrHostname, pkg->dhcp.sname, 64); + + switch (*optMsg.pValue) { + case DHCP_MSG_OFFER: + ret = dhcp_get_option(OPT_NETMASK, pkg->dhcp.options, optSize, &opt); + if (ret == ERR_SUCCESS && opt.len == sizeof(U32)) { + rspDhcp.netmask = ntohl(*((U32 *)opt.pValue)); + } + + ret = dhcp_get_option(OPT_DNS, pkg->dhcp.options, optSize, &opt); + if (ret == ERR_SUCCESS) { + U32 *pVal = (U32 *)opt.pValue; + rspDhcp.primeDNS = ntohl(*pVal); + if (optSize == 8) { + pVal++; + rspDhcp.secondDNS = ntohl(*pVal); + } + } + + ret = dhcp_get_option(OPT_ROUTER, pkg->dhcp.options, optSize, &opt); + if (ret == ERR_SUCCESS) { + rspDhcp.route = ntohl(*((U32 *)opt.pValue)); + DEBUG_CODE_LINE(); + } + + ret = dhcp_get_option(OPT_IPADDRLEASE, pkg->dhcp.options, optSize, &opt); + if (ret == ERR_SUCCESS) { + rspDhcp.leaseTime = ntohl(*((U32 *)opt.pValue)); + DEBUG_CODE_LINE(); + } + + ret = dhcp_get_option(OPT_SERVERID, pkg->dhcp.options, optSize, &opt); + if (ret == ERR_SUCCESS) { + rspDhcp.svrIp = ntohl(*((U32 *)opt.pValue)); + DEBUG_CODE_LINE(); + } else { + printf("++++++++++++++++++++++++ret = %d\n", ret); + } + + ret = dhcp_get_option(OPT_DOMAINNAME, pkg->dhcp.options, optSize, &opt); + if (ret == ERR_SUCCESS) { + strncpy(rspDhcp.domainName, (char *)opt.pValue, MIN((int)opt.len, 64)); + DEBUG_CODE_LINE(); + } + + pInfo->step = STEP_OFFER; + pInfo->status = STA_RECV_RSP; + + printf("rspIp = %08X, svrIp = %08X\n", rspDhcp.ipAddr, rspDhcp.svrIp); + + cacheDhcpOfferBuffer(pInfo, pWork->pPkgBase, pWork->nSize); + memcpy(&pInfo->offerRsp, &rspDhcp, sizeof(DHCP_RSP)); + break; + case DHCP_MSG_ACK: + break; + case DHCP_MSG_NAK: + break; + } } int dhcp_tools_init_network(const char *pNicName) { @@ -195,7 +329,7 @@ int dhcp_tools_init_network(const char *pNicName) { static uv_poll_t uvSocket; static uv_timer_t uvTm; int ret; - + if (g_pNicName == NULL) { g_pNicName = strdup(pNicName); } else if (strcmp(pNicName, g_pNicName) != 0) { diff --git a/srcs/service/dhcpd/include/rfc2131.h b/srcs/service/dhcpd/include/rfc2131.h index 99cf2e6..64ce1c2 100644 --- a/srcs/service/dhcpd/include/rfc2131.h +++ b/srcs/service/dhcpd/include/rfc2131.h @@ -29,6 +29,20 @@ typedef struct { char hostName[256]; } DHCP_REQ, *PDHCP_REQ; +typedef struct { + U8 cliMac[ETH_ALEN]; + char svrHostname[64]; + char domainName[64]; + U32 svrIp; + U32 xid; + U32 ipAddr; + U32 netmask; + U32 route; + U32 primeDNS; + U32 secondDNS; + U32 leaseTime; +} DHCP_RSP, *PDHCP_RSP; + #ifdef __cplusplus } #endif