目录
- 前言
- 环境配置(将Ubuntu虚拟机挂载GNS3拓扑网络)
-
- 配置Ubuntu虚拟机
- GNS3.拓扑网络配置
- 先进行一下数据传输检验通路是否正常
- 开始实验
-
- task1 TCP在传输过程中IP分片
- task2 TCP请求与不存在的实体连接
- task3 TCP重传
- 附TCP报文结构及各字段的含义
前言
环境配置(将Ubuntu虚拟机挂载GNS3拓扑网络)
??首先,根据文档,将使用两个思科C3640路由器,路由器插件资源站可以自己搜索下载,我也上传了一个百度网盘,提取码:0b1b,具体的GNS如何导入路由器可参见站内教程,此处不再赘述。
配置Ubuntu虚拟机
??这里使用的虚拟环境是VMware,Ubuntu镜像使用18.03版本,虚拟机设置完全默认。根据实验手册,我们需要两个Ubuntu这里设置的虚拟机PCA和PCB。 ??虚拟机安装完成后,我们需要设置两张虚拟网卡进行实验,并打开VMware在纯净的环境下,虚拟网络编辑器应该有三张默认网卡VMnet0,VMnet1以及VMnet8.点击更改设置,选择添加网络,添加两张网卡VMnet2以及VMnet三分别作为实验PC-A和PC-B的网卡。 ??添加网卡后,选择只有主机模式,然后两个PC配置子网的虚拟机IP可以根据自己的需要设置和掩码,但是PC-A和PC-B不要在同一网段配置。 PC-A子网IP配置 PC-B子网IP配置 ??配置完成后,重启物理计算机配置的两张网卡GNS3.识别。注意,配置网卡后,将相应虚拟机的网络适配器设置为相应的网卡。 配置网卡到相应的虚拟机 ??然后进入虚拟机,设置虚拟机地址、子网掩码和网关PC-A为例PC-B同理。 配置PC-A地址、子网掩码及网关
??虚拟机配置完成后,返回GNS开始配置网络。首先,我们配置路由器c3640端口可根据文档要求直接配置: 配置c3640串口 两台Ubuntu主机在设备栏中使用cloud挂载,在这里PC-A为例。 ??进入cloud设置页面,检查左下角的显示特殊以太网接口选项,现在我们可以在上面的下拉栏中看到以太网接口(不检查只能看到以太网选项)。 配置cloud以太网接口 ??我们对应虚拟PC选择相应的网卡,然后删除默认的以太网接口。 PC-A配置以太网接口的结果 ??我们还能做到cloud更改图标,方便我们清楚地观看,然后连接各种结构,路由器通过串行接口s3/0连接,使用路由器f0/0端口和虚拟端口PC连接网卡。
GNS3.拓扑网络配置
??接下来,配置每个端口IP以及路由,限于篇幅,我们在这里R1为例,R类似地,命令如下:
conf t int s3/0 ip add 10.0.2.1 255.255.255.0 no shut int f0/0 ip add 10.0.1.1 255.255.255.0 no shut end write
??R2设置同理,PC-A以及PC-B上一步VMware在网卡设置中完成,这里不需要单独设置,这里各端口设置完成后,结果如下表所示。 各设备及端口IP和网关配置
PC | IP | 默认网关 | |
---|---|---|---|
PC-A | 10.0.1.11 / 24 | 10.0.1.1 | |
PC-B | 10.0.3.33 / 24 | 10.0.3.1 | |
路由 | F0/0 | S3/0 | 默认网关 |
R1 | 10.0.1.1/24 | 10.0.2.1/24 | 10.0.2.2 |
R2 | 10.0.3.1/24 | 10.0.2.2/24 | 10.0.2.1 |
路由器R1 ping路由器R2,可以ping通 路由器R1 ping虚拟机PC-A,可以ping通 ??虚拟机PC-A ping路由器R1,也成功ping如果是这里ping不,可以先在GNS三、使用路由器R1 ping虚拟机PC-A然后尝试,因为在开始时PC-A是没有R使用路由表时R1 ping过后会在PC-A生成关于R1路由表,此时可以ping通了)。 虚拟机PC-A ping路由器R1,可以ping通 ??尝试PC-A ping虚拟机PC-B(或者ping路由器R2的端口,或路由器ping另一个路由器下的虚拟PC机) PC-A ping PC-B失败 ??会发现此时两台虚拟机之间没有交换,因为静态路由表中没有相应的路由跳转项,所以发送的路由不知道如何转发,或者回来的包无法接收。接下来,我们将为路由器和虚拟机配置路由表,首先是路由器,首先观察网络拓扑结构。 ??我们R接收到的发送PC-A网段要发送网段的数据包R1的s3/0端口,然后由s3/0端口处理发往该网段内的设备,因此我们配置R静态路由表2,指令如下。
conf t ip routing #开路由转发很重要! ip route 10.0.1.0 255.255.255.0 10.0.2.1
do show ip route
end
write
这里尤为要注意,配置路由转发时一定一定要使用ip routing指令开启路由转发功能,否则使用do show ip route指令可以看到配置并不能被挂载,从而使得配置无效。同理配置好R1静态路由表。配置完成后,我们尝试在虚拟机PC-A上ping PC-B以及其他组件,我们可以发现,已经可以成功的ping通了。 PC-A ping PC-B成功 PC-A ping R2 S3/0端口
先进行一下数据传输检验通路是否正常
数据传输我们使用Ubuntu虚拟机自带的netcat工具,neccat工具能让我们很容易的在两台虚拟机间建立TCP通信并发送数据。 我们抓包GNS3拓扑网段上PC-A到R1的通路 首先我们在PC-B上使用nc命令开启10086端口的监听:nc -l 10086
然后在PC-A上连接PC-B的10086端口:nc 10.0.3.33 10086
上诉过程会进行三次握手,然后TCP连接被建立,可以开始通信了,我们先在PCA上向PCB发送消息“hello PCA”然后PCB回复“hello PCB”,接着在PCB上使用ctrl+c中断连接,这一过程会进行四次挥手。 很标准的三次握手,数据传输,四次挥手过程,此处不展开分析。
开始实验
先按照实验手册做好环境设置,即禁掉SACK。
task1 TCP传输过程中的IP分片
以下资料参考自MTU TCP-MSS详解和实验手册 MTU(Maximum Transmission Unit,最大传输单元)用来通知对方所能接受数据服务单元的最大尺寸,说明发送方能够接受的有效载荷大小。 当传输层向IP层发送的数据包超过基础数据链路网络的最大传输单元(MTU)时,就会发生分片。例如,在以太网中,MTU是1500字节。如果IP数据报超过MTU大小,则IP数据报将被分割成多个IP数据报,或者,如果在IP标头中设置了不分段(DF)标志,则丢弃IP数据报,并将ICMP消息发送回发送方,指示问题。 当一个IP数据报被分割时,其有效负载被分割成多个IP数据报,每个IP数据报都满足MTU施加的限制。每个片段都是一个独立的IP数据报,在网络中独立于其他片段进行路由。分片可能发生在发送主机或中间IP路由器上。片段仅在目标主机上重新组装。 尽管IP分片化提供了灵活性,可以将数据链路技术的差异隐藏到更高层,但它会带来相当大的开销,因此应该避免。TCP尝试使用路径MTU发现方案来避免碎片,该方案确定最大段大小(MSS),这不会导致碎片。 : 建立TCP连接时,它会协商要使用的最大段大小(MSS)。TCP客户端和TCP服务器都将MSS作为选项发送到第一个传输的TCP段的TCP标头中。每侧设置MSS,以便在传输段时,传出网络接口上不会出现分片。采用较小的值作为连接的MSS值。 MSS的交换仅解决主机上的MTU约束,而不是中间路由器上的MTU约束。为了确定从发送方到接收方的路径上的最小MTU,TCP采用了一种称为路径发现MTU发现的方法,其工作原理如下。发送方总是在所有IP数据报中设置DF位。当路由器需要使用DF位集对IP数据包进行分段时,它会丢弃该数据包并生成类型为“目的地不可访问;需要分段”的ICMP错误消息。在收到此类ICMP错误消息后,TCP发送方将减小段大小。这将一直持续到确定不触发ICMP错误消息的段大小为止。 我们探究TCP的路径发现MTU设置方式,因此我们要设置路由过程中路由器的MTU而非主机的MTU。首先我们可以查看路由器R1和R2的端口默认MTU大小(限于篇幅,这里只显示路由器R1的,R2类似) 指令为:show int
可以看到默认端口大小都为1500,我们将s3/0端口的MTU大小减小,修改为500,指令如下,R1与R2都修改:
conf t
int s3/0
mtu 500
再次查看MTU大小: 正如我们上面介绍的那样,TCP有两种机制避免分片,第一种是主机沟通确立最小的MSS,第二种是由于路由MTU限制而使用的路径MTU发现机制,这里我们修改了路由的MTU大小,则使得TCP的第一种方法失效,只能使用路径MTU发现机制,因此我们要确保两台主机均开启了路径发现MTU机制,查询和开启指令如下:
#查询
#0-禁用
#1-默认禁用,检测到ICMP不可达时开启
#2-开启
sysctl net.ipv4.tcp_mtu_probing
#开启
sudo sysctl -w net.ipv4.tcp_mtu_probing=2
查询是否开启MTU发现机制 开启MTU发现机制 接下来创建一个大于所设置MTU大小的文件,并传输用于触发分片以及MTU发现机制。指令如下,这里创建一个test.txt文件,包含512个字符“a”(保证大于mtu值500),指令:for i in {0..512}; do echo -n "a" >> test.txt; done;
PC-B开启监听,PC-A连接并将上诉产生的数据包传输,抓取PC-A与R1间的报文。PC-A向PC-B传输文件文件指令为:cat test.txt | nc 10.0.3.33 10086
== PC-B启动监听并接收数据==
连接PC-B并发送上诉产生大文件 抓包结果 可以看到前三个包是三次握手数据包,最后的包是挥手包。中间是数据交互产生的数据包。我们可以从握手包中看到主机间进行的MSS交互。
主机间进行MSS大小交互
发送的第一个数据包报文 可以看到该报文设置了DF位,且一次发送了513字节的数据,则该报文被路由器接收后,因为513字节大于MTU500字节,因此路由器会尝试进行分片操作,然后DF位设置会拒绝路由器的分片,使得路由器丢弃该分片并回送ICMP不可达报文,即下一个报文。 ICMP回传差错报文 可以看到该报文类型为3,表示不可达,代码为4,解释为需要分片,并给出了下一跳的MTU大小为500,且下面还封装了导致这一差错的IP原始报文头。 接下来进入路径MTU发现流程,发送方在接收到上诉的ICMP差错报文后,根据下一跳MTU大小,计算出适合传输的最大段长度,从而在后续的发送报文过程中,避免在中间路径被分片的情况产生。(当然我们这里只有两个路由相连且MTU均为500,因此反映在报文中的情况是只发现了一次就解决了,如果有多跳,且每跳MTU都不相同,则会通过多次的ICMP差错回复进行多次调整,直到找到最合适的发送长度。)
根据下一跳MTU传输数据长度修改为500并分片 第二片发送剩下的13字节
task2 TCP请求与一个不存在的实体连接
以下资料参考TCP连接一个IP或端口不存在的主机时,会发生什么?和实验手册 首先要在PC-A的主机arp表上新增加一条不存在的IP的静态条目,这是因为我们要保证数据包(至少第一次握手请求包)要能发送出去,否则数据包会因为arp询问失败而被丢弃。指令:arp -s 10.0.1.100 00:01:02:03:04
然后抓取PC-A与R1之间的数据包,并在PC-A上尝试与该主机进行连接,指令:nc 10.0.1.100 10086
请求连接到不存在主机 可以看到,抓取到的第一个包是TCP第一次握手的SYN数据包。
TCP第一次握手数据包 接下来重传了该数据包三次。 重传 这是因为第一个数据包会通过路由器R传递到网段为10.0.1.xx的路由器上,然后路由器发出ARP 请求,询问该局域网内的机器有没有叫10.0.1.100的 (结果当然没有)。导致该包最终没能发送成功,发送端也就迟迟收不到目的机的第二次握手响应,因此触发TCP重传。
使用PC-A连接PC-B一个未开启监听的端口,这里选择连接80端口,指令:nc 10.0.3.33 80
连接到不存在的端口
因为我们连接的IP地址是存在的。所以我们可以正常发消息到目的IP,因为对应的MAC地址和IP都是正确的,所以,数据传输过程没有问题。然而当数据包传输到目标主机传输层时,TCP协议在识别到这个端口号对应的进程根本不存在时,就会把数据丢弃,响应一个RST消息给发送端。 报文如下图,可以看到返回的数据包RST位被置为1,且发送的源端口为80,即我们的目的端口(不存在的端口)。
RST响应报文
RST是一种强制关闭连接的机制,我们知道TCP正常情况下断开连接是用四次挥手,那是正常时候的优雅做法。但异常情况下,收发双方都不一定正常,连挥手这件事本身都可能做不到,所以就需要一个机制去强行关闭连接。RST 就是用于这种情况,一般用来异常地关闭一个连接。它在TCP包头中,在收到置了这个标志位的数据包后,连接就会被关闭。
task3 TCP重传
以下资料参考TCP重传机制详解和实验手册 TCP有常见的几种重传机制:超时重传、快速重传、SACK(Selective Acknowledgment)等。
:当发送数据时,设立一个定时器,当超过指定时间后,发送端没有收到ACK应答报文,则触发超时重传机制,重发该数据。超时重传时间存在一个度量,既不能太长,超时时间过长的话,那么就会导致数据包丢失了一段花时间才重传,减低了网络传输效率,但是也不能太短,超时重传时间过短的话,那么就很有可能造成数据包还没到接收端或者应答ACK没到发送方就会重传。因此超时重传需要一个适中的标准,一般设置超时重传略大于往返时延(即数据从网络的一端到另外一段的往返时间)。TCP采用的是发送端每次的重传都是经过越来越长的时间进行的。每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。超时重传存在的问题之一就是超时周期过长,当我们多次丢失数据包时,这种长周期的机制就会使我们延迟发送,加大了端到端的时延。
:为了解决这种超时重传的长周期情况,TCP通常可在超时时间发生之前通过冗余的ACK来较好的检测丢包情况。我们都知道,在数据传输过程中,如果一个数据包丢失,其后发送的数据包到接收端都会返回丢失数据包的ACK,因为该数据包还未被接收确认。因为发送方经常一个接一个地发送大量的报文段,如果一个报文段丢失,就很可能引起许多一个接一个的冗余ACK,如果TCP发送方接收到对相同数据的3个冗余ACK,它把这当作一种指示,说明跟在这个已被确认过3次的报文段之后的报文段已经丢失.一旦收到3个冗余ACK, TCP就执行快速重传,即在该报文段的定时器过期之前重传丢失的报文段。
: 快速重传机制解决了超时时间的问题,但是它依然面临着另外一个问题。就是重传的时候,是重传之前的一个,还是重传所有的问题。针对这一问题,改进的方法就是SACK,这种方式需要在 TCP 头部[选项]字段里加一个 SACK,简单来讲就是在快速重传的基础上,返回最近收到的报文段的序列号范围,这样客户端就知道,哪些数据包已经到达服务器了,可以避免丢失后需要重传所有数据包的问题。
接下来我们会引起TCP传输的重传机制,探究其报文特征。由于往返时延我们不能测量,因此我们通过设置时钟以及手动开关网络来达到引起重传的目的。 首先设置R1和R2的s3/0端口的时钟,指令如下:
conf t
int s3/0
clock rate 1200
设置路由器端口时钟
接着我们要创建一个巨大的文件使得传输时间够久,以便我们控制中断和开启数据传输,造成时延。这里创建一个含有100000个字符“a”的文件test.txt,指令:for i in {1..100000}; do echo -n "a">>test.txt; done;
接着抓取PC-A与R1之间的包,PC-B启动nc监听,PC-A传输该文件给PC-B。 PC-B启动监听并接收文件
PC-A连接PC-B并传输大文件 因为文件够大,所以会传输一段时间,我们在传输过程中关闭R1的s3/0端口一段时间(约5秒),再开启,指令如下:
conf t
int s3/0
#关闭端口
shut
#等待一定时间开启端口
no shut
传输过程中开关S3/0端口 抓取到的重传包 首先我们可以看到超时重传包。 该包第一次在23:05:27秒被发送。
图2.61 第一次发送 此时我们切断了端口,并等待了一段时间后开启,该包在23:05:48重传。. 我们还可以看到快速重传包 可以看到,再收到3个关于549的冗余ACK后,发送了快速重传包。
附TCP报文结构以及各字段含义
以下资料参考自《网络协议——分析、设计与仿真》
TCP 报文段的报头有前 20 字节的固定部分,后面 4n 字节是根据需要而添加的字段。 各字段含义为:
- 16位源端口号:用于标识发送TCP报文段的应用程序的端口号,占2字节16位。
- 目的端口号:用于接收TCP报文段的应用程序的端口号,占2字节16位。
- 32位序号:用于指明报文段在发送方的数据字节流中的位置,占4字节32位。
- 32位确认号:用于通知对方期望接收下一个报文段的序号,占4字节32位。序号和确认号是发送方和接收方用来对数据字节进行技术的。
- 4位首部长度:也是数据偏移量,用于度量从报文段开始位置到数据开始位置的偏移量,即TCP报文段首部长度。本字段占4bit位,以4字节为基本计数单位。
- 保留:占6bit位,固定为0,保留给未来使用。
- URG(紧急bit):占1bit,当该位为1,表示紧急指针字段有效,并通知协议处理程序本报文段中有紧急数据,应当尽快传送。
- ACK(确认bit):占1bit。当该位为1时表示本报文段携带有确认信息。
- PSH(推送bit):占1bit。当该位为1时,表示本报文段请求推操作来强制进行数据发送。
- RST(复位bit):占1bit。当该位为1时,表示TCP链接出现严重偏差,必须释放链接,此外也用于拒绝非法报文段和拒绝打开连接。
- SYN(同步bit):占1bit。当该位为1时,表示连接请求或连接接受报文,通常需要与ACK结合使用。
- FIN(终止bit):占1bit。当该位为1时,表示发送方的数据已发送完毕,要求释放TCP连接。
- 16位窗口大小:用于控制对方发送的数据量,进行流量控制,占2字节16位。
- 检验和:用于检查报文是否在传输过程中出差错。占2 字节16位。
- 紧急指针:当 URG=1 时才有效,指出本报文段紧急数据的字节数。占2字节16位。
- 选项:长度可变,最长可达 40 字节。