Linux网络协子系统架构
System call interface :网络相关系统调用的实现。
Protocol agnostic interface(协议无关接口):屏蔽协议相关的操作,不论什么协议(UDP,TCP),提供给系统调用一个统一的接口。Network protocols:Linux网络协议栈,用来实现各种各样的网络协议。Device agnostic interface(设备无关接口):两个部分作用,向上:下面是设备驱动程序,不同网卡设备驱动程序有不同的网卡接口,那么网络协议栈发送数据就不知道要使用哪个网卡接口,网卡驱动每修改一次名字,协议栈就要做一次修改,所以就通过设备无关接口把不同的函数接口统一。比如协议栈send(),设备无关接口找到不同网卡设备驱动的send()调用。向下:不同协议(以太网,TCP。。。),设备驱动要有不同处理方法,
网卡驱动就要修改不同处理函数吗,不会,Device agnostic interface给Network protocols提供一套统一使用网络协议接口(TCP,UDP.....),驱动处理协议数据包,用的都是同一套函数。这就是Device agnostic interface的作用屏蔽协议的无关性,屏蔽设备的无关性,提供统一的接口给大家使用。
网卡驱动程序设计
学驱动:如何描述设备device,如何注册设备。
每个网络接口都由一个net_device结构描述,该结构通过如下内核函数动态分配:
1:struct net_device *alloc_netdev (int sizeof_priv, const char *mask, void (*setup)(struct net_device *))
sizeof_priv 私有数据区大小;mask 设备名;setup:初始化函数
2:struct net_device *alloc_etherdev (int sizeof)_priv)
Struct net_device的主要成员:
char name [IFNAMS] 设备名,如:eth0,eth1... unsigned long state 设备状态
unsigned long base_addr I/O基地址 unsigned int irq 中断号
int (*init) (struct net_device *dev) 初始化函数。该函数在register_netdev被调用时完成net_dev结构初始化。
int (*open) (struct net_device *dev) 打开网络接口。
int (*stop) (struct net_device *dev ) 停止网络接口。
int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev) 网络接口数据发送函数。
int (*do_ioctl) (struct net_device *dev,struct ifreq *ifr,,int cmd)
ioctl命令
int (*set_mac_address) (struct net_device *dev, void *addr)
改变mac地址的函数,需要硬件支持该功能。
int register_netdev (struct net_device *dev) 注册网络设备,网络接口驱动的注册方式与字符驱动不同之处在于它没有主次设备号,不是通过设备文件访问,通过socket访问网络设备。
Linux内核中的每个数据包都由一个套接字缓冲结构sk_buff描述,简称skb,一个sk_buff就是一个网络包,由帧头、帧数据组成。重要结构成员:
struct device *dev ; // 处理该包的设备
__u32 saddr ; //IP源地址
__u32 daddr; // IP目的地址
__u32 raddr; //IP路由器地址
unsigned char *head ; //分配空间的开始
unsigned char *data ; // 有效数据的开始
unsigned char *tail ; //有效数据的结束
unsigned char *end ; // 分配空间的结束
unsigned long len ; //有效数据的长度
Skb操作函数:struct sk_buff *alloc_skb (unsigned int len, int priority )
分配一个sk_buff结构,供协议栈代码使用。
struct sk_buff *dev_alloc_skb (unsigned int len , int priority)
分配一个sk_buff结构,供驱动代码使用。
向sk_buff存放数据 1:unsigned char* skb_push(struct sk_buff *skb, int len)
向后移动skb的tail指针,并返回tail移动之前的值。
2:unsigned char *skb_put (struct sk_buff *skb, int len)
向前移动skb的head指针,并会返回head移动之后的值。
设备打开open() 1:注册中断,DMA等 2:设置寄存器,启动设备 3:启动发送队列,告诉内核网卡已经准备好。
int net_open (struct net_device *dev) {
//申请中断
request_irq(dev->irq, &net_interrupt, SA_SHIRD, “dm9000”, dev);
//设置寄存器启动设备
...... . ....... ........
//启动发送队列
netif_start_queue(dev);
}
设备发送数据:当需要发送一个数据包时,它调用hard_start_transmit函数,该函数最终调用net_device结构中的hard_start_xmit函数指针,网卡驱动就是实现hard_start_xmit函数指针。
设备接收数据:网络接口驱动可以实现两种形式的报文接受:中断和查询,Linux中驱动多采用中断,当网卡接收到数据包,会产生中断。接收中断处理程序流程
1:分配skb,存放接受的数据 skb = dev_alloc_skb(pkt->datalen+2)
2:从网卡寄存器中读取数据到skb
3:调用netif_rx将数据交给协议栈;
整个过程没有关心数据包的协议是TCP还是UDP,还是其他协议,这就是设备无关接口(Device agnostic interface)提供了一个统一的处理函数netif_rx(skb),Device agnostic interface会去分析属于那个协议,然后把包交给相应的协议处理。