在NF_IP_PRE_ROUTING挂钩子修改skbuff问题

本文讨论了在Linux 2.6.31内核中NF_IP_PRE_ROUTING阶段修改sk_buff时遇到的问题,包括IP头version字段错误、UDP头部未正确更新等,并探讨了解决方案。

在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]

 

 

 

 

 

请大家帮帮忙看是什么问题

@sky 发表于 2010-07-19 08:17

你首先要保证sk_buff是可写的,没有分段的,这些你的代码都没有处理

platinum 发表于 2010-07-19 09:29

[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 的头指针,这里是网络层,还没到系统的传输层

kangle000 发表于 2010-07-19 09:47

[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头

platinum 发表于 2010-07-19 09:57

[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 的数据是正确的吗?

@sky 发表于 2010-07-19 10:07

当前工作的L3层,udp_hdr返回的仍然是L3层头部,不是L4头部

lyl19 发表于 2010-07-19 23:53

[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头部是线性的

kangle000 发表于 2010-07-20 09:02

[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头部,还要修改它,不用直接取偏移地址用什么函数

Godbach 发表于 2010-07-20 09:32

如果去四层头部的话,就可以用iph头部加偏移取得四层头部。

kangle000 发表于 2010-07-20 20:50

[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?

kangle000 发表于 2010-07-20 20:53

[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");这条语句我不太明白是什么意思

kangle000 发表于 2010-07-20 20:59

[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这个字段有关

lyl19 发表于 2010-07-20 22:10

kb_ip_make_writable(skb, len), 它会自动帮你判断是否需要拷贝
返回成功后,你可以安全的修改skb buffer内容了。

@sky 发表于 2010-07-21 17:15

应该是skb_make_writable吧,有skb_ip_make_writable这个吗:em14:

@sky 发表于 2010-07-21 17:19

[b]回复 [url=http://linux.chinaunix.net/bbs/redirect.php?goto=findpost&pid=7276082&ptid=1167632]12#[/url] [i]kangle000[/i] [/b]


    我指的是数据包的线性化,skb_make_writable执行后可以保证数据包是可写的和线性化的

kangle000 发表于 2010-07-22 21:04

[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之前有

 

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值