vcpe/srcs/service/dhcpd/dhcpd_network.c

1045 lines
36 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;
VLAN_TYPE get_package_vlan_type(void *pBuf) {
U8 *p = (U8 *)pBuf;
if (p[12] == 0x81 && p[13] == 0x00 && p[16] == 0x81 && p[17] == 0x00) {
return VLAN_LEVEL2;
} else if (p[12] == 0x81 && p[13] == 0x00) {
return VLAN_LEVEL1;
} else {
return VLAN_NONE;
}
}
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 add_eth_head(U8 *pRsp, struct ethhdr *pEth, int isBst) {
struct ethhdr *p = (struct ethhdr *)pRsp;
// 二层头
// 目的 MAC 地址
if (!isBst) {
memcpy(p->h_dest, pEth->h_source, ETH_ALEN);
} else {
memset(p->h_dest, 0xFF, ETH_ALEN);
}
// 源 MAC 地址
memcpy(p->h_source, g_nicInfo.macAddr, ETH_ALEN);
// protol
p->h_proto = pEth->h_proto;
}
static void add_vlan_head(U8 *pRsp, PVLAN_HDR pvHdr) {
PVLAN_HDR p = (PVLAN_HDR)(pRsp + sizeof(struct ethhdr));
// QinQ 隧道
p->id = pvHdr->id;
p->type = pvHdr->type;
}
static void add_vlan2_head(U8 *pRsp, PVLAN_HDR2 pvHdr) {
PVLAN_HDR2 p = (PVLAN_HDR2)(pRsp + sizeof(struct ethhdr));
// QinQ 隧道
p->id1 = pvHdr->id1;
p->h_type = pvHdr->h_type;
p->id2 = pvHdr->id2;
p->type = pvHdr->type;
}
static void add_ip_head(struct iphdr *p, struct iphdr *pIp) {
// IP 头
memcpy(p, pIp, sizeof(struct iphdr));
// TOS
p->tos = 0;
// 更新源IP
p->saddr = g_nicInfo.ipAddr;
// 更新目的IP地址广播255.255.255.255
if (pIp->saddr == 0) {
p->daddr = 0xFFFFFFFF;
} else {
p->daddr = pIp->saddr;
}
}
static void add_udp_head(struct udphdr *p, struct udphdr *pUdp) {
// UDP 头
// 目的端口
p->dest = pUdp->source;
// 源端口
p->source = pUdp->dest;
}
static void add_dhcp_common(PDHCP_PROTO p, PDHCP_PROTO pDhcp) {
//DHCP 协议
// 返回类型
p->op = BOOTP_REPLY;
// 地址类型长度
p->htype = pDhcp->htype;
// 地址长度
p->hlen = pDhcp->hlen;
// xid
p->xid = pDhcp->xid;
// 客户端 MAC 地址
memcpy(p->chaddr, pDhcp->chaddr, ETH_ALEN);
// DHCP服务端主机名
memcpy(p->sname, g_nicInfo.hostname, MIN(64, strlen(g_nicInfo.hostname)));
// Magic Cookie: DHCP
//
// memcpy(&pRsp->dhcp.cookie, DHCP_COOKIE_VAL, 4);
p->cookie = htonl(DHCP_COOKIE_VAL);
p->hops = 0;
p->secs = 0;
p->flags = 0;
p->ciaddr = 0;
p->siaddr = 0;
p->giaddr = 0;
}
static PDHCP_PROTO fill_pkg(U8 *pRsp, U8 *pReq) {
U8 *p;
struct ethhdr *pEth = (struct ethhdr *)pReq;
struct iphdr *pIp = NULL;
struct udphdr *pUdp = NULL;
PDHCP_PROTO pDhcp = NULL;
memset(pRsp, 0, MAX_DHCP_PKG_SIZE);
switch (get_package_vlan_type(pReq)) {
default:
pIp = (struct iphdr *)(pReq + IP_HDR_OFFSET);
pUdp = (struct udphdr *)(UDP_HDR_OFFSET);
pDhcp = (PDHCP_PROTO)((U8 *)pReq + DHCP_OFFSET);
p = pRsp + sizeof(struct ethhdr);
break;
case VLAN_LEVEL1:
pIp = (struct iphdr *)(pReq + IP_HDR_OFFSET + sizeof(VLAN_HDR));
pUdp = (struct udphdr *)(pReq + UDP_HDR_OFFSET + sizeof(VLAN_HDR));
pDhcp = (PDHCP_PROTO)((U8 *)pReq + DHCP_OFFSET + sizeof(VLAN_HDR));
add_vlan_head(pRsp, (PVLAN_HDR)(pReq + IP_HDR_OFFSET));
p = pRsp + sizeof(struct ethhdr) + sizeof(VLAN_HDR);
break;
case VLAN_LEVEL2:
pIp = (struct iphdr *)(pReq + IP_HDR_OFFSET + sizeof(VLAN_HDR2));
pUdp = (struct udphdr *)(pReq + UDP_HDR_OFFSET + sizeof(VLAN_HDR2));
pDhcp = (PDHCP_PROTO)((U8 *)pReq + DHCP_OFFSET + sizeof(VLAN_HDR2));
add_vlan2_head(pRsp, (PVLAN_HDR2)(pReq + IP_HDR_OFFSET));
p = pRsp + sizeof(struct ethhdr) + sizeof(VLAN_HDR2);
break;
}
add_eth_head(pRsp, pEth, (pIp->saddr == 0) ? TRUE : FALSE);
add_ip_head((struct iphdr *)p, pIp);
p += sizeof(struct iphdr);
add_udp_head((struct udphdr *)p, pUdp);
p += sizeof(struct udphdr);
add_dhcp_common((PDHCP_PROTO)p, pDhcp);
return (PDHCP_PROTO)p;
}
static int dhcp_prepare_tx(U8 *pRsp, U32 optSize) {
struct iphdr *pIp = NULL;
struct udphdr *pUdp = NULL;
U16 csum;
U32 tolSize;
U8 vlanSize = 0;
switch (get_package_vlan_type(pRsp)) {
default:
pIp = (struct iphdr *)(pRsp + IP_HDR_OFFSET);
pUdp = (struct udphdr *)(UDP_HDR_OFFSET);
// 计算包总长度
tolSize = optSize + sizeof(DHCP_PROTO) + DHCP_OFFSET;
break;
case VLAN_LEVEL1:
pIp = (struct iphdr *)(pRsp + IP_HDR_OFFSET + sizeof(VLAN_HDR));
pUdp = (struct udphdr *)(pRsp + UDP_HDR_OFFSET + sizeof(VLAN_HDR));
// 计算包总长度
tolSize = optSize + sizeof(DHCP_PROTO) + DHCP_OFFSET + sizeof(VLAN_HDR);
vlanSize = sizeof(VLAN_HDR);
break;
case VLAN_LEVEL2:
pIp = (struct iphdr *)(pRsp + IP_HDR_OFFSET + sizeof(VLAN_HDR2));
pUdp = (struct udphdr *)(pRsp + UDP_HDR_OFFSET + sizeof(VLAN_HDR2));
// 计算包总长度
tolSize = optSize + sizeof(DHCP_PROTO) + DHCP_OFFSET + sizeof(VLAN_HDR2);
vlanSize = sizeof(VLAN_HDR2);
break;
}
// 计算 IP 数据长度
pIp->tot_len = htons(tolSize - sizeof(struct ethhdr) - vlanSize);
// 计算 UDP 数据长度
pUdp->len = htons(tolSize - sizeof(struct ethhdr) - vlanSize - sizeof(struct iphdr));
// 计算 IP 校验和
csum = htons(ip_checksum((unsigned char *)pIp));
pIp->check = htons(csum);
// 计算 UDP 校验和
csum = htons(udp_checksum(pIp->saddr, pIp->daddr, (unsigned char *)pUdp));
pUdp->check = htons(csum);
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(void *pReq) {
U8 *pOpt;
PDHCP_PROTO pDhcp = NULL;
U8 *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;
}
pDhcp = fill_pkg(pRsp, (U8 *)pReq);
// DHCP Options
pOpt = pDhcp->options;
// DHCP 消息类型
pOpt += dhcp_add_u8_option(pOpt, OPT_MESSAGETYPE, DHCP_MSG_NAK);
*pOpt = OPT_END;
return dhcp_prepare_tx(pRsp, (int)((pOpt - pDhcp->options) + 1));
}
static int dhcp_resp_ack(void *pReq, PPOOL_CTX pIpInfo) {
U8 *pOpt;
PDHCP_PROTO pDhcp = NULL;
U8 *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;
}
pDhcp = fill_pkg((U8 *)pRsp, (U8 *)pReq);
// 分配的 IP 地址
pDhcp->yiaddr = htonl(pIpInfo->minAddr);
// DHCP Options
pOpt = pDhcp->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(pRsp, (int)((pOpt - pDhcp->options) + 1));
}
static int dhcp_resp_offer(void *pReq, PPOOL_CTX pIpInfo, U32 ip) {
U8 *pOpt;
PDHCP_PROTO pDhcp = NULL;
U8 *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;
}
pDhcp = fill_pkg(pRsp, (U8 *)pReq);
// 分配的 IP 地址
pDhcp->yiaddr = htonl(ip);
// DHCP Options
pOpt = pDhcp->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(pRsp, (int)((pOpt - pDhcp->options) + 1));
}
PDHCP_PROTO get_dhcp_date(void *pBuf, U32 *pOptSize, U32 bufSize) {
U8 *p = (U8 *)pBuf;
U32 hdrCom = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr);
if (p[12] == 0x81 && p[13] == 0x00 && p[16] == 0x81 && p[17] == 0x00) {
*pOptSize = bufSize - hdrCom - sizeof(DHCP_PROTO) - sizeof(VLAN_HDR2);
return (PDHCP_PROTO)(p + hdrCom + 8);
} else if (p[12] == 0x81 && p[13] == 0x00) {
*pOptSize = bufSize - hdrCom - sizeof(DHCP_PROTO) - sizeof(VLAN_HDR);
return (PDHCP_PROTO)(p + hdrCom + 4);
} else {
*pOptSize = bufSize - hdrCom - sizeof(DHCP_PROTO);
return (PDHCP_PROTO)(p + hdrCom);
}
}
U32 dhcp_get_uid(void *pBuf) {
U8 *p = (U8 *)pBuf;
PVLAN_HDR pQinQ = (PVLAN_HDR)(p + sizeof(struct ethhdr));
if (p[12] == 0x81 && p[13] == 0x00 && p[16] == 0x81 && p[17] == 0x00) {
PVLAN_HDR2 pHdr = (PVLAN_HDR2)pQinQ;
return (pHdr->id1 & 0x0FFF) >> 12 | (pHdr->id2 & 0x0FFF) << 12;
} else if (p[12] == 0x81 && p[13] == 0x00) {
return (pQinQ->id & 0x0FFF);
} else {
return 0;
}
}
static void on_sock_recv(uv_work_t *req) {
U32 optSize = 0;
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_PROTO pkg = get_dhcp_date(pWork->pPkgBase, &optSize, pWork->nSize);
struct iphdr *pIpHdr = (struct iphdr *)((U8 *)pkg - sizeof(struct iphdr) - sizeof(struct udphdr));
//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->op != BOOTP_REQUEST) {
LOG_MOD(error, ZM_DHCP_NET, "Error message op code %d\n", pkg->op);
LOG_MSG_HEX(error, pkg, pWork->nSize);
return;
}
// 获取消息类型
ret = dhcp_get_option(OPT_MESSAGETYPE, pkg->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->flags != 0 ? TRUE : FALSE;
reqDhcp.xid = DHCP_XID(pkg->xid);
reqDhcp.uid = dhcp_get_uid(pWork->pPkgBase);
reqDhcp.serverAddr = g_nicInfo.ipAddr;
reqDhcp.cliAddr = ntohl(pkg->ciaddr);
memcpy(reqDhcp.cliMac, pkg->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->options, optSize, &opt);
if (ret == ERR_SUCCESS && opt.len == sizeof(U32)) {
reqDhcp.reqIpAddr = ntohl(*((U32 *)opt.pValue));
}
ret = dhcp_get_option(OPT_IPADDRLEASE, pkg->options, optSize, &opt);
if (ret == ERR_SUCCESS && opt.len == sizeof(U32)) {
reqDhcp.leaseTime = ntohl(*((U32 *)opt.pValue));
}
ret = dhcp_get_option(OPT_CLIENTID, pkg->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->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->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(pWork->pPkgBase, 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(pWork->pPkgBase);
}
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->options, optSize, &opt);
if (ret == ERR_SUCCESS && opt.len == sizeof(U32)) {
reqDhcp.reqServer = ntohl(*((U32 *)opt.pValue));
} else {
// Request 服务器IP可以取目的 IP 地址
if (pIpHdr->saddr != 0) {
reqDhcp.reqServer = ntohl(pIpHdr->daddr);
}
}
// 客户端请求的IP
ret = dhcp_get_option(OPT_REQUESTEDIPADDR, pkg->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->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(pWork->pPkgBase, &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(pWork->pPkgBase);
}
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->options, optSize, &opt);
if (ret == ERR_SUCCESS && opt.len == sizeof(U32)) {
reqDhcp.reqServer = ntohl(*((U32 *)opt.pValue));
} else {
// Request 服务器IP可以取目的 IP 地址
if (pIpHdr->saddr != 0) {
reqDhcp.reqServer = ntohl(pIpHdr->saddr);
}
}
reqDhcp.reqIpAddr = ntohl(pkg->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->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->options, pWork->nSize - sizeof(DHCP_PACKAGE));
//LOG_MSG_HEX(trace, pkg, pWork->nSize);
//LOG_MSG(info, "Recv, xid: 0x%08X\n", DHCP_XID(pkg->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->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->chaddr[0],
pkg->chaddr[1],
pkg->chaddr[2],
pkg->chaddr[3],
pkg->chaddr[4],
pkg->chaddr[5]);
#endif
}
static void after_msg_recv(uv_work_t *req, int UNUSED(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++) {
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) {
uid = dhcp_get_uid(pMsg->pPkgInfo[i].pPkgBase);
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;
}