vcpe/srcs/service/dhcpd/dhcpd_network.c

924 lines
32 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 xajhuang on 2023/3/16.
//
#include <stdlib.h>
#include <string.h>
#include <linux/filter.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/mman.h>
#include <pcap/pcap.h>
#include <unistd.h>
#include "user_errno.h"
#include "task_manager.h"
#include "zlog_module.h"
#include "dhcp_options.h"
#include "config.h"
#include "misc.h"
#include "user_mgr.h"
#include "rfc2131.h"
#include "dhcp_network.h"
#include "lease.h"
#define MAXIMUM_SNAPLEN (262144)
/*
TORs (Top of Rack switch) at Facebook run DHCP relayers, these relayers are
responsible for relaying broadcast DHCP traffic (DISCOVERY and SOLICIT
messages) originating within their racks to anycast VIPs, one DHCPv4 and one
for DHCPv6.
*/
#ifdef USED_DEFAULT_BPF
static struct sock_filter g_filterCode[] = {
#ifdef UDP_DHCP_FILTER
// create by: tcpdump "udp and port 67 and port 68" -dd
{0x28, 0, 0, 0x0000000c},
{0x15, 0, 9, 0x000086dd},
{0x30, 0, 0, 0x00000014},
{0x15, 0, 21, 0x00000011},
{0x28, 0, 0, 0x00000036},
{0x15, 0, 2, 0x00000043},
{0x28, 0, 0, 0x00000038},
{0x15, 16, 17, 0x00000044},
{0x15, 0, 16, 0x00000044},
{0x28, 0, 0, 0x00000038},
{0x15, 13, 14, 0x00000043},
{0x15, 0, 13, 0x00000800},
{0x30, 0, 0, 0x00000017},
{0x15, 0, 11, 0x00000011},
{0x28, 0, 0, 0x00000014},
{0x45, 9, 0, 0x00001fff},
{0xb1, 0, 0, 0x0000000e},
{0x48, 0, 0, 0x0000000e},
{0x15, 0, 2, 0x00000043},
{0x48, 0, 0, 0x00000010},
{0x15, 3, 4, 0x00000044},
{0x15, 0, 3, 0x00000044},
{0x48, 0, 0, 0x00000010},
{0x15, 0, 1, 0x00000043},
{0x6, 0, 0, 0x00040000},
{0x6, 0, 0, 0x00000000},
#endif
#if 0
// create by: tcpdump "vxlan" -dd
{0x28, 0, 0, 0x0000000c},
{0x15, 2, 0, 0x00008100},
{0x15, 1, 0, 0x000088a8},
{0x15, 0, 1, 0x00009100},
{0x6, 0, 0, 0x00040000},
{0x6, 0, 0, 0x00000000},
#endif
// region BPF code
// create by: tcpdump "vlan and udp and port 67 and port 68" -dd
{0x0, 0, 0, 0x00000000},
{0x2, 0, 0, 0x00000000},
{0x2, 0, 0, 0x00000001},
{0x30, 0, 0, 0xfffff030},
{0x15, 7, 0, 0x00000001},
{0x0, 0, 0, 0x00000004},
{0x2, 0, 0, 0x00000000},
{0x2, 0, 0, 0x00000001},
{0x28, 0, 0, 0x0000000c},
{0x15, 2, 0, 0x00008100},
{0x15, 1, 0, 0x000088a8},
{0x15, 0, 56, 0x00009100},
{0x61, 0, 0, 0x00000001},
{0x48, 0, 0, 0x0000000c},
{0x15, 0, 13, 0x000086dd},
{0x61, 0, 0, 0x00000000},
{0x50, 0, 0, 0x00000014},
{0x15, 0, 50, 0x00000011},
{0x61, 0, 0, 0x00000000},
{0x48, 0, 0, 0x00000036},
{0x15, 0, 3, 0x00000043},
{0x61, 0, 0, 0x00000000},
{0x48, 0, 0, 0x00000038},
{0x15, 43, 44, 0x00000044},
{0x15, 0, 43, 0x00000044},
{0x61, 0, 0, 0x00000000},
{0x48, 0, 0, 0x00000038},
{0x15, 39, 40, 0x00000043},
{0x15, 0, 39, 0x00000800},
{0x61, 0, 0, 0x00000000},
{0x50, 0, 0, 0x00000017},
{0x15, 0, 36, 0x00000011},
{0x61, 0, 0, 0x00000000},
{0x48, 0, 0, 0x00000014},
{0x45, 33, 0, 0x00001fff},
{0x61, 0, 0, 0x00000000},
{0x50, 0, 0, 0x0000000e},
{0x54, 0, 0, 0x0000000f},
{0x64, 0, 0, 0x00000002},
{0xc, 0, 0, 0x00000000},
{0x7, 0, 0, 0x00000000},
{0x48, 0, 0, 0x0000000e},
{0x15, 0, 8, 0x00000043},
{0x61, 0, 0, 0x00000000},
{0x50, 0, 0, 0x0000000e},
{0x54, 0, 0, 0x0000000f},
{0x64, 0, 0, 0x00000002},
{0xc, 0, 0, 0x00000000},
{0x7, 0, 0, 0x00000000},
{0x48, 0, 0, 0x00000010},
{0x15, 16, 17, 0x00000044},
{0x61, 0, 0, 0x00000000},
{0x50, 0, 0, 0x0000000e},
{0x54, 0, 0, 0x0000000f},
{0x64, 0, 0, 0x00000002},
{0xc, 0, 0, 0x00000000},
{0x7, 0, 0, 0x00000000},
{0x48, 0, 0, 0x0000000e},
{0x15, 0, 9, 0x00000044},
{0x61, 0, 0, 0x00000000},
{0x50, 0, 0, 0x0000000e},
{0x54, 0, 0, 0x0000000f},
{0x64, 0, 0, 0x00000002},
{0xc, 0, 0, 0x00000000},
{0x7, 0, 0, 0x00000000},
{0x48, 0, 0, 0x00000010},
{0x15, 0, 1, 0x00000043},
{0x6, 0, 0, 0x00040000},
{0x6, 0, 0, 0x00000000},
// endregion
};
#endif
static struct sock_fprog bpf;
static PACKET_MMAP_RING g_pkgRing;
static NIC_INFO g_nicInfo;
static uv_udp_t g_uvRawSockReq;
static DHCP_WORK_MODE g_dhcpMode;
U32 dhcp_get_default_netmask() {
return g_nicInfo.netmask;
}
void *get_pkg_free_buf() {
int i;
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))) {
return (U8 *)hdr + TPACKET3_HDRLEN - sizeof(struct sockaddr_ll);
}
}
return NULL;
}
U32 pkg_mmap_tx(U8 *pData, U32 nBytes) {
struct tpacket3_hdr *hdr = (struct tpacket3_hdr *)(pData + sizeof(struct sockaddr_ll) - TPACKET3_HDRLEN);
hdr->tp_next_offset = 0;
hdr->tp_len = nBytes;
hdr->tp_snaplen = nBytes;
hdr->tp_status = TP_STATUS_SEND_REQUEST;
return hdr->tp_len;
}
static void fill_package(PDHCP_PACKAGE pRsp, PDHCP_PACKAGE pReq) {
memset(pRsp, 0, MAX_DHCP_PKG_SIZE);
// 二层头
// 目的IP地址
if (pReq->hdr.ip.saddr != 0) {
memcpy(pRsp->hdr.eth.h_dest, pRsp->hdr.eth.h_source, ETH_ALEN);
} else {
memset(pRsp->hdr.eth.h_dest, 0xFF, ETH_ALEN);
}
// 源 IP 地址
memcpy(pRsp->hdr.eth.h_source, g_nicInfo.macAddr, ETH_ALEN);
// protol
pRsp->hdr.eth.h_proto = pReq->hdr.eth.h_proto;
#if VLAN_SUPPORT
// QinQ 隧道
pRsp->hdr.vlan.id = pReq->hdr.vlan.id;
pRsp->hdr.vlan.type = pReq->hdr.vlan.type;
// TODO 可能的二层QinQ隧道
#endif
// IP 头
memcpy(&pRsp->hdr.ip, &pReq->hdr.ip, sizeof(struct iphdr));
// TOS
pRsp->hdr.ip.tos = 0;
// 更新源IP
pRsp->hdr.ip.saddr = g_nicInfo.ipAddr;
// 更新目的IP地址广播255.255.255.255
if (pReq->hdr.ip.saddr == 0) {
pRsp->hdr.ip.daddr = 0xFFFFFFFF;
} else {
pRsp->hdr.ip.daddr = pReq->hdr.ip.saddr;
}
// UDP 头
// 目的端口
pRsp->hdr.udp.dest = pReq->hdr.udp.source;
// 源端口
pRsp->hdr.udp.source = pReq->hdr.udp.dest;
//DHCP 协议
// 返回类型
pRsp->dhcp.op = BOOTP_REPLY;
// 地址类型长度
pRsp->dhcp.htype = pReq->dhcp.htype;
// 地址长度
pRsp->dhcp.hlen = pReq->dhcp.hlen;
// xid
pRsp->dhcp.xid = pReq->dhcp.xid;
// 客户端 MAC 地址
memcpy(pRsp->dhcp.chaddr, pReq->dhcp.chaddr, ETH_ALEN);
// DHCP服务端主机名
memcpy(pRsp->dhcp.sname, g_nicInfo.hostname, MIN(64, strlen(g_nicInfo.hostname)));
// Magic Cookie: DHCP
//
// 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;
pRsp->dhcp.ciaddr = 0;
pRsp->dhcp.siaddr = 0;
pRsp->dhcp.giaddr = 0;
}
static int dhcp_prepare_tx(U8 *pOpt, PDHCP_PACKAGE pRsp) {
U16 csum;
int tolSize;
// 计算包总长度
tolSize = (int)((pOpt - pRsp->dhcp.options) + 1 + sizeof(DHCP_PACKAGE));
// 计算 IP 数据长度
pRsp->hdr.ip.tot_len = htons(tolSize - sizeof(struct ethhdr) - sizeof(struct vlan_hdr));
// 计算 UDP 数据长度
pRsp->hdr.udp.len = htons(tolSize - sizeof(struct ethhdr) - sizeof(struct vlan_hdr) - sizeof(struct iphdr));
// 计算 IP 校验和
csum = htons(ip_checksum((unsigned char *)&pRsp->hdr.ip));
pRsp->hdr.ip.check = htons(csum);
// 计算 UDP 校验和
csum = htons(udp_checksum(pRsp->hdr.ip.saddr, pRsp->hdr.ip.daddr, (unsigned char *)&pRsp->hdr.udp));
pRsp->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_nack(PDHCP_PACKAGE pReq) {
U8 *pOpt;
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);
// DHCP Options
pOpt = pRsp->dhcp.options;
// DHCP 消息类型
pOpt += dhcp_add_u8_option(pOpt, OPT_MESSAGETYPE, DHCP_MSG_NAK);
*pOpt = OPT_END;
return dhcp_prepare_tx(pOpt, pRsp);
}
static int dhcp_resp_ack(PDHCP_PACKAGE pReq, PPOOL_CTX pIpInfo) {
U8 *pOpt;
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;
return dhcp_prepare_tx(pOpt, pRsp);
}
static int dhcp_resp_offer(PDHCP_PACKAGE pReq, PPOOL_CTX pIpInfo, U32 ip) {
U8 *pOpt;
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;
// DHCP 消息类型
pOpt += dhcp_add_u8_option(pOpt, OPT_MESSAGETYPE, DHCP_MSG_OFFER);
// 子网掩码
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;
return dhcp_prepare_tx(pOpt, pRsp);
}
static void on_sock_recv(uv_work_t *req) {
char macStr[20] = {0};
U32 ip;
PPOOL_CTX pIpInfo;
DHCP_REQ reqDhcp;
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);
//sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", macStr[0], macStr[1], macStr[2], macStr[3], macStr[4], macStr[5]);
// Check op flag
if (pkg->dhcp.op != BOOTP_REQUEST) {
LOG_MOD(error, ZM_DHCP_NET, "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, ZM_DHCP_NET, "Get \'message type\' option error %d\n", ret);
return;
}
memset(&reqDhcp, 0, sizeof(DHCP_REQ));
reqDhcp.unicast = pkg->dhcp.flags != 0 ? TRUE : FALSE;
reqDhcp.xid = DHCP_XID(pkg->dhcp.xid);
#if VLAN_SUPPORT
reqDhcp.uid = VLAN_VNI_ID(pkg->hdr.vlan.id);
#else
reqDhcp.uid = 0;
#endif
reqDhcp.serverAddr = g_nicInfo.ipAddr;
reqDhcp.cliAddr = ntohl(pkg->dhcp.ciaddr);
memcpy(reqDhcp.cliMac, pkg->dhcp.chaddr, ETH_ALEN);
MAC_TO_STR(reqDhcp.cliMac, macStr);
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));
}
ret = dhcp_get_option(OPT_IPADDRLEASE, pkg->dhcp.options, optSize, &opt);
if (ret == ERR_SUCCESS && opt.len == sizeof(U32)) {
reqDhcp.leaseTime = ntohl(*((U32 *)opt.pValue));
}
ret = dhcp_get_option(OPT_CLIENTID, pkg->dhcp.options, optSize, &opt);
if (ret == ERR_SUCCESS) {
strncpy(reqDhcp.clientId, (char *)opt.pValue, MIN((int)opt.len, 256));
}
ret = dhcp_get_option(OPT_VENDORCLASSID, pkg->dhcp.options, optSize, &opt);
if (ret == ERR_SUCCESS) {
strncpy(reqDhcp.vendorClassId, (char *)opt.pValue, MIN((int)opt.len, 256));
}
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));
}
ret = pre_alloc_dhcp_res(&reqDhcp, pWork->pUser, &ip, &pIpInfo);
if (ret == ERR_SUCCESS) {
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);
if (ret != ERR_SUCCESS) {
LOG_MOD(error, ZM_DHCP_NET, "Send Offer error: %d\n", ret);
}
} 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));
// Send NACK
ret = dhcp_resp_nack(pkg);
}
if (ret != ERR_SUCCESS) {
LOG_MOD(error, ZM_DHCP_NET, "Send Offer error: %d\n", ret);
}
// endregion
break;
case DHCP_MSG_REQUEST:
// region DHCP 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));
} else {
// Request 服务器IP可以取目的 IP 地址
if (pkg->hdr.ip.saddr != 0) {
reqDhcp.reqServer = ntohl(pkg->hdr.ip.daddr);
}
}
// 客户端请求的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));
} else {
reqDhcp.reqIpAddr = reqDhcp.cliAddr;
}
// 客户端主机名
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) && reqDhcp.reqServer != 0) {
const char *pReq = u32_to_str_ip_safe(htonl(reqDhcp.reqServer));
const char *pLocal = u32_to_str_ip_safe(g_nicInfo.ipAddr);
LOG_MOD_HEX(error, ZM_DHCP_NET, pWork->pPkgBase, pWork->nSize);
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};
if ((ret = is_pre_assigned(&reqDhcp, &ctx)) == ERR_SUCCESS) {
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);
} 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));
// Send NACK
ret = dhcp_resp_nack(pkg);
}
if (ret != ERR_SUCCESS) {
LOG_MOD(error, ZM_DHCP_NET, "Send Ack error: %d\n", ret);
}
}
// endregion
break;
case DHCP_MSG_RELEASE:
// region DHCP Release 处理
// 客户端请求的服务器
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));
} else {
// Request 服务器IP可以取目的 IP 地址
if (pkg->hdr.ip.saddr != 0) {
reqDhcp.reqServer = ntohl(pkg->hdr.ip.saddr);
}
}
reqDhcp.reqIpAddr = ntohl(pkg->dhcp.ciaddr);
lease_release(&reqDhcp);
LOG_MOD(debug, ZM_DHCP_NET, "User %u DHCP release lease: [%s(%s)] --> %s\n", reqDhcp.uid, macStr,
reqDhcp.hostName, u32_to_str_ip(ntohl(reqDhcp.reqIpAddr)));
// endregion
break;
case DHCP_MSG_INFORM:
// 客户端主机名
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));
}
// 当前分配的租约添加到本地数据库中, 后续分配能够发现对应的主机信息
ret = lease_add_host(&reqDhcp);
if (ret == ERR_SUCCESS) {
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(reqDhcp.cliAddr)));
// 可以向客户端回复租约信息
} 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));
}
break;
case DHCP_MSG_DECLINE:
// 清理掉上次分配的租约
break;
default:
LOG_MOD(error, ZM_DHCP_NET, "Unkonwn DHCP message type: %d\n", *optMsg.pValue);
LOG_MSG_HEX(trace, pkg, pWork->nSize);
break;
}
//dhcp_option_prase(optMsg, pkg->dhcp.options, pWork->nSize - sizeof(DHCP_PACKAGE));
LOG_MSG_HEX(trace, pkg, pWork->nSize);
//LOG_MSG(info, "vni: %d, xid: 0x%08X\n", VLAN_VNI_ID(pkg->hdr.vlan.id), DHCP_XID(pkg->dhcp.xid));
#if 0
LOG_MOD(info, ZM_DHCP_NET, "vlan = %u\n", VXLAN_VIN_ID_PACK(pkg->hdr.vlan.id));
LOG_MSG(info, "xid: 0x%08X\n", ntohl(pkg->dhcp.xid));
LOG_MSG(info,
"dest mac: %02X:%02X:%02X:%02X:%02X:%02X\n",
pkg->hdr.eth.h_dest[0],
pkg->hdr.eth.h_dest[1],
pkg->hdr.eth.h_dest[2],
pkg->hdr.eth.h_dest[3],
pkg->hdr.eth.h_dest[4],
pkg->hdr.eth.h_dest[5]);
LOG_MSG(info,
"client mac: %02X:%02X:%02X:%02X:%02X:%02X\n",
pkg->dhcp.chaddr[0],
pkg->dhcp.chaddr[1],
pkg->dhcp.chaddr[2],
pkg->dhcp.chaddr[3],
pkg->dhcp.chaddr[4],
pkg->dhcp.chaddr[5]);
#endif
}
static void after_msg_recv(uv_work_t *req, int status) {
PPKG_PROCESS_INFO pInfo = (PPKG_PROCESS_INFO)req->data;
PPKG_MSG pMsg = (PPKG_MSG)pInfo->pData;
pMsg->nf -= 1;
if (pMsg->nf == 0) {
LOG_MOD(trace, ZM_DHCP_NET, "---Free resources: %p\n", pMsg);
free(pMsg->pPkgInfo);
free(pMsg);
}
}
void raw_sock_recv_cb(uv_poll_t *handle, int status, int events) {
static unsigned int block_num = 0;
PRECV_CB_DATA pCbData = handle->data;
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;
struct tpacket3_hdr *ppd;
unsigned int memSize = sizeof(PKG_PROCESS_INFO) * pbd->h1.num_pkts;
PPKG_MSG pMsg = (PPKG_MSG)malloc(sizeof(PKG_MSG));
if (pMsg == NULL) {
LOG_MOD(error, ZM_DHCP_NET, "Malloc memory error: %lu\n", sizeof(PKG_MSG));
return;
}
memset(pMsg, 0, sizeof(PKG_MSG));
pMsg->pPkgInfo = (PPKG_PROCESS_INFO)malloc(memSize);
if (pMsg->pPkgInfo == NULL) {
LOG_MOD(error, ZM_DHCP_NET, "Malloc memory error: %u\n", memSize);
free(pMsg);
return;
}
memset(pMsg->pPkgInfo, 0, memSize);
pMsg->nf = pbd->h1.num_pkts;
ppd = (struct tpacket3_hdr *)((uint8_t *)pbd + pbd->h1.offset_to_first_pkt);
for (i = 0; i < pbd->h1.num_pkts; i++) {
PDHCP_PACKAGE pkg;
U32 uid;
pMsg->pPkgInfo[i].pPkgBase = ((uint8_t *)ppd + ppd->tp_mac);
pMsg->pPkgInfo[i].nSize = ppd->tp_snaplen;
pMsg->pPkgInfo[i].uvWork.data = &pMsg->pPkgInfo[i];
pMsg->pPkgInfo[i].pData = pMsg;
if (g_dhcpMode == MODE_DHCP_SERVER) {
#if VLAN_SUPPORT
pkg = (PDHCP_PACKAGE)pMsg->pPkgInfo[i].pPkgBase;
uid = VLAN_VNI_ID(pkg->hdr.vlan.id);
#else
uid = 0;
#endif
pMsg->pPkgInfo[i].pUser = dhcp_user_create(uid);
// LOG_MOD(trace, ZM_DHCP_NET, ">>> User %u xid %08X addr %p\n", uid, DHCP_XID(pkg->dhcp.xid),
// pMsg->pPkgInfo[i].pUser);
}
if (pCbData) {
uv_queue_work(get_task_manager(), &(pMsg->pPkgInfo[i].uvWork),
pCbData->work_cb ? pCbData->work_cb : on_sock_recv,
pCbData->after_work_cb ? pCbData->after_work_cb : after_msg_recv);
} else {
uv_queue_work(get_task_manager(), &(pMsg->pPkgInfo[i].uvWork), on_sock_recv, after_msg_recv);
}
ppd = (struct tpacket3_hdr *)((uint8_t *)ppd + ppd->tp_next_offset);
}
}
pbd->h1.block_status = TP_STATUS_KERNEL;
block_num = (block_num + 1) % PKG_MMAP_BLOCKNUM;
}
}
int create_udp_raw_socket(const char *pNicName) {
struct sockaddr_ll addr;
unsigned int size;
int i;
int err;
int v = TPACKET_V3;
// 1. create socket
int sock_fd = socket(PF_PACKET, SOCK_RAW, 0);
if (sock_fd < 0) {
LOG_MOD(error, ZM_DHCP_NET, "Socket created failure: %s --> %s\n", pNicName, strerror(errno));
return -ERR_SOCK_CREATE;
}
// 2. attach filter (no need to call bind)
if ((err = setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf))) < 0) {
LOG_MOD(error, ZM_DHCP_NET, "Attaching filter failed: %d\n", err);
return -ERR_SOCK_SETOPT;
}
// 3. set PACKET_MMAP version
if ((err = setsockopt(sock_fd, SOL_PACKET, PACKET_VERSION, &v, sizeof(v))) < 0) {
LOG_MOD(error, ZM_DHCP_NET, "Set PACKET_VERSION option failed: %d\n", err);
return -ERR_SOCK_SETOPT;
}
// 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.recv, sizeof(g_pkgRing.recv))) < 0) {
LOG_MOD(error, ZM_DHCP_NET, "Set PACKET_RX_RING option failed: %d\n", err);
return -ERR_SOCK_SETOPT;
}
// 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 ((err = setsockopt(sock_fd, SOL_PACKET, PACKET_TX_RING, &g_pkgRing.send, sizeof(g_pkgRing.recv))) < 0) {
LOG_MOD(error, ZM_DHCP_NET, "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, ZM_DHCP_NET, "MMAP socket ring failed\n");
perror("title");
return -ERR_MMAP_MEMORY;
}
LOG_MOD(trace, ZM_DHCP_NET, "size = %u\n", size);
LOG_MOD(trace, ZM_DHCP_NET, "MMAP address recv = %p, send = %p\n", g_pkgRing.map_recv, g_pkgRing.map_send);
// 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, ZM_DHCP_NET, "Malloc memory failed: %lu\n", g_pkgRing.recv.tp_block_nr * sizeof(struct iovec));
return -ERR_MMAP_MEMORY;
}
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;
}
// 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, ZM_DHCP_NET, "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(pNicName);
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(struct sockaddr_ll))) < 0) {
LOG_MOD(error, ZM_DHCP_NET, "Bind raw socket failed: %d\n", err);
return -ERR_SOCK_SETOPT;
}
g_pkgRing.sock = sock_fd;
return ERR_SUCCESS;
}
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, ZM_DHCP_NET, "Send packet error\n");
}
break;
}
}
}
void init_filter(const char *pNetFilter) {
#ifdef USED_DEFAULT_BPF
bpf.len = sizeof(g_filterCode) / (sizeof(struct sock_filter));
bpf.filter = g_filterCode;
#else
static pcap_t *pd;
pd = pcap_open_dead(DLT_EN10MB, MAXIMUM_SNAPLEN);
struct bpf_program fcode;
pcap_compile(pd, &fcode, pNetFilter, 1, 0);
struct bpf_insn *insn = fcode.bf_insns;
struct sock_filter *g_filters = (struct sock_filter *)malloc(fcode.bf_len * sizeof(struct sock_filter));
for (int i = 0; i < fcode.bf_len; ++insn, ++i) {
g_filters[i].code = insn->code;
g_filters[i].jt = insn->jt;
g_filters[i].jf = insn->jf;
g_filters[i].k = insn->k;
}
bpf.len = fcode.bf_len;
bpf.filter = g_filters;
pcap_close(pd);
pcap_freecode(&fcode);
#endif
}
void init_raw_socket_poll(void *pRecv, void *pClean) {
static RECV_CB_DATA rcData;
static uv_poll_t uvSocket;
static uv_timer_t uvTm;
uv_udp_init(get_task_manager(), &g_uvRawSockReq);
uv_udp_open(&g_uvRawSockReq, g_pkgRing.sock);
uv_poll_init_socket(get_task_manager(), &uvSocket, g_pkgRing.sock);
rcData.work_cb = pRecv;
rcData.after_work_cb = pClean;
uvSocket.data = &rcData;
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);
}
int dhcpd_init(DHCP_WORK_MODE workMode) {
int ret;
size_t size = MAX_PATH;
g_dhcpMode = workMode;
memset(&g_nicInfo, 0, sizeof(NIC_INFO));
g_nicInfo.pIfName = (char *)config_get_dhcp_nic_name();
uv_os_gethostname(g_nicInfo.hostname, &size);
get_nic_info(g_nicInfo.pIfName, &g_nicInfo.ipAddr, &g_nicInfo.netmask, NULL, g_nicInfo.macAddr);
init_filter(config_get_dhcp_net_filter());
ret = create_udp_raw_socket(g_nicInfo.pIfName);
if (ret != ERR_SUCCESS) {
LOG_MOD(error, ZM_DHCP_NET, "Create receive RAW Socket Error: %s(%d)\n", getErrorEnumNameString(-ret), ret);
return ret;
}
// 加载所有DHCP配置
ip_pool_init_from_config();
dhcp_user_mgr_init();
dhcp_option_cfg_init();
dhcp_lease_init();
init_raw_socket_poll(NULL, NULL);
return ERR_SUCCESS;
}
int dhcp_uninit() {
unsigned int 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;
close(g_pkgRing.sock);
g_pkgRing.sock = -1;
if (g_pkgRing.tx) {
free(g_pkgRing.tx);
}
if (g_pkgRing.rx) {
free(g_pkgRing.rx);
}
munmap(g_pkgRing.map_recv, size);
return ERR_SUCCESS;
}