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:滑动 ...
随机推荐
- luogu P4688 [Ynoi2016]掉进兔子洞
luogu 我们要求的答案应该是三个区间长度\(-3*\)在三个区间中都出现过的数个数 先考虑数列中没有相同的数怎么做,那就是对三个区间求交,然后交集大小就是要求的那个个数.现在有相同的数,考虑给区间 ...
- 爬虫中什么是requests
print(response.text) #响应的信息 print(response.headers) #获取响应头 print(response.status_code) #响应状态码 print( ...
- The last packet successfully received from the server was 39,900 milliseconds ago问题解决
1,之前用Mysql或者mycat的时候都没有这个问题.后来改为haproxy+keepalived+mycat后出现这个问题 2,网上查了很多说法,我按照网上说的改了 datasource: url ...
- 2 java开发环境的配置步骤
1 首先需要下载JDK(以java se development kit java标准版开发包) 8.0 如果只是单纯的运行java程序则只需要安装JRE(java runtime envirome ...
- tensorboardX使用中 AttributeError: 'function' object has no attribute 'graph'
最近在使用tensorboardX可视化网络结构,因为tensorboardX并非pytorch原生的可视化工具,所以版本之间并不兼容 在使用的过程中会遇到,AttributeError: 'func ...
- CentOS7 使用光盘镜像作为yum源
1. 首先,如果是虚拟机,则确认guest有光驱并且处于激活状态,如果是真机则在光驱中插入光盘 :-) 2. mkdir /media/cdrom mount /dev/cdrom /media/c ...
- python部署到服务器(1) 一一 搭建环境
本机环境说明 linux下的CentOS 7, 自带python2.7.5, 使用 python --version 命令查看,因系统需要python2.7.5,因此我们并不卸载,另外安装python ...
- 9、nginx常用基础模块
1Nginx目录索引 ngx_http_autoindex_module模块处理以斜杠字符('/')结尾的请求(就是处理location /),并生成目录列表.当ngx_http_index_modu ...
- Elasticsearch中Mapping
映射(Mapping) 概念:创建索引时,可以预先定义字段的类型以及相关属性.从而使得索引建立得更加细致和完善.如果不预先设置映射,会自动识别输入的字段类型. 官方文档(字段数据类型):https:/ ...
- zencart新增categories分类表字段步骤
zencart新增分类字段步骤 1.categories表新增字段related_categories.related_products ) ) NOT NULL; 2.修改admin\categor ...