DPDK-IP分片和重组库

0x01 缘由

     在网络数据包处理过程中,大量的包处理,就涉及到IP分片和重组。TCP/IP协议原理中,有对为什么要IP分片有描述。
     在TCP/IP分层中,数据链路层用MTU(Maximum Transmission Unit,最大传输单元)来限制所能传输的数据包大小,MTU是指一次传送的数据最大长度,不包括数据链路层数据帧的帧头,如以太网的MTU为1500字节,实际上数据帧的最大长度为1512字节,其中以太网数据帧的帧头为12字节。
     当发送的IP数据报的大小超过了MTU时,IP层就需要对数据进行分片,否则数据将无法发送成功。

0x02 介绍    

     该库实现IPv4和IPv6包分片和重组。

     1.包分片

     包分片程序将一个包分解为多个分片。rte_ipv4_fragment_packet()和rte_ipv6_fragment_packet()函数都假定输入mbuf数据指向分组的IP报头的开始(即L2报头已经被剥离)。为避免实际包的拷贝,rte_pktmbuf_attach()使用了零拷贝技术。
     对于么每个分片将创建两个新的mbufs结构:
     Direct mbuf--mbuf将包含新分片的L3头部。
     Indirect mbuf--mbuf将附加在原始数据包上。它的数据域指针指向原始数据包附加偏移的开始处。
     L3 头部从原来mbuf结构中赋值到direct mbuf结构,并且更新分片状态。注意IPv4,头部校验和不会重新计算,且校验和置位0。
     最后,通过mbuf的下一个文件将每个片段的“直接”和“间接”mbuf链接在一起,以构成新片段的数据包。
     调用者有能力明确指定哪些mempools应用于从中分配“直接”和“间接”mbufs。

     2.包重组

     2.1 IP分片表

     分片表维持一个已经接受的包的分片信息。每个Ip 包有一个唯一的标识,通过三元组 源ip-目的ip-id。
     注意:所有的更新和查找操作为非线程安全的。因此,如果不同的执行上下文(线程/进程)将同时访问同一个表,那么必须提供一些外部同步机制。
     每个表条目能够持有信息关于包由多少分片组成( RTE_LIBRTE_IP_FRAG_MAX,默认为4)。
     代码例子,说明一个分片表的创建:
     frag_cycles = (rte_get_tsc_hz() + MS_PER_S - 1) / MS_PER_S * max_flow_ttl;
     bucket_num = max_flow_num + max_flow_num / 4;
     frag_tbl = rte_ip_frag_table_create(max_flow_num, bucket_entries, max_flow_num, frag_cycles, socket_id);
     内部分片表是一个简单的hash表。这个基本做法是用两个hash函数和桶条目组成。提供了2 * <bucket_entries>可能的位置为每个key的hash表。当发生冲突并且所有2 * <bucket_entries>都被占用时,ip_frag_tbl_add()只是返回失败,而不是将现有的Key重新插入到另外的位置。
     而且,表中长于<max cycles>的条目也被认为是无效的,可以被新的条目删除/替换。
     请注意,重新组合需要分配很多mbuf。 在任何给定时间(2 * bucket_entries * RTE_LIBRTE_IP_FRAG_MAX * <每个数据包的最大mbufs数>>)可以存储在等待剩余片段的Fragment Table中。

     2.2 包重组

     通过rte_ipv4_frag_reassemble_packet()/rte_ipv6_frag_reassemble_packet完成分片和重组处理。它们返回一个指向有效mbuf的指针,它包含重新组合的数据包,或者返回NULL(如果数据包由于某种原因而无法重新组合)。
     这些函数负责:
     1、用包的三元组(src_ip,dest_ip,id)搜索分片表。
     2、如果条目发现,检查条目是否超时。如果超时,释放前面所有分片,然后移除关于这个条目的所有信息。
     3、如果这个key没有找到相关条目,按照如下步骤 尝试创建一个新的:
          a.用作空条目。
          b.删除超时条目,释放相关mbufs,存储一个指定key的新条目。
     4、用新的分片信息更新条目,并且检查一个包是否被重组。(这个包条目包含所有的分片)。
          a.如果是,重组包,标记表的条目为空,并返回一个重组mbuf的调用。
          b.如果不是,返回一个NULL给调用者。
     如果在分组处理的任何阶段遇到错误(例如:不能将新条目插入片段表或无效/超时片段),则该函数将释放所有与分组片段相关联的标记表条目 作为无效并将NULL返回给调用者。

     2.3 调试日志和统计收集

     RTE_LIBRTE_IP_FRAG_TBL_STAT配置宏控制片段表的统计信息收集。 此宏默认情况下未启用。RTE_LIBRTE_IP_FRAG_DEBUG控制IP片段处理和重新组装的调试日志记录。 默认情况下禁用此宏。 请注意,在日志记录包含大量详细信息时,会减慢数据包处理速度,并可能导致丢失大量数据包。

0x03 源码

     
     /* if this is an IPv4 packet */
    if (m->ol_flags & PKT_RX_IPV4_HDR) {
        struct ipv4_hdr *ip_hdr;
        uint32_t ip_dst;
        /* Read the lookup key (i.e. ip_dst) from the input packet */
        ip_hdr = rte_pktmbuf_mtod(m, struct ipv4_hdr *);
        ip_dst = rte_be_to_cpu_32(ip_hdr->dst_addr);

        /* Find destination port */
        if (rte_lpm_lookup(rxq->lpm, ip_dst, &next_hop) == 0 &&
                (enabled_port_mask & 1 << next_hop) != 0) {
            port_out = next_hop;

            /* Build transmission burst for new port */
            len = qconf->tx_mbufs[port_out].len;
        }

        /* if we don't need to do any fragmentation */
        if (likely (IPV4_MTU_DEFAULT >= m->pkt_len)) {
            qconf->tx_mbufs[port_out].m_table[len] = m;
            len2 = 1;
        } else {
            len2 = rte_ipv4_fragment_packet(m,
                &qconf->tx_mbufs[port_out].m_table[len],
                (uint16_t)(MBUF_TABLE_SIZE - len),
                IPV4_MTU_DEFAULT,
                rxq->direct_pool, rxq->indirect_pool);

            /* Free input packet */
            rte_pktmbuf_free(m);

            /* If we fail to fragment the packet */
            if (unlikely (len2 < 0))
                return;
        }

     详细查看另一篇博文。   
     
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值