linux初始化TCP服务失败,深入Linux系统追踪TCP初始化
struct socket {
socket_state state;shorttype;
unsignedlongflags;
struct socket_wq*wq;
structfile *file;
struct sock*sk;
const struct proto_ops*ops;
};
在socket中Proto_ops结构体定义如下:
structproto_ops {intfamily;struct module *owner;int (*release) (struct socket *sock);int (*bind) (struct socket *sock,struct sockaddr *myaddr,intsockaddr_len);int (*connect) (struct socket *sock,struct sockaddr *vaddr,int sockaddr_len, intflags);int (*socketpair)(struct socket *sock1,struct socket *sock2);int (*accept) (struct socket *sock,struct socket *newsock, int flags, boolkern);int (*getname) (struct socket *sock,struct sockaddr *addr,intpeer);
__poll_t (*poll) (struct file *file, struct socket *sock,struct poll_table_struct *wait);int (*ioctl) (struct socket *sock, unsigned intcmd,
unsignedlongarg);
#ifdef CONFIG_COMPATint (*compat_ioctl) (struct socket *sock, unsigned intcmd,
unsignedlongarg);#endif
int (*listen) (struct socket *sock, intlen);int (*shutdown) (struct socket *sock, intflags);int (*setsockopt)(struct socket *sock, intlevel,int optname, char __user *optval, unsigned intoptlen);int (*getsockopt)(struct socket *sock, intlevel,int optname, char __user *optval, int __user *optlen);
#ifdef CONFIG_COMPATint (*compat_setsockopt)(struct socket *sock, intlevel,int optname, char __user *optval, unsigned intoptlen);int (*compat_getsockopt)(struct socket *sock, intlevel,int optname, char __user *optval, int __user *optlen);#endif
int (*sendmsg) (struct socket *sock, struct msghdr *m,
size_t total_len);/*Notes for implementing recvmsg:
* ===============================
* msg->msg_namelen should get updated by the recvmsg handlers
* iff msg_name != NULL. It is by default 0 to prevent
* returning uninitialized memory to user space. The recvfrom
* handlers can assume that msg.msg_name is either NULL or has
* a minimum size of sizeof(struct sockaddr_storage).*/
int (*recvmsg) (struct socket *sock, struct msghdr *m,
size_t total_len,intflags);int (*mmap) (struct file *file, struct socket *sock,struct vm_area_struct *vma);
ssize_t (*sendpage) (struct socket *sock, struct page *page,int offset, size_t size, intflags);
ssize_t (*splice_read)(struct socket *sock, loff_t *ppos,struct pipe_inode_info *pipe, size_t len, unsigned intflags);int (*set_peek_off)(struct sock *sk, intval);int (*peek_len)(struct socket *sock);/*The following functions are called internally by kernel with
* sock lock already held.*/
int (*read_sock)(struct sock *sk, read_descriptor_t *desc,
sk_read_actor_t recv_actor);int (*sendpage_locked)(struct sock *sk, struct page *page,int offset, size_t size, intflags);int (*sendmsg_locked)(struct sock *sk, struct msghdr *msg,
size_t size);int (*set_rcvlowat)(struct sock *sk, intval);
};
在上次是实验中,已经追踪到bind函数之后,在进行安全检查之后,调用了ops结构体中的listen指针指向的方法
在经过单步调试之后,
ops结构体中间的各个函数指针指向了net/ipv4/af_inet文件,我们接下来去找找这个文件中的各个方法
对该文件中各个方法打上断点
按照断点运行,可得过程如下:
上图中断点运行步骤展示了服务器端的函数执行顺序如下:
Inet_create->inet_bind ->inet_listen ->accept
因此TCP初始化过程大部分都是在create函数中完成的:
static int inet_create(struct net *net, struct socket *sock, intprotocol,intkern)
{struct sock *sk;struct inet_protosw *answer;struct inet_sock *inet;struct proto *answer_prot;
unsignedcharanswer_flags;int try_loading_module = 0;interr;if (protocol < 0 || protocol >=IPPROTO_MAX)return -EINVAL;
sock->state =SS_UNCONNECTED;/*Look for the requested type/protocol pair.*/lookup_protocol:
err= -ESOCKTNOSUPPORT;
rcu_read_lock();
list_for_each_entry_rcu(answer,&inetsw[sock->type], list) {
err= 0;/*Check the non-wild match.*/
if (protocol == answer->protocol) {if (protocol !=IPPROTO_IP)break;
}else{/*Check for the two wild cases.*/
if (IPPROTO_IP ==protocol) {
protocol= answer->protocol;break;
}if (IPPROTO_IP == answer->protocol)break;
}
err= -EPROTONOSUPPORT;
}if(unlikely(err)) {if (try_loading_module < 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)*/
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)*/
elserequest_module("net-pf-%d-proto-%d",
PF_INET, protocol);gotolookup_protocol;
}else
gotoout_rcu_unlock;
}
err= -EPERM;if (sock->type == SOCK_RAW && !kern &&
!ns_capable(net->user_ns, CAP_NET_RAW))gotoout_rcu_unlock;
sock->ops = answer->ops;
answer_prot= answer->prot;
answer_flags= answer->flags;
rcu_read_unlock();
WARN_ON(!answer_prot->slab);
err= -ENOBUFS;
sk=sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot, kern);if (!sk)goto out;
err= 0;if (INET_PROTOSW_REUSE &answer_flags)
sk->sk_reuse =SK_CAN_REUSE;
inet=inet_sk(sk);
inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;
inet->nodefrag = 0;if (SOCK_RAW == sock->type) {
inet->inet_num =protocol;if (IPPROTO_RAW ==protocol)
inet->hdrincl = 1;
}if (net->ipv4.sysctl_ip_no_pmtu_disc)
inet->pmtudisc =IP_PMTUDISC_DONT;elseinet->pmtudisc =IP_PMTUDISC_WANT;
inet->inet_id = 0;
sock_init_data(sock, sk);
sk->sk_destruct =inet_sock_destruct;
sk->sk_protocol =protocol;
sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
inet->uc_ttl = -1;
inet->mc_loop = 1;
inet->mc_ttl = 1;
inet->mc_all = 1;
inet->mc_index = 0;
inet->mc_list =NULL;
inet->rcv_tos = 0;
sk_refcnt_debug_inc(sk);if (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);/*Add to protocol hash chains.*/err= sk->sk_prot->hash(sk);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);if(err) {
sk_common_release(sk);goto out;
}
}out:returnerr;
out_rcu_unlock:
rcu_read_unlock();goto out;
}
那么Linux的 TCP协议中又是通过什么来切换状态的呢?
在逐步跟踪之后,我们找到了如下状态切换函数
tcp_set_state(sk, TCP_SYN_SENT);
err=inet_hash_connect(tcp_death_row, sk);if(err)gotofailure;
sk_set_txhash(sk);
rt=ip_route_newports(fl4, rt, orig_sport, orig_dport,
inet->inet_sport, inet->inet_dport, sk);if(IS_ERR(rt)) {
err=PTR_ERR(rt);
rt=NULL;goto failure;
tcp_set_state(sk, TCP_SYN_SENT)把套接字的状态从CLOSE切换到SYN_SENT,并进一步调用了 tcp_connect(sk)来实际构造SYN并发送出去。然后调用inet_hash_connect(&tcp_death_row, sk)和
ip_route_newports(fl4, rt, orig_sport, orig_dport, inet->inet_sport, inet->inet_dport, sk),为套接字绑定一个端口,并记录在TCP的哈希表中。
总的来说,整个过程的函数调用关系是这样的:
tcp_v4_connect -> tcp_connect_init -> tcp_transmit_skb -> icsk->icsk_af_ops->send_check (tcp_v4_send_check)-> icsk->icsk_af_ops->queue_xmit (ip_queue_xmit)-> inet_csk_reset_xmit_timer
总体来说TCP的初始化过程大概就是如上文所示了,最后放一张TCP状态转换图
linux初始化TCP服务失败,深入Linux系统追踪TCP初始化相关推荐
- linux启动网络服务的命令,linux重启服务命令
linux重启服务命令 重启:service 服务名 restart 或systemctl restart 服务名 service和systemctl 1.service命令 service命令其实是 ...
- linux操作系统的cron服务用于管理,Linux的系统管理
Linux的系统管理 一.Linux的系统启动的过程 1.Linux操作系统的启动的过程一般包括以下几个阶段: a) 主机启动并进行硬盘自检后,读取硬盘mbr中的启动引导器并进行加载. b) 启动引导 ...
- linux停止network服务命令是,Linux系统服务启动和停止
systemd 是在Linux上运行服务的新方式. systemd 有一个被替代的 sysvinit . systemd 为Linux带来更快的启动时间,现在是管理Linux服务的标准方式.虽然稳定, ...
- linux关闭telnet服务的命令,linux如何开启telnet服务
linux系统你只到吗,他可以开启telnet服务,那linux如何开启telnet服务的呢?下面是学习啦小编收集整理的linux如何开启telnet服务,希望对大家有帮助~~ linux开启teln ...
- Linux查hudi服务的进程,Linux查看非root运行的进程
Linux查看非root运行的进程 youhaidong@youhaidong-ThinkPad-Edge-E545:~$ ps -U root -u root -N PID TTY TIME CMD ...
- linux mysql端口启动失败怎么办,Linux下apache mysql等服务修改默认端口后无法正常启动解决办法...
Linux下apache mysql等服务修改默认端口后无法正常启动解决办法 linux下 apache 等服务修改默认端口后无法正常启动解决办法 服务器上装了两个webserver,一个是nginx ...
- linux有关网络服务的接口,linux系统有关网络服务接口定义是哪个?
浮云间 (1)网络接口的命名 这里并不存在一定的命名规范,但网络接口名字的定义一般都是要有意义的.例如: eth0: ethernet的简写,一般用于以太网接口. wifi0:wifi是无线局域网,因 ...
- Linux添加http服务失败,Apache Web服务器配置http2各种失败
我在 Debian Stretch 最新稳定版 9.6 的系统下通过 Apt 源安装了 Apache 2.4.25 ,想配置 http2 协议支持,首先 a2enmod 了 http2 模块,确保已开 ...
- linux d path失败返回,linux – elasticsearch systemd服务失败
我刚刚在linux 14.10上切换到systemd,现在我的弹性搜索服务没有正常启动 elasticsearch.service loaded failed failed systemctl状态为我 ...
最新文章
- 项目需求 | PC VR头戴显示器实时三维坐标(项目经费20万)
- java与fabric区块链--fabric-java-jdk部署搭建--(1)
- MapReduce示例——WordCount(统计单词)
- [Array]Pascal's Triangle II
- javaScript变量、作用域链
- redis设置开机自启动
- MAUI安卓子系统调试方法(附安装教程)
- webgl坐标转换_OpenGL/WebGL顶点坐标变换过程简介
- jQuery 利用 $.getJson() 实现跨域
- 如何优化网站页面提高网页的加载速度
- Ubuntu 完全卸载 Apache2
- input数字开头不能为0_Python新手上车5:数字和注释
- [Node] 基础知识
- 树为什么能长这么高?
- Event的三个阶段:CAPTURING_PHASE,AT_TARGET,BUBBLING_PHASE
- cad中填充的剖面线不能被修剪_为什么CAD中填充无法修剪?
- Switch新机发布引全球吐槽
- GDR(Gradual Decoder Refresh)帧
- Server 2008改成个人习惯的配置
- python --获取内网IP地址
热门文章
- linux命令的-和--参数问题
- 绝对不能错过!计算机视觉Polygon Mesh Processing读书笔记——3
- 推荐系统多兴趣召回最新进展
- 人类社会发明的第一台计算机是什么,第二周第一课概述计算机
- 2021年用独立站铁赚的四种模式
- 课外题:需要排序的子数组
- TensorFlow2.0:梯度计算
- 花书+吴恩达深度学习(十七)序列模型之长短期记忆 LSTM 和门控循环单元 GRU
- Iptables(2) - iptables命令的基本用法
- unity如何检测内存泄漏_如何排查Java内存泄漏?看懂这一篇就够用了