vcpe/srcs/service/dhcpd/db_interface.c

510 lines
20 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// Created by HuangXin on 2023/4/24.
//
#include <sqlite3.h>
#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;
}