目录
以太网协议
以太网协议的简介
在<<网络层协议——IP协议>>一文中说过,实际上IP协议也不真正负责传输数据,IP协议和TCP协议一样也只负责提供传输数据的策略,只不过策略的着重点不一样,IP协议提供的策略主要是负责给数据指路,真正负责传输数据的只有数据链路层,举个例子,当我们想去某个地方旅游时是需要一本指南攻略和一俩车的,IP协议就像这本指南攻略,数据链路层就像车,如果没有IP协议,每到达一个中间站就不知道下一步该往哪走,而如果没有车,则一步都无法前进,一切都是空谈了。其中以太网协议就是在数据链路层中负责传输数据的协议。
以太网协议所处的位置

如上图就很好地说明了以太网协议所处的位置。
以太网帧(或者说MAC帧)的格式

以太网帧中各个字段的含义如下:
- 数据字段:表示以太网帧的有效载荷,其可能是一个完整的IP报文、也可能是ARP报文、RARP报文。如果是IP报文,则IP报文的大小只能在46字节到1500字节浮动;如果是另外两个报文,这两个报文的大小通常只有28字节,因为有规定(为什么有这个规定会在下文中说明)以太网帧中的数据字段(即有效载荷)的长度最小为46字节,最大为1500字节,所以如果因为以太网帧的有效载荷的类型是ARP报文和RARP报文而导致该以太网帧的有效载荷的长度不够46字节,则需要在该帧的有效载荷的后面补填充位PAD。
- 目的地址字段、源地址字段:在<<网络层协议——IP协议>>一文中说过,网络层的IP协议只负责给数据指南,数据链路层才真正负责传输数据,即数据链路层要负责将IP报文封装成以太网帧,然后将以太网帧发给下一个设备,那么问题来了,网络层负责指南时定位的是下一个设备的IP地址,IP地址是网络层的概念,为了将数据链路层和网络层解耦(如果不解耦会有什么后果呢?第一,如果未来IP地址的格式发生了变动,那么不光整个网络层需要进行修改、整个数据链路层也需要跟着修改;第二、网络层是软件层,其是在操作系统内部的,修改网络层只需要修改各类OS的源码、然后重装系统即可,虽然工程量很大,但并非不能做到,而数据链路层是在驱动、硬件层上的,如果想要修改数据链路层,那全球所有的硬件设备都得跟着变动,否则设备就无法使用,这个成本就太大了),数据链路层是不能使用IP地址来定位下一个设备的,于是才诞生了目的地址字段和源地址字段。填充在这两个字段上的地址也被称为MAC地址、物理地址、网卡地址,它是一个用6个字节、即48个比特位表示的正整数,一般用16进制数字加上冒号的形式来表示,例如:08:00:27:03:fb:19。MAC地址通常在网卡出厂时就被内嵌到网卡中,是一个固定的序列,用于标识网卡的唯一性,每台设备只要配备了一张网卡,那么就具有一个MAC地址(即每个网卡都有一个全球唯一的MAC地址),只要网卡具有唯一性,使用网卡的主机也就具有唯一性了。注意虽然每个MAC地址在全球也是具有唯一性的,但MAC地址并不被用于在公网中标识唯一一台主机,而被用在同一个局域网中标识唯一一台主机,源地址字段上的MAC地址就表示当前以太网帧是哪台主机发送的,目的地址字段上的MAC地址就表示当前以太网帧是要发给哪台主机。
- 类型字段:所占两个字节,如果值是16进制的0800、即0x0800,则表示数据字段里值的类型是一个IP报文;如果值是0x0806或者0x8035,则说明数据字段里值的类型分别又是ARP报文或者RARP报文。
对MAC地址的补充说明:我们可以在Linux机器中通过ifconfig命令查看当前主机的MAC地址,如下图所示。

如何理解帧头和帧尾呢?
(结合下图思考)操作系统内核是C语言写的,而数据链路层和网络层一样又是属于内核协议栈的,因此以太网协议和IP协议也一定是用C语言编写的,但注意和IP、TCP、UDP报头不一样的是,以太网帧的帧头和帧尾都不再是位段类型,而是正常的结构体类型了,如下图所示就是帧头和帧尾的定义:

数据链路层如何将以太网帧解包并向上交付给网络层呢?

是比较简单的,因为以太网帧的帧头或者说报头是固定大小的6+6+2=14字节,帧尾是固定大小的4字节,剩下的所有内容都是有效载荷,所以解包的过程就是把收到的以太网帧的前14个字节去掉,然后把后4个字节去掉,最后把剩下的内容即有效载荷交给网络层的对应协议(通过2个字节的类型字段上的值即可得知有效载荷的类型,然后数据链路层就知道该把有效载荷交给网络层的哪个协议了,比如说如果根据类型字段得知有效载荷是IP报文、则就会把IP报文上交给网络层的IP协议;如果得知有效载荷是ARP报文、则就会把ARP报文上交给网络层的ARP协议)。本段内容体现在代码层面上就是:
- 数据链路层接收一个以太网帧时肯定是要new出【MTU+帧头14+帧尾4】字节、即一般是1518字节的空间以防止接收不下的,顺便在new的时候把这块空间全初始化成\0或者说0(字符\0的阿斯克码就是整形0,所以数字0就表示字符\0),假如用指针char* p管理这块空间,接收到以太网帧后,首先通过(*(struct _MAC_FRAME_HEADER*)p).m_cType得到类型字段上的值以确定有效载荷的类型进而确定未来需要将有效载荷向上交付给上层中的哪个协议,然后通过某种手段比如类似于(说“类似”是因为笔者举例时所用的计算以太网帧的方法不是很准确,因为当有效载荷中存在字符\0时,笔者所说的这种计算方法就无法成功计算,至于OS中是如何计算出以太网帧的大小的,我们不必关心,只要只能OS能够计算即可)通过while循环计算出该以太网帧的大小(即在while循环中让指针p不断++,只要指针p指向的不是\0或者说0,就让计数器变量int cnt加1,循环终止条件是遇到\0,循环终止后,cnt就是以太网帧的大小了),计算出以太网帧的大小cnt后,想要去掉占用14字节的报头和占用4字节的帧尾,则只需要通过while循环从以太网帧的第15个字节开始,循环cnt-14-4次,每次将一个字节拷贝到另一块空间上即可,这另一块空间存储就是完整的有效载荷,最后直接将这块存储着完整有效载荷的空间的地址向上交付给有效载荷对应的、之前通过(*(struct _MAC_FRAME_HEADER*)p).m_cType算出来的上层协议,这样一来该上层协议就能通过解引用这块地址获取有效载荷的内容了。
数据链路层如何将上层交给自己的IP报文或者其他类型的报文比如ARP报文封装成以太网帧呢?
非常简单,以网络层交给数据链路层的报文是IP报文为例,数据链路层首先创建帧头对象和帧尾对象,然后给它们赋值,然后把收到的IP报文拷贝到帧头对象的后面,最后再把帧尾对象拷贝到IP报文的后面即可。本段内容体现在代码层面上就是:
- 首先数据链路层在接收IP报文时肯定是要开空间的,因为根据IP协议,每个IP报文是不会超过MTU字节、即1500字节的,所以可以new一个1501字节的空间以防止接收不下,并把空间全部初始化成\0,然后再接收该IP报文,然后在数据链路层中计算出该IP报文的大小,这里计算IP报文的大小可以完全仿照上文计算以太网帧的方式(比如设置计数器变量int cnt,通过while循环让管理这块空间的指针p不断++,只要指针p指向的不是\0或者说0,就让计数器变量int cnt加1,循环终止条件是遇到\0,循环终止后,cnt就是IP报文的大小了),当然数据链路层也可以选择读取IP报头中的16位总长度字段,该字段上的值直接就是整个IP报文的大小,但如果以太网帧的有效载荷不是IP报文而是ARP报文,那就只能按照本段蓝色圆括号中说的方法老老实实手动计算ARP报文的大小了。
- 计算出有效载荷的大小cnt后,直接char *p1 = new char【cnt+帧头14+帧尾4】,然后(*(struct _MAC_FRAME_HEADER*)p1) = {在其中填充帧头对象的各个成员的值},这样就完成了填充帧头对象;然后char* p2 = p1+sizeof(struct _MAC_FRAME_HEADER),以p2指向的位置开始填充有效载荷的内容,因为我们知道有效载荷的大小cnt,所以设置一个while循环循环cnt次,每次让p2[i] = p[i]即可完成填充有效载荷的内容(注意p[i]的这个p是上一段中用于指向IP报文的起始地址的指针);最后再char *p3 = p2+cnt,以p3指向的位置开始填充CRC校验字段的内容,因为该字段是4字节,所以直接(*(int*)p3)={在其中填充CRC校验字段的内容}即可完成填充该字段了。可以看到,通过这样的方式就完成了将以太网帧的帧头、有效载荷、帧尾给“拼凑”在一起,完成了将IP报文封装成以太网帧的任务。
局域网通信原理
在

本文详细介绍了以太网协议和ARP协议。以太网协议在数据链路层负责传输数据,阐述了其帧格式、局域网通信原理等。ARP协议用于根据IP地址获取MAC地址,说明了其工作过程、缓存机制,还介绍了ARP伪装成为中间人及ARP攻击让主机无法上网的原理。
最低0.47元/天 解锁文章
3027

被折叠的 条评论
为什么被折叠?



