snort_inline中与防火墙联动的实现以及代码inline.c分析

     近来想测试一个防ddos的算法的性能怎么样,但是这总要用代码实现啊,一开始是想在netfiler中实现,即在某个钩子函数下注册一个模块,但是还没怎么开始就停止了,原因就是内核编程可以使你想跳楼!其次想在iptables下实现,因为想到既然iptables能建立一些规则来阻止攻击包,那么至少iptables中应该可以见到内核的包,另外就是有权限控制包的去留,但是要改iptables的代码也不是很好扩展。
     今天在snort的官方主页上见到有是snort-inline,以前用过snort,也见别人用过无线下的snort(wireless snort),但是这个snort绝对不同,因为可以用它和iptables实现IPS!!!而且它可以设置成nf_queue模式,即它分析的包都是防火墙内核中返回到用户态的,只要加这样的命令iptables -A INPUT -p icmp -j QUEUE 即可,其实这归根于Netfiler的NF_queue功能(暂时这么说吧,只是内核于用户态通信的一个机制),当然少不了iptables中libipq库的支持,libipq提供一些函数直接和netfilter通信。
    首先我们要下载snort-inline,它要libnet的支持以及libipq的支持,版本问题也要注意,网上有安装这个的一些解答但相对较少。
    其次我们要知道snort-inline是怎么实现于防火墙的通信的。看inline.h和inline.c
inline.h:

void InitInlinePostConfig(void);

#ifndef IPFW
void IpqLoop();
#else
void IpfwLoop();
#endif /* IPFW */

int InlineDrop(); /* call to drop current packet */
int InlineReject(Packet *); /* call to reject current packet */
int InlineAccept();
int InlineReplace();
int InlineMode();

#else
#define InlineMode(a)    (0)
#endif /* GIDS */


#endif /* __INLINE_H__ */

其实由inlilne.h可以看出来以后要DROP只需在你的头文件中包括inline.h然后调用相关的InlineDrop(); 即可。

inline.c部分代码

 

// $Id: inline.c,v 1.3 2003/02/15 21:46:14 redmaze Exp $

#ifdef GIDS
#include "inline.h"
#include "rules.h"
#include <pcap.h>
#include <string.h>
#include <stdlib.h>
#include <libnet.h>

#define PKT_BUFSIZE 65536

/* Most of the code related to libnet (resets and icmp unreach) was
 * taken from sp_respond.c */


/* vars */
int libnet_nd; /* libnet descriptor */
char errbuf[LIBNET_ERRBUF_SIZE];

Packet *tmpP;

char *l_tcp, *l_icmp;

/* predeclarations */
#ifndef IPFW
void HandlePacket(ipq_packet_msg_t *);
void TranslateToPcap(ipq_packet_msg_t *, struct pcap_pkthdr *);
#else
void HandlePacket();
void TranslateToPcap(struct pcap_pkthdr *phdr, ssize_t len);
#endif /* IPFW */
void ResetIV(void);


/**
 * InlineMode - determine if we are in inline mode
 *
 * @returns 1 if we are in inline mode, 0 otherwise
 */

int InlineMode()
{
    if (pv.inline_flag)    /*设置了inline模试flag就等于1*/
        return 1;

    return 0;
}


#ifndef IPFW
void TranslateToPcap(ipq_packet_msg_t *m, struct pcap_pkthdr *phdr)
{
    static struct timeval t;
    if (!m->timestamp_sec)
    {
        memset (&t, 0, sizeof(struct timeval));
        gettimeofday(&t, NULL);
        phdr->ts.tv_sec = t.tv_sec;
        phdr->ts.tv_usec = t.tv_usec;
    }
    else
    {
        phdr->ts.tv_sec = m->timestamp_sec;
        phdr->ts.tv_usec = m->timestamp_usec;
    }
    phdr->caplen = m->data_len;
    phdr->len = m->data_len;
    /* copy the mark to the iv struct, so we can reach it inside stream4 */
    iv.mark = m->mark;

}
#else
#endif


void ResetIV()
{
    iv.drop = 0;
    iv.reject = 0;
    iv.replace = 0;
    iv.mark = 0;
}


/*
 * Function: void InitInlinePostConfig
 *
 * Purpose: perform initialization tasks that depend on the configfile
 *
 * Args: none
 *
 * Returns: nothing void function
 */

void InitInlinePostConfig(void)
{
    int tcp_size = 0;
    int icmp_size = 0;

    //printf("InitInline stage 2: InitInlinePostConfig starting...\n");


    /* Let's initialize Libnet, but not if we are in
     * layer 2 resets mode, because we use the link
     * layer then... */

#ifndef IPFW
    if(pv.layer2_resets)
    {
        tcp_size = ETH_H + IP_H + TCP_H;
        icmp_size = 128 + ETH_H;
    }
    else
#endif
    {
        //printf("opening raw socket in IP-mode\n");

 
    if((libnet_nd = libnet_open_raw_sock(IPPROTO_RAW)) < 0)
    {
     fprintf(stdout, "InitInline: Could not open raw socket for libnet\n");
     exit(-1);
    }

        tcp_size = IP_H + TCP_H;
        icmp_size = 128;
    }

    /* init */
    l_tcp = calloc(tcp_size, sizeof(char));
    if (l_tcp == NULL)
    {
        perror("InitInline: Could not allocate l_tcp\n");
        exit(-1);
    }
    l_icmp = calloc(icmp_size, sizeof(char));
    if (l_icmp == NULL)
    {
        perror("InitInline: Could not allocate l_icmp\n");
        exit(-1);
    }


#ifndef IPFW
    if(pv.layer2_resets)
    {
        /* Building Layer 2 Reset Packets */
        printf("building cached link layer reset packets\n");
   
        libnet_build_ip(TCP_H, 0, libnet_get_prand(PRu16), 0, 255,
                        IPPROTO_TCP, 0, 0, NULL, 0, l_tcp + ETH_H);

        libnet_build_tcp(0, 0, 0, 0, TH_RST|TH_ACK, 0, 0, NULL, 0,
                         l_tcp + ETH_H + IP_H);

        /* create icmp cached packet */
        libnet_build_ip(ICMP_UNREACH_H, 0, libnet_get_prand(PRu16), 0,
                        255, IPPROTO_ICMP, 0, 0, NULL, 0, l_icmp + ETH_H);
        libnet_build_icmp_unreach(3, 3, 0, 0, 0, 0, 0, 0, 0, 0, NULL, 0,
                                  l_icmp + ETH_H + IP_H);
    }
    else
#endif
    {
        /* Building Socket Reset Packets */
        printf("building cached socket reset packets\n");
  
        libnet_build_ip(TCP_H, 0, libnet_get_prand(PRu16), 0, 255,
                        IPPROTO_TCP, 0, 0, NULL, 0, l_tcp);

        libnet_build_tcp(0, 0, 0, 0, TH_RST|TH_ACK, 0, 0, NULL, 0,
                         l_tcp + IP_H);

        /* create icmp cached packet */
        libnet_build_ip(ICMP_UNREACH_H, 0, libnet_get_prand(PRu16), 0,
                        255, IPPROTO_ICMP, 0, 0, NULL, 0, l_icmp);
        libnet_build_icmp_unreach(3, 3, 0, 0, 0, 0, 0, 0, 0, 0, NULL, 0,
                                  l_icmp + IP_H);
    }

}


/* InitInline is called before the Snort_inline configuration file is read. */
int InitInline()//初始化
{
    int status;

#ifdef DEBUG_GIDS
    printf("Initializing Inline mode \n");
#endif

    printf("Initializing Inline mode \n");

#ifndef IPFW
    ipqh = ipq_create_handle(0, PF_INET);
    if (!ipqh)
    {
        ipq_perror("InlineInit: ");
        ipq_destroy_handle(ipqh);
        exit(1);
    }
 
    status = ipq_set_mode(ipqh, IPQ_COPY_PACKET, PKT_BUFSIZE);
    if (status < 0)
    {
        ipq_perror("InitInline: ");
        ipq_destroy_handle(ipqh);
        exit(1);
    }
#endif /* IPFW */

    ResetIV();//重设iv结构

    /* Just in case someone wants to write to a pcap file
     * using DLT_RAW because iptables does not give us datalink layer.
     */

    pd = pcap_open_dead(DLT_RAW, SNAPLEN);

    return 0;
}

#ifndef IPFW
void IpqLoop()  //对捕获的包进行的操作
{
    int status;
    struct pcap_pkthdr PHdr;
    unsigned char buf[PKT_BUFSIZE];
    static ipq_packet_msg_t *m;

#ifdef DEBUG_GIDS
    printf("Reading Packets from ipq handle \n");
#endif

    while(1)
    {
        ResetIV();
        status = ipq_read(ipqh, buf, PKT_BUFSIZE, 0);
        if (status < 0)
        {
            ipq_perror("IpqLoop: ");
        }
        else
        {
            switch(ipq_message_type(buf))
            {
                case NLMSG_ERROR:
                    fprintf(stderr, "Received error message %d\n",
                            ipq_get_msgerr(buf));
                    break;

                case IPQM_PACKET:
                    m = ipq_get_packet(buf);
#ifdef DEBUG_INLINE
                    printf("%02X:%02X:%02X:%02X:%02X:%02X\n", m->hw_addr[0], m->hw_addr[1],
                           m->hw_addr[2], m->hw_addr[3], m->hw_addr[4], m->hw_addr[5]);
#endif

                    TranslateToPcap(m, &PHdr);
                    ProcessPacket(NULL, &PHdr, (u_char *)m->payload);
                    HandlePacket(m);//以上两个函数是对包处理,处理结果有这函数决定
                    break;
            } /* switch */
        } /* if - else */
    } /* while() */
}
#else


/* Loop reading packets from IPFW
   - borrowed mostly from the TCP-MSSD daemon in FreeBSD ports tree
    Questions, comments send to: nick@rogness.net
*/

#endif


/*
 * Function: static void RejectSocket
 *
 * Purpose: send a reject packet (tcp-reset or icmp-unreachable
 *
 * Args: none
 *
 * Returns: nothing void function
 */



/*
 * Function: static void RejectLayer2(ipq_packet_msg_t *m)
 *
 * Purpose: send a reject packet (tcp-reset or icmp-unreachable
 *
 * Args: the ipq_packet_msg_t m for determining the output interface
 * and the source mac for our packet.
 *
 * Returns: nothing void function
 *
 * TODO: make it also work on *BSD.
 */

#ifndef IPFW
#ifndef IPFW
void HandlePacket(ipq_packet_msg_t *m)
#else
void HandlePacket()
#endif
{
    int status;

    if (iv.drop)
    {
#ifndef IPFW
        status = ipq_set_verdict(ipqh, m->packet_id, NF_DROP, 0, NULL);
        if (status < 0)
        {
            ipq_perror("NF_DROP: ");
        }
#endif
        if (iv.reject)
        {
#ifndef IPFW
     if(pv.layer2_resets)
     {
        RejectLayer2(m);
     }
     else
#endif
     {
        RejectSocket();
     }
        }
    }
#ifndef IPFW
    else if (!iv.replace)
    {
        status = ipq_set_verdict(ipqh, m->packet_id, NF_ACCEPT, 0, NULL);
        if (status < 0)
        {
            ipq_perror("NF_ACCEPT: ");
        }
    }
    else
    {
        status = ipq_set_verdict(ipqh, m->packet_id, NF_ACCEPT,
                 m->data_len, m->payload);
        if (status < 0)
        {
            ipq_perror("NF_ACCEPT: ");
        }
    }
#endif
}
  

int InlineDrop()
{
    //printf("InlineDrop(): dropping\n");

    iv.drop = 1;
    return 0;
}

int InlineReject(Packet *p)
{
    //printf("InlineReject(): rejecting\n");

    iv.reject = 1;
    iv.drop = 1;
    tmpP = p;
    return 0;
}

int InlineAccept()
{
    iv.drop = 0;
    return 0;
}

int InlineReplace()
{
    iv.replace = 1;
    return 0;
}

#endif /* GIDS */

只要稍微分析一下,联系上下文就可看出有了这个inline的实现,在snort中可以任由我们的发挥了,你可以在你的预处理器中#include"inline.h",然后判断后调用相关的DROP即可以实现在防火墙的功能。
posted @ 2009-03-02 09:20  有安科技  阅读(2077)  评论(0编辑  收藏  举报