天天看点

网络协议栈设计(三)---链路层以太网分析(发送)

网络协议栈设计(三)

本篇将给大家带来网络协议栈设计的数据链路层以太网设计。

前边介绍过我们负责发送方代码编写,那么,先看看在数据链路层的工业以太网中,我们发送方需要做哪些事情。

要求:

主要设计和实现一个可以通信的数据链路层

(1)发送数据:网卡初始化,获取句柄,将数据帧封装,发送。

(2)接收数据:数据帧接收,并判断数据帧来源且完成CRC校验等检查最后传数据部分给上层协议

我们将发送和接收分两步分完成,这里我们先讨论发送数据。

我们先来看看工业以太网帧格式:

网络协议栈设计(三)---链路层以太网分析(发送)

前导码:01111110(逢5加0, 逢5删0)

目的MAC地址:接收方的MAC地址,占6个字节

源MAC地址:本地的MAC地址,占6个字节

类型: ICMPv4 ,IGMP, IPv4,ARP; ICMP v4, IPv6等,占2个字节

数据: 由上层交付。(46~1500个字节)

校验字段: 32比特位CRC校验

注:我们在此不讨论前导码,因为前导码由网卡自动完成,仅仅是为了同步发送方和接收方,提醒接收方准备接受数据,并不属于以太网MAC帧的内容。

经过分析我们可以先得到我们的发送流程图如下:

网络协议栈设计(三)---链路层以太网分析(发送)

Q&A:

看完数据流程图,可能小伙伴就要问了,为什么要控制数据最小为46个字节,最长为1500个字节?

因为在总线型网络中数据发送存在碰撞,所以数据链路层采用CSMA/CD协议来保证数据的正常发送。因此,以太网规定了一个最短帧长64字节,凡长度小于64字节的帧都是由于冲突而异常终止的无效帧。所以,我们以太网帧首部长14字节(这里不算前导码,它并不真正属于我们的数据帧)加上4个字节的校验码,数据部分最少为46字节。关于最长1500字节,我们知道,在IP层下面的每一种数据链路层协议都规定了一个数据帧中的数据字段的最大长度,这称为最大传送单元MTU。最常用的以太网就规定其MTU的值是1500字节。看到这里还是不太懂的小伙伴就需要好好去看书了。

有了上边的数据流程图和相关知识,我们开始设计我们以太网。

1、首先是数据结构的设计。

我们需要一个以太网首部结构:

struct ethernet_header
{
    u_int8_t destination_mac[];
    u_int8_t source_mac[];
    u_int16_t ethernet_type;
};
           

封装MAC帧数据我们需要一个数组来保存:

u_int8_t buffer[MAX_SIZE];
           

2、接下来是函数设计。

封装的过程我们简单描述为:

1、加载MAC帧首部

2、加载MAC数据部分

3、加载CRC检验码部分

我们将以上过程分别以一个函数来完成:

1、首先需要加载MAC帧首部,源MAC地址由我们定义的全局变量local_mac[6]直接得到,目的MAC地址则由上层交付,上层协议也由上层交付,返回值为空。

void load_ethernet_header(u_int8_t *destination_mac,u_int16_t ethernet_type)    
           

2、加载数据部分,加载的数据由上层协议提供,上层也提供一个交付的数据长度,返回值为int,加载失败返回-1。

int load_ethernet_data(u_int8_t *buffer, u_int8_t *upper_buffer, int len)
           

在此函数中,我们要检查若len的长度大于1500字节,则直接丢弃,返回-1,否则若少于46个字节,则在数据部分填充0至最少46字节。

3、加载CRC校验码。

这里我们将计算CRC校验码独立作为一个函数,返回4个字节的校验码。然后将返回值复制到我们的数据部分末尾。然后调用和复制包含在加载数据函数中。

u_int32_t calculate_crc(u_int8_t *buffer, int len)
           

至此,我们的数据帧就算封装完成了,我们将上边三个函数整合为上层协议直接调用的发送函数:

那么上层需要提供的有:一个目的MAC地址,以太网上层协议类型,以及需要发送的数据。待发送的数据长度我们可以用前边全局变量定义中的ethernet_upper_len来得到。

发送函数设计为:

int ethernet_send_packet(u_int8_t *upper_buffer,u_int8_t *destination_mac,u_int16_t ethernet_type)
{
        加载首部;
        加载数据,如果加载数据失败返回-;
        调用winpcap的发送函数pcap_sendpacket()发送;
}
           

好了,关于以太网发送数据的分析我们就做这些,下一篇为大家带来设计的细节完善和代码的具体实现。

继续阅读