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:滑动 ...
随机推荐
- O039、Unshelve Instance 操作详解
参考https://www.cnblogs.com/CloudMan6/p/5529915.html 上一节我们 shelve Instance 到 Glance,本节学习如何通过 unshelv ...
- vue-loading图
父组件给子组件src地址: columns(){ return [ {'title': '图片', 'key': 'img', render(h, {row}){ return h(LoadingIm ...
- php扩展库解释
扩展库 说明 注解 php_bz2.dll bzip2 压缩函数库 无 php_calendar.dll 历法转换函数库 自 PHP 4.0.3 起内置 php_cpdf.dll ClibPDF 函数 ...
- Java List集合深入学习
List: https://blog.csdn.net/qq_37939251/article/details/83499291 https://blog.csdn.net/weixin_403043 ...
- 9、nginx常用基础模块
1Nginx目录索引 ngx_http_autoindex_module模块处理以斜杠字符('/')结尾的请求(就是处理location /),并生成目录列表.当ngx_http_index_modu ...
- Bridge的数据在内核处理流程
转:http://blog.sina.com.cn/s/blog_67cc0c8f0101oh33.html 转载一篇Bridge的数据在内核处理流程,文章写的不错啊! (2013-07-05 16: ...
- 8.4.Zookeeper结构和命令
1. Zookeeper结构 1.1.ZooKeeper数据模型Znode ZooKeeper拥有一个层次化的目录结构,命名符合常规文件系统规范 ZooKeeper树中的每个节点被称为—Znode,和 ...
- Linux ping route nslookup ifconfig arp traceroute
route -n 查看默认网关 ping -c 包个数 ping -s 包大小 host 目标主机 主机解析 nslookup 目标主机 arp -an 查看arp arp -s IP地 MA ...
- P4149 距离为K的点对(最少边数) n=200000 点分治
这题数据范围变成了200000 n^2就过不了 同时要求求的是最少的边数 不能容斥 #include<bits/stdc++.h> using namespace std; ; ; ], ...
- Java动态绑定与多态
在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特性.多态通过分离做什么和怎么做,从另一个角度将接口和实现分离开来.在一开始接触多态这个词的时候,我们或许会因为这个词本身而感到困惑, ...