// // Created by HuangXin on 2023/4/24. // #include #include "user_errno.h" #include "database.h" #include "dhcp_network.h" #include "rfc2131.h" #include "ip_pool.h" #include "db_interface.h" #include "misc.h" #include "zlog_module.h" #include "lease.h" // 10小时以上清理无效的预分配IP #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 " \ " ( id INTEGER PRIMARY KEY AUTOINCREMENT," \ " uid INTEGER NOT NULL," \ " mac CHAR(20) NOT NULL," \ " ip INTEGER NOT NULL," \ " lease INTEGER NOT NULL," \ " netmask INTEGER," \ " gateway INTEGER," \ " dns1 INTEGER," \ " dns2 INTEGER," \ " server INTEGER NOT NULL," \ " hostname CHAR(64) DEFAULT '' NOT NULL," \ " createTm TIMESTAMP DEFAULT (datetime('now', 'localtime')) NOT NULL" \ ");" \ "CREATE INDEX IF NOT EXISTS " \ "lease_index ON lease (uid, mac);" #define CREATE_PRE_ASSIGN_TABLE() \ "CREATE TABLE IF NOT EXISTS pre_assign" \ " ( id INTEGER PRIMARY KEY AUTOINCREMENT," \ " uid INTEGER NOT NULL," \ " xid INTEGER NOT NULL," \ " hostname CHAR(64) DEFAULT '' NOT NULL," \ " mac CHAR(20) NOT NULL," \ " ip CHAR(24) NOT NULL," \ " lease INTEGER NOT NULL," \ " netmask CHAR(24)," \ " gateway CHAR(24)," \ " dns1 CHAR(24)," \ " dns2 CHAR(24)," \ " server CHAR(24) NOT NULL," \ " createTm TIMESTAMP DEFAULT (datetime('now', 'localtime')) NOT NULL" \ "); CREATE INDEX IF NOT EXISTS pre_assign_index ON pre_assign(ip, uid);" #define INSERT_PRE_ASSIGN_ROW_FMT \ "INSERT INTO pre_assign (uid, xid, hostname, mac, ip, lease, netmask, gateway, dns1, dns2, server) " \ "VALUES (%d, %d, '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s');" #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;" #define IP_IS_PRE_ASSIGN "SELECT ip FROM pre_assign WHERE ip = '%s' AND uid = %d" #define IP_IS_PRE_ASSIGN_TIMEOUT_FMT \ "SELECT (strftime('%%s', 'now', 'localtime') - strftime('%%s', createTm)) as tm, id FROM pre_assign WHERE ip = " \ "'%s' AND tm >= %d AND uid = %d ORDER BY tm DESC LIMIT 1;" #define GET_PRE_ASSIGN_ROW_FMT \ "SELECT ip, uid FROM pre_assign WHERE (strftime('%%s', 'now', 'localtime') - strftime('%%s', createTm)) < %d;" #define CLS_TIMEOUT_PRE_ASSIGN_ROW_FMT \ "DELETE FROM pre_assign WHERE (strftime('%%s', 'now', 'localtime') - strftime('%%s', createTm)) > %d;" #define UPDATE_CREATE_TIME_BY_ID_FMT "UPDATE pre_assign SET createTm = datetime('now', 'localtime') WHERE id = %s" #define INSERT_LEASE_FMT \ "INSERT INTO lease(uid, hostname, mac, ip, lease, netmask, gateway, dns1, dns2, server) " \ "VALUES (%d, '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s');" #define LEASE_FIND_ITEM_FMT \ "SELECT id FROM lease WHERE mac = '%s' AND hostname = '%s' AND uid = %d AND ip = '%s' ORDER BY createTm DESC " \ "LIMIT 1;" #define LEASE_ITEM_UPDATE_FMT \ "UPDATE lease SET lease = %d, netmask = '%s', gateway = '%s', dns1 = '%s', dns2 = '%s', server = '%s', " \ "createTm = datetime('now', 'localtime') WHERE id = %s;" #define CLS_TIMEOUT_LEASE_ROW_FMT \ "DELETE FROM lease WHERE (strftime('%%s', 'now', 'localtime') - - strftime('%%s', createTm)) > lease.lease" #define GET_LEASE_ROW_FMT \ "SELECT ip, uid FROM lease WHERE (strftime('%%s', 'now', 'localtime') - strftime('%%s', createTm)) < lease.lease;" #define GET_LEASE_EXISTS_ROW_FMT \ "SELECT ip, id FROM lease WHERE mac = '%s' AND hostname = '%s' AND uid = %d AND (strftime('%%s', 'now', " \ "'localtime') - strftime('%%s', createTm)) < lease.lease ORDER BY createTm DESC LIMIT 1;" #define GET_LEASE_INFO_FMT \ "SELECT id, ip, lease, netmask, gateway, dns1, dns2 FROM lease WHERE mac = '%s' AND hostname = '%s' AND " \ "uid = %d AND server = '%s' AND (strftime('%%s', 'now','localtime') - strftime('%%s', createTm)) < lease.lease " \ "ORDER BY createTm DESC LIMIT 1;" #define UPDATE_LEASE_TIME_BY_ID_FMT "UPDATE lease SET createTm = datetime('now', 'localtime') WHERE id = %llu" #define LEASE_RELEASE_FMT "DELETE FROM lease WHERE uid = %d AND mac = '%s' AND ip = '%s'" static int lease_add(PDHCP_REQ pReq, const char *ip, const char *netmask, const char *gw, const char *dns1, const char *dns2, U32 leaseTime) { int rc; char buf[2048] = {0}; char macStr[20] = {0}; char **dbResult; int nRow = 0, nColumn = 0; MAC_TO_STR(pReq->cliMac, macStr); snprintf(buf, 2048, LEASE_FIND_ITEM_FMT, macStr, pReq->hostName, pReq->uid, ip); rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL); if (rc == ERR_SUCCESS) { // 已经存在,则更新 if (nRow > 0 && nColumn > 0) { memset(buf, 0, 2048); snprintf(buf, 2048, LEASE_ITEM_UPDATE_FMT, leaseTime, netmask, gw, dns1, dns2, u32_to_str_ip(pReq->serverAddr), dbResult[nColumn]); rc = db_sqlite3_sql_exec(buf, NULL, NULL, NULL); if (rc != ERR_SUCCESS) { LOG_MOD(error, ZM_DHCP_DB, "Add upgrade to lease db error: id = %s\n", dbResult[nColumn]); } } else { //添加一个新的 // 记录设备分配的DHCP信息到数据库 memset(buf, 0, 2048); snprintf(buf, 2048, INSERT_LEASE_FMT, pReq->uid, pReq->hostName, macStr, ip, leaseTime, netmask, gw, dns1, dns2, u32_to_str_ip(pReq->serverAddr)); rc = db_sqlite3_sql_exec(buf, NULL, NULL, NULL); if (rc != ERR_SUCCESS) { LOG_MOD(error, ZM_DHCP_DB, "Add date to lease db error\n"); } } } sqlite3_free_table(dbResult); return ERR_SUCCESS; } int db_add_lease(PDHCP_REQ pReq, PPOOL_CTX pCtx) { const char *pNetmask = u32_to_str_ip_safe(htonl(pCtx->netMask)); const char *pGateway = u32_to_str_ip_safe(htonl(pCtx->gwAddr)); const char *pDns1 = u32_to_str_ip_safe(htonl(pCtx->primeDNS)); const char *pDns2 = u32_to_str_ip_safe(htonl(pCtx->salveDNS)); const char *pIp = u32_to_str_ip_safe(htonl(pReq->cliAddr)); // 添加租约信息到lease数据库 lease_add(pReq, pIp, pNetmask, pGateway, pDns1, pDns2, pCtx->leaseTime); free((void *)pNetmask); free((void *)pGateway); free((void *)pDns1); free((void *)pDns2); free((void *)pIp); return ERR_SUCCESS; } int db_release_lease(PDHCP_REQ pReq) { int rc; char buf[1024] = {0}; char macStr[20] = {0}; MAC_TO_STR(pReq->cliMac, macStr); snprintf(buf, 1024, LEASE_RELEASE_FMT, pReq->uid, macStr, u32_to_str_ip(htonl(pReq->reqIpAddr))); rc = db_sqlite3_sql_exec(buf, NULL, NULL, NULL); if (rc != ERR_SUCCESS) { LOG_MOD(error, ZM_DHCP_DB, "DB remove lease item error: %s\n", buf); } return ERR_SUCCESS; } int db_lock_pre_assign_ip() { int rc; char buf[1024] = {0}; char **dbResult; int nRow = 0, nColumn = 0; 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) { int i; for (i = 1; i <= nRow; i++) { U32 uid = strtoul(dbResult[i * nColumn + 1], NULL, 10); U32 ip = ntohl(inet_addr(dbResult[i * nColumn])); PDHCP_USER pUser = dhcp_user_create(uid); if (pUser) { usr_lease_lock_ip(pUser, ip); LOG_MOD(debug, ZM_DHCP_DB, "Lock prepare assign ip %s for user %u\n", dbResult[i * nColumn], uid); } // printf("-- Row %d value %s, %s, %s\n", i, dbResult[i * nColumn], dbResult[i * nColumn + 1], dbResult[i * nColumn + 2]); } } sqlite3_free_table(dbResult); memset(buf, 0, 1024); snprintf(buf, 1024, GET_LEASE_ROW_FMT); rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL); if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) { int i; for (i = 1; i <= nRow; i++) { U32 uid = strtoul(dbResult[i * nColumn + 1], NULL, 10); U32 ip = ntohl(inet_addr(dbResult[i * nColumn])); PDHCP_USER pUser = dhcp_user_create(uid); if (pUser) { usr_lease_lock_ip(pUser, ip); LOG_MOD(debug, ZM_DHCP_DB, "Lock prepare assign ip %s for user %u\n", dbResult[i * nColumn], uid); } } } sqlite3_free_table(dbResult); return ERR_SUCCESS; } int db_clearup_timeout_lease() { int rc; char buf[1024] = {0}; snprintf(buf, 1024, CLS_TIMEOUT_PRE_ASSIGN_ROW_FMT, DCHP_STEP_CLEANUP_TIMEOUT); rc = db_sqlite3_sql_exec(buf, NULL, NULL, NULL); LOG_MOD(trace, ZM_DHCP_DB, "Cleanup pre_assign database timeout(%u seconds) data\n", DCHP_STEP_CLEANUP_TIMEOUT); if (rc != ERR_SUCCESS) { LOG_MOD(error, ZM_DHCP_DB, "Cleanup pre_assign database error: %s\n", buf); } rc = db_sqlite3_sql_exec(CLS_TIMEOUT_LEASE_ROW_FMT, NULL, NULL, NULL); LOG_MOD(trace, ZM_DHCP_DB, "Cleanup assign database lease expire data\n"); if (rc != ERR_SUCCESS) { LOG_MOD(error, ZM_DHCP_DB, "Cleanup lease database error: %s\n", buf); } return ERR_SUCCESS; } int db_ip_is_pre_assign(U32 uid, U32 ip) { int rc; char buf[1024] = {0}; char **dbResult; int nRow = 0, nColumn = 0; // 判断IP是否存曾经被预分配 snprintf(buf, 1024, IP_IS_PRE_ASSIGN, u32_to_str_ip(htonl(ip)), uid); rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL); sqlite3_free_table(dbResult); if (rc == ERR_SUCCESS && nRow == 0) { // 数据库没有相关记录,直接返回 LOG_MOD(trace, ZM_DHCP_DB, "New prepare assign ipaddr %s form user %u\n", u32_to_str_ip(htonl(ip)), uid); return FALSE; } // 判断数据库中该IP是否未超时 memset(buf, 0, 1024); 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); if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) { // 如果数据库存在记录说明该IP暂时不可以用 LOG_MOD(trace, ZM_DHCP_DB, "No free ip address form user %u\n", uid); return TRUE; } // 判断是否有超时预分配的IP, 存在的话可以预分配给其它请求 memset(buf, 0, 1024); 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) { // 清理超时资源并预分配 // 更新时间戳 memset(buf, 0, 1024); snprintf(buf, 1024, UPDATE_CREATE_TIME_BY_ID_FMT, dbResult[3]); db_sqlite3_sql_exec(buf, NULL, NULL, NULL); sqlite3_free_table(dbResult); LOG_MOD(trace, ZM_DHCP_DB, "New prepare assign ipaddr %s form user %u by clearup resource\n", u32_to_str_ip(htonl(ip)), uid); return FALSE; } sqlite3_free_table(dbResult); // 处理失败,默认返回 return TRUE; } static int db_get_table_items(PPOOL_CTX pAssign, const char *pRunSql, U64 *pDbId) { int rc; char **dbResult; int nRow = 0, nColumn = 0; rc = db_sqlite3_get_rows(pRunSql, &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)); } if (pDbId) { *pDbId = strtoull(dbResult[nColumn], NULL, 10); } sqlite3_free_table(dbResult); return ERR_SUCCESS; } else { sqlite3_free_table(dbResult); return -ERR_ITEM_UNEXISTS; } } int db_get_lease(PDHCP_REQ pReq, PPOOL_CTX pAssign) { int rc; U64 id; char buf[2048] = {0}; char macStr[20] = {0}; MAC_TO_STR(pReq->cliMac, macStr); snprintf(buf, 2048, GET_LEASE_INFO_FMT, macStr, pReq->hostName, pReq->uid, u32_to_str_ip(pReq->serverAddr)); rc = db_get_table_items(pAssign, buf, &id); if (rc == ERR_SUCCESS) { // 更新租约时间信息 memset(buf, 0, 1024); snprintf(buf, 1024, UPDATE_LEASE_TIME_BY_ID_FMT, id); if (db_sqlite3_sql_exec(buf, NULL, NULL, NULL) != ERR_SUCCESS) { LOG_MOD(warn, ZM_DHCP_DB, "Update %llu lease time error: %s\n", id, buf); } return ERR_SUCCESS; } return -ERR_ITEM_UNEXISTS; } int db_get_pre_lease(PDHCP_REQ pReq, PPOOL_CTX pAssign) { int rc; char buf[2048] = {0}; char macStr[20] = {0}; U64 id; MAC_TO_STR(pReq->cliMac, macStr); snprintf(buf, 2048, GET_ASSIGN_IP_INFO_FMT, macStr, pReq->hostName, pReq->uid, u32_to_str_ip(pReq->serverAddr), DCHP_STEP_REQUEST_TIMEOUT); rc = db_get_table_items(pAssign, buf, &id); if (rc == ERR_SUCCESS) { const char *pNetmask = u32_to_str_ip_safe(htonl(pAssign->netMask)); const char *pGateway = u32_to_str_ip_safe(htonl(pAssign->gwAddr)); const char *pDns1 = u32_to_str_ip_safe(htonl(pAssign->primeDNS)); const char *pDns2 = u32_to_str_ip_safe(htonl(pAssign->salveDNS)); const char *pIp = u32_to_str_ip_safe(htonl(pAssign->minAddr)); // 删除预分配IP信息 #if 0 memset(buf, 0, 2048); sprintf(buf, "DELETE FROM pre_assign WHERE id = %llu;", id); db_sqlite3_sql_exec(buf, NULL, NULL, NULL); #endif // 添加租约信息到lease数据库 lease_add(pReq, pIp, pNetmask, pGateway, pDns1, pDns2, pAssign->leaseTime); free((void *)pNetmask); free((void *)pGateway); free((void *)pDns1); free((void *)pDns2); free((void *)pIp); return ERR_SUCCESS; } return -ERR_ITEM_UNEXISTS; } int db_get_pre_assign(U32 uid, const char *mac, const char *hostname, U32 *preAssign) { int rc; char buf[1024] = {0}; char **dbResult; int nRow = 0, nColumn = 0; snprintf(buf, 1024, GET_PRE_ASSIGN_EXISTS_ROW_FMT, mac, hostname, uid); rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL); if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) { if (preAssign) { *preAssign = ntohl(inet_addr(dbResult[2])); // 更新时间戳 memset(buf, 0, 1024); snprintf(buf, 1024, UPDATE_CREATE_TIME_BY_ID_FMT, dbResult[3]); db_sqlite3_sql_exec(buf, NULL, NULL, NULL); } sqlite3_free_table(dbResult); return ERR_ITEM_EXISTS; } else { memset(buf, 0, 1024); snprintf(buf, 1024, GET_LEASE_EXISTS_ROW_FMT, mac, hostname, uid); rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL); if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) { if (preAssign) { *preAssign = ntohl(inet_addr(dbResult[2])); // 更新时间戳 memset(buf, 0, 1024); snprintf(buf, 1024, UPDATE_CREATE_TIME_BY_ID_FMT, dbResult[3]); db_sqlite3_sql_exec(buf, NULL, NULL, NULL); } sqlite3_free_table(dbResult); return ERR_ITEM_EXISTS; } } sqlite3_free_table(dbResult); return ERR_ITEM_UNEXISTS; } int db_add_pre_assign(PDHCP_REQ pReq, U32 ip, PPOOL_CTX pPool) { int rc; char buf[1024] = {0}; char macStr[20] = {0}; const char *pIp = u32_to_str_ip_safe(htonl(ip)); const char *pMask = u32_to_str_ip_safe(htonl(pPool->netMask)); const char *pGw = u32_to_str_ip_safe(htonl(pPool->gwAddr)); const char *pDns1 = u32_to_str_ip_safe(htonl(pPool->primeDNS)); const char *pDns2 = u32_to_str_ip_safe(htonl(pPool->salveDNS)); const char *pServer = u32_to_str_ip_safe(pReq->serverAddr); MAC_TO_STR(pReq->cliMac, macStr); snprintf(buf, 1024, INSERT_PRE_ASSIGN_ROW_FMT, pReq->uid, pReq->xid, pReq->hostName, macStr, pIp, pPool->leaseTime, pMask, pGw, pDns1, pDns2, pServer); free((void *)pIp); free((void *)pMask); free((void *)pGw); free((void *)pDns1); free((void *)pDns2); free((void *)pServer); rc = db_sqlite3_sql_exec(buf, NULL, NULL, NULL); if (rc != ERR_SUCCESS) { return rc; } return ERR_SUCCESS; } int db_init_lease_database() { int rc; rc = db_sqlite3_sql_exec(CREATE_LEASE_TABLE(), NULL, NULL, NULL); if (rc != ERR_SUCCESS) { return rc; } rc = db_sqlite3_sql_exec(CREATE_PRE_ASSIGN_TABLE(), NULL, NULL, NULL); if (rc != ERR_SUCCESS) { return rc; } return ERR_SUCCESS; }