看流星社区

 找回密码
 注册账号
查看: 1833|回复: 0

Linux Netfilter开发小结

[复制链接]

该用户从未签到

发表于 2017-6-1 12:52:00 | 显示全部楼层 |阅读模式
前置知识:







IP包:
  1. struct ip {
  2. #if BYTE_ORDER == LITTLE_ENDIAN
  3.         unsigned char        ip_hl:4,                /* header length */
  4.                 ip_v:4;                        /* version */
  5. #endif
  6.         unsigned char        ip_tos;                        /* type of service */
  7.         short        ip_len;        /* total length */
  8.         unsigned short        ip_id;/* identification */
  9.         short        ip_off;               
  10.         unsigned char        ip_ttl;/* time to live */
  11.         unsigned char        ip_p;/* protocol */
  12.         unsigned short        ip_sum;       
  13.           struct        in_addr ip_src,ip_dst;/* source and dest address */
  14. };
复制代码


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




TCP头
  1. struct tcphdr {
  2.         u_short        th_sport;        /* source port */
  3.         u_short        th_dport;        /* destination port */
  4.         tcp_seq        th_seq;        /* sequence number */
  5.         tcp_seq        th_ack;        /* acknowledgement number */
  6. #if BYTE_ORDER == LITTLE_ENDIAN
  7.         u_char        th_x2:4,        /* (unused) */
  8.                 th_off:4;        /* data offset */
  9. #endif
  10. #if BYTE_ORDER == BIG_ENDIAN
  11.         u_char        th_off:4,        /* data offset */
  12.                 th_x2:4;        /* (unused) */
  13. #endif
  14.         u_char        th_flags;
  15. #define        TH_FIN        0x01
  16. #define        TH_SYN        0x02
  17. #define        TH_RST        0x04
  18. #define        TH_PUSH        0x08
  19. #define        TH_ACK        0x10
  20. #define        TH_URG        0x20
  21. #define TH_FLAGS (TH_FIN|TH_SYN|
  22.              TH_RST|TH_ACK|TH_URG)
  23.         u_short        th_win;        /* window */
  24.         u_short        th_sum;        /* checksum */
  25.         u_short        th_urp;        /* urgent pointer */
  26. };
复制代码




UDP
  1. struct udphdr
  2. {
  3.         u_short        uh_sport;                /* source port */
  4.         u_short        uh_dport;                /* destination port */
  5.         short        uh_ulen;                /* udp length */
  6.         u_short        uh_sum;       
  7.                 /* udp checksum */
  8. };
复制代码





ARP
  1. typedef struct _ETHERNET_FRAME
  2. {
  3.     BYTE    DestinationAddress[6];
  4.        BYTE    SourceAddress[6];    
  5.               WORD    FrameType;    // in host-order
  6. } EHTERNET_FRAME, *PETHERNET_FRAME;
  7. typedef struct _ARP_HEADER
  8. {   
  9.         WORD    HardType;      //硬件类型   
  10.         WORD    ProtocolType;  //协议类型
  11.         BYTE    HardLength;    //硬件地址长度   
  12.         BYTE    ProtocolLength; //协议地址长度     
  13.         WORD    Opcode;        //操作类型      
  14.         BYTE    SourceMAC[6];           
  15.         BYTE    SourceIP[4];           
  16.         BYTE    DestinationMAC[6];      
  17.         BYTE    DestinationIP[4];   
  18. } ARP_HEADER, *PARP_HEADER;
  19. typedef struct _ARP
  20. {    
  21.         EHTERNET_FRAME EthernetFrame;    
  22.         ARP_HEADER  ArpHeader;
  23. }ARP, *PARP;
复制代码








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


基于linux内核的网络防火墙开发
Linux核心网络堆栈中有一个全局变量 :
struct list_head nf_hooks[NPROTO][NF_MAX_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点的定义:
  1. NF_INET_PRE_ROUTING    在完整性校验之后,选路确定之前
  2. NF_INET_LOCAL_IN        在选路确定之后,且数据包的目的是本地主机
  3. NF_INET_FORWARD        目的地是其它主机地数据包
  4. NF_INET_LOCAL_OUT     来自本机进程的数据包在其离开本地主机的过程中
  5. NF_IP_POST_ROUTING    在数据包离开本地主机“上线”之前
  6. NF_INET_POST_ROUTING 同上
复制代码

对包的处理结果:
  1. NF_DROP        丢弃该数据包
  2. NF_ACCEPT    保留该数据包
  3. NF_STOLEN    忘掉该数据包
  4. NF_QUEUE     将该数据包插入到用户空间
  5. NF_REPEAT    再次调用该hook函数
复制代码

优先级:
  1. enum nf_ip_hook_priorities {
  2.         NF_IP_PRI_FIRST = INT_MIN,
  3.         NF_IP_PRI_CONNTRACK_DEFRAG = -400,
  4.         NF_IP_PRI_RAW = -300,
  5.         NF_IP_PRI_SELINUX_FIRST = -225,
  6.         NF_IP_PRI_CONNTRACK = -200,
  7.         NF_IP_PRI_MANGLE = -150,
  8.         NF_IP_PRI_NAT_DST = -100,
  9.         NF_IP_PRI_FILTER = 0,
  10.         NF_IP_PRI_SECURITY = 50,
  11.         NF_IP_PRI_NAT_SRC = 100,
  12.         NF_IP_PRI_SELINUX_LAST = 225,
  13.         NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
  14.         NF_IP_PRI_LAST = INT_MAX,
  15. };
复制代码
一般写NF_IP_PRI_FIRST啦~或+1 +2...




///////////////////////////////////////////////////开始编写防火墙//////////////////////////////////////////////////////////////////
在注册之前 我们需要填写一个结构
  1. struct nf_hook_ops {
  2.       struct list_head list;
  3.       /* 此下的值由用户填充 */
  4.       nf_hookfn *hook//回调函数;
  5.       int pf;//协议 IPV4 还是ipV6
  6.       int hooknum;//hook点
  7.       /* Hook以升序的优先级排序 */
  8.      int priority;
  9. };
复制代码

例子:
  1. struct nf_hook_ops   nfho;
  2.     /* 填充我们的hook数据结构 */    
  3.     nfho.hook = hook_func;  /* 处理函数 */    
  4.     nfho.hooknum  = NF_INET_PRE_ROUTING;
  5.     /* 使用IPv4的第一个hook */    
  6.     nfho.pf       = PF_INET;
  7.     //优先级 /* 让我们的函数首先执行 */   
  8.     nfho.priority = NF_IP_PRI_FIRST;
复制代码

过滤函数:
  1.         unsigned int hook_func(
  2.                                unsigned int hooknum,                        
  3.                                struct sk_buff *skb,                        
  4.                                const struct net_device *in,                        
  5.                                const struct net_device *out,                        
  6.                                int (*okfn)(struct sk_buff *))
  7.         {    
  8.                 return NF_DROP;  /* 丢弃所有的数据包 */         
  9.         }
复制代码
  1.    
  2.     //初始化函数调用
  3.     nf_register_hook(&nfho);
  4.     //卸载函数中调用
  5.     nf_unregister_hook(&nfho);
复制代码

完整代码:
  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/netfilter.h>
  4. #include <linux/netfilter_ipv4.h>
  5. static struct nf_hook_ops nfho;
  6. static unsigned int hook_func(unsigned int hooknum,
  7.                        struct sk_buff *skb,
  8.                        const struct net_device *in,
  9.                        const struct net_device *out,
  10.                        int (*okfn)(struct sk_buff *))
  11. {
  12.     return NF_ACCEPT;
  13. }
  14. static int nf_init(void)
  15. {
  16.     nfho.hook = hook_func;
  17.     nfho.hooknum  = NF_INET_PRE_ROUTING;
  18.     nfho.pf       = PF_INET;
  19.     nfho.priority = NF_IP_PRI_FIRST;
  20.     nf_register_hook(&nfho);
  21.     return 0;
  22. }
  23. static void  nf_cleanup(void)
  24. {
  25.     nf_unregister_hook(&nfho);
  26. }
  27. module_init(nf_init);
  28. module_exit(nf_cleanup);
  29. MODULE_AUTHOR("djwow");
  30. 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地址:
  1. #define NIPQUAD(addr) \
  2.   ((unsigned char *)&addr)[0], \
  3.   ((unsigned char *)&addr)[1], \
  4.   ((unsigned char *)&addr)[2], \
  5.   ((unsigned char *)&addr)[3]
  6. #define NIPQUAD_FMT "%u.%u.%u.%u"
  7. __be sip, dip;
  8. struct iphdr *iph=ip_hdr(sb);
  9. sip=iph->saddr;
  10. dip=iph->daddr;
  11. printk("sip:%u.%u.%u.%u,dip:%u.%u.%u.%u\m",
  12.     NIPQUAD(sip),NIPQUAD(dip));
复制代码



协议:
  1. struct iphdr *iph=ip_hdr(sb);
  2. iph->protocol==IPPROTO_TCP
复制代码
端口:
  1. struct iphdr *iph=ip_hdr(sb);
  2. struct tcphdr *tcph = NULL;
  3. struct udphdr *udph = NULL;
  4. unsigned short sport = 0;
  5. unsigned short dport = 0;
  6. if(iph->protocol==IPPROTO_TCP)
  7. {
  8.     tcph = (struct tcphdr *)((char *)skb->data + (int)(iph->ihl * 4));
  9.     sport=ntohs(tcph->source);
  10.     dport=ntohs(tcph->dest);
  11. }
  12. else if(iph->protocal==IPPROTO_UDP)
  13. {
  14.     udph = (struct udphdr *)((char *)skb->data + (int)(iph->ihl * 4));
  15.     sport=ntohs(udph->source);
  16.     dport=ntohs(udph->dest);
  17. }
复制代码
tcp的数据:
  1. char *data = NULL;
  2. struct tcphdr *tcph = (struct tcphdr *)((char *)skb->data + (int)(iph->ihl * 4));;
  3. 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
  1. //打印IP:
  2. #include <linux/module.h>
  3. #include <linux/kernel.h>
  4. #include <linux/init.h>
  5. #include <linux/types.h>
  6. #include <linux/netdevice.h>
  7. #include <linux/skbuff.h>
  8. #include <linux/netfilter_ipv4.h>
  9. #include <linux/inet.h>
  10. #include <linux/in.h>
  11. #include <linux/ip.h>
  12. #include <linux/tcp.h>
  13. #include <linux/udp.h>
  14. #define NIPQUAD(addr) \
  15.   ((unsigned char *)&addr)[0], \
  16.   ((unsigned char *)&addr)[1], \
  17.   ((unsigned char *)&addr)[2], \
  18.   ((unsigned char *)&addr)[3]
  19. static unsigned int ipprint_func(
  20. unsigned int hooknum,
  21. struct sk_buff * skb,
  22. const struct net_device *in,
  23. const struct net_device *out,
  24. int (*okfn) (struct sk_buff *))
  25. {
  26.     __be32 sip,dip;
  27.     struct tcphdr *tcph = NULL;
  28.     struct udphdr *udph = NULL;
  29.     unsigned short sport = 0;
  30.     unsigned short dport = 0;
  31.     struct iphdr *iph;
  32.     if(skb)
  33.     {
  34.         struct sk_buff *sb = NULL;
  35.         sb = skb;
  36.         iph  = ip_hdr(sb);
  37.         sip = iph->saddr;
  38.         dip = iph->daddr;
  39.         if(iph->protocol==IPPROTO_TCP)
  40.         {
  41.             tcph = (struct tcphdr *)((char *)skb->data + (int)(iph->ihl * 4));
  42.             //tcph = tcp_hdr(sb);
  43.             sport=ntohs(tcph->source);
  44.             dport=ntohs(tcph->dest);
  45.         }
  46.         else if(iph->protocol==IPPROTO_UDP)
  47.         {
  48.             udph = (struct udphdr *)((char *)skb->data + (int)(iph->ihl * 4));
  49.             //udph = udp_hdr(sb);
  50.             sport=ntohs(udph->source);
  51.             dport=ntohs(udph->dest);
  52.         }
  53.         printk("Packet for source address: %u.%u.%u.%u:%u destination address: %u.%u.%u.%u:%u\n ", NIPQUAD(sip),sport,NIPQUAD(dip),dport);
  54.     }
  55.     return NF_DROP;
  56. }
  57. struct nf_hook_ops ipprint_ops = {
  58.    .list =  {NULL,NULL},
  59.    .hook = ipprint_func,
  60.    .pf = PF_INET,
  61.    //.hooknum = NF_INET_PRE_ROUTING,
  62.    .hooknum = NF_INET_LOCAL_IN,
  63.    .priority = NF_IP_PRI_FILTER+2
  64. };
  65. static int __init ipprint_init(void) {
  66.   nf_register_hook(&ipprint_ops);
  67.   return 0;
  68. }
  69. static void __exit ipprint_exit(void) {
  70.   nf_unregister_hook(&ipprint_ops);
  71. }
  72. module_init(ipprint_init);
  73. module_exit(ipprint_exit);
  74. MODULE_AUTHOR("djwow");
  75. MODULE_DESCRIPTION("ipprint");
  76. 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
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-4-25 21:22

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

快速回复 返回顶部 返回列表