sk_buff结构体内容:
1、协议头 transport_header(传输层协议头),network_header(网络层协议头),mac_header(数据链路层协议头)。
2、数据缓冲区指针
head :缓冲区起始地址, sk_buff 一旦创建, head 数据就固定了。
data :当前协议层的有效数据起始地址
tail : 当前协议层有效数据的结尾地址,和data 对应
end :缓冲区的结尾地址, sk_buff 一旦创建, end 数据就固定了。
3、指针移动
(1)put 操作:
往数据缓冲区尾部 添加可以存储网络数据包的空间。
unsigned char *skb_put (struct sk_buff *skb , unsigned int len); // 会检测放入的数据
unsigned char *__skb_put ( 同上 ) ; // 不检查
上述函数使 tail 指针下移,增加 sk_buff 中的 len 值,并返回 skb_tail 的值。
(2)push 操作:
往数据缓冲区头部 增加一段可以存储网络数据包的空间。主要用于在数据包发送时添加头部。
unsigned char *skb_push (struct sk_buff *skb , unsigned int len); // 会检测放入的数据
unsigned char *__skb_push ( 同上 ) ; // 不检查
会使 data 指针上移,也增加 len 的值。
(3)pull 操作:
用于下层协议向上层协议移交数据包,使 data 指针指向上一层协议的协议头。
unsigned char *skb_pull (struct sk_buff *skb , unsigned int len);
会将 data 指针下移,并减小 skb 的 len 值。
(4)reserve 操作:
主要用于在存储空间的头部预留 len 长度的空隙。
void skb_reserve (struct sk_buff *skb , unsigned int len);
会使 data 指针和 tail 指针同时下移。
举例以太网卡收到一个UDP数据包后,linux从底层到应用层处理的过程
1、驱动创建一个sk_buff结构体和数据缓冲区,收到的数据复制到data指向的空间,并将skb->mac_header指向data(链路层以太网头),此时有效数据的起始位置是链路层以太网头。
2、数据链路层调用skb_pull 传到网络层之后,以太网协议头被剥掉了, skb->data 指向下移到 IP 头了, len 也减掉链路层头部那个长度 skb->network_header指向 data ,即 IP 头部。
3、网络层调用skb_pull pull 传到传输层,剥掉 IP 头, data 指针继续向下移, len 长度再减掉 ip 头长度, skb->transport_header 指向 UDP 头部。
4、应用程序调用 recv() 接收数据时,从 skb->data+sizeof(struct udphdr) 的位置开始复制到应用层缓冲区,所以 ,UDP 头得以幸存,没有被剥掉