linux内核代码-注释详解:inet_create

/*linux-5.10.x\net\ipv4\af_inet.c
 * 主要作用是分配和初始化一个新的网络套接字,并将其添加到系统的网络套接字表中。总结:
	套接字创建:					首先会调用 sock_create() 函数创建一个新的套接字实例,该函数返回一个指向 struct socket 结构体的指针,表示创建的套接字
	套接字类型和协议设置:			  	根据指定的协议类型,函数会设置套接字的类型和协议族。常见的协议族包括 IPv4(AF_INET)和 IPv6(AF_INET6)
	套接字表操作:					创建的套接字添加到套接字表中。调用 sk_alloc() 函数为套接字分配一个  sock 结构体,然后将其添加到当前网络命名空间的套接字表中
	协议处理:					调用适当的协议处理函数来处理特定协议类型的操作。
									例如 IPv4 协议,会调用 inet_sock_alloc() 和 inet_hashinfo_init() 来分配套接字并初始化相关的 IP 信息
	返回结果:					返回创建的套接字实例(struct socket 结构体的指针)作为结果
 */
static int inet_create(struct net *net, struct socket *sock, int protocol,
		       int kern)
{
	struct sock *sk;
	struct inet_protosw *answer;		//表示IPv4协议的处理函数
	struct inet_sock *inet;				//表示IPv4协议的socket私有数据结构
	struct proto *answer_prot;			//表示IPv4协议的协议控制块
	unsigned char answer_flags;			//表示IPv4协议的标志位
	int try_loading_module = 0;			//表示尝试加载模块的次数
	int err;

	if (protocol < 0 || protocol >= IPPROTO_MAX)					//检查指定的协议是否合法
		return -EINVAL;

	sock->state = SS_UNCONNECTED;									//将socket的状态设置为未连接状态

	/* Look for the requested type/protocol pair. */
lookup_protocol:													/*用于在查找协议时跳转到指定位置*/
	err = -ESOCKTNOSUPPORT;											//表示不支持指定的协议类型
	rcu_read_lock();
	list_for_each_entry_rcu(answer, &inetsw[sock->type], list) {	//遍历inetsw数组中指定类型的协议处理函数。inetsw数组存储了不同类型的协议处理函数的链表头

		err = 0;
		/* Check the non-wild match. */
		if (protocol == answer->protocol) {							//检查指定的协议是否与当前遍历到的协议处理函数的协议相匹配
			if (protocol != IPPROTO_IP)								//如匹配,检查指定的协议是否为IPv4协议
				break;												//匹配但不是IPv4协议,跳出循环,结束遍历
		} else {
			/* Check for the two wild cases. */
			if (IPPROTO_IP == protocol) {							//不匹配,但指定的协议为IPv4协议
				protocol = answer->protocol;						//将协议设置为当前遍历到的协议处理函数的协议
				break;												//跳出循环,结束遍历
			}
			if (IPPROTO_IP == answer->protocol)						//不匹配,但当前遍历到的协议处理函数的协议是IPv4协议
				break;
		}
		err = -EPROTONOSUPPORT;										//表示不支持指定的协议
	}

	if (unlikely(err)) {											//如果错误码不为零,说明没有找到匹配的协议处理函数
		if (try_loading_module < 2) {								//检查尝试加载模块的次数是否小于2
			rcu_read_unlock();
			/*
			 * Be more specific, e.g. net-pf-2-proto-132-type-1
			 * (net-pf-PF_INET-proto-IPPROTO_SCTP-type-SOCK_STREAM)

			 * "net-pf-2-proto-132-type-1" 表示请求加载的内核模块名称。解释它们的含义:
				"net-pf-2": "net-pf" 表示网络协议族(Network Protocol Family),数字 2 表示 AF_INET,代表 IPv4 协议族
				"proto-132": "proto" 表示协议(Protocol),数字 132 表示 IPPROTO_SCTP,即 Stream Control Transmission Protocol(SCTP)
				"type-1": "type" 表示套接字类型(Socket Type),数字 1 表示 SOCK_STREAM,表示流式套接字
			 * 因此,"net-pf-2-proto-132-type-1" 表示加载用于支持 IPv4 的 SCTP 流式套接字的内核模块
			 */
			if (++try_loading_module == 1)							//第一次尝试加载模块
				request_module("net-pf-%d-proto-%d-type-%d",		//请求加载指定的内核模块。这个函数会触发内核模块加载机制,尝试加载指定的模块
					       PF_INET, protocol, sock->type);
			/*
			 * Fall back to generic, e.g. net-pf-2-proto-132
			 * (net-pf-PF_INET-proto-IPPROTO_SCTP)
			 
			 * "net-pf-2-proto-132" 表示请求加载的内核模块名称。解释它们的含义:
				 "net-pf-2": "net-pf" 表示网络协议族(Network Protocol Family),数字 2 表示 AF_INET,代表 IPv4 协议族
				 "proto-132": "proto" 表示协议(Protocol),数字 132 表示 IPPROTO_SCTP,即 Stream Control Transmission Protocol(SCTP)
			 * 因此,"net-pf-2-proto-132" 表示请求加载用于支持 IPv4 的 SCTP 协议的内核模块
			 */
			else													//已经尝试过加载模块
				request_module("net-pf-%d-proto-%d",				//可以假设该协议族和协议的处理函数已经被成功加载到内核中了,参数只包含了协议族和协议的信息,不再包含套接字类型的信息
					       PF_INET, protocol);
			goto lookup_protocol;									//重新进行协议的查找
		} else						//尝试加载模块的次数不小于2
			goto out_rcu_unlock;
	}

	err = -EPERM;
	if (sock->type == SOCK_RAW && !kern &&
	    !ns_capable(net->user_ns, CAP_NET_RAW))						//如果套接字类型为原始套接字,且不在内核态运行,且当前用户使用原始套接字的权限
		goto out_rcu_unlock;										//跳转到out_rcu_unlock标签处进行解锁操作后的处理

	sock->ops = answer->ops;										//获取操作、协议和标志,并将其分别存储到套接字的ops、answer_prot和answer_flags字段中
	answer_prot = answer->prot;
	answer_flags = answer->flags;
	rcu_read_unlock();

	WARN_ON(!answer_prot->slab);									//如果协议的内存高速缓存对象指针字段slab为空,发出警告

	err = -ENOBUFS;
	sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot, kern);		//接收网络命名空间、协议族、内存分配标志、协议和内核态参数,并返回一个指向新套接字结构的指针
	if (!sk)														//如果分配失败,则err为-ENOBUFS,并跳转到out标签处进行处理
		goto out;

	err = 0;
	if (INET_PROTOSW_REUSE & answer_flags)							// 如果answer_flags中包含INET_PROTOSW_REUSE标志
		sk->sk_reuse = SK_CAN_REUSE;								// 则将sk的sk_reuse标记设置为SK_CAN_REUSE,表示允许端口重用

	inet = inet_sk(sk);
	inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0; 		// 如果answer_flags中包含INET_PROTOSW_ICSK标志,则将inet->is_icsk设置为true,表示套接字是inet_connection_sock(TCP套接字)

	inet->nodefrag = 0;												// 将inet->nodefrag设置为0,表示禁用IP分片

	if (SOCK_RAW == sock->type) {									// 如果套接字类型是RAW套接字
		inet->inet_num = protocol;									// 则将inet->inet_num设置为protocol,表示协议类型
		if (IPPROTO_RAW == protocol)								// 如果协议类型是IPPROTO_RAW(原始IP协议)
			inet->hdrincl = 1;										// 则将inet->hdrincl设置为1,表示包括IP头部
	}

	if (READ_ONCE(net->ipv4.sysctl_ip_no_pmtu_disc))				// 如果sysctl_ip_no_pmtu_disc为true,
		inet->pmtudisc = IP_PMTUDISC_DONT;							// 则设置inet->pmtudisc为IP_PMTUDISC_DONT,表示禁用路径MTU发现
	else
		inet->pmtudisc = IP_PMTUDISC_WANT;							// 否则设置inet->pmtudisc为IP_PMTUDISC_WANT,表示启用路径MTU发现

	inet->inet_id = 0;												// 表示初始化时没有分配ID

	sock_init_data(sock, sk);										// 初始化套接字的数据结构

	sk->sk_destruct	   = inet_sock_destruct;						// 设置套接字的析构函数为inet_sock_destruct
	sk->sk_protocol	   = protocol;									// 将套接字的协议设置为protocol
	sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;					// 将套接字的接收队列回调函数设置为协议的backlog_rcv函数

	inet->uc_ttl	= -1;											// 表示使用系统默认的TTL值
	inet->mc_loop	= 1;											// 将组播环路标志设置为1,表示启用组播环路
	inet->mc_ttl	= 1;											// 将组播TTL设置为1,表示组播消息只能在本地网络传输
	inet->mc_all	= 1;											// 将多播全部主机标志设置为1,表示接收所有组播消息
	inet->mc_index	= 0;											// 将组播索引设置为0,表示未指定任何组播接口
	inet->mc_list	= NULL;											// 将组播成员列表设置为NULL,表示没有加入任何组播组
	inet->rcv_tos	= 0;											// 将接收端TOS(Type of Service)值设置为0,表示接收所有类型的IP包

	sk_refcnt_debug_inc(sk);										// 增加套接字的引用计数,用于调试和内存管理

	if (inet->inet_num) {											// 如果inet->inet_num存在,
		/* It assumes that any protocol which allows
		 * the user to assign a number at socket
		 * creation time automatically
		 * shares.
		 */
		inet->inet_sport = htons(inet->inet_num);					// 则将inet->inet_num转换为网络字节序并赋值给inet->inet_sport,表示端口号
		/* Add to protocol hash chains. */
		err = sk->sk_prot->hash(sk);								// 调用套接字协议的hash函数对套接字进行哈希处理
		if (err) {
			sk_common_release(sk);									// 如果哈希处理出错,则释放套接字的共享数据结构
			goto out;
		}
	}

	if (sk->sk_prot->init) {										// 如果套接字协议有初始化函数,
		err = sk->sk_prot->init(sk);								// 则调用初始化函数对套接字进行初始化
		if (err) {
			sk_common_release(sk);									// 如果初始化出错,则释放套接字的共享数据结构
			goto out;
		}
	}

	if (!kern) {													// 如果不是内核空间的套接字,
		err = BPF_CGROUP_RUN_PROG_INET_SOCK(sk);					// 根据是否启用了Cgroup BPF功能,选择性地运行指定类型的BPF程序,并返回执行结果
		if (err) {													
			sk_common_release(sk);									// 如果处理出错,则释放套接字的共享数据结构
			goto out;
		}
	}
out:
	return err;
out_rcu_unlock:
	rcu_read_unlock();
	goto out;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值