fjx16852318 发表于 2017-6-1 12:52:00

Linux Netfilter开发小结

前置知识:







IP包:
struct ip {
#if BYTE_ORDER == LITTLE_ENDIAN
        unsigned char        ip_hl:4,                /* header length */
                ip_v:4;                        /* version */
#endif
        unsigned char        ip_tos;                        /* type of service */
        short        ip_len;        /* total length */
        unsigned short        ip_id;/* identification */
        short        ip_off;               
        unsigned char        ip_ttl;/* time to live */
        unsigned char        ip_p;/* protocol */
        unsigned short        ip_sum;       
          struct        in_addr ip_src,ip_dst;/* source and dest address */
};



IHL(Internet Header Length 报头长度),位于IP报文的第二个字段,4位,表示IP报文头部按32位字长(32位,4字节)计数的长度,也即报文头的长度等于IHL的值乘以4。 (ip_hl)




TCP头
struct tcphdr {
        u_short        th_sport;        /* source port */
        u_short        th_dport;        /* destination port */
        tcp_seq        th_seq;        /* sequence number */
        tcp_seq        th_ack;        /* acknowledgement number */
#if BYTE_ORDER == LITTLE_ENDIAN
        u_char        th_x2:4,        /* (unused) */
                th_off:4;        /* data offset */
#endif
#if BYTE_ORDER == BIG_ENDIAN
        u_char        th_off:4,        /* data offset */
                th_x2:4;        /* (unused) */
#endif
        u_char        th_flags;
#define        TH_FIN        0x01
#define        TH_SYN        0x02
#define        TH_RST        0x04
#define        TH_PUSH        0x08
#define        TH_ACK        0x10
#define        TH_URG        0x20
#define TH_FLAGS (TH_FIN|TH_SYN|
             TH_RST|TH_ACK|TH_URG)


        u_short        th_win;        /* window */
        u_short        th_sum;        /* checksum */
        u_short        th_urp;        /* urgent pointer */
};





UDP
struct udphdr
{
        u_short        uh_sport;                /* source port */
        u_short        uh_dport;                /* destination port */
        short        uh_ulen;                /* udp length */
        u_short        uh_sum;       
                /* udp checksum */
};





ARP
typedef struct _ETHERNET_FRAME
{
    BYTE    DestinationAddress;
       BYTE    SourceAddress;    
              WORD    FrameType;    // in host-order
} EHTERNET_FRAME, *PETHERNET_FRAME;


typedef struct _ARP_HEADER
{
        WORD    HardType;      //硬件类型
        WORD    ProtocolType;//协议类型
        BYTE    HardLength;    //硬件地址长度
        BYTE    ProtocolLength; //协议地址长度
        WORD    Opcode;      //操作类型   
        BYTE    SourceMAC;      
        BYTE    SourceIP;      
        BYTE    DestinationMAC;   
        BYTE    DestinationIP;   
} ARP_HEADER, *PARP_HEADER;


typedef struct _ARP
{    
        EHTERNET_FRAME EthernetFrame;    
        ARP_HEADERArpHeader;
}ARP, *PARP;









其他的可以看或文章最尾部的参考文章
数据包报头百度百科
http://baike.baidu.com/link?url=DuxdZVeorGksQX94G8UP19wx_iy-o504SjAhiQjZuRWSWNaZEVthpt6cm4L_z0FryXPqF4-YPtaN0UBbe_8Yeq


基于linux内核的网络防火墙开发
Linux核心网络堆栈中有一个全局变量 :
struct list_head nf_hooks,该变量是一个二维数组,其中第一维用于指定协议族,第二维用于指定hook的类型(即5个HOOK点 )。注册一个Netfilter hook实际就是在由协议族和hook类型确定的链表中添加一个新的节点。




在Linux防火墙开发中,有5个地方可以拦截




图很清楚的说明了在对应的位置能干什么


Filter:包过滤
Nat:地址转换
Mangle:修改包数据


规则链:
五个钩子函数(hook functions),也叫五个规则链。
1.PREROUTING (路由前)
2.INPUT (数据包流入口)
3.FORWARD (转发管卡)
4.OUTPUT(数据包出口)
5.POSTROUTING(路由后)
这是NetFilter规定的五个规则链,任何一个数据包,只要经过本机,必将经过这五个链中的其中一个链。


5个HOOK点的定义:
NF_INET_PRE_ROUTING    在完整性校验之后,选路确定之前
NF_INET_LOCAL_IN      在选路确定之后,且数据包的目的是本地主机
NF_INET_FORWARD      目的地是其它主机地数据包
NF_INET_LOCAL_OUT   来自本机进程的数据包在其离开本地主机的过程中
NF_IP_POST_ROUTING    在数据包离开本地主机“上线”之前
NF_INET_POST_ROUTING 同上


对包的处理结果:
NF_DROP      丢弃该数据包
NF_ACCEPT    保留该数据包
NF_STOLEN    忘掉该数据包
NF_QUEUE   将该数据包插入到用户空间
NF_REPEAT    再次调用该hook函数


优先级:
enum nf_ip_hook_priorities {
      NF_IP_PRI_FIRST = INT_MIN,
      NF_IP_PRI_CONNTRACK_DEFRAG = -400,
      NF_IP_PRI_RAW = -300,
      NF_IP_PRI_SELINUX_FIRST = -225,
      NF_IP_PRI_CONNTRACK = -200,
      NF_IP_PRI_MANGLE = -150,
      NF_IP_PRI_NAT_DST = -100,
      NF_IP_PRI_FILTER = 0,
      NF_IP_PRI_SECURITY = 50,
      NF_IP_PRI_NAT_SRC = 100,
      NF_IP_PRI_SELINUX_LAST = 225,
      NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
      NF_IP_PRI_LAST = INT_MAX,
};
一般写NF_IP_PRI_FIRST啦~或+1 +2...




///////////////////////////////////////////////////开始编写防火墙//////////////////////////////////////////////////////////////////
在注册之前 我们需要填写一个结构

struct nf_hook_ops {
      struct list_head list;
      /* 此下的值由用户填充 */
      nf_hookfn *hook//回调函数;
      int pf;//协议 IPV4 还是ipV6
      int hooknum;//hook点
      /* Hook以升序的优先级排序 */
   int priority;
};

例子:
struct nf_hook_ops   nfho;
    /* 填充我们的hook数据结构 */    
    nfho.hook = hook_func;/* 处理函数 */    
    nfho.hooknum= NF_INET_PRE_ROUTING;
    /* 使用IPv4的第一个hook */    
    nfho.pf       = PF_INET;
    //优先级 /* 让我们的函数首先执行 */
    nfho.priority = NF_IP_PRI_FIRST;

过滤函数:
        unsigned int hook_func(
                               unsigned int hooknum,                      
                               struct sk_buff *skb,                      
                               const struct net_device *in,                      
                               const struct net_device *out,                      
                               int (*okfn)(struct sk_buff *))
        {    
                return NF_DROP;/* 丢弃所有的数据包 */       
        }

   
    //初始化函数调用
    nf_register_hook(&nfho);
    //卸载函数中调用
    nf_unregister_hook(&nfho);

完整代码:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>


static struct nf_hook_ops nfho;


static unsigned int hook_func(unsigned int hooknum,
                     struct sk_buff *skb,
                     const struct net_device *in,
                     const struct net_device *out,
                     int (*okfn)(struct sk_buff *))
{
    return NF_ACCEPT;
}


static int nf_init(void)
{
    nfho.hook = hook_func;
    nfho.hooknum= NF_INET_PRE_ROUTING;
    nfho.pf       = PF_INET;
    nfho.priority = NF_IP_PRI_FIRST;


    nf_register_hook(&nfho);


    return 0;
}


static voidnf_cleanup(void)
{
    nf_unregister_hook(&nfho);
}


module_init(nf_init);
module_exit(nf_cleanup);
MODULE_AUTHOR("djwow");
MODULE_LICENSE("GPL");

struct sk_buff










sk_buff结构的成员skb->head指向一个已分配的空间的头部,即申请到的整个缓冲区的头,skb->end指向该空间的尾部,这两个成员指针从空间创建之后,就不能被修改。skb->data指向分配空间中数据的头部,skb->tail指向数据的尾部,这两个值随着网络数据在各层之间的传递、修改,会被不断改动。刚开始接触skb_buf的时候会产生一种错误的认识,就是以为协议头都会是放在skb->head和skb->data这两个指针之间,但实际上skb_buf的操作函数都无法直接对这一段内存进行操作,所有的操作函数所做的就仅仅是修改skb->data和skb->tail这两个指针而已,向套接字缓冲区拷贝数据也是由其它函数来完成的,所以不管是从网卡接受的数据还是上层发下来的数据,协议头都是被放在了skb->data到skb->tail之间,通过skb_push前移skb->data加入协议头,通过skb_pull后移skb->data剥离协议头。




sk_buf常用解析
struct sk_buff *sb = skb;
IP地址:
#define NIPQUAD(addr) \
((unsigned char *)&addr), \
((unsigned char *)&addr), \
((unsigned char *)&addr), \
((unsigned char *)&addr)


#define NIPQUAD_FMT "%u.%u.%u.%u"
__be sip, dip;
struct iphdr *iph=ip_hdr(sb);
sip=iph->saddr;
dip=iph->daddr;


printk("sip:%u.%u.%u.%u,dip:%u.%u.%u.%u\m",
    NIPQUAD(sip),NIPQUAD(dip));



协议:
struct iphdr *iph=ip_hdr(sb);
iph->protocol==IPPROTO_TCP

端口:
struct iphdr *iph=ip_hdr(sb);
struct tcphdr *tcph = NULL;
struct udphdr *udph = NULL;
unsigned short sport = 0;
unsigned short dport = 0;
if(iph->protocol==IPPROTO_TCP)
{
    tcph = (struct tcphdr *)((char *)skb->data + (int)(iph->ihl * 4));
    sport=ntohs(tcph->source);
    dport=ntohs(tcph->dest);
}
else if(iph->protocal==IPPROTO_UDP)
{
    udph = (struct udphdr *)((char *)skb->data + (int)(iph->ihl * 4));
    sport=ntohs(udph->source);
    dport=ntohs(udph->dest);
}

tcp的数据:
char *data = NULL;
struct tcphdr *tcph = (struct tcphdr *)((char *)skb->data + (int)(iph->ihl * 4));;
data = (char *)((int)tcph + (int)(tcph->doff * 4));
ihl(Internet Header Length 报头长度),位于IP报文的第二个字段,4位,表示IP报文头部按32位字长(32位,4字节)计数的长度,也即报文头的长度等于IHL的值乘以4。

三个demo
一个是打印ip地址 一个是获取ftp账号密码 最后一个老外写一个获取ftp和修改数据的demo
//打印IP:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/netfilter_ipv4.h>
#include <linux/inet.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>


#define NIPQUAD(addr) \
((unsigned char *)&addr), \
((unsigned char *)&addr), \
((unsigned char *)&addr), \
((unsigned char *)&addr)




static unsigned int ipprint_func(
unsigned int hooknum,
struct sk_buff * skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn) (struct sk_buff *))
{
    __be32 sip,dip;


    struct tcphdr *tcph = NULL;
    struct udphdr *udph = NULL;
    unsigned short sport = 0;
    unsigned short dport = 0;
    struct iphdr *iph;
    if(skb)
    {
      struct sk_buff *sb = NULL;
      sb = skb;
      iph= ip_hdr(sb);
      sip = iph->saddr;
      dip = iph->daddr;




      if(iph->protocol==IPPROTO_TCP)
      {
          tcph = (struct tcphdr *)((char *)skb->data + (int)(iph->ihl * 4));
            //tcph = tcp_hdr(sb);
            sport=ntohs(tcph->source);
            dport=ntohs(tcph->dest);
      }
      else if(iph->protocol==IPPROTO_UDP)
      {
          udph = (struct udphdr *)((char *)skb->data + (int)(iph->ihl * 4));
            //udph = udp_hdr(sb);
            sport=ntohs(udph->source);
            dport=ntohs(udph->dest);
      }
      printk("Packet for source address: %u.%u.%u.%u:%u destination address: %u.%u.%u.%u:%u\n ", NIPQUAD(sip),sport,NIPQUAD(dip),dport);
    }
    return NF_DROP;
}


struct nf_hook_ops ipprint_ops = {
   .list ={NULL,NULL},
   .hook = ipprint_func,
   .pf = PF_INET,
   //.hooknum = NF_INET_PRE_ROUTING,
   .hooknum = NF_INET_LOCAL_IN,
   .priority = NF_IP_PRI_FILTER+2
};


static int __init ipprint_init(void) {
nf_register_hook(&ipprint_ops);
return 0;
}




static void __exit ipprint_exit(void) {
nf_unregister_hook(&ipprint_ops);
}


module_init(ipprint_init);
module_exit(ipprint_exit);
MODULE_AUTHOR("djwow");
MODULE_DESCRIPTION("ipprint");
MODULE_LICENSE("GPL");



获取FTP账号密码: 结合通信的话可以参考我的另一篇文章
《linux防火墙开发实例 获取FTP账号密码》
6319521


Linux内核sk_buff结构分析
7415748


skb_buf结构分析
8797236


sk_buff结构分析
http://www.cnblogs.com/qq78292959/archive/2012/06/06/2538358.html
页: [1]
查看完整版本: Linux Netfilter开发小结