(Linux 高性能服务器) – TCP/IP 协议

TCP/IP 协议

TCP/IP 协议族体系结构以及主要的协议:

数据链路层

实现了网卡接口的网络驱动程序,以及处理数据在物理媒介上的传输。网络驱动程序隐藏了这些细节为上层的协议提供了统一的接口。数据链路常用的协议是 ARP(地址解析协议) 和 RARP(逆向地址解析协议)。他们实现了 IP 地址和机器物理地址(通常是 MAC地址)之间的转换。

ARP 的用途:网络层使用 IP地址 寻址一台服务器,而数据链路层使用物理地址寻址一台机器,因此网络层必须先将目标机器的 IP 地址转换成物理地址,才能使用数据链路层提供的服务。

RARP 的用途:网络上的无盘工作站

网络层

网络层提供路由和寻址的功能,使两终端系统能够互连且决定最佳路径,并具有一定的拥塞控制和流量控制的能力。TCP/IP协议体系中的网络层功能由IP协议规定和实现。同时网络层对上层协议隐藏了网络拓扑连接的细节,使得在传输层和网络层的应用程序看来,通信双方是直接相连的。

  • IP (英特网协议):根据数据包的目的 IP地址 来决定如何投递它。如果数据包不能 直接发送给目标主机,那么IP协议就为它寻找一个合适的下一跳(next hop)路由器,并将数据包交付给该路由器来转发。多次重复这一过程,数据包最终到达目标主机,或者由于发送失败而被丢弃。可见,IP协议使用逐跳(hop by hop)的方式确定通信路径。
  • ICMP(英特网控制报文协议):通常用于返回的错误信息或是分析路由。ICMP错误消息总是包括了源数据并返回给发送者。 ICMP错误消息的例子之一是TTL值过期。每个路由器在转发数据报的时候都会把IP包头中的TTL(存活时间,指一个数据包在经过一个网络时,可传递的最长距离(跃点数)。每当数据包经过一个路由器时,其存活次数就会被减一。当其存活次数为0时,路由器便会取消数据包并发送一个ICMP TTL数据包给原数据包的发出者。)值减1。如果TTL值为0,“TTL在传输中过期”的消息将会回报给源地址。 每个ICMP消息都是直接封裝在一个IP数据包中的,因此,和UDP一样,ICMP是不可靠的。

ICMP 报文的格式:

ICMP 中的类型和代码:

由于 ICMP 是使用同一层的 IP 提供服务,所以 ICMP 并非完整意义上的网络层协议。

传输层

网络层和传输层的区别

  • TCP:为应用层提供可靠的、面向连接的和基于流(stream)的服务。TCP协议使用超时重传、数据确认等方式来确保数据包被正确地发送至目的端,因此TCP服务是可靠的。
  • UDP:与TCP协议完全相反,它为应用层提供不可靠、无连接和基于数据报的服务。“不可靠”意味着UDP协议无法保证数据从发送端正确地传送到目的端。如果数据在中途丢失,或者目的端通过数据校验发现数据错误而将其丢弃,则UDP协议只是简单地通知应用程序发送失败。
  • SCTP(流控制协议)

应用层

应用层负责处理应用程序的逻辑。数据链路层、网络层和传输层负责处理网络通信细节,这部分必须既稳定又高效,因此它们都在内核空间中实现。如果服务器程序在内核实现,那么无须在用户空间与内核空间来回切换,这样效率就会高一些,不过实现起来也很复杂也不方便移植。

应用程序/协议 作用
ping 应用 利用 ICMP 检测网络连接
telnet 远程登录协议
OSPF(开放最短路径优先) 用于路由器之间的同学以告知对方各自的路由信息
DNS(域名服务) 协议提供机器域名到IP地址的转换

应用程序也可以跳过传输层使用网络层的服务,例如 ping 程序使用 OSPF 等。具体可以查看 /etc/services 查看所有知名的应用层协议以及他们使用哪些传输服务。

封装

通过封装(encapsulation)实现上层协议使用下层协议。应用程序数据在发送到物理网络上之前,将沿着协议栈从上往下依次传递。每层协议都将在上层数据的基础上加上自己的头部信息(有时还包括尾部信息),以实现该层的功能,这个过程就称为封装。

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

UDP 对应用程序数据的封装与 TCP 类似。不同的是,UDP无须为应用层数据保存副本,因为它提供的服务是不可靠的。当一个UDP数据报被成功发送之后,UDP内核缓冲区中的该数据报就被丢弃了。如果应用程序检测到该数据报未能被接收端正确接收,并打算重发这个数据报,则应用程序需要重新从用户空间将该数据报拷贝到UDP内核发送缓冲区中。

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

以太网封装。传输媒介不同,帧的类型也不同。比如,以太网上传输的是以太网帧(ethernet frame),而令牌环网络上传输的则是令牌环帧(token ring frame)。

图1.6:以太网帧的封装:

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

帧的最大传输单元(Max Transmit Unit,MTU),即帧最多能携带多少上层协议数据(比如IP数据报),通常受到网络类型的限制。单位是字节

帧才是最终在物理网络上传送的字节序列。封装的过程最终完成。

分用

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

以以太网帧为例:帧中的 2 个字节来标志上层的协议:

帧部分的数据类型
0x800 IP 数据报
0x806 ARP 请求或者应答报文
0x835 RARP 请求或者应答报文

因为 ICMP、TCP 和 UDP 都采用了 IP,所以 IP数据包的头部采用16位的协议字段来区分。

TCP报文和 UDP数据报 则通过头部的16位端口号字段来区分上层的应用程序。例如 DNS 对应端口 53。

帧通过上述分用步骤后,最终将封装前的原始数据送至目标服务。

ARP 的工作原理

ARP协议能实现任意网络层地址到任意物理地址的转换。主机向自己所在的网络广播一个ARP请求,该请求包含目标机器的网络地址。此网络上的其他机器都将收到这个请求,但只有被请求的目标机器会回应一个ARP应答,其中包含自己的物理地址。

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

ARP 请求/应答报文长度 28字节,加上以太网的头尾18字节,则携带一个 ARP 请求/应答报文的以太网帧为 46字节。具体的大小(有的实现需要填充字节以满足条件)不确定。

查看某一时刻对方的 ARP 缓存(物理机和虚拟机通过桥接模式连接,同时处于 192.168.1.100 的子网下)

Linux 和 Windows 系统使用 arp -a

ARP 运作过程

由于我的一台机器是 Windows,所以使用的是 Windows 平台的 tcpdump。使用 tcpdump -D 显示所有的设备。

测试环境:

以 DESKTOP-4KGFP68 连接 localhost.localdomain 的 echo 服务,在 DESKTOP-4KGFP68 的终端上输入抓包的监听命令

sudo arp -d 192.168.100.102  #清除ARP缓存中Kongming20对应的项
sudo tcpdump -i eth0 -ent  # (-e, -n, -t 见 tcpdump 的说明))
# 新的终端
telnet 192.168.100.102

在执行telnet命令之前,应先清除ARP缓存中与 localhost.localdomain 对应的项,否则ARP通信不被执行,我们也就无法抓取到期望的以太网帧。当执行 telnet 命令并在两台通信主机之间建立TCP连接后(telnet 输出“Connected to 192.168.1.102”),输入Ctrl+]以调出 telnet 程序的命令提示符,然后在telnet命令提示符后输入quit,退出 telnet 客户端程序(因为 ARP 通信在 TCP 连接建立之前就已经完成,故我们不关心后续内容)。

最终捕获的内容:

第一个数据包 > 之后是目标,DESKTOP-4KGFP68 的网卡发送的目标是 ff:ff:ff:ff:ff:ff 这是以太网的广播地址,用以表示整个LAN。该LAN上的所有机器都会收到并处理这样的帧。数值 0x806 是以太网帧头部的类型字段的值,它表示分用的目标是ARP模块。该以太网帧的长度为42字节(实际上是46字节,tcpdump 未统计以太网帧尾部4字节的CRC字段),其中数据部分长度为28字节。“Request”表示这是一个ARP请求,who-has 192.168.100.102 tell 192.168.100.101 则表示是DESKTOP-4KGFP68 要查询 localhost.localdomain 的IP地址。

第二个数据包 ARP通信的源端的物理地址是 00:0c:29:c4:5d:bf(localhost.localdomain),目的端的物理地址是 02:00:4c:4f:4f:50(DESKTOP-4KGFP68)。“Reply”表示这是一个ARP应答,Reply 192.168.100.102 is-at 00:0c:29:c4:5d:bf 则表示目标机器 localhost.localdomain 报告其物理地址。该以太网帧的长度为60字节(实际上是64字节),可见它使用了填充字节来满足最小帧长度。

关于该图,需要说明三点:

  1. 我们将两次传输的以太网帧按照图1-6所描述的以太网帧封装格式绘制在图的下半部分。
  2. ARP请求和应答是从以太网驱动程序发出的,而并非像图中描述的那样从ARP模块直接发送到以太网上,所以我们将它们用虚线表示,这主要是为了体现携带ARP数据的以太网帧和其他以太网帧(比如携带IP数据报的以太网帧)的区别。
  3. 路由器也将接收到以太网帧1,因为该帧是一个广播帧。不过很显然,路由器并没有回应其中的ARP请求,正如前文讨论的那样。

DNS 协议

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

域名服务器由高层次到低层次划分:

  • 根域名服务器
  • 顶级域名服务器:例如查询 “ddayzzz.wang”,根域名服务器把查询的结果也就是 “wang” 顶级域名服务器的 IP地址 返回给查询者。“wang”顶级域名服务器会找到 “ddayzzz.wang”的域名服务器的记录,域名服务器检查其区域文件,并发现它有与 “ddayzzz.wang” 相关联的区域文件。在此文件的内部,有该主机的记录。此记录说明此主机所在的 IP 地址,并向请求者返回最终答案。
  • 权限域名服务器:负责一个区的域名解析工作
  • 本地域名服务器:当一个主机发出DNS查询请求的时候,这个查询请求首先就是发给本地域名服务器的。

通过 Wireshark 的图形化界面,我们可以轻松分析网卡中收发的数据包。

大致的解析过程:

  1. 主机 192.168.1.122 先向本地域名服务器 192.168.1.1 (网关的 IP地址,路由会把发往他的请求转发到路由器中设置的 DNS地址上去)进行递归查询
  2. 本地域名服务器采用迭代查询,向一个根域名服务器进行查询
  3. 根域名服务器告诉本地域名服务器,下一次应该查询的顶级域名服务器 wang 的IP地址
  4. 本地域名服务器向顶级域名服务器 dns.wang 进行查询
  5. 顶级域名服务器 wang 告诉本地域名服务器,下一步查询权限服务器 dns.ddayzzz.wang 的IP地址
  6. 本地域名服务器向权限服务器 dns.ddayzzz.me进行查询
  7. 权限服务器 dns.ddayzzz.me 告诉本地域名服务器所查询的主机的IP地址
  8. 本地域名服务器最后把查询结果告诉 192.168.1.122

其中:

  • 递归查询:本机向本地域名服务器发出一次查询请求,就静待最终的结果。如果本地域名服务器无法解析,自己会以DNS客户机的身份向其它域名服务器查询,直到得到最终的IP地址告诉本机
  • 迭代查询:本地域名服务器向根域名服务器查询,根域名服务器告诉它下一步到哪里去查询,然后它再去查,每次它都是以客户机的身份去各个服务器查询。

DNS协议的报文格式:

头部

  • 会话标识(2字节):是DNS报文的ID标识,对于请求报文和其对应的应答报文,这个字段是相同的,通过它可以区分DNS应答报文是哪个请求的响应
  • 标志(2字节):
标志 含义
QR(1bit) 查询/响应标志,0为查询,1为响应
opcode(4bit) 0表示标准查询,1表示反向查询,2表示服务器状态请求
AA(1bit) 表示授权回答
TC(1bit) 表示可截断的
RD(1bit) 表示期望递归
RA(1bit) 表示可用递归
rcode(4bit) 表示返回码,0表示没有差错,3表示名字差错,2表示服务器错误(Server Failure)
  • 数量字段(总共8字节):Questions、Answer RRs、Authority RRs、Additional RRs 各自表示后面的四个区域的数目。Questions表示查询问题区域节的数量,Answers表示回答区域的数量,Authoritative namesversers表示授权区域的数量,Additional recoreds表示附加区域的数量

正文

Queries 区域

  1. 查询名:长度不固定,且不使用填充字节,一般该字段表示的就是需要查询的域名(如果是反向查询,则为IP,反向查询即由IP地址反查域名),一般的格式是“长度+字符”。

Queries 的起始位置是 DNS的数据(数据包 + 0x2a) + 96 位(12字节,也就是 0xc) = 0x36,也就是说在数据包的 0x36 位置。请求的URL:

ddayzzz-blog.oss-cn-shenzhen.aliyuncs.com

以及 0x36 的报文:

0030   00 00 00 00 00 00 0c 64 64 61 79 7a 7a 7a 2d 62
0040   6c 6f 67 0f 6f 73 73 2d 63 6e 2d 73 68 65 6e 7a
0050   68 65 6e 08 61 6c 69 79 75 6e 63 73 03 63 6f 6d
0060   00 00 01 00 01

也就是按照小端顺存储的“长度+ 相同数量的 ASCII”,‘.’ 用来风格不同级别的主机。 12(0xc) d d a y z z z - b l o g,后面的可以依此类推。

  1. 查询类型:
类型 助记符 说明
1 A 由域名获得IPv4地址
2 NS 查询域名服务器
5 CNAME 查询规范名称
6 SOA 开始授权
11 WKS 熟知服务
12 PTR 把IP地址转换成域名
13 HINFO 主机信息
15 MX 邮件交换
28 AAAA 由域名获得IPv6地址
252 AXFR 传送整个区的请求
255 ANY 对所有记录的请求
  1. 查询类:通常为1,表明是Internet数据

资源记录(RR)区域(包括回答区域,授权区域和附加区域)

该区域有三个,但格式都是一样的。这三个区域分别是:回答区域,授权区域和附加区域。

  1. 域名(2字节或不定长):它的格式和Queries区域的查询名字字段是一样的。有一点不同就是,当报文中域名重复出现的时候,该字段使用2个字节的偏移指针来表示。比如,在资源记录中,域名通常是查询问题部分的域名的重复,因此用2字节的指针来表示,具体格式是最前面的两个高位是 11,用于识别指针。其余的14位从DNS报文的开始处计数(从0开始),指出该报文中的相应字节数。一个典型的例子,C00C(1100000000001100,12正好是头部的长度,其正好指向Queries区域的查询名字字段)。例如在应答中:。指向了 0x2a + 0x0c = 0x36 就是 Queries 的位置了。是不是很省?
  2. 查询类型:表明资源纪录的类型,见查询类型表格。
  3. 查询类:对于Internet信息,总是IN
  4. 生存时间(TTL):以秒为单位,表示的是资源记录的生命周期,一般用于当地址解析程序取出资源记录后决定保存及使用缓存数据的时间,它同时也可以表明该资源记录的稳定程度,极为稳定的信息会被分配一个很大的值(比如86400,这是一天的秒数)。
  5. 资源数据:该字段是一个可变长字段,表示按照查询段的要求返回的相关资源记录的数据。可以是Address(表明查询报文想要的回应是一个IP地址)或者CNAME(表明查询报文想要的回应是一个规范主机名)等。

Wireshark 分析

请求报文:

应答报文:

参考&引用