网桥调用IP层netfilter的HOOK函数

2023-12-19 10:58

本文主要是介绍网桥调用IP层netfilter的HOOK函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Linux中的网桥属于二层转发设备,可利用ebtables工具根据数据包的ethernet头等信息,配置规则,如下,将目的MAC地址为00:01:02:03:04:05的数据包,转发到目的MAC地址为00:06:07:08:09:0a的主机上:

ebtables -t nat -A PREROUTING -d 00:01:02:03:04:05 -j dnat --to-destination 00:06:07:08:09:0a

但是,网桥不仅可以支持ebtables配置的二层规则,也可支持iptables配置的三层规则。可通过PROC文件系统实现动态控制,控制文件位于/proc/sys/net/bridge目录下的bridge-nf-call-iptables和bridge-nf-call-ip6tables。


初始化

内核中网桥代码,通过调用br_netfilter_init函数,注册二层netfilter的三个hook点,分别是NF_BR_PRE_ROUTING,NF_BR_FORWARD和NF_BR_POST_ROUTING。这三个hook点,分别对应了三层netfilter的三个相同的hook点(准确说,是三层的6个hook点,IPv4与IPv6各三个点),这样在数据包经过网桥的这三个hook点时,就可以调用三层的iptables规则了。

三层netfilter的另外两个hook,NF_INET_LOCAL_IN和NF_INET_LOCAL_OUT,为发往主机和从主机发出的数据包所经过的hook,其本身自会经过三层协议栈的处理而被调用到,不需要在二层网桥上调用。


static struct nf_hook_ops br_nf_ops[] __read_mostly = {{.hook = br_nf_pre_routing,.pf = NFPROTO_BRIDGE,.hooknum = NF_BR_PRE_ROUTING,},{.hook = br_nf_forward_ip,.pf = NFPROTO_BRIDGE,.hooknum = NF_BR_FORWARD,},{.hook = br_nf_post_routing,.pf = NFPROTO_BRIDGE,.hooknum = NF_BR_POST_ROUTING,},
}static int __init br_netfilter_init(void)
{ret = nf_register_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
}


网桥PRE_ROUTING HOOK

数据包在进入网桥(br_handle_frame)后,调用其NF_BR_PRE_ROUTING hook点上注册的函数。其中,会调用到之前注册的br_nf_pre_routing函数。其首先检查proc文件bridge-nf-call-iptables是否允许数据包到达三层netfilter的hook点。之后调用setup_pre_routing函数准备调用NF_INET_PRE_ROUTING hook点注册的函数,两点准备工作:第一skb中pkt_type需要由PACKET_OTHERHOST修改为PACKET_HOST,否则将会被三层丢弃;第二skb中的dev由物理口设备修改为其父设备(网桥设备),因为像REDIRECT等的iptables规则需要使用设备的IP地址(子设备无三层地址),所以作此修改。注意,最后在br_nf_pre_routing_finish函数中,会将此两项修改还原回去。

IPv6相关的NF_INET_PRE_ROUTING调用也在br_nf_pre_routing函数中处理,此处略去。


struct net_device *setup_pre_routing(struct sk_buff *skb)
{if (skb->pkt_type == PACKET_OTHERHOST) {skb->pkt_type = PACKET_HOST;nf_bridge->pkt_otherhost = true;}nf_bridge->physindev = skb->dev;skb->dev = brnf_get_logical_dev(skb, skb->dev);
}static unsigned int br_nf_pre_routing(...)
{if (!brnf_call_iptables && !br->nf_call_iptables)return NF_ACCEPT;if (!setup_pre_routing(skb))return NF_DROP;NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, state->sk, skb, skb->dev, NULL,br_nf_pre_routing_finish);
}


网桥转发HOOK


在网桥转发(NF_BR_FORWARD)hook点上,除了要修改skb的pkt_type到PACKET_HOST外,还要在调用三层NF_INET_FORWARD hook点函数之前,将入口设备和出口设备修改为对应的父设备(网桥设备)。skb成员dev中指向的为真实的出口设备(非网桥)。最后br_nf_forward_finish函数会还原skb->pkt_type的值。

另外,只有PROC文件bridge-nf-call-iptables为真,nf_bridge才会在br_nf_pre_routing函数中分配,此时检查nf_bridge就可以了。


static unsigned int br_nf_forward_ip(...)
{if (!skb->nf_bridge)return NF_ACCEPT;parent = bridge_parent(state->out);if (!parent)return NF_DROP;if (skb->pkt_type == PACKET_OTHERHOST) {skb->pkt_type = PACKET_HOST;nf_bridge->pkt_otherhost = true;}NF_HOOK(pf, NF_INET_FORWARD, NULL, skb,brnf_get_logical_dev(skb, state->in), parent, br_nf_forward_finish);
}


网桥POST_ROUTING


网桥的NF_BR_POST_ROUTING hook点调用三层的NF_INET_POST_ROUTING hook点,但与前两个HOOK点不同,主机本身发出的报文也会调用NF_INET_POST_ROUTING hook点的函数,所以需要再此判断physoutdev是否为空,为空说明主机已经在IP三层处理中调用了此hook点,直接返回NF_ACCEPT。


static unsigned int br_nf_post_routing(...)
{if (!nf_bridge || !nf_bridge->physoutdev)return NF_ACCEPT;if (skb->pkt_type == PACKET_OTHERHOST) {skb->pkt_type = PACKET_HOST;nf_bridge->pkt_otherhost = true;}NF_HOOK(pf, NF_INET_POST_ROUTING, state->sk, skb,NULL, realoutdev, br_nf_dev_queue_xmit);
}

内核版本

linux-3.10.0



这篇关于网桥调用IP层netfilter的HOOK函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

Python中使用正则表达式精准匹配IP地址的案例

《Python中使用正则表达式精准匹配IP地址的案例》Python的正则表达式(re模块)是完成这个任务的利器,但你知道怎么写才能准确匹配各种合法的IP地址吗,今天我们就来详细探讨这个问题,感兴趣的朋... 目录为什么需要IP正则表达式?IP地址的基本结构基础正则表达式写法精确匹配0-255的数字验证IP地

MySQL高级查询之JOIN、子查询、窗口函数实际案例

《MySQL高级查询之JOIN、子查询、窗口函数实际案例》:本文主要介绍MySQL高级查询之JOIN、子查询、窗口函数实际案例的相关资料,JOIN用于多表关联查询,子查询用于数据筛选和过滤,窗口函... 目录前言1. JOIN(连接查询)1.1 内连接(INNER JOIN)1.2 左连接(LEFT JOI

MySQL中FIND_IN_SET函数与INSTR函数用法解析

《MySQL中FIND_IN_SET函数与INSTR函数用法解析》:本文主要介绍MySQL中FIND_IN_SET函数与INSTR函数用法解析,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一... 目录一、功能定义与语法1、FIND_IN_SET函数2、INSTR函数二、本质区别对比三、实际场景案例分

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

C语言函数递归实际应用举例详解

《C语言函数递归实际应用举例详解》程序调用自身的编程技巧称为递归,递归做为一种算法在程序设计语言中广泛应用,:本文主要介绍C语言函数递归实际应用举例的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录前言一、递归的概念与思想二、递归的限制条件 三、递归的实际应用举例(一)求 n 的阶乘(二)顺序打印

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

Kotlin 作用域函数apply、let、run、with、also使用指南

《Kotlin作用域函数apply、let、run、with、also使用指南》在Kotlin开发中,作用域函数(ScopeFunctions)是一组能让代码更简洁、更函数式的高阶函数,本文将... 目录一、引言:为什么需要作用域函数?二、作用域函China编程数详解1. apply:对象配置的 “流式构建器”最

在C#中调用Python代码的两种实现方式

《在C#中调用Python代码的两种实现方式》:本文主要介绍在C#中调用Python代码的两种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#调用python代码的方式1. 使用 Python.NET2. 使用外部进程调用 Python 脚本总结C#调