From 6ef6baaba37a9e1ca913661b2bd2b417a80ce4d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=98=95?= Date: Thu, 27 Apr 2023 17:04:43 +0800 Subject: [PATCH] =?UTF-8?q?OCT=201.=20=E5=A2=9E=E5=8A=A0=20DHCP=20ACK=20?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dhcp_tools/detail_wnd.c | 4 +- srcs/libs/misc/err.c | 4 + srcs/service/dhcpd/db_interface.c | 63 +++++++- srcs/service/dhcpd/dhcpd_network.c | 167 +++++++++++++++++++--- srcs/service/dhcpd/include/db_interface.h | 1 + srcs/service/dhcpd/include/lease.h | 1 + srcs/service/dhcpd/include/rfc2131.h | 1 + srcs/service/dhcpd/lease.c | 4 + 8 files changed, 215 insertions(+), 30 deletions(-) diff --git a/dhcp_tools/detail_wnd.c b/dhcp_tools/detail_wnd.c index 42c9bd4..14001e7 100644 --- a/dhcp_tools/detail_wnd.c +++ b/dhcp_tools/detail_wnd.c @@ -98,11 +98,11 @@ static void add_dhcp_tree_colums(GtkWidget *treeView) { gtk_tree_store_set(store, &iterSub, 0, (name), 1, (s), -1); \ } while (0) -#define ADD_SUB_INT(name, fmt, v) \ +#define ADD_SUB_INT(name, fmt, v) \ do { \ gtk_tree_store_append(store, &iterSub, &iter); \ s = sdsempty(); \ - sprintf(s, (fmt), (v)); \ + sprintf(s, (fmt), (v)); \ gtk_tree_store_set(store, &iterSub, 0, (name), 1, s, -1); \ sdsfree(s); \ } while (0) diff --git a/srcs/libs/misc/err.c b/srcs/libs/misc/err.c index 6db4d08..3e4bf69 100644 --- a/srcs/libs/misc/err.c +++ b/srcs/libs/misc/err.c @@ -12,6 +12,10 @@ static const char *g_enumStrVal[][MAX_DESC_LENGTH] = { }; const char *getErrorEnumNameString(int errCode) { + if (errCode < 0) { + errCode = -errCode; + } + if (errCode >= ARRAY_SIZE(g_enumStrVal) || errCode < 0) { return g_enumStrVal[ARRAY_SIZE(g_enumStrVal) - 1][0]; } diff --git a/srcs/service/dhcpd/db_interface.c b/srcs/service/dhcpd/db_interface.c index 1404d96..6e7c128 100644 --- a/srcs/service/dhcpd/db_interface.c +++ b/srcs/service/dhcpd/db_interface.c @@ -14,7 +14,9 @@ #include "lease.h" // 10小时以上清理无效的预分配IP -#define DCHP_STEP_TIMEOUT (36000) +#define DCHP_STEP_CLEANUP_TIMEOUT (36000) +// Discover 预分配IP 和Request 请求间的超时时间 +#define DCHP_STEP_REQUEST_TIMEOUT (600000) #define CREATE_LEASE_TABLE() \ "CREATE TABLE IF NOT EXISTS lease " \ @@ -59,6 +61,11 @@ #define GET_PRE_ASSIGN_EXISTS_ROW_FMT \ "SELECT ip, id FROM pre_assign WHERE mac = '%s' AND hostname = '%s' AND uid = %d ORDER BY createTm DESC LIMIT 1;" +#define GET_ASSIGN_IP_INFO_FMT \ + "SELECT id, ip, lease, netmask, gateway, dns1, dns2 FROM pre_assign WHERE mac = '%s' AND hostname = '%s' AND " \ + "uid = %d AND server = '%s' AND (strftime('%%s', 'now', 'localtime') - strftime('%%s', createTm)) < " \ + "%d ORDER BY createTm DESC LIMIT 1;" + #define IP_IS_PRE_ASSIGN_NOT_TIMEOUT_FMT \ "SELECT (strftime('%%s', 'now', 'localtime') - strftime('%%s', createTm)) as tm FROM pre_assign WHERE ip = " \ "'%s' AND tm < %d AND uid = %d ORDER BY tm DESC LIMIT 1;" @@ -83,7 +90,7 @@ int lease_lock_pre_assign_ip() { char **dbResult; int nRow = 0, nColumn = 0; - snprintf(buf, 1024, GET_PRE_ASSIGN_ROW_FMT, DCHP_STEP_TIMEOUT); + snprintf(buf, 1024, GET_PRE_ASSIGN_ROW_FMT, DCHP_STEP_CLEANUP_TIMEOUT); rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL); if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) { @@ -109,7 +116,7 @@ int lease_clearup_timeout_pre_assign() { int rc; char buf[1024] = {0}; - snprintf(buf, 1024, CLS_TIMEOUT_PRE_ASSIGN_ROW_FMT, DCHP_STEP_TIMEOUT); + snprintf(buf, 1024, CLS_TIMEOUT_PRE_ASSIGN_ROW_FMT, DCHP_STEP_CLEANUP_TIMEOUT); rc = db_sqlite3_sql_exec(CREATE_LEASE_TABLE(), NULL, NULL, NULL); @@ -138,7 +145,7 @@ int lease_ip_is_pre_assign(U32 uid, U32 ip) { // 判断数据库中该IP是否未超时 memset(buf, 0, 1024); - snprintf(buf, 1024, IP_IS_PRE_ASSIGN_NOT_TIMEOUT_FMT, u32_to_str_ip(htonl(ip)), DCHP_STEP_TIMEOUT, uid); + snprintf(buf, 1024, IP_IS_PRE_ASSIGN_NOT_TIMEOUT_FMT, u32_to_str_ip(htonl(ip)), DCHP_STEP_CLEANUP_TIMEOUT, uid); rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL); sqlite3_free_table(dbResult); @@ -150,7 +157,7 @@ int lease_ip_is_pre_assign(U32 uid, U32 ip) { // 判断是否有超时预分配的IP, 存在的话可以预分配给其它请求 memset(buf, 0, 1024); - snprintf(buf, 1024, IP_IS_PRE_ASSIGN_TIMEOUT_FMT, u32_to_str_ip(htonl(ip)), DCHP_STEP_TIMEOUT, uid); + snprintf(buf, 1024, IP_IS_PRE_ASSIGN_TIMEOUT_FMT, u32_to_str_ip(htonl(ip)), DCHP_STEP_CLEANUP_TIMEOUT, uid); rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL); if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) { // 清理超时资源并预分配 @@ -169,6 +176,52 @@ int lease_ip_is_pre_assign(U32 uid, U32 ip) { return TRUE; } +int lease_get_assign_info(PDHCP_REQ pReq, PPOOL_CTX pAssign, U64 *pCtx) { + int rc; + char buf[2048] = {0}; + char macStr[20] = {0}; + U32 ipAddr; + char **dbResult; + int nRow = 0, nColumn = 0; + + MAC_TO_STR(pReq->cliMac, macStr); + snprintf(buf, 1024, GET_ASSIGN_IP_INFO_FMT, macStr, pReq->hostName, pReq->uid, u32_to_str_ip(pReq->serverAddr), + DCHP_STEP_REQUEST_TIMEOUT); + + rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL); + if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) { + char *pNetmask = dbResult[nColumn + 3]; + char *pGateway = dbResult[nColumn + 4]; + char *pDns1 = dbResult[nColumn + 5]; + char *pDns2 = dbResult[nColumn + 6]; + + // minAddr 记录可分配的IP + pAssign->minAddr = ntohl(inet_addr(dbResult[nColumn + 1])); + pAssign->leaseTime = strtoul(dbResult[nColumn + 2], NULL, 10); + + if (strlen(pNetmask) > 0) { + pAssign->netMask = ntohl(inet_addr(pNetmask)); + } + + if (strlen(pGateway) > 0) { + pAssign->gwAddr = ntohl(inet_addr(pGateway)); + } + + if (strlen(pDns1) > 0) { + pAssign->primeDNS = ntohl(inet_addr(pDns1)); + } + + if (strlen(pDns2) > 0) { + pAssign->salveDNS = ntohl(inet_addr(pDns2)); + } + + *pCtx = strtoull(dbResult[nColumn], NULL, 10); + return ERR_SUCCESS; + } + + return -ERR_ITEM_UNEXISTS; +} + int lease_get_pre_assign(U32 uid, const char *mac, const char *hostname, U32 *preAssign) { int rc; char buf[1024] = {0}; diff --git a/srcs/service/dhcpd/dhcpd_network.c b/srcs/service/dhcpd/dhcpd_network.c index f99cc79..4c3d55f 100644 --- a/srcs/service/dhcpd/dhcpd_network.c +++ b/srcs/service/dhcpd/dhcpd_network.c @@ -158,7 +158,6 @@ U32 dhcp_get_default_netmask() { void *get_pkg_free_buf() { int i; - ssize_t ret; struct tpacket3_hdr *hdr; for (i = 0; i < g_pkgRing.send.tp_frame_nr; i++) { @@ -183,17 +182,7 @@ U32 pkg_mmap_tx(U8 *pData, U32 nBytes) { return hdr->tp_len; } -static int dhcp_resp_offer(PDHCP_PACKAGE pReq, PPOOL_CTX pIpInfo, U32 ip) { - U8 *pOpt; - U16 csum; - int tolSize; - PDHCP_PACKAGE pRsp = get_pkg_free_buf(); - - if (pRsp == NULL) { - LOG_MOD(error, ZM_DHCP_NET, "Malloc memory error: %u\n", MAX_DHCP_PKG_SIZE); - return -ERR_MALLOC_MEMORY; - } - +static void fill_package(PDHCP_PACKAGE pRsp, PDHCP_PACKAGE pReq) { memset(pRsp, 0, MAX_DHCP_PKG_SIZE); // 二层头 @@ -226,15 +215,13 @@ static int dhcp_resp_offer(PDHCP_PACKAGE pReq, PPOOL_CTX pIpInfo, U32 ip) { //DHCP 协议 // 返回类型 - pRsp->dhcp.op = BOOTP_REPLY; + pRsp->dhcp.op = BOOTP_REPLY; // 地址类型长度 - pRsp->dhcp.htype = pReq->dhcp.htype; + pRsp->dhcp.htype = pReq->dhcp.htype; // 地址长度 - pRsp->dhcp.hlen = pReq->dhcp.hlen; + pRsp->dhcp.hlen = pReq->dhcp.hlen; // xid - pRsp->dhcp.xid = pReq->dhcp.xid; - // 分配的 IP 地址 - pRsp->dhcp.yiaddr = htonl(ip); + pRsp->dhcp.xid = pReq->dhcp.xid; // 客户端 MAC 地址 memcpy(pRsp->dhcp.chaddr, pReq->dhcp.chaddr, ETH_ALEN); // DHCP服务端主机名 @@ -250,6 +237,97 @@ static int dhcp_resp_offer(PDHCP_PACKAGE pReq, PPOOL_CTX pIpInfo, U32 ip) { pRsp->dhcp.ciaddr = 0; pRsp->dhcp.siaddr = 0; pRsp->dhcp.giaddr = 0; +} + +static int dhcp_resp_ack(PDHCP_PACKAGE pReq, PPOOL_CTX pIpInfo) { + U8 *pOpt; + U16 csum; + int tolSize; + PDHCP_PACKAGE pRsp = get_pkg_free_buf(); + + if (pRsp == NULL) { + LOG_MOD(error, ZM_DHCP_NET, "Malloc memory error: %u\n", MAX_DHCP_PKG_SIZE); + return -ERR_MALLOC_MEMORY; + } + + fill_package(pRsp, pReq); + + // 分配的 IP 地址 + pRsp->dhcp.yiaddr = htonl(pIpInfo->minAddr); + + // DHCP Options + pOpt = pRsp->dhcp.options; + + // DHCP 消息类型 + pOpt += dhcp_add_u8_option(pOpt, OPT_MESSAGETYPE, DHCP_MSG_ACK); + // 子网掩码 + pOpt += dhcp_add_u32_option(pOpt, OPT_NETMASK, htonl(pIpInfo->netMask)); + // 租约 + pOpt += dhcp_add_u32_option(pOpt, OPT_IPADDRLEASE, htonl(pIpInfo->leaseTime)); + // DHCP Server + pOpt += dhcp_add_u32_option(pOpt, OPT_SERVERID, g_nicInfo.ipAddr); + // DNS + if (pIpInfo->primeDNS != 0) { + if (pIpInfo->salveDNS == 0) { + pOpt += dhcp_add_u32_option(pOpt, OPT_DNS, htonl(pIpInfo->primeDNS)); + } else { + 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) { + pOpt += dhcp_add_u32_option(pOpt, OPT_ROUTER, htonl(pIpInfo->gwAddr)); + } + // DHCP 主机名 + pOpt += dhcp_add_string_option(pOpt, OPT_DOMAINNAME, "workgroup"); + *pOpt = OPT_END; + + // 计算包总长度 + tolSize = (int)((pOpt - pRsp->dhcp.options) + 1 + sizeof(DHCP_PACKAGE)); + + // 计算 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, ZM_DHCP_NET, "OPTIONS size: %ld\n", (intptr_t)(pOpt - pRsp->dhcp.options) + 1); + LOG_MOD(trace, ZM_DHCP_NET, "Total size: %d\n", tolSize); + + // 发送数据 + if (pkg_mmap_tx((U8 *)pRsp, tolSize) != tolSize) { + LOG_MOD(error, ZM_DHCP_NET, "Send package(%u bytes) error\n", tolSize); + return -ERR_SOCK_SEND; + } + + return ERR_SUCCESS; +} + +static int dhcp_resp_offer(PDHCP_PACKAGE pReq, PPOOL_CTX pIpInfo, U32 ip) { + U8 *pOpt; + U16 csum; + int tolSize; + PDHCP_PACKAGE pRsp = get_pkg_free_buf(); + + if (pRsp == NULL) { + LOG_MOD(error, ZM_DHCP_NET, "Malloc memory error: %u\n", MAX_DHCP_PKG_SIZE); + return -ERR_MALLOC_MEMORY; + } + + fill_package(pRsp, pReq); + // 分配的 IP 地址 + pRsp->dhcp.yiaddr = htonl(ip); // DHCP Options pOpt = pRsp->dhcp.options; @@ -343,11 +421,9 @@ static void on_sock_recv(uv_work_t *req) { reqDhcp.serverAddr = g_nicInfo.ipAddr; memcpy(reqDhcp.cliMac, pkg->dhcp.chaddr, ETH_ALEN); - // LOG_MOD(trace, ZM_DHCP_NET, "<<< User %u xid %08X addr %p with user %u\n", reqDhcp.uid, reqDhcp.xid, pWork->pUser, - // pWork->pUser->uid); - switch (*optMsg.pValue) { case DHCP_MSG_DISCOVER: + // region DHCP Discover 处理 ret = dhcp_get_option(OPT_REQUESTEDIPADDR, pkg->dhcp.options, optSize, &opt); if (ret == ERR_SUCCESS && opt.len == sizeof(U32)) { reqDhcp.reqIpAddr = ntohl(*((U32 *)opt.pValue)); @@ -377,7 +453,7 @@ static void on_sock_recv(uv_work_t *req) { ret = pre_alloc_dhcp_res(&reqDhcp, pWork->pUser, &ip, &pIpInfo); if (ret == ERR_SUCCESS) { - LOG_MOD(debug, ZM_DHCP_NET, "User %5u DHCP prepare assign ipaddress: [%s(%s)] --> %s\n", reqDhcp.uid, + LOG_MOD(debug, ZM_DHCP_NET, "User %u DHCP prepare assign ipaddress: [%s(%s)] --> %s\n", reqDhcp.uid, macStr, reqDhcp.hostName, u32_to_str_ip(ntohl(ip))); ret = dhcp_resp_offer(pkg, pIpInfo, ip); @@ -387,10 +463,55 @@ static void on_sock_recv(uv_work_t *req) { } } else { LOG_MOD(error, ZM_DHCP_NET, "DHCP prepare assign ipaddress error: User %u [%s(%s)] resion %s\n", - reqDhcp.uid, macStr, reqDhcp.hostName, getErrorEnumNameString(-ret)); + reqDhcp.uid, macStr, reqDhcp.hostName, getErrorEnumNameString(ret)); } + // endregion break; case DHCP_MSG_REQUEST: + // 客户端请求的服务器 + ret = dhcp_get_option(OPT_SERVERID, pkg->dhcp.options, optSize, &opt); + if (ret == ERR_SUCCESS && opt.len == sizeof(U32)) { + reqDhcp.reqServer = ntohl(*((U32 *)opt.pValue)); + } + // 客户端请求的IP + ret = dhcp_get_option(OPT_REQUESTEDIPADDR, pkg->dhcp.options, optSize, &opt); + if (ret == ERR_SUCCESS && opt.len == sizeof(U32)) { + reqDhcp.reqIpAddr = ntohl(*((U32 *)opt.pValue)); + } + + // 客户端主机名 + ret = dhcp_get_option(OPT_HOSTNAME, pkg->dhcp.options, optSize, &opt); + if (ret == ERR_SUCCESS) { + strncpy(reqDhcp.hostName, (char *)opt.pValue, MIN((int)opt.len, 256)); + } + + // 当改请求的服务端不是本机时,忽略该消息 + if (reqDhcp.reqServer != ntohl(g_nicInfo.ipAddr)) { + const char *pReq = u32_to_str_ip_safe(htonl(reqDhcp.reqServer)); + const char *pLocal = u32_to_str_ip_safe(g_nicInfo.ipAddr); + LOG_MOD(debug, ZM_DHCP_NET, "Receive others server's DHCP request: request need %s, localhost %s\n", + pReq, pLocal); + free((void *)pReq); + free((void *)pLocal); + } else { + POOL_CTX ctx = {0}; + U64 id = 0; + + if ((ret = is_pre_assigned(&reqDhcp, &ctx, &id)) == ERR_SUCCESS) { + MAC_TO_STR(reqDhcp.cliMac, macStr); + LOG_MOD(debug, ZM_DHCP_NET, "User %u DHCP assign ipaddress: [%s(%s)] --> %s\n", reqDhcp.uid, macStr, + reqDhcp.hostName, u32_to_str_ip(ntohl(ctx.minAddr))); + + ret = dhcp_resp_ack(pkg, &ctx); + + if (ret != ERR_SUCCESS) { + LOG_MOD(error, ZM_DHCP_NET, "Send Offer error: %d\n", ret); + } + } else { + LOG_MOD(error, ZM_DHCP_NET, "DHCP assign ipaddress error: User %u [%s(%s)] resion %s\n", + reqDhcp.uid, macStr, reqDhcp.hostName, getErrorEnumNameString(ret)); + } + } break; case DHCP_MSG_RELEASE: break; diff --git a/srcs/service/dhcpd/include/db_interface.h b/srcs/service/dhcpd/include/db_interface.h index fc4de94..2b7aed3 100644 --- a/srcs/service/dhcpd/include/db_interface.h +++ b/srcs/service/dhcpd/include/db_interface.h @@ -11,6 +11,7 @@ int lease_db_add_pre_assign(PDHCP_REQ pReq, U32 ip, PPOOL_CTX pPool); int lease_init_database(); int lease_get_pre_assign(U32 uid, const char *mac, const char *hostname, U32 *preAssign); int lease_ip_is_pre_assign(U32 uid, U32 ip); +int lease_get_assign_info(PDHCP_REQ pReq, PPOOL_CTX pAssign, U64 *pCtx); int lease_clearup_timeout_pre_assign(); int lease_lock_pre_assign_ip(); #ifdef __cplusplus diff --git a/srcs/service/dhcpd/include/lease.h b/srcs/service/dhcpd/include/lease.h index 8945e6d..46c3463 100644 --- a/srcs/service/dhcpd/include/lease.h +++ b/srcs/service/dhcpd/include/lease.h @@ -31,6 +31,7 @@ typedef struct { int dhcp_lease_init(); int pre_alloc_dhcp_res(PDHCP_REQ pReq, PDHCP_USER pUser, U32 *pOutIp, PPOOL_CTX *pOutPool); int usr_lease_lock_ip(PDHCP_USER pUser, U32 ip); +int is_pre_assigned(PDHCP_REQ pReq, PPOOL_CTX pCtx, U64 *dbId); #ifdef __cplusplus } #endif diff --git a/srcs/service/dhcpd/include/rfc2131.h b/srcs/service/dhcpd/include/rfc2131.h index 2f4fd47..8b759fb 100644 --- a/srcs/service/dhcpd/include/rfc2131.h +++ b/srcs/service/dhcpd/include/rfc2131.h @@ -27,6 +27,7 @@ typedef struct { U32 leaseTime; char clientId[256]; char vendorClassId[256]; + U32 reqServer; U32 serverAddr; char hostName[256]; } DHCP_REQ, *PDHCP_REQ; diff --git a/srcs/service/dhcpd/lease.c b/srcs/service/dhcpd/lease.c index 6b2d6c2..b6991d7 100644 --- a/srcs/service/dhcpd/lease.c +++ b/srcs/service/dhcpd/lease.c @@ -39,6 +39,10 @@ int usr_lease_lock_ip(PDHCP_USER pUser, U32 ip) { return -ERR_MALLOC_MEMORY; } +int is_pre_assigned(PDHCP_REQ pReq, PPOOL_CTX pCtx, U64 *dbId) { + return lease_get_assign_info(pReq, pCtx, dbId); +} + //int pre_alloc_dhcp_res(U32 uid, const char *pMac, U32 *pOutIp, PIPPOOL_INFO *pOutPool) { int pre_alloc_dhcp_res(PDHCP_REQ pReq, PDHCP_USER pUser, U32 *pOutIp, PPOOL_CTX *pOutPool) { PPOOL_CTX pPool, pTemp;