socket也可以使用mmap啦。

 如果你的内核提供了CONFIG_PACKET_MMAP选项,那么恭喜你,对网络包你可以采用mmap了,用了mmap,你就节省了内核从内核内存区拷贝到用户内存区的这一步,效率提高很多。
下面我们说说怎么用这么强的功能。

int fd;

fd= socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))
socket函数的protocal参数采用了ETH_P_ALL,表示抓取所有以太帧。SOCK_RAW表示抓取到的包的数据是IP包。
setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, sizeof(req))
其中req参数是个结构,如下所示:
struct tpacket_req
{
unsigned int tp_block_size; /* Minimal size of contiguous block */
unsigned int tp_block_nr; /* Number of blocks */
unsigned int tp_frame_size; /* Size of frame */
unsigned int tp_frame_nr; /* Total number of frames */
};
该结构是用来在内核中创建循环缓冲区,这个缓冲区随后会被映射到调用进程的用户空间。这块内存在内核中用block来组织,每个块是连续的物理内存区,每 个块的大小是tp_block_size个字节,每个块最多可以装tp_block_size/tp_frame_size个帧。
这几个参数是由用户程序设置的,但有以下一些限制
tp_block_size 必须是 PAGE_SIZE 的倍数
tp_frame_size 必须大

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
`packet_mmap` 是 Linux 内核提供的一种高性能、零拷贝的网络数据包捕获机制。使用 `packet_mmap` 可以实现对网络数据包的实时捕获和分析,常用于网络流量监控、安全审计等领域。 下面是一个基于 `packet_mmap` 的简单例子: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <linux/if_packet.h> #include <linux/if_ether.h> #include <linux/if.h> #define MAX_PACKET_SIZE 2048 int main(int argc, char *argv[]) { int sockfd, ret; struct ifreq ifr; struct sockaddr_ll sll; char buffer[MAX_PACKET_SIZE]; struct tpacket_req req; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; char control_buffer[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; int num_packets = 10; int packet_idx = 0; int bytes_received; int i; if (argc < 2) { printf("Usage: %s <interface_name>\n", argv[0]); return 1; } sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sockfd < 0) { perror("socket"); return 1; } memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, argv[1], IFNAMSIZ); if (ioctl(sockfd, SIOCGIFINDEX, &ifr) == -1) { perror("ioctl"); close(sockfd); return 1; } memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; sll.sll_ifindex = ifr.ifr_ifindex; sll.sll_protocol = htons(ETH_P_ALL); if (bind(sockfd, (struct sockaddr *)&sll, sizeof(sll)) < 0) { perror("bind"); close(sockfd); return 1; } memset(&req, 0, sizeof(req)); req.tp_block_size = getpagesize() * 4; req.tp_block_nr = 1; req.tp_frame_size = getpagesize(); req.tp_frame_nr = req.tp_block_size / req.tp_frame_size; if (setsockopt(sockfd, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)) < 0) { perror("setsockopt"); close(sockfd); return 1; } iov.iov_base = buffer; iov.iov_len = MAX_PACKET_SIZE; memset(&msg, 0, sizeof(msg)); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control_buffer; msg.msg_controllen = sizeof(control_buffer); for (i = 0; i < num_packets; i++) { bytes_received = recvmsg(sockfd, &msg, 0); if (bytes_received < 0) { perror("recvmsg"); close(sockfd); return 1; } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) { struct tpacket_auxdata *aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg); if (packet_idx == aux->tp_frame_nr) { // Process the received packet here printf("Received packet %d of size %d\n", packet_idx, bytes_received); packet_idx++; break; } } } } close(sockfd); return 0; } ``` 这个例子实现了从指定的网络接口上捕获 10 个数据包,并输出每个数据包的序号和大小。具体实现中,首先创建一个 `AF_PACKET` 类型的 socket,并绑定到指定的网络接口上。然后设置 `PACKET_RX_RING` 选项,以便使用 `packet_mmap` 机制来捕获网络数据包。接下来,循环调用 `recvmsg` 函数来接收数据包,每次接收完一个数据包后,从 `msg` 结构体中获取附加数据 `PACKET_AUXDATA`,从而得到当前数据包的序号 `tp_frame_nr`,并输出序号和大小。最后关闭 socket 并退出程序。 需要注意的是,使用 `packet_mmap` 机制需要对硬件环境和内核配置有一定的要求,否则可能会出现性能瓶颈或者捕获不到数据包的情况。同时,使用 `packet_mmap` 机制也要注意防止缓冲区溢出等安全问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值