mybname 发表于 2013-09-20 00:17

NF中调用dev_queue_xmit(skb)发送UDP包,对端没收到。

本帖最后由 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.100: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

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

staticstruct nf_hook_opshttp_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 = {0};
    char szPayLoad = {"--- 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 != uhEthIpProto \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;
}





mybname 发表于 2013-09-21 14:15


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




瀚海书香 发表于 2013-09-23 08:09

回复 2# mybname
在两边分别抓包看一下。另外可以参考本版精华帖合集http://bbs.chinaunix.net/thread-1930079-1-1.html

   

bfdhczw 发表于 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是线性的?

iamhycljc 发表于 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);

mybname 发表于 2015-04-20 23:38

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

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

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

kevin12221 发表于 2015-05-05 16:20

我现在也在学习内核刚好在做dns的内核回包,我是在prerouting 中做的,手动封装udp数据包,在postrouting 挂在钩子 监测    结果:没有发出去。我没有封eth头。使用的发送函数是ip_local_out。不知道您是否解决了最后。望回答:mail:532961704@qq.com
页: [1]
查看完整版本: NF中调用dev_queue_xmit(skb)发送UDP包,对端没收到。