linux网络设备分析.docx
《linux网络设备分析.docx》由会员分享,可在线阅读,更多相关《linux网络设备分析.docx(54页珍藏版)》请在冰豆网上搜索。
linux网络设备分析
linux网络设备分析
[摘要]在本文中,起首概括了收集设备总体特点和工作道理,接着在分析了一个重要的数据构造device后,重点分析了收集设备的全部初始化工作过程;简单地分析了设备的打开和封闭的操作后,是有关数据包的传输和接收的分析;在最后,本文对写收集设备驱动法度榜样做了一个总结。
以上的每部分的分析,差不多上在NE2000以太网卡的差不多长进行的。
在附录中是一个虚拟的字符设备驱动法度榜样以及写那个法度榜样的领会,该法度榜样已成功应用过,它是在收集设备分析之前本人做的一个小小的实验。
一.收集设备概述
在LINUX中,为了简化对设备的治理,所有外围的硬件设备被归结为三类:
字符设备(如键盘、鼠标等)、块设备(如硬盘、光驱、软驱等)和收集设备(也称为收集接口,networkinferface),如以太网卡。
在本文中,我们将等效应用“收集设备”和“收集接口”这两个概念,而对某个具体的收集设备,我们将称之为“物理设备”或“物理收集设备”。
为了樊篱收集情形中物理收集设备的多样性,LINUX对所有的物理设备进行抽象并定义了一个同一的概念,称之为接口(Interface)。
所有对收集硬件的拜望差不多上经由过程接口进行的,接口供给了一个对所有类型的硬件一致化的操作集合来处理全然数据的发送和接收。
一个收集接口被看作是一个发送和接收数据包(packets)的实体。
关于每个收集接口,都用一个device的数据构造表示,有关该数据构造的具体内容,将在本文的后面具体介绍。
平日,收集设备是一个物理设备如以太网卡,但软件也能够作为收集设备,如回送设备(loopback)。
在内核启动时,经由过程收集设备驱动法度榜样,将挂号存在的收集设备。
设备用标准的支撑收集的机制来转递收到的数据到响应的收集层。
所有被发送和接收的包都用数据构造sk_buff表示。
这是一个具有专门好的灵活性的数据构造,能够专门轻易增长或删除收集协定数据包的首部。
收集设备作为个中的三类设备之一,它有其专门专门的处所。
它与字符设备及块设备都有专门大年夜的不合:
♣收集接口不存在于Linux的文件体系中,而是在核心顶用一个device数据构造表示的。
每一个字符设备或块设备则在文件体系中都存在一个响应的专门设备文件来表示该设备,如/dev/hda1、/dev/sda1、/dev/tty1等。
收集设备在做数据包发送和接收时,直截了当经由过程接口拜望,不须要进行文件的操作;而对字符设备和块设备的拜望都需经由过程文件操作界面。
♣收集接口是在体系初始化时及时生成的,关于核心支撑的但不存在的物理收集设备,将弗成能有与之相对应的device构造。
而关于字符设备和块设备,即使该物理设备不存在,在/dev下也必定有响应的专门文件与之相对应。
且在体系初始化时,核心将会对所有内核支撑的字符设备和块设备进行挂号,初始化该设备的文件操作界面(structfile_operations),而不管该设备在物理上是否存在。
以上两点是收集设备与其他设备之间存在的最重要的不合。
然而,它们之间又有一些合营之处,如在体系中一个收集设备的角色和一个安装的块设备类似。
一个块设备在blk_dev数组及核心其他的数据构造中挂号本身,然后依照要求,经由过程本身的request_function函数“发送”和“接收”数据块。
类似地,为了能与别处世界进行数据交换,一个收集接口也必须在一个专门的数据构造中挂号本身。
在体系内核中,存在字符设备治理表chardevs和块设备治理表blkdevs,这两张储存着指向file_operations构造的指针的设备治理表,分别用来描述各类字符驱动法度榜样和块设备驱动法度榜样。
类似地,在内核中也存在着一张收集接口治理表dev_base,但与前两张表不合,dev_base是指向device构造的指针,因为收集设备是经由过程device数据构造来表示的。
dev_base实际上是一条device构造链表的表头,在体系初始化完成今后,体系检测到的收集设备将主动地储存在这张链表中,个中每一个链表单位表示一个存在的物理收集设备。
当要发送数据时,收集子体系将依照体系路由表选择响应的收集接口进行数据传输,而当接收到数据包时,经由过程驱动法度榜样挂号的中断办事法度榜样进行数据的接收处理(软件收集接口除外)。
以下是收集设备工作道理图:
图一Linux收集设备工作道理图
每一个具体的收集接口都应当有一个名字,以在体系中能独一标识一个收集接口。
平日一个名字仅注解该接口的类型。
Linux对收集设备定名有以下商定:
(个中N为一个非负整数)
ethN以太网接口,包含10Mbps和100Mbps;
trN令牌环接口;
slNSLIP收集接口;
pppNPPP收集接口,包含同步和异步;
plipNPLIP收集接口,个中N与打印端标语雷同;
tunlNIPIP紧缩频道收集接口;
nrNNetROM虚拟设备接口;
isdnNISDN收集接口;
dummyN空设备;
lo回送收集接口。
二.重要数据构造——structdevice
构造device储备一个收集接口的重要信息,是收集驱动法度榜样的核心。
在逻辑上,它能够瓜分为两个部分:
可见部分和隐藏部分。
可见部分是由外部赋值;隐藏部分的域段仅面向体系内部,它们能够随时被改变。
下面我们将对之进行具体的分析和解剖。
/*frominclude/linux/netdevice.h*/
structdevice
{
1.属性
char*name;
设备的名字。
假如第一字符为NULL(即’\0’),register_netdev(drivers/net/net_init.c)将会赋给它一个n最小的可用收集设备名ethn。
unsignedlongrmem_end;/*shmem"recv"end*/
unsignedlongrmem_start;/*shmem"recv"start*/
unsignedlongmem_end;/*sharedmemend*/
unsignedlongmem_start;/*sharedmemstart*/
这些域段标识被设备应用的共享内存的首地址及尾地址。
假如设备用来接收和发送的内存块不合,则mem域段用来标识发送的内存地位,rmem用来标识接收的内存地位。
mem_start和mem_end可在体系启动时用内核的敕令行指定,用ifconfig能够查看它们的值。
rmem域段从来不被驱动法度榜样以外的法度榜样所引用。
unsignedlongbase_addr;/*deviceI/Oaddress*/
unsignedcharirq;/*deviceIRQnumber*/
I/O基地址和中断号。
它们差不多上在设备检测时代被赋值的,但也能够在体系启动时指定传入(如传给LILO)。
ifconfig敕令可显示及修改他们的当前值。
volatileunsignedcharstart;/*startanoperation*/
volatileunsignedcharinterrupt;/*interruptarrived*/
这是两个二值的低层状况标记。
平日在设备打开时置start标记,在设备封闭时清start标记。
当interrupt置位时,表示有一个中断已达到且正在进行中断办事法度榜样理。
unsignedlongtbusy;/*transmitterbusymustbelongforbitops*/
标识“发送忙”。
在驱动法度榜样不克不及接收一个新的需传输的包时,该域段应当为非零。
structdevice*next;
指向下一个收集设备,用于爱护链表。
unsignedcharif_port;
记录哪个硬件I/O端口正在被接口所用,如BNC,AUI,TP等(drivers/net/de4x5.h)。
unsignedchardma;
设备用的DMA通道。
一些设备可能须要以上两个域段,但非必须的。
unsignedlongtrans_start;/*Time(injiffies)oflastTx*/
前次传输的时刻点(injiffies)
unsignedlonglast_rx;/*TimeoflastRx*/
前次接收的时刻点(injiffies)。
如trans_start可用来赞助内核检测数据传输的逝世锁(lockup)。
unsignedshortflags;/*interfaceflags(alaBSD)*/
该域描述了收集设备的才能和特点。
它包含以下flags:
(include/linux/if.h)
IFF_UP
表示接口在运行中。
当接口被激活时,内核将置该标记位。
IFF_BROADCAST
表示设备中的广播地址时有效的。
以太网支撑广播。
IFF_DEBUG
调试模式,表示设备调试打开。
当想操纵printk及其他一些基于调试目标的信息显示时,可应用那个标记位。
因此当前没有正式的驱动法度榜样应用它,但它能够在法度榜样中经由过程ioctl来设置从而应用它。
IFF_LOOPBACK
表示这是一个回送(loopback)设备,回送接口应当置该标记位。
核心是经由过程检查此标记位来确信设备是否是回送设备的,而不是看设备的名字是否是lo。
IFF_POINTTOPOINT
表示这是一个点对点链接(SLIPandPPP),点对点接口必须置该标记位。
Ifconfig也能够置此标记位及清除它。
若置上该标记位,则dev->dstaddr应也响应的置为链接对方的地址。
IFF_MASTER/*masterofaloadbalancer*/
IFF_SLAVE/*slaveofaloadbalancer*/
此两个标记位在装入平等化中要用到。
IFF_NOARP
表示不支撑ARP协定。
平日的收集接口能传输ARP包,假如想让接口不履行ARP,可置上该标记位。
如点对点接口不须要运行ARP。
IFF_PROMISC
全局接收模式。
在该模式下,设备将接收所有的包,而不关这些包是发给谁的。
在缺省情形下,以太网接口会应用硬件过滤,以包管只接收广播包及发给本收集接口的包。
Sniff的道理确实是经由过程设置收集接口为全局接收模式,接收所有达到本接口序言的包,来“偷听”簿子网的“隐秘”。
IFF_MULTICAST
能接收多点传送的IP包,具有多点传输的才能。
ether_setup缺省是置该标记位的,故若不想支撑多点传送,必须在初始化时清除该标记位。
IFF_ALLMULTI
接收所有多点传送的IP包。
IFF_NOTRAILERS/*无收集TRAILER*/
IFF_RUNNING/*资本被分派*/
此标记在Linux中没什么用,只是为了与BSD兼容。
unsignedshortfamily;/*addressfamilyID(AF_INET)*/
该域段标识本设备支撑的协定地址簇。
大年夜部分为AF_INET(英特网IP协定),接口平日不须要用那个域段或赋值给它。
unsignedshortmetric;/*routingmetric(notused)*/
unsignedshortmtu;
不包含数据链路层帧首帧尾的最大年夜传输单位(MaximumTransferUnit)。
收集层在包传输时要用到。
对以太网而言,该域段为1500,不包含MAC帧的帧首和帧尾(MAC帧格局稍后所示)。
unsignedshorttype;/*interfacehardwaretype*/
接口的硬件类型,描述了与该收集接口绑在一路的序言类型。
Linux收集设备支撑专门多不合种类的序言,如以太网,X.25,令牌环,SLIP,PPP,AppleLocaltalk等。
ARP在剖断接口支撑哪种类型的物理地址时要用到该域段。
若是以太网接口,则在ether_setup中将之设为ARPHRD_ETHER(Ethernet10Mbps)。
unsignedshorthard_header_len;/*hardwarehdrlength*/
在被传送的包中IP头之前的字节数。
关于以太网接口,该域段为14(ETH_HLEN,include\linux\if_ether.h),那个值可由MAC帧的格局得出:
MAC帧格局:
目标地址(6字节)+源地址(6字节)+数据长度(2字节)+数据(46~~1500)+FCS
void*priv;/*pointertoprivatedata*/
该指针指向私稀有据,平日该数据构造中包含structenet_statistics。
类似于structfile的private_data指针,但priv指针是在设备初始化时被分派内存空间的(而不是在设备打开时),因为该指针指向的内容包含设备接口的统计数据,而这些数据即使在接口卸下(down)时也应能够获得的,如用户经由过程ifconfig查看。
unsignedcharpad;/*makedev_addralignedto8bytes*/
unsignedcharbroadcast[MAX_ADDR_LEN];/*hwbcastadd*/
广播地址由六个0xff构成,即表示255.255.255.255。
memset(dev->broadcast,0xFF,ETH_ALEN);(drivers/net/net_init.c)
unsignedchardev_addr[MAX_ADDR_LEN];/*hwaddress*/
设备的物理地址。
当包传送给驱动法度榜样传输时,要用物理地址来产生精确的帧首。
unsignedcharaddr_len;/*hardwareaddresslength*/
物理地址的长度。
以太网网卡的物理地址为6字节(ETH_ALEN)。
unsignedlongpa_addr;/*protocoladdress*/
unsignedlongpa_brdaddr;/*protocolbroadcastaddr*/
unsignedlongpa_mask;/*protocolnetmask*/
该三个域段分别描述接口的协定地址、协定广播地址和协定的收集掩码。
若dev->family为AF_INET,则它们即为IP地址。
这些域段可用ifconfig赋值。
unsignedshortpa_alen;/*protocoladdresslength*/
协定地址的长度。
AF_INET的为4。
unsignedlongpa_dstaddr;/*protocolP-Pothersideaddr*/
点对点协定接口(如SLIP、PPP)用那个域记录连接另一边的IP值。
structdev_mc_list*mc_list;/*Multicastmacaddresses*/
intmc_count;/*Numberofinstalledmcasts*/
structip_mc_list*ip_mc_list;/*IPmulticastfilterchain*/
这三个域段用于处理多点传输。
个中mc_count表示mc_list中的项目数。
__u32tx_queue_len;/*Maxframesperqueueallowed*/
一个设备的传输队列能容纳的最大年夜的帧数。
对以太网,缺省为100;而plip则为节俭体系资本,仅设为10。
/*Forloadbalancingdriverpairsupport*/
unsignedlongpkt_queue;/*Packetsqueued*/
structdevice*slave;/*Slavedevice*/
structnet_alias_info*alias_info;/*maindevaliasinfo*/
structnet_alias*my_alias;/*aliasdevs*/
structsk_buff_headbuffs[DEV_NUMBUFFS];
指向收集接口缓冲区的指针。
2.办事处理法度榜样
以下是一些对收集接口的操作,类似与字符设备和块设备。
收集接口操作能够分为两部分,一部分为全然操作,即每个收集接口都必须有的操作;另一部分是可选操作。
/*全然操作*/
int(*init)(structdevice*dev);/*Calledonlyonce.*/
初始化函数的指针,仅被调用一次。
当挂号一个设备时,核心一样会让驱动法度榜样初始化该设备。
初始化函数功能包含以下内容:
检测设备是否存在;主动检测该设备的I/O端口和中断号;填写该设备device构造的大年夜部分域段;用kmalloc分派所需的内存空间等。
若初始化掉败,该设备的device构培养可不能被链接到全局的收集设备表上。
在体系启动时,每个驱动法度榜样都试图挂号本身,当只有那些实际存在的设备才会挂号成功。
这与用主设备号及次设备号索引的字符设备和块设备不合。
int(*open)(structdevice*dev);
打开收集接口。
每当接口被ifconfig激活时,收集接口都要被打开。
Open操作做以下工作:
挂号一些须要的体系资本,如IRQ、DMA、I/O端口等;打开硬件;将module应用计数器加一。
int(*stop)(structdevice*dev);
停止收集接口。
操作内容与open相逆。
int(*hard_start_xmit)(structsk_buff*skb,structdevice*dev);
硬件开端传输。
那个操作要求对一个包的传输,那个包原储存在一个socket缓冲区构造中(sk_buff)。
int(*hard_header)(structsk_buff*skb,structdevice*dev,unsignedshorttype,
void*daddr,void*saddr,unsignedlen);
那个函数可依照先前获得的源物理地址和目标物理地址建立硬件头(hardwareheader)。
以太网接口的缺省函数是eth_header。
int(*rebuild_header)(void*eth,structdevice*dev,unsignedlongraddr,structsk_buff*skb);
在一个包被发送之前重建硬件头。
关于以太网设备,如有未知的信息,缺省函数将应用ARP填写。
structenet_statistics*(*get_stats)(structdevice*dev);
当一个应用法度榜样须要明白收集接口的一些统计数据时,可调用该函数,如ifconfig、netstat等。
/*可选操作*/
void(*set_multicast_list)(structdevice*dev);
设置多点传输的地址链表(*mc_list)。
int(*set_mac_address)(structdevice*dev,void*addr);
改变硬件的物理地址。
假如收集接口支撑改变它的硬件物理地址,就可用那个操作。
专门多硬件不支撑该功能。
int(*do_ioctl)(structdevice*dev,structifreq*ifr,intcmd);
履行依附接口的ioctl敕令。
int(*set_config)(structdevice*dev,structifmap*map);
改变接口设备。
设备的I/O地址和中断号能够经由过程该函数进行及时修改。
void(*header_cache_bind)(structhh_cache**hhp,structdevice*dev,
unsignedshorthtype,__u32daddr);
void(*header_cache_update)(structhh_cache*hh,structdevice*dev,unsignedchar*haddr);
int(*change_mtu)(structdevice*dev,intnew_mtu);
那个函数负责使接口MTU改变后生效。
假如当MTU改变时驱动法度榜样要作一些专门的工作,就应当写那个函数。
structiw_statistics*(*get_wireless_stats)(structdevice*dev);
};
三.收集设备的初始化
收集设备的初始化重要工作是检测设备的存在、初始化设备的device构造及在体系中挂号该设备。
类似于字符设备和快块设备,体系内核中也存在着一张收集接口治理表dev_base,但与dev_base是指向device构造的,因为收集设备是经由过程device数据构造来表示的。
dev_base实际上是一条device构造链表的表头,在体系初始化完成今后,体系检测到的收集设备将主动地储存在这张链表中,个中每一个链表单位表示一个存在的物理收集设备。
挂号成功的收集设备必定可在dev_base链表中找到。
收集设备的初始化从触发角度看可分为两类:
一类是由shell敕令insmod触发的模块化驱动法度榜样(module),只有模块化的收集设备驱动法度榜样才能用这种方法对设备进行初始化,称为“模块初始化模式”;另一类是体系驱动时由核心主动检测收集设备并进行初始化,我们称为“启动初始化模式”。
明显,这两种初始化模式存在专门多不合之处,以下我们对两者分别进行分析。
1.“模块初始化模式”的分析
♣概述
insmod敕令将调用响应模块的init_module(),装载模块。
init_module函数在初始化dev->init函数指针后,将调用register_netdev()在体系挂号该设备。
若挂号成功,则模块装载成功,不然返回掉足信息。
register_netdev起首检查设备名是否已确信,若没赋值则给它一个缺省的值ethN,N为最小的可用以太网设备号注;然后,收集设备本身的init_function,即刚在init_module中赋值的dev->init,将被调用,用来实现对收集接口的实际的初始化工作。
若初始化成功,则将该收集接口加到收集设备治理表dev_base的尾部。
全部函数调用关系图如下所示。
下面我们以用得最广泛以太网卡之一——NE2000兼容网卡为例子进行分析。
NE2000网卡的重要驱动法度榜样在文件drivers/net/ne.c中。
图二“模块初始化模式”的函数调用关系图
♣init_module
init_module---模块初始化