内核网络【1.套接口缓存描述结构及相关操作函数】

在上一篇中提出了sk_buff结构体,这一章就详细介绍sk_buff结构体里每个成员的意义。

结构sk_buff在include/linux/skbuff.h中定义的

SKB在不同网络协议层之间传递,可被用于不同的网络协议,如二层的链路层,三层的网络层,四层的传输层,某些成员变量在传递时会发生改变,四层向三层传递前会添加一个四层首部,同样三层向二层传递前也会添加三层首部,添加比复制数据效率高,由于在数据缓存区的头部添加数据意味着要修改指向数据缓存区的真正,操作复杂,所有内核提供了skb_reserve()来完成这个功能。协议栈中的每一层在往下一层传递SKB前,首先就是调用该函数在数据缓存区头部预留出空间,如果是向上层协议传递SKB,则下层协议层的首部信息就没有用了,例如二层首部只有在二层协议时有用,三层不关心。但是内核页没有把二层首部从数据缓存区删除,只是把有效载荷指针指向三层首部,这样做的目的是提高效率。

static inline void skb_reserve(struct sk_buff *skb, int len)
{
	skb->data += len;
	skb->tail += len;
}

struct sk_buff *next和struct sk_buff *prev是为了便于组织该数据结构本身,内核把sk_buff组织成一个双向链表,sk_buff结构中有两个指针next和prev,next指向下一个结点,prev指向上一个结点。这个链表的组织比传统的双向链表要复杂,要求每个SKB必须能被整个链表的头部快速找到,为了满足这个需求,在第一个SKB结点前面会插入另一个辅助的sk_buff_head结构的头结点,可以认为sk_buff_head结构就是SKB链表的头结点。

struct sk_buff_head {
	/* These two members must be first. */
	struct sk_buff	*next;
	struct sk_buff	*prev;

	__u32		qlen;
	spinlock_t	lock;
};

qlen表示的是SKB链表中结点数,即队列长度

lock用来控制对SKB链表并发操作的自旋锁

struct  net_device *dev是一个net_device对象,表示与SKB相关联的网络接口设备,既可以是数据包到达的网络设备,也可能是要向外发送数据包的网络设备

struct sock *sk 对于本地生成的流量或发送给当前主机的流量,sk为拥有SKB的套接字,对于需要转发的数据包,sk为NULL,这里的套接字即是用户空间调用系统调用socket()创建的套接字,也包括内核套接字,通过sock_create_kern()创建的。

ktime_t tstamp 数据包到达时间,在SKB中,存储的时间戳为相对于参考时间的偏移量

char cb[48]控制缓存区,可供任何层用,用于存储专用信息,tcp协议将其用作了tcp控制缓存区,蓝牙协议也使用这个控制块

unsigned log _skb_refdst目标条目(dst_entry)地址,结构dst_entry表示给定目的地的路由选择条目。对于每个数据包(无论是入站还是出站),都需要执行路由选择表查找。这种查找有时候被称为FIB查找,查找结果决定了应如何处理数据包,如是否需要转发(如何需要转发,应该从哪个接口发出去),是否应该丢弃,是否要发送ICMP错误消息等。

struct sec_path *sp安全路径指针,包含一个IPsec XFRM变换状态(xfrm_state对象)数组。IPsec是一种第三层协议,主要用于VPN,在ipv6中必须实现IPsec,但是在ipv4中是可选的,linux在ipv4和ipv6都实现了ipsec,结构sec_path在include/net/xtrm.h中定义

unsigned int len数据包的总字节数,包括线性缓存区中数据长度,SG类型的聚合分散IO的数据以及FRAGLIST类型的聚合分散IO的数据长度,该地段值随着SKB从不同协议层传递而改变。

unsigned int data_len数据长度,仅当数据包有非线性数据(分页数据,paged data)时,才使用这个字段,即SG类型和FRAGLIST类型聚合分散IO存储区中的数据长度。

__16 mac_len 第二次报头的长度

__wsum csum校验和

__u32 priority 数据包的排队优先级,在接收路径中,SKB的优先级是根据套接字的优先级(套接字的sk_priority字段)设置的,而套接字的优先级是使用套接字选项SO_PRIORITY调用系统调用setsockopt()设置的,使用cgroupn内核模块net_prio时,可定义设置SKB优先级的规则,见cgroup/netprio.txt描述,对于转发的数据包优先级是根据ip报头的TOS(服务类型)字段设置的,有一个名为ip_tos2prio的表,它包含16个元素,将TOS映射到优先级的工作是由方法rt_tos2priority()根据ip报头的TOS字段完成的。参考net/ipv4/ip_forward.c中的方法ip_forward()以及include/net/route.h中ip_tos2prio的定义

__u8 local_df:1允许本地分段标志,如果发送数据包的套接字的pmtudisc字段的值为IP_PMTUDISC_DONT或者IP_PMTUDISC_WANT,local_df将被设置为1,如果这个套接字的pmtudisc字段的值为IP_PMTUDISC_DO或IP_PMTUDISC_PROBE,local_Df将被设置为0,参考net/ipv5/ip_output.c中的__ip_make_skb()的实现,仅当数据包的local_df为0时,才设置IP报头的不分段标志IP_DF.参考net/ipv4/ip_output.c中的方法ip_queue_xmit()

__u8 cloned:1

使用方法__skb_clone()克隆数据包时,在被克隆和克隆得到的数据包中,这个字段都被设置为1,克隆SKB意味着创建结构sk_buff的一个私有副本,该数据块由克隆SKB和主SKB共享。

__u8 ip_summed:2

IP(第三层)校验和指示器,可取值如下:

CHECKSUM_NONE:设备驱动程序不支持使用硬件校验和时,将ip_summed字段设置为CHECKSUM_NONE,这表面必须使用软件来计算校验和

CHECKSUM_UNNECESSARY:不需要计算校验和

CHECKSUM_COMPLETE:硬件已为入站数据包计算校验和

CHECKSUM_PATTIAL:已为出站数据包完成部分校验和计算,硬件应完成校验和计算

CHECKSUM_COMPLETE和CHECKSUM_PARTIAL取代了已被摒弃的标志CHECKSUM_HW。

__u8 nohdr:1

只参考有效载荷,禁止修改报头,有时候,SKB拥有者根本不需要再访问报头,在这种情况下,可调用方法skb_header_release()来设置SKB的nohdr字段,指出不应该修改这个SKB的报头

__u8 ufctiofo:3

连接跟踪信息,连接跟踪让内核能够跟踪所有的网络连接和会话,NAT依赖连接跟踪信息来完成转换,nfctinfo字段的值对应的值对应于枚举ip_conntrack_info的值,例如,连接刚开始时,nfctinfo的值为IP_CT_NEW;连接建立后,nfctinfo的值为IP_CT_ESTABLISHED,数据包与既有连接相关时,nfctinfo的值将为IP_CT_RELATED,完整的ip_conntrack_info枚举值列表在include/uapi/linux/netfilter/nf_conntrack_common.h,SKB的nfctinfo字段是在方法resolve_normal_ct()(net/netfilter/nf_conntrack_core.c)中设置的,这个方法用于执行连接跟踪查找,如果没有找到,就创建一个新的连接跟踪条目。

__u8 pkt_type:3

对于以太网,数据包类型取决与以太网报头中的目标MAC地址,并最终由方法eth_type_trans()确定

PACKET_BROADCAST:广播

PACKET_MULTICAST:组播

PACKET_HOST:目标MAC地址作为参数传入的设置的MAC地址

参考include/uapi/linux/if_packet.h中数据包类型的定义

__u8 ipv

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值