Lab6_Network Driver

本文档详细介绍了MIT6.828实验六的网络驱动编写过程,涉及Intel E1000网卡的初始化、PCI接口、内存映射I/O、DMA、数据包发送和接收。此外,还涵盖了网络服务器和Web服务器的实现,包括接收系统调用、数据包处理和错误处理策略。实验中,你需要完成多个练习,如设置PCI设备、创建接收和发送数据包的系统调用,以及实现Web服务器回显功能。

简介

在本实验中,我们将为 NIC(Network Interface Card, 网络接口卡)编写驱动程序。这个网卡基于Intel 82540EM芯片,也称为E1000。

除了编写驱动程序之外,我们还需要创建一个system call 来访问我们的驱动程序。我们将实现缺失的网络服务器代码,以在网络堆栈和驱动程序之间传输数据包。我们还可以通过完成Web服务器将所有内容绑定在一起。使用新的Web服务器,我们将能够从文件系统中提供文件。

这个 lab 比之前的都要难,因为 there are no skeleton files, no system call interfaces written in stone, and many design decisions are left up to you.

QEMU’s virtual network

QEMU’s documentation has more about user-net here

QEMU 默认提供一个运行在 IP 10.0.2.2上的虚拟路由,并且为 JOS 分配IP 10.0.2.15。To keep things simple, we hard-code these defaults into the network server in net/ns.h. 为了解决连接内网问题(host 无法连接运行在QEMU上的Web server),我们配置QEMU在主机某个端口上运行服务。该端口只需连接到JOS中的某个端口,并在真实主机和虚拟网络之间来回传送数据。

To find out what ports QEMU is forwarding to on your development host, run make which-ports. For convenience, the makefile also provides make nc-7 and make nc-80, which allow you to interact directly with servers running on these ports in your terminal.

~/workspaces/MIT6.828/lab$ make which-ports
Local port 26001 forwards to JOS port 7 (echo server)
Local port 26002 forwards to JOS port 80 (web server)

Packet Inspection

Makefile 配置了QEMU的网络栈,将进出的网络包记录到 qemu.pcap中。我们可以使用图形化的 wireshark,或是使用命令行获得网络包的hex/ASCII信息。

tcpdump -XXnr qemu.pcap

Debugging the E1000

The E1000 can produce a lot of debug output, so you have to enable specific logging channels. Some channels you might find useful are:

Flag Meaning
tx Log packet transmit operations
txerr Log transmit ring errors
rx Log changes to RCTL
rxfilter Log filtering of incoming packets
rxerr Log receive ring errors
unknown Log reads and writes of unknown registers
eeprom Log reads from the EEPROM
interrupt Log interrupts and changes to interrupt registers.

2.2. The Network Server

从零开始写一个网络栈是十分困难的,因此我们使用lwIP, 其是一个开源的轻量级TCP/IP协议套件,其中包含网络栈。You can find more information on lwIP here

The network server is actually a combination of four environments:

  • core network server environment (includes socket call dispatcher and lwIP)
  • input environment
  • output environment
  • timer environment

我们将实现图中标记为绿色的部分。
network Server

2.3. The Core Network Server Environment

For each user environment IPC, the dispatcher in the network server calls the appropriate BSD socket interface function provided by lwIP on behalf of the user.

Regular user environments do not use the nsipc_* calls directly. Instead, they use the functions in lib/sockets.c, which provides a file descriptor-based sockets API.

there is a key difference between file server and network server. BSD socket calls like accept and recv can block indefinitely. Since this is unacceptable, the network server uses user-level threading to avoid blocking the entire server environment.

In addition to the core network environment there are three helper environments. Besides accepting messages from user applications, the core network environment’s dispatcher also accepts messages from the input and timer environments.

Part A: Initialization and transmitting packets

我们需要为我们的内核添加时间概念。使用时钟中断,并用一个变量进行计数。

Exercise 1

Add a call to time_tick for every clock interrupt in kern/trap.c(in kern/time.c). Implement sys_time_msec and add it to syscall in kern/syscall.c so that user space has access to the time.

只是添加一个系统调用,很简单。

// 1
// LAB 4: Your code here.
	case (IRQ_OFFSET + IRQ_TIMER):
		// 回应8259A 接收中断。
		lapic_eoi();
		time_tick();
		sched_yield();
		break;
// 2
static int
sys_time_msec(void)
{
   
   
	// LAB 6: Your code here.
	return time_msec();
	// panic("sys_time_msec not implemented");
}
// 3
case SYS_time_msec:
		return sys_time_msec();

The Network Interface Card

Exercise 2

Browse Intel’s Software Developer’s Manual for the E1000. This manual covers several closely related Ethernet controllers. QEMU emulates the 82540EM.

我们应该浏览第2章以了解设备。 要编写驱动程序,还需要熟悉第3章和第14章以及4.1(尽管不是4.1的小节)。还要参考第13章。

82540em接口图

Receive and Transmit Description

  • Packet Reception
    • recognizing the presence of a packet on the wire,
    • performing address filtering,
    • storing the packet in the receive data FIFO,
    • transferring the data to a receive buffer in host memory,
      • buffer size RCTL.BSIZE & RCTL.BSEX
    • updating the state of a receive descriptor.
      • Status
      • Errors
      • Special

PCI Interface

PCI是外围设备互连(Peripheral Component Interconnect)的简称,是在目前计算机系统中得到广泛应用的通用总线接口标准:

  • 在一个PCI系统中,最多可以有256根PCI总线,一般主机上只会用到其中很少的几条。
  • 在一根PCI总线上可以连接多个物理设备,可以是一个网卡、显卡或者声卡等,最多不超过32个。
  • 一个PCI物理设备可以有多个功能,比如同时提供视频解析和声音解析,最多可提供8个功能。
  • 每个功能对应1个256字节的PCI配置空间

为了在引导期间执行PCI初始化,PCI代码遍历PCI总线以寻找设备。 当它找到设备时,它会读取其供应商ID和设备ID,并使用这两个值作为搜索pci_attach_vendor数组的键。 该数组由struct pci_driver条目组成,如下所示:

struct pci_driver {
   
   
    uint32_t key1, key2;
    int (*attachfn) (struct pci_func *pcif);
};

如果发现的设备的供应商ID和设备ID与数组中的条目匹配,则PCI代码会调用该条目的attachfn来执行设备初始化。

struct pci_func {
   
   
    struct pci_bus *bus;

    uint32_t dev;
    uint32_t func;

    uint32_t dev_id;
    uint32_t dev_class;

    uint32_t reg_base[6];
    uint32_t reg_size[6];
    uint8_t irq_line;
};

我们重点关注struct pci_func的最后三个成员,因为它们记录了设备的negotiated memory,I/O 和中断资源。 reg_basereg_size数组包含最多六个基址寄存器或BAR的信息。reg_base存储内存映射 I/O 区域的基地址(或 I/O 端口资源的基本 I/O 端口),reg_size包含reg_base中相应基值的大小(以字节为单位)或 I/O 端口数, 和irq_line包含分配给设备的 IRQ line 以进行中断。

pci_func_enable将会使能设备,协调资源,并将其填入struct pci_func

Exercise 3

Implement an attach function to initialize the E1000.

查看手册以及根据内核启动时的打印信息,我们可以知道 E1000 的 Vender ID = 0x8086, Device ID = 0x100E。

PCI: 00:00.0: 8086:1237: class: 6.0 (Bridge device) irq: 0
PCI: 00:01.0: 8086:7000: class: 6.1 (Bridge device) irq: 0
PCI: 00:01.1: 8086:7010: class: 1.1 (Storage controller) irq: 0
PCI: 00:01.3: 8086:7113: class: 6.80 (Bridge device) irq: 9
PCI: 00:02.0: 1234:1111: class: 3.0 (Display controller
<think> 我们正在处理一个Docker Compose网络创建失败的错误。错误信息是:"failed to create network docker-compose-lab_hm: Error response from daemon: Failed to program NAT chain: reverse_rule() takes exactly 2 arguments (1 given)"。这个错误通常与Docker的网络配置有关,特别是与iptables或网络规则有关。 根据错误信息,我们看到`reverse_rule() takes exactly 2 arguments (1 given)`,这暗示在调用`reverse_rule`函数时传递的参数数量不正确。这可能是Docker内部的一个问题,但更可能与系统环境有关,比如Docker版本与系统内核或iptables版本的兼容性问题。 解决思路: 1. **更新Docker到最新版本**:因为这个问题可能是已知的bug,在最新版本中已经修复。 2. **检查iptables版本**:某些iptables版本与Docker存在兼容性问题。 3. **重启Docker服务**:有时候重启可以解决临时性的问题。 4. **检查网络配置**:特别是与NAT相关的配置。 具体步骤: **步骤1:更新Docker** 首先,确保Docker和Docker Compose是最新版本。更新方法因操作系统而异。 对于Ubuntu: ```bash sudo apt update sudo apt upgrade docker-ce docker-ce-cli containerd.io docker-compose-plugin ``` 注意:如果你使用的是docker-compose独立版本(而不是Docker Compose Plugin),那么你需要单独更新docker-compose。但是,从错误信息看,你使用的是`docker compose`(注意中间没有横线),这通常是Docker Compose Plugin的命令。 **步骤2:检查iptables版本** 运行: ```bash iptables --version ``` 如果iptables版本较旧(比如1.6.x以下),可能会出现问题。建议升级到较新的版本(例如1.8.x)。但升级iptables需要谨慎,因为它会影响系统的网络配置。 **步骤3:重启Docker服务** ```bash sudo systemctl restart docker ``` 然后再次尝试运行`docker compose up -d`。 **步骤4:检查是否有冲突的网络配置** 有时候,已有的网络配置(如之前创建的Docker网络)可能会导致冲突。你可以尝试删除未使用的网络: ```bash docker network prune ``` **步骤5:检查内核模块** 确保与网络相关的内核模块已加载,特别是`iptable_nat`模块。运行: ```bash sudo modprobe iptable_nat ``` 如果该模块已经加载,则不会显示任何输出。你也可以通过`lsmod | grep iptable_nat`来检查。 **步骤6:检查Docker的默认网络配置** 如果上述方法都不奏效,可以尝试重置Docker的网络设置(注意:这将删除所有Docker网络,可能需要重新创建容器): ```bash docker network disconnect -f $(docker network ls -q) docker network rm $(docker network ls -q) sudo systemctl restart docker ``` 然后再次尝试。 **步骤7:降级Docker版本** 如果问题是在更新Docker后出现的,可以考虑降级到之前的稳定版本。 由于错误信息中提到`reverse_rule`函数,这可能是Docker内部的一个bug。在Docker的GitHub issue中,我们可以搜索到类似的问题(例如moby/moby#41831)。如果上述步骤都不能解决,可以尝试在Docker社区中查找该问题是否有其他解决方案。 根据经验,这个错误通常由Docker版本过旧或与iptables版本不兼容引起,因此更新Docker和iptables是最可能的解决方案。 如果问题仍然存在,请提供以下信息以便进一步排查: - 操作系统版本 - Docker版本(`docker version`) - Docker Compose版本(`docker compose version`) - iptables版本(`iptables --version`) - 完整的错误日志(如果有) 希望这些步骤能帮助你解决问题。 </think>### Docker Compose 网络创建失败解决方案 执行 `docker compose up -d` 时出现 `failed to create network` 错误,核心原因是 **Docker 的 NAT 规则配置异常**,具体是 `reverse_rule()` 函数参数传递错误。以下是逐步解决方案: --- #### 📌 **根本原因分析** 1. **Docker 网络驱动问题** `reverse_rule()` 是 Docker 网络驱动处理 NAT 规则的函数,此错误表明: - Docker 版本与宿主机网络配置(iptables/nftables)不兼容 - 内核模块加载异常(如 `iptable_nat` 模块未加载) 2. **典型触发场景** - Docker 版本过旧(<20.10.0) - 系统升级后内核与 Docker 不兼容 - iptables 被替换为 nftables 但 Docker 未适配 --- #### 🛠️ **解决方案步骤** ##### **方法1:更新 Docker 和依赖组件** ```bash # 更新 Docker 至最新稳定版 sudo apt update sudo apt install --only-upgrade docker-ce docker-ce-cli containerd.io # 更新 Docker Compose(独立版本) sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose ``` ✅ **验证更新**: ```bash docker --version # 需 ≥20.10.0 docker-compose --version ``` ##### **方法2:加载内核 NAT 模块** ```bash # 加载必需内核模块 sudo modprobe iptable_nat sudo modprobe ipt_MASQUERADE # 使配置永久生效 echo "iptable_nat" | sudo tee -a /etc/modules echo "ipt_MASQUERADE" | sudo tee -a /etc/modules ``` ##### **方法3:重置 Docker 网络规则** ```bash # 停止 Docker 服务 sudo systemctl stop docker # 清理旧网络配置 sudo iptables -t nat -F sudo ip link delete docker0 # 重启 Docker sudo systemctl start docker ``` ##### **方法4:手动创建网络(临时方案)** 在 `docker-compose.yml` 中显式声明网络: ```yaml networks: default: driver: bridge name: my_custom_network # 避免使用默认名称 ``` --- #### ⚠️ **注意事项** 1. **避免混合使用 iptables/nftables** 检查防火墙后端: ```bash sudo update-alternatives --config iptables ``` - 选择 `iptables-legacy` 而非 `nftables`(Docker 兼容性更好) 2. **检查内核支持** 确保内核启用 NAT: ```bash zgrep NAT /proc/config.gz | grep CONFIG_NF_NAT ``` 输出需包含 `CONFIG_NF_NAT=y` 3. **升级内核(旧系统适用)** 对于 Ubuntu 18.04/CentOS 7 等旧系统: ```bash # Ubuntu sudo apt install linux-generic-hwe-20.04 # CentOS sudo yum install kernel kernel-devel ``` --- #### 🔍 **故障排查命令** ```bash # 查看 Docker 日志 journalctl -u docker -n 100 --no-pager # 检查 NAT 模块状态 lsmod | grep -E 'iptable_nat|nf_nat' # 验证网络驱动 docker info | grep -i network ``` 若输出显示 `Network Driver: bridge` 且无错误日志,表明修复成功。 > 💡 **经验提示**:此错误常见于 Ubuntu 18.04/Debian 10 → 20.04 升级后的环境,更新 Docker 至 23.0+ 版本可彻底解决[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值