网络协议栈CH4及CH6

题外话

image-20230424232559912

去年11月份做IO相关源码的时候就已经觉得有些宏定义纯纯恶心,没想到今日的网络协议栈再一次体会了这种感觉😅,感谢网络协议栈让我重温旧日的美好时光。

第四章 接口:以太网

以太网接口

image-20230424225903986

IP分组的以太网帧如上图所示,包含6字节的目的和源地址,2字节的类型,末尾4字节的CRC。前14个字节我们在上一篇文章讨论输入输出时已经见过。对于IP分组,其类型是固定的0x0800(十进制2048)

leintr函数

感觉记住这几个函数干什么就行,也没啥特别说的。一个个源码都挺长的,放上来估计也没人想看。

leintr检测硬件,并且如果有一个帧到达,就调用leread把这个帧从接口转移到一个mbuf链中。如果硬件报告一个帧已传输完或发现一个差错(如一个有错误的检验和),则leintr更新相应的接口统计,复位这个硬件,并调用lestart来传输另一个帧。所有以太网设备驱动程序将它们接收到的帧传给ether_input做进一步的处理。设备驱动程序构造的mbuf链不包括以太网首部,以太网首部作为一个独立的参数传递给ether_input。结构ether_header如下所示:

struct ether_ header {
    u_char ether_dhost[6] ;		/* Ethernet destination address */
    u_char ether_shost[6] ;		/* Ethernet source address*/
    u_short ether_type;			/* Ethernet frame type */
};

leread函数

函数leread的开始是由leintr传给它的一个连续的内存缓冲区,并且构造了一个ether_header结构和 一个mbuf链。这个链表存储来自以太网帧的数据。leread还将输入帧传给BPF。

ether_input函数

函数ether_input,它检查结构ether_header来判断接收到的数据的类型,并将接收到的分组加入到队列中等待处理。

ether_output函数

当一个网络层协议,如IP,调用此接口ifnet结构中指定的函数if_ output时,开始处理输出。所有以太网设备的if_ outputether_ outputether_output用14字节以太网首部封装一个以太网帧的数据部分,并将它放置到接口的发送队列中。

lestart函数

函数lestart从接口输出队列中取出排队的帧,并交给LANCE以太网卡发送。如果设备空闲,调用此函数开始发送帧。

如果设备忙,当它完成了当前帧的传输时产生一个中断。设备调用lestart来退队并传输下一帧。一旦开始,协议层不再用调用lestart来排队帧,因为驱动程序不断退队并传输帧,直到队列为空为止。

ioctl系统调用

个人认为ioctl是一个非常重要的部分,因为他不仅仅涉及网络设备。在我们常用的Linux系统中,ioctl是作为进程和设备交互的最重要的系统调用或者说接口,包括字符型设备(例:终端、文件)、存储设备(例:磁盘)以及网络设备(例:网卡)。可以说绝大多数设备的驱动程序(至少在我的认知里)都要依靠此函数来实现,是研究Linux系统内核、内核态开发和安全非常重要的一部分。

但在这门课里我不确定它是不是那么重要?😝

原型如下:

int ioctl(int fd, unsigned long com, ...);

这个函数的三个参数分别为如下含义:

  • fd,文件描述符,即ioctl要交互的设备是谁
  • com,command命令,即ioctl要调用哪个命令(在驱动程序中会根据不同的值执行不同的函数或者命令)
  • 第三个参数为执行command命令时传入的参数

本书中涉及到的与网络接口相关的com命令如下:

image-20230425001108434

其中命令列即为ioctl的第二个参数com

ifioctl函数

ifioctl起始就是ioctl调用后,网络接口驱动程序所对应的处理函数。

大概流程就是用户进程调用ioctl系统调用,发生中断陷入内核态,然后操作系统根据fd找到对应设备驱动程序,这里就是网络接口的驱动程序。随后网络接口的ifioctl根据ioctl的后两个参数进行处理,参照上图的表,已经有了一些命令的描述。

  • 对于命令SIOCGIFCONFifioctl调用ifconf来构造一个可变长ifreq结构的表。
  • 对于其他ioctl命令,数据参数是指向一个ifreq结构的指针。ifunit在ifnet列表中查找名称为进程在ifr->ifr_name中提供的文本名称(例如:“s10”,“le1”或“lo0”)的接口。如果没有匹配的接口,ifioctl返回 ENXIO。
  • 对于命令SIOSIFFLAGSifioctl 可以利用ifconfig来设置或清除标志IFF_UP,实现对一个接口的禁用和启用。这将会调用if_upif_down函数

第6章 IP编址

基本概念

几类IP地址划分,主机和路由器简单看看就好,也没什么说的

image-20230425004924718

sockaddr_in结构

struct in_addr {
    u_1ong s_addr;			/* 32-bit IP address, net byte order */
};
struct sockaddr_ in {
    u_char sin_len;				/* sizeof (struct sockaddr_ in) = 16 */
    u_char sin_family;			/* AF_INET */
    u_short sin_port;			/* 16-bit port number, net byte order */
    struct in_addr sin_ addr ;
    char sin_zero[8] ;			/* unused */
};

这个结构体比较简单,但还是挺常用的,需要额外注意的是s_addr的存储并不根据系统的大小端,而是根据网络流的顺序

sockaddr和sockaddr_in对比如下:

image-20230425010936449

in_ifaddr结构

in_ifaddr为Internet协议的接口地址结构。对于每个指派给一个接口的IP地址,分配了一个in_ifaddr结构,并且添加到接口地址列表和IP地址全局列表中。

in_ifinit函数

按课件和课本的话中间还有ifioctl和io_control函数的部分没搞,但说实话真不知道分析这几个函数有什么用😅,总不能让我考试也隔着审源码吧,我个人觉得知道这些函数干什么就行了。希望老师不要打我的脸,求求了😇

主要步骤:

  • 将地址复制到此结构并将此变化通知硬件;
  • 忽略原地址配置的任何路由;
  • 为这个地址建立-一个子网掩码;
  • 建立一个默认路由到连接的网络(或主机);
  • 将此接口加入到所有主机组。

练习题

这两章复习的真没什么道理,全是函数源码分析,实在没什么看头。课件上还有几个练习题可以看看,希望考试别恶心人

image-20230425013954629

image-20230425014013419

image-20230425013925701