免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 5346 | 回复: 6
打印 上一主题 下一主题

[内核模块] NF中调用dev_queue_xmit(skb)发送UDP包,对端没收到。 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-09-20 00:17 |只看该作者 |倒序浏览
本帖最后由 mybname 于 2013-09-21 12:40 编辑

在路由器上做开发,操作系统是openWRT,Linux内核版本3.10。

在NF_INET_POST_ROUTING点挂入勾子函数,勾子函数中检测DNS请求包。

检测到后,调用dev_alloc_skb()申请一个新skb,组装成UDP应答包,负载为一个字符串128字节。

UDP应答包中的Mac、IP、Port,按DNS请求包的源目的交换,最后调用dev_queue_xmit()函数发送。


测试:

      PC ------ Route -------Internat

PC接路由器lan口,路由器wan口接外网。

PC上开抓包工具,ping www.baidu.com

抓包工具显示正常的DNS请求和应答,没发现路由器发出的UDP应答包。


刚接触内核开发,在CU看了一些贴子,模仿着写了这么个东西,出了问题就不会调了。
尝试过不自己取设备指针,直接 新skb->dev = 旧skb->dev; PC仍未抓到包。
知道的有限, 请高手指点,非常感谢。

如果有相关的贴子,介绍一下也好。


附件中是可编译运行的代码。


路由器有四个端口,运行ifconfig看到:
br-lan   200.200.50.1  00:BB:CC:DD:EE:FF
eth0     记不清了
eht0.1   IP为空   00:BB:CC:DD:EE:FF
eth0.2   IP为空   00:BB:CC:DD:EE:FF

wan      192.168.4.1   00:01:02:03:04:05

--------------------------------------------------------
关键代码如下:

static  struct nf_hook_ops  http_hooks[] =
{
  {
    .owner= THIS_MODULE,
    .hook = ng_nf,
    .pf = PF_INET,
    .hooknum = NF_INET_POST_ROUTING,
    .priority = NF_IP_PRI_FIRST,
  },
};

static int ng_nf_init(void)
{
    if(nf_register_hooks(http_hooks, ARRAY_SIZE(http_hooks)))
    {
        printk("%s %s %d nf_register_hooks failed\n", __FILE__,__func__,__LINE__);
    }

    return 0;
}

unsigned int ng_nf(unsigned int hooknum,
                            struct sk_buff *skb,
                    const struct net_device *in,
                    const struct net_device *out,
                    int(*okfn)(struct sk_buff *))
{
    int iRet = netfilter_udp_send(skb);
    return NF_ACCEPT;        
}

//发送UDP包
unsigned int netfilter_udp_send(struct sk_buff *skb)
{
    #define IF_NAME "eth0"
    struct socket *sock;
    char szBuff[4096] = {0};
    char szPayLoad[128] = {"--- Test UDP package ---"};
   
    unsigned short uhDnsPort = htons(53);
    unsigned short uhEthIpProto = htons(ETH_P_IP);

    struct net_device *netdev = NULL;
    struct net *net = NULL;

    struct ethhdr *eth_header = NULL;
    struct iphdr  *ip_header  = NULL;
    struct udphdr *udp_header = NULL;

    if(NULL == skb)
    {
        return 0;
    }

    //IP头
    struct iphdr* pIpHdr = ip_hdr(skb);
    if(pIpHdr->protocol != IPPROTO_UDP)  //DNS是UDP包
    {
        return 0;
    }

    //UDP头
    //struct udphdr* pUdpHdr = udp_hdr(skb);
    struct udphdr* pUdpHdr = skb->data + pIpHdr->ihl*4;
    if(pUdpHdr->dest != uhDnsPort)  //DNS端口53
    {
        return 0;
    }

    //以太帧
    struct ethhdr* pEthHdr = (struct ethhdr*)(skb->mac_header);
    if(pEthHdr->h_proto != uhEthIpProto)
    {
        write_log("pEthHdr->h_proto[0x%x] != uhEthIpProto[0x%x] \n", pEthHdr->h_proto, uhEthIpProto);
        return 0;
    }

    //显示收到的码流
    printk("\n");
    sprintfHex(szBuff, sizeof(szBuff), skb->mac_header, skb->data_len+14);
    write_log("%s", szBuff);

    //------------------------------------------

    struct ifreq ifr;
    sock_create_kern(PF_INET, SOCK_DGRAM, 0, &sock);

    // copy the interface name to the ifrn_name.
    strcpy(ifr.ifr_ifrn.ifrn_name, "eth0");
    kernel_sock_ioctl(sock, SIOCSIFNAME, (unsigned long) &ifr);
   
    net = sock_net((const struct sock *) sock->sk);
    netdev = dev_get_by_name(net, "eth0");

    int skb_len = LL_RESERVED_SPACE(netdev) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(szPayLoad);

    struct sk_buff * nskb = dev_alloc_skb(skb_len);
    if (NULL == nskb)
    {
        return 0;
    }

    //nskb->dev = skb->dev;  //直接用原skb的设备也未收到包
    nskb->dev = netdev;

    nskb->pkt_type = PACKET_OTHERHOST;
    nskb->protocol = htons(ETH_P_IP);
    nskb->ip_summed = CHECKSUM_NONE;
    nskb->priority  = 0;   

    skb_reserve(nskb, LL_RESERVED_SPACE(netdev));

    //ip头
    skb_set_network_header(nskb, 0);
    skb_put(nskb, sizeof(struct iphdr));

    //udp头
    skb_set_transport_header(nskb, sizeof(struct iphdr));
    skb_put(nskb, sizeof(struct udphdr));

    //--------------------------------------------

    //construct udp header in skb
    udp_header = udp_hdr(nskb);
    udp_header->source = pUdpHdr->dest;
    udp_header->dest = htons(9988); //pUdpHdr->source;
    udp_header->len = htons(sizeof(struct udphdr) + sizeof(szPayLoad)); //htons(256);
    udp_header->check = 0;
            
    //construct ip header in skb
    ip_header = ip_hdr(nskb);
    ip_header->version = 4;
    ip_header->ihl = sizeof(struct iphdr) >> 2;
    ip_header->frag_off = 0;
    ip_header->protocol = IPPROTO_UDP;
    ip_header->tos = 0;
    ip_header->daddr = pIpHdr->saddr;
    ip_header->saddr = in_aton("200.200.50.1"); //pIpHdr->daddr;
    ip_header->ttl = 0x40;
    ip_header->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(szPayLoad));
    ip_header->check = 0;
   
    // caculate checksum
    ip_header->check = ip_fast_csum(ip_header, ip_header->ihl);
    nskb->csum = skb_checksum(nskb, ip_header->ihl*4, (sizeof(struct udphdr) + sizeof(szPayLoad)), 0);
    udp_header->check = csum_tcpudp_magic(pIpHdr->daddr, pIpHdr->saddr, (sizeof(struct udphdr) + sizeof(szPayLoad)), IPPROTO_UDP, nskb->csum);

    //copy data in skb
    char *pData = skb_put(nskb, sizeof(szPayLoad));
    if (NULL != pData)
    {
        memcpy(pData, szPayLoad, sizeof(szPayLoad));
        //write_log("--payload: %s", szBuff);
    }   

    //construct ethernet header in skb
    eth_header = (struct ehthdr *)skb_push(nskb, 14);
    memcpy(eth_header->h_dest, pEthHdr->h_source, ETH_ALEN);
    memcpy(eth_header->h_source, pEthHdr->h_dest, ETH_ALEN);
    eth_header->h_proto = htons(ETH_P_IP); //uhEthIpProto;

    sprintfHex(szBuff, sizeof(szBuff), nskb->data-14, nskb->data_len+14);

    // send packet
    if ( dev_queue_xmit(nskb) < 0 )
    {
        dev_put(netdev);
        kfree_skb(nskb);
        printk("send packet by nskb failed.\n");
        return 0;
    }

    write_log("--- send packet by nskb success. \n%s", szBuff);

    return true;
}

skbuff_Linux.3.10.rar (19.7 KB, 下载次数: 30)

ng_nf_dns.rar (25.66 KB, 下载次数: 45)

论坛徽章:
0
2 [报告]
发表于 2013-09-21 14:15 |只看该作者

可能计算校验和时,传的参数len有问题。
之前认为 len=UDP头长度 + 负载的长度。还要确认一下。
skb_checksum(const struct sk_buff *skb, int offset, int len, __wsum csum);




论坛徽章:
6
金牛座
日期:2013-10-08 10:19:10技术图书徽章
日期:2013-10-14 16:24:09CU十二周年纪念徽章
日期:2013-10-24 15:41:34狮子座
日期:2013-11-24 19:26:19未羊
日期:2014-01-23 15:50:002015年亚洲杯之阿联酋
日期:2015-05-09 14:36:15
3 [报告]
发表于 2013-09-23 08:09 |只看该作者
回复 2# mybname
在两边分别抓包看一下。另外可以参考本版精华帖合集http://bbs.chinaunix.net/thread-1930079-1-1.html

   

论坛徽章:
2
2015年亚洲杯之乌兹别克斯坦
日期:2015-04-15 15:43:482015亚冠之迪拜阿赫利
日期:2015-06-30 20:36:46
4 [报告]
发表于 2013-10-11 12:04 |只看该作者
不知道你的checksum计算对不对,下面是我验证可行的方法,当个参考,你比较下。如果实在不行就把udp checksum置0吧,udp校验和是可选的。
static void update_chksum(struct iphdr *iph, struct udphdr *udp)
{
        //ip
        iph->check = 0;       
        iph->check = ip_fast_csum(iph,iph->ihl);

        //udp
        udp->check = 0;
        udp->check = csum_tcpudp_magic(iph->saddr, iph->daddr,  
            ntohs(udp->len), IPPROTO_UDP,  
            csum_partial(udp, ntohs(udp->len), 0));
}

还有两点不太确定的,不知道对不对,说出来,楼主也考量下,如果说错了,请见谅。
1、看到你代码中在用udp_hdr(skb)取udp头部,这个似乎取不到吧,udp header是第四层的,现在才第三层,应该还没赋值,我记得是这样的。
2、你在做数据操作的时候,你确认你的skb是线性的?

论坛徽章:
0
5 [报告]
发表于 2014-07-03 16:12 |只看该作者
// caculate checksum
    ip_header->check = ip_fast_csum(ip_header, ip_header->ihl);
    nskb->csum = skb_checksum(nskb, ip_header->ihl*4, (sizeof(struct udphdr) + sizeof(szPayLoad)), 0);
    udp_header->check = csum_tcpudp_magic(pIpHdr->daddr, pIpHdr->saddr, (sizeof(struct udphdr) + sizeof(szPayLoad)), IPPROTO_UDP, nskb->csum);

调整一下顺序
                skb->csum = skb_checksum (skb, iph->ihl*4, skb->len - iph->ihl * 4, 0);
                udph->check = csum_tcpudp_magic (iph->saddr, iph->daddr,
                        skb->len - iph->ihl * 4, iph->protocol, skb->csum);
                ip_header->check = ip_fast_csum(ip_header, ip_header->ihl);

论坛徽章:
0
6 [报告]
发表于 2015-04-20 23:38 |只看该作者

感谢各位的答复,转眼2年时间,问题细节已模糊,大概做个交待。

本帖最后由 mybname 于 2015-04-20 23:44 编辑

我的理解,调用dev_queue_xmit()函数发包,与校验和什么的没关系,随便给段码流,函数都会发出去。
但格式不对的话,对端网卡不收。抓包工具自然也抓不到。
关键要目地Mac正确,对端机器上抓包工具设置为抓所有的包,是可以抓到的。
1. 目地Mac对,源Mac错,对端网卡会收。抓包工具抓所有的包,可以抓到。
2. IP头校验和错误,内核解析失败抛弃。
只要对端能收到,配合抓包工具,哪里不对可以看出来了,慢慢调吧。

论坛徽章:
0
7 [报告]
发表于 2015-05-05 16:20 |只看该作者
我现在也在学习内核  刚好在做dns的内核回包,我是在prerouting 中做的,手动封装udp数据包,在postrouting 挂在钩子 监测    结果:没有发出去。我没有封eth头。使用的发送函数是ip_local_out  。不知道您是否解决了最后。望回答:mail:532961704@qq.com
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP