📄 (ldd) ch14-网络驱动程序(下)(转载).txt
字号:
除了使用标准的调用,每个接口可以定义它自己的ioctl命令。例如plip接口允许通过io
ctl修改其内部超时值。套接字的ioctl实现将16个命令看作对接口是私有的:从SIOCDEV
PRIVATE到SIOCDEVPRIVATE+15。
当这些命令中的一个被认识时,dev->do_ioctl在相关的接口驱动程序里被调用。这个函
数接收与通用目的的ioctl函数使用的一样的ifreq*指针。
Int (*do_ioctl)(struct device *dev, struct ifreq *ifr, int cmd);
Ifr指针指向核心空间的一个地址,放有被用户传来结构的一个拷贝。在do_ioctl返回后
,这个结构又被拷贝回用户空间;这样,驱动程序可以使用私有命令来接收和返回数据
。
设备特定的命令可以选择使用结构ifreq中的域,但它们已经带有标准的含义,驱动程序
不太可能根据自己的需要适配这个结构。域ifr_data是个caddr_t项(一个指针),用于
设备特定的需要。驱动程序和调用ioctl命令的程序应在ifr_data的使用上取得一致。例
如,pppstats使用设备特定的命令来从ppp接口驱动程序中获取信息。
在这里不值得给出do_ioctl的一个实现,但根据本章的信息和核心的例子,你应能在需
在这里不值得给出do_ioctl的一个实现,但根据本章的信息和核心的例子,你应能在需
要的时候写出一个。不过注意,plip实现不正确地使用了ifr_data,不应做为ioctl实现
的一个例子。
统计信息
一个驱动程序需要的最后一个方法是get_stats。这个方法返回指向设备统计信息的一个
指针。它的实现相当容易:
(代码335)
返回有意义的统计信息所需的实际工作散布在驱动程序中,不同的域分别被更新。下表
给出enet_statistics结构中最有趣的几个域。
int rx_packets;
int tx_packets;
这两个域含有接口成功传送的进来和外出包的总数。
这两个域含有接口成功传送的进来和外出包的总数。
int rx_errors;
int tx_errors;
出错的接收和发送的数目。接收错可能是错误的校验和、错误的包大小,以及其它问题
的结果。发送错误不太常见,一般都是线缆的问题。
int rx_dropped;
int tx_dropped;
在接收和发送时被丢弃的包的个数。当包数据没有可用内存时,包便被丢弃了。tx_drop
ped很少使用。
这个结构还有几个域,可以用来细分发送和接收时发生的错误。感兴趣的读者可以看<li
nux/if_ether.h>中结构的定义。
选播
“选播”包是指一个网络包,它将要被多于一个,但又不是全部的主机接收。
这个功能是通过给一组主机赋以特殊的硬件地址来获得的。指向这些特殊地址中的一个
的包将被这个组中所有的主机收到。在以太网的情况下,一个选播地址是将目的地址中
第一个八元组的最低位设置而得到,而所有的设备板子都在其硬件地址中将这一位清除
。
处理主机组以及硬件地址的棘手部分都有应用或核心完成了,接口驱动程序并不需要处
理这些问题。
选播包的发送很简单,与其它包完全一样。接口在传输介质上发送它们,根本不管目的
地址。核心必须设置一个正确的硬件目的地址;rebuild_header设备方法(如果被定义
)并不需要查看它整理的数据。
而另一方面,接收选播包需要设备的一些合作。当一个“有趣的”选播包被收到时(也
就是一个包的目的地址确定一组主机,其中包含了这个接口),硬件应该通知操作系统
。这意味着硬件过滤器应被设计为能够区别不同的选播地址。这个过滤器在接口的一般
操作中,将网络包的地址与其自己的硬件地址进行匹配。
操作中,将网络包的地址与其自己的硬件地址进行匹配。
典型地,在考虑选播的情况下,硬件可分为一下三类:
l 不能处理选播的接口。这类接口要么接收指向自己硬件地址的包(包括播送
包),要么节收所有的包。它们接收选播包是通过接收所有的包实现的,这样,操作系
统中会充斥着大量“无意义”的包。一般我们不认为这类接口为支持选播的,驱动程序
不在dev->flags中置IFF_MULTICAST。
点到点接口是一种特殊情况,它们通常接收所有的包,根本不进行任何硬件过滤。
l 能区别选播包和其它包(主机到主机或播送)的接口。这类接口可以被要求
接收所有的选播包,然后用软件来判断自己是否是有效的接收者。这种情形引入的开销
是可以接收的,因为一个典型的网络中选播包的数量都很少。
l 能够进行选播地址硬件检测的接口。可以给这类接口一组需要接收的选播地
址,它们会忽略其它的选播包。这对核心来说是最优化的情况,因为不会浪费处理器事
件去丢弃接口收到的“无意义”的包。
核心试图利用高级接口的能力,能最好地支持第三类接口(用途最广)。因此,当有效
的选播地址被改变时核心应通知驱动程序,它把新的一组地址传给驱动程序,这样它可
以按照新的信息更新硬件过滤器。
以按照新的信息更新硬件过滤器。
核心对选播的支持
下面是与驱动程序的选播能力相关的数据结构和函数的概括:
void (*dev->set_multicast_list)(struct device *dev)
当与设备相关的机器地址表改变时调用这个设备方法。当dev->flags被修改时它也被调
用,因为有些标志也要求你重新配置硬件过滤器。这个方法接收一个指向device结构的
指针作为参数,返回void。对实现这个方法不感兴趣的驱动程序可以留域为NULL。
struct dev_mc_list *dev->mc_list
这是域设备相关的所有选播地址的链表。这个结构的实际定义在本节结束时介绍。
int dev->mc_count
链表项数。这个信息有点冗余,但检查mc_count是否为0是优于列表检查的一个有用的快
捷方式。
IFF_MULTICAST
除非驱动程序在dev->flags中设置了这个标志,接口将不必处理选播包。当dev->flags
改变时,至少set_multicast_list会被调用。
IFF_ALLMULTI
dev->flags中的这个标志被网络软件设置以告诉驱动程序从网络中抽取所有播送包。这
在multicast-routing被使用时发生。如果这个标志被置位,dev->mc_list将不再使用去
过滤选播包。
IFF_PROMISC
当接口被置为杂乱模式时,dev->flags中的这个标志被设置。所有的包都被接口抽取,
不考虑dev->mc_list。
驱动程序开发者需要的最后一点信息是结构dev_mc_list的定义,它居于<linux/netdevi
ce.h>中。
(代码337)
(代码337)
由于选播和硬件地址与包的实际发送无关,这个结构可在不同的网络实现上移植,每个
地址由一串八元组和一个长度确定,就象dev->dev_addr。
一个典型的实现
描述set_multicast_list设计的最号办法是给出一些伪代码。
下面的函数是这个函数在一个全特征(ff)驱动程序中的一个典型实现。说这个驱动程序
是全特征的是因为它控制的接口有一个复杂的硬件包过滤器,它可以存放一个由本机接
收的选播地址表。这个表的最大尺寸是FF_TABLE_SIZE。
所由带有前缀ff_的函数是放置硬件特定操作的地方。
(代码338)
如果这个接口不能在硬件过滤器中存储到来包的选播表,那么这个实现还可以简化。在
这种情况下,FF_TABLE_SIZE减为0,代码的最后四行也不需要了。
现在,接口板一般不能存储选播表。不过,这并不是一个大问题,因为网络代码的高层
现在,接口板一般不能存储选播表。不过,这并不是一个大问题,因为网络代码的高层
会负责将不需要的包丢弃。
如我前面建议的,即使不能处理选播包的接口也需要实现set_multicast_list方法,这
样当dev->flags发生改变时可以被通知。我称这个为“无特征”(nf)的实现。它非常简
单,如下面所示:
(代码339)
处理IFF_PROMISC是很重要的,因为不然的话,用户将无法运行tcpdump或其它一些网络
分析工具。另一方面,如果接口运行一个点到点的连接,则没有任何实现set_multicast
_list的必要,因为它们接收所有的包。
快速参考
本节提供在本章中介绍的概念的参考。它也解释了驱动程序应包含的每个头文件的作用
。不过,device和sk_buff的每个域的列表,就不再重复了。
#include <linux/netdevice.h>
#include <linux/netdevice.h>
这个头文件含有struct device的定义,还包含了网络驱动程序需要的几个其它头文件。
void netif_rx(struct sk_buff *skb);
这个函数在中断时可以被调用通知核心一个包被收到了,并且封装在一个套接字缓冲区
中。
#include <linux/if.h>
被netdevice.h包含,这个文件声明接口标志(IFF_macros)和结构ifmap,它在网络驱动
程序的ioctl实现中用重要的作用。
#include <linux/if_ether.h>
ETH_ALEN
ETH_P_IP
struct ethhdr;
struct enet_statistics;
被netdevice.h包含,if_ether.h定义所有的ETH_macros,用来表示八元组长度(象地址
长度)和网络协议(象IP)。它也定义了结构ethhdr和enet_statistics。注意,不要看
enet_statistics的名字和包含它的头文件,事实上,所有的接口都要用到它,不仅仅是
以太网。
#include <linux/skbuff.h>
结构sk_buff和一些相关结构的定义,以及在缓冲区上操作的线入函数。这个头文件包含
在netdevice.h中。
#include <linux/etherdevice.h>
void ether_setup(struct device *dev);
这个函数为以太网驱动程序设置大多数设备方法为通用目的的实现。它同样设置dev->fl
ags,并且在名字中的第一个字符是空格或空字符时,将下一个可用的ethx名赋给dev->n
ame。
unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
当以太网接口收到一个包,这个函数将被调用来设置skb->pkt_type。返回值是一个协议
号,通常被存在skb->protocol中。
#include <linux/sockios.h>
SIOCDEVPRIVATE
这是16个ioctl命令的第一个,可以被每个驱动程序实现以供私用。所有的网络ioctl命
令都在sockios.h中定义。
--
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -