ip_queue应用层编程

Linux内核在Netfilter框架的基础上提供了IP Queue机制,从而使得基于用户态的防火墙开发成为可能。从而可以在用户态对报文内容进行分析,同时可以给出对这个报文的处理意见,也可以修改报文。

简单介绍一下NF中各个钩子(hook)函数对数据包处理的返回值,即该函数告诉内核对该数据包的处理意见。

NF_DROP: 丢弃该报文,释放所有与该报文相关的资源;

NF_ACCEPT: 接受该报文,并继续处理;

NF_STOLEN: 该报文已经被HOOK函数接管,协议栈无须继续处理;

NF_QUEUE: 将该报文传递到用户态去做进一步的处理;

NF_REPEAT: 再次调用本HOOK函数。

当HOOK处理函数返回值为NF_QUEUE时,内核协议栈将通过IP Queue机制把当前报文传递到用户态,由用户态的应用程序进行处理。这样,只要能够在相应的HOOK点上返回NF_QUEUE值,就可以将符合要求的报文传送到用户态去做进一步对报文行处理。随后,用户态程序会将处理后的报文以及对报文的处理意见(ACCEPT,DROP等)传递给内核协议栈。内核协议栈就会按照用户态对报文的处理意见将报文做接受、丢弃等处理。整个处理的过程就相当于一个用户态的防火墙,所有数据包的实质性处理都放在用户态进行。这样,即使是不具有深入内核知识的开发人员,也可以开发出适应一定应用场合的用户态防火墙。

 

基于以上的介绍可以大致讲一下应用层编程实现防火墙的流程:内核和应用层之间的通信以及报文的传输采用netlink套接字来实现。用户态可以通过IPTABLES来决定哪一类报文将要发送到应用层(采用iptables的QUEUE任务,如iptables -t mangle -A FORWARD  -j QUEUE,则所有匹配到这条规则的报文都将通过IP_QUEUE发送到用户空间),而应用层通过对报文的分析和处理最终返回一个处理意见给内核,返回的值同上面的hook函数返回值一样,可以是其中的一种。

 

下面是一个简单的应用层获获取内核报文的实现:

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <stdlib.h>  
  4. #include <errno.h>  
  5. #include <sys/time.h>  
  6. #include <sys/types.h>  
  7. #include <unistd.h>  
  8. #include <asm/types.h>  
  9. #include <sys/socket.h>  
  10. #include <linux/netlink.h>  
  11. /*IP_QUEUE主要数据结构头文件*/  
  12. #include<linux/netfilter_ipv4/ip_queue.h>  
  13.   
  14.   
  15. #define VAL_LEN  1024  
  16. #define RS_OK 0  
  17. #define RS_WRONG -1  
  18.   
  19. typedef unsigned int u32;  
  20. /* 
  21.  应用层发送到内核的报文的二种模式。 
  22. */  
  23. enum {  
  24.     SET_MODE = 0,  
  25.     RETURN_IDE  
  26. };  
  27.   
  28. /* 
  29.  应用层返回给内核的几种报文的处理意见 
  30. */  
  31. enum {  
  32.     NF_DROP = 0,   
  33.     NF_ACCEPT,  
  34.     NF_STOLEN,  
  35.     NF_QUEUE,  
  36.     NF_REPEAT,  
  37.     NF_STOP,  
  38.     NF_MAX_VERDICT,  
  39. };  
  40.   
  41. typedef struct ipq_hand {  
  42.     int ipqfd;  
  43.     struct sockaddr_nl local, peer;  
  44. }ipqhander;  
  45.   
  46. /* 
  47.  应用层请求内核报文头部 
  48. */  
  49. struct req_head {  
  50.  /*netlink特有报文头部*/  
  51.     struct nlmsghdr nlh;  
  52.  /*ip_queue头部*/  
  53.     ipq_peer_msg_t reqh;  
  54. };  
  55.   
  56. struct ipq_hand *ipqh;  
  57.   
  58. /*创建netlink套接字,并初始化*/  
  59. int create_ipqueue_hander()  
  60. {  
  61.     ipqh = (struct ipq_hand *)malloc(sizeof(struct ipq_hand));  
  62.     if (NULL == ipqh) {  
  63.         printf ("[%s:%d]malloc ipq hander failed, error is %s\n", __FUNCTION__, __LINE__, strerror(errno));  
  64.         return RS_WRONG;  
  65.     }  
  66.       
  67.     memset(ipqh, 0, sizeof(struct ipq_hand));  
  68.     ipqh->ipqfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);  
  69.     if (ipqh->ipqfd == -1) {  
  70.         printf ("[%s:%d]create ip_queue socket failed, error is %s\n", __FUNCTION__, __LINE__, strerror(errno));  
  71.         return RS_WRONG;  
  72.     }  
  73.   
  74.     memset(&ipqh->local,0, sizeof(ipqh->local));  
  75.     ipqh->local.nl_family = AF_NETLINK;  
  76.     ipqh->local.nl_pid = getpid();  
  77.     ipqh->local.nl_groups = 0;  
  78.   
  79.     memset(&ipqh->peer, 0, sizeof(ipqh->peer));  
  80.     ipqh->peer.nl_family = AF_NETLINK;  
  81.     ipqh->peer.nl_pid = 0;  
  82.     ipqh->peer.nl_groups = 0;    
  83.       
  84.     if(bind(ipqh->ipqfd, (struct sockaddr *)&(ipqh->local), sizeof (ipqh->local))) {  
  85.         printf ("[%s:%d]bind ip_queue socket failed, error is %s\n", __FUNCTION__, __LINE__, strerror(errno));  
  86.         return RS_WRONG;  
  87.     }  
  88.       
  89.     return RS_OK;  
  90. }  
  91.   
  92. /*向内核发送消息,包括二种,一种为模式设置,一种为返回处理意见*/  
  93. int send_msg_to_kernel(u32 msgtype, ipq_packet_msg_t *m, u32 verdict)  
  94. {  
  95.     int ret;  
  96.     struct req_head req;  
  97.       
  98.     req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof (req));  
  99.     req.nlh.nlmsg_flags = NLM_F_REQUEST;  
  100.     req.nlh.nlmsg_pid = getpid();  
  101.      
  102.     switch(msgtype) {  
  103.     case SET_MODE:  
  104.         printf ("now set the mode\n");  
  105.         req.nlh.nlmsg_type = IPQM_MODE;  
  106.         req.reqh.msg.mode.value = IPQ_COPY_PACKET;  
  107.         req.reqh.msg.mode.range = VAL_LEN;  
  108.         break;  
  109.     case RETURN_IDE:  
  110.         req.nlh.nlmsg_type = IPQM_VERDICT;  
  111.         req.reqh.msg.verdict.value = verdict;  
  112.         req.reqh.msg.verdict.id = m->packet_id;  
  113.         req.reqh.msg.verdict.data_len = 0;  
  114.         break;  
  115.     default:  
  116.         break;  
  117.     }  
  118.   
  119.     ret = sendto(ipqh->ipqfd, &req, sizeof(req), 0, (struct sockaddr *)&ipqh->peer, sizeof(ipqh->peer));  
  120.     if (ret == -1) {  
  121.         printf ("[%s:%d]send ipq msg failed, error is %s\n", __FUNCTION__, __LINE__, strerror(errno));  
  122.         return RS_WRONG;  
  123.     }  
  124.   
  125.     return RS_OK;  
  126. }  
  127. /*从内核接收消息*/  
  128. int recv_msg_from_kernel()  
  129. {  
  130.     int ret, status;  
  131.     char buf[VAL_LEN];  
  132.     struct nlmsghdr *nlh;  
  133.     ipq_packet_msg_t *packet;  
  134.     fd_set read_fds;  
  135.     FD_ZERO(&read_fds);  
  136.     FD_SET(ipqh->ipqfd, &read_fds);  
  137.   
  138.     while (1) {     
  139.         ret = select(ipqh->ipqfd +1, &read_fds, NULL, NULL, NULL);  
  140.  if (ret < 0) {  
  141.      if (errno == EINTR) {  
  142.                 continue;  
  143.      } else {  
  144.   printf ("[%s:%d]recvfrom msg failed, error is %s\n", __FUNCTION__, __LINE__, strerror(errno));  
  145.   continue;  
  146.      }  
  147.  }  
  148.       
  149.  if (!FD_ISSET(ipqh->ipqfd, &read_fds))   
  150.      continue;  
  151.         memset(buf, 0, sizeof(buf));  
  152.  status = recvfrom(ipqh->ipqfd, buf, VAL_LEN, 0, NULL, NULL);  
  153.  if (status < 0) {  
  154.                 printf ("[%s:%d]recvfrom msg failed, error is %s\n", __FUNCTION__, __LINE__, strerror(errno));  
  155.   continue;  
  156.  }  
  157.   
  158.  if (status == 0) {  
  159.                 printf ("[%s:%d]recv NULL msg\n", __FUNCTION__, __LINE__);  
  160.   continue;  
  161.  }  
  162.  nlh = (struct nlmsghdr *)buf;  
  163.  if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) {  
  164.      continue;  
  165.  }  
  166.    
  167.        packet = NLMSG_DATA((struct nlmsghdr *)(buf));  
  168.        printf("recv bytes =%d, nlmsg_len=%d, indev=%s, datalen=%d, packet_id=%x\n", status, nlh->nlmsg_len,  
  169.                   packet->indev_name,  packet->data_len, (u32)packet->packet_id);  
  170.        if (send_msg_to_kernel(RETURN_IDE, packet, NF_ACCEPT)) {  
  171.             printf ("[%s:%d]send_msg_to_kernel error\n", __FUNCTION__, __LINE__);  
  172.             continue;  
  173.        }    
  174.     }  
  175.     return RS_OK;  
  176. }  
  177.   
  178. int main(int argc, char *argv[])  
  179. {  
  180.    if (create_ipqueue_hander()) {  
  181.         printf ("[%s:%d]create_ipqueue_hander error\n", __FUNCTION__, __LINE__);  
  182.         return RS_WRONG;  
  183.    }  
  184.          
  185.    if (send_msg_to_kernel(SET_MODE, NULL, 0)) {  
  186.         printf ("[%s:%d]send_msg_to_kernel error\n", __FUNCTION__, __LINE__);  
  187.         return RS_WRONG;  
  188.    }  
  189.    recv_msg_from_kernel();  
  190.   
  191.    return 0;  
  192. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值