问题描述
我想在内核空间中回显包码.我在此计算机上运行一个带端口6000的echo服务器. 现在,客户端在另一台计算机上运行向回声服务器发送数据.现在,我想做的就是回应从内核空间回来的数据包.我不想用数据包打扰服务器,它将默默地从内核空间静音.我在下面提出我的代码:
#include <linux/kernel.h> #include <linux/module.h> #include <linux/skbuff.h> #include <linux/netfilter.h> #include <linux/netdevice.h> #include <linux/ip.h> #include <linux/udp.h> #include <linux/mm.h> #include <linux/err.h> #include <linux/crypto.h> #include <linux/init.h> #include <linux/crypto.h> #include <linux/scatterlist.h> #include <net/ip.h> #include <net/udp.h> #include <net/route.h> #include <net/checksum.h> #include <linux/netfilter_ipv4.h> #define IP_HDR_LEN 20 #define UDP_HDR_LEN 8 #define TOT_HDR_LEN 28 static unsigned int pkt_echo_begin(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)); static struct nf_hook_ops pkt_echo_ops __read_mostly = { .pf = NFPROTO_IPV4, .priority = 1, .hooknum = NF_INET_PRE_ROUTING, .hook = pkt_echo_begin, }; static int __init pkt_echo_init(void) { printk(KERN_ALERT "\npkt_echo module started ..."); return nf_register_hook(&pkt_echo_ops); } static void __exit pkt_echo_exit(void) { nf_unregister_hook(&pkt_echo_ops); printk(KERN_ALERT "pkt_echo module stopped ..."); } static unsigned int pkt_echo_begin (unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct iphdr *iph; struct udphdr *udph; unsigned char *data; unsigned char *temp; __u16 dst_port, src_port; int data_len; if (skb) { iph = (struct iphdr *) skb_header_pointer (skb, 0, 0, NULL); if (iph && iph->protocol &&(iph->protocol == IPPROTO_UDP)) { udph = (struct udphdr *) skb_header_pointer (skb, IP_HDR_LEN, 0, NULL); src_port = ntohs (udph->source); dst_port = ntohs (udph->dest); if (dst_port == 6000) { printk(KERN_ALERT "\nUDP packet goes in"); data = (unsigned char *) skb_header_pointer (skb, TOT_HDR_LEN, 0, NULL); data_len = skb->len - TOT_HDR_LEN; struct sk_buff *newskb; struct iphdr *newiph; struct udphdr *newudph; unsigned char *newdata; unsigned int newdata_len; newskb = skb_copy(skb, GFP_ATOMIC); newiph = (struct iphdr *) skb_header_pointer (newskb, 0, 0, NULL); newudph = (struct udphdr *) skb_header_pointer (newskb, IP_HDR_LEN, 0, NULL); newdata = (unsigned char *) skb_header_pointer (newskb, TOT_HDR_LEN, 0, NULL); newiph->saddr = iph->daddr; newiph->daddr = iph->saddr; newudph->source = udph->dest; newudph->dest = udph->source; struct sk_buff *tempskb; tempskb = skb_copy(skb, GFP_ATOMIC); *tempskb = *skb; *skb = *newskb; *newskb = *tempskb; kfree_skb(newskb); } } } return NF_ACCEPT; } module_init(pkt_echo_init); module_exit(pkt_echo_exit); MODULE_AUTHOR("Rifat Rahman Ovi: <rifatrahmanovi@gmail.com>"); MODULE_DESCRIPTION("Echoing a packet from kernel space."); MODULE_LICENSE("GPL");
现在我不明白需要什么.我在pre_routing hook中捕获了数据包. 然后我创建了一个新的skb,从旧的skb填充它,然后改变了 源地址(SADDR),目标地址(DADDR),源端口(源)和目标 端口(DEST),以便可以从PRE_ROUTING HOOK转发数据包.作为路由 在数据包从挂钩传递后进行决策,我查找要转发到其始发源的数据包.但由于某种原因,它没有这样做.收到数据包,并更改了一切,但数据包似乎没有向后移动.我不明白缺少什么,也是需要做什么的东西.更具体地说,从Pre_Routing Hook向网络发送数据包?
推荐答案
很多丢失.
读取关于函数(如dev_queue_xmit(struct sk_buff *)),但请注意,在使用此函数之前,您的skb必须具有链接层标题,因为此功能实际上将数据包队列队列以立即向NIC发送到NIC,并且它是您的作业来设置链路层地址.
第二,请记住,更改IP-Header地址后,您必须重新计算数据包的校验和,或者您的数据包将在另一端丢弃.
第三,请注意,在内核空间中做你想要做的事情主要被认为是一个非常糟糕的做法. 内核模块存在有原因,这不是其中之一,NetFilter是一个很好的工具,但它并不是真正应该用于发送正常流量.
编辑:
阅读您的最新评论;我建议你仔细阅读Libpcap库,它应该很好地为您服务,仍然在其正确的位置,用户空间保持工作.
其他推荐答案
除上一个答案外,可以使用从NetFilter回调中回应UDP数据包的另一种技术是:
将数据包发送回,作为新的UDP数据包,使用:
int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
在net/socket.c
中原始数据包也可以使用nf_drop删除.
在NetFilter回调中,可以在"中断上下文"中运行可以发送数据包,但无法接收它们(因为每次尝试等待导致内核恐慌). 出于这个原因,我提出的解决方案与UDP一起使用,但不能使用TCP(TCP握手需要必须接收ACK消息).
无论如何,如今已经说,在内核空间中做这种事情很糟糕,应该仅用于学习目的.