network arplab finished
This commit is contained in:
130
network/implementation_report.md
Normal file
130
network/implementation_report.md
Normal file
@ -0,0 +1,130 @@
|
||||
# ARP及ICMP协议实现报告
|
||||
|
||||
本文档详细记录了在一个C语言网络协议栈项目中,从零开始实现地址解析协议(ARP)和Internet控制报文协议(ICMP)的完整过程。项目基于一个为Windows环境设计的初始模板,整个过程不仅包括了协议逻辑的实现,还涉及了代码的Linux兼容性改造和整体架构的模块化重构。
|
||||
|
||||
## 1. 初始代码分析
|
||||
|
||||
项目开始于一个名为`start`的目录,其中包含一个简化的网络协议栈框架。
|
||||
|
||||
### 1.1. 项目结构勘探
|
||||
|
||||
首先,我们分析了`start`目录下的源代码,特别是`xnet_tiny/src`子目录。我们识别出以下几个核心文件:
|
||||
|
||||
- `xnet_tiny.h`: 定义了协议栈的基本数据结构,如以太网帧头`xether_hdr_t`和网络数据包`xnet_packet_t`。
|
||||
- `xnet_tiny.c`: 包含了协议栈的核心驱动逻辑,如`xnet_init()`(初始化)和`xnet_poll()`(轮询)。
|
||||
- `app.c`: 项目的主入口,调用`xnet_init()`和`xnet_poll()`来启动和运行协议栈。
|
||||
- `xnet_app/port_pcap.c`: 负责与`pcap`库交互,进行底层网络数据包的收发。
|
||||
|
||||
### 1.2. 关键实现点定位
|
||||
|
||||
在`xnet_tiny.c`中,我们发现了`ethernet_in`函数,它负责处理接收到的以太网数据帧。函数内部的`switch`语句根据以太网帧头的协议类型字段来分发数据包:
|
||||
|
||||
```c
|
||||
switch (swap_order16(hdr->protocol)) {
|
||||
case XNET_PROTOCOL_ARP:
|
||||
// 此处为空,是ARP实现入口
|
||||
break;
|
||||
case XNET_PROTOCOL_IP: {
|
||||
// 此处为空,是IP及ICMP实现入口
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这两个`case`分支是空的,这正是我们需要插入ARP和IP/ICMP处理逻辑的地方。
|
||||
|
||||
## 2. ARP协议实现
|
||||
|
||||
为了保持代码的模块化,我们将ARP相关的逻辑实现在单独的文件中。
|
||||
|
||||
### 2.1. 创建ARP模块
|
||||
|
||||
我们创建了以下两个文件:
|
||||
|
||||
- `xarp.h`: 定义ARP协议包结构`xarp_packet_t`和ARP缓存条目`xarp_entry_t`,并声明了外部接口`xarp_init()`和`xarp_in()`。
|
||||
- `xarp.c`: 实现ARP的具体逻辑。
|
||||
|
||||
### 2.2. 实现ARP核心逻辑
|
||||
|
||||
在`xarp.c`中,我们完成了以下工作:
|
||||
|
||||
1. **ARP缓存(ARP Table)**: 定义了一个静态数组`arp_table[]`作为ARP缓存,用于存储IP地址到MAC地址的映射。
|
||||
2. **`xarp_init()`**: 此函数在协议栈初始化时被调用,负责清空ARP缓存中的所有条目。
|
||||
3. **`xarp_in()`**: 这是处理ARP报文的核心。
|
||||
- 当接收到一个ARP请求报文时,检查其请求的目标IP地址是否为本机IP。如果是,则构建一个ARP响应报文,填入本机的MAC地址,然后通过`ethernet_out_to()`函数发送出去。
|
||||
- 无论是请求还是响应,都会从报文中提取发送方的IP和MAC地址,并更新到ARP缓存中(`update_entry`函数)。
|
||||
4. **`xarp_resolve()`**: 为了让上层协议(如IP)能够通过IP地址查询MAC地址,我们添加了此函数。它会首先查询ARP缓存。如果找到匹配的条目,则返回对应的MAC地址;如果未找到,则调用`send_arp_request()`广播一个ARP请求,并返回`NULL`,由上层决定是否重试。
|
||||
|
||||
### 2.3. 集成到协议栈
|
||||
|
||||
我们将ARP模块集成到主协议栈中:
|
||||
|
||||
1. 在`xnet_tiny.c`中包含了`xarp.h`。
|
||||
2. 在`xnet_init()`函数的末尾调用`xarp_init()`。
|
||||
3. 在`ethernet_in()`的`switch`语句中,为`XNET_PROTOCOL_ARP`添加了对`xarp_in(packet)`的调用。
|
||||
|
||||
为了让`xarp.c`能够调用`xnet_tiny.c`中的`ethernet_out_to`等函数,我们将这些函数的`static`关键字移除,并在`xnet_tiny.h`中添加了它们的声明,使其成为公共接口。
|
||||
|
||||
## 3. ICMP及IP层实现
|
||||
|
||||
与ARP类似,我们为ICMP和IP创建了独立的模块。
|
||||
|
||||
### 3.1. 创建ICMP和IP模块
|
||||
|
||||
- `xicmp.h`: 定义了IP报头`xip_hdr_t`和ICMP报文结构`xicmp_packet_t`。
|
||||
- `xicmp.c`: 实现了`xicmp_in()`函数,用于处理传入的ICMP报文。
|
||||
- `xip.h` / `xip.c`: 创建了一个简单的IP输出模块,核心是`xip_out()`函数,用于发送IP包。
|
||||
|
||||
### 3.2. 实现ICMP Echo Reply
|
||||
|
||||
`xicmp_in()`是ICMP实现的核心,其工作流程如下:
|
||||
|
||||
1. **IP报头解析**: 首先,剥离以太网头部,解析IP报头。验证版本号、头部长度和头部校验和。
|
||||
2. **目标IP检查**: 确认该IP包的目标地址是本机。
|
||||
3. **ICMP报文处理**:
|
||||
- 剥离IP头部,解析ICMP报文。
|
||||
- 检查ICMP类型是否为**Echo Request**(Ping请求)。
|
||||
- 如果是,则准备一个**Echo Reply**(Ping应答)报文。大部分数据(如ID、序列号和数据负载)可以直接从请求报文中复制。
|
||||
- 将ICMP类型设置为`ICMP_TYPE_ECHO_REPLY`,然后重新计算ICMP校验和。
|
||||
4. **构建IP响应包**:
|
||||
- 构建IP头部,源和目的IP地址与收到的请求包相反。
|
||||
- 重新计算IP头部校验和。
|
||||
5. **发送响应**: 调用`xip_out()`函数将响应包发送出去。`xip_out`内部会使用`xarp_resolve`来查找下一跳的MAC地址。
|
||||
|
||||
### 3.3. 集成到协议栈
|
||||
|
||||
1. 在`xnet_tiny.c`中包含了`xicmp.h`。
|
||||
2. 在`ethernet_in()`的`switch`语句中,为`XNET_PROTOCOL_IP`添加了对`xicmp_in(packet)`的调用。
|
||||
3. 在`xicmp.c`中,使用新创建的`xip_out()`函数来发送响应包,而不是直接调用底层的`ethernet_out_to()`,实现了更好的分层。
|
||||
|
||||
## 4. Linux兼容性改造与重构
|
||||
|
||||
原始代码是为Windows设计的,为了在Linux上运行,我们进行了以下关键修改:
|
||||
|
||||
1. **修正`pcap_device.c`**:
|
||||
- 该文件包含了大量`#if defined(WIN32)`的条件编译块,用于加载`npcap`动态库。
|
||||
- 我们删除了所有Windows特定的代码(如`#pragma comment`, `GetSystemDirectory`, `MessageBox`等),只保留了`#else`分支下的标准`pcap`实现,使其在Linux下能够直接编译通过。
|
||||
2. **修正`port_pcap.c`**:
|
||||
- 原始代码中的`my_mac_addr`数组长度为8字节,这是错误的。我们将其修正为标准的6字节以太网MAC地址。
|
||||
- 将硬编码的pcap设备IP地址从`192.168.254.1`修改为`192.168.254.2`,与我们协议栈配置的IP保持一致。
|
||||
3. **IP地址集中管理**:
|
||||
- 最初,本机IP地址在`xarp.c`和`xicmp.c`中都有硬编码定义。
|
||||
- 我们将其移至`xnet_tiny.c`中定义,并在`xnet_tiny.h`中通过`extern`声明,实现了全局统一配置。
|
||||
|
||||
## 5. 编译与测试
|
||||
|
||||
完成编码后,即可进行编译测试:
|
||||
|
||||
1. **安装依赖**: 确保Linux系统上已安装`libpcap`的开发库(如`sudo apt install libpcap-dev`)。
|
||||
2. **编译**:
|
||||
- 进入`start/build`目录。
|
||||
- 运行`cmake ..`来生成Makefile。
|
||||
- 运行`make`进行编译。
|
||||
3. **运行与测试**:
|
||||
- 编译成功后,会在`build`目录下生成可执行文件`xnet.exe`。
|
||||
- 使用`sudo ./xnet.exe`运行(需要root权限以访问网络接口)。
|
||||
- 在另一台处于同一局域网的主机上,执行`ping 192.168.254.2`,即可测试ICMP Echo功能。同时,可以通过`tcpdump`或`Wireshark`抓包观察ARP请求和响应过程。
|
||||
|
||||
## 6. 总结
|
||||
|
||||
通过以上步骤,我们成功地在一个基础网络框架上,完整地实现了ARP和ICMP Echo功能。整个过程不仅加深了对网络协议底层工作原理的理解,也锻炼了在现有代码基础上进行功能扩展、重构和跨平台移植的能力。最终的代码结构清晰,模块化程度高,易于理解和进一步扩展。
|
||||
Reference in New Issue
Block a user