跨平台底层网络库libdnet源码分析系列(十二)
源码分析mettle后门工具学习 所使用的依赖库

libdnet 以太网与防火墙模块源码深入分析
目录
- 以太网模块概述
- Linux平台以太网实现
- BSD/macOS平台以太网实现
- Solaris平台以太网实现
- 防火墙模块概述
- BSD IPFW防火墙实现
- Linux ipchains防火墙实现
- BSD IPF防火墙实现
- OpenBSD PF防火墙实现
- Windows PktFilter实现
- 模块对比与最佳实践
以太网模块概述
1. 模块架构
libdnet的以太网模块提供了跨平台的二层网络访问接口,主要功能包括:
|
|
|
|---|---|
eth_open() |
|
eth_get() |
|
eth_set() |
|
eth_send() |
|
eth_close() |
|
2. 以太网帧结构
// include/dnet/eth.h:29-33struct eth_hdr {eth_addr_t eth_dst;// 目标MAC地址(6字节)eth_addr_t eth_src;// 源MAC地址(6字节)uint16_t eth_type;// 以太类型(2字节)};
帧格式说明:
- 标准以太网帧:14字节头部 + 46-1500字节载荷 + 4字节CRC
- 最小帧:64字节(包括CRC)
- 最大帧:1518字节(包括CRC)
3. 常见以太类型
// include/dnet/eth.h:38-52#define ETH_TYPE_IP 0x0800// IPv4协议#define ETH_TYPE_ARP 0x0806// 地址解析协议#define ETH_TYPE_REVARP 0x8035// 反向ARP#define ETH_TYPE_8021Q 0x8100// IEEE 802.1Q VLAN#define ETH_TYPE_IPV6 0x86DD// IPv6协议#define ETH_TYPE_MPLS 0x8847// MPLS#define ETH_TYPE_PPPOE 0x8864// PPPoE#define ETH_TYPE_LOOPBACK 0x9000// 回环测试
4. MAC地址操作
// include/dnet/eth.h:74-79#define eth_pack_hdr(h, dst, src, type)do{ \struct eth_hdr *eth_pack_p =(struct eth_hdr *)(h); \memcpy(ð_pack_p->eth_dst,&(dst), ETH_ADDR_LEN); \memcpy(ð_pack_p->eth_src,&(src), ETH_ADDR_LEN); \eth_pack_p->eth_type = htons(type); \}while(0)
Linux平台以太网实现
1. 核心数据结构
// src/eth-linux.c:36-40struct eth_handle {int fd;// PF_PACKET套接字描述符struct ifreq ifr;// 接口请求结构struct sockaddr_ll sll;// 链路层套接字地址};
2. eth_open() 实现分析
// src/eth-linux.c:42-67eth_t*eth_open(constchar*device){eth_t*e;int n;if((e = calloc(1,sizeof(*e)))!= NULL){// 步骤1: 创建PF_PACKET原始套接字if((e->fd = socket(PF_PACKET, SOCK_RAW,htons(ETH_P_ALL)))<0)return(eth_close(e));// 步骤2: 允许广播#ifdef SO_BROADCASTn =1;if(setsockopt(e->fd, SOL_SOCKET, SO_BROADCAST,&n,sizeof(n))<0)return(eth_close(e));#endif// 步骤3: 保存设备名称strlcpy(e->ifr.ifr_name, device,sizeof(e->ifr.ifr_name));// 步骤4: 获取接口索引if(ioctl(e->fd, SIOCGIFINDEX,&e->ifr)<0)return(eth_close(e));// 步骤5: 配置链路层地址e->sll.sll_family = AF_PACKET;e->sll.sll_ifindex = e->ifr.ifr_ifindex;}return(e);}
实现细节:
- PF_PACKET套接字创建
c socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL))
PF_PACKET: Linux特有的协议族,用于链路层访问SOCK_RAW: 原始套接字模式ETH_P_ALL: 接收所有协议类型的以太网帧
- ioctl调用流程
mermaid graph TD A[SIOCGIFINDEX]-->B[获取接口索引]B-->C[绑定到特定接口]A-->D[SIOCGIFHWADDR]D-->E[获取MAC地址]E-->F[eth_get调用]A-->G[SIOCSIFHWADDR]G-->H[设置MAC地址]H-->I[eth_set调用]
3. eth_send() 实现
// src/eth-linux.c:69-78ssize_teth_send(eth_t*e,constvoid*buf,size_t len){struct eth_hdr *eth =(struct eth_hdr *)buf;// 从以太网头中提取协议类型e->sll.sll_protocol = eth->eth_type;// 通过sendto发送数据包,指定链路层地址return(sendto(e->fd, buf, len,0,(struct sockaddr *)&e->sll,sizeof(e->sll)));}
发送流程:
1.解析以太网头,提取eth_type2.设置sll_protocol字段3.调用sendto()发送4.内核根据sll_ifindex路由到指定接口5.硬件发送帧
4. eth_get() 实现
// src/eth-linux.c:91-104inteth_get(eth_t*e,eth_addr_t*ea){struct addr ha;// 通过ioctl获取硬件地址if(ioctl(e->fd, SIOCGIFHWADDR,&e->ifr)<0)return(-1);// 转换地址格式if(addr_ston(&e->ifr.ifr_hwaddr,&ha)<0)return(-1);// 复制MAC地址memcpy(ea,&ha.addr_eth,sizeof(*ea));return(0);}
5. eth_set() 实现
// src/eth-linux.c:106-118inteth_set(eth_t*e,consteth_addr_t*ea){struct addr ha;ha.addr_type = ADDR_TYPE_ETH;ha.addr_bits = ETH_ADDR_BITS;memcpy(&ha.addr_eth, ea, ETH_ADDR_LEN);// 转换地址格式addr_ntos(&ha,&e->ifr.ifr_hwaddr);// 通过ioctl设置硬件地址return(ioctl(e->fd, SIOCSIFHWADDR,&e->ifr));}
注意事项:
- 修改MAC地址需要
CAP_NET_ADMIN权限 - 某些网卡驱动可能不支持MAC地址修改
- 修改后需要重启接口才能生效
BSD/macOS平台以太网实现
1. 核心数据结构
// src/eth-bsd.c:34-37struct eth_handle {int fd;// BPF文件描述符char device[16];// 设备名称};
2. BPF (Berkeley Packet Filter) 简介
BPF是BSD系统的数据包捕获和过滤机制,特点:
- 基于设备文件
/dev/bpf* - 支持高效的BPF字节码过滤
- 提供原始数据包访问
3. eth_open() 实现分析
// src/eth-bsd.c:39-74eth_t*eth_open(constchar*device){struct ifreq ifr;char file[32];eth_t*e;int i;if((e = calloc(1,sizeof(*e)))!= NULL){// 步骤1: 尝试打开可用的BPF设备for(i =0; i <128; i++){snprintf(file,sizeof(file),"/dev/bpf%d", i);/* O_RDWR: Mac OS X 10.6有bug,使用O_RDWR而不是O_WRONLY */e->fd = open(file, O_RDWR);if(e->fd !=-1|| errno != EBUSY)break;}if(e->fd <0)return(eth_close(e));// 步骤2: 将BPF绑定到指定网络接口memset(&ifr,0,sizeof(ifr));strlcpy(ifr.ifr_name, device,sizeof(ifr.ifr_name));if(ioctl(e->fd, BIOCSETIF,(char*)&ifr)<0)return(eth_close(e));// 步骤3: 设置BIOCSETIF标志#ifdef BIOCSHDRCMPLTi =1;if(ioctl(e->fd, BIOCSHDRCMPLT,&i)<0)return(eth_close(e));#endifstrlcpy(e->device, device,sizeof(e->device));}return(e);}
BPF ioctl命令说明:
|
|
|
|
|---|---|---|
BIOCSETIF |
|
|
BIOCSHDRCMPLT |
|
|
BIOCGBLEN |
|
|
BIOCSBLEN |
|
|
4. eth_send() 实现
// src/eth-bsd.c:76-80ssize_teth_send(eth_t*e,constvoid*buf,size_t len){return(write(e->fd, buf, len));}
发送流程:
1.应用程序调用write()2.数据写入BPF设备3.内核通过绑定的接口发送4.数据包进入网络栈
5. eth_get() 实现(sysctl方式)
// src/eth-bsd.c:94-138inteth_get(eth_t*e,eth_addr_t*ea){struct if_msghdr *ifm;struct sockaddr_dl *sdl;struct addr ha;u_char *p,*buf;size_t len;int mib[]={ CTL_NET, AF_ROUTE,0, AF_LINK, NET_RT_IFLIST,0};// 步骤1: 获取所需缓冲区大小if(sysctl(mib,6, NULL,&len, NULL,0)<0)return(-1);// 步骤2: 分配缓冲区if((buf = malloc(len))== NULL)return(-1);// 步骤3: 获取接口列表if(sysctl(mib,6, buf,&len, NULL,0)<0){free(buf);return(-1);}// 步骤4: 遍历接口列表查找目标接口for(p = buf; p < buf + len; p += ifm->ifm_msglen){ifm =(struct if_msghdr *)p;sdl =(struct sockaddr_dl *)(ifm +1);if(ifm->ifm_type != RTM_IFINFO ||(ifm->ifm_addrs & RTA_IFP)==0)continue;if(sdl->sdl_family != AF_LINK || sdl->sdl_nlen ==0||memcmp(sdl->sdl_data, e->device, sdl->sdl_nlen)!=0)continue;if(addr_ston((struct sockaddr *)sdl,&ha)==0)break;}free(buf);if(p >= buf + len){errno = ESRCH;return(-1);}memcpy(ea,&ha.addr_eth,sizeof(*ea));return(0);}
sysctl MIB树结构:
CTL_NET (4)└── AF_ROUTE (17)└──0(协议族)└── AF_LINK (18)└── NET_RT_IFLIST (3)└──0(接口索引)
6. eth_set() 实现
// src/eth-bsd.c:148-164inteth_set(eth_t*e,consteth_addr_t*ea){struct ifreq ifr;struct addr ha;ha.addr_type = ADDR_TYPE_ETH;ha.addr_bits = ETH_ADDR_BITS;memcpy(&ha.addr_eth, ea, ETH_ADDR_LEN);memset(&ifr,0,sizeof(ifr));strlcpy(ifr.ifr_name, e->device,sizeof(ifr.ifr_name));addr_ntos(&ha,&ifr.ifr_addr);// 使用SIOCSIFLLADDR设置链路层地址return(ioctl(e->fd, SIOCSIFLLADDR,&ifr));}
Solaris平台以太网实现
1. 核心数据结构
// src/eth-dlpi.c:42-45struct eth_handle {int fd;// DLPI流文件描述符int sap_len;// SAP长度};
2. DLPI (Data Link Provider Interface) 简介
DLPI是Solaris和其他System V系统的数据链路接口规范:
- 基于STREAMS机制
- 支持多种介质类型(以太网、FDDI等)
- 使用putmsg/getmsg进行消息交换
3. DLPI消息处理
// src/eth-dlpi.c:47-72staticintdlpi_msg(int fd,union DL_primitives *dlp,int rlen,int flags,int ack,int alen,int size){struct strbuf ctl;ctl.maxlen =0;ctl.len = rlen;ctl.buf =(caddr_t)dlp;// 发送DLPI请求if(putmsg(fd,&ctl, NULL, flags)<0)return(-1);ctl.maxlen = size;ctl.len =0;flags =0;// 接收DLPI响应if(getmsg(fd,&ctl, NULL,&flags)<0)return(-1);// 验证响应类型和长度if(dlp->dl_primitive != ack || ctl.len < alen)return(-1);return(0);}
4. eth_open() 实现分析
// src/eth-dlpi.c:132-205eth_t*eth_open(constchar*device){union DL_primitives *dlp;uint32_t buf[8192];char*p, dev[16];eth_t*e;int ppa;if((e = calloc(1,sizeof(*e)))== NULL)return(NULL);#ifdef HAVE_SYS_DLPIHDR_H// OSF1方式if((e->fd = open("/dev/streams/dlb", O_RDWR))<0)return(eth_close(e));if((ppa = eth_match_ppa(e, device))<0){errno = ESRCH;return(eth_close(e));}#else// 标准DLPI方式e->fd =-1;snprintf(dev,sizeof(dev),"/dev/%s", device);if((p = dev_find_ppa(dev))== NULL){errno = EINVAL;return(eth_close(e));}ppa = atoi(p);*p ='\0';// 尝试多个可能的设备路径if((e->fd = open(dev, O_RDWR))<0){snprintf(dev,sizeof(dev),"/dev/%s", device);if((e->fd = open(dev, O_RDWR))<0){snprintf(dev,sizeof(dev),"/dev/net/%s", device);if((e->fd = open(dev, O_RDWR))<0)return(eth_close(e));}}#endif// 步骤1: 发送DL_INFO_REQ获取提供商信息dlp =(union DL_primitives *)buf;dlp->info_req.dl_primitive = DL_INFO_REQ;if(dlpi_msg(e->fd, dlp, DL_INFO_REQ_SIZE, RS_HIPRI,DL_INFO_ACK, DL_INFO_ACK_SIZE,sizeof(buf))<0)return(eth_close(e));e->sap_len = dlp->info_ack.dl_sap_length;// 步骤2: 如果是STYLE2,需要附加到PPAif(dlp->info_ack.dl_provider_style == DL_STYLE2){dlp->attach_req.dl_primitive = DL_ATTACH_REQ;dlp->attach_req.dl_ppa = ppa;if(dlpi_msg(e->fd, dlp, DL_ATTACH_REQ_SIZE,0,DL_OK_ACK, DL_OK_ACK_SIZE,sizeof(buf))<0)return(eth_close(e));}// 步骤3: 绑定到服务接入点memset(&dlp->bind_req,0, DL_BIND_REQ_SIZE);dlp->bind_req.dl_primitive = DL_BIND_REQ;#ifdef DL_HP_RAWDLSdlp->bind_req.dl_sap =24;// HP-UX特殊值dlp->bind_req.dl_service_mode = DL_HP_RAWDLS;#elsedlp->bind_req.dl_sap = DL_ETHER;dlp->bind_req.dl_service_mode = DL_CLDLS;#endifif(dlpi_msg(e->fd, dlp, DL_BIND_REQ_SIZE,0,DL_BIND_ACK, DL_BIND_ACK_SIZE,sizeof(buf))<0)return(eth_close(e));// 步骤4: 启用原始模式#ifdef DLIOCRAWif(strioctl(e->fd, DLIOCRAW,0, NULL)<0)return(eth_close(e));#endifreturn(e);}
DLPI握手流程:
1. DL_INFO_REQ -> DL_INFO_ACK (获取提供商信息)2. DL_ATTACH_REQ -> DL_OK_ACK (STYLE2:附加到PPA)3. DL_BIND_REQ -> DL_BIND_ACK (绑定到SAP)4. DLIOCRAW ioctl (启用原始模式)
5. eth_send() 实现
// src/eth-dlpi.c:207-257ssize_teth_send(eth_t*e,constvoid*buf,size_t len){#if defined(DLIOCRAW)// 简单模式:直接写入return(write(e->fd, buf, len));#else// 复杂模式:构建DL_UNITDATA_REQ消息union DL_primitives *dlp;struct strbuf ctl, data;struct eth_hdr *eth;uint32_t ctlbuf[8192];u_char sap[4]={0,0,0,0};int dlen;dlp =(union DL_primitives *)ctlbuf;#ifdef DL_HP_RAWDATA_REQdlp->dl_primitive = DL_HP_RAWDATA_REQ;dlen = DL_HP_RAWDATA_REQ_SIZE;#elsedlp->unitdata_req.dl_primitive = DL_UNITDATA_REQ;dlp->unitdata_req.dl_dest_addr_length = ETH_ADDR_LEN;dlp->unitdata_req.dl_dest_addr_offset = DL_UNITDATA_REQ_SIZE;dlp->unitdata_req.dl_priority.dl_min =dlp->unitdata_req.dl_priority.dl_max =0;dlen = DL_UNITDATA_REQ_SIZE;#endifeth =(struct eth_hdr *)buf;*(uint16_t*)sap = ntohs(eth->eth_type);// 构建控制消息ctl.maxlen =0;ctl.len = dlen + ETH_ADDR_LEN + abs(e->sap_len);ctl.buf =(char*)ctlbuf;if(e->sap_len >=0){memcpy(ctlbuf + dlen, sap, e->sap_len);memcpy(ctlbuf + dlen + e->sap_len,eth->eth_dst.data, ETH_ADDR_LEN);}else{memcpy(ctlbuf + dlen, eth->eth_dst.data, ETH_ADDR_LEN);memcpy(ctlbuf + dlen + ETH_ADDR_LEN, sap, abs(e->sap_len));}// 构建数据消息data.maxlen =0;data.len = len;data.buf =(char*)buf;// 使用putmsg发送if(putmsg(e->fd,&ctl,&data,0)<0)return(-1);return(len);#endif}
6. ethget() 和 ethset() 实现
// src/eth-dlpi.c:270-287inteth_get(eth_t*e,eth_addr_t*ea){union DL_primitives *dlp;u_char buf[2048];dlp =(union DL_primitives *)buf;dlp->physaddr_req.dl_primitive = DL_PHYS_ADDR_REQ;dlp->physaddr_req.dl_addr_type = DL_CURR_PHYS_ADDR;if(dlpi_msg(e->fd, dlp, DL_PHYS_ADDR_REQ_SIZE,0,DL_PHYS_ADDR_ACK, DL_PHYS_ADDR_ACK_SIZE,sizeof(buf))<0)return(-1);memcpy(ea, buf + dlp->physaddr_ack.dl_addr_offset,sizeof(*ea));return(0);}// src/eth-dlpi.c:289-304inteth_set(eth_t*e,consteth_addr_t*ea){union DL_primitives *dlp;u_char buf[2048];dlp =(union DL_primitives *)buf;dlp->set_physaddr_req.dl_primitive = DL_SET_PHYS_ADDR_REQ;dlp->set_physaddr_req.dl_addr_length = ETH_ADDR_LEN;dlp->set_physaddr_req.dl_addr_offset = DL_SET_PHYS_ADDR_REQ_SIZE;memcpy(buf + DL_SET_PHYS_ADDR_REQ_SIZE, ea,sizeof(*ea));return(dlpi_msg(e->fd, dlp, DL_SET_PHYS_ADDR_REQ_SIZE + ETH_ADDR_LEN,0, DL_OK_ACK, DL_OK_ACK_SIZE,sizeof(buf)));}
防火墙模块概述
1. 模块架构
防火墙模块提供跨平台的防火墙规则管理接口:
// include/dnet/fw.hstruct fw_rule {char fw_device[INTF_NAME_LEN];// 网络接口uint8_t fw_op;// 操作:允许/拒绝uint8_t fw_dir;// 方向:入站/出站uint8_t fw_proto;// 协议:TCP/UDP/ICMPstruct addr fw_src;// 源地址struct addr fw_dst;// 目标地址uint16_t fw_sport[2];// 源端口范围uint16_t fw_dport[2];// 目标端口范围};
2. 核心函数
|
|
|
|---|---|
fw_open() |
|
fw_add() |
|
fw_delete() |
|
fw_loop() |
|
fw_close() |
|
3. 平台支持矩阵
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BSD IPFW防火墙实现
1. 核心数据结构
// src/fw-ipfw.c:28-30struct fw_handle {int fd;// 原始套接字描述符};
2. 规则转换:fwrule -> ipfw
// src/fw-ipfw.c:42-109staticvoidfr_to_ipfw(conststruct fw_rule *fr,struct ip_fw *ipfw){int i;memset(ipfw,0,sizeof(*ipfw));// 方向和接口if(fr->fw_dir == FW_DIR_IN){if(*fr->fw_device !='\0'){fr_to_ipfw_device(fr->fw_device,ipfw->fw_in_if.fu_via_if.name,&ipfw->fw_in_if.fu_via_if.unit);ipfw->fw_flg |= IP_FW_F_IIFNAME;}ipfw->fw_flg |= IP_FW_F_IN;}else{if(*fr->fw_device !='\0'){fr_to_ipfw_device(fr->fw_device,ipfw->fw_out_if.fu_via_if.name,&ipfw->fw_out_if.fu_via_if.unit);ipfw->fw_flg |= IP_FW_F_OIFNAME;}ipfw->fw_flg |= IP_FW_F_OUT;}// 操作if(fr->fw_op == FW_OP_ALLOW)ipfw->fw_flg |= IP_FW_F_ACCEPT;elseipfw->fw_flg |= IP_FW_F_DENY;// 协议和地址ipfw->fw_prot = fr->fw_proto;ipfw->fw_src.s_addr = fr->fw_src.addr_ip;ipfw->fw_dst.s_addr = fr->fw_dst.addr_ip;addr_btom(fr->fw_src.addr_bits,&ipfw->fw_smsk.s_addr, IP_ADDR_LEN);addr_btom(fr->fw_dst.addr_bits,&ipfw->fw_dmsk.s_addr, IP_ADDR_LEN);// 端口处理switch(fr->fw_proto){case IP_PROTO_TCP:case IP_PROTO_UDP:i =0;if(fr->fw_sport[0]!= fr->fw_sport[1]){ipfw->fw_flg |= IP_FW_F_SRNG;ipfw->fw_uar.fw_pts[i++]= fr->fw_sport[0];ipfw->fw_uar.fw_pts[i++]= fr->fw_sport[1];IP_FW_SETNSRCP(ipfw,2);}elseif(fr->fw_sport[0]>0){ipfw->fw_uar.fw_pts[i++]= fr->fw_sport[0];IP_FW_SETNSRCP(ipfw,1);}if(fr->fw_dport[0]!= fr->fw_dport[1]){ipfw->fw_flg |= IP_FW_F_DRNG;ipfw->fw_uar.fw_pts[i++]= fr->fw_dport[0];ipfw->fw_uar.fw_pts[i++]= fr->fw_dport[1];IP_FW_SETNDSTP(ipfw,2);}elseif(fr->fw_dport[0]>0){ipfw->fw_uar.fw_pts[i++]= fr->fw_dport[0];IP_FW_SETNDSTP(ipfw,1);}break;case IP_PROTO_ICMP:if(fr->fw_sport[1]){ipfw->fw_uar.fw_icmptypes[fr->fw_sport[0]/32]|=1<<(fr->fw_sport[0]%32);ipfw->fw_flg |= IP_FW_F_ICMPBIT;}break;}}
3. fw_open() 实现
// src/fw-ipfw.c:184-194fw_t*fw_open(void){fw_t*fw;if((fw = calloc(1,sizeof(*fw)))!= NULL){// 创建原始套接字用于防火墙操作if((fw->fd = socket(AF_INET, SOCK_RAW, IPPROTO_IP))<0)return(fw_close(fw));}return(fw);}
4. fw_add() 实现
// src/fw-ipfw.c:196-207intfw_add(fw_t*fw,conststruct fw_rule *rule){struct ip_fw ipfw;assert(fw != NULL && rule != NULL);// 转换规则格式fr_to_ipfw(rule,&ipfw);// 使用setsockopt添加规则return(setsockopt(fw->fd, IPPROTO_IP, IP_FW_ADD,&ipfw,sizeof(ipfw)));}
5. fw_delete() 实现
// src/fw-ipfw.c:222-273intfw_delete(fw_t*fw,conststruct fw_rule *rule){struct ip_fw *ipfw;struct fw_rule fr;int nbytes, nalloc, ret;u_char *buf,*new;assert(rule != NULL);// 步骤1: 获取所有规则nbytes = nalloc =sizeof(*ipfw);if((buf = malloc(nbytes))== NULL)return(-1);while(nbytes >= nalloc){nalloc = nalloc *2+200;nbytes = nalloc;if((new= realloc(buf, nbytes))== NULL){if(buf)free(buf);return(-1);}buf =new;if(getsockopt(fw->fd, IPPROTO_IP, IP_FW_GET,buf,&nbytes)<0){free(buf);return(-1);}}// 步骤2: 查找匹配的规则ret =-1;// 65535是ipfw的默认规则for(ipfw =(struct ip_fw *)buf; ipfw->fw_number <65535; ipfw++){ipfw_to_fr(ipfw,&fr);if(fw_cmp(&fr, rule)==0){if(setsockopt(fw->fd, IPPROTO_IP, IP_FW_DEL,ipfw,sizeof(*ipfw))<0)ret =-2;elseret =0;break;}}free(buf);if(ret <0){if(ret ==-1)errno = ESRCH;return(-1);}return(0);}
6. fw_loop() 实现
// src/fw-ipfw.c:275-313intfw_loop(fw_t*fw, fw_handler callback,void*arg){struct ip_fw *ipfw;struct fw_rule fr;int i, cnt, nbytes, nalloc, ret;u_char *buf,*new;// 获取规则列表nbytes = nalloc =sizeof(*ipfw);if((buf = malloc(nbytes))== NULL)return(-1);while(nbytes >= nalloc){nalloc = nalloc *2+200;nbytes = nalloc;if((new= realloc(buf, nbytes))== NULL){if(buf)free(buf);return(-1);}buf =new;if(getsockopt(fw->fd, IPPROTO_IP, IP_FW_GET,buf,&nbytes)<0){free(buf);return(-1);}}cnt = nbytes /sizeof(*ipfw);ipfw =(struct ip_fw *)buf;ret =0;// 遍历规则for(i =0; i < cnt; i++){ipfw_to_fr(&ipfw[i],&fr);if((ret = callback(&fr, arg))!=0)break;}free(buf);return(ret);}
Linux ipchains防火墙实现
1. 核心数据结构
// src/fw-ipchains.c:40-42struct fw_handle {int fd;// 原始套接字描述符};#define PROC_IPCHAINS_FILE "/proc/net/ip_fwchains"
2. 规则转换:fwrule -> ipfwchange
// src/fw-ipchains.c:44-78staticvoidfr_to_fwc(conststruct fw_rule *fr,struct ip_fwchange *fwc){memset(fwc,0,sizeof(*fwc));// 接口名称strlcpy(fwc->fwc_rule.ipfw.fw_vianame, fr->fw_device, IFNAMSIZ);// 操作和方向标签if(fr->fw_op == FW_OP_ALLOW)strlcpy(fwc->fwc_rule.label, IP_FW_LABEL_ACCEPT,sizeof(fwc->fwc_rule.label));elsestrlcpy(fwc->fwc_rule.label, IP_FW_LABEL_BLOCK,sizeof(fwc->fwc_rule.label));if(fr->fw_dir == FW_DIR_IN)strlcpy(fwc->fwc_label, IP_FW_LABEL_INPUT,sizeof(fwc->fwc_label));elsestrlcpy(fwc->fwc_label, IP_FW_LABEL_OUTPUT,sizeof(fwc->fwc_label));// 协议和地址fwc->fwc_rule.ipfw.fw_proto = fr->fw_proto;fwc->fwc_rule.ipfw.fw_src.s_addr = fr->fw_src.addr_ip;fwc->fwc_rule.ipfw.fw_dst.s_addr = fr->fw_dst.addr_ip;addr_btom(fr->fw_src.addr_bits,&fwc->fwc_rule.ipfw.fw_smsk.s_addr,IP_ADDR_LEN);addr_btom(fr->fw_dst.addr_bits,&fwc->fwc_rule.ipfw.fw_dmsk.s_addr,IP_ADDR_LEN);// 端口范围fwc->fwc_rule.ipfw.fw_spts[0]= fr->fw_sport[0];fwc->fwc_rule.ipfw.fw_spts[1]= fr->fw_sport[1];fwc->fwc_rule.ipfw.fw_dpts[0]= fr->fw_dport[0];fwc->fwc_rule.ipfw.fw_dpts[1]= fr->fw_dport[1];}
3. fw_open() 实现
// src/fw-ipchains.c:114-124fw_t*fw_open(void){fw_t*fw;if((fw = calloc(1,sizeof(*fw)))!= NULL){// 创建原始套接字if((fw->fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))<0)return(fw_close(fw));}return(fw);}
4. fwadd() 和 fwdelete() 实现
// src/fw-ipchains.c:126-146intfw_add(fw_t*fw,conststruct fw_rule *rule){struct ip_fwchange fwc;fr_to_fwc(rule,&fwc);// 使用IP_FW_APPEND追加规则return(setsockopt(fw->fd, IPPROTO_IP, IP_FW_APPEND,&fwc,sizeof(fwc)));}intfw_delete(fw_t*fw,conststruct fw_rule *rule){struct ip_fwchange fwc;fr_to_fwc(rule,&fwc);// 使用IP_FW_DELETE删除规则return(setsockopt(fw->fd, IPPROTO_IP, IP_FW_DELETE,&fwc,sizeof(fwc)));}
5. fw_loop() 实现(通过/proc解析)
// src/fw-ipchains.c:148-216intfw_loop(fw_t*fw, fw_handler callback,void*arg){FILE*fp;struct ip_fwchange fwc;struct fw_rule fr;char buf[BUFSIZ];u_int phi, plo, bhi, blo, tand, txor;int ret;// 打开/proc/net/ip_fwchains文件if((fp = fopen(PROC_IPCHAINS_FILE,"r"))== NULL)return(-1);// 解析每一行while(fgets(buf,sizeof(buf), fp)!= NULL){if(sscanf(buf,"%8s %X/%X->%X/%X %s %hX %hX %hu %u %u %u %u ""%hu-%hu %hu-%hu A%X X%X %hX %u %hu %s\n",fwc.fwc_label,&fwc.fwc_rule.ipfw.fw_src.s_addr,&fwc.fwc_rule.ipfw.fw_smsk.s_addr,&fwc.fwc_rule.ipfw.fw_dst.s_addr,&fwc.fwc_rule.ipfw.fw_dmsk.s_addr,fwc.fwc_rule.ipfw.fw_vianame,&fwc.fwc_rule.ipfw.fw_flg,&fwc.fwc_rule.ipfw.fw_invflg,&fwc.fwc_rule.ipfw.fw_proto,&phi,&plo,&bhi,&blo,&fwc.fwc_rule.ipfw.fw_spts[0],&fwc.fwc_rule.ipfw.fw_spts[1],&fwc.fwc_rule.ipfw.fw_dpts[0],&fwc.fwc_rule.ipfw.fw_dpts[1],&tand,&txor,&fwc.fwc_rule.ipfw.fw_redirpt,&fwc.fwc_rule.ipfw.fw_mark,&fwc.fwc_rule.ipfw.fw_outputsize,fwc.fwc_rule.label)!=23)break;// 过滤不支持的规则类型if(strcmp(fwc.fwc_rule.label, IP_FW_LABEL_ACCEPT)!=0&&strcmp(fwc.fwc_rule.label, IP_FW_LABEL_BLOCK)!=0&&strcmp(fwc.fwc_rule.label, IP_FW_LABEL_REJECT)!=0)continue;if(strcmp(fwc.fwc_label, IP_FW_LABEL_INPUT)!=0&&strcmp(fwc.fwc_label, IP_FW_LABEL_OUTPUT)!=0)continue;// 转换地址格式(从网络字节序到主机字节序)if(strcmp(fwc.fwc_rule.label,"-")==0)(fwc.fwc_rule.label)[0]='\0';if(strcmp(fwc.fwc_rule.ipfw.fw_vianame,"-")==0)(fwc.fwc_rule.ipfw.fw_vianame)[0]='\0';fwc.fwc_rule.ipfw.fw_src.s_addr =htonl(fwc.fwc_rule.ipfw.fw_src.s_addr);fwc.fwc_rule.ipfw.fw_dst.s_addr =htonl(fwc.fwc_rule.ipfw.fw_dst.s_addr);fwc.fwc_rule.ipfw.fw_smsk.s_addr =htonl(fwc.fwc_rule.ipfw.fw_smsk.s_addr);fwc.fwc_rule.ipfw.fw_dmsk.s_addr =htonl(fwc.fwc_rule.ipfw.fw_dmsk.s_addr);fwc_to_fr(&fwc,&fr);if((ret = callback(&fr, arg))!=0){fclose(fp);return(ret);}}fclose(fp);return(0);}
BSD IPF防火墙实现
1. 核心数据结构
// src/fw-ipf.c:46-49struct fw_handle {int fd;// IPF设备文件描述符int kfd;// 内核内存文件描述符};#define KMEM_NAME "/dev/kmem"
2. 规则转换:fw_rule -> frentry
// src/fw-ipf.c:51-100staticvoidrule_to_ipf(conststruct fw_rule *rule,struct frentry *fr){memset(fr,0,sizeof(*fr));// 接口名称if(*rule->fw_device !='\0'){strlcpy(fr->fr_ifname, rule->fw_device, IFNAMSIZ);strlcpy(fr->fr_oifname, rule->fw_device, IFNAMSIZ);}// 操作和方向if(rule->fw_op == FW_OP_ALLOW)fr->fr_flags |= FR_PASS;elsefr->fr_flags |= FR_BLOCK;if(rule->fw_dir == FW_DIR_IN)fr->fr_flags |= FR_INQUE;elsefr->fr_flags |= FR_OUTQUE;// 协议和地址fr->fr_ip.fi_p = rule->fw_proto;fr->fr_ip.fi_saddr = rule->fw_src.addr_ip;fr->fr_ip.fi_daddr = rule->fw_dst.addr_ip;addr_btom(rule->fw_src.addr_bits,&fr->fr_mip.fi_saddr, IP_ADDR_LEN);addr_btom(rule->fw_dst.addr_bits,&fr->fr_mip.fi_daddr, IP_ADDR_LEN);// 端口和ICMP类型switch(rule->fw_proto){case IPPROTO_ICMP:fr->fr_icmpm = rule->fw_sport[1]<<8|(rule->fw_dport[1]&0xff);fr->fr_icmp = rule->fw_sport[0]<<8|(rule->fw_dport[0]&0xff);break;case IPPROTO_TCP:case IPPROTO_UDP:fr->fr_sport = rule->fw_sport[0];if(rule->fw_sport[0]!= rule->fw_sport[1]){fr->fr_scmp = FR_INRANGE;fr->fr_stop = rule->fw_sport[1];}elsefr->fr_scmp = FR_EQUAL;fr->fr_dport = rule->fw_dport[0];if(rule->fw_dport[0]!= rule->fw_dport[1]){fr->fr_dcmp = FR_INRANGE;fr->fr_dtop = rule->fw_dport[1];}elsefr->fr_dcmp = FR_EQUAL;break;}}
3. fw_open() 实现
// src/fw-ipf.c:178-191fw_t*fw_open(void){fw_t*fw;if((fw = calloc(1,sizeof(*fw)))!= NULL){fw->fd = fw->kfd =-1;// 打开IPF设备文件if((fw->fd = open(IPL_NAME, O_RDWR,0))<0)return(fw_close(fw));// 打开内核内存if((fw->kfd = open(KMEM_NAME, O_RDONLY))<0)return(fw_close(fw));}return(fw);}
4. fwadd() 和 fwdelete() 实现
// src/fw-ipf.c:193-215intfw_add(fw_t*fw,conststruct fw_rule *rule){struct frentry fr;assert(fw != NULL && rule != NULL);rule_to_ipf(rule,&fr);// 使用ioctl添加规则return(ioctl(fw->fd, SIOCADDFR,&fr));}intfw_delete(fw_t*fw,conststruct fw_rule *rule){struct frentry fr;assert(fw != NULL && rule != NULL);rule_to_ipf(rule,&fr);// 使用ioctl删除规则return(ioctl(fw->fd, SIOCDELFR,&fr));}
5. fw_loop() 实现(通过内核内存)
// src/fw-ipf.c:217-268intfw_loop(fw_t*fw, fw_handler callback,void*arg){struct friostat fio;struct friostat *fiop =&fio;struct frentry *frp, fr;struct fw_rule rule;int ret;memset(&fio,0,sizeof(fio));// 获取防火墙统计信息#ifdef __OpenBSD__if(ioctl(fw->fd, SIOCGETFS, fiop)<0)#elseif(ioctl(fw->fd, SIOCGETFS,&fiop)<0)/* XXX - OpenBSD差异 */#endifreturn(-1);// 遍历出站规则for(frp = fio.f_fout[(int)fio.f_active]; frp != NULL;frp = fr.fr_next){if(fw_kcopy(fw,(u_char *)&fr,(u_long)frp,sizeof(fr))<0)return(-1);ipf_to_rule(&fr,&rule);if((ret = callback(&rule, arg))!=0)return(ret);}// 遍历入站规则for(frp = fio.f_fin[(int)fio.f_active]; frp != NULL;frp = fr.fr_next){if(fw_kcopy(fw,(u_char *)&fr,(u_long)frp,sizeof(fr))<0)return(-1);ipf_to_rule(&fr,&rule);if((ret = callback(&rule, arg))!=0)return(ret);}return(0);}
内核内存复制:
// src/fw-ipf.c:217-232staticintfw_kcopy(fw_t*fw, u_char *buf,off_t pos,size_t n){int i;if(lseek(fw->kfd, pos,0)<0)return(-1);while((i = read(fw->kfd, buf, n))< n){if(i <=0)return(-1);buf += i;n -= i;}return(0);}
OpenBSD PF防火墙实现
1. 核心数据结构
// src/fw-pf.c:67-69struct fw_handle {int fd;// PF设备文件描述符};
2. PF版本兼容性处理
// src/fw-pf.c:29-65/** XXX - 处理不断变化的PF API*/#if defined(DIOCRCLRTABLES)/* OpenBSD 3.3+ - 3.6 */#define HAVE_PF_CHANGE_GET_TICKET 1#define PFRA_ADDR(ra)(ra)->addr.v.a.addr.v4.s_addr#define PFRA_MASK(ra)(ra)->addr.v.a.mask.v4.s_addr#define pfioc_changerule pfioc_rule#define oldrule rule#define newrule rule#elif defined(DIOCBEGINADDRS)/* OpenBSD 3.2 */#define PFRA_ADDR(ra)(ra)->addr.addr.v4.s_addr#define PFRA_MASK(ra)(ra)->addr.mask.v4.s_addr#elif defined(PFRULE_FRAGMENT)/* OpenBSD 3.1 */#define PFRA_ADDR(ra)(ra)->addr.addr.v4.s_addr#define PFRA_MASK(ra)(ra)->mask.v4.s_addr#elif defined (PF_AEQ)/* OpenBSD 3.0 */#define PFRA_ADDR(ra)(ra)->addr#define PFRA_ADDR(ra)(ra)->mask#endif
3. 规则转换:fwrule -> pfrule
// src/fw-pf.c:71-115staticvoidfr_to_pr(conststruct fw_rule *fr,struct pf_rule *pr){memset(pr,0,sizeof(*pr));// 接口名称strlcpy(pr->ifname, fr->fw_device,sizeof(pr->ifname));// 动作和方向pr->action =(fr->fw_op == FW_OP_ALLOW)? PF_PASS : PF_DROP;pr->direction =(fr->fw_dir == FW_DIR_IN)? PF_IN : PF_OUT;pr->proto = fr->fw_proto;// 地址pr->af = AF_INET;PFRA_ADDR(&pr->src)= fr->fw_src.addr_ip;addr_btom(fr->fw_src.addr_bits,&(PFRA_MASK(&pr->src)), IP_ADDR_LEN);PFRA_ADDR(&pr->dst)= fr->fw_dst.addr_ip;addr_btom(fr->fw_dst.addr_bits,&(PFRA_MASK(&pr->dst)), IP_ADDR_LEN);// 端口和ICMP类型switch(fr->fw_proto){case IP_PROTO_ICMP:if(fr->fw_sport[1])pr->type =(u_char)(fr->fw_sport[0]&fr->fw_sport[1])+1;if(fr->fw_dport[1])pr->code =(u_char)(fr->fw_dport[0]&fr->fw_dport[1])+1;break;case IP_PROTO_TCP:case IP_PROTO_UDP:pr->src.port[0]= htons(fr->fw_sport[0]);pr->src.port[1]= htons(fr->fw_sport[1]);if(pr->src.port[0]== pr->src.port[1]){pr->src.port_op = PF_OP_EQ;}elsepr->src.port_op = PF_OP_IRG;pr->dst.port[0]= htons(fr->fw_dport[0]);pr->dst.port[1]= htons(fr->fw_dport[1]);if(pr->dst.port[0]== pr->dst.port[1]){pr->dst.port_op = PF_OP_EQ;}elsepr->dst.port_op = PF_OP_IRG;break;}}
4. fw_open() 实现
// src/fw-pf.c:186-196fw_t*fw_open(void){fw_t*fw;if((fw = calloc(1,sizeof(*fw)))!= NULL){// 打开PF设备if((fw->fd = open("/dev/pf", O_RDWR))<0)return(fw_close(fw));}return(fw);}
5. fw_add() 实现
// src/fw-pf.c:198-235intfw_add(fw_t*fw,conststruct fw_rule *rule){struct pfioc_changerule pcr;assert(fw != NULL && rule != NULL);memset(&pcr,0,sizeof(pcr));#ifdef HAVE_PF_CHANGE_GET_TICKET// 检查规则是否已存在{struct fw_rule fr;if(ioctl(fw->fd, DIOCGETRULES,&pcr)<0)return(-1);while((int)--pcr.nr >=0){if(ioctl(fw->fd, DIOCGETRULE,&pcr)==0&&pr_to_fr(&pcr.rule,&fr)==0){if(_fw_cmp(rule,&fr)==0){errno = EEXIST;return(-1);}}}}#endif#ifdef DIOCBEGINADDRS// 获取地址池票据{struct pfioc_pooladdr ppa;if(ioctl(fw->fd, DIOCBEGINADDRS,&ppa)<0)return(-1);pcr.pool_ticket = ppa.ticket;}#endif// 添加规则到规则集尾部pcr.action = PF_CHANGE_ADD_TAIL;fr_to_pr(rule,&pcr.newrule);return(ioctl(fw->fd, DIOCCHANGERULE,&pcr));}
6. fw_delete() 实现
// src/fw-pf.c:237-279intfw_delete(fw_t*fw,conststruct fw_rule *rule){struct pfioc_changerule pcr;assert(fw != NULL && rule != NULL);memset(&pcr,0,sizeof(pcr));#ifdef HAVE_PF_CHANGE_GET_TICKET// 查找要删除的规则{struct fw_rule fr;int found =0;if(ioctl(fw->fd, DIOCGETRULES,&pcr)<0)return(-1);while((int)--pcr.nr >=0){if(ioctl(fw->fd, DIOCGETRULE,&pcr)==0&&pr_to_fr(&pcr.rule,&fr)==0){if(_fw_cmp(rule,&fr)==0){found =1;break;}}}if(!found){errno = ENOENT;return(-1);}}#endif#ifdef DIOCBEGINADDRS{struct pfioc_pooladdr ppa;if(ioctl(fw->fd, DIOCBEGINADDRS,&ppa)<0)return(-1);pcr.pool_ticket = ppa.ticket;}#endif// 删除规则pcr.action = PF_CHANGE_REMOVE;fr_to_pr(rule,&pcr.oldrule);return(ioctl(fw->fd, DIOCCHANGERULE,&pcr));}
7. fw_loop() 实现
// src/fw-pf.c:281-310intfw_loop(fw_t*fw, fw_handler callback,void*arg){struct pfioc_rule pr;struct fw_rule fr;uint32_t n, max;int ret =0;memset(&pr,0,sizeof(pr));// 获取规则数量if(ioctl(fw->fd, DIOCGETRULES,&pr)<0)return(-1);// 遍历规则for(n =0, max = pr.nr; n < max; n++){pr.nr = n;if((ret = ioctl(fw->fd, DIOCGETRULE,&pr))<0)break;#ifdef PF_TABLE_NAME_SIZE// 跳过表地址规则if(pr.rule.src.addr.type == PF_ADDR_TABLE ||pr.rule.dst.addr.type == PF_ADDR_TABLE)continue;#endifif(pr_to_fr(&pr.rule,&fr)<0)continue;if((ret = callback(&fr, arg))!=0)break;}return(ret);}
Windows PktFilter实现
1. 核心数据结构
2. PktFilter简介
PktFilter是Windows上的防火墙实现,特点:
- 基于命名管道通信
- 通过PktFlt服务实现
- 提供简单的规则语法
3. 规则格式化
// src/fw-pktfilter.c:214-275staticintformat_rule(conststruct fw_rule *rule,char*buf,int len){char tmp[128];// 操作和方向strlcpy(buf,(rule->fw_op == FW_OP_ALLOW)?"pass ":"block ", len);strlcat(buf,(rule->fw_dir == FW_DIR_IN)?"in ":"out ", len);// 接口snprintf(tmp,sizeof(tmp),"on %s ", rule->fw_device);strlcat(buf, tmp, len);// 协议if(rule->fw_proto !=0){snprintf(tmp,sizeof(tmp),"proto %d ", rule->fw_proto);strlcat(buf, tmp, len);}// 源地址if(rule->fw_src.addr_type != ADDR_TYPE_NONE){snprintf(tmp,sizeof(tmp),"from %s ",addr_ntoa(&rule->fw_src));strlcat(buf, tmp, len);}elsestrlcat(buf,"from any ", len);// 源端口if(rule->fw_proto == IP_PROTO_TCP || rule->fw_proto == IP_PROTO_UDP){if(rule->fw_sport[0]== rule->fw_sport[1])snprintf(tmp,sizeof(tmp),"port = %d ",rule->fw_sport[0]);elsesnprintf(tmp,sizeof(tmp),"port %d >< %d ",rule->fw_sport[0]-1, rule->fw_sport[1]+1);strlcat(buf, tmp, len);}// 目标地址if(rule->fw_dst.addr_type != ADDR_TYPE_NONE){snprintf(tmp,sizeof(tmp),"to %s ",addr_ntoa(&rule->fw_dst));strlcat(buf, tmp, len);}elsestrlcat(buf,"to any ", len);// 目标端口if(rule->fw_proto == IP_PROTO_TCP || rule->fw_proto == IP_PROTO_UDP){if(rule->fw_dport[0]== rule->fw_dport[1])snprintf(tmp,sizeof(tmp),"port = %d",rule->fw_dport[0]);elsesnprintf(tmp,sizeof(tmp),"port %d >< %d",rule->fw_dport[0]-1, rule->fw_dport[1]+1);strlcat(buf, tmp, len);}elseif(rule->fw_proto == IP_PROTO_ICMP){if(rule->fw_sport[1]){snprintf(tmp,sizeof(tmp),"icmp-type %d",rule->fw_sport[0]);strlcat(buf, tmp, len);if(rule->fw_dport[1]){snprintf(tmp,sizeof(tmp)," code %d",rule->fw_dport[0]);strlcat(buf, tmp, len);}}}return(strlen(buf));}
PktFilter规则语法:
passin on eth0 proto tcp from192.168.1.0/24 to any port 80block out on eth0 proto udp from any to any port 53passin on eth0 proto icmp from any to any icmp-type echo
4. 管道通信
// src/fw-pktfilter.c:277-313staticchar*call_pipe(constchar*msg,int len){HANDLE *pipe;DWORD i;char*reply, status;// 等待命名管道if(!WaitNamedPipe(PKTFILTER_PIPE, NMPWAIT_USE_DEFAULT_WAIT)||(pipe =CreateFile(PKTFILTER_PIPE, GENERIC_READ | GENERIC_WRITE,0, NULL, OPEN_EXISTING,0, NULL))== INVALID_HANDLE_VALUE){return(NULL);}reply = NULL;// 发送命令if(WriteFile(pipe, msg, len,&i, NULL)){// 接收响应if(ReadFile(pipe,&status,sizeof(status),&i, NULL)){if(status == FILTER_FAILURE){ReadFile(pipe,&status,sizeof(status),&i, NULL);}elseif(status == FILTER_MESSAGE){// 获取消息长度if(ReadFile(pipe,&len,4,&i, NULL)){// 获取消息内容reply = calloc(1, len +1);if(!ReadFile(pipe, reply, len,&i, NULL)){free(reply);reply = NULL;}}}elseif(status == FILTER_SUCCESS)reply = strdup("");/* XXX */}}CloseHandle(pipe);return(reply);}
5. fw_open() 实现
// src/fw-pktfilter.c:315-352fw_t*fw_open(void){fw_t*f;IP_ADAPTER_INFO *ifinfo;ULONG size;if((f = calloc(1,sizeof(*f)))== NULL)return(NULL);// 获取适配器信息size =sizeof(*f->ifinfo);f->ifinfo = malloc(size);if(GetAdaptersInfo(f->ifinfo,&size)!= ERROR_SUCCESS){free(f->ifinfo);f->ifinfo = malloc(size);GetAdaptersInfo(f->ifinfo,&size);}// 规范化接口名称for(ifinfo = f->ifinfo; ifinfo != NULL; ifinfo = ifinfo->Next){char*fmt;if(ifinfo->Type== MIB_IF_TYPE_ETHERNET)fmt ="eth";elseif(ifinfo->Type== MIB_IF_TYPE_PPP)fmt ="ppp";elseif(ifinfo->Type== MIB_IF_TYPE_SLIP)fmt ="sl";elseif(ifinfo->Type== MIB_IF_TYPE_LOOPBACK)fmt ="lo";elseif(ifinfo->Type== MIB_IF_TYPE_TOKENRING)fmt ="tr";elseif(ifinfo->Type== MIB_IF_TYPE_FDDI)fmt ="fd";elsefmt ="if";sprintf(ifinfo->AdapterName,"%s%lu", fmt, ifinfo->ComboIndex);}return(f);}
6. fw_add() 实现
// src/fw-pktfilter.c:354-366intfw_add(fw_t*f,conststruct fw_rule *rule){char*p, buf[MAX_RULE_LENGTH];int len;// 格式化规则len = format_rule(rule, buf,sizeof(buf));// 通过管道发送规则if((p = call_pipe(buf, len))== NULL)return(-1);free(p);return(0);}
7. fw_delete() 实现
// src/fw-pktfilter.c:368-405intfw_delete(fw_t*f,conststruct fw_rule *rule){struct fw_rule tmp;char*p,*line,*msg, cmd[128], buf[MAX_RULE_LENGTH];int n, ruleno, len;format_rule(rule, buf,sizeof(buf));// 列出规则len = snprintf(cmd,sizeof(cmd),"List on %s", rule->fw_device);if((msg = call_pipe(cmd, len))== NULL)return(-1);// 查找匹配的规则for(ruleno =0, p = msg;(line = strsep(&p,"\r\n"))!= NULL;){if(strncmp(line,"rule ",5)==0){line +=5;n = atoi(strsep(&line,":"));if(parse_rule(line +1,&tmp)==0&&memcmp(&tmp, rule,sizeof(tmp))==0){ruleno = n;break;}}}free(msg);if(ruleno ==0){errno = ENXIO;SetLastError(ERROR_NO_DATA);return(-1);}// 删除规则len = snprintf(cmd,sizeof(cmd),"delete %d on %s",ruleno, rule->fw_device);if((p = call_pipe(cmd, len))== NULL)return(-1);free(p);return(0);}
模块对比与最佳实践
1. 以太网模块对比
|
|
|
|
|
|
|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2. 防火墙模块对比
|
|
|
|
|
|
|
|---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3. 使用最佳实践
以太网模块
// 1. 正确的错误处理eth_t*eth = eth_open("eth0");if(eth == NULL){if(errno == EPERM){fprintf(stderr,"需要root权限\n");}else{perror("eth_open失败");}exit(EXIT_FAILURE);}// 2. 构造以太网帧struct eth_hdr hdr;eth_pack_hdr(&hdr, dst_mac, src_mac, ETH_TYPE_IP);// 3. 发送数据包ssize_t sent = eth_send(eth,&hdr,sizeof(hdr));if(sent <0){perror("eth_send失败");}// 4. 清理资源eth_close(eth);
防火墙模块
// 1. 构造规则struct fw_rule rule;memset(&rule,0,sizeof(rule));strcpy(rule.fw_device,"eth0");rule.fw_op = FW_OP_ALLOW;rule.fw_dir = FW_DIR_IN;rule.fw_proto = IP_PROTO_TCP;addr_aton("192.168.1.0/24",&rule.fw_src);rule.fw_dport[0]= rule.fw_dport[1]=80;// 2. 打开防火墙fw_t*fw = fw_open();if(fw == NULL){perror("fw_open失败");exit(EXIT_FAILURE);}// 3. 添加规则if(fw_add(fw,&rule)<0){perror("fw_add失败");fw_close(fw);exit(EXIT_FAILURE);}// 4. 清理资源fw_close(fw);
4. 性能优化建议
以太网发送优化:
- 批量发送:使用writev而非多次write
- 缓冲区调整:设置适当的SO_SNDBUF
- 零拷贝:考虑使用mmap或sendfile
防火墙规则优化:
- 规则顺序:将最常用的规则放在前面
- 规则聚合:使用CIDR聚合相似规则
- 状态检测:优先使用状态跟踪
5. 安全注意事项
- 权限管理:
- 以最小权限原则运行
- 完成特权操作后立即降级
- 输入验证:
- 验证MAC地址格式
- 检查接口名称合法性
- 验证防火墙规则参数
- 错误处理:
- 不泄露敏感信息
- 记录安全事件
- 优雅地处理失败
总结
本文档深入分析了libdnet以太网和防火墙模块的跨平台实现:
以太网模块:
- Linux使用PF_PACKET套接字
- BSD使用BPF设备文件
- Solaris使用DLPI STREAMS接口
- 每个平台都提供了统一的API
防火墙模块:
- 支持多种防火墙系统(IPFW、ipchains、IPF、PF、PktFilter)
- 通过转换层统一不同防火墙的规则格式
- 使用平台特定的通信机制(setsockopt、ioctl、管道)
通过理解这些底层实现,开发者可以:
- 更好地调试网络问题
- 优化性能瓶颈
- 选择合适的平台特性
- 编写更可靠的代码
-
公众号:安全狗的自我修养
-
vx:2207344074
-
http://gitee.com/haidragon
-
http://github.com/haidragon
-
bilibili:haidragonx
-


夜雨聆风