用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变量在内存中的值,非常的方便;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342