/*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;
}
linux内核代码-注释详解:inet_create
于 2023-07-06 14:51:46 首次发布