在NF_IP_PRE_ROUTING挂钩子修改skbuff问题
[i=s] 本帖最后由 kangle000 于 2010-07-19 06:59 编辑 [/i]
以下代码是在2.6.31内核中执行的,根据send_rst改写的。但是代码存在以下几个问题:
1、捕包发现ip头的version字段为0,offset的值不为0,校验出错。
2、udp头的内容好像并没有更改。
3、us指针指向的内容并没有改变
4、经常死机
请大家帮我看看是哪里的问题[code]#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/if_ether.h>
#include <linux/netdevice.h>
#include <linux/if_packet.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <asm/string.h>
#define ETH "eth0"
#define ETH_HLEN 14
#define UDP_HLEN 8
#define loc_swap(a,b) {(a)=(a)^(b);(b)=(a)^(b);(a)=(a)^(b);}
#define DNSPORT 53
#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
#define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],((u8*)(x))[4],((u8*)(x))[5]
#define FLAGS 0x8003
typedef struct _rfc1035_header rfc1035_header;
struct _rfc1035_header {
unsigned short id;
unsigned int qr:1;
unsigned int opcode:4;
unsigned int aa:1;
unsigned int tc:1;
unsigned int rd:1;
unsigned int ra:1;
unsigned int rcode:4;
unsigned short qdcount;
unsigned short ancount;
unsigned short nscount;
unsigned short arcount;
};
unsigned int modify_dns(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct net_device *dev;
struct iphdr *iph = NULL;
struct ethhdr *eth = NULL;
struct udphdr *udph = NULL;
unsigned char mac_temp[ETH_ALEN] = {0};
unsigned int iph_len;
char *dns_data;
int data_len;
rfc1035_header *qh;
unsigned short *us;
printk("before dev = in/n");
dev = dev_get_by_name(&init_net, "eth0");
if(NULL == dev)
goto out;
if(NULL == skb)
return NF_ACCEPT;
printk("before decide packet type/n");
if((IPPROTO_UDP != (ip_hdr(skb)->protocol))
&& (DNSPORT != udp_hdr(skb)->dest))
return NF_ACCEPT;
iph = (struct iphdr *)ip_hdr(skb);
iph_len = iph->ihl << 2;
printk("iphdr_len=%d data_len=%d sip=%04x dip=%04x/n", iph_len, ntohs(iph->tot_len), iph->saddr, iph->daddr);
udph = udp_hdr(skb);
printk("source=%hu dest=%hu len=%d/n", ntohs(udph->source), ntohs(udph->dest), ntohs(udph->len));
data_len = ntohs(udph->len) - UDP_HLEN;
dns_data = (char *)udph + UDP_HLEN;
printk("before modify dns data/n");
qh = (rfc1035_header *) dns_data;
us = (unsigned short *)(dns_data + 2);
*us = *us | FLAGS;
printk("flags=%hu/n", *us);
loc_swap(iph->saddr, iph->daddr);
loc_swap(udph->source, udph->dest);
iph->ihl = sizeof(struct iphdr)/4;
iph->tot_len = htons(skb->len);
printk("source=%uh dest=%uh len=%d/n", ntohs(udph->source), ntohs(udph->dest), ntohs(udph->len));
printk("before udp checksum/n");
udph->check = 0;
udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, ntohs(udph->len), IPPROTO_UDP, 0);
iph->frag_off = htons("IP_DF");
iph->id = 0;
printk("before ip checksum/n");
iph->check = 0;
iph->check = ip_fast_csum((unsigned char *)iph, iph_len);
printk("before skb_push/n");
eth = (struct ethhdr *)skb_push(skb, ETH_HLEN);
if(NULL == eth)
goto out;
if(NULL != eth->h_source)
printk("SOURCE:" MAC_FMT "/n", MAC_ARG(eth->h_source));
if(NULL != eth->h_dest)
printk("DEST:" MAC_FMT "/n", MAC_ARG(eth->h_dest));
printk("before swap mac adress/n");
memcpy(mac_temp, (unsigned char *)eth->h_dest, ETH_ALEN);
memcpy(eth->h_dest, (unsigned char *)eth->h_source, ETH_ALEN);
memcpy(eth->h_source, mac_temp, ETH_ALEN);
printk("before send packet/n");
if (0 > dev_queue_xmit(skb))
goto out;
return NF_ACCEPT;
out:
printk("in out: ");
if (NULL != skb)
{
printk("skb not null/n");
dev_put(dev);
kfree_skb(skb);
}
return NF_ACCEPT;
}
static struct nf_hook_ops dns_ops[] = {
{
.hook = modify_dns,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
.priority = NF_IP_PRE_ROUTING,
}
};
static int __init init(void)
{
int ret;
printk("before nf_register_hooks/n");
ret = nf_register_hooks(dns_ops, ARRAY_SIZE(dns_ops));
if(ret < 0)
{
printk("can't register dns_ops!/n");
return ret;
}
printk("insmod dns_ops ok!/n");
return 0;
}
static void __exit fini(void)
{
nf_unregister_hooks(dns_ops, ARRAY_SIZE(dns_ops));
printk("rmmod dns_ops ok!/n");
}
module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("KAL");
MODULE_DESCRIPTION("MODIFY DNS PACKETS");
[/code]
请大家帮帮忙看是什么问题
你首先要保证sk_buff是可写的,没有分段的,这些你的代码都没有处理
[code]
.priority = NF_IP_PRE_ROUTING,
[/code]没见过这样的 prio 设置[code]
iph = (struct iphdr *)ip_hdr(skb);
iph_len = iph->ihl << 2;
[/code]这个算出来可不是 IP 头的长度[code]
udph = udp_hdr(skb);
[/code]在这里你无法通过这种方法计算 UDP 的头指针,这里是网络层,还没到系统的传输层
[quote]没见过这样的 prio 设置这个算出来可不是 IP 头的长度在这里你无法通过这种方法计算 UDP 的头指针,这里是网 ...
[size=2][color=#999999]platinum 发表于 2010-07-19 09:29[/color] [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=7275290&ptid=1167632][img]http://linux.chinaunix.net/bbs/images/common/back.gif[/img][/url][/size][/quote]
谢谢,那个prio确实错了。但是ip头长度不就是iph->ihl *4 吗。那个udp指针是没问题的。udp_hdr调用了[code]
static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
{
return skb->head + skb->transport_header;
}[/code]来计算udp头
[quote]
谢谢,那个prio确实错了。但是ip头长度不就是iph->ihl *4 吗。
[/quote]
你从哪里看到的 IP 头长度就是 iph->ihl * 4 了?
如果是这样,那么 iph->tot_len 又是做什么的?
建议恶补一下 TCP/IP 详解卷一的 IP 那一章,基本概念都错了!
[quote]
那个udp指针是没问题的。udp_hdr调用了
1. static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
2. {
3. return skb->head + skb->transport_header;
4. }
复制代码
来计算udp头
[/quote]
你能确保此时 skb->transport_header 的数据是正确的吗?
当前工作的L3层,udp_hdr返回的仍然是L3层头部,不是L4头部
[b]回复 [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=7275207&ptid=1167632]1#[/url] [i]kangle000[/i] [/b]
1 不要在自己模块中想当然的修改数据包内容,因为有可能这个数据包被其它模块引用,如果是这样,你需要copy一份。可以使用skb_ip_make_writable
2 不打算修改内容,但想查看L4头部时,不要直接使用偏移来取,使用skb_header_pointer, 因为你不能保证L4头部是线性的
[b]回复 [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=7275746&ptid=1167632]8#[/url] [i]lyl19[/i] [/b]
哦,这样啊,谢谢指导。我看了skb_header_pointer函数,它是把L4的头部拷贝出来,然后查看。但是我现在需求就是要修改数据包。或者拷贝一份修改之后把原来的数据包drop掉。如果我要取L4头部,还要修改它,不用直接取偏移地址用什么函数
如果去四层头部的话,就可以用iph头部加偏移取得四层头部。
[b]回复 [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=7275264&ptid=1167632]3#[/url] [i]@sky[/i] [/b]
确实是因为这个原因错了。我有一个问题,在L3层的时候skb->transport_header是不是没有初始化,这个变量是在L4初始化吗?skb->len是实际数据长度,skb->data_len是数据的长度,但是在L3层的时候skb->data_len为0?
[quote]你首先要保证sk_buff是可写的,没有分段的,这些你的代码都没有处理
[size=2][color=#999999]@sky 发表于 2010-07-19 08:17[/color] [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=7275264&ptid=1167632][img]http://linux.chinaunix.net/bbs/images/common/back.gif[/img][/url][/size][/quote]
你所指的分段是DF字段吗?iph->frag_off = htons("IP_DF");这条语句我不太明白是什么意思
[quote]回复 kangle000
1 不要在自己模块中想当然的修改数据包内容,因为有可能这个数据包被其它模块 ...
[size=2][color=#999999]lyl19 发表于 2010-07-19 23:53[/color] [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=7275746&ptid=1167632][img]http://linux.chinaunix.net/bbs/images/common/back.gif[/img][/url][/size][/quote]
1、你所指的这个数据包被其他模块引用是指其他模块使用skb->clone产生的吗?
2、L4层数据不是线性的数据包太大用多个skbuff存储吗?如果分为多个skbuff存储,是不是和local_df这个字段有关
kb_ip_make_writable(skb, len), 它会自动帮你判断是否需要拷贝
返回成功后,你可以安全的修改skb buffer内容了。
应该是skb_make_writable吧,有skb_ip_make_writable这个吗:em14:
[b]回复 [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=7276082&ptid=1167632]12#[/url] [i]kangle000[/i] [/b]
我指的是数据包的线性化,skb_make_writable执行后可以保证数据包是可写的和线性化的
[quote]应该是skb_make_writable吧,有skb_ip_make_writable这个吗
[size=2][color=#999999]@sky 发表于 2010-07-21 17:15[/color] [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=7276565&ptid=1167632][img]http://linux.chinaunix.net/bbs/images/common/back.gif[/img][/url][/size][/quote]
现在内核没有skb_ip_make_writable函数了,好像是在2.6.9之前有
本文讨论了在Linux 2.6.31内核中NF_IP_PRE_ROUTING阶段修改sk_buff时遇到的问题,包括IP头version字段错误、UDP头部未正确更新等,并探讨了解决方案。
1080

被折叠的 条评论
为什么被折叠?



