网桥调用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

相关文章

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数

函数式编程思想

我们经常会用到各种各样的编程思想,例如面向过程、面向对象。不过笔者在该博客简单介绍一下函数式编程思想. 如果对函数式编程思想进行概括,就是f(x) = na(x) , y=uf(x)…至于其他的编程思想,可能是y=a(x)+b(x)+c(x)…,也有可能是y=f(x)=f(x)/a + f(x)/b+f(x)/c… 面向过程的指令式编程 面向过程,简单理解就是y=a(x)+b(x)+c(x)

【LabVIEW学习篇 - 21】:DLL与API的调用

文章目录 DLL与API调用DLLAPIDLL的调用 DLL与API调用 LabVIEW虽然已经足够强大,但不同的语言在不同领域都有着自己的优势,为了强强联合,LabVIEW提供了强大的外部程序接口能力,包括DLL、CIN(C语言接口)、ActiveX、.NET、MATLAB等等。通过DLL可以使用户很方便地调用C、C++、C#、VB等编程语言写的程序以及windows自带的大

利用matlab bar函数绘制较为复杂的柱状图,并在图中进行适当标注

示例代码和结果如下:小疑问:如何自动选择合适的坐标位置对柱状图的数值大小进行标注?😂 clear; close all;x = 1:3;aa=[28.6321521955954 26.2453660695847 21.69102348512086.93747104431360 6.25442246899816 3.342835958564245.51365061796319 4.87

OpenCV结构分析与形状描述符(11)椭圆拟合函数fitEllipse()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C++11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆,该椭圆在最小二乘意义上最好地拟合一组2D点。它返回一个内切椭圆的旋转矩形。使用了由[90]描述的第一个算法。开发者应该注意,由于数据点靠近包含的 Mat 元素的边界,返回的椭圆/旋转矩形数据

2024.9.8 TCP/IP协议学习笔记

1.所谓的层就是数据交换的深度,电脑点对点就是单层,物理层,加上集线器还是物理层,加上交换机就变成链路层了,有地址表,路由器就到了第三层网络层,每个端口都有一个mac地址 2.A 给 C 发数据包,怎么知道是否要通过路由器转发呢?答案:子网 3.将源 IP 与目的 IP 分别同这个子网掩码进行与运算****,相等则是在一个子网,不相等就是在不同子网 4.A 如何知道,哪个设备是路由器?答案:在 A

Unity3D 运动之Move函数和translate

CharacterController.Move 移动 function Move (motion : Vector3) : CollisionFlags Description描述 A more complex move function taking absolute movement deltas. 一个更加复杂的运动函数,每次都绝对运动。 Attempts to

string字符会调用new分配堆内存吗

gcc的string默认大小是32个字节,字符串小于等于15直接保存在栈上,超过之后才会使用new分配。