美文网首页
用C构造网络数据包(可作为SV的激励)

用C构造网络数据包(可作为SV的激励)

作者: sarai_c7eb | 来源:发表于2020-09-10 15:49 被阅读0次

    作为一个非SDK开发者,对于舞弄C完成硬件寄存器配置,写各种底层SDK的JOB觉得非常的神奇;对于bit,byte级别的操作,觉得C操作起来好吃力,没有verilog来的直接;但对于高层的复杂的数据流,觉得用verilog和SV又好麻烦;
    本文介绍一种用C来构造网络数据包的过程;期望给自己打开一个思路,为构造更复杂的bit流打下基础;

    1.定义数据类型

    //the data stucture defined by user
    typedef unsigned char           uint8_t;
    typedef unsigned short int      uint16_t;
    typedef unsigned int            uint32_t;
    typedef unsigned long long int  uint64_t;
    

    不再纠结int,double,float占用几个字节了,自定义这一组数据结构,将其理解为8bit,16bit,32bit和64bit的数据;

    C中都是byte对齐的,想定义3bit,7bit,11bit就别想了;把这些想要的数据宽度向上取整为8的倍数,然后选择对应的自定义类型;

    2.定义struct

    struct就像一个包,将想要的数据类型按顺序放在包中;
    注意一个问题,C默认放置方法是申请一个最宽的盒子,然后每个成员都放在这个盒子中,作为硬件工作者,最好不用C默认的规则,使用8bit/1byte作为最小盒子单元,每个成员能用几个就用几个吧,要实现这个功能,就需要如下定义:

    //system contorl
    #pragma pack(1)
    

    再定义一组控制系统变量的宏:

    //packet control
    #define TCP 1
    #define UDP 0
    #define L3_PLD_LEN 16
    #define L4_PLD_LEN 16
    #define MAX_BUF_DP 380
    

    struct定义如下:

    struct l4_tcp {
        uint16_t SRC_PORT;
        uint16_t DES_PORT;
        uint32_t SEQ;
        uint32_t ACK;
        uint16_t OF_OTHER;
        uint16_t WINDOW;
        uint16_t CHECKSUM;
        uint16_t UPTR;
        uint8_t  PLD[L4_PLD_LEN];
    };
    
    struct l4_udp {
        uint16_t SRC_PORT;
        uint16_t DES_PORT;
        uint16_t UDP_LEN;
        uint16_t CHECKSUM;
        uint8_t  PLD[L4_PLD_LEN];
    };
    
    struct l3_ip {
        uint8_t  VERSION_AND_LEN;
        uint8_t  TOS;
        uint16_t IP_PKT_LEN;
        uint16_t ID;
        uint16_t FLAGS_FOS;
        uint16_t TTL_PROTOCOL;
        uint16_t CHECKSUM;
        uint32_t SA_IP;
        uint32_t DA_IP;
    #if TCP
        struct l4_tcp TCP_PKT;
    #elif UDP
        struct l4_udp UDP_PKT;
    #else
        uint8_t  PLD[L3_PLD_LEN];
    #endif
    };
    
    struct l2_ether2 {
        uint16_t  DA_H;
        uint32_t  DA_L;
        uint16_t  SA_H;
        uint32_t  SA_L;
        uint16_t  TL;
        struct l3_ip IP_PLD;
        uint32_t  FCS;
    };
    

    如上,

    • 定义了4层的TCP和UDP;
    • 定义了3层的IP,IP中封装 TCP也可封装UTP,通过宏定义区分;
    • 定义了2层的ethernet2包类型,其中包含了IP包;

    3.定义各种struct的函数

    uint32_t crc32(uint8_t a, uint32_t crc) {
        uint32_t poly;
        uint32_t crc_new,ct;
        uint32_t b;
        poly     = 0x04c11db7;
        crc_new  = crc;
        b = a;
        int i,j;
        uint32_t c[32];
        for (j = 0; j < 8; j++) {
            ct = 0;
            for (i = 0; i < 32; i++) {
                if (i ==0) {
                    c[i] = crc_new >> 31;
                }
                else {
                    c[i] = (((b >> 7) ^ (crc_new >> 31)) << i) & poly;
                }
                ct ^= c[i];
            }
            crc_new = ((crc_new << 1) + (b >> 7)) ^ ct;
            b = (b << 1)&0x000000ff;
        }
        //printf("%.8x,%.2x\n", crc_new,a);
        return crc_new;
    };
    
    uint32_t swap32(uint32_t a) {
        uint32_t b;
        b = ((a & 0x000000ff) << 24) | ((a & 0x0000ff00) << 8) | ((a & 0x00ff0000) >> 8) | ((a & 0xff000000) >> 24);
        return b;
    };
    
    uint16_t swap16(uint16_t a) {
        uint16_t b;
        b = ((a & 0x00ff) << 8) | ((a & 0xff00) >> 8);
        return b;
    };
    
    uint8_t bflip8(uint8_t a) {
        uint8_t b;
        b = (a & (0x01 << 0)) << 7 |
            (a & (0x01 << 1)) << 5 |
            (a & (0x01 << 2)) << 3 |
            (a & (0x01 << 3)) << 1 |
            (a & (0x80 >> 0)) >> 7 |
            (a & (0x80 >> 1)) >> 5 |
            (a & (0x80 >> 2)) >> 3 |
            (a & (0x80 >> 3)) >> 1;
        return b;
    };
    
    uint32_t bflip8_in32(uint32_t a) {
        uint32_t b;
        uint8_t  a_tmp;
        uint8_t  b_tmp;
        int i;
        b = 0x0;
        for (i = 0; i < 4; i++) {
            memcpy(&a_tmp, &a, 1);
            b_tmp = bflip8(a_tmp);
    
            b = b | b_tmp << (i * 8);
            //printf("%.2x\n", a_tmp);
            a >>= 8;
        }
        return b;
    };
    
    struct l4_tcp l4_tcp_sort(struct l4_tcp a) {
        struct l4_tcp b;
        uint32_t  cks_tmp;
        uint16_t  cks;
        int i;
    
        b.SRC_PORT = swap16(a.SRC_PORT);
        b.DES_PORT = swap16(a.DES_PORT);
        b.SEQ      = swap32(a.SEQ);
        b.ACK      = swap32(a.ACK);
        b.OF_OTHER = swap16(a.OF_OTHER);
        b.WINDOW   = swap16(a.WINDOW);
        b.UPTR     = swap32(a.UPTR);
        for (i = 0; i < L4_PLD_LEN; i++)
            b.PLD[i] = a.PLD[i];
    
        cks_tmp = a.SRC_PORT + a.DES_PORT + a.OF_OTHER + a.WINDOW + a.UPTR +
            ((a.SEQ & 0xffff0000) >> 16) + (a.SEQ & 0x0000ffff) +
            ((a.ACK & 0xffff0000) >> 16) + (a.ACK & 0x0000ffff);
    
        cks        = ((cks_tmp & 0xffff0000) >> 16) + (cks_tmp & 0x0000ffff);
        b.CHECKSUM = swap16(cks);
        return b;
    };
    
    struct l4_udp l4_udp_sort(struct l4_udp a) {
        struct l4_udp b;
        uint32_t  cks_tmp;
        uint16_t  cks;
        int i;
    
        b.SRC_PORT = swap16(a.SRC_PORT);
        b.DES_PORT = swap16(a.DES_PORT);
        b.UDP_LEN  = swap32(a.UDP_LEN);
        for (i = 0; i < L4_PLD_LEN; i++)
            b.PLD[i] = a.PLD[i];
    
        cks_tmp    = a.SRC_PORT + a.DES_PORT + a.UDP_LEN;
        cks        = ((cks_tmp & 0xffff0000) >> 16) + (cks_tmp & 0x0000ffff);
        b.CHECKSUM = swap16(cks);
        return b;
    };
    
    struct l3_ip l3_sort(struct l3_ip a) {
        struct l3_ip b;
        uint32_t  cks_tmp;
        uint16_t  cks;
    
        b.VERSION_AND_LEN = a.VERSION_AND_LEN;
        b.TOS             = a.TOS;
        b.ID              = swap16(a.ID);
        b.FLAGS_FOS       = swap16(a.FLAGS_FOS);
        b.TTL_PROTOCOL    = swap16(a.TTL_PROTOCOL);
        b.SA_IP           = swap32(a.SA_IP);
        b.DA_IP           = swap32(a.DA_IP);
    #if TCP
        b.IP_PKT_LEN = swap16(a.IP_PKT_LEN + sizeof(a.TCP_PKT));
        b.TCP_PKT    = l4_tcp_sort(a.TCP_PKT);
    #elif UDP
        b.IP_PKT_LEN = swap16(a.IP_PKT_LEN + sizeof(a.UDP_PKT));
        b.UDP_PKT    = l4_udp_sort(a.UDP_PKT);
    #else
        int i;
        b.IP_PKT_LEN = swap16(a.IP_PKT_LEN + L4_PLD_LEN + 20); //tcp
        for (i = 0; i < L3_PLD_LEN; i++)
            b.PLD[i] = a.PLD[i];
    #endif
    
        cks_tmp = (a.VERSION_AND_LEN << 8) + a.TOS + a.IP_PKT_LEN + a.ID + a.FLAGS_FOS + a.TTL_PROTOCOL +
            ((a.SA_IP & 0xffff0000) >> 16) + (a.SA_IP & 0x0000ffff) +
            ((a.DA_IP & 0xffff0000) >> 16) + (a.DA_IP & 0x0000ffff);
    
        cks = ((cks_tmp & 0xffff0000) >> 16) + (cks_tmp & 0x0000ffff);
        b.CHECKSUM = swap16(cks);
        return b;
    };
    
    struct l2_ether2 l2_sort(struct l2_ether2 a) {
        struct l2_ether2 b;
        b.DA_H   = swap16(a.DA_H);
        b.DA_L   = swap32(a.DA_L);
        b.SA_H   = swap16(a.SA_H);
        b.SA_L   = swap32(a.SA_L);
        b.TL     = swap16(a.TL);
        b.IP_PLD = l3_sort(a.IP_PLD);
        b.FCS    = swap32(a.FCS);
        return b;
    };
    
    void l2_pkt_gen(struct l2_ether2 l2_pkt) {
        struct l2_ether2  * l2, s_l2_pkt; //Ethernet packet
        int               i;
        int               pbyte;
        int               pkt_len;
        l2      = &l2_pkt;
        pbyte   = sizeof(l2_pkt) - 4;        //byte number
        pkt_len = (sizeof(l2_pkt) + 4) >> 2; //word number
        uint32_t l2_m[MAX_BUF_DP];
        uint32_t l2_n[MAX_BUF_DP];
        uint8_t  l2_f[MAX_BUF_DP];
        uint32_t crc             ;
        //sorted pkt
        crc      = 0xFFFFFFFF;
        s_l2_pkt = l2_sort(l2_pkt);
        //caculate the FCS
        memcpy(l2_f, &s_l2_pkt, sizeof(s_l2_pkt));
        for (i = 0; i < pbyte; i++)
            crc = crc32(l2_f[i],crc);
        crc = crc^0xffffffff;
        crc = ((bflip8(crc >> 24)) << 24) + 
              ((bflip8(crc >> 16)) << 16) + 
              ((bflip8(crc >> 8))  << 8 ) + bflip8(crc);
        l2->FCS = crc;
        s_l2_pkt = l2_sort(l2_pkt);
        //m
        memcpy(l2_m, &s_l2_pkt, sizeof(s_l2_pkt));
        //n
        for (i = 0; i < pkt_len; i++)
            l2_n[i] = swap32(l2_m[i]);
    
        //write to the file
        FILE* fp = NULL;
        fp = fopen("D:\ctest\Project1\l2_packet.txt", "w");
    
        printf("the l2 ethernet packet as following:\n");
        for (i = 0; i < pkt_len; i++) {
            printf("%.8x\n", l2_n[i]);
            fprintf(fp, "%.8x\n", l2_n[i]);
        }
        printf("\n");
    
        //l4 network sequence
        printf("the pkt len is %d\n", sizeof(s_l2_pkt));
        fprintf(fp, "ths pkt len is %d\n", sizeof(s_l2_pkt));
        fclose(fp);
    
    
    • crc32: 以太包的CRC计算;这里并不是FCS的计算,FCS计算在l2_pkt_gen函数中实现;
    • swap32:32bit数据按字节序反转;
    • swap16:16bit数据按字节序反转;
    • bflip8 :8bit数据按bit序反转;
    • bflip8_in32:32bit数据中每个字节按bit反转;
    • l4_tcp_sort:对tcp每个数据高低字节做反转,因为在windows中memcpy函数是按照先copy低字节,再copy高字节的顺序执行的;
    • l4_udp_sort,l3_sort,l2_sort:功能和l4_tcp_sort类似;
    • l2_pkt_gen:计算FCS,产生以太包,写入文件;

    4. main function

    int main() {
        int i;
        struct l2_ether2 l2_pkt, * l2, s_l2_pkt; //Ethernet packet
        struct l3_ip     l3_pkt, * l3  ;         //IP packet
        struct l4_tcp    l4_tcp, * l4_1;         //TCP packet
        struct l4_udp    l4_udp, * l4_2;         //TCP packet
    
        l4_1 = &l4_tcp;
        l4_2 = &l4_udp;
        l3   = &l3_pkt;
        l2   = &l2_pkt;
        //build the L4 TCP packet
        l4_1->SRC_PORT = 0x4567;  //IPV4 and 20byte header
        l4_1->DES_PORT = 0x89AB;  //COMMON type
        l4_1->SEQ      = 0x0011;  //Total len 24 bytes
        l4_1->ACK      = 0x0008;  //ID
        l4_1->OF_OTHER = 0x1010;
        l4_1->WINDOW   = 0x00ED;
        l4_1->CHECKSUM = 0x0000;
        l4_1->UPTR     = 0x0000;
        for (i = 0; i < L4_PLD_LEN; i++)
            l4_1->PLD[i] = i;
          //l4_1->PLD[i] = rand();
    
    //build the L4 UDP packet
        l4_2->SRC_PORT = 0x4567;  //IPV4 and 20byte header
        l4_2->DES_PORT = 0x89AB;  //COMMON type
        l4_2->UDP_LEN  = L4_PLD_LEN + 8;
        l4_2->CHECKSUM = 0x0000;
        for (i = 0; i < L4_PLD_LEN; i++)
            l4_2->PLD[i] = i;
          //l4_2->PLD[i] = rand();
    
    //build the L3 IP packet
        l3->VERSION_AND_LEN = 0x45;  //IPV4 and 20byte header
        l3->TOS             = 0x00;  //COMMON type
        l3->IP_PKT_LEN      = 0x14;  //Total len 24 bytes
        l3->ID              = 0xE1C2;  //ID
        l3->FLAGS_FOS       = 0x0000;
        l3->TTL_PROTOCOL    = 0x0000;
        l3->CHECKSUM        = 0x0000;
        l3->SA_IP           = 0xC0A00002;
        l3->DA_IP           = 0xC0A00003;
    #if TCP
        l3->TCP_PKT         = l4_tcp;
    #elif UDP
        l3->UDP_PKT         = l4_udp;
    #else
        for (i = 0; i < L3_PLD_LEN; i++)
            l3->PLD[i] = i;
    #endif
    
        //build the L2 ethernet packet
        l2->DA_H   = 0x1122;
        l2->DA_L   = 0x33445566;
        l2->SA_H   = 0x7788;
        l2->SA_L   = 0x99AABBCC;
        l2->TL     = 0x0800;
        l2->FCS    = 0xCCDDEEFF;
        l2->IP_PLD = l3_pkt;
    
        //generate the packet
        l2_pkt_gen(l2_pkt);
        return 0;
    }
    

    指定TCP、UDP,IP,MAC层的参数,调用函数产生数据报文;

    5. 小结

    C中常用struct,数组,指针来构造数据和引用数据;
    对于bit操作,除&^!|等逻辑运算符外,常常需要移位操作来进行bit级的运算;
    另外memcpy对bit的copy非常的有效,读者可以慢慢体会;
    最后推荐使用VS进行compile和debug,可以watch变量在内存中的值,非常的方便;

    相关文章

      网友评论

          本文标题:用C构造网络数据包(可作为SV的激励)

          本文链接:https://www.haomeiwen.com/subject/ytspektx.html