Linnux5.0.0下,基于Netlink与NetFilter对本机数据包进行筛选监控

本文档介绍了在Linux5.0.0环境下,如何使用Netlink和NetFilter来实现对本机数据包的筛选监控。用户程序发送源地址给内核模块,模块通过NetFilter钩子函数监测匹配的5元组信息,并通过Netlink传递给用户程序,最终保存到文件中。内容包括需求、程序逻辑、开发环境、常见问题及代码示例。
摘要由CSDN通过智能技术生成

Linnux5.0.0下,基于NetlinkNetFilter对本机数据包进行筛选监控

需求:

开发一个Linux lkm + app program,由app program提供需要监控的源IP地址,内核模块根据此IP地址监控本机发送处与该源IP地址相同的所有的packet的5元组,源地址、目标地址、原端口、目标端口、协议,并将相关的信息传给应用程序,应用程序将该信息保存在文件中。

程序逻辑:

​ 通信由用户程序发起,用户程序在开始时发送给内核模块一个源IP地址,之后用户程序将进入监听状态,内核模块将该IP地址以及用户程序的pid存下来作为目标IP地址和目标用户程序。之后用Netfilter中钩子函数判断每一个从本机发出的数据包中的源IP是否与目标IP地址相同,如果相同则钩子程序将数据包中的路由信息保存下来,通过Netlink发送给用户程序。用户程序接收到路由信息后,存在操作系统文件中。

开发/运行环境:

内核版本:Linux5.0.-37

发行版本:Ubuntu 18.04.1

运行日志

在这里插入图片描述

常用命令:

#查看系统日志
cat /var/log/kern.log
#打印系统日志到控制台
tail -f /var/log/kern.log &
#查看内核版本
cat /proc/version
#安装/卸载模块
insmod [mod]
rmmod [mod]

必备知识:

  1. Linux内核模块编程
  2. Netfilter子系统与hook函数编程
  3. struct sk_buff,struct iphdr,struct tcphdr,struct udphdr等网络相关结构体使用
  4. Netlink通讯机制

踩坑集锦:

高内核版本Netfilter hook函数注册:

Linux4.13之前,注册钩子使用的函数为:

nf_register_hook(reg);

高于Linux4.13版本后,注册钩子使用的函数改变成了:

nf_register_net_hook(&init_net, reg);

若希望兼容Linux4.13之前和之后的版本,可以这样写:

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
    nf_register_net_hook(&init_net, reg)
#else
    nf_register_hook(reg)
#endif
高内核版本hook函数原型声明:

早期linux内核中,Netfilterhook函数原型为:

static unsigned int sample( unsigned int hooknum,
							struct sk_buff * skb,
							const struct net_device *in,
							const struct net_device *out,
							int (*okfn) (struct sk_buff *));

但在高版本linux内核(至少4.10以上已改变),Netfilterhook函数原型变成了:

int sample_nf_hookfn(void *priv,
                     struct sk_buff *skb,
                     const struct nf_hook_state *state);
高内核版本创建Netlink处理函数:

在较低版本linux内核(Linux2.6)中,创建Netlink处理函数使用:

//假设nl_data_ready为处理函数
nl_sk = netlink_kernel_create(&init_net,
                              NETLINK_TEST, 
                              1,
                              nl_data_ready, 
                              NULL, 
                              HIS_MODULE);

高版本linux内核(至少3.8.13以上已经改变)中,创建netlink处理函数使用:

struct netlink_kernel_cfg cfg = {
   
    .input = nl_data_ready,//该函数原型可参考内核代码,其他参数默认即可,可参考内核中的调用
};
nl_sk = netlink_kernel_create(&init_net, 
                              NETLINK_TEST, 
                              &cfg);
消息发送后,skb释放问题:

当执行完netlink_unicast函数后skb不需要内核模块去释放,也不能去释放,否则会导致崩溃。因为netlink_unicast函数的返回不能保证用户层已经接受到消息,如果此时内核模块释放skb,会导致用户程序接收到一个已经释放掉的消息,当内核尝试处理此消息时会导致崩溃。内核会处理skb的释放,所以不会出现内存泄露问题, 这里给出了详细解释。

消息封装:

在这里插入图片描述

​ 在封装发送到kernel的消息时,我们需要依次对struct nlmsghdrstruct iovecstruct msghdr进行封装。

​ 内核模块和用户程序之间通讯与正常的使用socket类似,还需要封装源地址和目的地址,但需要注意此处的地址本质上是进程pid,而不是IP地址。

程序代码:

getRoutingInfo.c:

//内核编程需要的头文件
#include <linux/module.h>
#include <linux/kernel.h>
//Netfilter需要的头文件
#include <linux/net.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/if_vlan.h>
#include <linux/if_ether.h>
#include <linux/netdevice.h>  
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <net/ip.h>
#include <net/tcp.h>
#include <net/icmp.h>
#include <net/protocol.h>
//netlink需要的头文件
#include <net/sock.h>
#include <net/net_namespace.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/netlink.h>

//NIPQUAD宏便于把数字IP地址输出
#define NIPQUAD(addr) \
((unsigned char *)&addr)[0], \
((unsigned char *)&addr)[1], \
((unsigned char *)&addr)[2], \
((unsigned char *)&addr)[3]

#define NETLINK_TEST 17         //用于自定义协议
#define MAX_PAYLOAD 1024        //最大载荷容量
#define ROUTING_INFO_LEN 100    //单个路由信息的容量

//函数声明
unsigned int kern_inet_addr(char *ip_str);
void kern_inet_ntoa(char *ip_str , unsigned int ip_num);
unsigned 
  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值