世事皆虚幻 发表于 2016-05-06 23:17

在模块加载时构造SYN包并发送到本机

Hi 大大们,在下初学 Netfilter 遇到一个问题,麻烦大家帮帮忙,在此先谢谢了~

有一台客户端机器为 Win7 系统,服务器为 RHEL-6.2 x86_64 系统,现在在服务器上编写了一个内核模块,并在加载时向服务器上层发送 SYN 包,源地址、端口为客户端地址 172.30.40.26:1234, 目标地址为服务器地址 172.30.40.90:80,
并且在服务器上已开启 80 端口。我的代码如下,在客户端上使用 Wireshark 抓包,测试结果:
1. 调用 dev_queue_xmit(skb) 时能在客户端抓到抓,由于目标地址不为客户端,所以客户端无响应;
2. 调用 dst_input(skb) 时,在客户端不能抓到来自服务端响应的 SYN_ACK;
3. 调用 netif_rx(skb) 与第 2 点测试情况相同;

skb 中加入 MAC 头或不加,测试结果相同。
请各位高手帮忙一下,看看代码哪里有问题?非常谢谢~
int SendTcpPacket(__be32 sip, __be16 sport, __be32 dip, __be16 dport, __be32 seq, __u8 syn, __u8 fin, __u8 rst, __u8 ack, __be32 seq_ack)
{
        struct dst_entry* dest = NULL;
        struct sk_buff *skb;
        struct tcphdr* tcph;
        struct iphdr *iph;
        struct ethhdr *ethh;
        int ip_payload_size;
        int result;

        unsigned char mac_source = {0xC0, 0x3F, 0xD5, 0x71, 0x97, 0xA5};
        unsigned char mac_dest   = {0x00, 0x0C, 0x29, 0x51, 0x97, 0xA7};

        skb = alloc_skb(MAX_TCP_HEADER + 128, GFP_ATOMIC|__GFP_ZERO);
        if (skb == NULL)
        {
                printk(KERN_ALERT"Failed to alloc_skb\n");
                return -1;
        }
        skb_reserve(skb, MAX_TCP_HEADER);

        // TCP header
        tcph = (struct tcphdr *)skb_push(skb, sizeof(struct tcphdr));
        tcph->source= sport;
        tcph->dest    = dport;
        tcph->doff    = (sizeof(struct tcphdr) >> 2);
        tcph->seq   = seq;
        tcph->syn   = syn;
        tcph->fin   = fin;
        tcph->rst   = rst;
        tcph->ack   = ack;
        tcph->ack_seq = seq_ack;
        tcph->window   = htons(0xFFFF);
        skb_reset_transport_header(skb);

        // IP Header
        iph = (struct iphdr *)skb_push(skb, sizeof(struct iphdr));
        iph->ihl      = (sizeof(struct iphdr) >> 2);
        iph->version= 4;
        iph->tot_len= htons(skb->len);
        iph->ttl      = 64;
        iph->protocol = IPPROTO_TCP;
        iph->saddr    = sip;
        iph->daddr    = dip;
        ip_send_check(iph);
        skb_reset_network_header(skb);

        // MAC header
        ethh = (struct ethhdr *)skb_push(skb, sizeof(struct ethhdr));
        memcpy(ethh->h_source, mac_source, ETH_ALEN);
        memcpy(ethh->h_dest, mac_dest, ETH_ALEN);
        ethh->h_proto = __constant_htons(ETH_P_IP);
        skb_reset_mac_header(skb);

        ip_payload_size = skb->len - sizeof(struct ethhdr) - sizeof(struct iphdr);
        skb->csum = skb_checksum(skb, sizeof(struct ethhdr) + sizeof(struct iphdr), ip_payload_size, 0);
        tcph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, ip_payload_size, iph->protocol, skb->csum);
        skb->ip_summed = CHECKSUM_UNNECESSARY;
        skb->pkt_type = PACKET_HOST;

        if (0 != get_dest(sip, &dest))
        {
                printk(KERN_ALERT"Failed to get_dest\n");
                return -1;
        }

        skb->dev = dest->dev;
        skb_dst_set(skb, dest);

        result = dev_queue_xmit(skb);
//        result = dst_input(skb);
//        result = dst_output(skb);
//        result = netif_rx(skb);
        if (result != NET_RX_SUCCESS)
        {
                kfree_skb(skb);
                printk(KERN_ALERT"Failed to send packet. ErrCode=%d\n", result);
        }
        else
        {
                printk(KERN_ALERT"Send packet succeeded from %pI4:%d to %pI4:%d\n", &sip, ntohs(sport), &dip, ntohs(dport));
        }

        return 0;
}

Godbach 发表于 2016-05-06 23:29

回复 1# 世事皆虚幻

你在服务器上构造 SYN 包,然后发给上层,那还要让走 dev_queue_xmit() 啊?


   

世事皆虚幻 发表于 2016-05-06 23:38

回复 2# Godbach


    谢谢关注,我是调了一下这个函数测试一下在客户端抓包,Wireshark 分析出计算的 checksum 是正确的,先保证一下构造的 sk_buff 内容是正确的。

世事皆虚幻 发表于 2016-05-06 23:53

回复 2# Godbach


    麻烦版主帮忙看看,谢谢~

Godbach 发表于 2016-05-07 00:14

回复 3# 世事皆虚幻

你构造包到底发给谁,发给 server 的话,为什么还要走 dev_queue_xmit ?

   

世事皆虚幻 发表于 2016-05-07 00:19

回复 5# Godbach


    多谢版主回复,我是要在服务器中运行内核模块,发送给本机。其实我是想调用 dst_input(skb) 函数,理论上发送到上层后,服务器会向源地址回应 SYN_ACK, 但是我在客户端上抓不到包。

Godbach 发表于 2016-05-07 00:34

回复 1# 世事皆虚幻

1. 调用 dev_queue_xmit(skb) 时能在客户端抓到抓,由于目标地址不为客户端,所以客户端无响应;

你构造的 SYN 包源 IP 是 client 的 IP 吗。

不太清楚你写这个程序,要是做什么的。就算是构造包,用的环境感觉也有些非常规。
   

世事皆虚幻 发表于 2016-05-07 00:41

回复 7# Godbach


    多谢回复。我是这个需求,有二台服务器,实现主备,当主服务器宕机时,将客户端的 TCP 连接转移至备机。
目前在主备上部署 heartbeat 模块,实现宕机后的 IP 漂移。在这二台服务器上,运行着二个相同的内核模块,有客户端连接时,主服务器会将连接信息通过 UDP 同步至备机,一旦主机挂掉,备机会切为主机,备机会与上层发送 SYN 报文,并拦截上层的 SYN_ACK, 回复 ACK 后 NF_DROP 掉,这样的话,便可以保证该 TCP 连接相对于客户端来说是未断开的。

目前阻塞在备机向上层发送 SYN 报文这儿,SYN 报文里填写的源地址是客户端的 IP。

Godbach 发表于 2016-05-07 00:49

回复 8# 世事皆虚幻

你发 syn 包的话,相当于初始连接。而大多时候,主机 down 掉了,现有的了连接要是在备机上维持,要保证 seq/ack 等信息都一样的。



   

世事皆虚幻 发表于 2016-05-07 00:52

回复 9# Godbach


    这个没问题,主机上客户端的发送序号和服务端的发送序号都同步过去了,在我的模块中都会转换。
现在我的问题是,构造出一个 SYN 报文的 skb, 用 dst_input() 函数发给上层没反应,不知哪儿出问题了。
页: [1] 2
查看完整版本: 在模块加载时构造SYN包并发送到本机