tcp/ip协议栈-tcp层(1)
来源:互联网 发布:java timestamp 比较 编辑:程序博客网 时间:2024/06/05 07:47
0x01 缘由
上章节简单学习了内核网络栈路由的过程,这章节继续tcp三次握手过程。
0x02 调式环境
在guest虚拟机中跑一个server端,然后其他机器跑个客户端进行分析。
0x03 源码解析
主要分几部分来:
1.收到客户端syn包处理流程
2.服务端构造syn + ack相应客户端
3.客户端发送ack给服务端连接建立
1、2过程调用图
过程1的源码分析:
1.1 tcp_v4_rcv()函数解析
/*
* 在 tcp_input.c 中调用 */int tcp_v4_rcv(struct sk_buff *skb){ const struct iphdr *iph; //ip头部结构 struct tcphdr *th; //tcp头部结构 struct sock *sk; //对应的应用层头部结构 int ret; struct net *net = dev_net(skb->dev); //网络设备相关 if (skb->pkt_type != PACKET_HOST) //不是指向本机,抛弃 goto discard_it; /* 省略部分代码,下面是对tcp头部进行校验,数据偏移 */ th = tcp_hdr(skb); if (th->doff < sizeof(struct tcphdr) / 4) goto bad_packet; /* 省略部分代码 */ th = tcp_hdr(skb); iph = ip_hdr(skb); TCP_SKB_CB(skb)->seq = ntohl(th->seq); //tcp序号 TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + skb->len - th->doff * 4); //tcp结束序号 开始序号 + 数据长度 TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); //ack序号 TCP_SKB_CB(skb)->when = 0; //窗口大小 TCP_SKB_CB(skb)->flags = iph->tos; //标识 TCP_SKB_CB(skb)->sacked = 0; //拥塞处理 /* 查询是否已经存在对应的sock结构. TCP 通过搜索 tcp_hashinfo 查找已经建立的 sock{},不过出于性能的考虑, 凡是出于 TCP_ESTABLISHED 状态的 sock{}放入了 tcp_hashinfo->ehash 表 中,而处于 TCP_LISTEN 状态的 sock{}放入了 tcp_hashinfo->lhash 表中。 */ sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest); if (!sk) goto no_tcp_socket;process: //tcp目前的状态 if (sk->sk_state == TCP_TIME_WAIT) goto do_time_wait; //过滤相关 if (sk_filter(sk, skb)) goto discard_and_relse; skb->dev = NULL; bh_lock_sock_nested(sk); ret = 0; if (!sock_owned_by_user(sk)) {#ifdef CONFIG_NET_DMA struct tcp_sock *tp = tcp_sk(sk); if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) tp->ucopy.dma_chan = dma_find_channel(DMA_MEMCPY); if (tp->ucopy.dma_chan) ret = tcp_v4_do_rcv(sk, skb); else#endif { if (!tcp_prequeue(sk, skb)) ret = tcp_v4_do_rcv(sk, skb); //进入下一个处理函数; } } else sk_add_backlog(sk, skb); bh_unlock_sock(sk); sock_put(sk); return ret; /* 省略部分代码*/}
1.2 tcp_v4_do_rcv ()函数解析
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb){ struct sock *rsk; /* 如果连接到了建立的状态,则走快速路径。*/ if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ TCP_CHECK_TIMER(sk); //后面讲解 if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) { rsk = sk; goto reset; } TCP_CHECK_TIMER(sk); return 0; } //检查数据长度 if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb)) goto csum_err; //处理监听状态 if (sk->sk_state == TCP_LISTEN) { //处理相关请求 struct sock *nsk = tcp_v4_hnd_req(sk, skb); if (!nsk) goto discard; if (nsk != sk) { if (tcp_child_process(sk, nsk, skb)) { rsk = nsk; goto reset; } return 0; } } TCP_CHECK_TIMER(sk); //tcp状态机处理 if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) { rsk = sk; goto reset; } TCP_CHECK_TIMER(sk); return 0;reset: //发送rst包 tcp_v4_send_reset(rsk, skb); /* 部分代码省略 */}
1.3 tcp_rcv_state_process()函数解析
不管是客户端还是服务器端, 当连接不是 TCP_ESTABLISHED 状态的时候,就会调用 tcp_rcv_state_process 函数, 这就是专门处理 3 次握手协议的状态机处理函数。 对于服务器端 TCP
协议栈, 当收到一个 connect 请求时,会去调用此函数中出现的 tp->af_specific->conn_request(sk, skb),也就是 tcp_v4_conn_request,如果不记得如何指向此函数,请参考 TCP 的 socket 初始化:
下面仅讲解单步调式过程中一些关键函数:
int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, struct tcphdr *th, unsigned len){ struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); int queued = 0; int res; tp->rx_opt.saw_tstamp = 0; switch (sk->sk_state) { case TCP_CLOSE: goto discard; case TCP_LISTEN: //服务端处理监听状态 if (th->ack)//检查ack标识 return 1; if (th->rst)//检查rst标识,rst标识导致数据包丢弃 goto discard; if (th->syn) { //处理syn过程,下一个函数查看这个 if (icsk->icsk_af_ops->conn_request(sk, skb) < 0) return 1; kfree_skb(skb); return 0; } goto discard; case TCP_SYN_SENT: queued = tcp_rcv_synsent_state_process(sk, skb, th, len); if (queued >= 0) return queued; /* Do step6 onward by hand. */ tcp_urg(sk, skb, th); __kfree_skb(skb); tcp_data_snd_check(sk); return 0; } res = tcp_validate_incoming(sk, skb, th, 0); if (res <= 0) return -res; /* 省略大部分代码,暂不关注 */ } return 0;}
1.4 tcp_rcv_state_process()函数解析
/* * 处理一个输入包SYN_RECV sockets代表一个请求sock。 */struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, struct request_sock *req, struct request_sock **prev){ /* 检查为纯syn重传 */ if (TCP_SKB_CB(skb)->seq == tcp_rsk(req)->rcv_isn && flg == TCP_FLAG_SYN && !paws_reject) { req->rsk_ops->rtx_syn_ack(sk, req); return NULL; } /* 省略相关代码 */}
1.5 tcp_v4_send_synack ()函数解析
static int tcp_v4_send_synack(struct sock *sk, struct request_sock *req){ return __tcp_v4_send_synack(sk, req, NULL);}/* * 在接收到一个SYN包时发送一个SYN-ACK * 尽在request_sock 中 */static int __tcp_v4_send_synack(struct sock *sk, struct request_sock *req, struct dst_entry *dst){ const struct inet_request_sock *ireq = inet_rsk(req); int err = -1; struct sk_buff * skb; /* First, grab a route. */ if (!dst && (dst = inet_csk_route_req(sk, req)) == NULL) return -1; skb = tcp_make_synack(sk, dst, req); //构造syn + ack包 if (skb) { struct tcphdr *th = tcp_hdr(skb); //校验和 th->check = tcp_v4_check(skb->len, ireq->loc_addr, ireq->rmt_addr, csum_partial(th, skb->len, skb->csum)); //构造包并发送 err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr, ireq->rmt_addr, ireq->opt); err = net_xmit_eval(err); } dst_release(dst); return err;}
0x04 总结
此处简单学了服务端收到一个syn收的处理过程,下一步继续学习三次握手的过程。(学习过程,大神勿喷,多指点)
阅读全文
0 0
- tcp/ip协议栈-tcp层(1)
- TCP/IP协议栈传输层协议(TCP/UDP)
- TCP /IP 协议-(传输层)TCP 协议
- TCP/IP四层协议栈
- TCP/IP 协议栈和主要层
- 网络协议层--<TCP/IP详解1>
- TCP/IP协议(二)---网络层
- TCP/IP协议(三)---传输层
- TCP/IP协议(四)---应用层
- TCP/IP协议学习(1)—数据链路层
- TCP/IP协议(七层、四层协议)
- TCP/IP应用层协议
- TCP/IP 七层协议
- TCP/IP四层协议
- TCP/IP 四层协议
- TCP/IP 5层协议
- OSI7层模型与TCP/IP协议栈4层
- OSI7层模型与TCP/IP协议栈4层
- mfc/vc slider control控件 鼠标左键点击后直接滑到点击的位置
- IIS7错误:不能在此路径中使用此配置节。如果在父级别上锁定了该节,便会出现这种情况。锁定是默认设置的(overrideModeDefault="Deny")......
- CAD转换PDF技巧分享 手把手教你快速转换CAD文件
- Swift正式选用超级账本Fabric技术,节约30%跨境支付成本
- 简单PullToRefreshListView+ViewPager无限轮播
- tcp/ip协议栈-tcp层(1)
- apache 部署https报错
- Ubuntu进入root权限和退出(su,sudo)
- Windows下Maven的安装与配置
- ModifyStyle
- 图解SparkContext创建过程
- 适配器模式(Adapter)
- 2.Button按钮实例:普通按钮和图片按钮
- 最新版spark-2.2.0安装教程