我是靠谱客的博主 漂亮爆米花,最近开发中收集的这篇文章主要介绍OpenWrt内核模块开发(二)-通过linux netfilter框架拦截协议栈报文,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

    • 功能简介
    • netfilter简介
    • 网卡转发包流程
    • netfilter框架流程
    • netfilter钩子注册
    • netfilter外部接口
      • 接口列表
      • 注册结构体定义
      • 结构体参数说明
      • hooknum可配置的值
      • priority可配置的值
    • 注册实例
      • 钩子实例
      • ops注册实例
    • 编译运行
    • 测试结果
      • 作者简介
      • 源码和文档

功能简介

通过内核模块的方式向netfilter框架注册钩子函数,实现简单的数据包转发调试,当有数据包经过Linux协议栈时,打印相关信息。目的在于熟悉netfilter框架的基础api,为后续开发防火墙等功能做准备。

netfilter简介

netfilter是由Rusty Russell提出的Linux 2.4内核防火墙框架,该框架既简洁又灵活,可实现安全策略应用中的许多功能,如数据包过滤、数据包处理、地址伪装、透明代理、动态网络地址转换(Network Address Translation,NAT),以及基于用户及媒体访问控制(Media Access Control,MAC)地址的过滤和基于状态的过滤、包速率限制等。

网卡转发包流程

以下为网口收包到转发的流程,NF_HOOK位置为netfilter钩子
pkt-flow-dxt1

netfilter框架流程

netfilter是Linux协议栈的一个子系统,用于数据报文的过滤。在路由器设备中,数据报文的来源和去向有很多场景。比如转发数据、本地发出的数据、访问设备本身的数据等,可以通过下图简单了解基本流程。

netfilter

netfilter钩子注册

netfilter为通用的框架,Linux系统已经内建了常用的钩子。netfilter和iptables命令时密切相关的,iptables命令的最终实现是基于netfilter,比如filter表、nat表、mangle表的各个链的实现,都在netfilter源码中找到具体实现,这里就不一一讲解。

netfilter外部接口

我们可以通过以下接口注册和卸载钩子函数,在钩子函数中可以将数据报文解析出来进行处理,比如过滤。

接口列表

注册一个钩子函数
int nf_register_net_hook(struct net *net, const struct nf_hook_ops ops);
void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops ops);
注册多个钩子函数
int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg,unsigned int n);
void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg,unsigned int n);

注册结构体定义

struct nf_hook_ops {
/* User fills in from here down. */
nf_hookfn
*hook;
struct net_device	*dev;
void
*priv;
u_int8_t
pf;
unsigned int
hooknum;
/* Hooks are ordered in ascending priority. */
int
priority;
};

结构体参数说明

参数类型说明
hooknf_hookfn钩子函数
pfu_int8_t协议,默认采用PF_INET即可
hooknumunsigned int钩子点, NF_INET_FORWARD、NF_IP_LOCAL_IN 等
priorityint优先级,值越小,优先级越高,可为负数

hooknum可配置的值

hooknum说明
NF_IP_PRE_ROUTING0在路由之前的数据,无论是转发还是到本地的数据都会经过
NF_IP_LOCAL_IN1访问设备的数据
NF_INET_FORWARD2转发的数据
NF_IP_LOCAL_OUT3本地进程发出的数据
NF_IP_POST_ROUTING4通过设备出去的数据,包括转发和从本地进程发出的数据,可以理解为离开设备的数据都要经过POST_ROUTING

priority可配置的值

priority可以配置任意整数的,因为系统有默认的钩子,定义了一些参考值,我们开发过程中一般在系统priority的基础上进行加减操作,用于标记自己加入钩子的位置
以下为系统定义的优先级

priority说明
NF_IP_PRI_FIRSTINT_MIN-
NF_IP_PRI_RAW_BEFORE_DEFRAG-450-
NF_IP_PRI_CONNTRACK_DEFRAG-400-
NF_IP_PRI_RAW-300-
NF_IP_PRI_SELINUX_FIRST-225-
NF_IP_PRI_CONNTRACK-200-
NF_IP_PRI_MANGLE-150-
NF_IP_PRI_NAT_DST-100-
NF_IP_PRI_FILTER0-
NF_IP_PRI_SECURITY50-
NF_IP_PRI_NAT_SRC100-
NF_IP_PRI_SELINUX_LAST225-
NF_IP_PRI_CONNTRACK_HELPER300-
NF_IP_PRI_CONNTRACK_CONFIRMINT_MAX-
NF_IP_PRI_LASTINT_MAX-

注册实例

下面我们写一个实例,通过打印数据包信息,看看数据包的走向
代码中,我们定义hook_ops数组,包含3个钩子实体 forward_hook、forward_hook2、local_in_hook,其中前两个为forward钩子,但挂载的优先级不一样,我们可以通过打印看到,优先级情况,而local_in_hook为访问设备本身的数据,通过ip地址我们可以很方便查看是否是我们想要的结果。

钩子实例

static u_int32_t forward_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
// ct为连接跟踪指针,标记一条数据流,非常重要的数据结构,贯穿整个netfilter框架,这里有些变量没有用到,后面的具体开发中会用
struct nf_conn *ct = (struct nf_conn *)skb->_nfct;
struct udphdr *udph = NULL; // udp头部指针,用于解析udp包头部
struct tcphdr *tcph = NULL; // tcp头部指针,用于解析tcp包头部
struct iphdr *iph = NULL; // ip头部,包含源ip、目的ip等三层报文信息
u_int16_t sport = 0;
u_int16_t dport = 0;
if (ct == NULL || !nf_ct_is_confirmed(ct))
{
return NF_ACCEPT;
}
iph = ip_hdr(skb); // skb为数据报文指针,在转发过程中skb用于记录一个数据报文,转发过程都是操作skb指针
if (!iph)
return NF_ACCEPT;
// 这里将数据包的源ip和目的ip打印出来
printk("1-----------forward %pI4--->%pI4n", &iph->saddr, &iph->daddr);
// 返回NF_ACCEPT,让netfilter框架继续处理,该返回值决定了数据报文的命运,如果返回NF_DROP,那么该数据报文会丢弃,当然还有其他几个返回值,用的比较少
return NF_ACCEPT;
}

ops注册实例

static struct nf_hook_ops hook_ops[] __read_mostly = {
{
.hook = forward_hook, // 转发的数据会经过该函数处理
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_FIRST + 1,
},
{
.hook = forward_hook2,// 转发的数据会经过该函数处理,但在forward_hook之后处理
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_FIRST + 2, // 优先级比第一个低一点
},
{
.hook = local_in_hook, // 访问设备的数据经过该函数处理
.pf = PF_INET,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_FIRST + 1,
}};
static int __init hook_init(void)
{
nf_register_net_hooks(&init_net, hook_ops, ARRAY_SIZE(hook_ops));
printk("#init netfilter hook....okn");
return 0;
}

编译运行

环境
代码:OpenWrt 19.07 或其他版本OpenWrt分支版本,linux内核5.4.xx版本
编译环境: ubuntu18
运行环境: OpenWrt系统(内核版本和编译模块版本一致)
编译
将源码包拷贝到OpenWrt package目录,make menuconfig选择kmod-netfilter_hook并保存

menu-netfilter

通过以下命令编译
make package/netfilter_hook/compile V=s

编译完成后会在build_dir子目录生成netfilter_hook.ko文件,将该文件通过winscp工具传输到OpenWrt开发板(路由器)中
然后执行以下命令:

insmod netfilter_hook.ko
cat /proc/kmsg

保证开发板能够通过wan口连接外网,并接入一台pc到lan口,pc分别访问设备和外网

测试结果

<4>[ 4343.926739] 1-----------forward 192.168.66.167--->14.215.177.38
<4>[ 4343.928167] 2-----------forward 192.168.66.167--->14.215.177.38
<4>[ 4343.930772] local in 192.168.10.189--->192.168.10.199
<4>[ 4343.978416] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.022984] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.070944] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.120639] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.168305] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.218524] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.266218] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.280309] 1-----------forward 192.168.66.167--->14.215.177.38
<4>[ 4344.281698] 2-----------forward 192.168.66.167--->14.215.177.38
<4>[ 4344.283171] 1-----------forward 192.168.66.167--->14.215.177.38
<4>[ 4344.284758] 2-----------forward 192.168.66.167--->14.215.177.38
<4>[ 4344.286710] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.310932] 1-----------forward 192.168.66.167--->14.215.177.38
<4>[ 4344.313649] 2-----------forward 192.168.66.167--->14.215.177.38
<4>[ 4344.319432] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.352559] 1-----------forward 192.168.66.167--->14.215.177.38
<4>[ 4344.354507] 2-----------forward 192.168.66.167--->14.215.177.38
<4>[ 4344.356741] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.372923] 1-----------forward 192.168.66.167--->14.215.177.38
<4>[ 4344.374703] 2-----------forward 192.168.66.167--->14.215.177.38
<4>[ 4344.380287] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.430356] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.478390] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.522968] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.568144] local in 192.168.10.189--->192.168.10.199
<4>[ 4344.618331] local in 192.168.10.189--->192.168.10.199

作者简介

OpenWrt应用过滤插件作者(应用过滤用于控制app联网,可以过滤游戏、视频、聊天等几百款app)
从事嵌入式Linux开发近10年,主要负责路由器网通产品研发,精通OpenWrt系统,包括luci、消息机制、内核模块等。擅长模块:路由器上网行为管理、智能流控、上网认证、防火墙、虚拟服务器、多wan负载均衡等

开源作品地址:
https://github.com/destan19/OpenAppFilter

源码和文档

关注微信公众号可以获取更多技术文档、固件、源码等

微信扫码关注:

最后

以上就是漂亮爆米花为你收集整理的OpenWrt内核模块开发(二)-通过linux netfilter框架拦截协议栈报文的全部内容,希望文章能够帮你解决OpenWrt内核模块开发(二)-通过linux netfilter框架拦截协议栈报文所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(34)

评论列表共有 0 条评论

立即
投稿
返回
顶部