免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1308 | 回复: 0
打印 上一主题 下一主题

Linux内核IP Queue机制的分析(三)——ip_queue内核模块的分析(中) [复制链接]

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-04 22:27 |只看该作者 |倒序浏览

                                                               

  Normal
  0
  
  7.8 磅
  0
  2
  
  false
  false
  false
  
   
   
   
   
   
   
   
   
   
   
   
   
  
  MicrosoftInternetExplorer4



/* Style Definitions */
table.MsoNormalTable
        {mso-style-name:普通表格;
        mso-tstyle-rowband-size:0;
        mso-tstyle-colband-size:0;
        mso-style-noshow:yes;
        mso-style-parent:"";
        mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
        mso-para-margin:0cm;
        mso-para-margin-bottom:.0001pt;
        mso-pagination:widow-orphan;
        font-size:10.0pt;
        font-family:"Times New Roman";
        mso-fareast-font-family:"Times New Roman";
        mso-ansi-language:#0400;
        mso-fareast-language:#0400;
        mso-bidi-language:#0400;}

  Normal
  0
  
  7.8 磅
  0
  2
  
  false
  false
  false
  
   
   
   
   
   
   
   
   
   
   
   
   
  
  MicrosoftInternetExplorer4



st1\:*{behavior:url(#ieooui) }
/* Style Definitions */
table.MsoNormalTable
        {mso-style-name:普通表格;
        mso-tstyle-rowband-size:0;
        mso-tstyle-colband-size:0;
        mso-style-noshow:yes;
        mso-style-parent:"";
        mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
        mso-para-margin:0cm;
        mso-para-margin-bottom:.0001pt;
        mso-pagination:widow-orphan;
        font-size:10.0pt;
        font-family:"Times New Roman";
        mso-fareast-font-family:"Times New Roman";
        mso-ansi-language:#0400;
        mso-fareast-language:#0400;
        mso-bidi-language:#0400;}

  Normal
  0
  
  7.8 磅
  0
  2
  
  false
  false
  false
  
   
   
   
   
   
   
   
   
   
   
   
   
  
  MicrosoftInternetExplorer4



st1\:*{behavior:url(#ieooui) }
/* Style Definitions */
table.MsoNormalTable
        {mso-style-name:普通表格;
        mso-tstyle-rowband-size:0;
        mso-tstyle-colband-size:0;
        mso-style-noshow:yes;
        mso-style-parent:"";
        mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
        mso-para-margin:0cm;
        mso-para-margin-bottom:.0001pt;
        mso-pagination:widow-orphan;
        font-size:10.0pt;
        font-family:"Times New Roman";
        mso-fareast-font-family:"Times New Roman";
        mso-ansi-language:#0400;
        mso-fareast-language:#0400;
        mso-bidi-language:#0400;}

  Normal
  0
  
  7.8 磅
  0
  2
  
  false
  false
  false
  
   
   
   
   
   
   
   
   
   
   
   
   
  
  MicrosoftInternetExplorer4



st1\:*{behavior:url(#ieooui) }
/* Style Definitions */
table.MsoNormalTable
        {mso-style-name:普通表格;
        mso-tstyle-rowband-size:0;
        mso-tstyle-colband-size:0;
        mso-style-noshow:yes;
        mso-style-parent:"";
        mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
        mso-para-margin:0cm;
        mso-para-margin-bottom:.0001pt;
        mso-pagination:widow-orphan;
        font-size:10.0pt;
        font-family:"Times New Roman";
        mso-fareast-font-family:"Times New Roman";
        mso-ansi-language:#0400;
        mso-fareast-language:#0400;
        mso-bidi-language:#0400;}
本文分析ip_queue的内核态源码。文中如有任何疏漏和差错,欢迎各位朋友指正。
由于本文内容较多,本人将其分为上、中、下三篇。其中上篇和下篇的链接如下:
上篇:
http://blog.chinaunix.net/u/33048/showart_2139488.html
[/url]
下篇:
[url=http://blog.chinaunix.net/u/33048/showart_2139500.html]http://blog.chinaunix.net/u/33048/showart_2139500.html


      本文欢迎自由转载,但请标明出处,并保证本文的完整性。
      作者:Godbach
        Blog:
http://Godbach.cublog.cn
      日期:2010/01/04
(四)入队函数ipq_enqueue_packet
—— 发送数据包到用户空间
      第二部分已经分析过了该函数会在什么条件下被触发。这里详细分析该函数的实现,代码在ip_queue.c中。
      分析代码之前,我们先了解一下IP Queue对数据包队列管理的核心数据结构:
struct ipq_queue_entry {
    struct
list_head list;
    struct
nf_info *info;
    struct
sk_buff *skb;
};
所有被Queue到用户空间的数据包都会有这样一个结构体。其中,
该数据结构的第1个元素是个双向链表结构,用于将所有queue的数据包用双向链表连
接起来,实现队列管理。
第2个元素同样是一个结构体,其定义在netfilter.h中:
/* Each queued (to userspace) skbuff
has one of these. */
struct nf_info
{
    /*
The ops struct which sent us to userspace. */
    struct
nf_hook_ops *elem;
   
    /*
If we're sent to userspace, this keeps housekeeping info */
    int
pf;
    unsigned
int hook;
    struct
net_device *indev, *outdev;
    int
(*okfn)(struct sk_buff *);
};
这个结构体应该很容易看出他的作用,就是记录下当数据包被Queue时所在的hook函数的相关信息,包括hook的操作结构、协议号、hook点等相关信息。当用户空间下发了对数据包的处理结果时,内核就需要参考这个结构对数据包进行进一步的处理。具体的我们会在后面数据包回注函数中分析。
第3个元素是skb本身,也就是回注函数中要处理的数据包。

OK,我们下面就开始如对函数的分析。
static int
ipq_enqueue_packet(struct
sk_buff *skb, struct nf_info *info,
               
unsigned int queuenum, void *data)
{
       int status = -EINVAL;
       struct sk_buff *nskb;
       struct ipq_queue_entry *entry;

       /*判断用户配置的模式,可以为拷贝元数据或者整个数据包的信息*/
       if (copy_mode == IPQ_COPY_NONE)
              return -EAGAIN;
       /*为即将入队的数据包分配一个struct ipq_queue_entry 结构体,用于实现对数据包的管理,我们称之为queue管理结构体*/
       entry = kmalloc(sizeof(*entry),
GFP_ATOMIC);
       if (entry == NULL) {
              printk(KERN_ERR "ip_queue:
OOM in ipq_enqueue_packet()\n");
              return -ENOMEM;
       }
       /*将该数据包对应的相关信息保存到管理结构体中*/
       entry->info = info;
       entry->skb = skb;
      
/*构建用于发往用户空间的消息*/
       nskb = ipq_build_packet_message(entry,
&status);
       if (nskb == NULL)
              goto err_out_free;
              
       write_lock_bh(&queue_lock);
      
       /如果用户空间没有开启进程,等待接收消息的话,就释放该消息/
       if (!peer_pid)
              goto err_out_free_nskb;
       /*如果当前队列中的数据包总数超过了设置的最大值,则是放该消息,并且增加丢弃数据包的统计计数*/
       if (queue_total >= queue_maxlen) {
                queue_dropped++;
              status = -ENOSPC;
              if (net_ratelimit())
                        printk (KERN_WARNING "ip_queue:
full at %d entries, "
                              "dropping packets(s). Dropped:
%d\n", queue_total,
                              queue_dropped);
              goto err_out_free_nskb;
       }

       /* 将消息发送给用户空间 */
       status = netlink_unicast(ipqnl, nskb,
peer_pid, MSG_DONTWAIT);
       if (status
              
queue_user_dropped++;
              goto err_out_unlock;
       }
       /*成功发送到用户空间之后,将该数据包的queue管理结构添加到全局队列链表中*/
       __ipq_enqueue_entry(entry);

       write_unlock_bh(&queue_lock);
       return status;

err_out_free_nskb:
       kfree_skb(nskb);
      
err_out_unlock:
       write_unlock_bh(&queue_lock);

err_out_free:
       kfree(entry);
       return status;
}
该函数成功执行之后,就将数据包相关的信息发送到用户空间,同时将该数据包对应的queue管理结构加到全局链表中。
下面对于ipq_enqueue_packet函数中调用的几个重要函数做一些分析。
首先是构建消息的函数ipq_build_packet_message。
static struct
sk_buff *
ipq_build_packet_message(struct
ipq_queue_entry *entry, int *errp)
{
       unsigned char *old_tail;
       size_t size = 0;
       size_t data_len = 0;
       struct sk_buff *skb;
       struct ipq_packet_msg *pmsg;
       struct nlmsghdr *nlh;

       read_lock_bh(&queue_lock);
       /*根据用户配置的copy模式,确定发给用户空间消息的长度*/
       switch (copy_mode) {
       /*对于初始模式和拷贝元数据的模式,消息应该包括netlink的消息头和ipq的消息头,长度为两个消息头长度之和,即sizeof(struct nlmsghdr) + sizeof(struct
ipq_packet_msg),并考虑对齐的因素。这里直接调用netlink封装的宏NLMSG_SPACE来计算长度。该宏本身已经包含了netlink消息头的长度*/
       case IPQ_COPY_META:
       case IPQ_COPY_NONE:
              /*消息长度为netlink的消息头和ipq的消息头的长度之和*/
              size = NLMSG_SPACE(sizeof(*pmsg));
              data_len = 0;
              break;
      
       case IPQ_COPY_PACKET:
              if (entry->skb->ip_summed ==
CHECKSUM_HW &&
                 
(*errp = skb_checksum_help(entry->skb,
                                            
entry->info->outdev == NULL))) {
                     read_unlock_bh(&queue_lock);
                     return NULL;
              }
              /*如果用户需要拷贝整个数据包的内容,那么如果配置的长度为0或者超出数据包的实际长度,则以数据包的实际长度进行拷贝*/
              if (copy_range == 0 || copy_range
> entry->skb->len)
                     data_len =
entry->skb->len;
              else
                     data_len = copy_range;
              /*消息长度为netlink的消息头、ipq的消息头的长度以及要拷贝数据包的长度之和*/
              size = NLMSG_SPACE(sizeof(*pmsg) +
data_len);
              break;
      
       default:
              *errp = -EINVAL;
              read_unlock_bh(&queue_lock);
              return NULL;
       }

       read_unlock_bh(&queue_lock);
       /*为构建的消息申请内存,同样适用skb结构体,消息的内容应该在skb->data和skb->tail之间*/
       skb = alloc_skb(size, GFP_ATOMIC);
       if (!skb)
              goto nlmsg_failure;
       /*记录一下新分配的skb中的tail指针的地址,此时应该skb->tail指向skb->data*/
       old_tail= skb->tail;
       /*填充netlink消息头*/
       nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET,
size - sizeof(*nlh));
       /*获取netlink消息体的起始地址,即ipq的消息头*/
       pmsg = NLMSG_DATA(nlh);
       memset(pmsg, 0, sizeof(*pmsg));

       /*将相关的ipq消息记录到消息头中*/
              /*将skb对应的queue管理结构体的地址作为packet_id*/
       pmsg->packet_id       = (unsigned long )entry;
       pmsg->data_len        = data_len;
       pmsg->timestamp_sec   = entry->skb->tstamp.off_sec;
       pmsg->timestamp_usec  = entry->skb->tstamp.off_usec;
       pmsg->mark            = entry->skb->nfmark;
       pmsg->hook            = entry->info->hook;
       pmsg->hw_protocol     = entry->skb->protocol;
      
       /*记录被queue数据包对应的输入设备名称*/
       if (entry->info->indev)
              strcpy(pmsg->indev_name,
entry->info->indev->name);
       else
              pmsg->indev_name[0] = '\0';
       /*记录被queue数据包对应的输出设备名称*/
       if (entry->info->outdev)
              strcpy(pmsg->outdev_name,
entry->info->outdev->name);
       else
              pmsg->outdev_name[0]
= '\0';

       /*记录被queue数据包对应的链路层的协议类型及地址长度*/
       if (entry->info->indev &&
entry->skb->dev) {
              pmsg->hw_type =
entry->skb->dev->type;
              if
(entry->skb->dev->hard_header_parse)
                     pmsg->hw_addrlen =
                            entry->skb->dev->hard_header_parse(entry->skb,
                                                              
pmsg->hw_addr);
       }
       /* data_len != 0 说明需要拷贝数据包的原始数据。skb_copy_bits 函数的实现不再分析,其主要功能就是将从skb->data+offset开始的数据拷贝data_len个字节到pmsg->payload中,即ipq消息的载荷。另外一个需要注意的问题,如果skb挂载的有分片包,则skb_copy_bits也会按照顺序拷贝分片包中的数据*/
       if (data_len)
              if (skb_copy_bits(entry->skb,
0, pmsg->payload, data_len))
                     BUG();
       /*设置netlink消息的长度*/
       nlh->nlmsg_len = skb->tail -
old_tail;
       return skb;

nlmsg_failure:
       if (skb)
              kfree_skb(skb);
       *errp = -EINVAL;
       printk(KERN_ERR "ip_queue: error
creating packet message\n");
       return NULL;
}
其次,对于netlink_unicast函数,我们这里不具体分析,它的作用就是将内核封装好的netlink消息发送到用户态。
最后分析一下__ipq_enqueue_entry
函数。
static inline void
__ipq_enqueue_entry(struct
ipq_queue_entry *entry)
{
       list_add(&entry->list,
&queue_list);
       queue_total++;
}
该函数的功能很简答,就是将当前skb的queue管理结构添加到全局的queue管理链表queue_list中,并增加队列的统计计数。该链表记录了所有发往用户空间,且并未收到用户空间下发处理结果的数据包。
至此,数据包的入队处理函数已经分析完毕。一切顺利执行的话,现在用户态已经接收到关于该数据包的消息。
--未完待续
               
               
               
               
               
               
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/33048/showart_2139494.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP