TCP输入 之 tcp_v4_rcv
tcp_v4_rcv函数为TCP的总入口,数据包从IP层传递上来,进入该函数;其协议操作函数结构如下所示,其中handler即为IP层向TCP传递数据包的回调函数,设置为tcp_v4_rcv;
static struct net_protocol tcp_protocol = {
.early_demux = tcp_v4_early_demux,
.early_demux_handler = tcp_v4_early_demux,
.handler = tcp_v4_rcv,
.err_handler = tcp_v4_err,
.no_policy = ,
.netns_ok = ,
.icmp_strict_tag_validation = ,
};
在IP层处理本地数据包时,会获取到上述结构的实例,并且调用实例的handler回调,也就是调用了tcp_v4_rcv;
static int ip_local_deliver_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
/* 获取协议处理结构 */
ipprot = rcu_dereference(inet_protos[protocol]);
if (ipprot) {
int ret; /* 协议上层收包处理函数 */
ret = ipprot->handler(skb);
if (ret < ) {
protocol = -ret;
goto resubmit;
}
__IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
}
}
tcp_v4_rcv函数只要做以下几个工作:(1) 设置TCP_CB (2) 查找控制块 (3)根据控制块状态做不同处理,包括TCP_TIME_WAIT状态处理,TCP_NEW_SYN_RECV状态处理,TCP_LISTEN状态处理 (4) 接收TCP段;
int tcp_v4_rcv(struct sk_buff *skb)
{
struct net *net = dev_net(skb->dev);
const struct iphdr *iph;
const struct tcphdr *th;
bool refcounted;
struct sock *sk;
int ret; /* 非本机 */
if (skb->pkt_type != PACKET_HOST)
goto discard_it; /* Count it even if it's bad */
__TCP_INC_STATS(net, TCP_MIB_INSEGS); /* 检查头部数据,若不满足,则拷贝分片 */
if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
goto discard_it; /* 取tcp头 */
th = (const struct tcphdr *)skb->data; /* 长度过小 */
if (unlikely(th->doff < sizeof(struct tcphdr) / ))
goto bad_packet; /* 检查头部数据,若不满足,则拷贝分片 */
if (!pskb_may_pull(skb, th->doff * ))
goto discard_it; /* An explanation is required here, I think.
* Packet length and doff are validated by header prediction,
* provided case of th->doff==0 is eliminated.
* So, we defer the checks. */ if (skb_checksum_init(skb, IPPROTO_TCP, inet_compute_pseudo))
goto csum_error; /* 取tcp头 */
th = (const struct tcphdr *)skb->data;
/* 取ip头 */
iph = ip_hdr(skb);
/* This is tricky : We move IPCB at its correct location into TCP_SKB_CB()
* barrier() makes sure compiler wont play fool^Waliasing games.
*/
/* 移动ipcb */
memmove(&TCP_SKB_CB(skb)->header.h4, IPCB(skb),
sizeof(struct inet_skb_parm));
barrier(); /* 获取开始序号*/
TCP_SKB_CB(skb)->seq = ntohl(th->seq);
/* 获取结束序号,syn与fin各占1 */
TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
skb->len - th->doff * );
/* 获取确认序号 */
TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
/* 获取标记字节,tcp首部第14个字节 */
TCP_SKB_CB(skb)->tcp_flags = tcp_flag_byte(th);
TCP_SKB_CB(skb)->tcp_tw_isn = ;
/* 获取ip头的服务字段 */
TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph);
TCP_SKB_CB(skb)->sacked = ; lookup:
/* 查找控制块 */
sk = __inet_lookup_skb(&tcp_hashinfo, skb, __tcp_hdrlen(th), th->source,
th->dest, &refcounted);
if (!sk)
goto no_tcp_socket; process: /* TIME_WAIT转过去处理 */
if (sk->sk_state == TCP_TIME_WAIT)
goto do_time_wait; /* TCP_NEW_SYN_RECV状态处理 */
if (sk->sk_state == TCP_NEW_SYN_RECV) {
struct request_sock *req = inet_reqsk(sk);
struct sock *nsk; /* 获取控制块 */
sk = req->rsk_listener;
if (unlikely(tcp_v4_inbound_md5_hash(sk, skb))) {
sk_drops_add(sk, skb);
reqsk_put(req);
goto discard_it;
} /* 不是listen状态 */
if (unlikely(sk->sk_state != TCP_LISTEN)) {
/* 从连接队列移除控制块 */
inet_csk_reqsk_queue_drop_and_put(sk, req); /* 根据skb参数重新查找控制块 */
goto lookup;
}
/* We own a reference on the listener, increase it again
* as we might lose it too soon.
*/
sock_hold(sk);
refcounted = true; /* 处理第三次握手ack,成功返回新控制块 */
nsk = tcp_check_req(sk, skb, req, false); /* 失败 */
if (!nsk) {
reqsk_put(req);
goto discard_and_relse;
} /* 未新建控制块,进一步处理 */
if (nsk == sk) {
reqsk_put(req);
}
/* 有新建控制块,进行初始化等 */
else if (tcp_child_process(sk, nsk, skb)) {
/* 失败发送rst */
tcp_v4_send_reset(nsk, skb);
goto discard_and_relse;
} else {
sock_put(sk);
return ;
}
} /* TIME_WAIT和TCP_NEW_SYN_RECV以外的状态 */ /* ttl错误 */
if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) {
__NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP);
goto discard_and_relse;
} if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
goto discard_and_relse; if (tcp_v4_inbound_md5_hash(sk, skb))
goto discard_and_relse; /* 初始化nf成员 */
nf_reset(skb); /* tcp过滤 */
if (tcp_filter(sk, skb))
goto discard_and_relse; /* 取tcp和ip头 */
th = (const struct tcphdr *)skb->data;
iph = ip_hdr(skb); /* 清空设备 */
skb->dev = NULL; /* LISTEN状态处理 */
if (sk->sk_state == TCP_LISTEN) {
ret = tcp_v4_do_rcv(sk, skb);
goto put_and_return;
} /* TIME_WAIT和TCP_NEW_SYN_RECV和LISTEN以外的状态 */ /* 记录cpu */
sk_incoming_cpu_update(sk); bh_lock_sock_nested(sk); /* 分段统计 */
tcp_segs_in(tcp_sk(sk), skb);
ret = ; /* 未被用户锁定 */
if (!sock_owned_by_user(sk)) {
/* 未能加入到prequeue */
if (!tcp_prequeue(sk, skb))
/* 进入tcpv4处理 */
ret = tcp_v4_do_rcv(sk, skb);
}
/* 已经被用户锁定,加入到backlog */
else if (tcp_add_backlog(sk, skb)) {
goto discard_and_relse;
}
bh_unlock_sock(sk); put_and_return:
/* 减少引用计数 */
if (refcounted)
sock_put(sk); return ret; no_tcp_socket:
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto discard_it; if (tcp_checksum_complete(skb)) {
csum_error:
__TCP_INC_STATS(net, TCP_MIB_CSUMERRORS);
bad_packet:
__TCP_INC_STATS(net, TCP_MIB_INERRS);
} else {
/* 发送rst */
tcp_v4_send_reset(NULL, skb);
} discard_it:
/* Discard frame. */
kfree_skb(skb);
return ; discard_and_relse:
sk_drops_add(sk, skb);
if (refcounted)
sock_put(sk);
goto discard_it; do_time_wait:
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
inet_twsk_put(inet_twsk(sk));
goto discard_it;
} /* 校验和错误 */
if (tcp_checksum_complete(skb)) {
inet_twsk_put(inet_twsk(sk));
goto csum_error;
} /* TIME_WAIT入包处理 */
switch (tcp_timewait_state_process(inet_twsk(sk), skb, th)) { /* 收到syn */
case TCP_TW_SYN: {
/* 查找监听控制块 */
struct sock *sk2 = inet_lookup_listener(dev_net(skb->dev),
&tcp_hashinfo, skb,
__tcp_hdrlen(th),
iph->saddr, th->source,
iph->daddr, th->dest,
inet_iif(skb)); /* 找到 */
if (sk2) {
/* 删除tw控制块 */
inet_twsk_deschedule_put(inet_twsk(sk));
/* 记录监听控制块 */
sk = sk2;
refcounted = false; /* 进行新请求的处理 */
goto process;
}
/* Fall through to ACK */
} /* 发送ack */
case TCP_TW_ACK:
tcp_v4_timewait_ack(sk, skb);
break;
/* 发送rst */
case TCP_TW_RST:
tcp_v4_send_reset(sk, skb);
/* 删除tw控制块 */
inet_twsk_deschedule_put(inet_twsk(sk));
goto discard_it;
/* 成功*/
case TCP_TW_SUCCESS:;
}
goto discard_it;
}
TCP输入 之 tcp_v4_rcv的更多相关文章
- TCP输入 之 tcp_rcv_established
概述 tcp_rcv_established用于处理已连接状态下的输入,处理过程根据首部预测字段分为快速路径和慢速路径: 1. 在快路中,对是有有数据负荷进行不同处理: (1) 若无数据,则处理输入a ...
- tcp 输入 简析 转载
正常来说 TCP 收消息过程会涉及三个队列: Backlog Queue sk->sk_backlog Prequeue tp->ucopy.prequeue Receive Queue ...
- tcp 输入 prequeue以及backlog队列
/*ipv4_specific是TCP传输层到网络层数据发送以及TCP建立过程的真正OPS, 在tcp_prot->init中被赋值给inet_connection_sock->icsk_ ...
- TCP输入 之 快速路径和慢速路径
概述 快速路径:用于处理预期的,理想情况下的数据段,在这种情况下,不会对一些边缘情形进行检测,进而达到快速处理的目的: 慢速路径:用于处理那些非预期的,非理想情况下的数据段,即不满足快速路径的情况下数 ...
- TCP输入 之 tcp_data_queue
tcp_data_queue作用为数据段的接收处理,其中分为多种情况: (1) 无数据,释放skb,返回: (2) 预期接收的数据段,a. 进行0窗口判断:b. 进程上下文,复制数据到用户空间:c. ...
- TCP输入 之 tcp_prequeue
在未开启tcp_low_latency的情况下,软中断将skb送上来,加入到prequeue中,然后 在未启用tcp_low_latency且有用户进程在读取数据的情况下,skb入队到prequeue ...
- TCP输入 之 tcp_queue_rcv
tcp_queue_rcv用于将接收到的skb加入到接收队列receive_queue中,首先会调用tcp_try_coalesce进行分段合并到队列中最后一个skb的尝试,若失败则调用__skb_q ...
- Linux 内核协议栈 学习资料
终极资料 1.<Understanding Linux Network Internals> 2.<TCP/IP Architecture, Design and Implement ...
- 常见网络编程面试题答案征集与面试题(收集) ZZ 【网络编程】
http://www.cnblogs.com/wickedboy237/archive/2013/05/12/3074362.html 1:tcp和udp的区别2:流量控制和拥塞控制的实现机制3:滑动 ...
随机推荐
- 币种大写算法(js)
注意事项:小数精度处理问题,n*10出现精度误差,如1.88*10计算得18.799999999999997,实际想要的数据是18.8: 思路一:小数变成整数(通过字符串处理),计算后,变成小数: 思 ...
- 第十五章、Python多线程同步锁,死锁和递归锁
目录 第十五章.Python多线程同步锁,死锁和递归锁 1. 引子: 2.同步锁 3.死锁 引子: 4.递归锁RLock 原理: 不多说,放代码 总结: 5. 大总结 第十五章.Python多线程同步 ...
- MySQL踩坑及MySQL解压版安装
MySQL默认当前时间: MySQL5.5版本以下是不支持:datetime default now() 的,只能写成 timestamp default now() ; 而MySQL5.6以上是支持 ...
- yolo模型的特点与各版本性能对比
目录 一.YOLOV1 二.YOLOV2 二.YOLOV3 正文 目前,基于深度学习的目标检测算法大致可以分为两大流派: 1.两阶段(two-stage)算法:先产生候选区域然后再进行CNN分类(RC ...
- 《python解释器源码剖析》第1章--python对象初探
1.0 序 对象是python中最核心的一个概念,在python的世界中,一切都是对象,整数.字符串.甚至类型.整数类型.字符串类型,都是对象.换句话说,python中面向对象的理念观测的非常彻底,面 ...
- [转] - Linux中使用alternatives切换Jdk版本
1. 准备JDK包,分别是1.7和1.8,jdk-7u79-linux-x64.tar.gz和jdk-8u161-linux-x64.gz: 2. 解压,解压后的目录结构如图所示: JDK1.7: J ...
- python函数:匿名函数、函数递归与二分法、面向过程编程
今天主要讲三大部分内容: 一.匿名函数二.函数递归与二分法三.面向过程编程 一.匿名函数: """ 1. 什么时匿名函数 def定义的是有名函数:特点是可以通过名字重复调 ...
- 8.2.ZooKeeper应用场景
7.ZooKeeper应用举例 为了方便大家理解ZooKeeper,在此就给大家举个例子,看看ZooKeeper是如何实现的他的服务的,我以ZooKeeper提供的基本服务分布式锁为例. 7.1 分布 ...
- 初次安装虚拟机WindowsServer2016
因学习需要,安装windosServer2016,遇到了几个小坑,特地分享一下. 一.windows server 2016 登录 在登录是会出现 输入CTRL+ALT+DEL,才能响应进入用户界面, ...
- 简单混合锁(HybridLock)
internal sealed class SimpleHybridLock : IDisposable { //基元用户模式构造使用 ; //基元内核模式构造 private AutoResetEv ...