58、深入解析Linux网络过滤、防火墙及软中断机制

深入解析Linux网络过滤、防火墙及软中断机制

1. IP过滤与防火墙

在进行分支操作前,需要在回溯指针中指向当前链的下一个条目(第353行)。这是为了应对分支链中没有规则匹配的情况,此时需要从当前链的下一个条目开始匹配。同时,由于回溯指针已被修改,需要将当前链的当前回溯指针偏移量存储在下一个条目的 comefrom 字段中(第350行),之后开始遍历新的分支链。

当目标为非标准目标时,会为该目标设置一个目标回调例程,并在第364行调用。目标的返回值可以是最终裁决结果或 IPT_CONTINUE 。若是前者,将带着裁决结果从例程返回;否则,继续处理链中的下一个条目。

1.1 IPT_MATCH_ITERATE

该宏用于遍历钩子条目的特定协议规则列表。这些匹配规则位于 ipt_entry 对象的末尾,目标则位于特定协议规则列表的末尾。从偏移量(即 ipt_entry 对象的大小)处开始访问第一条规则(第305行),在每次迭代中,通过加上当前规则的大小来计算下一条规则条目的偏移量(第307行)。循环迭代直至到达钩子条目目标的起始位置(第306行)。对于每条规则,使用函数指针在第310行处理过滤规则。若当前规则匹配成功,则继续匹配下一条规则;否则,在第一次匹配失败时返回(第311行)。

2. 网络过滤框架与防火墙实现

网络过滤框架用于在Linux中实现防火墙,同时还借助网络过滤钩子来实现IP栈的各种扩展,如IP安全、连接跟踪、IP伪装、NAT、重定向等。

网络过滤钩子的入口点是 NF_HOOK 宏。Linux 2.4内核的TCP/IP栈为上下栈都实现了网络过滤钩子条目,具体如下:
| 类型 | 描述 |
| ---- | ---- |
| 传出数据包钩子 | - NF_IP_LOCAL_OUT :应用于传出数据包的过滤规则
- NF_IP_POST_ROUTING :实现IP伪装、IP安全等 |
| 传入数据包钩子 | - NF_IP_LOCAL_IN :应用于传入数据包的过滤规则,在内核为本地传递路由数据包后应用
- NF_IP_PRE_ROUTING :数据包进入IP层后,在路由之前应用的钩子,IP安全、IP伪装、NAT等可能需要此钩子 |

Compat 提供了一个网络过滤框架,该框架下只能向内核注册一个防火墙。使用 register_firewall() 函数通过 Compat 框架注册 firewall_ops 类型的对象, Ipchain 就是为配合 Compat 框架而设计的。

Iptables Compat 框架不兼容,使用 nf_register_hook() 函数注册网络过滤钩子,它会为特定的钩子类型注册一个 nf_hook_ops 类型的对象,已注册的钩子会链接到全局哈希表 nf_hooks 中。同时,提供了 ipt_register_table() 接口来注册 Ipchain 表,它会将 ipt_table 类型的对象注册到全局列表 ipt_tables 中。与 Ipchains 相比, Iptable 速度更快,且具有许多高级功能,它为每个CPU维护过滤表,由于缓存局部性,性能得到了显著提升。

3. 网络软中断概述

在Linux系统中,中断处理被分为两部分。小部分工作在中断处理程序中完成,大部分或下半部分工作则被推迟到安全的时间以最小的延迟进行处理,这样做是为了避免较长的中断延迟。在中断处理程序执行期间,中断会被禁用,处理完成后再启用。如果在中断处理程序中花费过多时间,中断延迟将会很高。

早期的Linux内核版本(2.2及以下)实现了底半(bottom - half)框架来处理大部分中断处理工作。该框架在单CPU机器上运行良好,因为它会持有一个大的底半锁来执行底半操作。但在对称多处理(SMP)机器上,由于执行底半操作需要持有锁,该框架会对每个CPU的底半执行进行串行化访问,无法实现良好的扩展性。

为了提高底半执行的可扩展性,对框架进行了修改,新框架被称为软中断(SoftIRQ)。软中断设计为可以在多个CPU上并行运行,同一个软中断也可以同时在不同的CPU上并行执行。由于软中断操作的数据是按CPU维护的,因此每个CPU可以独立地触发软中断。

并非每个中断事件都有单独的软中断。网络方面有两个软中断,分别用于发送(Tx)和接收(Rx)中断。其他中断事件将其底半操作注册为高优先级或低优先级的小任务(tasklet),高、低优先级各有一个软中断对应一个小任务。小任务的特点是同一时间只能在一个CPU上执行。

4. 为何需要网络软中断以及如何触发它们
4.1 传输情况

在具有两个CPU的SMP机器上,当需要从同一接口并行传输两个帧时,会出现性能问题。一个内核控制路径获取设备锁并传输帧后返回,而另一个也需要在同一传出接口传输帧的内核控制路径,要么等待设备锁释放,要么在循环中不断尝试获取锁。如果因为其他CPU正在传输帧而返回,内核会丢弃数据包,上层需要重新构建并重新传输整个数据包;如果在循环中等待设备锁释放,会浪费另一个CPU的周期。在SMP架构下,这种情况会严重影响系统性能,在中等网络流量下会显著降低系统速度。

为了解决这个问题,可以将待传输的帧排队,并将帧传输的处理推迟到不久后的某个时间,如下图所示:

graph LR
    A[数据包待传输] --> B[加入传输队列]
    B --> C[触发NET_TX_SOFTIRQ]
    C --> D[软中断处理传输]
4.2 接收情况

以单个接口为例,当接收到一个帧时,中断处理程序需要完成很多工作,如从设备DMA缓冲区中提取帧、确定下一个协议层、在每个协议层处理数据包,最后将数据或控制消息传递到套接字层,这些操作会花费大量时间。由于长时间停留在中断处理程序中会增加网络接口的延迟,在此期间接收到的所有帧都会被丢弃,因此中断处理程序应尽可能快速地完成最少的工作。

可以在中断处理程序中仅将帧从设备DMA缓冲区提取到内核缓冲区并排队等待后续处理,接收到的帧可以由协议层安排后续处理,然后快速从中断返回,如下图所示:

graph LR
    A[接收到帧] --> B[从DMA缓冲区提取到内核缓冲区]
    B --> C[加入接收队列]
    C --> D[触发NET_RX_SOFTIRQ]
    D --> E[软中断处理接收]

综上所述,在传输和接收两种情况下都需要对帧进行延迟处理,这种延迟处理通过触发网络软中断来安排数据包的处理。传输和接收有相互独立的软中断,其概念与内核2.4之前的底半操作类似,但底半操作在多个CPU之间是串行执行的,而软中断可以在多个CPU上并行运行,无需获取全局锁,从而在SMP架构上显著提高了网络性能。

网络软中断可以通过调用 raise_softirq() 函数来触发。系统为每个注册的软中断分配一个位,发送软中断使用 NET_TX_SOFTIRQ ,接收软中断使用 NET_RX_SOFTIRQ 。软中断是按CPU划分的,不同的软中断可以在不同的CPU上独立调度。

调用 raise_softirq() 时传入对应软中断的位,由于需要为当前CPU触发中断,所以调用 cpu_raise_softirq() cpu_raise_softirq() 实际上借助 __cpu_raise_softirq() 宏来触发软中断,该宏会设置CPU特定结构字段中对应软中断的位。通过调用 softirq_pending() 函数访问CPU特定字段, softirq_pending() 借助 __IRQ_STAT() 宏访问 irq_cpustat_t 结构的 __softirq_pending 字段。系统为每个CPU维护一个 irq_cpustat_t 结构的数组 irq_stat ,最终是在当前CPU对应的 irq_stat[CPU].__softirq_pending 字段中设置对应软中断的位。

irq_cpustat_t 结构用于保存任何CPU的状态信息和进行统计,其字段如下:
| 字段名 | 描述 |
| ---- | ---- |
| __softirq_pending | 保存当前CPU上任何待处理软中断的信息,该字段中的每个位对应一个特定的中断。若该字段为正值,则表示有软中断待处理,需要检查位字段。 |
| __local_irq_count | 记录该CPU上触发的中断数量。 |
| __local_bh_count | 记录底半操作执行的次数。 |
| __syscall_count | 记录该CPU上进行的系统调用次数。 |
| __ksoftirqd_task | 保存负责处理当前CPU上软中断的 ksoftirqd 守护进程的 task_struct 结构的指针。 |

如果从中断或底半操作中触发软中断,则无需唤醒处理该CPU软中断的守护进程;否则,需要在 cpu_raise_softirq() 中唤醒它。

5. 软中断的处理时机和方式

软中断在 do_softirq() 函数中进行处理,该函数在内核的多个位置被调用。如果从中断模式调用 do_softirq() ,函数会直接返回(第68 - 69行)。因为若在中断处理程序中调用,会违背通过软中断进行延迟处理的初衷,导致中断延迟过高;若在底半处理程序中调用,会造成递归调用,可能导致内核栈溢出。

do_softirq() 函数中,使用 softirq_pending() 宏检查当前CPU上是否有待处理的软中断(第73行)。若有,将当前活跃软中断对应的位模式复制到本地变量 pending 中,并将 irq_stat[cpu].__softirq_pending 置为零,同时初始化 mask pending 的补码。然后开始处理活跃软中断,通过左移 pending 一位的方式遍历其所有位,直到 pending 为零(第92行)。

处理完所有活跃软中断后,再次检查在此期间是否有新的软中断被触发(第97行)。由于 mask 中对应已处理软中断的位都被置为零,将 mask pending 进行按位与操作,若结果为正数,则表示有未处理的软中断被触发,再次进入处理循环(第88 - 93行);若 pending 大于零但 mask pending 按位与结果为零,则表示触发的是刚刚处理过的软中断,此时唤醒该CPU的 softirqd 守护进程,让其在稍后处理这些软中断,以保证用户空间应用程序能获得适当的CPU资源,因为内核是非抢占式的,软中断处理时间比中断长,若中断频率较高,会在软中断处理上花费过多时间。

在操作 irq_stat[cpu].__softirq_pending 时,通过调用 local_irq_save() local_irq_disable() 函数禁用本地CPU的中断(第71和95行),操作完成后再调用 local_irq_enable() local_irq_restore() 函数启用中断(第84和108行),这是因为 irq_stat[cpu].__softirq_pending 会在中断处理程序中被修改。

在处理软中断时,通过调用 local_bh_disable() 函数禁用底半操作(第79行),这会使 irq_stat[cpu].__local_bh_count 加一,目的是防止其他内核控制路径在当前CPU上处理软中断。例如,一个内核控制路径正在执行 do_softirq() 时,若有中断触发,在中断返回时可能会再次调用 do_softirq() ,禁用底半操作可以避免这种情况。

do_softirq() 函数的调用时机如下:
- do_IRQ() 函数返回时 :从中断例程返回时,有可能触发了软中断,因为现在大部分中断工作都在底半操作(即软中断)中完成。但需要检查本地CPU上的软中断是否被禁用,若被禁用,则即使触发了软中断也不会处理。
- 启用本地底半操作时 :在某些情况下,为了避免在不禁用中断的情况下操作被软中断访问的数据时出现问题,会禁用软中断,此时只是增加本地底半计数器的值,允许本地CPU接收中断。这样做可以避免在SMP架构上出现死锁,以及防止单CPU机器冻结。当内核中的关键代码执行完成后,启用底半操作,此时会减少本地底半计数器的值,若计数器变为零,则调用 do_softirq() 函数处理待处理的软中断,从而实现底半操作的嵌套禁用,最外层启用软中断时会触发待处理软中断的处理。例如,在调用 lock_sock() 函数锁定套接字时会禁用底半操作,因为 tcp_v4_rcv() 函数在底半操作中运行,也需要获取套接字锁( bh_lock_sock() )。

以下是 do_softirq() 函数处理软中断的流程图:

graph TD
    A[调用do_softirq()] --> B{是否从中断模式调用}
    B -- 是 --> C[返回]
    B -- 否 --> D{是否有软中断待处理}
    D -- 否 --> E[结束]
    D -- 是 --> F[初始化pending和mask]
    F --> G[设置irq_stat[cpu].__softirq_pending为零]
    G --> H[处理活跃软中断]
    H --> I{是否有新软中断触发}
    I -- 是 --> J{是否为未处理的软中断}
    J -- 是 --> H
    J -- 否 --> K[唤醒softirqd处理]
    I -- 否 --> E
6. 软中断的注册

每个软中断与 irq_stat[cpu].__softirq_pending 中的一个特定位相关联。在当前的设计中,使用 struct softirq_action 结构体来表示软中断,该结构体有两个字段: action data action 是指向软中断处理程序的函数指针, data 保存传递给处理程序 action 的参数。

系统有一个名为 softirq_vec struct softirq_action 数组,数组中的每个元素对应一个软中断。截至内核2.4.20版本,只有四个软中断,数组索引对应每个软中断关联的位编号。例如, TASKLET_SOFTIRQ 被分配到第三位,在 softirq_vec 数组中对应第四个元素。这种设计使得在处理软中断时无需搜索软中断处理程序,只需遍历32位变量 pending 中的所有位,每次迭代将 pending 左移一位并检查该位是否被设置,若设置则执行对应的软中断处理程序。

总结

本文深入探讨了Linux系统中的IP过滤与防火墙机制,以及网络软中断的相关内容。在IP过滤和防火墙方面,介绍了分支链规则匹配的处理方式、 IPT_MATCH_ITERATE 宏的工作原理,以及不同网络过滤钩子的作用和特点,还对比了 Compat 框架下的 Ipchain Iptables 的差异。在网络软中断方面,阐述了引入软中断的原因,包括解决传输和接收过程中的性能问题,详细说明了软中断的触发方式、处理时机和方式,以及软中断的注册机制。

通过合理利用网络过滤框架和软中断机制,可以有效提高Linux系统的网络性能和安全性,特别是在SMP架构下,软中断的并行执行能力能够充分发挥多CPU的优势,避免了传统底半框架在扩展性方面的不足。在实际应用中,开发者可以根据具体需求,灵活运用这些机制来优化系统的网络处理能力。

需求响应动态冰蓄冷系统与需求响应策略的优化研究(Matlab代码实现)内容概要:本文围绕需求响应动态冰蓄冷系统及其优化策略展开研究,结合Matlab代码实现,探讨了在电力需求侧管理背景下,冰蓄冷系统如何通过优化运行策略参与需求响应,以实现削峰填谷、降低用电成本和提升能源利用效率的目标。研究内容包括系统建模、负荷预测、优化算法设计(如智能优化算法)以及多场景仿真验证,重点分析不同需求响应机制下系统的经济性和运行特性,并通过Matlab编程实现模型求解与结果可视化,为实际工程应用提供理论支持和技术路径。; 适合人群:具备一定电力系统、能源工程或自动化背景的研究生、科研人员及从事综合能源系统优化工作的工程师;熟悉Matlab编程且对需求响应、储能优化等领域感兴趣的技术人员。; 使用场景及目标:①用于高校科研中关于冰蓄冷系统与需求响应协同优化的课题研究;②支撑企业开展楼宇能源管理系统、智慧园区调度平台的设计与仿真;③为政策制定者评估需求响应措施的有效性提供量化分析工具。; 阅读建议:建议读者结合文中Matlab代码逐段理解模型构建与算法实现过程,重点关注目标函数设定、约束条件处理及优化结果分析部分,同时可拓展应用其他智能算法进行对比实验,加深对系统优化机制的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值