libpcap之数据分流

当前系统都是多核系统,为了充分利用多核优势,数据包可以在底层就进行分流,可以通过多线程/多进程对同一个接口的数据进行并行的处理。

一、实验

  • 一个server/client程序
  • 一个简单的抓包程序,抓取server/client之间的通信数据

在这里插入图片描述

1.1 server.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>void
Usage() {printf("ip port\n");
}int
start_srv(const char *ip, int port) {int ret = 0;int sockfd;sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {perror("socket:");ret = -1;goto out;}struct sockaddr_in srv;srv.sin_family = AF_INET;srv.sin_port = htons(port);inet_pton(AF_INET, ip, &srv.sin_addr.s_addr);socklen_t len = sizeof(srv);if (bind(sockfd, (const struct sockaddr *) &srv, len)) {perror("bind:");ret = -2;goto out;}if (listen(sockfd, 5)) {perror("listen:");ret = -3;goto out;}int cli_fd = accept(sockfd, NULL, NULL);if (cli_fd < 0) {perror("accept:");ret = -4;goto out;}char buf[128] = {0};read(cli_fd, buf, sizeof(buf)-1);sleep(1);memcpy(buf, "hello world", sizeof("hello world"));write(cli_fd, buf, strlen(buf));out:if (sockfd > 0) {close(sockfd);}return ret;
}int 
main(int argc, char **argv) {if (argc < 3) {Usage();exit(1);}start_srv(argv[1], atoi(argv[2]));return 0;
}

1.2 client.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>int
start_cli(const char *remote_ip, int port) {int ret = 0;int sockfd;sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {perror("socket:");ret = -1;goto out;}struct sockaddr_in srv;srv.sin_family = AF_INET;srv.sin_port = htons(port);inet_pton(AF_INET, remote_ip, &srv.sin_addr.s_addr);socklen_t len = sizeof(srv);if (connect(sockfd, (const struct sockaddr *) &srv, len)) {perror("connect:");ret = -2;goto out;}char buf[128];snprintf(buf, sizeof(buf), "I'm client.");write(sockfd, buf, strlen(buf));read(sockfd, buf, sizeof(buf));out:if (sockfd > 0) {close(sockfd);}return ret;
}void
Usage() {printf("remote_ip port\n");
}int
main(int argc, char **argv) {if (argc < 3) {Usage();exit(1);}start_cli(argv[1], atoi(argv[2]));return 0;
}

1.3 fanouttest.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pcap.h>
#include <pcap-int.h>
#include <pcap/bpf.h>void 
Usage() {printf("-i device\n");printf("-w filename\n");printf("-f filter\n");
}int recv_pkt = 0;void 
handler_cb(u_char *priv, const struct pcap_pkthdr *h, const u_char *pkt) {if (priv) {pcap_dump(priv, h, pkt);} else {printf("recv pkt length %u\n", h->caplen);}++recv_pkt;
}pcap_t *handle = NULL;void
sig_handler(int num) {if (handle) {pcap_breakloop(handle);}
}int
fanout_test(const char *device, const char *filter, const char *filename) {signal(SIGUSR1, sig_handler);pcap_dumper_t *dumper = NULL;struct bpf_program bpf = {0};int ret = 0;char errbuf[PCAP_ERRBUF_SIZE];handle = pcap_open_live(device, 65535, 1, 1000, errbuf);if (!handle) {printf("open %s failed. %s\n", device, errbuf);ret = -1;goto out;}if (filter) {bpf_u_int32 localnet, netmask;if (pcap_lookupnet(device, &localnet, &netmask, errbuf)) {localnet = 0;netmask = 0;printf("lookupnet err: %s\n", errbuf);}if (pcap_compile(handle, &bpf, filter, 1, netmask)) {printf("compile %s failed. %s\n", filter, pcap_geterr(handle));ret = -2;goto out;}if (pcap_setfilter(handle, &bpf)) {printf("set filter failed. %s\n", pcap_geterr(handle));ret = -3;goto out;}}if (filename) {dumper = pcap_dump_open(handle, filename);if (!dumper) {printf("dump open %s failed. %s", filename, pcap_geterr(handle));ret = -4;goto out;}}pcap_loop(handle, -1, handler_cb, (u_char *)dumper);printf("recv %d packet\n", recv_pkt);out:if (dumper) {pcap_dump_close(dumper);}if (handle) {pcap_close(handle);}pcap_freecode(&bpf);return ret;
}int 
main(int argc, char **argv) {char *device = NULL;char *save_file = NULL;char *filter = NULL;int opt;while ((opt = getopt(argc, argv, "i:w:f:")) != -1) {switch (opt) {case 'i':device = optarg;break;case 'w':save_file = optarg;break;case 'f':filter = optarg;break;default:Usage();exit(1);break;}}if (!device) {Usage();exit (2);}fanout_test(device, filter, save_file);return 0;
}

1.4 实验过程

复用libpcap编译结构,直接在testprogs目录增加源码文件。
testprogs/Makefile
testprogs/fanouttest.c

拷贝获取数据

在这里插入图片描述
在这里插入图片描述

gcc server.c -o srv
gcc client.c -o cli

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可以从结果看,数据确实拷贝了两份,每个文件都是完整的数据。

分流获取数据

在这里插入图片描述

在函数中添加setsockopt设置相关属性。

...
#include <errno.h>
#include <string.h>
#include <linux/if_packet.h>...int
fanout_test(const char *device, const char *filter, const char *filename) {...int val;int id = 0;val = (id << 16) | PACKET_FANOUT_QM;if (setsockopt(handle->fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) {printf("set fanout failed. %s \n", strerror(errno));}pcap_loop(handle, -1, handler_cb, (u_char *)dumper);printf("recv %d packet\n", recv_pkt);
...
}

在这里插入图片描述
从结果看,左边抓取到20个包,右边抓取到10个包,数据确实被分流了,没有完全复制。

二、原理


static int
packet_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
{struct sock *sk = sock->sk;struct packet_sock *po = pkt_sk(sk);int ret;if (level != SOL_PACKET)return -ENOPROTOOPT;switch (optname) {...case PACKET_FANOUT:{int val;if (optlen != sizeof(val))return -EINVAL;if (copy_from_user(&val, optval, sizeof(val)))return -EFAULT;return fanout_add(sk, val & 0xffff, val >> 16);}...default:return -ENOPROTOOPT;}
}

在这里插入图片描述
在这里插入图片描述

static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
{struct packet_rollover *rollover = NULL;struct packet_sock *po = pkt_sk(sk);struct packet_fanout *f, *match;u8 type = type_flags & 0xff;u8 flags = type_flags >> 8;int err;switch (type) {case PACKET_FANOUT_ROLLOVER:if (type_flags & PACKET_FANOUT_FLAG_ROLLOVER)return -EINVAL;case PACKET_FANOUT_HASH:case PACKET_FANOUT_LB:case PACKET_FANOUT_CPU:case PACKET_FANOUT_RND:case PACKET_FANOUT_QM:case PACKET_FANOUT_CBPF:case PACKET_FANOUT_EBPF:break;default:return -EINVAL;}mutex_lock(&fanout_mutex);err = -EALREADY;if (po->fanout)goto out;if (type == PACKET_FANOUT_ROLLOVER ||(type_flags & PACKET_FANOUT_FLAG_ROLLOVER)) {err = -ENOMEM;rollover = kzalloc(sizeof(*rollover), GFP_KERNEL);if (!rollover)goto out;atomic_long_set(&rollover->num, 0);atomic_long_set(&rollover->num_huge, 0);atomic_long_set(&rollover->num_failed, 0);}if (type_flags & PACKET_FANOUT_FLAG_UNIQUEID) {if (id != 0) {err = -EINVAL;goto out;}if (!fanout_find_new_id(sk, &id)) {err = -ENOMEM;goto out;}/* ephemeral flag for the first socket in the group: drop it */flags &= ~(PACKET_FANOUT_FLAG_UNIQUEID >> 8);}match = NULL;list_for_each_entry(f, &fanout_list, list) {if (f->id == id &&read_pnet(&f->net) == sock_net(sk)) {match = f;break;}}err = -EINVAL;if (match && match->flags != flags)goto out;if (!match) {err = -ENOMEM;match = kzalloc(sizeof(*match), GFP_KERNEL);if (!match)goto out;write_pnet(&match->net, sock_net(sk));match->id = id;match->type = type;match->flags = flags;INIT_LIST_HEAD(&match->list);spin_lock_init(&match->lock);refcount_set(&match->sk_ref, 0);fanout_init_data(match);match->prot_hook.type = po->prot_hook.type;match->prot_hook.dev = po->prot_hook.dev;match->prot_hook.func = packet_rcv_fanout;match->prot_hook.af_packet_priv = match;match->prot_hook.id_match = match_fanout_group;list_add(&match->list, &fanout_list);}err = -EINVAL;spin_lock(&po->bind_lock);if (po->running &&match->type == type &&match->prot_hook.type == po->prot_hook.type &&match->prot_hook.dev == po->prot_hook.dev) {err = -ENOSPC;if (refcount_read(&match->sk_ref) < PACKET_FANOUT_MAX) {__dev_remove_pack(&po->prot_hook);po->fanout = match;po->rollover = rollover;rollover = NULL;refcount_set(&match->sk_ref, refcount_read(&match->sk_ref) + 1);__fanout_link(sk, po);err = 0;}}spin_unlock(&po->bind_lock);if (err && !refcount_read(&match->sk_ref)) {list_del(&match->list);kfree(match);}out:kfree(rollover);mutex_unlock(&fanout_mutex);return err;
}

通过将入口函数tpacket_rcv替换成packet_rcv_fanout,并且将相同的抓包方式放在fanout中的一个数据中,最后将此fanout挂在一个全局列表fanout_list中。
并且将po从ptype_all列表中剔除掉,最后在ptype_all中只保留一个即可,这样数据就只有一个入口,然后在到packet_rcv_fanout中进行分流处理。

static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev,struct packet_type *pt, struct net_device *orig_dev)
{struct packet_fanout *f = pt->af_packet_priv;unsigned int num = READ_ONCE(f->num_members);struct net *net = read_pnet(&f->net);struct packet_sock *po;unsigned int idx;if (!net_eq(dev_net(dev), net) || !num) {kfree_skb(skb);return 0;}if (fanout_has_flag(f, PACKET_FANOUT_FLAG_DEFRAG)) {skb = ip_check_defrag(net, skb, IP_DEFRAG_AF_PACKET);if (!skb)return 0;}switch (f->type) {case PACKET_FANOUT_HASH:default:idx = fanout_demux_hash(f, skb, num);break;case PACKET_FANOUT_LB:idx = fanout_demux_lb(f, skb, num);break;case PACKET_FANOUT_CPU:idx = fanout_demux_cpu(f, skb, num);break;case PACKET_FANOUT_RND:idx = fanout_demux_rnd(f, skb, num);break;case PACKET_FANOUT_QM:idx = fanout_demux_qm(f, skb, num);break;case PACKET_FANOUT_ROLLOVER:idx = fanout_demux_rollover(f, skb, 0, false, num);break;case PACKET_FANOUT_CBPF:case PACKET_FANOUT_EBPF:idx = fanout_demux_bpf(f, skb, num);break;}if (fanout_has_flag(f, PACKET_FANOUT_FLAG_ROLLOVER))idx = fanout_demux_rollover(f, skb, idx, true, num);po = pkt_sk(f->arr[idx]);return po->prot_hook.func(skb, dev, &po->prot_hook, orig_dev);
}

packet_rcv_fanout 进入后,根据不同的算法进行选择一个po,然后调用相应的func(tpacket_rcv)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/131182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【蓝桥杯 第十四届省赛Java B组】真题训练(A - C)正在更新

目录 A、阶乘求和 - BigInteger B、幸运数字 - 字符串 进制转换 暴力大法 C、数组分割 - A、阶乘求和 - BigInteger 思路&#xff1a; 当时比赛时&#xff0c;拿计算器算的&#xff0c;然后辛辛苦苦也没对 看到这个数肯定很大&#xff0c;而且只求后9位&#xff0c;阶乘越…

【计算机网络】应用层

应用层协议原理 客户-服务器体系结构&#xff1a; 特点&#xff1a;客户之间不能直接通信&#xff1b;服务器具有周知的&#xff0c;固定的地址&#xff0c;该地址称为IP地址。 配备大量主机的数据中心常被用于创建强大的虚拟服务器&#xff1b;P2P体系结构&#xff1a; 特点&…

K8S知识点(二)

&#xff08;1&#xff09;K8S概念 K8S是通过控制pod来控制容器进而控制程序的 service是沟通Pod和外键的桥梁&#xff0c;可以实现负载均衡的效果&#xff0c;加权负载的效果 &#xff08;2&#xff09;环境搭建-环境规划 &#xff08;3&#xff09;环境搭建-主机安装 使用…

时序分解 | Matlab实现PSO-VMD粒子群算法优化变分模态分解时间序列信号分解

时序分解 | Matlab实现PSO-VMD粒子群算法优化变分模态分解时间序列信号分解 目录 时序分解 | Matlab实现PSO-VMD粒子群算法优化变分模态分解时间序列信号分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 PSO-VMD粒子群算法PSO优化VMD变分模态分解 可直接运行 分解效果…

2023-11-04 LeetCode每日一题(数组中两个数的最大异或值)

2023-11-04每日一题 一、题目编号 421. 数组中两个数的最大异或值二、题目链接 点击跳转到题目位置 三、题目描述 给你一个整数数组 nums &#xff0c;返回 nums[i] XOR nums[j] 的最大运算结果&#xff0c;其中 0 ≤ i ≤ j < n 。 示例 1&#xff1a; 示例 2&…

[动态规划] (六) 路径问题 LeetCode 63.不同路径II

[动态规划] (六) 路径问题: LeetCode 63.不同路径II 文章目录 [动态规划] (六) 路径问题: LeetCode 63.不同路径II题目解析解题思路状态表示状态转移方程初始化和填表返回值 代码实现总结 63. 不同路径 II 题目解析 (1) 机器人从左上角移动到右下角 (2) 机器人只能向右或者向…

【漏洞复现】Nginx_0.7.65_空字节漏洞

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 1.1、漏洞描述 1.2、漏洞等级 1.3、影响版本 0.7.65 1.4、漏洞复现 1、基础环…

JavaSpringbootMySQL高校实训管理平台01557-计算机毕业设计项目选题推荐(附源码)

目 录 摘要 1 绪论 1.1 研究背景 1.2 研究意义 1.3论文结构与章节安排 2 高校实训管理平台系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据增加流程 2.2.2 数据修改流程 2.2.3 数据删除流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系…

Oracle安全基线检查

一、账户安全 1、禁止SYSDBA用户远程连接 用户具备数据库超级管理员(SYSDBA)权限的用户远程管理登录SYSDBA用户只能本地登录,不能远程。REMOTE_LOGIN_PASSWORDFILE函数的Value值为NONE。这意味着禁止共享口令文件,只能通过操作系统认证登录Oracle数据库。 1)检查REMOTE…

【python 深拷贝与浅拷贝】

python 深拷贝与浅拷贝 问题&#xff1a; 在用影刀编写流程的时候发现&#xff0c;明明只修改人名为“小张”对应的字典里面的值&#xff0c;但是所有的人名对应的值都被修改了。 原因&#xff1a; 第14行&#xff0c;设置键值对&#xff0c;值对应的变量“初始打卡类型字…

伪随机序列——m序列及MATLAB仿真

文章目录 前言一、m 序列1、m 序列的产生2、m 序列的性质①、均衡性②、游程分布③、移位相加特性④、自相关函数⑤、功率谱密度⑥、伪噪声特性 二、M 序列1、m 序列的产生2、m 序列的性质 三、MATLAB 中 m 序列1、m 序列生成函数的 MATLAB 代码2、MATLAB 仿真 前言 在通信系统…

CVE-2023-34040 Kafka 反序列化RCE

漏洞描述 Spring Kafka 是 Spring Framework 生态系统中的一个模块&#xff0c;用于简化在 Spring 应用程序中集成 Apache Kafka 的过程&#xff0c;记录 (record) 指 Kafka 消息中的一条记录。 受影响版本中默认未对记录配置 ErrorHandlingDeserializer&#xff0c;当用户将容…

二叉树第i层结点个数

//二叉树第i层结点个数 int LevelNodeCount(BiTree T, int i) {if (T NULL || i < 1)return 0;if (i 1) return 1;return LevelNodeCount(T->lchild, i - 1) LevelNodeCount(T->rchild, i - 1); } int GetDepthOfBiTree(BiTree T) {if (T NULL)return 0;return Ge…

【HTML】播放器如何自动播放【已解决】

自动播放器策略 先了解浏览器的自动播放器策略 始终允许静音自动播放在以下情况&#xff0c;带声音的自动播放才会被允许 2.1 用户已经与当前域进行交互 2.2 在桌面上&#xff0c;用户的媒体参与指数阈值(MEI)已被越过&#xff0c;这意味着用户以前播放带有声音的视频。 2.3 …

发现一款PDF转换成翻页电子书的网站

​随着科技的发展&#xff0c;电子书越来越受到人们的喜爱。而PDF格式的文件也越来越多地被人们使用。那么&#xff0c;如何将PDF文件转换成翻页电子书呢&#xff1f;今天就为大家推荐一款好用的PDF转翻页电子书网站。 一、网站介绍 这款网站是一款非常实用的在线转换工具&…

【漏洞复现】Apache_Shiro_1.2.4_反序列化漏洞(CVE-2016-4437)

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞分析3、漏洞验证 说明内容漏洞编号CVE-2016-4437漏洞名称Apache_Shiro_1.2.4_反序列化漏洞漏洞评级…

selenium自动化测试入门 —— cookie 处理

driver.get_cookies() # 获得cookie 信息 driver.get_cookies(name) # 获得对应name的cookie信息 add_cookie(cookie_dict) # 向cookie 添加会话信息 delete_cookie(name) # 删除特定(部分)的cookie delete_all_cookies() # 删除所有cookie 示例&#xff1a; from sel…

基于深度学习的视频多目标跟踪实现 计算机竞赛

文章目录 1 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的视频多目标跟踪实现 …

php7.4.32如何快速正确的开启OpenSSL扩展库,最简单的办法在这里!

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…