一、Netfilter框架以及挂接点
1.Netfilter子系统的主要功能
数据包选择(iptables)
数据包过滤
网络地址转换(NAT)
数据包操控
连接跟踪
网络统计信息
1. 数据包选择(iptables 规则匹配)
- 功能:根据预设规则(如源 / 目标 IP、端口、协议等)精准识别和选择特定数据包,为后续操作(如过滤、NAT 等)提供条件。
- 例子:
使用iptables
规则选择来自192.168.1.100
且目标端口为 80 的 HTTP 数据包:该规则仅匹配符合条件的数据包,并执行允许(ACCEPT)操作。iptables -A INPUT -s 192.168.1.100 -p tcp --dport 80 -j ACCEPT
2. 数据包过滤
- 功能:基于数据包选择结果,决定允许或拒绝数据包通过,实现网络访问控制。
- 例子:
禁止来自203.0.113.5
的所有流量: 该规则会直接丢弃来自该 IP 的数据包,阻止其访问本地系统。iptables -A INPUT -s 203.0.113.5 -j DROP
3. 网络地址转换(NAT)
- 功能:修改数据包的源或目标 IP 和端口,实现多设备共享公网 IP(SNAT)或将公网流量转发到内网(DNAT)。
- 例子:
- SNAT(源地址转换):
局域网内设备通过路由器访问互联网时,路由器将数据包的源 IP 转换为自己的公网 IP:iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
- DNAT(目标地址转换):
将公网对路由器 8080 端口的访问转发到内网服务器192.168.1.200
的 80 端口:bash
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.200:80
4. 数据包操控
- 功能:修改数据包的字段(如 TTL、TOS、DSCP 等),以实现流量标记、QoS 优先级调整等高级功能。
- 例子:
使用mangle
表为特定数据包设置 DSCP 值(用于 QoS): 该规则将目标端口为 80 的 HTTP 数据包的 DSCP 值设置为 0x20,以便网络设备优先处理。iptables -t mangle -A PREROUTING -p tcp --dport 80 -j DSCP --set-dscp 0x20
5. 连接跟踪
- 功能:跟踪网络连接的状态(如新建、已建立、相关),确保返回流量能正确匹配原有连接,简化规则配置。
- 例子:
允许已建立的连接返回流量:该规则自动允许与现有连接相关的响应数据包通过,无需为每个响应单独设置规则。iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
6. 网络统计信息
- 功能:统计匹配特定规则的数据包数量和流量大小,用于监控和分析网络行为。
- 例子:
统计访问 22 端口(SSH)的数据包数量:iptables -A INPUT -p tcp --dport 22 -m statistic --mode count --probability 1 -j LOG --log-prefix "SSH Access: "
该规则会记录所有匹配的 SSH 连接尝试次数,并写入系统日志,便于后续分析。
2.Linux内核子系统常用框架
1. IPVS(IP Virtual Server,IP 虚拟服务器)
- 功能与原理:
IPVS 是 Linux 内核实现的传输层负载均衡框架,工作在网络层(L4),支持多种负载均衡模式(如 NAT 模式、DR 模式、TUN 模式),通过调度算法将客户端请求分发到后端多个真实服务器,实现高可用、高性能的服务集群。内核 2.6.28 起支持 IPv6。- 示例(DR 模式配置):
- 安装工具:
yum install ipvsadm # 或 apt install ipvsadm
- 配置负载均衡规则(假设后端服务器 IP 为
192.168.1.10
和192.168.1.11
,服务端口 80,调度算法为轮询):- 说明:客户端访问虚拟 IP(VIP_IP)的 80 端口时,IPVS 会通过轮询算法将请求分发到后端真实服务器。
2. ip sets
- 功能与原理:
ip sets 是用户空间工具ipset
和内核模块组成的框架,用于管理 IP 集合(如 IP 地址、端口范围、MAC 地址等)。通过预定义集合,可在 iptables 规则中快速匹配大量 IP,提升规则效率,避免逐条编写规则。- 示例(封禁多个恶意 IP):
- 创建 IP 集合:
ipset create bad_ips hash:ip # 创建名为 bad_ips 的 IP 集合
- 添加恶意 IP 到集合:
ipset add bad_ips 203.0.113.10 ipset add bad_ips 203.0.113.11
- 结合 iptables 过滤:
iptables -A INPUT -m set --match-set bad_ips src -j DROP # 丢弃来自集合中 IP 的流量
- 说明:通过
ipset
管理恶意 IP,iptables 规则直接匹配集合,简化大量 IP 过滤场景。3. iptables
- 功能与原理:
iptables 是 Netfilter 的前端管理工具,通过定义规则链(如 INPUT、OUTPUT、FORWARD)和规则,实现数据包过滤、NAT、流量操控等功能。规则基于匹配条件(如协议、端口、IP)执行动作(ACCEPT、DROP、MASQUERADE 等)。- 示例(基础防火墙规则):
- 允许本地回环流量:
iptables -A INPUT -i lo -j ACCEPT
- 允许已建立的连接返回流量:
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
- 禁止外部访问本地 22 端口(SSH):
iptables -A INPUT -p tcp --dport 22 -j DROP
- 说明:通过 iptables 逐条定义规则,控制不同场景下的网络流量,是构建 Linux 防火墙的核心工具。
3.Netfilter挂接点
在网络栈有5个地方设置Netfilter挂接点,但要注意ipv4和ipv6中挂接点名称相同。
1. NF_INET_PRE_ROUTING
- 位置与作用:
所有入站数据包(无论是发往本地还是需转发的)进入内核后遇到的第一个挂接点,位于路由选择(判断数据包是本地接收还是转发)之前。主要用于修改数据包的目标地址(如 DNAT)、过滤非法入站流量等。- 典型场景:
- 实现网络地址转换(NAT)中的目标地址转换。例如,将公网用户对路由器 8080 端口的访问,通过该挂接点修改目标地址,转发到内网 Web 服务器的 80 端口。
- 临时地址修改:在测试场景中,可临时修改数据包的目标地址(类似简易 DNAT),将外部对特定端口的访问重定向到本地其他服务端口,用于调试网络服务映射逻辑。
- 流量过滤:例如,在数据包进入路由判断前,通过规则丢弃来源不可信的数据包(如恶意 IP 扫描流量)。
2. NF_INET_LOCAL_IN
- 位置与作用:
处理发往本地主机的数据包(经路由选择判定为本地接收)。数据包到达此挂接点后,会进入上层协议(如 TCP、UDP)进一步处理,最终交付给本地应用程序(如 Web 服务器、SSH 服务)。- 典型场景:
本地防火墙规则在此挂接点过滤非法访问本地服务的流量。例如,禁止外部主机访问本地的 SSH 服务(端口 22),可在此挂接点添加规则丢弃相关数据包。
3. NF_INET_FORWARD
- 位置与作用:
处理需转发的数据包(经路由选择判定不发往本地,需转发到其他网络)。只有开启内核转发功能(net.ipv4.ip_forward=1
),且数据包符合转发规则时,才会经过此挂接点。- 典型场景:
在路由器或网关设备中,对转发的流量进行过滤或策略控制。例如,限制转发的数据包带宽,或丢弃恶意 IP 的转发流量。
4. NF_INET_LOCAL_OUT
- 位置与作用:
处理本地主机生成的出站数据包(如本地发起的 HTTP 请求、SSH 连接)。数据包在此挂接点完成本地协议栈处理(如封装 IP 头),随后进入NF_INET_POST_ROUTING
挂接点。- 典型场景:
对本地生成的数据包进行标记或策略调整。例如,为本地发送的视频流数据包设置 QoS 优先级,确保实时传输质量。
5. NF_INET_POST_ROUTING
- 位置与作用:
所有出站数据包(包括本地生成的数据包和转发的数据包)离开内核前的最后一个挂接点。常用于修改数据包的源地址(如 SNAT),确保出站流量的源 IP 符合网络规划。- 典型场景:
局域网共享公网 IP 上网时,路由器通过此挂接点将内网设备的源 IP 转换为公网 IP(SNAT),使内网设备能访问互联网。
总结:挂接点处理流程
- 入站数据包先经过 NF_INET_PRE_ROUTING,路由选择后:
- 若发往本地,进入 NF_INET_LOCAL_IN,交付本地应用;
- 若需转发,进入 NF_INET_FORWARD,再经 NF_INET_POST_ROUTING 发送。
- 本地生成的出站数据包先经过 NF_INET_LOCAL_OUT,再通过 NF_INET_POST_ROUTING 发送。
这些挂接点为 Netfilter 实现数据包过滤、NAT、流量控制等功能提供了核心支撑。
NF_HOOK
数据包在内核网络栈中传输时,会在某些地方调用NF_HOOK宏使用挂接函数。
- pf:协议簇,对于 IPv4 用
NFPROTO_IPV4
;对于 IPv6 用NFPROTO_IPV6
。 - hook:表示 5 个 Netfilter 挂接点之一,如
NF_INET_PRE_ROUTING
。 - skb:要处理的数据包的 SKB(Socket Buffer,套接字缓冲区)对象。
- indev:输入网络设备(接收数据包的接口)。
- outdev:输出网络设备(发送数据包的接口)。
- okfn:函数指针,指向钩子回调函数执行完毕后需调用的默认处理函数,该函数接收
skb
作为参数。
4.注册Netfilter钩子回调函数结构体
要在前面5个挂接点注册Netfilter钩子回调函数,首先需要定义一个nf_hook_ops对象,然后进行注册:
netfilter回调函数返回值如下:
1.
#define NF_DROP 0
- 含义:表示直接丢弃当前数据包,阻止其继续在网络协议栈中传输。
- 示例:
在内核模块中,若检测到数据包源 IP 属于恶意攻击源(如扫描本地端口的 IP),可通过返回NF_DROP
丢弃该数据包。static unsigned int drop_malicious_pkt(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct iphdr *iph = ip_hdr(skb); if (is_malicious_ip(iph->saddr)) { // 假设存在检测恶意 IP 的函数 printk(KERN_INFO "Dropping malicious packet\n"); return NF_DROP; } return NF_ACCEPT; }
2.
#define NF_ACCEPT 1
- 含义:允许数据包像正常流程一样继续在内核网络协议栈中传输,不进行阻断。
- 示例:
对合法的 Web 访问流量,钩子函数不做干预,返回NF_ACCEPT
让数据包继续到达传输层。static unsigned int allow_web_traffic(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct iphdr *iph = ip_hdr(skb); if (iph->dport == htons(80)) { // 目标端口为 80(HTTP) return NF_ACCEPT; // 允许访问 Web 服务的数据包通过 } return NF_DROP; }
3.
#define NF_STOLEN 2
- 含义:表示数据包不再继续沿协议栈传输,由钩子函数完全接管处理(如复制、保存数据包用于其他用途)。
- 示例:
网络监控场景中,钩子函数复制数据包到自定义缓冲区分析,不再让原数据包继续传输。static unsigned int steal_pkt_for_monitor(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct sk_buff *new_skb; new_skb = skb_clone(skb, GFP_ATOMIC); // 克隆数据包 if (new_skb) { // 对 new_skb 进行分析(如解析协议内容) monitor_packet(new_skb); return NF_STOLEN; // 原数据包不再继续传输 } return NF_ACCEPT; }
4.
#define NF_QUEUE 3
- 含义:将数据包送入用户空间队列,由用户空间程序(如
iptables
配合libnetfilter_queue
库)进一步处理。- 示例:
用户空间程序需定义回调函数处理入队数据包,实现灵活的策略(如动态过滤)。// 内核模块钩子函数 static unsigned int queue_pkt(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { return NF_QUEUE; // 数据包入队,交用户空间处理 } // 用户空间程序(简化逻辑) int main() { struct nfq_handle *h; struct nfq_q_handle *qh; // 初始化队列连接... nfq_callback *cb = &process_packet; nfq_set_callback(qh, cb); // 循环处理入队数据包... return 0; } static int process_packet(struct nfq_q_handle *qh, struct nfgenmsg *msg, struct nfq_data *data, void *user) { // 分析数据包,决定放行或丢弃(如用户空间判断后返回 ACCEPT 或 DROP) return nfq_verdict(qh, NF_ACCEPT, 0, NULL); }
5.
#define NF_REPEAT 4
- 含义:要求内核重新调用当前挂接点的钩子函数链,通常用于特殊场景(如钩子函数修改了数据包关键信息,需重新检查)。
- 示例:
钩子函数修改了数据包的目标 IP 后,希望重新执行路由判断,可返回NF_REPEAT
。static unsigned int modify_dst_ip_and_repeat(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct iphdr *iph = ip_hdr(skb); iph->daddr = new_dst_ip; // 修改目标 IP return NF_REPEAT; // 重新执行当前挂接点的钩子函数链(如重新路由判断) }
具体案例参考:Linux内核Netfilter使用实战案例分析-CSDN博客
二、连接跟踪
链接跟踪能够让内核跟踪会话,主要目标为NAT打基础。
1.连接跟踪的初始化
名为ipv4_conntrack_ops的nf_hook_ops对象数组如下:
在 Linux 内核的网络子系统中,
ipv4_conntrack_ops
结构体数组是用于配置 IPv4 连接跟踪功能的 Netfilter 钩子操作的。下面详细讲解相关内容。1. Netfilter 框架与钩子机制概述
Netfilter 是 Linux 内核中一个强大的网络数据包处理框架,它允许内核在网络数据包处理的不同阶段(钩子点)插入自定义的处理函数(钩子函数),以实现诸如数据包过滤、地址转换、连接跟踪等功能。
2.
nf_hook_ops
结构体
ipv4_conntrack_ops
数组中的元素类型是nf_hook_ops
,这是一个用于描述 Netfilter 钩子操作的结构体,其定义大致如下(不同内核版本可能略有差异):struct nf_hook_ops { struct list_head list; nf_hookfn *hook; struct module *owner; int pf; int hooknum; int priority; };
下面详细解释其主要成员:
nf_hookfn *hook
:指向实际的钩子函数的指针。当数据包到达对应的钩子点时,内核会调用这个函数来处理数据包。int pf
:指定该钩子操作适用的协议族,例如NFPROTO_IPV4
表示 IPv4 协议族。int hooknum
:指定钩子点的编号,例如NF_INET_PRE_ROUTING
、NF_INET_LOCAL_IN
等。不同的编号代表不同的网络数据包处理阶段。int priority
:指定钩子函数的优先级。当多个钩子函数注册到同一个钩子点时,内核会按照优先级顺序依次调用这些函数。3.
ipv4_conntrack_ops
数组示例以下是一个简化的
ipv4_conntrack_ops
数组示例:static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = { { .hook = ipv4_conntrack_in, .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_CONNTRACK, }, { .hook = ipv4_conntrack_local, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_CONNTRACK, }, };
4. 数组元素详细解释
第一个元素
.hook = ipv4_conntrack_in
:指定了钩子函数为ipv4_conntrack_in
。这个函数的主要作用是在数据包进入内核且还未进行路由决策之前,对 IPv4 数据包进行连接跟踪处理。它会检查数据包是否属于一个已经存在的连接,如果是,则更新连接状态;如果不是,则尝试创建一个新的连接记录。.pf = NFPROTO_IPV4
:明确该钩子操作只处理 IPv4 协议的数据包。.hooknum = NF_INET_PRE_ROUTING
:指定钩子点为NF_INET_PRE_ROUTING
,即路由前阶段。这意味着该钩子函数会在数据包进入内核后,在进行路由决策之前被调用。.priority = NF_IP_PRI_CONNTRACK
:设置钩子函数的优先级为NF_IP_PRI_CONNTRACK
。这个优先级确保连接跟踪操作在其他可能依赖连接状态的操作(如防火墙规则匹配)之前执行。第二个元素
.hook = ipv4_conntrack_local
:指定钩子函数为ipv4_conntrack_local
。该函数用于处理从本地系统发出的 IPv4 数据包的连接跟踪。.pf = NFPROTO_IPV4
:同样表示只处理 IPv4 协议的数据包。.hooknum = NF_INET_LOCAL_OUT
:指定钩子点为NF_INET_LOCAL_OUT
,即本地输出阶段。这意味着该钩子函数会在本地系统发出数据包时被调用。.priority = NF_IP_PRI_CONNTRACK
:设置优先级为NF_IP_PRI_CONNTRACK
,确保连接跟踪操作优先执行。5. 作用总结
ipv4_conntrack_ops
数组的主要作用是初始化 IPv4 连接跟踪的 Netfilter 钩子,使得内核能够在网络数据包处理的关键阶段(如路由前和本地输出阶段)对 IPv4 数据包进行连接状态的跟踪和管理。通过这种方式,内核可以为后续的网络功能(如防火墙规则匹配、NAT 转换等)提供准确的连接状态信息。
2.连接跟踪的基本元素是nf_conntrack_tuple结构
一个 nf_conntrack_tuple
是 Linux 内核中用于唯一标识一个网络连接的元组结构体,它包含了连接的源和目的地址、端口、协议等关键信息,为网络连接跟踪、防火墙规则匹配和 NAT 转换等操作提供基础依据。
它是一个相对轻量级的结构体,主要用来描述网络连接的基本元组信息。这些信息包含源地址、目的地址、源端口、目的端口以及所使用的协议(如 TCP、UDP、ICMP 等),就像是网络连接的一个 “标识标签”,能唯一确定一条网络连接。
nf_conntrack_tuple
是 Linux 内核中用于 连接跟踪(Connection Tracking) 的核心结构体,用于描述网络连接的 “流” 特征(如地址、端口、协议等),帮助内核识别网络连接的属性,为防火墙、NAT 等功能提供连接状态匹配依据。以下是关键字段解析:核心字段讲解
struct nf_conntrack_man src
- 用于标识连接的 源端信息,包含源地址、端口等核心标识数据。在连接跟踪中,通过解析该字段可确定数据包的源端特征,是识别网络流的重要依据。
联合体内的
u3
和all
u3
:存储 IPv4/IPv6 地址相关信息,适配不同协议的地址格式。all
:以__be16
类型存储通用地址数据,支持扩展其他协议的地址处理,确保结构体兼容多网络协议(如 IPv4、IPv6)的地址解析需求。
struct { __be16 port; } tcp
- 专门用于 TCP 协议,存储 TCP 连接的端口号。在 TCP 连接跟踪中,端口是识别连接的关键要素之一,结合源 / 目的地址,可唯一标识一条 TCP 连接。
union nf_conntrack_proto proto
- 存放 不同协议的特定数据(如端口、ICMP 类型 / 代码等)。例如,处理 TCP/UDP 时存储端口,处理 ICMP 时存储类型和代码,使结构体能够适配多种协议的连接跟踪需求,是协议无关性设计的核心体现。
使用场景
- 连接跟踪:内核运用
nf_conntrack_tuple
来跟踪网络连接的状态,记录连接的建立、传输和关闭过程。- 防火墙规则匹配:防火墙规则可以依据
nf_conntrack_tuple
里的信息进行匹配,从而决定是否允许数据包通过。- NAT(网络地址转换):NAT 设备在进行地址转换时,会根据
nf_conntrack_tuple
来修改数据包的源或目的地址。
3.连接跟踪的条目结构
一个 nf_conn
是 Linux 内核连接跟踪系统中,代表一条网络连接的综合管理结构体,它整合了连接的标识信息、状态、生命周期管理等关键内容,是实现防火墙、NAT 等网络功能的底层连接状态数据载体。
该结构体则更为复杂和全面,它代表了一个完整的网络连接对象。nf_conn
会包含 nf_conntrack_tuple
类型的成员,借助 nf_conntrack_tuple
来记录连接的基本标识信息。同时,nf_conn
还会记录连接的其他状态信息,像连接的当前状态(新建、已建立、关闭等)、超时时间、引用计数等。可以把 nf_conn
看作是对 nf_conntrack_tuple
的进一步扩展和封装,它不仅包含了连接的标识,还涵盖了连接的整个生命周期管理和状态信息。
重要字段
struct nf_conntrack ct_general
:
作为nf_conn
的核心成员,ct_general
整合了连接跟踪的通用信息,包括:
- 连接元组信息:通过
nf_conntrack_tuple
描述连接的源 / 目的地址、端口、协议等标识信息,用于唯一识别网络连接。- 连接状态:记录连接当前所处状态(如新建
CT_NEW
、已建立CT_ESTABLISHED
、断开CT_CLOSE
等)。- 超时管理:维护连接的超时时间,用于自动清理长时间无活动的连接,释放系统资源。
引用计数机制:
虽然代码片段未完全展示,但nf_conn
依赖引用计数(如通过nf_conntrack_get()
和nf_conntrack_put()
接口)管理结构体的生命周期。例如,哈希表引用、数据包关联引用等场景都会增减计数,确保连接资源在无引用时正确释放。
4.函数nf_contrack_in
函数概述
nf_conntrack_in
是 Linux 内核中用于 IPv4 连接跟踪的关键函数,它作为 Netfilter 钩子函数,在数据包进入内核网络栈且尚未进行路由决策时被调用。其主要功能是对输入的数据包进行连接跟踪处理,判断数据包是否属于已存在的连接,若不属于则尝试创建新的连接记录,同时更新连接状态,为后续的防火墙规则匹配、NAT 转换等网络功能提供基础。参数说明
struct sk_buff *skb
:指向sk_buff
结构体的指针,sk_buff
是 Linux 内核中用于表示网络数据包的结构体,包含了数据包的所有信息,如网络层头部、传输层头部、数据部分等。const struct nf_hook_state *state
:指向nf_hook_state
结构体的指针,该结构体包含了当前钩子点的状态信息,如协议族、钩子点编号等。内部重要流程讲解
1. 检查数据包是否已被跟踪
tmpl = nf_ct_get(skb, &ctinfo); if (tmpl || ctinfo == IP_CT_UNTRACKED) { /* Previously seen (loopback or untracked)? Ignore. */ if ((tmpl && !nf_ct_is_template(tmpl)) || ctinfo == IP_CT_UNTRACKED) { NF_CT_STAT_INC_ATOMIC(state->net, ignore); return NF_ACCEPT; } skb->_nfct = 0; }
- 功能:通过
nf_ct_get
函数尝试获取数据包对应的连接跟踪信息。如果已经存在连接跟踪信息(tmpl
不为空)或者该数据包被标记为无需跟踪(ctinfo == IP_CT_UNTRACKED
),则根据情况进行处理。若满足特定条件,则增加ignore
统计计数器,并直接接受该数据包(返回NF_ACCEPT
)。若不满足条件,则将skb
中的连接跟踪指针置为 0。- 意义:避免对已经处理过或者无需跟踪的数据包进行重复处理,提高处理效率。
2. 获取传输层协议信息
dataoff = get_l4proto(skb, skb_network_offset(skb), state->pf, &protonum); if (dataoff <= 0) { pr_debug("not prepared to track yet or error occurred\n"); NF_CT_STAT_INC_ATOMIC(state->net, error); NF_CT_STAT_INC_ATOMIC(state->net, invalid); ret = NF_ACCEPT; goto out; }
- 功能:调用
get_l4proto
函数从数据包中提取传输层协议信息,包括协议号(存储在protonum
中)和传输层头部相对于数据包起始位置的偏移量(存储在dataoff
中)。如果获取失败(dataoff <= 0
),则记录错误信息,增加error
和invalid
统计计数器,将返回值设为NF_ACCEPT
并跳转到out
标签处结束处理。- 意义:为后续根据不同的传输层协议进行相应的连接跟踪处理做准备。
3. 处理 ICMP 数据包
if (protonum == IPPROTO_ICMP || protonum == IPPROTO_ICMPV6) { ret = nf_conntrack_handle_icmp(tmpl, skb, dataoff, protonum, state); if (ret <= 0) { ret = -ret; goto out; } /* ICMP[v6] protocol trackers may assign one conntrack. */ if (skb->_nfct) goto out; }
- 功能:如果数据包的协议号是 ICMP(
IPPROTO_ICMP
)或 ICMPv6(IPPROTO_ICMPV6
),则调用nf_conntrack_handle_icmp
函数对 ICMP 数据包进行特殊处理。根据该函数的返回值进行相应操作,如果返回值小于等于 0,则取其相反数作为最终返回值并跳转到out
标签处;如果skb
中已经有连接跟踪信息,则直接跳转到out
标签处。- 意义:ICMP 协议有其特殊的工作机制,需要专门的处理函数来进行连接跟踪,确保 ICMP 数据包的连接状态能被正确跟踪。
4. 解析并处理正常连接
repeat: ret = resolve_normal_ct(tmpl, skb, dataoff, protonum, state); if (ret < 0) { /* Too stressed to deal. */ NF_CT_STAT_INC_ATOMIC(state->net, drop); ret = NF_DROP; goto out; } ct = nf_ct_get(skb, &ctinfo); if (!ct) { /* Not valid part of a connection */ NF_CT_STAT_INC_ATOMIC(state->net, invalid); ret = NF_ACCEPT; goto out; } ret = nf_conntrack_handle_packet(ct, skb, dataoff, ctinfo, state); if (ret <= 0) { /* Invalid: inverse of the return code tells * the netfilter core what to do */ pr_debug("nf_conntrack_in: Can't track with proto module\n"); nf_conntrack_put(&ct->ct_general); skb->_nfct = 0; NF_CT_STAT_INC_ATOMIC(state->net, invalid); if (ret == -NF_DROP) NF_CT_STAT_INC_ATOMIC(state->net, drop); /* Special case: TCP tracker reports an attempt to reopen a * closed/aborted connection. We have to go back and create a * fresh conntrack. */ if (ret == -NF_REPEAT) goto repeat; ret = -ret; goto out; }
- 功能:
- 调用
resolve_normal_ct
函数尝试解析并创建正常的连接跟踪信息。如果返回值小于 0,表示系统压力过大无法处理,增加drop
统计计数器,将返回值设为NF_DROP
并跳转到out
标签处。- 通过
nf_ct_get
函数获取数据包对应的连接跟踪结构体ct
。如果获取失败,说明该数据包不属于一个有效的连接,增加invalid
统计计数器,将返回值设为NF_ACCEPT
并跳转到out
标签处。- 调用
nf_conntrack_handle_packet
函数对数据包进行进一步处理。如果返回值小于等于 0,表示处理失败,记录错误信息,释放连接跟踪资源,增加invalid
和drop
统计计数器。如果返回值为-NF_REPEAT
,则跳转到repeat
标签处重新尝试创建连接跟踪信息。- 意义:这是处理非 ICMP 数据包的核心流程,负责解析、创建和处理正常的连接跟踪信息,确保数据包所属的连接能被正确跟踪和管理。
5.网络地址转换NAT
网络地址转换(NAT)模块主要要用于处理IP地址转换或端口操纵。NAT最常见的用途之一是:让局域网中的一组使用私有ip地址的主机能够通过内部网关访问Internet,为此可以设置NAT规则。
一、NAT 基础概念讲解
1. NAT 类型
- 静态 NAT:建立私有 IP 与公共 IP 的固定映射关系,一对一永久绑定。常用于需要外部主动访问内部固定服务的场景(如内部服务器对外提供服务)。
- 动态地址 NAT:维护一个公共 IP 地址池,当内部主机访问外部网络时,从地址池中动态分配公共 IP 进行转换,释放后可重新分配给其他主机。
- 网络地址端口转换(NAPT):将内部多个私有 IP 映射到外部网络的 同一个公共 IP 的不同端口,通过端口区分不同连接,解决公共 IP 稀缺问题,是家庭路由器最常用的 NAT 方式。
2. NAT 主要功能
- 数据伪装:修改 IP 包的源 / 目的地址,隐藏内部网络结构,实现私有网络与公共网络的通信。
- 负载均衡:通过 NAT 网关将流量分配到多个内部服务器,提升服务处理能力。
- 端口转发:将外部对网关某端口的访问转发到内部特定主机的指定端口,实现内部服务对外暴露。
- 透明代理:对用户透明,自动处理地址转换,无需终端用户配置。
3. NAT 工作原理
当私有网络主机与公共网络主机通信时,IP 包经过 NAT 网关,网关修改 IP 包的源 IP(出网时,私有 IP → 公共 IP)或目的 IP(入网时,公共 IP → 私有 IP),使内部网络借助少量公共 IP 实现与外部通信,同时隐藏内部网络拓扑。
二、
nf_nat_ipv4_ops
结构体数组解析
nf_nat_ipv4_ops
是 Linux 内核中用于 IPv4 NAT 功能的 Netfilter 钩子操作数组,通过在不同网络处理阶段挂载钩子函数,实现 NAT 地址转换。以下是数组元素详解:1. 过滤数据包前修改目标地址(
NF_INET_PRE_ROUTING
){ .hook = iptable_nat_ipv4_in, .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_NAT_DST, },
- 作用:在数据包进入内核且未路由前,处理 目的地址转换(DNAT)。例如,外部访问网关某端口时,通过此钩子将目的 IP 转换为内部服务器 IP。
- 关键字段:
hook
:绑定钩子函数iptable_nat_ipv4_in
,执行 DNAT 逻辑。hooknum
:NF_INET_PRE_ROUTING
钩子点,确保在路由决策前修改目的地址。2. 过滤数据包后修改源地址(
NF_INET_POST_ROUTING
){ .hook = iptable_nat_ipv4_out, .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_NAT_SRC, },
- 作用:在数据包即将离开内核网络栈时,处理 源地址转换(SNAT)。例如,内部主机访问外部网络,通过此钩子将源 IP 替换为网关公共 IP。
- 关键字段:
hook
:钩子函数iptable_nat_ipv4_out
负责 SNAT 操作。hooknum
:NF_INET_POST_ROUTING
钩子点,保证在数据包路由后、发送前修改源地址。3. 本地输出前修改目标地址(
NF_INET_LOCAL_OUT
){ .hook = iptable_nat_ipv4_local_fn, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_NAT_DST, },
- 作用:处理本地生成的数据包(如本地程序发起的网络请求)的目的地址转换,确保本地流量按 NAT 规则正确转换目标地址。
4. 本地输入后修改源地址(
NF_INET_LOCAL_IN
){ .hook = iptable_nat_ipv4_fn, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP_PRI_NAT_SRC, },
- 作用:对进入本地的数据包(如外部响应本地请求的数据包)进行源地址转换,确保 NAT 转换的双向一致性。
注册数组nf_nat_ipv4_ops的工作有iptable_nat_init()来完成。
NAT钩子回调函数。