to myself 的分类学习日志

做自己想做的事
posts - 232, comments - 6, trackbacks - 0, articles - 0

学习《TCP/IP详解》

Posted on 2010-08-19 10:27 kongkongzi 阅读(1696) 评论(0)  编辑 收藏 引用 所属分类: network programming

  TCP/IP详解

(在第21章我们将看到TCP的超时和重发算法的细节)

本书第1722章将详细讨论TCP的内部操作细节。然后,我们将介绍一些TCP的应用,如第26章中的TelnetRlogin、第27章中的FTP以及第28章中的SMTP等。这些应用通常都是用户进程。

本书第11章将讨论UDP,然后在第14章(DNS:域名系统),第15章(TFTP:简单文件传送协议),以及第16章(BOOTP:引导程序协议)介绍使用UDP的应用程序。SNMP也使用了UDP协议,但是由于它还要处理许多其他的协议,因此本书把它留到第25章再进行讨论。

2010-6-8 概述、链路层、IPARPRARP

分层:
         

1)       链路层,有时也称作数据链路层或网络接口层,通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡。它们一起处理与电缆(或其他任何传输媒介)的物理接口细节。

2)       网络层,有时也称作互联网层,处理分组在网络中的活动,例如分组的选路。在TCP/IP协议族中,网络层协议包括IP协议(网际协议),ICMP协议(Internet互联网控制报文协议),以及IGMP协议(Internet组管理协议)。

3)       运输层主要为两台主机上的应用程序提供端到端的通信。在TCP/IP协议族中,有两个互不相同的传输协议: TCP(传输控制协议)和UDP(用户数据报协议)。

TCP为两台主机提供高可靠性的数据通信。它所做的工作包括把应用程序交给它的数据分成合适的小块交给下面的网络层,确认接收到的分组,设置发送最后确认分组的超时时钟等。由于运输层提供了高可靠性的端到端的通信,因此应用层可以忽略所有这些细节。

而另一方面, UDP则为应用层提供一种非常简单的服务。它只是把称作数据报的分组从一台主机发送到另一台主机,但并不保证该数据报能到达另一端。任何必需的可靠性必须由应用层来提供。

4)       应用层负责处理特定的应用程序细节。TelnetFTPSMTPSNMP

 

以太网驱动程序、令牌环驱动程序

网桥是在链路层上对网络进行互连,而路由器则是在网络层上对网络进行互连。网桥使得多个局域网(LAN)组合在一起,这样对上层来说就好像是一个局域网。

有三类I P地址:单播地址(目的为单个主机)、广播地址(目的端为给定网络上的所有主机)以及多播地址(目的端为同一组内的所有主机)。

典型以太网帧首部的字节长度:(UDP的首部长为8字节)


由于TCPUDPICMPIGMP都要向IP传送数据,因此IP必须在生成的IP首部中加入某种标识,以表明数据属于哪一层。

网络接口分别要发送和接收IPARPRARP数据,因此也必须在以太网的帧首部中加入某种形式的标识,以指明生成数据的网络层协议。为此,以太网的帧首部也有一个16 bit的帧类型域。

 

链路层:

     TCP/IP支持多种不同的链路层协议,这取决于网络所使用的硬件,如以太网、令牌环网、FDDI(光纤分布式数据接口)及RS-232串行线路等。

以太网链路层协议,两个串行接口链路层协议(SLIPPPP),以及大多数实现都包含的环回(loopback)驱动程序。

以太网:

     它是当今TCP/IP采用的主要的局域网技术。它采用一种称作CSMA/CD的媒体接入方法,其意思是带冲突检测的载波侦听多路接入Carrier Sense, Multiple Access with Collision Detection)。它的速率为10 Mb/s,地址为48 bit

以太网首部:

    

     这里的目的地址和源地址都是Mac地址。

     类型:对于IP数据,该值为0800;对于ARP数据,该值为0806;对于RARP数据,该值为8035

以太网尾部:

     CRC字段,用于帧内后续字节差错的循环冗余码检验(检验和)(它也被称为FCS或帧检验序列)。

SLIP:

     全称是Serial Line IP。它是一种在串行线路上对IP数据报进行封装的简单形式, SLIP适用于家庭中每台计算机几乎都有的RS-232串行端口和高速调制解调器接入Internet

PPP: 
      

     协议字段: 当它的值为0x0021时,表示信息字段是一个IP数据报;值为0xc021时,表示信息字段是链路控制数据;值为0x8021时,表示信息字段是网络控制数据。

     由于标志字符的值是0x7e,因此当该字符出现在信息字段中时, PPP需要对它进行转义。在异步链路中,特殊字符0x7d用作转义字符。当它出现在PPP数据帧中时,那么紧接着的字符的第6个比特要取其补码。

MTU:

     不同类型的网络会有不同的MTU值。

     路径MTU的发现机制,即在任何时候确定路径MTU的方法。

IP:

          

//定义IP首部
typedef struct _iphdr{
    unsigned 
char h_lenver; //4 位IP版本号+4位首部长度
    unsigned char tos; //8 位服务类型TOS
    unsigned short total_len; //16 位IP包总长度(字节)
    unsigned short ident; //1 6位标识, 用于辅助IP包的拆装,本实验不用,置零
    unsigned short frag_and_flags; //3 位标志位+13位偏移位, 也是用于IP包的拆装,本实验不用,置零
    unsigned char ttl; //8 位IP包生存时间 TTL
    unsigned char proto; //8 位协议 (TCP, UDP 或其他), 本实验置ICMP,置为1
    unsigned short checksum; //16 位IP首部校验和,最初置零,等所有包头都填写正确后,计算并替换.
    unsigned int sourceIP; //32 位源IP地址
    unsigned int destIP; //32 位目的IP地址
}IP_HEADER;

     总长度字段是指整个IP数据报的长度,以字节为单位。利用首部长度字段和总长度字段,就可以知道IP数据报中数据内容的起始位置和长度。

     为了计算一份数据报的IP检验和,首先把检验和字段置为0。然后,对首部中每个16 bit进行二进制反码求和(整个首部看成是由一串16 bit的字组成),结果存在检验和字段中。当收到一份IP数据报后,同样对首部中每个16 bit进行二进制反码的求和。由于接收方在计算过程中包含了发送方存在首部中的检验和,因此,如果首部在传输过程中没有发生任何差错,那么接收方计算的结果应该为全1。如果结果不是全1(即检验和错误),那么IP就丢弃收到的数据报。但是不生成差错报文,由上层去发现丢失的数据报并进行重传。

     以太网地址一般通过ARP获得。

     子网对于子网内部的路由器是不透明的。

ARP:

地址解析为这两种不同的地址形式提供映射:32bitIP地址和数据链路层使用的任何类型的地址。

ARP发送一份称作ARP请求的以太网数据帧给以太网上的每个主机。ARP请求数据帧中包含目的主机的IP地址,其意思是“如果你是这个IP地址的拥有者,请回答你的硬件地址。” 目的主机的ARP层收到这份广播报文后,识别出这是发送端在寻问它的IP地址,于是发送一个ARP应答。这个ARP应答包含IP地址及对应的硬件地址。

ARP的功能是在32 bitIP地址和采用不同网络技术的硬件地址之间提供动态映射。

     

     帧类型:对于ARP请求或应答来说,该字段的值为0x0806

     硬件类型:它的值为1即表示以太网地址。

     协议类型:它的值为0x0800即表示IP地址。

     硬件地址长度:对于以太网上IP地址的ARP请求或应答来说,它的值为6

     协议地址长度:对于以太网上IP地址的ARP请求或应答来说,它的值为4

     操作(op)ARP请求(值为1)、ARP应答(值为2)、RARP请求(值为3)和RARP应答(值为4)。

RARP:

     RARP协议是许多无盘系统在引导时用来获取IP地址的。

     RARP请求或应答的帧类型代码为0x8035

     RARP服务程序。

     RARP服务器的设计与系统相关而且比较复杂。RARP服务器的功能就由用户进程来提供,

而不是作为内核的TCP/IP实现的一部分。

ARP服务器很简单,通常是TCP/IP在内核中实现的一部分。由于内核知道I P地址和硬件地址,因此当它收到一个询问I P地址的ARP请求时,只需用相应的硬件地址来提供应答就可以了。

ICMP:

     
      

//定义ICMP首部
typedef struct _icmphdr{
    unsigned 
char i_type; //8 位类型, 本实验用 8: ECHO 0:ECHO REPLY
    unsigned char i_code; //8 位代码, 本实验置零
    unsigned short i_cksum; //16位校验和, 从TYPE开始,直到最后一位用户数据,如果为字节数为奇数则补充一位
    unsigned short i_id ; // 识别号(一般用进程号作为识别号), 用于匹配ECHO和ECHO REPLY包
    unsigned short i_seq ; // 报文序列号, 用于标记ECHO报文顺序
    unsigned int i_data; //不同类型和代码有不同的内容: type==0时为时间戳timestamp;
}ICMP_HEADER;

     不同类型由报文中的类型字段和代码字段来共同决定。

     ICMP地址掩码请求用于无盘系统在引导过程中获取自己的子网掩码:

     

     ICMP时间戳请求允许系统向另一个系统查询当前的时间。返回的建议值是自午夜开始计算的毫秒数,协调的统一时间(UTC):(这种ICMP报文的好处是它提供了毫秒级的分辨率)

      

     发起时间戳(orig)、接收时间戳(recv)以及发送时间戳(xmit),往返时间(rtt)。

ICMP不可达报文:

UDP的规则之一是,如果收到一份UDP数据报而目的端口与某个正在使用的进程不相符,那么UDP返回一个ICMP不可达报文。

ICMP的一个规则是, ICMP差错报文必须包括生成该差错报文的数据报IP首部(包含任何选项),还必须至少包括跟在该IP首部后面的前8个字节。

         

采用一种指数退避方法来设置超时值,分别在051535秒时重发报文,这正是所推荐的方法。

netstat -s 是查看每个协议统计数据的常用方法。

Ping程序:

     几年前我们还可以作出这样没有限定的断言,如果不能Ping到某台主机,那么就不能Telne tFTP到那台主机。随着Internet安全意识的增强,出现了提供访问控制清单的路由器和防火墙,那么像这样没有限定的断言就不再成立了。一台主机的可达性可能不只取决于IP层是否可达,还取决于使用何种协议以及端口号。Ping程序的运行结果可能显示某台主机不可达,但我们可以用Telnet远程登录到该台主机的25号端口(邮件服务器)。

     大多数的TCP/IP实现都在内核中直接支持Ping服务器—这种服务器不是一个用户进程。

     地址掩码和时间戳请求,也都是直接在内核中进行处理的。

     IP记录路由选项。IP时间戳选项。

     提供-r选项,以提供记录路由的功能。它使得ping程序在发送出去的IP数据报中设置IP RR(记录路由)选项(该IP数据报包含ICMP回显请求报文)。这样,每个处理该数据报的路由器都把它的IP地址放入选项字段中。当数据报到达目的端时,IP地址清单应该复制到ICMP回显应答中,这样返回途中所经过的路由器地址也被加入清单中。当ping程序收到回显应答时,它就打印出这份IP地址清单。(RFC 791指定路由器记录出口IP地址。

Traceroute程序:Windows下为tracert

     开始时发送一个TTL字段为1UDP数据报,然后将TTL字段每次加1,以确定路径中的每个路由器。每个路由器在丢弃UDP数据报时都返回一个ICMP超时报文2,而最终目的主机则产生一个ICMP端口不可达的报文。

     Traceroute程序使用ICMP报文和IP首部中的TTL字段(生存周期)。

     每个处理数据报的路由器都需要把TTL的值减1

     Traceroute程序发送一份UDP数据报给目的主机,但它选择一个不可能的值作为UDP端口号(大于30000),使目的主机的任何一个应用程序都不可能使用该端口。因为,当该数据报到达时,将使目的主机的UDP模块产生一份“端口不可达”错误的ICMP报文。这样,Traceroute程序所要做的就是区分接收到的ICMP报文是超时还是端口不可达,以判断什么时候结束。

     40字节的数据报包含20字节IP首部、8字节的UDP首部和12字节的用户数据(12字节的用户数据包含每发一个数据报就加1的序列号,送出TTL的副本以及发送数据报的时间)。

     对于每个TTL值,发送3份数据报。每接收到一份ICMP报文,就计算并打印出往返时间。如果在5秒种内仍未收到3份数据报的任意一份的响应,则打印一个星号,并发送下一份数据报。

     每个打印出来的RTT值是从发送主机到路由器的总时间。

     目的主机UDP端口号最开始设置为33435,且每发送一个数据报加1。可以通过命令行选项来改变开始的端口号。

     返回的ICMP报文中的信源IP地址是UDP数据报到达的路由器接口的IP地址。这与IP记录路由选项不同,记录的IP地址指的是发送接口地址。

TCP:
    
17   TCP:传输控制协议

     TCP的介绍将由本章开始,并一直包括随后的7章。第18章描述如何建立和终止一个TCP连接,第19和第20章将了解正常的数据传输过程,包括交互使用(远程登录)和批量数据传送(文件传输)。第21章提供TCP超时及重传的技术细节,第22和第23章将介绍两种其他的定时器。最后,第24章概述TCP新的特性以及TCP的性能。

   

//定义TCP首部
typedef struct tcp_hdr 
{
    USHORT th_sport; 
//16位源端口
    USHORT th_dport; //16位目的端口
    unsigned int th_seq; //32位序列号
    unsigned int th_ack; //32位确认号
    unsigned char th_lenres; //4位首部长度/6位保留字
    unsigned char th_flag; //6位标志位
    USHORT th_win; //16位窗口大小
    USHORT th_sum; //16位校验和
    USHORT th_urp; //16位紧急数据偏移量
}TCP_HEADER; 

每个TCP段都包含源端和目的端的端口号,用于寻找发端和收端应用进程。这两个值加上IP首部中的源端IP地址和目的端IP地址唯一确定一个TCP连接。有时,一个IP地址和一个端口号也称为一个插口(socket)。插口对(socket  pair(包含客户IP地址、客户端口号、服务器IP地址和服务器端口号的四元组)可唯一确定互联网络中每个TCP连接的双方。

URG 紧急指针(urgent pointer)有效。

ACK 确认序号有效。

PSH 接收方应该尽快将这个报文段交给应用层。

RST 重建连接。

SYN 同步序号用来发起一个连接。

FIN 发端完成发送任务。

     TCP的流量控制由连接的每一端通过声明的窗口大小来提供。

    TCP检验和的计算和UDP检验和的计算相似,使用如11.3节所述的一个伪首部。(???)

    选项:

MSS (Maximum Segment Size),最长报文大小,每个连接方通常都在通信的第一个报文段(为建立连接而设置S Y N标志的那个段)中指明这个选项。它指明本端所能接收的最大长度的报文段。



IPv4头和TCP头校验和计算算法

IP首部校验和的计算方法:

◆当发送IP包时,需要计算IP报头的校验和:
1、把校验和字段置为0;
2、对IP头部中的每16bit进行二进制求和;
3、如果和的高16bit不为0,则将和的高16bit和低16bit反复相加,直到和的高16bit为0,从而获得一个16bit的值;
4、将该16bit的值取反,存入校验和字段。

◆当接收IP包时,需要对报头进行确认,检查IP头是否有误,算法同上2、3步,然后判断取反的结果是否为0,是则正确,否则有错。

 
算法:

SHORT checksum(USHORT* buffer, int size)
{
    unsigned 
long cksum = 0;

    
//step 2    
    while (size > 1)
    {
        cksum 
+= *buffer++;
        size 
-= sizeof(USHORT);
    }
    
if (size)
    {
        cksum 
+= *(UCHAR*)buffer;
    }
    
    
//step 3
    cksum = (cksum >> 16+ (cksum & 0xffff);  //将高16bit与低16bit相加
    cksum += (cksum >> 16);  //将进位到高位的16bit与低16bit 再相加    
    
    
//step 4
    return (USHORT)(~cksum);
}

实例:

IP头: 

              45 00    00 31

              89 F5    00 00

              6E 06    00 00(校验字段)

              DE B7   45 5D       ->    222.183.69.93

              C0 A8   00 DC     ->    192.168.0.220

计算:   

    4500 + 0031 +89F5 + 0000 + 6e06+ 0000 + DEB7 + 455D + C0A8 + 00DC =3 22C4

    0003 + 22C4 = 22C7

     ~22C7 = DD38      ->即为应填充的校验和

当接受到IP数据包时,要检查IP头是否正确,则对IP头进行检验,方法同上:

计算:

    4500 + 0031 +89F5 + 0000 + 6E06+ DD38 + DEB7 + 455D + C0A8 + 00DC =3 FFFC

    0003 + FFFC = FFFF

     ~FFFF = 00000     ->正确

 

TCP首部检验和与IP首部校验和的计算方法相同,在程序中使用同一个函数来计算。

需要注意的是,由于TCP首部中不包含源地址与目标地址等信息,为了保证TCP校验的有效性,在进行TCP校验和的计算时,需要增加一个TCP伪首部的校验和,定义如下:

struct 
{
    unsigned 
long saddr; //源地址
    unsigned long daddr; //目的地址
    char mbz; //置空
    char ptcl; //协议类型
    unsigned short tcpl; //TCP长度
} psd_header;

然后我们将这两个字段复制到同一个缓冲区SendBuf中并计算TCP校验和:

memcpy(SendBuf, &psd_header, sizeof(psd_header)); 

memcpy(SendBuf 
+ sizeof(psd_header), &tcp_header, sizeof(tcp_header));

tcp_header.th_sum 
= checksum((USHORT *)SendBuf, sizeof(psd_header) + sizeof(tcp_header));