Netfilter 是如何工作的(六):连接跟踪信息的入口创建(in)和出口确认(confirm)

Articles (gitee.io)

IPtables-朱双印博客 (zsythink.net)

在 Netfilter 是如何工作的(五) 中连接跟踪信息使用的创建-确认机制的

Netfilter在报文进入系统的入口处,将连接跟踪信息记录在报文上,在出口进行confirm.确认后的连接信息

本文以一个本机上送过程中的TCP/IPv4SYN握手报文为例,详细分析连接跟踪机制的工作流程。

入口 & 出口

由于是本机上送流程,因此SYN报文的入口是 PRE_ROUTING,而出口则是 LOCAL_IN。Netfilter 在初始化时,会在这两个 HOOK 点注册连接跟踪相关的处理函数。

static struct nf_hook_ops ipv4_conntrack_ops[] = {
	{
		.hook		= ipv4_conntrack_in,           // 入口回调函数
		.pf		    = NFPROTO_IPV4,
		.hooknum	= NF_INET_PRE_ROUTING,         //  PRE_ROUTING HOOK 点
		.priority	= NF_IP_PRI_CONNTRACK,         //  优先级
	},
	......
	{
		.hook		= ipv4_confirm,                // 出口的回调函数
		.pf		    = NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_IN,            // LOCAL_IN HOOK 点 
		.priority	= NF_IP_PRI_CONNTRACK_CONFIRM, // 优先级
	},
}
入口

SYN 报文进入连接跟踪的入口是 ipv4_conntrack_in(),然后调用nf_conntrack_in, 后面这个函数较长,因此我们这里分段来看

ipv4_conntrack_in
    |
    |-- nf_conntrack_in

nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, struct sk_buff *skb)
{
    // ......
	if (skb->nfct) {
		/* Previously seen (loopback or untracked)?  Ignore. */
		tmpl = (struct nf_conn *)skb->nfct;
		if (!nf_ct_is_template(tmpl)) {
			NF_CT_STAT_INC_ATOMIC(net, ignore);
			return NF_ACCEPT;
		}
		skb->nfct = NULL;
	}
    // ......
}

首先是检查 skb 上是否已经关联了连接跟踪信息。这里不是入口吗?为什么 skb 上会由连接跟踪信息 ?! 注释写了,如果这个包是从 loopback 口收到的话,这时它就有可能已经关联的链接跟踪信息了,这时就会把已关联的连接跟踪信息作为模板(tmpl)。而在我们的情境中,SYN 报文从外部网卡收到,所以显然这里 skb 是不会有连接跟踪信息的。

接着往下看:

	l3proto = __nf_ct_l3proto_find(pf);
	ret = l3proto->get_l4proto(skb, skb_network_offset(skb),
				   &dataoff, &protonum);
    l4proto = __nf_ct_l4proto_find(pf, protonum);                  

这一段代码片段完成了两件事

  • 将协议族 pf 转换得到 L3 协议对应的函数操作集(operations),在我们的例子中,入参pf = AF_INET,也就是 IP 协议,因此这里会得到l3proto = nf_conntrack_l3proto_ipv4
  • 进一步解析报文 L4 对应的协议号,这里会调用ipv4_get_l4proto,在我们的例子中,会得到 TCP 对应的协议号,protonum = 6,进而得到l4proto = nf_conntrack_l4proto_tcp4

接下来就是入口函数的核心流程resolve_normal_ct(), 正常情况下,这个函数会返回连接跟踪信息的指针ct和简要信息ctinfo,并将它们分别设置到skb->nfcfskb->ctinfo

    struct nf_conn *ct;
    enum ip_conntrack_info ctinfo;
    int set_reply = 0;
    
	ct = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum,
			       l3proto, l4proto, &set_reply, &ctinfo);

下面将 resolve_normal_ct 展开

第一步调用nf_ct_get_tuple, 将报文的五元组信息填充到struct nf_conntrack_tuple结构的变量中

static inline struct nf_conn *
resolve_normal_ct(struct net *net, struct nf_conn *tmpl,
		  struct sk_buff *skb,
		  unsigned int dataoff,
		  u_int16_t l3num,
		  u_int8_t protonum,
		  struct nf_conntrack_l3proto *l3proto,
		  struct nf_conntrack_l4proto *l4proto,
		  int *set_reply,
		  enum ip_conntrack_info *ctinfo)
{          
    const struct nf_conntrack_zone *zone;
	struct nf_conntrack_tuple tuple;
	struct nf_conntrack_tuple_hash *h;
    
    //  @tuple 是出参, 对tcp来说是 tcp_pkt_to_tuple, 只是填了dport和sport
	if (!nf_ct_get_tuple(skb, skb_network_offset(skb),
			     dataoff, l3num, protonum, net, &tuple, l3proto,
			     l4proto)) {
		pr_debug("resolve_normal_ct: Can't get tuple\n");
		return NULL;
	}
    // ...... 
}

第二步,计算tuple的哈希值,然后根据此哈希值查找连接跟踪信息表中是否已存在这个哈希值的记录,将其值赋予h

    ......
    zone = nf_ct_zone_tmpl(tmpl, skb, &tmp);
	hash = hash_conntrack_raw(&tuple);                    // 计算hash
	h = __nf_conntrack_find_get(net, zone, &tuple, hash); // 查找该tuple的元组信息是否已经存在

在我们的例子中,假设原本没有这样的记录,于是这里会使用init_conntrack创建一条新的连接跟踪信息

	if (!h) {
		h = init_conntrack(net, tmpl, &tuple, l3proto, l4proto,
				   skb, dataoff, hash);

最后,将连接跟踪信息设置到报文skb上。特别注意,这里只会将连接信息保存在skb上,并不会将这条信息插入到连接跟踪信息表中,插入到表中这是出口 confirm 阶段的工作。

    ct = nf_ct_tuplehash_to_ctrack(h);
	......
	} else {
		......
		} else {
			pr_debug("nf_conntrack_in: new packet for %p\n", ct);
			*ctinfo = IP_CT_NEW;
		}
		*set_reply = 0;
	}
	skb->nfct = &ct->ct_general;
	skb->nfctinfo = *ctinfo;
出口

出口处,Netfilter 将连接跟踪信息设置到报文 skb 上,这样在出口的地方就可以将其取出,再插入到连接信息的哈希表中,完成确认

ipv4_confirm
    |
    |-- nf_conntrack_confirm
        |
        |-- __nf_conntrack_confirm
        
/* Confirm a connection given skb; places it in hash table */
int
__nf_conntrack_confirm(struct sk_buff *skb)
{
	struct nf_conn *ct;
	enum ip_conntrack_info ctinfo;
    // ......
	ct = nf_ct_get(skb, &ctinfo);
    // ......
	__nf_conntrack_hash_insert(ct, hash, reply_hash);
    // ......
	return NF_ACCEPT;
}        

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

opencv3.4.12全景拼接

最近camera项目需要用到全景拼接,故此查阅大量资料,终于将此功能应用在实际项目上,下面总结一下此过程中遇到的一些问题及解决方式,同时也会将源码附在结尾处,供大家参考。 首先说一下此源码的大概执行流程&#xff0c…

阅读文献-胃癌

写在前面 今天先不阅读肺癌的了,先读一篇胃癌的文章 文献 An individualized stemness-related signature to predict prognosis and immunotherapy responses for gastric cancer using single-cell and bulk tissue transcriptomes IF:4.0 中科院分区:2区 医学…

行为型设计模式——备忘录模式

备忘录模式 备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原,很多软件都提供了撤销(Undo)操作…

Ceph入门到精通-通过 CloudBerry Explorer 管理对象bucket

简介 CloudBerry Explorer 是一款可用于管理对象存储(Cloud Object Storage,COS)的客户端工具。通过 CloudBerry Explorer 可实现将 COS 挂载在 Windows 等操作系统上,方便用户访问、移动和管理 COS 文件。 支持系统 支持 Wind…

【动态规划】【滑动窗口】C++算法:3003 执行操作后的最大分割数量

作者推荐 【动态规划】【字符串】扰乱字符串 本文涉及的基础知识点 C算法:滑动窗口总结 动态规划 LeetCode3003 执行操作后的最大分割数量 给你一个下标从 0 开始的字符串 s 和一个整数 k。 你需要执行以下分割操作,直到字符串 s 变为 空&#xff1…

如何开发测试框架?

基本概念 库 英文单词叫Library,库是由代码集合成的一个产品,供程序员调用。面向对象的代码组织形成的库叫类库,面向过程的代码组织形成的库叫函数库。 框架 英文单词叫Framework,框架是为解决一个或一类问题而开发的产品&#x…

【问题探讨】基于非支配排序的蜣螂优化算法NSDBO求解微电网多目标优化调度研究

目录 主要内容 模型研究 结果一览 下载链接 主要内容 该模型以环境保护成本和运行成本为双目标构建了微电网优化调度模型,模型目标函数和约束条件复现文献《基于改进粒子群算法的微电网多目标优化调度》,程序的特点是采用非支配排序的蜣螂…

Flutter-Web从0到部署上线(实践+埋坑)

本文字数:7743字 预计阅读时间:60分钟 01 前言 首先说明一下,这篇文章是给具备Flutter开发经验的客户端同学看的。Flutter 的诞生虽然来自 Google 的 Chrome 团队,但大家都知道 Flutter 最先支持的平台是 Android 和 iOS&#xff…

挖种子小游戏

欢迎来到程序小院 挖种子 玩法&#xff1a;看到种子点击鼠标左键进行挖种子&#xff0c;30秒内看你能够挖多少颗种子&#xff0c;快去挖种子吧^^。开始游戏https://www.ormcc.com/play/gameStart/251 html <canvas id"canvas" width"640" height"…

Docker五部曲之三:镜像构建

文章目录 前言Docker构建架构构建指令构建上下文本地目录Git存储库压缩文件纯文本文件.dockerignore文件 Dockerfile解析器指令环境变量命令执行格式exec格式shell格式 FROMRUNCMDLABELEXPOSEENVADDCOPYENTRYPOINTVOLUMEUSERWORKDIRARGONBUILDSHELL 多级构建 前言 本文均翻译自…

每日一题——LeetCode1103.分糖果 ||

方法一 个人方法&#xff1a; 有多少人就创建多大的数组并把数组的所有元素初始化为0&#xff0c;只要还有糖果&#xff0c;就循环给数组从头到尾添加糖果&#xff0c;每次分的糖果数递增1&#xff0c;最后可能刚好分完也可能不够&#xff0c;不够就还剩多少给多少。 var dis…

11Spring IoC注解式开发(下)(负责注入的注解/全注解开发)

1负责注入的注解 负责注入的注解&#xff0c;常见的包括四个&#xff1a; ValueAutowiredQualifierResource 1.1 Value 当属性的类型是简单类型时&#xff0c;可以使用Value注解进行注入。Value注解可以出现在属性上、setter方法上、以及构造方法的形参上, 方便起见,一般直…

【 ATU 随笔记 - Inverter 】PV Inverter 太阳能逆变器市场分析

一、简介 在上一篇的介绍中与大家分享了Micro Inverter ( 微型逆变器 )的用途与特色&#xff0c;也提到 Micro Inverter 适合家庭或是一些小型企业的需求。太阳能作为再生能源的代表&#xff0c;在当今能源转型中扮演着重要角色&#xff0c;也是有大型企业、大型能源站的需求&a…

Java项目:06 Springboot的进销存管理系统

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 进销存管理系统 介绍 进销存系统是为了对企业生产经营中进货、出货、批发销售、付款等全程进行&#xff08;从接获订单合同开 始&#xff0c;进入物料采购、入…

杨中科 .NETCORE ENTITY FRAMEWORK CORE-1 EFCORE 第一部分

一 、什么是EF Core 什么是ORM 1、说明: 本课程需要你有数据库、SOL等基础知识。 2、ORM: ObjectRelational Mapping。让开发者用对象操作的形式操作关系数据库 比如插入: User user new User(Name"admin"Password"123”; orm.Save(user);比如查询: Book b…

计算机三级(网络技术)一综合题(IP地址计算)

例题一 &#xff08;正常算&#xff09; 计算并填写下表 地址类别 A类地址段是1.0.0.0~127.255.255.255 1~127 B类地址段是128.0.0.0~191.255.255.255 128~191 C类地址段是192.0.0.0~223.255.255.255 192~223 所以41填A 网络地址为主机位全0 根据子网掩码&…

Redis实现分布式会话

Redis实现分布式会话 1 什么是分布式会话 1 这是我么之前学过的注册登录模式 2 如果非常多的人访问&#xff0c;因为单台服务器的访问承受能力是有限的&#xff0c;那么我们就想用多态服务器来承担压力 3 一般通过负载均衡的方式来实现&#xff0c;来分担服务器的压力。 4 负…

【Redis】Redis数据过期策略、数据淘汰策略

数据过期策略 首先&#xff0c;我们要知道Redis的数据过期策略是惰性删除和定期删除结合使用。 面试题&#xff1a; 惰性删除 定期删除 数据淘汰策略 Redis支持8种数据淘汰策略&#xff1a; noeviction&#xff1a;不淘汰任何key&#xff0c;当内存满时&#xff0c;不写入任何…

TCP之三次握手四次挥手与UDP区别

文章目录 1 TCP三次握手四次挥手1.1 数据包说明1.1.1 TCP数据包1.1.2 UDP数据包1.1.3 TCP和UDP差异1.1.4 TCP可靠性传输机制 1.2 三次握手1.2.1 三次握手定义1.2.2 三次握手问题1.2.2.1 问题引入分析1.2.2.2 历史连接1.2.2.3 同步双方初始序列号1.2.2.4 避免资源浪费 1.3 四次挥…

POSTGRESQL中ETL、fdw的平行替换

POSTGRESQL中ETL、fdw的平行替换 01、简介 “ 在我前两次的文章中&#xff0c;说到postgresql对于python的支持&#xff0c;其实很多功能也就可以封装进入的postgresql数据库中去。比如fdw、etl等&#xff0c;本文将以此为叙述点&#xff0c;进行演示展示” 在postgresql数据…
最新文章