TCP/IP层梳理

很久没更新了,这半年的时间把TCP/IP内核协议栈以及部分内核源码认认真真的捡起来看了一遍,虽然经常看的头痛不过好在慢慢能够看懂,再之后就连以前看了很多遍都没理解的东西也能看懂了比如epoll源码。

过年在家把《wireshark网络分析就这么简单》和《wireshark网络分析的艺术》(林沛满著)仔细读了一遍,决定把一些内容记录下来,结合之前读过的内核协议栈源码深入地理解一下。

可结合https://blog.csdn.net/russell_tao/article/details/9111769里的高性能网络编程系列了解内核协议栈
发送方:
TCP/IP内核协议栈主要负责的任务是将用户态的网络包封装成内核态的skb(加上TCP协议头、IP协议头等)MSS(传输层)+IP协议头+TCP/UDP协议头 = MTU(链路层),再将skb根据三次握手中得到的MTU值进行MSS分组(应用层分组比IP层分组要好,IP层分组控制丢失一个分组需要重传整个包),放至发送队列进行发送。当接收方传来ACK包时删除发送缓存队列里的该数据包(在没有开启MSG_PEEK的情况下)。补充:对于应用层协议如HTTP、NFS、SOCKS5等来说,应用层协议头也是在分组完后在内核态中加上的,而不是直接在用户态里添加。

接收方:
数据报文达到网卡接收至内核队列,乱序包放至out_of_order队列,正序包放至receive队列(prequeue和backlog队列不在本文讨论范围),当顺序一致后调整乱序包到receive队列中,如果包长度超过了内核的最低接收阈值则将数据包拷贝至用户态进程中。再接着就是回调函数唤醒等待该数据包的进程,有epoll则走epoll,没有就走poll或select,再或者就是自定义钩子进行回调。

以上两种就是大概的协议栈部分的处理方式,为了让发送方和接收方具体的连起来除了上面操作系统层面还需要计算机网络方面的知识储备。

分成三大块:
发送队列与三次握手
接收队列与乱序重传
各类协议的连接与交流

首先一个是三次握手,中间的ARP(VLAN广播域)、NAT、子网掩码等概念跳过我们直接去到TCP/IP层。三次握手SYN、ACK的同时会确定一个win值,该值出现在TCP协议头中,这个win值意为接收方的窗口大小,由接收方决定自己当前的接收窗口有多大,从而发送方根据接收方窗口大小调整自己的发送窗口。发送窗口的大小取决于接收方窗口大小以及当前的网络拥塞情况中的最小值。这个发送窗口的就对应了发送方的MSS分组(出现在将数据包从用户态复制到内核态中,最后也会将该大小的分组放入发送队列中实际发送出去)。MTU=MSS+包头大小,即MSS是数据大小,MTU是数据+协议头大小。

之后就是走各个协议的情况,wireshark可以很好地解析各个数据包的发送情况以及根据协议头字段分析每个数据包所做的事情。书中举了文件传输的例子(NFS/SMB/FTP)每个协议有各自的调用方式。这个放在最后讲,先把网络的流程走通。

数据包的传输过程中根据网络状况等因素的影响,发送的包1,2,3,4可能会有2,1,3,4的到达情况,这里的乱序包就对应了接收方的out_of_order队列以及receive队列。其中2会放入乱序队列,1放入接收队列,3放入乱序队列,一段时间内内核态会管理这四个队列(还有backlog和prequeue队列)将乱序的分组进行整理顺序对了就放入receive队列,等到接收完毕拷贝至用户态。这其中可能会遇到分组丢失的情况,如果一段时间内(接收到三个后续的数据包之后)还未收到该数据包,则会发起快速重传,开启了SACK后发送方就会至重传丢失的那一个分组,如果未开启则重传该包的所有分组。也有可能该包分组较少,不能凑到三个重复的ACK则会出现超时重传,超时重传会将发送窗口降至一个MSS并重新开始慢启动、临界窗口、拥塞避免、拥塞窗口等过程,比快速重传相比性能下降很多。发送方还管理Nagle和延迟确认,同时网络中还会有很多算法用于更好地估算拥塞窗口避免由拥塞
导致的超时重传。

所以wireshark的分析经常会针对重传数量以及接收窗口等问题进行处理,如增大接收方接收队列大小、开启SACK等。作者提到一个奇怪的现象就是wireshark显示正常接收而客户端在三个包内没有接收到该分组导致一直重传的现象,后来发现是是接收方(客户端)的TCP/IP内核协议栈对分组的处理出错。还有就是在FTP或要用到NLM(网络锁管理进程)等需要服务器向客户端主动发起连接的时候被客户端的防火墙挡住导致无法继续访问,客户端等不到服务器端的响应不断发起超时重传。还有一个是访问权限控制开启,导致一些经过了NAT转换的IP无法正常连接服务器,这个的解决方法是将客户端和服务器放在一个网络避免NAT。

以上讲完了网络与TCP/IP协议栈的一些联系。像其中还会涉及到ARP自学习表找寻MAC地址,如果接收方IP&子网掩码结果跨局域网(目前基本都是按照VLAN划分)则只需要学习到默认网关的MAC地址。一旦连接建立成功则写入路由表下次再访问时(300s内)直接根据ARP表中的MAC结果直接发送数据包。当出局域网后还有城域网等等,走的EGP+IBGP协议避免跨城域网传输出现flood,出国则需要走EGP的VPN,从北京等地连出去。路由器里有两个核心一个负责路由转发(FIB)一个路由管理(RIB),即前者是转发平面后者是控制平面,SDN就是依据分层细分进行管理的。

接着讲书中提到的几个协议,NFS有portmap(端口111)查找NFS进程以及mount进程从而挂载至文件服务器的云盘,根据目录以及文件的filehandle查找文件进行读写,还可以创建目录读取文件属性等等,还有NLM进程进行共享锁的管理。FTP古老并且明文,用法简单。CIFS/SMB有身份验证kerberos和NLTM,还有oplock管理缓存的锁,读写等。都在协议头中进行处理。HTTP和云存储访问资源前者是用的目录路径后者由于是对象存储用的是对象号。

延迟确认是指收到发送方回复后没有立即确认而是等待一段时间再确认,在此过程中可以等待接收方是否有回应消息和ACK一起发回。主要是为了节省带宽,但同时增加了延后,书上举的一个例子说中午没有吃饭而是等到下午出门顺路去吃,这里节省了一趟往返食堂的时间,坏处则是吃饭时间延后。延迟确认还会导致其他丢失的包出现超时从而多次重传的现象,大幅度降低性能。要么关闭延迟确认要么在打开延迟确认的同时启用SACK,关于SACK就是当有分组丢失时每次ACK都会带上目前已确认的后续分组,通过SACK里的leftedge和right edge可以一次性重传当前丢失的分组,而不是一次性重传一个分组。如果发送窗口小再加上延迟确认更是雪上加霜容易导致超时重传。

握手请求被拒绝会有SEQ=1&&RST=1(开启了关联顺序号)
SYN             SYN
SYN/ACK    RST/ACK 半连接状态         //半连接状态如果长期不收到ACK则可以怀疑是DDOS,防火墙可以直接RST
ACK        连接状态
RST包中会记录TTL和Identification,判断出流量清洗或者TCP握手代理。每台服务器有固定的Identification,该值不同说明来自不同设备,如果在握手间涉及了两种Identificatiton则表明服务器和客户端间可能存在一台TCP握手代理,很可能是防止SYN FLOOD的防火墙,该防火墙会导致每次端口不同。防火墙(要用到NAT服务,同样的还有Docker)里的应该是nf_conntrack,里面有很多hash桶用于记录当前连接记录用于跟踪连接的五元组,syn flood如果打满了哈希桶就会导致新连接无法进入。iptables是可以实现端口映射的。

NLTM和kerboros,后者更好,采用第三方可信任KDC,而前者的challenge-response方式加密是固定的且可看到加密字段,所以易破解。kerboros使用了时间戳可以避免重放攻击,并且使用缓存的ticket从而不用每次登陆都验证一次身份,而NTLM每次验证。

LSO(Large Segment Offload):应用层会先将产生的数据交给TCP层,TCP层再根据MSS大小进行分段(由CPU负责),再交由网卡。而LSO允许TCP层直接把大于MSS的数据块直接传给网卡,让网卡来进行分段。抓包时得到的是站在CPU的视角,因此开启了LSO在发送端就会发现未分段的数据。像VMWare有时候瓶颈在虚拟网卡而不是CPU,因此关闭LSO会更好。

LRO(Large Receive Offload):如果速度缓慢可以考虑cwnd的增长方式,目前的在拥塞避免阶段cwnd是考虑接收到一个ACK,增加一个MSS,在慢启动阶段则是增加一倍MSS。LRO的作用是积累多个TCP包集中发送,因此收到的ACK数会少,从而导致cwnd增长慢。

UDP的传输是个什么情况:UDP不能把大块数据在内核间进行分段,而需要被网络层分片(重传分片需要重传整个包效率很慢)。TCP具有分段机制因此可以先封装避免网络层分片,而重传TCP包的效率会比重传分片高得多,所以TCP相比于UDP的优势在此。并且只能由应用层进行重传而不是TCP/IP协议栈进行重传。而KCP这类东西,大概就是底层用的UDP,然后自己设计一套类似TCP协议栈的东西从而针对特定应用制定自定义协议。为什么要分片,因为目前的网路是分组交换,接收方根据off值和ID值进行重组。ID值表示分组属于同一个包,off值表示不同的顺序分组,More fragments为0表示是最后一个分组,所有分组到齐即可重组。对于巨帧或超过MTU的帧,如果设置了DF(Dont fragment)属性则会直接丢弃而不是网络层再分片。可以用ping -f -l <字节数>测得网络中MTU最小的设备。

你可能感兴趣的:(TCP/IP层梳理)