当前位置:文档之家› Ethernet帧结构解析..

Ethernet帧结构解析..

实验一Ethernet帧结构解析一.需求分析实验目的:(1)掌握Ethernet帧各个字段的含义与帧接收过程;(2)掌握Ethernet帧解析软件设计与编程方法;(3)掌握Ethernet帧CRC校验算法原理与软件实现方法。

实验任务:(1)捕捉任何主机发出的Ethernet 802.3格式的帧和DIX Ethernet V2(即Ethernet II)格式的帧并进行分析。

(2)捕捉并分析局域网上的所有ethernet broadcast帧进行分析。

(3)捕捉局域网上的所有ethernet multicast帧进行分析。

实验环境:安装好Windows 2000 Server操作系统+Ethereal的计算机实验时间; 2节课二.概要设计1.原理概述:以太网这个术语通常是指由DEC,Intel和Xerox公司在1982年联合公布的一个标准,它是当今TCP/IP采用的主要的局域网技术,它采用一种称作CSMA/CD的媒体接入方法。

几年后,IEEE802委员会公布了一个稍有不同的标准集,其中802.3针对整个CSMA/CD网络,802.4针对令牌总线网络,802.5针对令牌环网络;此三种帧的通用部分由802.2标准来定义,也就是我们熟悉的802网络共有的逻辑链路控制(LLC)。

以太网帧是OSI参考模型数据链路层的封装,网络层的数据包被加上帧头和帧尾,构成可由数据链路层识别的数据帧。

虽然帧头和帧尾所用的字节数是固定不变的,但根据被封装数据包大小的不同,以太网帧的长度也随之变化,变化的范围是64-1518字节(不包括8字节的前导字)。

帧格式Ethernet II和IEEE802.3的帧格式分别如下。

EthernetrII帧格式:----------------------------------------------------------------------------------------------| 前序| 目的地址| 源地址| 类型| 数据| FCS |----------------------------------------------------------------------------------------------| 8 byte | 6 byte | 6 byte | 2 byte | 46~1500 byte | 4 byte|IEEE802.3一般帧格式-----------------------------------------------------------------------------------------------------------| 前序| 帧起始定界符| 目的地址| 源地址| 长度| 数据| FCS | -----------------------------------------------------------------------------------------------------------| 7 byte | 1 byte | 2/6 byte | 2/6 byte| 2 byte| 46~1500 byte | 4 byte | Ethernet II和IEEE802.3的帧格式比较类似,主要的不同点在于前者定义的2字节的类型,而后者定义的是2字节的长度;所幸的是,后者定义的有效长度值与前者定义的有效类型值无一相同,这样就容易区分两种帧格式2程序流程图:三.详细设计:1.CRC校验部分设计:为了对以太网帧的对错进行检验,需要设计CRC校验部分。

采用以为相与的方式对帧的首部相继作8位CRC校验输入参数:chCurrByte 低8位数据有效,记录了上一次CRC校验的余数chNextByte 低8位数据有效,记录了本次要继续校验的一个字节传出参数:chCurrByte 低8位数据有效,记录了本次CRC校验的余数void checkCRC(int &chCurrByte, int chNextByte){// CRC循环:每次调用进行8次循环,处理一个字节的数据。

for (int nMask = 0x80; nMask > 0; nMask >>= 1){if ((chCurrByte & 0x80) != 0) // 首位为1:移位,并进行异或运算{chCurrByte <<= 1; // 移一位if ( (chNextByte & nMask) != 0) // 补一位{chCurrByte |= 1;}chCurrByte ^= 7; // 首位已经移出,仅对低8位进行异或运算,7的二进制为0000,0111}else // 首位为0,只移位,不进行异或运算{chCurrByte <<= 1; // 移一位if ( (chNextByte & nMask) != 0) // 补一位{chCurrByte |= 1;}}}}2.部分变量的声明:int nSN = 1; // 帧序号int nCheck = 0; // 校验码int nCurrDataOffset = 22; // 帧头偏移量int nCurrDataLength = 0; // 数据字段长度bool bParseCont = true; // 是否继续对输入文件进行解析int nFileEnd = 0; // 输入文件的长度3.计算数据段的长度:nCurrDataLength =bParseCont ? // 是否到达文件末尾(file.tellg() - 8 - 1 - nCurrDataOffset) : // 没到文件末尾:下一帧头位置- 前导码和定界符长度- CRC校验码长度- 数据字段起始位置(file.tellg() - 1 - nCurrDataOffset); // 已到达文件末尾:文件末尾位置- CRC校验码长度- 数据字段起始位置4.主函数的设计:void main(int argc, char* argv[]){// 检测命令行参数的正确性if (argc != 2){cout << "请以帧封装包文件为参数重新执行程序" << endl;exit(0);}// 检测输入文件是否存在,并可以按所需的权限和方式打开ifstream file(argv[1], ios::in|ios::binary|ios::nocreate);if (!file.is_open()){cout << "无法打开帧封装包文件,请检查文件是否存在并且未损坏" << endl;exit(0);}// 变量声明及初始化int nSN = 1; // 帧序号int nCheck = 0; // 校验码int nCurrDataOffset = 22; // 帧头偏移量int nCurrDataLength = 0; // 数据字段长度bool bParseCont = true; // 是否继续对输入文件进行解析int nFileEnd = 0; // 输入文件的长度// 计算输入文件的长度file.seekg(0, ios::end); // 把文件指针移到文件的末尾nFileEnd = file.tellg(); // 取得输入文件的长度file.seekg(0, ios::beg); // 文件指针位置初始化cout.fill('0'); // 显示初始化cout.setf(ios::uppercase); // 以大写字母输出// 定位到输入文件中的第一个有效帧// 从文件头开始,找到第一个连续的“AA-AA-AA-AA-AA-AA-AA-AB”while ( true ){for (int j = 0; j < 7; j++) // 找7个连续的0xaa{if (file.tellg() >= nFileEnd) // 安全性检测{cout<<"没有找到合法的帧"<<endl;file.close();exit(0);}// 看当前字符是不是0xaa,如果不是,则重新寻找7个连续的0xaa if (file.get() != 0xaa){j = -1;}}if (file.tellg() >= nFileEnd) // 安全性检测{cout<<"没有找到合法的帧"<<endl;file.close();exit(0);}if (file.get() == 0xab) // 判断7个连续的0xaa之后是否为0xab {break;}}// 将数据字段偏移量定位在上述二进制串之后14字节处,并准备进入解析阶段nCurrDataOffset = file.tellg() + 14;file.seekg(-8,ios::cur);// 主控循环while ( bParseCont ) // 当仍然可以继续解析输入文件时,继续解析{// 检测剩余文件是否可能包含完整帧头if (file.tellg() + 14 > nFileEnd){cout<<endl<<"没有找到完整帧头,解析终止"<<endl;file.close();exit(0);}int c; // 读入字节int i = 0; // 循环控制变量int EtherType = 0; // 由帧中读出的类型字段bool bAccept = true; // 是否接受该帧// 输出帧的序号cout << endl << "序号:\t\t" << nSN;// 输出前导码,只输出,不校验cout << endl << "前导码:\t";for (i = 0; i < 7; i++) // 输出格式为:AA AA AA AA AA AA AA{cout.width(2);cout << hex << file.get() << dec << " ";}// 输出帧前定界符,只输出,不校验cout << endl << "帧前定界符:\t";cout.width(2); // 输出格式为:ABcout << hex << file.get();// 输出目的地址,并校验cout << endl << "目的地址:\t";for (i = 0; i < 6; i++) // 输出格式为:xx-xx-xx-xx-xx-xx{c = file.get();cout.width(2);cout<< hex << c << dec << (i==5 ? "" : "-");if (i == 0) // 第一个字节,作为“余数”等待下一个bit{nCheck = c;}else // 开始校验{checkCRC(nCheck, c);}}// 输出源地址,并校验cout << endl << "源地址:\t";for (i = 0; i < 6; i++) // 输出格式为:xx-xx-xx-xx-xx-xx{c = file.get();cout.width(2);cout<< hex << c << dec << (i==5 ? "" : "-");checkCRC(nCheck, c); // 继续校验}// 输出类型字段,并校验cout<<endl<<"类型字段:\t";cout.width(2);// 输出类型字段的高8位c = file.get();cout<< hex << c << dec << " ";checkCRC(nCheck, c); // CRC校验EtherType = c;// 输出类型字段的低8位c = file.get();cout.width(2);cout<< hex << c;checkCRC(nCheck,c); // CRC校验EtherType <<= 8; // 转换成主机格式EtherType |= c;// 定位下一个帧,以确定当前帧的结束位置while ( bParseCont ){for (int i = 0; i < 7; i++) //找下一个连续的7个0xaa{if (file.tellg() >= nFileEnd) //到文件末尾,退出循环{bParseCont = false;break;}// 看当前字符是不是0xaa,如果不是,则重新寻找7个连续的0xaa if (file.get() != 0xaa)i = -1;}}// 如果直到文件结束仍没找到上述比特串,将终止主控循环的标记bParseCont置为true bParseCont = bParseCont && (file.tellg() < nFileEnd);// 判断7个连续的0xaa之后是否为0xabif (bParseCont && file.get() == 0xab){break;}}// 计算数据字段的长度nCurrDataLength =bParseCont ? // 是否到达文件末尾(file.tellg() - 8 - 1 - nCurrDataOffset) : // 没到文件末尾:下一帧头位置- 前导码和定界符长度- CRC校验码长度- 数据字段起始位置(file.tellg() - 1 - nCurrDataOffset); // 已到达文件末尾:文件末尾位置- CRC校验码长度- 数据字段起始位置// 以文本格式数据字段,并校验cout << endl << "数据字段:\t";unsigned char* pData = new unsigned char[nCurrDataLength]; // 创建缓冲区file.seekg(bParseCont ? (-8 - 1 -nCurrDataLength) : ( -1 - nCurrDataLength), ios::cur);file.read(pData, nCurrDataLength); // 读入数据字段int nCount = 50; // 每行的基本字符数量for (i = 0; i < nCurrDataLength; i++) // 输出数据字段文本{nCount--;cout << pData[i]; // 字符输出checkCRC(nCheck, (int)pData[i]); // CRC校验if ( nCount < 0) // 换行处理{// 将行尾的单词写完整if ( pData[i] == ' ' ){cout << endl << "\t\t";nCount = 50;}// 处理过长的行尾单词:换行并使用连字符if ( nCount < -10)cout<< "-" << endl << "\t\t";nCount = 50;}}}delete[] pData; //释放缓冲区空间// 输出CRC校验码,如果CRC校验有误,则输出正确的CRC校验码cout << endl <<"CRC校验";c = file.get(); // 读入CRC校验码int nTmpCRC = nCheck;checkCRC(nCheck, c); // 最后一步校验if ((nCheck & 0xff) == 0) // CRC校验无误{cout.width(2);cout<<"(正确):\t"<< hex << c;}else // CRC校验有误{cout.width(2);cout<< "(错误):\t" << hex << c;checkCRC(nTmpCRC, 0); // 计算正确的CRC校验码cout<< "\t应为:" << hex << (nTmpCRC & 0xff);bAccept = false; // 将帧的接收标记置为false}// 如果数据字段长度不足46字节或数据字段长度超过1500字节,则将帧的接收标记置为falseif (nCurrDataLength < 46 || nCurrDataLength > 1500 ){bAccept = false;}// 输出帧的接收状态cout<< endl << "状态:\t\t" << (bAccept ? "Accept" : "Discard") << endl <<endl;nSN++; // 帧序号加1nCurrDataOffset = file.tellg() + 22; // 将数据字段偏移量更新为下一帧的帧头结束位置}// 关闭输入文件file.close();}四.运行结果:五.简明用户手册:用户需将源程序在VC++6.0下运行成功后,将实验1.EXE放在DOS下运行,运行的同时需要输入位置参数,本例用的位置参数为input,回车即可。

相关主题