参考
http://tcpipguide.com
IP 地址的功能:标识 (Identification) 和路由 (Routing)
设备的 IP 地址数量
任何设备若要在网络层 (Network Layer) 传输数据就至少有一个 IP 地址,一个网络接口一个 IP 地址。一般的主机通常有一个 IP 地址,而路由器 (Routers) 则有多个 IP 地址。一些特殊的主机,如多宿 (Multihomed) 主机也会有多个 IP 地址。
多宿:一个主机拥有超过一个 IP 网络接口那么它就是多宿的,一个多宿设备可能有多个接口连着同一个网络也可能连着不同的网络。如果一个主机连着多个网络,那么它可以被配置成一个路由器 (Router)。
比网络层低层的设备如:中继器 (Repeaters)、桥 (Bridges) 和交换机 (Switches) 是不需要 IP 地址,因为它们是基于数据链路层 (Data Link Layer) 地址进行通信的。
IP 地址的网络特定性
当 IP 地址代表网络接口并用于路由时,这个 IP 地址是它所连接的网络特定的,如果设备移动到了一个新的网络,这个 IP 地址通常也需要改变,这个问题也催生了移动 IP (Mobile IP)。
IP 地址仅是 32 位二进制数。一般分为四个字节,每个字节转换成十进制数,用点分开来表示。
二进制 | 11100011 | 01010010 | 10011101 | 10110001 |
---|---|---|---|---|
十六进制 | E3 | 52 | 9D | B1 |
十进制 | 227 | 82 | 157 | 177 |
因为 IP 地址有 32-bit 长,所以总共有 2^32 即 4,294,967,296 个地址。
为了方便寻址,IP 地址被结构化成两部分:网络号 (Network ID) 和主机号 (Host ID)。
网络号:从 IP 地址最左端起的部分 bits,标识主机或其他网络接口所在的网络。也叫 Network prefix 或 prefix
主机号:除去网络号后剩余的 bits,标识网络上的主机。
将网络号包含进 IP 地址中,对寻址起到很大的帮助,因为可以通过比对数据包的目的地址的网络号和自身的网络号是否相同判断出目的地址在内网还是外网,从而快速确定路由。将 IP 地址分为网络号和主机号,还产生了一些有特殊含义的地址,如主机号全为 1 的广播地址。
网络号和主机号的分割点不是固定的,而是取决于各种因素的,并且可以在 32-bit 地址的任何地方,包括十进制八位字节的中间。
在分类寻址中,IP 地址被分为 ABCDE 五类地址
IP 地址类别 | 在 IP 地址中的占比 | 网络号数 | 主机号数 | 用于 |
---|---|---|---|---|
A 类地址 | 1/2 | 8 | 24 | 为需要上千万台主机连入网络的大型组织提供单播地址,最多提供 16,277,214 个地址 |
B 类地址 | 1/4 | 16 | 16 | 为需要上千台主机连入网络的中型组织提供单播地址,最多提供 65,534 个地址 |
C 类地址 | 1/8 | 24 | 8 | 为需要上百台主机连入网络的小型组织提供单播地址,最多提供 254 个地址 |
D 类地址 | 1/16 | - | - | 多播地址 |
E 类地址 | 1/16 | - | - | 为实验保留 |
其中 ABC 类占 7/8,用作单播地址。
尽管分类寻址对如今的网络来说缺点众多,以至于我们用无类寻址 (Classless IP Addressing) 代替了它,但要知道这是在十几年前开发出来的寻址系统,对于那时的网络环境,那时的计算机性能,分类寻址也有着许多的优点:
TCP/IP 刚被创造的时候,计算机的性能比起现在要弱得多,路由器要快速地决定数据包的去处,就必须快速的确定地址的类别并获得目的地址的网络号,当时大家也想象不到网络发展到今天会如此巨大,那时只需要一套简单高效的寻址机制即可,所以分类寻址就这么被发明并使用相当一段时间,也随之诞生了类别确认算法。
算法流程
网络号 | 主机号 | A 类地址例子 | B 类地址例子 | C 类地址例子 | 特殊含义 |
---|---|---|---|---|---|
网络号 | 全为 0 | 77.0.0.0 | 154.3.0.0 | 227.82.157.0 | 指整个网络。 |
全为 0 | 主机号 | 0.91.215.5 | 0.0.99.6 | 0.0.0.160 | 指当前网络或默认网络中的一个主机。 |
全为 0 | 全为 0 | 0.0.0.0 | 指自身,当设备不知道自己的 IP 地址时使用,常用于设备用主机配置协议(如 DHCP)确定自身地址。 | ||
网络号 | 全为 1 | 77.255.255.255 | 154.3.255.255 | 227.82.157.255 | 指某网络中的所有主机,即本地广播地址。 |
全为 1 | 全为 1 | 255.255.255.255 | 指网络中的所有主机,即全局广播地址,广播给所有直接连接的网络中的主机。 |
保留地址、环回地址和私人地址:
分类地址 | 无类地址 | 描述 |
---|---|---|
A 类 0.x.x.x | 0/8 | 保留 |
A 类 10.x.x.x | 10/8 | A 类私人地址 |
A 类 127.x.x.x | 127/8 | 环回地址 |
B 类 128.0.x.x | 128.0/16 | 保留 |
B 类 169.254.x.x | 169.254/16 | B 类私人地址,用于自动私人地址分配,详见 DHCP |
B 类 172.16.x.x ~ 172.31.x.x | 172.16/12 | B 类私人地址 |
B 类 191.255.x.x | 191.255/16 | 保留 |
C 类 192.0.0.x | 192.0.0/24 | 保留 |
C 类 192.168.0.x ~ 192.168.255.x | 192.168/16 | C 类私人地址 |
C 类 223.255.255.x | 223.255.255/24 | 保留 |
多播地址:
起始地址 | 终止地址 | 描述 | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
224.0.0.0 | 224.0.0.255 | 保留众所周知的多播地址。
|
||||||||||||||||||||||
224.0.1.0 | 238.255.255.255 | 全球范围的多播地址 | ||||||||||||||||||||||
239.0.0.0 | 239.255.255.255 | 管理权限范围(本地)的多播地址 |
分类寻址的主要问题是能供使用的单播地址只有 ABC 三类,而且三类网络地址块的地址数跨度太大。假如你的公司有 5000 台设备需要接入网络,C 类网络地址数最大为 254,B 类网络地址数最大为 65,534 。此时有两种选择,选择一个 B 类地址,可是这将浪费全球 60,534 个地址,而且需要不必要的开销;另一个选择是选择 20 个 C 类地址,这看似没有什么问题,但实际上,这导致在外部网络路由器的路由表上增加了不必要的路由信息,理想状况下,一个组织应只占据一个或少许几个路由表项。如果这样规模的公司有很多,且都选择了第一种解决方案,那么全球将会有一大部分的 IP 地址是被浪费的;而如果都选择了第二种方案,则路由表会变得非常的大,路由负担将会大大加重,网络速度也将大幅下降。
总结下来,分类寻址存在三大问题:
于是人们用子网寻址解决了第一个问题,用无类寻址替代了分类寻址从而解决了第二第三个问题。
子网简单的来说就是在原有的分类寻址上加的一个分层等级,使得 IP 地址变成了三层结构:一个网络含有若干个子网,一个子网含有若干台主机。这一改变带来的优势:
噢对了,子网号是通过从主机号划分一部分得来的,将主机号前面的一部分划作子网号,剩余的部分作为主机号。
既然子网的数量和每个子网的主机数都是可以自定义的,也就是说子网号和主机号的长度是可以自定义的,那么问题又来了,要怎么让路由器知道那部分是子网号,那部分是主机号呢?我们需要额外的信息来告诉路由器,采取的方法是利用一个和 IP 地址等长 (32-bit) 的掩码 (Masks)。与网络号和子网号对应的位置为 1,主机号对应的位置为 0。这样的话,只要将子网掩码和 IP 地址进行 AND 操作就可以清除主机号,迅速获得网络号和子网号用于确定路由。为了方便表示,我们一般在地址后面加 “/n” 来表示网络号和子网号的长度,n 为网络号和子网号的长度。例:IP 地址 119.29.192.194 子网掩码为 255.255.248.0 那么我们可以表示为 119.29.192.194/21。
划分子网的一些基础:
例如我们有一个 B 类地址,B 类地址有 16 位主机号,如果我们需要搭建 10 个子网,因为 23=8,24=16,所以我们需要从 16 位主机号中取 4 位作为子网号,那么我们还剩下 12 位主机号,也就是说每个子网最多有 2^12-2=4094 台主机。
hmmm,又出问题了,从上面的例子相信大家也会发现,子网号的长度是固定的,也就是说一个网络被等分成若干分,而现实中的应用,不会这么凑巧。和上面一样,如果我们需要 10 个子网,但需要其中一个很大,需要有 30,000 台主机,剩下的 9 个子网都只需要 1000 台主机,那么就出问题了,如果要 10 个子网,那么每个子网就只能最多有 4094 台主机。这个方案可以满足 9 个小的子网,但却因为需要一个大的子网而无法投入使用,那问题就大了,网管可能需要为了那一个大的子网再去申请一个 B 类地址,这样就把剩下的地址给浪费了。我们需要一个可以将网络根据需求划分成不同大小子网的机制。
顺应大家的需求,VLSM 就诞生了。简单的说 VLSM 就是允许将子网内部再划分子网的规则。
例如有一个 B 类地址 154.71.0.0,要满足上面所说的要求:
取一位子网号分成两个子网,分别为:154.71.0.0 和 154.71.128.0,每个子网最多可以有 32,766 台主机。
取子网 154.71.128.0,再取四位主机号作为子网号,将这个子网再划分成 16 个子网,每个子网最多可以有 2046 台主机。
154.71.0.0/16 | |||||||||||||||||||||||||||||||||||||||||||||||||||
|
这样网管就可以尽可能地匹配需求了。
子网寻址有着两个主要的优势:匹配现实物理网络结构和对外部网络不可见。但这里的子网寻址是在分类寻址的基础上的。所以还是没有解决分类寻址的一些问题。分类寻址的主要问题是地址块的“粒度”过低,只有 ABC 三类地址,且三类地址的大小跨度很大,难以匹配现实需求,从而造成地址空间的低效利用或路由表表项的增加。
为了解决分类寻址的主要问题,诞生了无类寻址 (Classless Inter-Domain Routing, CIDR)。
CIDR 的理论是不使用类对 IP 地址进行划分地址块,而是将全球网络进行子网划分,匹配各个组织的需求。各个网络聚合成一个超网 (supernet),这也是 CIDR 也称为超网的原因,超网再聚合,最后聚合成全球网络,使得全球网络形成一个多层次的结构,像一棵树。
CIDR 的优势:
分类寻址之所以存在是因为对于当时的网络环境和计算机性能有着一个重要的优势:简单。路由器可以通过前四个 bits 快速的判断出地址的类别,从而知道网络号和主机号。而无类寻址的缺点便是复杂,路由器不能通过前几个 bits 来确定网络号和主机号,这要求路由器的配置要小心和正确。
你肯定觉得 CIDR 和 LVSM 没什么区别,是的,我也觉得没什么区别。
下面是一些从分类寻址机制中保留下来的东西:
用于标识生成该数据报的 IP 版本。对于 IPv4 这里的值为 4。
以 32 比特为一个单位指出 IP 头的长度,包括选项和填补部分,当没有选项时这个值为 5。
携带 IP 数据报的服务质量特征信息,如优先传递,其含义后来被重新定义以供称为差异化服务 (Differentiated Services, DS) 的技术使用。
子字段 | 长度 (bits) | 描述 | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Precedence | 3 | 指示数据报的优先级,下面有八个定义的值,从最低到最高
|
||||||||||||||||||
D | 1 | 如果需要低延迟传输则设为 1,否则为 0 | ||||||||||||||||||
T | 1 | 如果需要更高吞吐量传输则设为 1,否则为 0 | ||||||||||||||||||
R | 1 | 如果需要更可靠的传输则设为 1,否则为 0 | ||||||||||||||||||
Reserved | 2 | 保留 |
以字节 (byte) 为单位指出 IP 数据报的总长度。因为这个字段只有 16 bits 所以 IP 数据报的最大长度为 65,535 Bytes,实际上大多数都远小于这个上限。
这个字段的值对于属于同一数据报的碎片是相同的,这样接收方在重组时才能分辨哪些碎片是属于一个数据报的。
用来管理碎片。
子字段名称 | 长度 (bits) | 描述 |
---|---|---|
Reserved | 1 | 保留 |
DF | 1 | 值为 1 时表示该数据报不能被分片,因为分片操作对更高层的协议不可见,所以很多时候不会设置这个值。但也不是没用,常用与测试最大 MTU |
MF | 1 | 值为 0 时表示该数据报为最后一块碎片;值为 1 时表示后面还有若干数据报碎片。 |
当数据报被分片时,这个字段指出当前碎片在原数据报中的偏移量或位置,以 8 Bytes 为一个单位,第一个碎片的偏移量为 0。
指出数据报允许在互联网中存活的时间,没经过一个路由器,路由器就会将这个值减 1,当这个值为 0 时路由器将丢弃该数据报不再转发。这其实是指出数据报的最多跳数而非时间。
指出数据报中携带的更高层协议。
值(16 进制) | 值(10 进制) | 协议 |
---|---|---|
00 | 0 | 保留 |
01 | 1 | ICMP |
02 | 2 | IGMP |
03 | 3 | GGP |
04 | 4 | IP-in-IP Encapsulation |
06 | 6 | TCP |
08 | 8 | EGP |
11 | 17 | UDP |
32 | 50 | Encapsulating Security Payload (ESP) Extension Header |
33 | 51 | Authentication Header (AH) Extension Header |
仅计算报头的校验和,以提供对传输中的损坏的基本保护,不对数据进行校验和。
源地址和目标地址。
每个 IP 选项都有自己的 子字段格式,下图和表表示通用选项格式:
子字段名称 | 长度 (Bytes) | 描述 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Option Type | 1 | 这 8 bits 被分成如下三个子子字段
|
||||||||||||
Option Length | 0 or 1 | 以 Byte 为单位指出变长选项的长度,包括这里所说的三个子字段。 | ||||||||||||
Option Data | 0 or Variable | 为变长选项提供,包含选项要发送的数据。 |
Option Class | Option Number | Length (Bytes) | Option Name | Description |
---|---|---|---|---|
0 | 0 | 1 | End Of Options List | 只含有一个 0 Byte 的选项,用来标记选项列表的结尾。 |
0 | 1 | 1 | No Operation | 虚拟选项,用于内部填充,用于一些需要 32-bit 对齐的选项。 |
0 | 2 | 11 | Security | 为军方提供的选项,用于指示 IP 数据报的安全分类。 |
0 | 3 | Variable | Loose Source Route | 包含一个 IP 地址列表,用来指定数据报必须准确、按顺序使用所给 IP 地址列表的路由器到达终点,途中可以使用列表外的路由器做中间跳转。 |
0 | 7 | Variable | Record Route | 如果数据报含有这个选项,那么处理这个数据报的路由器会将自己的 IP 地址添加到这个选项中,这样接收方就可以提取地址列表查看数据报采用的路由。 因为这个选项的长度是由发送该数据报的设备设置的,它不会在路由途中增大,所以,如果在到达目的之前该选项已经填满,那么将只记录了部分路由。 |
0 | 9 | Variable | Strict Source Route | 和 Loose Source Route 选项类似,但是不允许列表外的路由器做中间跳转。 |
2 | 4 | Variable | Timestamp | 和 Record Route 选项类似,不同的是处理该数据报的路由器往选项里添加的是时间戳,这样接收方就可以知道数据报在各个路由器之间传输所用的时间。途中同样不能修改该选项的长度。 |
2 | 18 | 12 | Traceroute | 用于 traceroute 程序的增强。 |
如果有选项,使得报头长度不为 32 bits 的倍数,就用 0 去填补,使得报头长度始终为 32 bits 的倍数。
要传送的数据。
在提及将数据报分片前不得不提一下 MTU:物理层所能传输的最大数据报大小。当一个数据报从一个 MTU 较高的网络传向 MTU 较低的网络时数据报就会被分片成一个个大小小于或等于要通过的网络的 MTU 的碎片。就像石头君在管道中旅行,他要进入一个口径小的管道,可是他比管道大进不去,那他就要分裂成小石头君然后才能进入小管道。
数据报在传输过程中往往都要经过数个网络,每个网络的 MTU 或许都不同,如果数据报大小比网络的 MTU 大时进行分片,如果比网络的 MTU 小时不做操作,也就是说传输过程中数据报可能被多次分片,但不进行重组,重组操作由数据报的最终接收方执行。互联网的最小 MTU 为 576 Bytes。
以上面的分片实例为背景,将一个大小为 12,000 Bytes 的数据报分别通过 MTU 为 3,300 1,300 3,300 的网络传送到目的地,其中进行了两次分片操作。
分片后,这个字段指的是碎片的长度,而不是原数据报的长度。
为属于同一个数据报的碎片指定相同的唯一标识。
除了最后一个碎片此值为 0 其余都为 1。
这个字段指示了每个碎片的位置,帮助接收方进行重组。该字段有 13 bits,所以偏移量最大为 8,191,8191 * 8 = 65,528 Bytes,还记得数据报的最大值吗:65,535 Bytes,这就是为什么偏移量以 8 Bytes 为单位。
如果报头有需要在分片时复制给碎片的选项,需要将该字段的值设为 1。每个选项都含有这个字段。
Flags 字段中的 DF 子字段,如果设为 1 该数据报将不会被进行分片处理,如果路由器遇到了一个数据报太大不能进入下一个网络,有设置了 DF Flag ,不能分片又传不下去,路由器有将这个数据报丢弃,并返回一个特定的 ICMP,这个特性常用于 MTU Path Discovery。
分片操作可以由源和目的之间的路由器进行,但重组操作只能由目的设备进行。
这一过程就像你获得一块块拼图,拼图的背面写着它所属哪幅图(碎片识别),还有它在图中的位置 (fragment offset),然后你会找个地方去放属于这幅图的碎片(初始化缓冲区),然后你会定个时间去完成拼图然后去吃饭碎片不够也不再等别人给你碎片(初始化计时器)。
间接传送中是包含直接传送的,间接传送中发送者发往路由器、路由器发往路由器和路由器发往接收者都属于直接传递。
路由的过程其实和显示中送信的过程是很像的,假如我要从深圳福田寄一封信到日本东京都米花町5丁目39番地1号,我将信放进街道的信桶,邮递员来信桶将信带到福田区的邮政局,然后再带到深圳市邮政局 -> 广东省邮政局 -> 中国邮政局 ->日本邮政局 -> 东京都邮政局 -> 米花町邮政局…… 数据报路由也是这样,先判断目的 IP 地址是否在同一个网络,如果是便直接传送到目的地,如果不是就传送给本地的路由器(本地邮局),由路由器判断目的地址是否在它的子网上(如深圳市邮局判断目的是不是在深圳市内)或在与之直接相连的网络上,如果不在就传给下一个路由器(广东省邮局),如果在则传给对应网络的路由器(如日本邮局判断目的在东京,东京属于日本),就这样一步步传到目的地址所在的网络。
这个过程用到了一个重要的概念:下一跳 (Next-Hop),发送者不需要知道去往全世界每个设备的路由,路由器也不需要知道,只要知道下一跳去哪就可以了。就像我要寄信去米花町,我不需要知道在哪怎么去,我只要知道怎么去街道的邮桶就可以了,然后信就通过邮递员和邮局一跳一跳的跳往目的地。
路由器接受数据报并将它传送到下一跳,那么问题来了,路由器怎么知道下一跳是谁?每个路由器都维护着一组信息,这组信息提供了不同网络所对应的路由器,这组信息称为路由表。路由表告诉路由器发往不同网络的下一跳路由器是哪个。每当数据报传送给路由器,路由器就在路由表中查找目的地址网络号所对应的下一跳路由器,然后将数据报传送给下一跳路由器。很显然路由器不可能知道去往全球每一个网络的路径,路由表也不会包含所有网络的下一跳,遇到路由表中所没有的网络时,路由器会传送给路由表中的默认下一跳,这是由网管设置的。到这里我们可以得知,下一跳不一定也通常不是目的网络,下一跳是目的网络的情况也就发生在最后一跳而已,虽然下一跳不一定是目的网络,但可以离目的更近。路由表中不仅包含与路由器直接相连网络的对应下一跳路由器,还包含路由器学习得知的一些远一点(与其不直接先连)的网络的下一跳路由器,当然这个下一跳并不是直接到达那个网络,而是通过这一跳可以离目的更近。