一個簡單的工業乙太網協議實現
阿新 • • 發佈:2020-12-27
技術標籤:C語言之旅
文章目錄
前言
基於winpcap和C提示:以下是本篇文章正文內容,下面案例可供參考
一、初步思想
首先需要定義資料幀的格式,這裡為了簡便設為:
目的MAC(6位元組)+源MAC(6位元組)+型別(2位元組)+資料+CRC32(4位元組)
為了傳送和接受資料幀,設定兩個結構體,一個用來發送資料幀(給底層的),一個用來儲存資料幀(給上層或使用者的)
還需要設定兩個結構體之間的轉換函式(封裝和解封)
二、結構體設計
1.上層
上層按照資料幀的欄位設計即可
設定兩個CRC32,一個用於儲存計算的CRC32,一個用來接收資料幀中的CRC32,用來判斷資料在傳輸過程是否發生改變
typedef struct ether_header{
u_int8_t dhost[6];
u_int8_t shost[6];
unsigned short type;
}ETHHEADER,*PETHHEADER;
typedef struct ether{
ETHHEADER header;
char *text;
int textsize;
u_int32_t CRC32, CRC32_Recv;
}ETHER,*PETHER;
2.底層
就是把上層(使用者)的結構體按位元組儲存到一個字串中,size是總共要傳送的資料位元組大小,data也是在使用中分配,這裡儲存的CRC32用於使用者輸出、檢視
傳送的時候呼叫winpcap中的函式傳送
typedef struct frame{
char *data;
int size;
u_int32_t CRC32;
}FRAME,*PFRAME;
三、初始化函式
用於使用者設定資料頭
ETHHEADER creat_ethheader(char *dhost,char *shost,u_int16_t type) {
ETHHEADER ethhdr;
for(int i=0;i<6;i++){
ethhdr.dhost[i]=dhost[i];
ethhdr.shost[i]=shost[i];
}
ethhdr.type=type;
return ethhdr;
}
四、上層和頂層之間的轉換函式
實現CRC32計算的程式碼
u_int32_t crc32_table[256];
void generate_crc32_table()
{
int i, j;
u_int32_t crc;
for (i = 0; i < 256; i++)
{
crc = i;
for (j = 0; j < 8; j++)
{
if (crc & 1)
crc = (crc >> 1) ^ 0xEDB88320;
else
crc >>= 1;
}
crc32_table[i] = crc;
}
}
u_int32_t calculate_crc(u_int8_t *buffer, int len){
generate_crc32_table();
int i;
u_int32_t crc;
crc = 0xffffffff;
for (i = 0; i < len; i++)
{
crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ buffer[i]];
}
crc ^= 0xffffffff;
return crc;
}
封裝資料
(上層轉換為下層(兩個函式作用一樣,只是引數不一樣))
要點:要判斷資料欄位是否在46和1500位元組之間,還有傳過來字串不要直接使用(可能會越界訪問)
FRAME* ether_pack(PETHHEADER pethheader,char *matadata,int size){
PFRAME pf=(PFRAME)malloc(sizeof(FRAME));
char data[1600];
int i;
for(i=0;i<size;i++) data[i]=matadata[i];
if(size<46){
for(;i<45;i++) data[i]=0;
data[45]=46-size;
size=46;
}else if(size>1500){
printf("資料部分過長,大於1500B,失敗");
return NULL;
}
pf->size=size+18; //6+6+2+4=18
pf->data=(char *)malloc(pf->size);
for(i=0;i<(pf->size)-4;i++){
if(i<6) pf->data[i]=pethheader->dhost[i];
if(i>=6&&i<12) pf->data[i]=pethheader->shost[i];
if(i>=12&&i<14) pf->data[i]=(pethheader->type)>>((13-i)*8);
if(i>=14) pf->data[i]=data[i-14];
}
u_int32_t crc32=calculate_crc(pf->data,(pf->size)-4);
pf->CRC32=crc32;
for(;i<pf->size;i++){
pf->data[i]=crc32>>(pf->size-1-i)*8;
}
return pf;
}
FRAME* ether_pack_one(PETHER pether){
return ether_pack(&(pether->header),pether->text,pether->textsize);
}
解封資料(下層到上層)
這裡沒有進行CRC32的比較
PETHER ether_unpack(PFRAME pf){
PETHER pe=(PETHER)malloc(sizeof(ETHER));
int i;
for(i=0;i<6;i++){
pe->header.dhost[i]=pf->data[i];
}
for(;i<12;i++){
pe->header.shost[i]=pf->data[i];
}
pe->textsize=pf->size-18;
pe->text=(char *)malloc(pe->textsize);
for(;i<12+pe->textsize;i++){
pe->text[i-12]=pf->data[i];
}
pe->CRC32_Recv=0;
for(;i<pf->size;i++){
pe->CRC32_Recv=pe->CRC32_Recv|pf->data[i];
if(i!=pf->size-1) pe->CRC32_Recv=pe->CRC32_Recv<<8;
}
u_int32_t crc=calculate_crc(pf->data,pf->size-4);
pe->CRC32=crc;
return pe;
}