我有一个使用netfilter钩子的内核模块 . 目标是将数据包转发到另一个目的地 . 正如我所看到的设计来自外部的数据包 daddr
设置为我的服务器IP通过NF_INET_PRE_ROUTING然后假设排队等待本地应用程序 . 在NF_INET_PRE_ROUTING上,我更改了特定的数据包(检测我自己的协议),并将 daddr
替换为远程服务器IP和 saddr
替换为我的服务器IP . 我想从内核模块本身做到这一点,但无法找到将现有数据包移动到另一个路由点( NF_INET_FORWARD
或 NF_INET_LOCAL_OUT
或甚至 NF_INET_POST_ROUTING
)或创建新数据包并将其插入TCP / IP堆栈的方法,就像它从服务器本身发送 . 目前,数据包只是在第一次挂钩后才进入黑洞 . 我不认为它会以某种方式进入任何其他钩子 . 我怎么能这样做?
我当前的代码(测试远程服务器与客户端相同的代码):
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <net/ip.h>
#include <net/tcp.h>
#include <net/route.h>
#define DEBUG 1
static struct nf_hook_ops nfho;
static __be32 srv_addr = 0x620aa8c0;
static __be32 cli_addr = 0x630aa8c0;
static __be32 rem_addr = 0x630aa8c0;
static unsigned int hook_func(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)){
struct iphdr *ip_header;
struct tcphdr *tcp_header;
ip_header = (struct iphdr *)skb_network_header(skb);
skb_set_transport_header(skb, ip_header->ihl * 4);
tcp_header = (struct tcphdr *)skb_transport_header(skb);
#if DEBUG > 0
if(tcp_header->dest == ntohs(80) || tcp_header->source == ntohs(80))//(ip_header->saddr == cli_addr || ip_header->saddr == srv_addr || ip_header->saddr == rem_addr) &&
printk(KERN_INFO "[HTTP] Got a packet to %d.%d.%d.%d:%d from %d.%d.%d.%d:%d in hooknum=%d\n",
ip_header->daddr & 0x000000FF,
(ip_header->daddr & 0x0000FF00) >> 8,
(ip_header->daddr & 0x00FF0000) >> 16,
(ip_header->daddr & 0xFF000000) >> 24,
ntohs(tcp_header->dest),
ip_header->saddr & 0x000000FF,
(ip_header->saddr & 0x0000FF00) >> 8,
(ip_header->saddr & 0x00FF0000) >> 16,
(ip_header->saddr & 0xFF000000) >> 24,
ntohs(tcp_header->source),
hooknum);
#endif
if(ip_header->saddr == cli_addr && tcp_header->dest == ntohs(80)){
ip_header->daddr = rem_addr;
ip_header->saddr = srv_addr;
ip_header->check = 0;
ip_send_check(ip_header);
tcp_header->check = 0;
tcp_header->check = tcp_v4_check(skb->len - 4*ip_header->ihl, ip_header->saddr, ip_header->daddr, csum_partial((char *)tcp_header, skb->len - 4*ip_header->ihl,0));
okfn(skb);
return NF_STOP;
}
if(ip_header->saddr == rem_addr && tcp_header->source == ntohs(80)){
ip_header->daddr = cli_addr;
ip_header->saddr = srv_addr;
ip_header->check = 0;
ip_send_check(ip_header);
tcp_header->check = 0;
tcp_header->check = tcp_v4_check(skb->len - 4*ip_header->ihl, ip_header->saddr, ip_header->daddr, csum_partial((char *)tcp_header, skb->len - 4*ip_header->ihl,0));
okfn(skb);
return NF_STOP;
}
return NF_ACCEPT;
}
static int __init init_main(void) {
nfho.hook = hook_func;
nfho.hooknum = 0;
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST;
nf_register_hook(&nfho);
#if DEBUG > 0
printk(KERN_INFO "[HTTP] Successfully inserted protocol module into kernel.\n");
#endif
return 0;
}
static void __exit cleanup_main(void) {
nf_unregister_hook(&nfho);
#if DEBUG > 0
printk(KERN_INFO "[HTTP] Successfully unloaded protocol module.\n");
#endif
}
module_init(init_main);
module_exit(cleanup_main);
MODULE_LICENSE("GPL v3");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
3 回答
亚历克斯,我遇到了你试图从内核发送损坏的skb的完全相同的问题 . 我经历了相同的思考过程,但找不到能够正确处理传出数据包路由的优雅解决方案 . 直到我发现我也可以在内核中使用套接字 .
使用
socket.h
中的sock_create
在内核模块中创建一个原始套接字,如下所示:修改IP标头后,您可以使用函数使用
sock_sendmsg
发送skb:请记住
IPPROTO_RAW
套接字,您必须自己创建IP标头,但是您已经在skb中有一个 . 现在,您只需创建并填充struct iovec
数组并将其传递给sock_send
.对于
struct sockaddr_in *addr
,使用与IP标头相同的目标地址:记得返回
NF_DROP
或释放skb并返回NF_STOLEN
以便在完成skb后将其清理干净 .我找不到任何方式以一种或多或少的正确方式以编程方式转发数据包 . 我找到的唯一方法(似乎是非常流行的解决方案)是手动修改
skb_buff
中的所有相关字段,并通过dev_queue_xmit
发送更改的数据包 . 这种方式似乎不可能从内核模块中找到合适的路径(或者我不知道这样的方式) . 内核TCP / IP堆栈的源代码也显示了ip_forward
函数的存在,该函数不能从内核模块的任何部分获得,我试图重现该函数最终将一半的TCP / IP堆栈拖入模块 . 这个函数可能是程序化数据包转发的理想选择,因为它只需要几个参数,并且所有参数都会自行改变所有需要的数据包部分 .无论如何 . 我自己的固定代码现在看起来像这样:
我很想听到对此代码的任何修改 .
使用内核钩子执行此操作的方法是手动修改skb_buff中的所有相关字段,并通过dev_queue_xmit传输更改的数据包 . 当您尝试创建一个“凭空”到目的地的数据包时,您需要小心路由 . 假设从用户空间的角度正确设置了路由,那么启用数据包的所有操作就是在dev_queue_xmit()之前使用ip_route_output() . 例如: