libpcap之零拷贝mmap

news2024/11/6 23:25:27

一、用户空间

在通过socket(AF_PACKET,…)创建fd后,进行接收队列的建立

//pcap-linux.c
static int
pcap_activate_linux(pcap_t *handle) {
...
	ret = setup_mmapped(handle, &status);
...
}

1.1 设置默认ring bufer size

static int
setup_mmapped(pcap_t *handle, int *status)
{
	...
	//1.
	if (handle->opt.buffer_size == 0) {
		/* by default request 2M for the ring buffer */
		handle->opt.buffer_size = 2*1024*1024;
	}
	...
}

1.2 确定tpacket版本

不同的版本格式略有不同。

static int
setup_mmapped(pcap_t *handle, int *status)
{
	...
	//1.
	...
	//2.
	ret = prepare_tpacket_socket(handle);
	...
}

1.3 创建ring

static int
setup_mmapped(pcap_t *handle, int *status)
{
	...
	//1.
	...
	//2.
	...
	//3.
	ret = create_ring(handle, status);
	...
}

计算block_size,frame_size等

以TPACKET3为例

static int
create_ring(pcap_t *handle, int *status)
{
	struct pcap_linux *handlep = handle->priv;
	unsigned i, j, frames_per_block;
...
	struct tpacket_req3 req;
...
	socklen_t len;
	unsigned int sk_type, tp_reserve, maclen, tp_hdrlen, netoff, macoff;
	unsigned int frame_size;
...

	switch (handlep->tp_version) {

	case TPACKET_V2:
	...
		break;

	case TPACKET_V3:
		req.tp_frame_size = MAXIMUM_SNAPLEN;
		req.tp_frame_nr = (handle->opt.buffer_size + req.tp_frame_size - 1)/req.tp_frame_size;
		break;
	default:
		...
		*status = PCAP_ERROR;
		return -1;
	}
	
	req.tp_block_size = getpagesize();
	while (req.tp_block_size < req.tp_frame_size)
		req.tp_block_size <<= 1;

	...

请求内核创建接收队列ring buffer

	...
frames_per_block = req.tp_block_size/req.tp_frame_size;
...
	/* ask the kernel to create the ring */
retry:
	req.tp_block_nr = req.tp_frame_nr / frames_per_block;

	/* req.tp_frame_nr is requested to match frames_per_block*req.tp_block_nr */
	req.tp_frame_nr = req.tp_block_nr * frames_per_block;
...
	if (setsockopt(handle->fd, SOL_PACKET, PACKET_RX_RING,
					(void *) &req, sizeof(req))) {
		...
	}

将内核创建的ring buffer映射到用户空间

	/* memory map the rx ring */
	handlep->mmapbuflen = req.tp_block_nr * req.tp_block_size;
	handlep->mmapbuf = mmap(0, handlep->mmapbuflen,
	    PROT_READ|PROT_WRITE, MAP_SHARED, handle->fd, 0);
	...

初始化用户态的映射空间头

...
/* fill the header ring with proper frame ptr*/
	handle->offset = 0;
	for (i=0; i<req.tp_block_nr; ++i) {
		u_char *base = &handlep->mmapbuf[i*req.tp_block_size];
		for (j=0; j<frames_per_block; ++j, ++handle->offset) {
			RING_GET_CURRENT_FRAME(handle) = base;
			base += req.tp_frame_size;
		}
	}

	handle->bufsize = req.tp_frame_size;
	handle->offset = 0;
...

二、内核空间

2.1 创建 rx ring

// net/packet/af_packet.c
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_RX_RING:
	case PACKET_TX_RING:
	{
		union tpacket_req_u req_u;
		int len;

		lock_sock(sk);
		switch (po->tp_version) {
		case TPACKET_V1:
		case TPACKET_V2:
			len = sizeof(req_u.req);
			break;
		case TPACKET_V3:
		default:
			len = sizeof(req_u.req3);
			break;
		}
		if (optlen < len) {
			ret = -EINVAL;
		} else {
			if (copy_from_user(&req_u.req, optval, len))
				ret = -EFAULT;
			else
				ret = packet_set_ring(sk, &req_u, 0,
						    optname == PACKET_TX_RING);
		}
		release_sock(sk);
		return ret;
	}
	
	default:
		return -ENOPROTOOPT;
	}
	...
}

分配空间

static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
		int closing, int tx_ring)
{
	struct pgv *pg_vec = NULL;
	struct packet_sock *po = pkt_sk(sk);
	...
	int was_running, order = 0;
	...
	/* Added to avoid minimal code churn */
	struct tpacket_req *req = &req_u->req;

	...
	order = get_order(req->tp_block_size);
	pg_vec = alloc_pg_vec(req, order);

初始化

...
switch (po->tp_version) {
		case TPACKET_V3:
			/* Block transmit is not supported yet */
			if (!tx_ring) {
				init_prb_bdqc(po, rb, pg_vec, req_u);
			} else {
				...
			}
			break;
		default:
			break;
		}

将新空间挂入队列上

mutex_lock(&po->pg_vec_lock);
	if (closing || atomic_read(&po->mapped) == 0) {
		err = 0;
		spin_lock_bh(&rb_queue->lock);
		swap(rb->pg_vec, pg_vec);
		rb->frame_max = (req->tp_frame_nr - 1);
		rb->head = 0;
		rb->frame_size = req->tp_frame_size;
		spin_unlock_bh(&rb_queue->lock);

		swap(rb->pg_vec_order, order);
		swap(rb->pg_vec_len, req->tp_block_nr);

		rb->pg_vec_pages = req->tp_block_size/PAGE_SIZE;
		po->prot_hook.func = (po->rx_ring.pg_vec) ?
						tpacket_rcv : packet_rcv;
		skb_queue_purge(rb_queue);
		if (atomic_read(&po->mapped))
			pr_err("packet_mmap: vma is busy: %d\n",
			       atomic_read(&po->mapped));
	}
	mutex_unlock(&po->pg_vec_lock);

2.2 将队列空间映射到用户空间


static int packet_mmap(struct file *file, struct socket *sock,
		struct vm_area_struct *vma)
{
	struct sock *sk = sock->sk;
	struct packet_sock *po = pkt_sk(sk);
	unsigned long size, expected_size;
	struct packet_ring_buffer *rb;
	unsigned long start;
	int err = -EINVAL;
	int i;

	...

	start = vma->vm_start;
	for (rb = &po->rx_ring; rb <= &po->tx_ring; rb++) {
		if (rb->pg_vec == NULL)
			continue;

		for (i = 0; i < rb->pg_vec_len; i++) {
			struct page *page;
			void *kaddr = rb->pg_vec[i].buffer;
			int pg_num;

			for (pg_num = 0; pg_num < rb->pg_vec_pages; pg_num++) {
				page = pgv_to_page(kaddr);
				err = vm_insert_page(vma, start, page);
				if (unlikely(err))
					goto out;
				start += PAGE_SIZE;
				kaddr += PAGE_SIZE;
			}
		}
	}

	atomic_inc(&po->mapped);
	vma->vm_ops = &packet_mmap_ops;
	err = 0;

out:
	mutex_unlock(&po->pg_vec_lock);
	return err;
}

在这里插入图片描述

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

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

相关文章

点云处理【六】(点云分割)

点云分割 第一章 点云数据采集 1. 点云分割 点云数据中包含目标物体&#xff0c;点云分割算法即将物体分割出来。 2 分割算法 2.1 RANSAC(随机采样一致性)方法 基于随机采样一致性的分割的步骤如下&#xff1a; 1.从一个样本集S中&#xff0c;随机抽取n个样本&#xff0c;…

下拉选择框监听el-option的方式

<el-select v-model"form.expenseType" placeholder"请选择费用类型" clearable filterable size"small"><el-option v-for"item in expenseNameList" :key"item.value" :label"item.label" :value"…

发现学习的新契机——广东开放大学电大搜题服务

广东开放大学一直以来致力于为广大学子提供优质的教育资源和学习支持&#xff0c;而最新推出的电大搜题微信公众号更进一步满足了学生们对于学习资料的需求。这一全新的学习辅助工具将为学子们带来便捷、高效的学习体验。 无论是传统的广播电视大学学生&#xff0c;还是广东开…

首届中国虚拟艺术巡展 NFT Showcase 圆满落幕!

由 Web3 营销先锋 Beep Crypto 精心策划&#xff0c;于 10 月 5 日至 10 月 17 日在广州潮流策展空间 YCC! 天宜隆重呈现。 本届展览以“虚拟时尚”为主题&#xff0c;融汇了众多的数字艺术展品&#xff0c;包括诸如 RTFKT X Nike、Gucci X 10KTF、Tiffany X Cryptopunks、Mee…

python二次开发Solidworks:读取立方体的高度

在SW中新建一个零件文档&#xff0c;建立一个立方体&#xff0c;长度和宽度自定义&#xff0c;高度100mm&#xff0c;下面通过python实现读取该立方体的高度&#xff1a; import win32com.client as win32 import pythoncomswApp win32.Dispatch(sldworks.application) swApp.…

使用telegram机器人发送通知

文章目录 背景1 创建机器人2 与机器人的会话3 调用API让机器人发送消息 背景 在训练深度学习模型时&#xff0c;除了粗略估计外&#xff0c;很难预测训练何时结束。此外&#xff0c;我们可能还想随时随地查看训练情况&#xff0c;如果每次都需要登录回服务器的话并不方便。因此…

RK3568笔记四:基于TensorFlow花卉图像分类部署

若该文为原创文章&#xff0c;转载请注明原文出处。 基于正点原子的ATK-DLRK3568部署测试。 花卉图像分类任务&#xff0c;使用使用 tf.keras.Sequential 模型&#xff0c;简单构建模型&#xff0c;然后转换成 RKNN 模型部署到ATK-DLRK3568板子上。 在 PC 使用 Windows 系统…

项目环境配置

一、后端环境搭建-前后端联调 1.1 nginx反向代理 1.2 nginx负载均衡配置 二、完善登录功能 处理密码明文&#xff0c;防止密码被别人获取破坏系统 MD5加密是单向的不可逆的 &#xff0c;不能由加密后的换算加密前的 //密码比对password DigestUtils.md5DigestAsHex ( pa…

二叉树,堆排序及TopK问题

要讲二叉树的概念&#xff0c;就要先讲树的概念。 树是什么呢&#xff1f; 树其实是一种储存数据的结构&#xff0c;因为他的结构倒过来和生活中的树很相似所以才被称之为树。 这是一颗多叉树&#xff0c;从最顶端的节点可以找到下边的几个节点&#xff0c;下边的节点又可以找…

Apollo:前端开发者的全栈探索之旅

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

【C语言必知必会 | 第四篇】一文带你精通顺序结构

引言 C语言是一门面向过程的、抽象化的通用程序设计语言&#xff0c;广泛应用于底层开发。它在编程语言中具有举足轻重的地位。 此文为【C语言必知必会】系列第四篇&#xff0c;进行C语言顺序结构的专项练习&#xff0c;结合专题优质题目&#xff0c;带领读者从0开始&#xff0…

Halcon手眼标定

手眼标定&#xff08;参考&#xff1a;B站王佳琪老师) 这里说的手眼标定中的手指的是机械手或者电机运动的轴&#xff0c;眼表示摄像头 就是两个空间坐标系的转换&#xff0c;这个转换需要一个转换矩阵&#xff0c;那么转换矩阵需要根据两个坐标系的对应的九个点来通过vec_to…

如何用记事本制作一个简陋的小网页(3)——注册信息表

目录 前提须知&#xff1a; 一、表格建立之前&#xff1a; 二、表格的建立&#xff1a; 三、信息表的内容填充&#xff1a; 1.昵称 和 电话 &#xff1a; 2.密码&#xff1a; 3.性别&#xff1a; 4. 爱好&#xff1a; 5.民族&#xff1a; 6. 出生日期&#xff1a; 7.…

Python用selenium实现自动登录和下单的项目实战

本文主要介绍了Python用selenium实现自动登录和下单的项目实战&#xff0c;文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值&#xff0c;需要的朋友们下面随着小编来一起学习学习吧− 前言 学python对selenium应该不陌生吧 Selenium…

什么是低代码开发平台?有什么优势?

目录 一、低代码平台演进 1. 低代码概念 2. 低代码衍生历程 二、为什么要用低代码&#xff1f; &#xff08;1&#xff09;降本提效&#xff0c;便捷开发 &#xff08;2&#xff09;降低开发门槛&#xff0c;扩大应用开发劳动力 &#xff08;3&#xff09;加快数字化转型建设 三…

“第四十五天” 数据结构基本概念

目前看的有关数据结构的课&#xff0c;估计这周就看完了&#xff0c;但感觉差很多&#xff0c;还是和c一样&#xff0c;这样过一下吧。但可能比较急&#xff0c;目前是打算争取寒假回家之前把四大件都先大致过一遍。 数据结构里面有很多新的定义和概念&#xff0c;学到现在&am…

054协同过滤算法的电影推荐系统

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

vue el-dialog弹出框自定义指令实现拖拽改变位置-宽度-高度

前言 在实际开发中我们经常使用el-dialog弹出框做表单&#xff0c;一般情况都是居中。遮挡到了一部分数据 当我们想要查看弹出框下面的数据时&#xff0c;就只能先把弹出框关闭&#xff0c;查看完数据之后在打开弹框 我们通过动态样式&#xff0c;和鼠标事件就可以实现。但自…