网络数据包内核探险记(一) —— linux内核网络分层结构
以数据包的接受为例:
网卡--->网络设备驱动
netfilter----监管层
协议栈 (进行解包,等待,控制 检查)
会话层 (sock) 面向会话 网络数据缓冲区
表示层 封装表示为一种类型的文件,它叫做套接字 socket
应用层 fd
目标:
①熟悉网络驱动设计结构
②能够读懂大部分网络设备驱动
③设计虚拟网卡
框架:
网络协议接口层
网络层调用网卡发送函数的统一接口:
static inline int dev_queue_xmit(struct sk_buff *skb)
{
return dev_queue_xmit_sk(skb->sk, skb);
}
dev_queue_xmit(struct sk_buff *skb)
|
dev_queue_xmit_sk(struct sock *sk, struct sk_buff *skb)
|
__dev_queue_xmit(struct sk_buff *skb, void *accel_priv)
/*该函数的功能:
上层数据发送的时候,已经携带net_device结构体
net_device相当于网卡
路由协议,通过IP检索出网卡类型
*/
struct net_device *dev = skb->dev;
skb = dev_hard_start_xmit(skb, dev, txq, &rc);
网络层接收数据包的统一接口:
int netif_rx(struct sk_buff *skb)
{
trace_netif_rx_entry(skb);
return netif_rx_internal(skb);
}
netif_rx(struct sk_buff *skb)
|
netif_rx_internal(struct sk_buff *skb)
网络有关的数据结构:
sk_buff: 网络数据的载体,又被称为套接字缓冲区。
sk_buff结构可以分为两个部分,一部分是存储数据包缓存,另一部分是由一组用于内核管理的指针组成。
unsigned char *head;
unsigned char *data;
sk_buff_data_t tail;
sk_buff_data_t end;
如图所示:
发送数据包:
①内核网络处理模块必须建立一个包含要传输的数据包和协议数据包的sk_buff结构体
②把sk_buff结构体传递给网卡驱动接口dev_queue_xmit
上层协议的组装过程就是在sk_buff层层的加入了不同的协议头
接受数据包:
①封装为sk_buff的结构体,并且通过netif_rx向上层提交
上层协议的解包过程就是在sk_buff的数据区域层层将协议剥离
sk_buff用法详解:
对象:sk_buff
分配:struct sk_buff *alloc_skb(unsigned int size,gfp_t priority)
//参数1:大小
//参数2:优先级
typedef enum
{
GFP_KERNEL, //内核级分配
GFP_ATOMIC, //原子级分配 优先级最高
__GFP_HIGHMEM,
__GFP_HIGH
}gfp_t;
//在中断中进行skb分配的时候一般使用dev_alloc_skb函数
struct sk_buff *dev_alloc_skb(unsigned int length)
释放:void kfree_skb(struct sk_buff *skb);
void dev_kfree_skb(struct sk_buff *skb) //非中断上下文调用
void dev_kfree_skb_irq(struct sk_buff *skb) //中断上下文调用
设置:unsigned char *skb_put(struct sk_buff *skb, unsigned int len) //添加数据
skb->tail += len;
skb->len += len;
unsigned char *skb_push(struct sk_buff * skb, unsigned int len) //组装数据
skb->data -= len;
skb->len += len;
unsigned char *skb_pull(struct sk_buff *skb, unsigned int len) //剥离协议
skb->len -= len;
BUG_ON(skb->len < skb->data_len);
return skb->data += len;
unsigned char *skb_reserve(struct sk_buff * skb, int len) //设置空间
skb->data += len;
skb->tail += len;
万能demo:
struct sk_buff *skb;
skb=alloc_skb(len+headroom,GFP_KERNEL);
//设置空间
skb_reserve(skb,headroom);
skb_put(sbk,len);
memcpy(skb->data,data,len);
netif_rx 传送给IP层