netfilter 子系统实现tcp断链

netfiler的优势不仅包含防火墙的实现,还包括各种报文的处理工作(如报文的加密,统计等)。可以方便地利用netfilter提供的接口实现内核态的报文处理。

在netfilter中可以解析报文,同时根据阻断规则做匹配,将符合阻断的报文直接DROP,但是在tcp的会话中,这样并不优雅,tcp是可靠传输,如果是直接drop掉某一个报文,则发送端会一直重发,所以在tcp的协议中需要向发送端发送一个fin或者是rst的报文,用来断来链接。下面是我测试使用的代码。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/udp.h>
#include <linux/ip.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/etherdevice.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/inet.h>
#include <linux/types.h>
#include <linux/string.h>

#include <net/checksum.h>
#include <net/tcp.h>
#include <net/netfilter/nf_conntrack.h>

//模块声明 
//GPL, Dual BSD/GPL, Dual MPL/GPL, Proprietary
//内核可以识别上述四种许可方式,没有采用上面的则被假定为私有的,内核加载这种私有模块会被“污染”
MODULE_LICENSE("GPL");

#define NIPQUAD(addr)          \
        ((uint8_t *)&addr)[0], \
        ((uint8_t *)&addr)[1], \
        ((uint8_t *)&addr)[2], \
        ((uint8_t *)&addr)[3], \
        addr

#define MAC_FFF(mac)          \
        ((uint8_t *)&mac)[0], \
        ((uint8_t *)&mac)[1], \
        ((uint8_t *)&mac)[2], \
        ((uint8_t *)&mac)[3], \
        ((uint8_t *)&mac)[4], \
        ((uint8_t *)&mac)[5]

#define BLOKEN_IP (1107077312) //  断链的IP (192.168.252.65 网络字节序)

const char* client_break_msg = "C_break"; //检查payload 前面的字符

static uint32_t ipv4_hook_in_func(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
{    
    //struct SKernelMsgIpV4Stats _ipv4Stats;
    struct ethhdr *eth_header = (struct ethhdr *)skb_mac_header(skb); //eth_hdr
    struct iphdr *ip_header = (struct iphdr *)skb_network_header(skb); //ip_hdr
    struct tcphdr *tcp_header = NULL;
    unsigned int nip_hdr_len;
    unsigned int ntcp_hdr_len;
    struct net_device * send_dev = NULL;
    int i=0;
    uint16_t nt_header=0;
    unsigned char *payload=NULL;
    uint16_t payload_len=0;
    int is_bloken = 0;

    struct sk_buff *fack_skb = NULL;
    struct ethhdr *fack_eth_header = NULL; //eth_hdr
    struct iphdr *fack_ip_header = NULL; //ip_hdr
    struct tcphdr *fack_tcp_header = NULL;

    if (NULL == skb || NULL == eth_header || NULL == ip_header)
    {
        return NF_ACCEPT;
    }

    //tcp协议
    if(likely(ip_header->protocol==IPPROTO_TCP))
    {
        tcp_header = (struct tcphdr *)skb_transport_header(skb); //tcp_hdr
        if (NULL == tcp_header)
        {
            return NF_ACCEPT;
        }
        
        //判断 包中ip是不是符合需要 断链的IP
        if (BLOKEN_IP == ip_header->saddr || BLOKEN_IP == ip_header->daddr)
        {
            printk(KERN_DEBUG "ipv4_hook_func. %02x:%02x:%02x:%02x:%02x:%02x_%u.%u.%u.%u(%u):%u->%02x:%02x:%02x:%02x:%02x:%02x_%u.%u.%u.%u(%u):%u. seq:%u ack:%u \n"
                , MAC_FFF(eth_header->h_source), NIPQUAD(ip_header->saddr), ntohs(tcp_header->source)
                , MAC_FFF(eth_header->h_dest), NIPQUAD(ip_header->daddr), ntohs(tcp_header->dest), tcp_header->seq, tcp_header->ack_seq);
            is_bloken
            nip_hdr_len = ip_header->ihl << 2; //IP头长度
            ntcp_hdr_len = tcp_header->doff << 2; //tcp头长度
            nt_header = nip_hdr_len + ntcp_hdr_len;
            payload = skb->data + nt_header;
            payload_len = skb->len - nt_header;

            if(payload_len > 0)
            {
                printk("tcp segment msg: "); 
                for(i=0;i<payload_len;i++)
                {
                    printk("%c", payload[i]);
                }
                printk("\r\n");
            }
            
            //检查payload中 从第一个字符开始是否符合
            if(payload_len >= strlen(client_break_msg))
            {
                is_bloken = 1;
                for(i=0; i < strlen(client_break_msg); i++)
                {
                    if(payload[i] != client_break_msg[i])
                    {
                        is_bloken = 0;
                        break;
                    }       
                }
            }
            
            //符号断链
            if(is_bloken)
            {
                printk("block.......\r\n");
                if(skb->data_len!=0)
                {           
                    if(skb_linearize(skb))
                    {
                        printk("error line skb\r\n");
                        printk("skb->data_len %d\r\n",skb->data_len);
                        return NF_DROP;
                    }
                }

                //获取包出去的网卡
                send_dev = dev_get_by_name(&init_net, "eth0");
                //send_dev = dev_get_by_name(&init_net, skb->dev->name);
                if(send_dev == NULL)
                {
                    printk("%s\n\n", "dev_get_by_name return NULL");
                    return NF_DROP;
                }

                //分配一个sk_buff 这个包发送给源端,包含了tcp的fin或事rst,用来断链,这个包不需要payload数据
                fack_skb = alloc_skb(LL_RESERVED_SPACE (send_dev) + sizeof (struct iphdr) + sizeof (struct tcphdr), GFP_ATOMIC);
                if(NULL == fack_skb)
                {
                    printk("skb_copy error\n\n");
                    return NF_DROP;
                }

                fack_skb->pkt_type = PACKET_OTHERHOST;
                fack_skb->protocol = skb->protocol;// __constant_htons(ETH_P_IP);
                fack_skb->ip_summed = CHECKSUM_NONE;
                fack_skb->priority = 0;

                //skb_reset_mac_header, skb_reset_network_header, skb_reset_transport_header
                //skb_mac_header, skb_network_header, skb_transport_header
                //skb_put、skb_push、skb_pull、skb_reserve
                
                //fack_skb是一个新的 sk_buff,可以将data和tail都置到末尾,然后自顶向下填充数据
                //data tail 全部置到末尾
                skb_reserve (fack_skb, LL_RESERVED_SPACE (send_dev) + sizeof (struct iphdr) + sizeof (struct tcphdr));

                //tcp data前移,此时data开始位置为tcphdr开始位置
                skb_push(fack_skb, sizeof (struct tcphdr));
                skb_reset_transport_header(fack_skb);
                {
                    fack_tcp_header = (struct tcphdr *) skb_transport_header(fack_skb);
                    memset (fack_tcp_header, 0, sizeof (struct tcphdr));
                    fack_tcp_header->source = tcp_header->dest; //用原包的目的port填充
                    fack_tcp_header->dest = tcp_header->source; //用原包的源port填充
                    fack_tcp_header->seq = tcp_header->ack_seq; //用原包的ack填充
                    fack_tcp_header->ack_seq = htonl(ntohl(tcp_header->seq) + payload_len); //这个需要确认收到的原包,注意字节序
                    fack_tcp_header->doff = sizeof(struct tcphdr)>>2;
                    fack_tcp_header->psh = 0;
                    fack_tcp_header->rst = 1; //这里将rst置为1,用于断链
                    fack_tcp_header->fin = 0;
                    fack_tcp_header->syn = 0;
                    fack_tcp_header->ack = 1;
                    fack_tcp_header->window = __constant_htons (5840);
                    fack_tcp_header->check = 0;
                }

                //ip data前移
                skb_push(fack_skb, sizeof (struct iphdr));
                skb_reset_network_header(fack_skb);
                {
                    fack_ip_header = (struct iphdr*) skb_network_header(fack_skb);
                    memset (fack_ip_header, 0, sizeof (struct iphdr));
                    fack_ip_header->version = ip_header->version;
                    fack_ip_header->ihl = sizeof(struct iphdr)>>2;
                    fack_ip_header->frag_off = 0x40;
                    fack_ip_header->protocol = ip_header->protocol;
                    fack_ip_header->tos = 0;
                    fack_ip_header->daddr = ip_header->saddr;
                    fack_ip_header->saddr = ip_header->daddr;
                    fack_ip_header->ttl = 0x40;
                    fack_ip_header->tot_len = __constant_htons(fack_skb->len);
                    fack_ip_header->check = 0;
                    fack_ip_header->check=ip_fast_csum((unsigned char*)fack_ip_header, fack_ip_header->ihl); //ip层的校验和
                }

                //校验和
                fack_skb->csum = 0;
                //fack_skb->csum = csum_partial(fack_tcp_header, sizeof(struct tcphdr), 0);
                fack_skb->csum = skb_checksum (fack_skb, sizeof(struct iphdr), sizeof(struct tcphdr), 0);
                fack_tcp_header->check = csum_tcpudp_magic (fack_ip_header->saddr, fack_ip_header->daddr, 
                                             sizeof(struct tcphdr), fack_ip_header->protocol, fack_skb->csum);
                
                //mac层
                skb_push(fack_skb, ETH_HLEN);
                skb_reset_mac_header(fack_skb);
                if(skb_mac_header_was_set(skb))
                {
                    fack_eth_header = (struct ethhdr *)skb_mac_header(fack_skb); //eth_hdr
                    memcpy(fack_eth_header->h_dest, eth_header->h_source, ETH_ALEN);
                    memcpy(fack_eth_header->h_source, eth_header->h_dest, ETH_ALEN);
                }
                fack_eth_header->h_proto = eth_header->h_proto;

                fack_skb->dev = send_dev;

                dev_hold(fack_skb->dev);
                printk("%s\n", "dev_hold ok");

                if (dev_queue_xmit(fack_skb) < 0)
                {
                    printk("dev_queue_xmit() error\n");
                    dev_put(fack_skb->dev);
                    kfree_skb(fack_skb);
                    return NF_DROP;
                }
                printk(KERN_DEBUG "ipv4_hook_func_sour. %02x:%02x:%02x:%02x:%02x:%02x_%u.%u.%u.%u(%u):%u->%02x:%02x:%02x:%02x:%02x:%02x_%u.%u.%u.%u(%u):%u seq:%u ack:%u. \n\n"
                , MAC_FFF(fack_eth_header->h_source), NIPQUAD(fack_ip_header->saddr), ntohs(fack_tcp_header->source)
                , MAC_FFF(fack_eth_header->h_dest), NIPQUAD(fack_ip_header->daddr), ntohs(fack_tcp_header->dest), fack_tcp_header->seq, fack_tcp_header->ack_seq);

            }
        }
    }
    
    return NF_ACCEPT;
}

static struct nf_hook_ops ipv4_nfin = {
    .hook = ipv4_hook_in_func,
    .hooknum = NF_INET_PRE_ROUTING,
    .pf = PF_INET,
    .priority = NF_IP_PRI_CONNTRACK - 1, // NF_IP_PRI_CONNTRACK
};

//子模块初始化回调函数
static int __init init_nf(void)
{
    int ret = 0;
    if(0 != nf_register_hook(&ipv4_nfin))
    {
        printk(KERN_ERR "nf_register_hook(&ipv4_nfin) error");
        ret = -1;
    }

    printk(KERN_INFO "Register netfilter module succ.\n");
    
    return 0;
}

//子模块销毁回调函数
static void __exit exit_nf(void)
{
    nf_unregister_hook(&ipv4_nfin);
    printk(KERN_INFO "Unregister netfilter module succ.\n");
}


module_init(init_nf);
module_exit(exit_nf);

MODULE_AUTHOR("xuwaiwai"); //作者描述
MODULE_DESCRIPTION("TCP broken chain test demo");//模块用途的简短描述
MODULE_VERSION("0.0.1");//模块的版本号
MODULE_ALLAS("broken ipv4");//模块的别名

例子中直接新分配了一个 skb_buff, 然后在此结构中填充依次L4,L3,L2的数据并发送。

关于编译可以参考:netfilter模块编译和运行


凡是过往,即为序章  

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值