• tcpreplay 缓存算法研究


    一.  缓存算法

    1.1  算法目的

    流量拆分算法的运算会明显影响包的发送速率,为了提高发送速率, tcpreplay 使用了缓存机制,该部分代码也封装在tcpprep工具里,运行 tcpprep (tcp-preparation)工具,结果是一个针对性的缓存文件,该文件存放流量拆分算法的运算结果。同时,cache.c 存放了部分读取cachefile的函数。

    1.2  算法思想

    如何设计符合目的的缓存?达到即高效又节省?

    最简单就是使用bool[SIZE] ,存放 0和1,这样的数据结构使用1个字节存放标识,可以表示2个方向的流量。但是用1个字节表示一个packet完全没必要,可以用1个bit表示一个packet。用如下方法可以做到:

    使用一个比特位表示一个packet 的方法

    包的id:  packetid

    Index = packetid/8

    Bit = packetid%8

    Byte = &Cache[Index]

    写缓存: *Byte = *Byte + (1<<Bit)

    读缓存: result = Cache[Index] & (char)(1 << Bit)

    上述方法依然不完善,第一,pcap 包包含的 packets 的数量是未知的,用固定的 char 数组存放不够灵活。其二,如果缓存结果需要扩展,比如在‘客户端->服务端’‘服务端->客户端’两种packets状态之外,还有‘发送’‘不发送’(表示该packet发送与否),‘正确’‘错误’(表示该packet是否异常)等状态运算结果,上述算法无法扩展。

    解决第一个问题,是使用数组链表的方式取代单一数组的方式。每个链表节点存放一个可配的小数组,当当前节点数组空间不够的时候,再生成一个小数组,连在链表后面。这样的缓存,可以根据具体的packets 数灵活得分配空间。

    解决第二个问题,是设定一个参数 packets_per_byte ,表示一个字节可以容纳多少packets缓存信息,只有2种状态时该参数值为 8 ,表示4种状态时该参数值为 4。6 种状态值是2,以此类推。packets_per_byte = 1 时,1 个字节可以最多表示 16 种缓存状态。

    Tcpprep3.4.4使用2个bit表示一个packet,下面是这种情况下算法思想的图形表示:

    1.1  算法流程

    使用缓存以及不使用缓存两种情况下的发包流程。

    1.1  算法实现

    1.1.1  数据结构

    struct tcpr_cache_s { /*一个缓存节点的数据结构*/

        char data[CACHEDATASIZE]; /*该节点的小数组*/

        unsigned int packets;       /*该节点已经存放缓存结果的packet的数目*/

        struct tcpr_cache_s *next; /*链表指针*/

    };

    typedef struct tcpr_cache_s tcpr_cache_t;

     

    struct tcpr_cache_file_hdr_s {/*缓存文件头数据结构*/

        char magic[8]; /*文件标识*/

        char version[4]; /*缓存版本*/

        u_int64_t num_packets;      /* total # of packets in file */

        u_int16_t packets_per_byte; /*每个字节存放多少个packet的缓存数据*/

        u_int16_t comment_len;      /* how long is the user comment? */

    } __attribute__((__packed__));

     

    enum tcpr_dir_e { /*缓存运算结果种类*/

        TCPR_DIR_ERROR  = -1,/*异常packet,该状态没有加入缓存,而是作为返回值*/

        TCPR_DIR_NOSEND = 0, /*是否发送*/

        TCPR_DIR_C2S    = 1, /* 客户端->服务器,从 PRIMARY 接口回放*/

        TCPR_DIR_S2C    = 2 /* 服务器->客户端,从SECONDARY接口回放 */

    };

    typedef enum tcpr_dir_e tcpr_dir_t;

    1.1.2  主要函数实现

     

    /**

     *生成1个packet的缓存数据

     */

    tcpr_dir_t

    add_cache(tcpr_cache_t ** cachedata, const int send, const tcpr_dir_t interface)

    {

        static tcpr_cache_t *lastcache = NULL;

        u_char *byte = NULL;

        u_int32_t bit;

        tcpr_dir_t result = TCPR_DIR_ERROR;

        COUNTER index;

        if (*cachedata == NULL) {  /* 第一次运行,生成第一个节点 */

            *cachedata = new_cache();

            lastcache = *cachedata;

        }

        else {

            if ((lastcache->packets + 1) > (CACHEDATASIZE *CACHE_PACKETS_PER_BYTE)) { /* 当前节点如果满了,生成一个新节点*/

                lastcache->next = new_cache();

                lastcache = lastcache->next;

            }

        }

        lastcache->packets++;

        if (send == SEND) { /* 是否发送的标志位赋值*/

    index = (lastcache->packets - 1) / (COUNTER)CACHE_PACKETS_PER_BYTE;

    bit = (((lastcache->packets - 1) % (COUNTER)CACHE_PACKETS_PER_BYTE) *

                   (COUNTER)CACHE_BITS_PER_PACKET) + 1;

     byte = (u_char *) & lastcache->data[index];

    *byte += (u_char) (1 << bit);

            /* if true, set low order bit. else, do squat */

            if (interface == TCPR_DIR_C2S) {/*流量方向的标志位赋值*/

                *byte += (u_char)(1 << (bit - 1));

                result = TCPR_DIR_C2S;

            }

            else {   result = TCPR_DIR_S2C;   }

        }

        else { result = TCPR_DIR_NOSEND; /*异常情况,结果返回异常*/  }

        return result;

    }

     

     

    /**

     * 下面函数显示如何根据packet id 读取缓存结果

     */

    tcpr_dir_t

    check_cache(char *cachedata, COUNTER packetid)

    {

        COUNTER index = 0;

        u_int32_t bit;

        if (packetid == 0)   err(-1, "packetid must be > 0");

     /* 定位到缓存数组的具体位 */

        index = (packetid - 1) / (COUNTER)CACHE_PACKETS_PER_BYTE;

        bit = (u_int32_t)(((packetid - 1) % (COUNTER)CACHE_PACKETS_PER_BYTE) *(COUNTER)CACHE_BITS_PER_PACKET) + 1;

        if (!(cachedata[index] & (char)(1 << bit))) {

            return TCPR_DIR_NOSEND; /*返回是否发送的标志位结果*/

        }

        /* go back a bit to get the interface */

        bit--;

        if (cachedata[index] & (char)(1 << bit)) { return TCPR_DIR_C2S; }

        else {  return TCPR_DIR_S2C;  } /*返回流量方向结果*/

        return TCPR_DIR_ERROR; /*如果上述情况都没发送,返回异常*/

    }

    1.2  实验结果

    1.2.1  实验1

    拥有14个packet的pcap生成的缓存文件

  • 相关阅读:
    日记 2018/1/12
    【程序员笔试面试必会——排序①】Python实现 冒泡排序、选择排序、插入排序、归并排序、快速排序、堆排序、希尔排序
    Python笔试、面试 【必看】
    高性能Go并发
    Go连接MySql数据库Error 1040: Too many connections错误解决
    MAC 配置文件 ~/.zshrc
    go-statsd项目
    日记 2017.11.20
    sed 命令详解
    Opentsdb简介(一)
  • 原文地址:https://www.cnblogs.com/jiayy/p/3447082.html
Copyright © 2020-2023  润新知