将Ubuntu虚拟机架设到GNS3拓扑网络上并进行TCP协议仿真分析

前言

实验过程使用的实验文档,该文档是随便找的一个,具体详细来源暂未考证,且只完成了该文档的部分TCP的实验内容。且因为写这文章时离实验已经有一段时间,主要是我懒不想再复现一遍,所以文章中可能部分地方会有错漏,如果遇到问题可以私信我或发在评论区,我尽量尝试解决。


环境配置(将Ubuntu虚拟机挂载到GNS3拓扑网络上)

我们最终要搭建的网络拓扑结构如下
拓扑结构

  首先根据文档,会使用两台思科C3640路由器,路由器插件资源站内有可以自行搜索下载,我也上传了一份到百度网盘,提取码:0b1b,具体的GNS3如何导入路由器可以参见站内教程,此处不再赘述。

配置Ubuntu虚拟机

  这里使用的虚拟环境是VMware,Ubuntu镜像使用的是18.03版本,虚拟机设置全默认即可。按照实验手册,我们需要两台Ubuntu虚拟机,这里设为PCA和PCB。
在这里插入图片描述


  安装完成虚拟机后,我们要新设置两块虚拟网卡用于实验,打开VMware的虚拟网络编辑器,纯净环境下应该存在三块默认网卡VMnet0,VMnet1以及VMnet8。点击更改设置,选择添加网络,添加两块网卡VMnet2以及VMnet3分别作为实验中的PC-A和PC-B的网卡。
在这里插入图片描述
  添加网卡后选择仅主机模式,然后为两台PC虚拟机配置子网IP和掩码即可,这里可以根据自己的需求进行设置,但是PC-A和PC-B不要配置在同一个网段。
PC-A子网IP配置
PC-A子网IP配置
PC-B子网IP配置
在这里插入图片描述
  配置完成后,重启物理电脑配置好的两块网卡就能被GNS3识别了。注意,配置好网卡后要将对应虚拟机的网络适配器设置为配置好的对应网卡。
配置网卡到对应虚拟机
在这里插入图片描述
  然后还要进入虚拟机,设置虚拟机的地址,子网掩码和网关,以PC-A为例PC-B同理。
配置PC-A的地址,子网掩码和网关
在这里插入图片描述

  虚拟机配置完成后,回到GNS3开始配置网络。首先我们配置路由器c3640的端口,直接按照文档中要求的配置即可:
配置c3640串口
在这里插入图片描述
两台Ubuntu主机使用设备栏里的cloud挂载,这里以PC-A为例。
在这里插入图片描述
  进入cloud的设置页面,勾选左下角的“显示特殊的以太网接口”选项,现在我们可以在上面的下拉栏里看到存在的以太网接口了(不勾选只能看到以太网一个选项)。
配置cloud以太网接口
在这里插入图片描述
  我们为对应虚拟PC选择对应网卡,然后删除默认的以太网接口。
PC-A以太网接口配置结果
在这里插入图片描述
  我们还可以为cloud换一个图标,方便我们清楚的观看,然后连接各结构,路由器间通过串行接口s3/0连接,路由器使用f0/0端口和虚拟PC的网卡进行连接。
在这里插入图片描述

GNS3拓扑网络配置

  接下来配置各个端口的IP以及路由,限于篇幅,我们这里以R1为例,R2类似,命令如下:

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和网关配置

PCIP默认网关
PC-A10.0.1.11 / 2410.0.1.1
PC-B10.0.3.33 / 2410.0.3.1
路由F0/0S3/0默认网关
R110.0.1.1/2410.0.2.1/2410.0.2.2
R210.0.3.1/2410.0.2.2/2410.0.2.1

尝试ping各台设备检验连通性
路由器R1 ping路由器R2,可以ping通
在这里插入图片描述
路由器R1 ping虚拟机PC-A,可以ping通
在这里插入图片描述
  虚拟机PC-A ping路由器R1,也成功ping通(如果这里ping不通,可以先在GNS3中使用路由器R1 ping虚拟机PC-A后再尝试,因为初始时PC-A是没有R1路由表的,当使用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失败
在这里插入图片描述
  会发现此时两台虚拟机之间是不互通的,这是因为静态路由表中还没有对应的路由跳转项,因此发出去的包路由不知道怎么转发,或者回来的包接收不到。接下来我们要为路由器以及虚拟机配置路由表,首先是路由器,先观察网络拓扑结构。
在这里插入图片描述
  我们可以看到R2接收到的发往PC-A网段的数据包要首先要发往R1的s3/0端口,然后由s3/0端口处理发往该网段内的设备,因此我们配置R2的静态路由表,指令如下。

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虚拟机挂载到GNS3拓扑网络上


备注:因为写这文章时离实验已经有一段时间,主要是我懒不想再复现一遍,所以不确定Ubuntu虚拟机是否还要额外配置路由表,如果上诉步骤后网络不通,可以尝试配置Ubuntu虚拟机的路由表,使用route工具即可,工具的使用可以参看这个教程


先进行一下数据传输检验通路是否正常

  数据传输我们使用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尝试通过以下两种机制完全避免分片
  建立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: 快速重传机制解决了超时时间的问题,但是它依然面临着另外一个问题。就是重传的时候,是重传之前的一个,还是重传所有的问题。针对这一问题,改进的方法就是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 字节。
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值