网络协议详解

ARP协议,DNS协议,IP协议,TCP协议,IP路由和TCP状态转换图

Posted by chensong on 2018-02-12 23::09::56

前言

网络通信一直都是所有的框架的基础

正文

一, 基础复习

1, 实际开发四层模型

① , 封装

  经过TCP封装后的数据称为TCP报文段,TCP协议为通讯双方维持一个连接,并且在内核中存储相关数据。这部分数据中的TCP头部信息和TCP内核缓冲区(发送缓冲区或者接受缓冲区)数据一起构成了TCP报文段。

  当发送端应用程序使用send(或者write)函数向一个TCP连接写入数据时,内核中的TCP模块首先把这些数据复制到与该连接对应的TCP内核发送缓冲区中,然后TCP模块调用IP模块提供的服务,传递的参数包括TCP头部信息和TCP发送缓冲区中的数据,即TCP报文段。

  经过IP封装后的数据称为IP数据报。IP数据报也包括头部信息和数据部分,其中数据部分就是一个TCP报文段,UDP数据报或者ICMP报文。

  经过数据链路层封装的数据称为帧(frame)。 传输媒介不同,帧的类型也不同。比如,以太网上传输的是以太网帧(ethernet frame),而令牌环网络上传输的则是令牌环帧(token ring frame)。

  以太网帧使用6字节的目的物理地址和6字节的源物理地址来表示通信的双方。关于类型(type)字段,4字节CRC字段对帧的其他部分提供循环冗余校验。

  帧的最大传输单元(max transmit unit, MTU), 即帧最多能携带多少上层协议数据(比如IP数据报),通常受到网络类型的限制。以太网帧的MTU 1500字节。正因为如此,过长的IP数据报可能需要被分片(fragment)传输。

② ,分用

  当帧到达目的主机时, 将沿着协议栈自低向上依次传递。各层协议依次处理帧中本层负责的头部数据, 以获取所需的信息,并最终处理后的帧交给目标应用程序。这个过长称为分用(demultiplexing)。

  因为IP协议,ARP协议和RARP协议都使用帧传输数据,使用帧的头部需要提供某个字段(具体情况取决于帧的类型)来区分他们。以以太网帧为例, 它使用2字节的类型字段来标识上层协议。如果主机接受到的以太网帧类型字段的值位0x800,则帧的数据部分为IP数据报,以太网驱动程序将帧交付给IP模块;若类型字段的值为0x806,则帧的数据部分为ARP请求或者应答报文,以太网驱动程序就将帧交付给ARP模块;若类型字段的值为0x835,则帧的数据报部分为RARP请求或者应答报文,以太网驱动程序就将帧交付给RARP模块。

  同样,因为ICMP协议,TCP协议和UDP协议都使用IP协议, 使用IP数据报的头部采用16位的协议(protocol)字段来区分他们。

  TCP报文段和UDP数据报则通过其头部中的16位的端口号(port number)字段来区分上层应用程序。比如DNS协议对应的端口号是53,HTTP协议(Hyper-Text Transfer protocol, 超文本传送协议)对应的端口号是80。 所有知名应用层协议使用的端口号都可在/etc/services 文件中找到。

  帧通过上述分用步骤后, 最终将封装前的原始数据送至目标服务(ARP服务,RARP服务,ICMP服务或者应用程序)。这样在顶层目标服务看来,封装和分用是乎没有发生过。

2, ARP的工作原理

  ARP协议能实现任意网络层地址到任意物理地址的转换仅讨论从IP地址到以太网自动(MAC地址)的转换。其工作原理是:主机向自己所在网络广播一个ARP请求,该请求包含目标机器的网络地址。此网络上的其他机器都将收到这个请求,但只有被请求的目标机器会回应一个ARP应答,其中包含自己的物理地址。

① 以太网ARP请求/应答报文详解

以太网ARP请求/应答报文的格式

介绍:

  1. 硬件类型字段定义物理地址的类型,它的值为1表示MAC地址。
  2. 协议类型字段表示要映射的协议地址类型,它的值为0x800,表示IP地址。
  3. 硬件地址长度字段和协议地址长度字段,顾名思义,其单位是字节。对MAC地址来说,其长度为6;对IP(v4)地址来说,其长度为4。
  4. 操作字段指出4种操作类型:ARP请求(值为1),ARP应答(值为2),RARP请求(值为3),和RARP应答(值为4)。
  5. 最后4个字段指定通信双方的以太网地址和IP地址。发送端填充除目的端以太网地址外其他3个字段,以构建ARP请求并发送之。接收端发现该请求的目的端IP地址是自己,就把自己的以网地址填进去,然后交换两个目的端地址和两个发送端地址,以构建ARP应答并返回之(当然,如前所述,操作字段需要设置为2)。

  上图所知, ARP请求/应答报文的长度为28字节。如果再加上以太网帧头部和尾部的18字节,则一个携带ARP请求/应答报文的以太网帧长度为46字节。不过有的实现要求以太网帧数据部分长度至少为46字节,此时ARP请求/应答报文将增加一些填充字节,以满足中国要求。在这种情况下,一个携带ARP请求/应答报文的以太网帧长度为64字节。

② ,ARP高速缓存的查看和修改

  通常,ARP维护一个高速缓存,其中包含经常访问(比如网关地址)或者最近访问的机械的IP地址到物理地址的映射。这样就避免了重复的ARP请求,提高了发送数据包的速度。

  linux下可以使用arp命令来查看和修改ARP高速缓存。比如,ernest-laptop在某一时刻(注意,ARP高速缓存是动态变化的)的ARP缓存内容如下

arp -a

描述是路由器 下面是两条命令则分别删除和添加一个ARP缓存项。

arp -d ip                                           # 删除一个IP对应的ARP缓存项
arp -s ip mac                                       # 添加ip和mac对应的ARP缓存项
③ 使用tcpdump观察ARP通讯过程

为了清楚了解ARP的运作过程,使用telnet命令测试一下

arp -d 192.168.64.128                              # 清除ARP缓存中ip地址对应缓存

tcpdump -i ens33 -ent ‘(dst 192.168.64.128 and src 192.168.64.132) or (dst 192.168.64.132 and src 192.168.64.129)’

在192.168.64.128上登录

telnet 192.168.64.128 echo

  在执行telent命令之前,应先清除ARP缓存中与IP对应的项,否则ARP通信不被执行,我们也就无法抓取到期望的以太网帧。当执行telent命令并在两台通信主机之间建立TCP连接,tcpdump抓取到的众多数据包中,只有最靠前的两个和ARP通信有关系。

在 192.168.64.132抓取的数据

  由tcpdump抓取的数据包本质上是以太网帧,我们通过该命令的众多选项了扩展帧的过滤(比如dst和src指定通信的目的端IP地址和源端IP地址)和显示(比如用-e 选项开启以太网帧头部选项的显示)。   第一数据包中ARP通信的源端的物理地址是00:0c:29:b8:08:9f,目的端的物理地址是00:0c:29:af:12:59, 这是以太网的广播地址,用以表示整个LAN。该LAN上所有的机械都会收到并处理这样的帧。数值0x806是以太网帧头部的类型字段的值,它表示分用的目标是ARP模块。该以太网帧的长度为42字节(实际上是46字节,tcpdump未统计以太网帧尾部4字节的CRC字段),其中数据部分长度为28字节。”Request”表示这个是ARP请求,”who-has 192.168.64.132 tell 192.168.64.129”则表示是 129机械要查询132的IP地址。   第二数据包中,ARP通信的源端的物理地址是00:0c:29:b8:08:9f,目的端的物理地址是00:0c:29:af:12:59。”Reply”表示这是一个ARP应答,”192.168.64.129 is-at 00:0c:29:b8:08:9f”则表示目标机器129报告其物理地址。该以太网帧的长度为60字节(实际上是64字节),可见它使用了填充字节来满足最小帧长度。

3,DNS工作原理

  我们通常使用机器的域名来访问这个机器,而不直接使用其IP地址,比如访问因特网上的各种网站。那么如何将机械的域名转换成IP地址呢?这就需要使用域名查询服务。域名查询服务有很多实现方式,比如NIS(Network information Service, 网络信息服务),DNS和本地静态文件等。

① DNS查询和应答报文详解

  DNS是一套分布式的域名服务系统。每个DNS服务器上都存放着大量的机器名和IP地址的映射,并且是动态更新的。众多网络客户端程序都使用DNS协议来向DNS服务器查询目标主机的IP地址。DNS查询和应答报文的格式

16位标识字段用于标记一对DNS查询和应答,以此区分一个DNS应答是哪个DNS查询的回应。 16位标志字段用于协商具体的通信方式和反馈通信状态。NDS报文头部的16位标志字段的细节

NDS报文标志的含义

  1. QR: 查询/应答标志。0表示这是一个查询报文, 1表示这是一个应答报文。
  2. opcode: 定义查询和应答的类型。 0表示标准查询, 1表示反向查询(由IP地址获得主机域名),2表示亲切服务器状态。
  3. AA: 授权应答标志,仅当应答报文使用。 1表示域名服务器是授权服务器。
  4. TC: 截断标志,仅当DNS报文使用UDP服务时使用。因为UDP数据报有长度限制,所以过长的DNS报文将截断。1表示DNS报文超过512字节,并截断。
  5. RD: 递归查询标志。 1表示执行递归查询,即如果目标DNS服务器无法继续某个主机名, 则它将向其他DNS服务器继续查询,如此递归,直到获得结果并把该结果返回给客户端。0表示执行迭代查询,即如果目标NDS服务器无法继续某个主机名,则它将自己知道的其他NDS服务器的IP地址返回给客户端,以供客户端参考。
  6. RA: 允许递归标志。仅由应答报文使用,1表示DNS服务器支持递归查询。
  7. zero: 这3位未用,必须设置为0。
  8. rcode: 4位返回码,表示应答的状态。常用值有0(无错误)和3(域名不存在)。

  接下来的4个字段则分别指出DNS报文的最后4个字段的资源记录数目。对查询报文而言,它一般包含1个查询问题,而应答资源记录数,授权资源记录数和额外资源记录数则为0。应答报文的应答资源记录数据则至少为1,而授权资源记录数和额外资源记录数可为0或者非0。

查询问题的格式

查询名以一定的格式封装了要查询的主机域名。16位查询类型表示如何执行查询操作,常见的类型有如下几种:

  1. 类型A: 值是1,表示获取目标主机的IP地址。
  2. 类型PTR: 值是12,表示反向查询。

16位查询类通常为1,表示获取因特网地址(IP地址)。 应答字段,授权字段和额外信息字段都使用资源记录(Resource record, RR)格式。

32位域名是该记录中与资源对应的名字,其格式和查询问题中的查询名字段相同。16位类型和16位类字段的含义也与DNS查询问题的对应字段相同。

32位生存时间表示该查询记录结果可被本地客户端程序缓存多长时间,单位是秒。

16位资源数据长度字段和资源数据字段的内存取决于类型字段。对类型A而言,资源数据是32位IPv4地址,而资源数据长度则为4(以字节为单位)。

② linux下访问DNS服务

我们要访问DNS服务, 就必须先字段DNS服务器的IP地址。linux使用/etc/resolv.conf 文件来存放DNS服务器的IP地址。

③ 使用tcpdump观察NDS通信过程

为了清楚了解NDS通信的过程,使用tcpdump抓取这一过程中LAN上传输的以太网帧。

tcpdump -i eth0 -nt -s 500 port domain

  这一次执行tcpdump抓包是,我们使用”port domain”来过滤数据包,表示只抓取使用domain(域名)服务的数据包,即DNS查询和应答报文。

  这两个数据包开始的”IP”指出,它们后面的内容描述的是IP数据报。tcpdump以”IP地址,端口号”的形式来描述通信的某一端:以”>”表示数据传输的方向,”>”前面是源端,后面是目的端,可见,第一个数据包是我的机器(IP地址是172.20.137.125) 向其首选DNS服务器(IP地址是100.100.2.128)发送的DNS查询报文(目标端口53是DNS服务事业的端口),第二个数据包是服务器反馈的DNS应答报文。

  第二个数据包中,”4/0/0” 表示该报文中包含4个应答资源记录,0个授权资源记录和0个额外信息记录。”CNAME www.a.shifen.com, CNAME www.wshifen.com., A 104.193.88.123, A 104.193.88.77 “ 则表示4个应答资源记录的内容。其中CNAME表示紧跟其后的记录是机器的别名, A表示紧随后其后的记录是IP地址。该应答报文的长度为226字节。

二, IP和TCP协议格式分析

1,IPv4分析

韩老师的视频学习,在这里感谢韩老师的视频

位置的分析

①,IPv4结构分析
  1. version 版本号 (4位, 其他的IPV4 协议的扩展版本[如SIP协议和PIP协议])
  2. 首部长度: 标识该ip头部有多少个32bit字(4字节)。因为4位最大表示15, 所以IP头部最长是60字节。
  3. 区分服务(TOS): 8位服务类型包括一个3位的优先权字段(现在已经被忽略),4位的TOS字段和1位保留字段(必须置0)。4位的TOS字段分别表示:最小延时,最大吞吐量,最高可靠性和最小费用。其中最多有一个能置为1,应用程序应该根据实际需求要来设置它。比如ssh和Telnet这样的登录程序需要的是最小延时的服务, 而文件传输程序ftp则需要最大吞吐量的服务。
  4. 16位总长度(total length): 是指整个IP数据报的长度, 以字节为单位,因此ip数据报的最大长度为65535($2^{16}$ - 1)字节。但由于 MTU 的限制, 长度超过MTU的数据报都将被分片传输, 所以实际传输的ip数据报(或分片) 的长度都远远没有达到最大值。接下来的3个字节则描述了如何实现分片。
  5. 16位标识: 唯一地标识主机发送的每一个数据报。其初始值由系统随机生成: 每发送一个数据报, 其值就加1. 该值在数据报分片时被复制到每个分片中, 因此同一个数据报的所有分片都具有相同的标识值。
  6. 3位标志: 3位标志字段的第一位保留。第二位(Don’t Fragment DF) 表示”禁止分片”。如果设置了这个位, IP模块将不对数据报进行分片。 在这种情况下, 如果IP数据报长度超过MTU的话, IP模块将丢弃该数据报并返回一个ICMP差错报文。第三位(More Fragment, MF)表示 “更多分片”。 除了数据报的最后一个分片外, 其他分片都要把它置1。
  7. 片偏移: 13位分片偏移(fragmentation offset) 是分片相对原始IP数据报开始处(仅指数据部分)的偏移。 实际的偏移值是该值左移3位(乘8)后得到的。 由于这个原因,除了最后一个IP分片外, 每个IP的数据部分的长度必须是8的整数倍(这样才能保证后面的IP分片拥有一个合适的偏移值)
  8. 生存时间: 8位生存时间(Time To Live, TTL)是数据报到达目的地之前允许经过的路由器跳数。TTL值被发送端设置(常见的值是64)。数据报在转发过程中每经过一个路由,该值就被路由器减1。 当TTL值被为0时, 路由器将丢弃数据报,并向源端发送一个ICMP差错报文。TTL值可以防止数据报陷入路由循环。
  9. 协议: 8位协议(protocol) 用来区分上层协议, 在文件/etc/protocols 中定义了所有上层协议对应的protocol字段的数值。其中, ICMP是1, TCP是 6, UDP是17,
  10. 首部校验和: 16位头部校验和(header checksum) 由发送端填充,接受端对其使用CRC算法以检验IP数据报头部(注意, 仅检验头部)在传输过程中是否损坏。
  11. 32位的源端IP地址和目的端IP地址用来标识数据报的发送端和接受端。一般情况下,这两个地址在整个数据报的传递过程中保持不变,而不论它中间经过多少个中转路由器。
  12. 可选择字段: IPv4最后一个选项字段(option)是可变长的可选信息。 这部分最多包含40字节,因为IP头部最长是60字节(其中还包含前面讨论的20字节的固定部分)。

可用的IP选项包含:

  1. 记录路由(record route), 告诉数据报途径的所有路由器都将自己的IP地址填入IP头部的选项部分, 这样我们就可用跟踪数据报的传递路径。
  2. 时间戳(timestamp), 告诉每个路由器都将数据报被转发的时间(或者时间与IP地址对)填入IP头部的选项部分,这样就可以测量途径路由之间数据报传输的时间。
  3. 松散源路由选择(loose source routing),指定一个路由器IP地址列表,数据报发送过过程必须经过其中所有的路由器。
  4. 严格源路由选择(strict source routing), 和松散源路由选择类似,不过数据报只能经过被指定的路由器。

抓包分析 开启二进制查看 [-x]

sudo tcpdump -ntx -i lo

IPv4头部各个字段详解

十六进制数 十进制数表示 IP头部信息
0x4 4 IP版本号
0x5 5 头部长度为5个32位(20字节)
0x10   TOS选项中最小延时服务被开启
0x003c 60 数据报总长度,60字节
0x17a9   数据报标识(动态的)
0x4   设置了禁止分片标志
0x000 0 分片偏移
0x40 64 TTL被设为64
0x06 6 协议字段为6,表示上层协议是TCP协议
0x2501   IP头部校验和
0x7f000001   32位源端IP地址127.0.0.1
0x7f000001   32位目的端IP地址127.0.0.1
     

telnet服务选择使用具有最小延时的服务

②,包的偏移量

包工具分析

2,IP分片(ICMP协议案例分析数据)

  当IP数据报的长度超过帧的MTU时,它将被分片传输。分片可能发送在发送端,也可能发生在中转路由器上,而且可能在传输过程中被多次分片,当只有在最终的目标机器上,这些分片才会被内核中的IP模块重新组装。

  IP头部中的三个字段给IP的分片和重组提供了足够的信息,数据报标识,标志和片偏移。一个IP数据报的每个分片都具有自己的IP头部,它们具有相同的标识值,但具有不同的片偏移。并且除了最后一个分片外,其他分片都将设置MF标志。此外,每个分片的IP头部的总长度字段将被设置为该分片的长度。

  以太网帧的MTU1500字节(可以通过ifconfig命令或者netstat命令查看),因此它携带的IP数据报的数据部分最多是1480字节(IP头部占用20字节)。考虑用IP数据报封装一个长度为1481字节的ICMP报文(包括8字节的ICMP头部,所以其数据部分长度为1473字节),则该数据报在使用以太网帧传输时必须被分片,长度为1501字节的IP数据报被拆分成两个IP分片,第一个IP分片长度为1500字节,第二个IP分片的长度为21字节。每个IP分片都包含字节的IP头部(20字节),其第一个IP分片的IP头部设置了MF标志,而第二个IP分片的IP头部则没有设置该标志,因为它已经是最后一个发片了。原始IP数据报中的ICMP头部内容被完整的复制到了第一个IP分片中。第二个IP分片不包含ICMP头部信息,因为IP模块重组该ICMP报文的时候只需要一份ICMP头部信息,重复传送这个信息没有任何益处。1473字节的ICMP报文数据的前1472字节被IP模块复制到第一IP分片中,使其总长度为1500字节,从而满足MTU的要求:而多出的最后1字节则被复制到第二个IP分片中。

  需要指出的是,ICMP报文的头部长度取决于报文的类型,其变化范围很大。上图中以8字节为例,因为后面的例子用到了ping程序,而ping程序使用的ICMP回显和应答报文的头部长度是8字节。   为了看清楚IP分片的具体过程,从129机器到320机器的ping过程,每次传送1473字节的数据(这是ICMP报文的数据部分)以强制引起IP分片,并用tcpdump抓取这一过程中双方交换的数据包。

$ tcpdump -ntv -i ens33 icmp         #只抓取ICMP报文
$ ping 192.168.64.129 -s 1473        # 用-s 选项指定每次发送1473字节的数据

抓取的数据报

  前两个IP分片的标识值都15881,说明它们是同一个IP数据报的分片。第一个分片的片偏移值为0,而第二个则是1480。很显然,第二个分片的片偏移值实际上也是第一个分片的ICMP报文的长度。第一个分片设置了MF标志以表示还有后续分片,所以tcpdump输出”flags[+]”, 而第二个分片则没有设置任何标志,所以tcpdump输出”flags[none]”。 这个两个分片的长度分别为1500字节和21字节。   最后,IP层传递给数据链路层的数据可能是一个完整的IP数据报,也可能是一个IP分片,它们统称为IP分组(packet)。

3,IP路由

  IP协议的一个核心任务是数据报的路由,即决定发送数据报到目标机器的路径。为了理解IP路由过程,我们先简要分析IP模块的基本工作流程。

① ,IP模块工作流程

  我们从右往左来分析,当IP模块收到来自数据链路层的IP数据报时,它首先对该数据报的头部做了CRC校验,确认无误之后就分析其头部的具体信息。   如果该IP数据报的头部设置了源站选路选项(松散源路由选择或严格源路由选择),则IP模块调用数据报转发自模块来处理该数据报。如果该IP数据报的头部中目标IP地址是本机的某个IP地址,或者是广播地址,即该数据报是发送给本机的,则IP模块就根据数据报头部中的协议字段来决定将它派发给你个上层应用(分用)。如果IP模块发现这个数据报标识发送给本机的,则也调用数据报转发自模块来处理该数据报。 IP数据报应该发送至你个下一跳路由(或者目标机器),以及价格哪个网卡来发送,就是IP路由过程,即上图中”计算下一跳路由”子模块。IP模块实现数据报路由的核心数据结构是路由表。这个表按照数据报的目标IP地址分类,同一类型的IP数据报将被发往相同的下一跳路由器(或者目标机器)。   IP输出队列中存放的是所有等待发送的IP数据报,其中除了需要转发的IP数据报外,还包括封装了本机上上层数据(ICMP报文,TCP报文段和UDP数据报)的IP数据报。   上图中的虚线箭头显示了路由表更新的过程。这一过程是指过程路由需要或者route命令调整路由表,使之更适应更新的网络拓扑结构,称为IP路由策略。

② ,路由机制

要研究IP路由机制,需要先了解路由表的内容。我们可以使用route命令或者netstat命令查看路由表。 我阿里云机器查看

$route

该路由表包含三项,每一项都包含8个字段

路由表内容

字段 含义
Destination 目标网络或者主机
Gateway 网关地址,*表示目标和本机在同一个网络,不需要路由
Genmask 网络掩码
Flags 路由项标志,常见标志有如下5中(更多标志见route命令的man手册);U:该路由项是活动的, H: 该路由项的目标是一台主机,G: 该路由项的目标是网关, D: 该路由项是由重定向生成的,M: 该路由项被重定向修改过
Metric 路由距离,即到达重定网络所需的中转数
Ref 路由项被引用的次数(linux未使用)
Use 该路由项被使用的次数
Iface 该路由项对应的输出网卡接口

  第一项的目标地址是default,即所谓的默认路由项。该项包含一个”G”标志,说明路由的下一跳目标是网关,其地址是0.0.0.0(这是阿里云网络中路由器的本地IP地址)。另外一个路由项的目标地址是172.20.128.0,它指的是本地局域网。该路由项的物理地址为0.0.0.0,说明数据报不需要路由中转,可以直接发送到目标机器。   那么路由表是如何按照IP地址分类的呢?或者是给定数据报的目标IP地址,它将匹配路由表中的哪一项呢?这就是IP的路由机制,分为3个步骤:

  1. 查找路由表中和数据报的目标IP地址完全匹配的主机IP地址。如果找到,就使用该路由项,没找到则转步骤2.
  2. 查找路由表中和数据报的目标IP地址具有相同网络ID网络IP地址(上图中路由表中的第二项)。如果找到,就使用该路由项;没有找到则步骤3。
  3. 选择默认路由项,这通常意味着数据报的下一跳路由是网关。
③ ,路由表更新

路由表必须能够更新,以反映网络连接的变化,这样IP模块才能准确,高效地转发数据报,route命令可以修改路由表。我们看几个例子

$ route add -host 192.168.1.109 dev eth0
$ route del -net 192.168.1.0 netmask 255.255.255.0
$ route del default
$ route add default gw 192.168.1.109 dev eth0

  第一行表示添加主机192.168.1.109对应的路由项。这样设置之后,所有从这台机器发送到109的IP数据报将通过网卡eth0 直接发送至目标机器的接收网卡。第2行表示上层网络192.168.1.0对应的路由项。这样,除了机器192外,测试机器将无法访问giant局域网上任何其他机器(能访问到192是由于执行了上一条命令)。第三行表示上层默认路由项,这样做的后果是无法访问因特网。第四行表示重新设置默认路由项,不过这次其网关是机器192(而表示能直接服务器因特网的路由器) 经过上述修改后的路由表

这个新的路由表中,第一个路由项是主机路由项,使用它被设置了”H”标志

  通过route命令或者其他工具手工修改路由表,是具体的路由更新方式。对于大型的路由器,它们通常通过BGP(Border Gateway Protocol, 边际网关协议),RIP(Routing Information Portocol, 路由信息协议),OSPF等协议来发现路径,并更新自己的路由表。这中更新方式是动态的,自动的。

4,IP转发

  前文提到,不是发送给本机的IP数据报将由数据报转发子模块来处理。路由器都你执行数据报的转发操作,而主机一般只发送和接收数据报,这是因为主机上/proc/sys/net/ipv4/ip_forward内核陈松默认被设置为0。我们可以通过相关它来使能主机的数据报转发功能

echo 1 > /proc/sys/net/ipv4/ip_forward

对于IP数据报转发的系统(主机或路由器),数据报转发子模块将对期望转发的数据报执行如下操作:

  1. 检查数据报头部的TTL值,如果TTL值已经是0,则丢弃该数据报。
  2. 查看数据报头部的严格源路由选择选项。如果该选项被设置,则检测数据报的目标IP地址是否是本机的某个IP地址。如果不是,则发送一个ICMP源站选路失败报文给发送端。
  3. 如果有必要,则给源端发送一个ICMP重定向报文,以告诉它一个更合理的下一跳路由器。
  4. 将TTL值减1。
  5. 处理IP头部选项。
  6. 如果有必要,则执行IP分片操作。

5,arp欺骗技术的分析

6, 协议

三, TCP状态转换图

1, TCP状态转换图

虚线:表示服务器的正常状态转换
实线:表示客户端的正常状态转换
应用:表示状态转换在应用进程发起操作时发生
接收:表示状态转换在接收到分节发生
发送:表示这个转换发生什么

2, TCP连接的分组交换

3,CLOSE_WAIT状态出现时机

有一端主动关闭socket链接, 在没有关闭socket链接的一端出现CLOSE_WAIT状态, 主动关闭socket的一端出现了FIN_WAIT_2状态, 在主动关闭socket一端没有收到被动关闭一端的响应会等待73秒后关闭, 而被动关闭有会有大量CLOSE_WAIT状态 的原因是没有关闭socket连接(网络连接无法释放)

①, 服务端有大量CLOSE_WAIT状态, 和客户端FIN_WAIT_2状态的使用

命令

tcpdump tcp port 80

抓包分析

②, 抓包分析结果

客户发送了FIN 服务端回一个ACK就介绍了

这是服务端的测试的代码测试的代码

#include	"unp.h"
#include	<time.h>

int main(int argc, char **argv)
{
	int					listenfd, connfd;
	socklen_t			len;
	struct sockaddr_in	servaddr, cliaddr;
	char				buff[MAXLINE];
	time_t				ticks;
	int 				optval = 1;

	listenfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family      = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port        = htons(SERV_PORT);	/* daytime server */

	Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

	Listen(listenfd, LISTENQ);
	//端口复用
	//端口复用
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

	for ( ; ; ) 
	{
		len = sizeof(cliaddr);
		connfd = Accept(listenfd, (SA *) &cliaddr, &len);
		printf("connection from %s, port %d\n",
			   Inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
			   ntohs(cliaddr.sin_port));

		ticks = time(NULL);
		snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
		//休息 了                就没有关闭服务端出现大量的CLOSE_WAIT状态
		sleep(1000);
		Write(connfd, buff, strlen(buff));

		Close(connfd);
	}
}

客户端的代码

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <strings.h>
#include <unistd.h>
#include <arpa/inet.h>

#define SERV_PORT 8888
#define SERV_IP "47.93.31.88"
#define MAXLINE 1024


int main(int argc, char **argv)
{
	int					sockfd, n;
	char				recvline[MAXLINE + 1];
	struct sockaddr_in	servaddr;


	if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		printf("socket error\n");

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port   = htons(SERV_PORT);	/* daytime server */
	if (inet_pton(AF_INET, SERV_IP, &servaddr.sin_addr) <= 0)
		printf("inet_pton error for %s\n", SERV_IP);

	if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
		printf("connect error\n");

	
	close(sockfd);

#if 0
	while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
		recvline[n] = 0;	/* null terminate */
		if (fputs(recvline, stdout) == EOF)
			printf("fputs error\n");
	}
	if (n < 0)
		printf("read error\n");
#endif 
	exit(0);
}

服务端的CLOSE_WAIT的状态

客户端FIN_WAIT_2状态

4, 解决上面的问题是 出现大量CLOSE_WAIT状态

原因是被动关闭的一端没有关闭socket连接 导致大量CLOSE_WAIT状态

现在就是关闭服务端socket连接

服务端的代码:

#include	"unp.h"
#include	<time.h>

int main(int argc, char **argv)
{
	int					listenfd, connfd;
	socklen_t			len;
	struct sockaddr_in	servaddr, cliaddr;
	char				buff[MAXLINE];
	time_t				ticks;
	int 				optval = 1;

	listenfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family      = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port        = htons(SERV_PORT);	/* daytime server */

	Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

	Listen(listenfd, LISTENQ);
	//端口复用
	//端口复用
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
	//地址复用
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

	for ( ; ; ) 
	{
		len = sizeof(cliaddr);
		connfd = Accept(listenfd, (SA *) &cliaddr, &len);
		printf("connection from %s, port %d\n",
			   Inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
			   ntohs(cliaddr.sin_port));

		ticks = time(NULL);
		snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
		//sleep(1000);
		Write(connfd, buff, strlen(buff));

		Close(connfd);
	}
}

效果图:

结语