RPF
逆向路径转发
的基本思想是:当多播数据报到达多播路由器时,路由器作RPF检查,以决定是否转发或抛弃该数据报。RPF检查过程如下:检查数据报的源地址,以确定该数据报经过的接口是否在从源到此路由器的最短路径上,若是,则RPF检查成功,转发该数据报到此路由器的输出接口表的所有接口,否则RPF检查失败抛弃该数据报。
ICMP
ICMP协议是IP协议的补充,用于IP层的差错报告、拥塞控制、路径控制以及路由器或主机信息的获取。ICMP与IP协议位于同一个层次(IP层),但ICMP报文是封装在IP数据报的数据部分进行传输的。
ICMP报文由首部和数据段组成。首部为定长的8个字节,前4个字节是通用部分,后4个字节随报文类型的不同有所差异。
ICMP一般报文格式
类型1 | 代码1 | 校验和2 |
首部其他部分4 | ||
数据 |
ICMP报文类型值对应报文
类型值 | 报文类型 |
---|---|
3 | 信宿不可达 |
4 | 源端抑制 |
5 | 重定向 |
11 | 超时 |
12 | 参数错误 |
13/14 | 时间戳请求和应答 |
8/0 | 回应请求和应答 |
17/18 | 地址掩码请求和应答 |
10/9 | 路由器询问和通告 |
相同类型值的不同代码值也决定不同的功能
ARP
ARP 协议包(ARP 报文)主要分为 ARP 请求包和 ARP 响应包。
ARP报文格式
每个字段的含义如下。
- 硬件类型:指明了发送方想知道的硬件接口类型,以太网的值为 1。
- 协议类型:表示要映射的协议地址类型。它的值为 0x0800,表示 IP 地址。
- 硬件地址长度和协议长度:分别指出硬件地址和协议的长度,以字节为单位。对于以太网上 IP 地址的ARP请求或应答来说,它们的值分别为 6 和 4。
- 操作类型:用来表示这个报文的类型,ARP 请求为 1,ARP 响应为 2,RARP 请求为 3,RARP 响应为 4。
- 发送方 MAC 地址:发送方设备的硬件地址。
- 发送方 IP 地址:发送方设备的 IP 地址。
- 目标 MAC 地址:接收方设备的硬件地址。
- 目标 IP 地址:接收方设备的IP地址。
IP
IP数据报格式
IP 数据报文由首部(称为报头)和数据两部分组成。首部的前一部分是固定长度,共 20 字节,是所有 IP 数据报必须具有的。在首部的固定部分的后面是一些可选字段,其长度是可变的。
IP 报头的最小长度为 20 字节,上图中每个字段的含义如下:
-
版本(version)
占 4 位,表示 IP 协议的版本。通信双方使用的 IP 协议版本必须一致。目前广泛使用的IP协议版本号为 4,即 IPv4。
-
首部长度(网际报头长度IHL)
占 4 位,可表示的最大十进制数值是 15。这个字段所表示数的单位是 32 位字长(1 个 32 位字长是 4 字节)。因此,当 IP 的首部长度为 1111 时(即十进制的 15),首部长度就达到 60 字节。当 IP 分组的首部长度不是 4 字节的整数倍时,必须利用最后的填充字段加以填充。
数据部分永远在 4 字节的整数倍开始,这样在实现 IP 协议时较为方便。首部长度限制为 60 字节的缺点是,长度有时可能不够用,之所以限制长度为 60 字节,是希望用户尽量减少开销。最常用的首部长度就是 20 字节(即首部长度为 0101),这时不使用任何选项。
-
区分服务(tos)
也被称为服务类型,占 8 位,用来获得更好的服务。这个字段在旧标准中叫做服务类型,但实际上一直没有被使用过。1998 年 IETF 把这个字段改名为区分服务(Differentiated Services,DS)。只有在使用区分服务时,这个字段才起作用。
-
总长度(totlen)
首部和数据之和,单位为字节。总长度字段为 16 位,因此数据报的最大长度为 2^16-1=65535 字节。
-
标识(identification)
用来标识数据报,占 16 位。IP 协议在存储器中维持一个计数器。每产生一个数据报,计数器就加 1,并将此值赋给标识字段。当数据报的长度超过网络的 MTU,而必须分片时,这个标识字段的值就被复制到所有的数据报的标识字段中。具有相同的标识字段值的分片报文会被重组成原来的数据报。
-
标志(flag)
占 3 位。第一位未使用,其值为 0。第二位称为 DF(不分片),表示是否允许分片。取值为 0 时,表示允许分片;取值为 1 时,表示不允许分片。第三位称为 MF(更多分片),表示是否还有分片正在传输,设置为 0 时,表示没有更多分片需要发送,或数据报没有分片。
-
片偏移(offsetfrag)
占 13 位。当报文被分片后,该字段标记该分片在原报文中的相对位置。片偏移以 8 个字节为偏移单位。所以,除了最后一个分片,其他分片的偏移值都是 8 字节(64 位)的整数倍。
-
生存时间(TTL)
表示数据报在网络中的寿命,占 8 位。该字段由发出数据报的源主机设置。其目的是防止无法交付的数据报无限制地在网络中传输,从而消耗网络资源。
路由器在转发数据报之前,先把 TTL 值减 1。若 TTL 值减少到 0,则丢弃这个数据报,不再转发。因此,TTL 指明数据报在网络中最多可经过多少个路由器。TTL 的最大数值为 255。若把 TTL 的初始值设为 1,则表示这个数据报只能在本局域网中传送。
-
协议
表示该数据报文所携带的数据所使用的协议类型,占 8 位。该字段可以方便目的主机的 IP 层知道按照什么协议来处理数据部分。不同的协议有专门不同的协议号。
ICMP 的协议号为 1,IGMP的协议号为2,TCP 的协议号为 6,UDP 的协议号为 17。
-
首部检验和(checksum)
用于校验数据报的首部,占 16 位。数据报每经过一个路由器,首部的字段都可能发生变化(如TTL),所以需要重新校验。而数据部分不发生变化,所以不用重新生成校验值。
-
源地址
表示数据报的源 IP 地址,占 32 位。
-
目的地址
表示数据报的目的 IP 地址,占 32 位。该字段用于校验发送是否正确。
-
可选字段
该字段用于一些可选的报头设置,主要用于测试、调试和安全的目的。这些选项包括严格源路由(数据报必须经过指定的路由)、网际时间戳(经过每个路由器时的时间戳记录)和安全限制。
-
填充
由于可选字段中的长度不是固定的,使用若干个 0 填充该字段,可以保证整个报头的长度是 32 位的整数倍。
-
数据部分
表示传输层的数据,如保存 TCP、UDP、ICMP 或 IGMP 的数据。数据部分的长度不固定
UDP
UDP报文格式
UDP 报文中每个字段的含义如下:
- 源端口:这个字段占据 UDP 报文头的前 16 位,通常包含发送数据报的应用程序所使用的 UDP 端口。接收端的应用程序利用这个字段的值作为发送响应的目的地址。这个字段是可选的,所以发送端的应用程序不一定会把自己的端口号写入该字段中。如果不写入端口号,则把这个字段设置为 0。这样,接收端的应用程序就不能发送响应了。
- 目的端口:接收端计算机上 UDP 软件使用的端口,占据 16 位。
- 长度:该字段占据 16 位,表示 UDP 数据报长度,包含 UDP 报文头和 UDP 数据长度。因为 UDP 报文头长度是 8 个字节,所以这个值最小为 8。
- 校验值:该字段占据 16 位,可以检验数据在传输过程中是否被损坏。
TCP
TCP的主要特点
- 提供面向连接的服务。两个使用TCP协议进行数据传输的应用进程之间首先必须建立TCP连接,并且在数据传输完毕后释放连接。一般把请求连接的一方叫客户机进程,而响应连接请求的一方叫服务器进程,即TCP连接的建立采用C/S工作模型。
- 提供全双工数据传输服务。只要建立了TCP连接,就能在两个应用进程之间进行双向的数据传输服务,但是这种传输只是端到端的传输,不支持广播和多播。
- 提供面向字节流的服务。两个建立了TCP连接的应用进程之间交换的是字节流。端到端不保留数据记录的边界,也就是说在传输层面上不存在数据记录的概念。
快重传,快恢复
快重传
发送方连续收到3个重复的ACK报文,意识到该数据包可能丢失,无论重传计时器是否溢出,都立即重传该数据包。
快恢复
快重传发送之后,不执行慢启动算法而是执行拥塞避免算法
Nagle,Clark和延迟确认算法
Nagle:针对发送端产生数据较慢,造成每次发送的报文段的数据量较小的情况。主要思想是强制发送方等待,让它收集发送数据以发送大块数据,即通过降低发送次数来加大每次发送的数据量。
Clark:针对接收方处理数据较慢,而造成频繁发送确认报文的情况。主要思想是只要接收方的接收缓冲区已满,则每次接收到TCP报文后返回窗口值为0的确认报文以停止发送方的数据发送,直到接收缓冲区的空闲区域已经能容纳最大长度的报文段或有一半以上的接收缓冲空间已空闲,再发送一个窗口值不为0的确认报文,以重新更新发送方的滑动窗口大小,使发送方继续发送数据。
延迟确认:当接收方收到报文段后,并不马上回复确认报文,而是等待接收缓冲区已经有一定数量的空闲空间后,再回送确认报文段。这样便可减慢发送方滑动窗口的滑动速度,进而降低数据的发送速度。同时也减少了确认报文的数量,进而减少了通信量。但是延迟确认的延迟时间过长有可能迫使发送方重传没有确认的报文,因此延迟确认的时间不能太长,一般不能超过500ms。
端口
(协议,主机IP,端口)三元组唯一标识一个进程。
常见熟知端口对应协议
端口号 | 协议 |
---|---|
20/21 | FTP |
22 | SSH |
23 | Telnet |
25 | SMTP |
53 | DNS |
110 | POP3 |
80/443 | HTTP/HTTPS |
143/993 | IMAP/IMAPS |
161/162 | SNMP |
函数
inet_ntoa()
将网络地址转换成“.”点隔的字符串格式。
linux下:
函数声明:char *inet_ntoa(struct in_addr in);
inet_addr()
将一个点分十进制的IP转换成一个长整数型数。
原型:in_addr_t inet_addr(const char *cp);
gethostbyname()
原型:struct hostent *gethostbyname(const char *name);
name:指向主机名的指针。
gethostbyaddr()
返回对应于给定地址的主机信息。
struct hostent * gethostbyaddr(const char * addr, socklen_t len, int family);
addr参数实际上不是char * 类型,而是一个指向存放IPv4地址的某个in_addr结构的指针;len参数是这个结构的大小,对于IPv4地址为4;family参数为AF_INET。
setsockopt()
原型:int setsockopt(int s,int level,int optname,const char *optval,int optlen);
s:标识一个套接字的描述符。
level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。
optname:需设置的选项。
optval:指针,指向存放选项值的缓冲区。
optlen:optval缓冲区长度。
WSAStartup()
原型:int PASCAL FAR WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData );
WSASocket()
SOCKET WSASocket (
int af,
int type,
int protocol,
LPWSAPROTOCOL_INFO lpProtocolInfo,
GROUP g,
DWORD dwFlags
);
af:[in]一个地址族规范。仅支持AF_INET格式,亦即ARPA Internet地址格式。
type:新套接口的类型描述。
protocol:套接口使用的特定协议,如果调用者不愿指定协议则定为0。
lpProtocolInfo:一个指向PROTOCOL_INFO结构的指针,该结构定义所创建套接口的特性。如果本参数非零,则前三个参数(af, type, protocol)被忽略。
iFlags:套接口属性描述。
WSARecv()
int WSAAPI WSARecv(
[in] SOCKET s,
[in, out] LPWSABUF lpBuffers,
[in] DWORD dwBufferCount,
[out] LPDWORD lpNumberOfBytesRecvd,
[in, out] LPDWORD lpFlags,
[in] LPWSAOVERLAPPED lpOverlapped,
[in] LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
[in] s
标识连接套接字的描述符。
[in, out] lpBuffers
指向WSABUF结构数组的指针。每个WSABUF结构都包含一个指向缓冲区的指针和缓冲区的长度(以字节为单位)。
[in] dwBufferCount
lpBuffers数组中WSABUF结构的数量。
[out] lpNumberOfBytesRecvd
指向此调用在接收操作立即完成时接收的数据数(以字节为单位)的指针。
如果lp重叠参数不是NULL,则对此参数使用NULL以避免潜在的错误结果。仅当lp重叠参数不是NULL时,此参数才能为NULL。
[in, out] lpFlags
指向用于修改WSARecv函数调用行为的标志的指针。有关详细信息,请参阅"备注"部分。
[in] lpOverlapped
指向WSAOVERLAPPED结构的指针(对于非重叠套接字,忽略)。
[in] lpCompletionRoutine
类型:_In_opt_LPWSAOVERLAPPED_COMPLETION_ROUTINE
指向在接收操作完成时调用的完成例程的指针(对于非重叠套接字,忽略该指针)。
结构体
in_addr
struct in_addr {
in_addr_t s_addr;
};
in_addr_t一般为32位的unsigned int,其字节顺序为网络字节顺序。
typedef struct in_addr {
union {
struct{ unsigned char s_b1,s_b2, s_b3,s_b4;} S_un_b;
struct{ unsigned short s_w1, s_w2;} S_un_w;
unsigned long S_addr;
} S_un;
} IN_ADDR;
阐述下in_addr的含义,很显然它是一个存储ip地址的共用体有三种表达方式:
第一种用四个字节来表示IP地址的四个数字;
第二种用两个双字节来表示IP地址;
第三种用一个长整型来表示IP地址。
hostent
hostent是host entry的缩写,该结构记录主机的信息,包括主机名、别名、地址类型、地址长度和地址列表。之所以主机的地址是一个列表的形式,原因是当一个主机有多个网络接口时,自然有多个地址。
struct hostent{
char * h_name;
char ** h_aliases;
short h_addrtype;
short h_length;
char ** h_addr_list;
#define h_addr h_addr_list[0];
};
h_name – 地址的正式名称。
h_aliases – 空字节-地址的预备名称的指针。
h_addrtype –地址类型; 通常是AF_INET。
h_length – 地址的比特长度。
h_addr_list – 零字节-主机网络地址指针。网络字节顺序。
h_addr - h_addr_list中的第一地址。
sockaddr
struct sockaddr {
unsigned short sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
sa_family :是2字节的地址家族,一般都是“AF_xxx”的形式,它的值包括三种:AF_INET,AF_INET6和AF_UNSPEC。
如果指定AF_INET,那么函数就不能返回任何IPV6相关的地址信息;如果仅指定了AF_INET6,则就不能返回任何IPV4地址信息。
AF_UNSPEC则意味着函数返回的是适用于指定主机名和服务名且适合任何协议族的地址。如果某个主机既有AAAA记录(IPV6)地址,同时又有A记录(IPV4)地址,那么AAAA记录将作为sockaddr_in6结构返回,而A记录则作为sockaddr_in结构返回
通常用的都是AF_INET。
sockaddr_in
struct sockaddr_in {
short int sin_family; /* Address family */
unsigned short int sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};
sin_family:指代协议族,在socket编程中只能是AF_INET
sin_port:存储端口号(使用网络字节顺序)
sin_addr:存储IP地址,使用in_addr这个数据结构
sin_zero:是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。