网络协议栈学习之socket, sock_common, sock, 和 sk_buff

2024-09-05 05:32

本文主要是介绍网络协议栈学习之socket, sock_common, sock, 和 sk_buff,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一. 前言

  一直很好奇socket是如何实现的,底层的数据结构又是如何,因此在这里对socket的数据结构进行分析。
  socket是传输层使用的数据结构,用于声明、定义套接字,网络层会调用sock结构体,其中sock会用到了通用sock_common结构体。而sk_buff则是内核中使用的套接字缓冲区结构体。在我们前文提到的NAT转换中,除了修改内核已有的Netfilter源码外,还有一种方式就是自己建立新的钩子函数,独立于Netfilter已有的NAT表之外,然后通过自己建立sk_buff的方式实现自制数据包或者修改已有数据包,完成NAT的类型转换(即实现通信)。下文会单独用一篇来说如何去自己建立sk_buff和钩子函数。
  这里我们看看源码中的数据结构。

二. 套接字结构体

  网络协议封装为多层,因此套接字结构体定义也有着多层结构,但是这里有一点要注意的:在网络通信中,我们通过网卡获取到的数据包至少包括了物理层,链路层和网络层的内容,因此套接字结构体仅仅从网络层开始,即通常我们只定义了传输层的套接字socket和网络层的套接字socksocket 是用于负责对上给用户提供接口,并且和文件系统关联。而 sock负责向下对接内核网络协议栈。

  首先看传输层的socket结构体,这个结构体表征BSD套接字的通用特性。首先是状态state,用以表示连接情况。type是套接字类型,如SOCK_STREAMwq是等待队列,在后续文章中会说明。file是套接字对应的文件指针,毕竟一切皆文件,所以需要统一的文件系统。sock结构体的sk变量则为网络层的套接字,ops是协议相关的一系列套接字操作。

struct socket {socket_state		state;short			type;unsigned long		flags;struct socket_wq	*wq;struct file		*file;struct sock		*sk;const struct proto_ops	*ops;
};

  接着看看网络层,这一层即IP层,该结构体sock中包含了一个基本结构体sock_common,整体较为复杂,所以对于其重要变量进行了说明,以注释的形式在每个变量后进行分析。

struct sock {struct sock_common	__sk_common;	   // 网络层套接字通用结构体
......socket_lock_t		sk_lock;	       // 套接字同步锁atomic_t		sk_drops;	           // IP/UDP包丢包统计int			sk_rcvlowat;        	   // SO_RCVLOWAT标记位
......struct sk_buff_head	sk_receive_queue;	// 收到的数据包队列
......int			sk_rcvbuf;				  // 接收缓存大小
......union {struct socket_wq __rcu	*sk_wq;	    // 等待队列struct socket_wq	*sk_wq_raw;};
......int			sk_sndbuf;			       // 发送缓存大小/* ===== cache line for TX ===== */int			sk_wmem_queued;			   // 传输队列大小refcount_t		sk_wmem_alloc;		    // 已确认的传输字节数unsigned long		sk_tsq_flags;	    // TCP Small Queue标记位union {struct sk_buff	*sk_send_head;		// 发送队列对首struct rb_root	tcp_rtx_queue;		 };struct sk_buff_head	sk_write_queue;		 // 发送队列
......u32			sk_pacing_status; /* see enum sk_pacing 发包速率控制状态*/ long			sk_sndtimeo;		    // SO_SNDTIMEO 标记位struct timer_list	sk_timer;			// 套接字清空计时器__u32			sk_priority;		    // SO_PRIORITY 标记位
......unsigned long		sk_pacing_rate; /* bytes per second 发包速率*/unsigned long		sk_max_pacing_rate;  // 最大发包速率struct page_frag	sk_frag;		    // 缓存页帧
......struct proto		*sk_prot_creator;rwlock_t		sk_callback_lock;int			sk_err,					  // 上次错误sk_err_soft;			  // “软”错误:不会导致失败的错误u32			sk_ack_backlog;			   // ack队列长度u32			sk_max_ack_backlog;		   // 最大ack队列长度kuid_t			sk_uid;				  // user idstruct pid		*sk_peer_pid;		   // 套接字对应的peer的id
......long			sk_rcvtimeo;		  // 接收超时ktime_t			sk_stamp;			  // 时间戳
......struct socket		*sk_socket;		   // Identd协议报告IO信号void			*sk_user_data;		  // RPC层私有信息
......struct sock_cgroup_data	sk_cgrp_data;   // cgroup数据struct mem_cgroup	*sk_memcg;		   // 内存cgroup关联void			(*sk_state_change)(struct sock *sk);	// 状态变化回调函数void			(*sk_data_ready)(struct sock *sk);		// 数据处理回调函数void			(*sk_write_space)(struct sock *sk);		// 写空间可用回调函数void			(*sk_error_report)(struct sock *sk);    // 错误报告回调函数int			(*sk_backlog_rcv)(struct sock *sk, struct sk_buff *skb);	// 处理存储区回调函数
......void                    (*sk_destruct)(struct sock *sk);	// 析构回调函数struct sock_reuseport __rcu	*sk_reuseport_cb;			   // group容器重用回调函数
......
};

   sock_common是套接口在网络层的最小表示,即最基本的网络层套接字信息,具体内容分析见注释。

struct sock_common {/* skc_daddr and skc_rcv_saddr must be grouped on a 8 bytes aligned* address on 64bit arches : cf INET_MATCH()*/union {__addrpair	skc_addrpair;struct {__be32	skc_daddr;		// 外部/目的IPV4地址__be32	skc_rcv_saddr;	// 本地绑定IPV4地址};};union  {unsigned int	skc_hash;	// 根据协议查找表获取的哈希值__u16		skc_u16hashes[2]; // 2个16位哈希值,UDP专用};/* skc_dport && skc_num must be grouped as well */union {__portpair	skc_portpair;	// struct {__be16	skc_dport;	    // inet_dport占位符__u16	skc_num;	    // inet_num占位符};};unsigned short		skc_family;	      // 网络地址familyvolatile unsigned char	skc_state;    // 连接状态unsigned char		skc_reuse:4;      // SO_REUSEADDR 标记位unsigned char		skc_reuseport:1;  // SO_REUSEPORT 标记位unsigned char		skc_ipv6only:1;   // IPV6标记位unsigned char		skc_net_refcnt:1; // 该套接字网络名字空间内引用数int			skc_bound_dev_if;		 // 绑定设备索引union {struct hlist_node	skc_bind_node;     // 不同协议查找表组成的绑定哈希表struct hlist_node	skc_portaddr_node; // UDP/UDP-Lite protocol二级哈希表};struct proto		*skc_prot;			  // 协议回调函数,根据协议不同而不同
......union {									struct hlist_node	skc_node;		    // 不同协议查找表组成的主哈希表struct hlist_nulls_node skc_nulls_node;  // UDP/UDP-Lite protocol主哈希表};unsigned short		skc_tx_queue_mapping;    // 该连接的传输队列unsigned short		skc_rx_queue_mapping;    // 该连接的接受队列
......union {int		skc_incoming_cpu; // 多核下处理该套接字数据包的CPU编号u32		skc_rcv_wnd;	  // 接收窗口大小u32		skc_tw_rcv_nxt; /* struct tcp_timewait_sock  */};refcount_t		skc_refcnt;   // 套接字引用计数
......
};

三. 套接字缓冲区结构体

  套接字结构体用于表征一个网络连接对应的本地接口的网络信息,而sk_buff则是该网络连接对应的数据包的存储。sk_buff的详细介绍宜参考《Linux网络技术内幕》,专门有一章来描述该结构体。对于我们学习源码来说,最重要的是了解其重点成员变量以及其整体结构。

  其源码大致可以分为四部分:

  • 布局:方便搜索以及组织结构,主要是一个双向链表用于管理全部的sk_buff。每个sk_buff对应一个数据包,多个sk_buff以双向链表的形式组合而成。

img

​ 除此之外还有指向sock的指针,缓冲区数据块大小,缓冲区及数据边界tail,end,head,data,truesize

img

  • 通用字段:与特定内核无关的字段,主要包括时间戳tstamp,网络设备dev,源设备input_device,L2-L4层包头对应的mac_header, network_header, transport_header等。其头部组织结构如下所示

img

  • 功能专用:当编译防火墙(Netfilter) 以及QOS等时才会用到的特殊字段,在此暂时不做详细介绍
  • 管理函数:由内核提供的简单的管理工具函数,用于对sk_buff元素和元素列表进行操作,如数据预留及对齐函数skb_put(), skb_push(),skb_pull(),skb_reserve()

img

  再比如分配回收函数alloc_skb()dev_alloc_skb()

img

  释放内存函数kfree_skb()dev_kfree_skb()

img

  除此之外还有克隆,复制等函数,不做过多展开介绍。

  sk_buff的整体填充过程如下图所示:

img

  通过以上学习,对sk_buff应该有了较为全面系统的了解,其详细源码如下所示,对于重点部分已写明中文注释,其他参见英文注释。

struct sk_buff {union {struct {/* These two members must be first. 构成sk_buff链表*/struct sk_buff		*next;struct sk_buff		*prev;union {struct net_device	*dev;	//网络设备对应的结构体,很重要但是不是本文重点,所以不做展开/* Some protocols might use this space to store information,* while device pointer would be NULL.* UDP receive path is one user.*/unsigned long		dev_scratch;   // 对于某些不适用net_device的协议需要采用该字段存储信息,如UDP的接收路径};};struct rb_node		rbnode; /* used in netem, ip4 defrag, and tcp stack 将sk_buff以红黑树组织,在TCP中有用到*/struct list_head	list;   // sk_buff链表头指针};union {struct sock		*sk;       // 指向网络层套接字结构体int			ip_defrag_offset;};union {ktime_t		tstamp;    // 时间戳u64		skb_mstamp_ns; /* earliest departure time */};/* 存储私有信息* This is the control buffer. It is free to use for every* layer. Please put your private variables there. If you* want to keep them across layers you have to do a skb_clone()* first. This is owned by whoever has the skb queued ATM.*/char			cb[48] __aligned(8);union {struct {unsigned long	_skb_refdst;				   // 目标entryvoid		(*destructor)(struct sk_buff *skb);	// 析构函数};struct list_head	tcp_tsorted_anchor;			    // TCP发送队列(tp->tsorted_sent_queue)};
....unsigned int		len,	// 实际长度data_len;	    // 数据长度__u16			mac_len,    // mac层长度hdr_len;        // 可写头部长度/* Following fields are _not_ copied in __copy_skb_header()* Note that queue_mapping is here mostly to fill a hole.*/__u16			queue_mapping;   // 多队列设备的队列映射
....../* fields enclosed in headers_start/headers_end are copied* using a single memcpy() in __copy_skb_header()*//* private: */__u32			headers_start[0];	/* public: */
......__u8			__pkt_type_offset[0];__u8			pkt_type:3;__u8			ignore_df:1;__u8			nf_trace:1;__u8			ip_summed:2;__u8			ooo_okay:1;__u8			l4_hash:1;__u8			sw_hash:1;__u8			wifi_acked_valid:1;__u8			wifi_acked:1;__u8			no_fcs:1;/* Indicates the inner headers are valid in the skbuff. */__u8			encapsulation:1;__u8			encap_hdr_csum:1;__u8			csum_valid:1;
......__u8			__pkt_vlan_present_offset[0];__u8			vlan_present:1;__u8			csum_complete_sw:1;__u8			csum_level:2;__u8			csum_not_inet:1;__u8			dst_pending_confirm:1;
......__u8			ipvs_property:1;__u8			inner_protocol_type:1;__u8			remcsum_offload:1;
......union {__wsum		csum;struct {__u16	csum_start;__u16	csum_offset;};};__u32			priority;int			skb_iif;		// 接收到该数据包的网络接口的编号__u32			hash;__be16			vlan_proto;__u16			vlan_tci;
......union {__u32		mark;__u32		reserved_tailroom;};union {__be16		inner_protocol;__u8		inner_ipproto;};__u16			inner_transport_header;__u16			inner_network_header;__u16			inner_mac_header;__be16			protocol;__u16			transport_header;	// 传输层头部__u16			network_header;		// 网络层头部__u16			mac_header;			// mac层头部/* private: */__u32			headers_end[0];/* public: *//* These elements must be at the end, see alloc_skb() for details.  */sk_buff_data_t		tail;sk_buff_data_t		end;unsigned char		*head, *data;unsigned int		truesize;refcount_t		users;
......
};

四. 总结

  本文分析了网络协议栈中经典的套接字结构体,希望对大家有所帮助


欢迎关注本人公众号,公众号会更新一些不一样的内容,相信一定会有所收获。
在这里插入图片描述

这篇关于网络协议栈学习之socket, sock_common, sock, 和 sk_buff的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1138084

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件