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:滑动 ...
随机推荐
- 谁是嫌疑犯问题Python枚举法
原文:https://blog.csdn.net/yunzifengqing/article/details/81941592 问题描述:有6名犯罪嫌疑人A.B.C.D.E.F,已知如下事实: A.B ...
- Eclipse 快捷键、文档注释、多行注释的快捷键
关于快捷键 Eclipse 的很多操作都提供了快捷键功能,我们可以通过键盘就能很好的控制 Eclipse 各个功能: 一.多行注释快捷键 1.选中你要加注释的区域,用ctrl+shift+C 或者ct ...
- 1 SQL SERVER 实现字符串分割成table的方法
CREATE FUNCTION [dbo].[fn_SplitStringToTable] ( @p_Input VARCHAR(MAX), @p_Delimeter CHAR() = ',' ) R ...
- Redis-Hash常用命令
Redis-Hash常用命令 hset key field value 设置一个散列,但是在散列中一次只能设置一个属性,如果要批量设置多个属性,则需要使用 hmset命令 hget key field ...
- 通过sql判断时间区间是否存在数据
在做项目的时候遇到过一个问题,用户需要获取当前月或者几个月的数据,但是有一个要求,如果已经存在一张单已经包含了这几个月的数据,那么就不能再提取到重复的数据. 其实这个问题,我做完了我的方式之后才发现, ...
- 上班时能不能戴耳机?V
上班时能不能戴耳机? 新入职一公司, 上班时间不能戴耳机在V站一石激起千层浪,网友意见主要分几派: 甩手走人型: 神经病公司,这还不赶紧走 不走等着过年 不走留着转正 离职,下一题 还是赶紧离职吧 这 ...
- ANSIBLE自动化管理工具
ansible 基础 自动化运维工具 官网:https://www.ansible.com/ 官方文档:https://docs.ansible.com/ ansible 特性 1. 模块化:调用特定 ...
- 使用VMware Workstation15安装RHEL7.5以及相关设置(RHEL7及其以上版本均适用)
预备信息 VMware Workstation 是VMware公司发布的一款桌面虚拟计算软件,此软件提供虚拟机功能,使计算机可以同时运行多个操作系统. RHEL7.5(Red Hat Enterpri ...
- 洛谷 P2939 [USACO09FEB]改造路Revamping Trails
题意翻译 约翰一共有N)个牧场.由M条布满尘埃的小径连接.小径可 以双向通行.每天早上约翰从牧场1出发到牧场N去给奶牛检查身体. 通过每条小径都需要消耗一定的时间.约翰打算升级其中K条小径,使之成为高 ...
- crc32 cpp Makefile可参考
https://github.com/stbrumme/crc32 # simple Makefile CPP = g++ # files PROGRAM = Crc32Test LIBS = -lr ...