把一个syn报文给rst掉

下面展示一个极其简单的例子,看如何使用netfilter来将一个指定端口的syn报文给rst掉。

//***************************************************************
// https://i.cnblogs.com/EditPosts.aspx?postid=10650263&update=1
//
//    Simple hook test to be called.
//    We drop the syn packet and rst the request
//***************************************************************
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <net/tcp.h>
#include <linux/init.h>

#ifndef NIPQUAD
#define NIPQUAD(addr) 
        ((unsigned char *)&addr)[0], 
        ((unsigned char *)&addr)[1], 
        ((unsigned char *)&addr)[2], 
        ((unsigned char *)&addr)[3]
#endif

#ifndef NIPQUAD_FMT
#define NIPQUAD_FMT "%u.%u.%u.%u"
#endif


#define Serve_Port 5886
#define My_DEBUG_ENABLE 1


static unsigned int test_nf_filter(const struct nf_hook_ops *ops,
                               struct sk_buff *skb,
                               const struct net_device *in,
                               const struct net_device *out,
#ifndef __GENKSYMS__
                               const struct nf_hook_state *state
#else
                               int (*okfn)(struct sk_buff *)
#endif
                               )
{
        struct sk_buff * skb_send;
        struct  iphdr  *iph_send ;
        struct  tcphdr  *tcph_send;
        struct ethhdr *eth_send;
        struct  iphdr  *iph ;
        struct  tcphdr  *tcph;
        struct ethhdr *eth;
        __u32 sip;
        __u32  dip;
        __u16  sport;
        __u16  dport;
        int tcphdrlen, iphdrlen, ethhdrlen;
        int err;

        int tcphdrlen_recv, iphdrlen_recv;

        /*取出mac头部*/
        eth =  (struct ethhdr *)skb_mac_header(skb);

        /*取出IP头,源IP地址.目的IP地址*/
        iph=ip_hdr(skb);
        sip=iph->saddr;
        dip=iph->daddr;

        /*如果不是TCP协议,直接返回*/
        if(iph->protocol != IPPROTO_TCP){
                return NF_ACCEPT;
        }

        tcph = (struct tcphdr *)((__u32 *)iph + iph->ihl);
        sport = tcph->source;
        dport = tcph->dest;

        /*如果不是指定的端口,直接返回*/
        if (ntohs(dport)!=Serve_Port)
        {
                return NF_ACCEPT;
        }
        if(!(tcph->syn))
        {
                return NF_ACCEPT;
        }
        if (My_DEBUG_ENABLE)
        {
               printk("TCP PACKET. ");
               printk("recv packet,syn=%u
,tcph->syn");
               printk("from %d.%d.%d.%d to %d.%d.%d.%d - ",
                         NIPQUAD(sip),NIPQUAD(dip));
               printk("from %u to %u. ",ntohs(sport),ntohs(dport));
               printk("skb->len(%u) ", skb->len);
               printk("seq(%x) ", ntohl(tcph->seq));
               printk("ack_seq(%x) ", ntohl(tcph->ack_seq));
                        printk(".
");
        }


        /*分析tcp载荷,并存放接收的Rtsp信息*/
        iphdrlen_recv = iph->ihl * 4;
        tcphdrlen_recv = tcph->doff * 4;

        /* handle syn packet ,first Malloc up new buffer. */
        ethhdrlen = sizeof(struct ethhdr);
        iphdrlen = sizeof(struct  iphdr);
        tcphdrlen = sizeof(struct  tcphdr);
        /* only respond with one byte */
        skb_send = dev_alloc_skb(ethhdrlen + iphdrlen + tcphdrlen);
        if (skb_send == NULL)
        {
                printk(KERN_WARNING "Memory squeeze, dropping packet.
");
                return NF_ACCEPT;
        }

        if (skb_send)
        {
                /*将头部指针往后移动。然后从高层协议开始填充。*/
                skb_reserve(skb_send, ethhdrlen + iphdrlen + tcphdrlen);

                /*调整TCP指针*/
                tcph_send = (struct tcphdr *)__skb_push(skb_send, tcphdrlen);
                skb_set_transport_header(skb_send, 0);
                tcph_send->source = dport;
                tcph_send->dest = sport;
                tcph_send->seq = tcph->ack_seq;
                //tcph_send->ack_seq =0;
                tcph_send->ack_seq = htonl(ntohl(tcph->seq)+1);

                tcph_send->res1 = 0;
                tcph_send->doff = htons(sizeof(struct tcphdr) >> 2);
                tcph_send->fin = 0;
                tcph_send->syn = 0;
                tcph_send->rst = 1;
                tcph_send->psh = 0;
                tcph_send->ack = 1;
                tcph_send->urg = 0;
                tcph_send->ece = 0;
                tcph_send->cwr = 0;

                //*(((__be16 *)tcph_send) + 6)  = htons(((tcphdrlen >> 2) << 12) |0x0014);
                tcph_send->doff = (tcphdrlen >> 2);


                tcph_send->window = htons(0x3CF2);
                tcph_send->urg_ptr = 0;
                tcph_send->check = 0;

                /*ip头部*/
                iph_send = (struct iphdr * )__skb_push(skb_send, iphdrlen);
                skb_set_network_header(skb_send, 0);

                iph_send->version = 4;
                iph_send->ihl = sizeof(struct iphdr)/4;
                iph_send->tos = 0;
                iph_send->tot_len = htons(iphdrlen + tcphdrlen);
                iph_send->id = 0;
                iph_send->frag_off = 0;
                iph_send->ttl = 55;
                iph_send->protocol = IPPROTO_TCP;
                iph_send->check = 0;
                iph_send->saddr = iph->daddr;
                iph_send->daddr = iph->saddr;
                iph_send->check = 0;
                iph_send->check = ip_fast_csum((unsigned char *)iph_send, iph_send->ihl);

                /*tcp头部校验和初始值计算*/
                skb_send->csum = 0;
                skb_send->ip_summed = CHECKSUM_UNNECESSARY;
                tcph_send->check = tcp_v4_check(tcphdrlen ,  iph_send->saddr, iph_send->daddr,
                                         csum_partial(tcph_send, tcphdrlen , 0));

                /*eth头部*/
                eth_send = (struct ethhdr * )__skb_push(skb_send, ethhdrlen);
                skb_set_mac_header(skb_send, 0);
                memcpy(eth_send->h_dest, eth->h_source, ETH_ALEN);
                memcpy(eth_send->h_source, eth->h_dest, ETH_ALEN);
                eth_send->h_proto = eth->h_proto;

                /*其他配置*/
                skb_send->mac_len = ethhdrlen;
                skb_send->dev = skb->dev;
                skb_send->queue_mapping = skb->queue_mapping;
                skb_send->protocol = htons(ETH_P_IP);;
                skb_send->vlan_tci = skb->vlan_tci;
                skb_send->pkt_type = PACKET_OUTGOING;
                skb_send->mark = 0;
                memset(skb_send->cb, 0, sizeof(skb_send->cb));

                /*完成数据包的发送*/
                err = dev_queue_xmit(skb_send);
                if (err > 0 && (err = net_xmit_errno(err)) != 0)
                {
                    printk(KERN_WARNING "dev_queue_xmit=%d.
", err);
                }
        }

        //*走到这,丢弃所有的包*/
        return NF_DROP;
}

static struct nf_hook_ops test_filter = {
        .owner = THIS_MODULE,
        .list = {NULL, NULL},
        .hook = &test_nf_filter,
        .pf             = PF_INET,
        //.hooknum      = NF_INET_LOCAL_IN, /*NF_IP_LOCAL_IN,*/
        .hooknum        = NF_INET_PRE_ROUTING, /*NF_IP_LOCAL_IN,*/
        .priority       = NF_IP_PRI_FIRST
};

static int testnf_init(void)
{
     printk(KERN_ALERT "Test netfilter!
");

     return nf_register_hook(&test_filter);
}

static void testnf_exit(void)
{
     nf_unregister_hook(&test_filter);

     printk(KERN_ALERT "GOODBYE netfilter!
");
}
MODULE_LICENSE("GPL");
module_init(testnf_init);

module_exit(testnf_exit);

服务器端监听端口:

[root@localhost hook]# gdb ./tcp_sever.o
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/caq/hook/tcp_sever.o...done.
(gdb) r
Starting program: /home/caq/hook/./tcp_sever.o
========waiting for client's request========

端口已经侦听:

[root@localhost hook]# ss -tlp |grep -i 5886
LISTEN     0      10         *:5886                     *:*                     users:(("tcp_sever.o",pid=42818,fd=7))

客户端发起链接,很快被服务器端rst掉。

linux:/home/caq # ./tcp_client.o
connect: Connection refused

执行的抓包结果:

10:39:29.762503 IP (tos 0x0, ttl 64, id 58665, offset 0, flags [DF], proto TCP (6), length 52) 10.229.200.12.13952 > 10.229.200.30.5886: S, cksum 0xa61b (incorrect (-> 0x0ae2), 744885068:744885068(0) win 14600 <mss 1460,nop,nop,sackOK,nop,wscale 9>
        0x0000:  0004 0001 0006 98f5 37e2 f373 0000 0800
        0x0010:  4500 0034 e529 4000 4006 afa5 0ae5 c80c
        0x0020:  0ae5 c81e 3680 16fe 2c66 0b4c 0000 0000
        0x0030:  8002 3908 a61b 0000 0204 05b4 0101 0402
        0x0040:  0103 0309
10:39:29.763024 IP (tos 0x0, ttl 55, id 0, offset 0, flags [none], proto TCP (6), length 40) 10.229.200.30.5886 > 10.229.200.12.13952: R, cksum 0x47b8 (correct), 0:0(0) ack 744885069 win 15602
        0x0000:  0000 0001 0006 74a4 b500 f12e 0000 0800
        0x0010:  4500 0028 0000 0000 3706 dddb 0ae5 c81e
        0x0020:  0ae5 c80c 16fe 3680 0000 0000 2c66 0b4d
        0x0030:  5014 3cf2 47b8 0000 0000 0000 0000

这个简单的示例,是展示netfilter的钩子到底怎么使用,当然,我们不可能对所有的规则都写一个模块实现,所以,用户态的配置工具就出来了,我们后续博客再记录。

有个tcpkill的工具,也可以实现类似的功能,它的原理是利用抓包,然后根据获取的seq和四源组信息,发送rst来干掉链路。如果抓不到包,比如对于一条不活跃的链接,

可以构造syn包,然后等对端回复一个正确的ack,里面有seq信息,再发送rst复位之。

原文地址:https://www.cnblogs.com/10087622blog/p/10650263.html