题外话
去年11月份做IO相关源码的时候就已经觉得有些宏定义纯纯恶心,没想到今日的网络协议栈再一次体会了这种感觉😅,感谢网络协议栈让我重温旧日的美好时光。
第四章 接口:以太网
以太网接口
IP分组的以太网帧如上图所示,包含6字节的目的和源地址,2字节的类型,末尾4字节的CRC。前14个字节我们在上一篇文章讨论输入输出时已经见过。对于IP分组,其类型是固定的0x0800(十进制2048)
leintr函数
感觉记住这几个函数干什么就行,也没啥特别说的。一个个源码都挺长的,放上来估计也没人想看。
leintr
检测硬件,并且如果有一个帧到达,就调用leread
把这个帧从接口转移到一个mbuf链中。如果硬件报告一个帧已传输完或发现一个差错(如一个有错误的检验和),则leintr
更新相应的接口统计,复位这个硬件,并调用lestart
来传输另一个帧。所有以太网设备驱动程序将它们接收到的帧传给ether_input
做进一步的处理。设备驱动程序构造的mbuf链不包括以太网首部,以太网首部作为一个独立的参数传递给ether_input
。结构ether_header
如下所示:
1 | struct ether_ header { |
leread函数
函数leread
的开始是由leintr
传给它的一个连续的内存缓冲区,并且构造了一个ether_header
结构和 一个mbuf链。这个链表存储来自以太网帧的数据。leread
还将输入帧传给BPF。
ether_input函数
函数ether_input
,它检查结构ether_header
来判断接收到的数据的类型,并将接收到的分组加入到队列中等待处理。
ether_output函数
当一个网络层协议,如IP,调用此接口ifnet结构中指定的函数if_ output
时,开始处理输出。所有以太网设备的if_ output
是ether_ output
。ether_output
用14字节以太网首部封装一个以太网帧的数据部分,并将它放置到接口的发送队列中。
lestart函数
函数lestart
从接口输出队列中取出排队的帧,并交给LANCE以太网卡发送。如果设备空闲,调用此函数开始发送帧。
如果设备忙,当它完成了当前帧的传输时产生一个中断。设备调用lestart来退队并传输下一帧。一旦开始,协议层不再用调用lestart来排队帧,因为驱动程序不断退队并传输帧,直到队列为空为止。
ioctl系统调用
个人认为ioctl是一个非常重要的部分,因为他不仅仅涉及网络设备。在我们常用的Linux系统中,ioctl是作为进程和设备交互的最重要的系统调用或者说接口,包括字符型设备(例:终端、文件)、存储设备(例:磁盘)以及网络设备(例:网卡)。可以说绝大多数设备的驱动程序(至少在我的认知里)都要依靠此函数来实现,是研究Linux系统内核、内核态开发和安全非常重要的一部分。
但在这门课里我不确定它是不是那么重要?😝
原型如下:
1 | int ioctl(int fd, unsigned long com, ...); |
这个函数的三个参数分别为如下含义:
- fd,文件描述符,即ioctl要交互的设备是谁
- com,command命令,即ioctl要调用哪个命令(在驱动程序中会根据不同的值执行不同的函数或者命令)
- 第三个参数为执行command命令时传入的参数
本书中涉及到的与网络接口相关的com命令如下:
其中命令列即为ioctl的第二个参数com
ifioctl函数
ifioctl起始就是ioctl调用后,网络接口驱动程序所对应的处理函数。
大概流程就是用户进程调用ioctl系统调用,发生中断陷入内核态,然后操作系统根据fd找到对应设备驱动程序,这里就是网络接口的驱动程序。随后网络接口的ifioctl根据ioctl的后两个参数进行处理,参照上图的表,已经有了一些命令的描述。
- 对于命令SIOCGIFCONF,
ifioctl
调用ifconf
来构造一个可变长ifreq结构的表。 - 对于其他
ioctl
命令,数据参数是指向一个ifreq
结构的指针。ifunit
在ifnet列表中查找名称为进程在ifr->ifr_name中提供的文本名称(例如:“s10”,“le1”或“lo0”)的接口。如果没有匹配的接口,ifioctl返回 ENXIO。 - 对于命令SIOSIFFLAGS,
ifioctl
可以利用ifconfig来设置或清除标志IFF_UP,实现对一个接口的禁用和启用。这将会调用if_up
和if_down
函数
第6章 IP编址
基本概念
几类IP地址划分,主机和路由器简单看看就好,也没什么说的
sockaddr_in结构
1 | struct in_addr { |
这个结构体比较简单,但还是挺常用的,需要额外注意的是s_addr的存储并不根据系统的大小端,而是根据网络流的顺序
sockaddr和sockaddr_in对比如下:
in_ifaddr结构
in_ifaddr
为Internet协议的接口地址结构。对于每个指派给一个接口的IP地址,分配了一个in_ifaddr
结构,并且添加到接口地址列表和IP地址全局列表中。
in_ifinit函数
按课件和课本的话中间还有ifioctl和io_control函数的部分没搞,但说实话真不知道分析这几个函数有什么用😅,总不能让我考试也隔着审源码吧,我个人觉得知道这些函数干什么就行了。希望老师不要打我的脸,求求了😇
主要步骤:
- 将地址复制到此结构并将此变化通知硬件;
- 忽略原地址配置的任何路由;
- 为这个地址建立-一个子网掩码;
- 建立一个默认路由到连接的网络(或主机);
- 将此接口加入到所有主机组。
练习题
这两章复习的真没什么道理,全是函数源码分析,实在没什么看头。课件上还有几个练习题可以看看,希望考试别恶心人