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

作为一个非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的激励))