我有一个利用netfilter Hook 的内核模块。目标是将数据包转发到另一个目标。正如我所看到的那样,来自外部的数据包的daddr设置为我的服务器IP,它通过NF_INET_PRE_ROUTING传递,然后假设要排队等待本地应用程序。在NF_INET_PRE_ROUTING上,我更改特定的数据包(检测自己的协议(protocol)),并将daddr替换为远程服务器IP,并将saddr替换为我的服务器IP。我想在内核模块本身中执行此操作,但是找不到找到将现有数据包移动到另一个路由点(NF_INET_FORWARDNF_INET_LOCAL_OUT甚至NF_INET_POST_ROUTING)的方法,也无法创建新数据包并将其插入TCP/IP堆栈是从服务器本身发送的。目前,该数据包在第一次挂接后便直接进入了黑洞。我不认为它会以某种方式与其他任何 Hook 。我该怎么办?

我当前的代码(远程服务器与客户端相同的测试代码):

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#定义调试1

静态结构nf_hook_ops nfho;

静态__be32 srv_addr = 0x620aa8c0;
静态__be32 cli_addr = 0x630aa8c0;
静态__be32 rem_addr = 0x630aa8c0;

静态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 =(结构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]将一个数据包从hooknum =%d\n中的%d。%d。%d。%d:%d发送到%d。%d。%d。%d:%d”,
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);
#万一

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);
返回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);
返回NF_STOP;
}
返回NF_ACCEPT;
}


静态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]已成功将协议(protocol)模块插入内核。\n”);
#万一
返回0;
}

静态void __exit cleanup_main(void){
nf_unregister_hook(&nfho);
#if DEBUG> 0
printk(KERN_INFO“[HTTP]已成功卸载协议(protocol)模块。\n”);
#万一
}

module_init(init_main);
module_exit(cleanup_main);

MODULE_LICENSE(“GPL v3”);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);

最佳答案

我找不到以或多或少适当的方式以编程方式转发数据包的任何方法。
我发现的唯一方法(似乎是非常流行的解决方案)是手动修改skb_buff中的所有相关字段,并通过dev_queue_xmit传输更改的数据包。这种方法不好,因为它没有实现为数据包找到合适的路由。 F.e.如果相邻网络包括许多实际上可以用于数据包路由的节点,那么似乎不可能从内核模块中找到正确的路由(或者我不知道这种方式)。内核TCP/IP堆栈的源代码也显示了ip_forward函数的存在,该函数在内核模块的任何部分都不可用,而我重现该功能的尝试最终导致将TCP/IP堆栈的一半拖到模块中。此功能对于编程式数据包转发可能是理想的选择,因为它只需几个参数,所有参数本身都会更改所有需要的数据包部分。

无论如何。我自己的固定代码现在看起来像这样:

#include
#include
#include
#include
#include
#include
#include“my_mod.h”

#define DRIVER_AUTHOR“AlexKey”
#define DRIVER_DESC“HTTP数据包操作”

#定义调试1

静态结构nf_hook_ops nfho;

静态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;
struct ethhdr * eth_header;

u32 saddr,daddr;
u16来源,dest;

/*获取所有标题*/
eth_header =(结构ethhdr *)skb_mac_header(skb);
ip_header =(struct iphdr *)skb_network_header(skb);
skb_set_transport_header(skb,ip_header-> ihl * 4);
tcp_header =(结构tcphdr *)skb_transport_header(skb);

/*如果数据包的源或目标不是80,则该数据包不适合我们:) */
if(tcp_header-> source!= ntohs(80)&& tcp_header-> dest!= ntohs(80))
返回NF_ACCEPT;

#if DEBUG> 0
printk(KERN_INFO“[HTTP]在%d上从%d\n获得了数据包”,htons(tcp_header-> dest),htons(tcp_header-> source));
#万一

saddr = ip_header-> saddr;
daddr = ip_header-> daddr;

源= tcp_header->源;
dest = tcp_header-> dest;

/*在链接层 header 中,将发送方mac更改为我们的以太网mac
和目标mac到发送者mac :)乒乓*/
memcpy(eth_header-> h_dest,eth_header-> h_source,ETH_ALEN);
memcpy(eth_header-> h_source,skb-> dev-> dev_addr,ETH_ALEN);

/*将新的链接层 header 设置为套接字缓冲区*/
skb-> data =(unsigned char *)eth_header;
skb-> len + = ETH_HLEN;

/*将其设置为传出数据包*/
skb-> pkt_type = PACKET_OUTGOING;

/*交换IP header 的发送方和目标地址*/
memcpy(&ip_header-> saddr,&daddr,sizeof(u32));
memcpy(&ip_header-> daddr,&saddr,sizeof(u32));

/*如果传输成功,则报告其被盗
如果失败,则将其丢弃*/
if(dev_queue_xmit(skb)== NET_XMIT_SUCCESS){
#if DEBUG> 0
printk(KERN_INFO“[HTTP]成功发送数据包\n”);
#万一
返回NF_STOLEN;
} 别的 {
#if DEBUG> 0
printk(KERN_INFO“[HTTP]发送失败\n”);
#万一
返回NF_DROP;
}

}

静态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]已成功将协议(protocol)模块插入内核。\n”);
#万一
返回0;
}

静态void __exit cleanup_main(void){
nf_unregister_hook(&nfho);
#if DEBUG> 0
printk(KERN_INFO“[HTTP]已成功卸载协议(protocol)模块。\n”);
#万一
}

module_init(init_main);
module_exit(cleanup_main);

MODULE_LICENSE(“GPL v3”);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);

我希望听到对此代码的任何修改。

09-06 09:55