1. IP协议
root@test:~# ip addr
1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether fa:16:3e:c7:79:75 brd ff:ff:ff:ff:ff:ff
inet 10.100.122.2/24 brd 10.100.122.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::f816:3eff:fec7:7975/64 scope link
valid_lft forever preferred_lft forever
10.100.122.2是一个IP地址,inet6 fe80::f816:3eff:fec7:7975/64是IPv6地址。IP地址分为五类:
D类是组播地址,使用这一类地址,属于某个组的机器都能收到。
下面这个表展示了ABC三类地址所能包含的主机的数量:
C类地址只能包含254的主机,而B类地址又太大了,能包含65534个主机,因此采用了CIDR方式进行优化
1.1 CIDR 无类型域间选路
1.1.1 CIDR的斜杆+数字的表示方式
这种方式打破了上述的分成几类地址的方式,将32位地址一分为二,前面是网络号,后面是主机号,例如:
10.100.122.2/24
斜杠和24表示对于这个32位地址而言,前24位是其网络号,后8位是其主机号。
1.1.2 广播地址
继续以上述的地址为例,其广播地址为
10.100.122.255
如果发送这个地址,所有10.100.122网络里的机器都可以收到
1.1.3 子网掩码
继续以上述的地址为例,其子网掩码为
255.255.255.0
与IP地址按位与,就可以得到网络号
1.2 公有IP地址和私有IP地址
这是按类对IP地址进行划分的时候的概念,私有地址,即允许组织内部的IT人员自己进行管理分配的地址。公有IP的资源有组织统一分配的,需要去买。
表格中就有我们很常见的192.168.0.0
,一般来说,整个网络的第一个地址192.168.0.1
就是你这个私有网络的出口地址,比如你家的路由器地址就是这个;而192.1678.0.255
就是广播地址。
1.3 动态主机配置协议(DHCP)
网络管理员需要做的是,配置一段共享的IP地址,每一台新接入的机器都会通过DHCP协议,来这个共享的IP地址里申请,然后自动配置好。
1.3.1 DHCP的工作方式
- 新机器加入进网络 -- DHCP discover
新机器使用IP地址0.0.0.0
发送一个广播包,目的IP地址是255.255.255.255
. 广播包封装了UDP, UDP封装了BOOTP,DHCP是BOOTP的增强版。
在这个广播包里,新机器会说我是新来的(Boot Request), 我的MAC地址是blabla,我还没有IP地址,需要租一个!
- DHCP offer
DHCP server 为客户保留一个IP地址,不会再给别的客户用这个IP地址了。DHCP的offer的格式如下,里面有给新人分配的地址:
这个时候仍然使用广播IP作为目的地址,因为此时新机器还没有IP地址呢。值得注意的是,有可能在配置的时候会有多个DHCP server,这个新机器可能收到多个DHCP Offer,新机器会选择一般是最近的一个,然后向网络发送一个告知信息的信息包,成为DHCP request
- DHCP request
这个广播数据包里面包含客户端的MAC地址,接受的租约中的IP地址,提供此租约的DHCP服务器地址等信息。
注意这个时候新机器的IP还没有得到确认,所以客户端仍然使用0.0.0.0作为源地址
- DHCP ACK
当DHCP server接收到客户机的DHCP request之后,会广播返回给客户机一个DHCP ACK消息,表示已经接受客户机的选择
1.3.2 IP地址的收回和续租
客户机会在租期过去 50% 的时候,直接向为其提供 IP 地址的 DHCP Server 发送 DHCP request 消息包。客户机接收到该服务器回应的 DHCP ACK 消息包,会根据包中所提供的新的租期以及其他已经更新的 TCP/IP 参数,更新自己的配置。这样,IP 租用更新就完成了。
1.3.3 DHCP协议- 自动安装操作系统 - PXE(预启动执行环境)
其实,这个过程和操作系统启动的过程有点儿像。首先,启动 BIOS。这是一个特别小的小系统,只能干特别小的一件事情。其实就是读取硬盘的 MBR 启动扇区,将 GRUB 启动起来;然后将权力交给 GRUB,GRUB 加载内核、加载作为根文件系统的 initramfs 文件;然后将权力交给内核;最后内核启动,初始化整个操作系统。那我们安装操作系统的过程,只能插在 BIOS 启动之后了。因为没安装系统之前,连启动扇区都没有。因而这个过程叫做预启动执行环境(Pre-boot Execution Environment)
PXE协议会先把客户端放到BIOS里面,当计算机启动的时候,BIOS把PXE客户端调入内存里面,就可以连接到服务端做一些操作了。
首先,PXE 客户端自己也需要有个 IP 地址。因为 PXE 的客户端启动起来,就可以发送一个 DHCP 的请求,让 DHCP Server 给它分配一个地址。PXE 客户端有了自己的地址,那它怎么知道 PXE 服务器在哪里呢?对于其他的协议,都好办,要么人告诉他。例如,告诉浏览器要访问的 IP 地址,或者在配置中告诉它;例如,微服务之间的相互调用。
但是 PXE 客户端启动的时候,啥都没有。好在 DHCP Server 除了分配 IP 地址以外,还可以做一些其他的事情。这里有一个 DHCP Server 的一个样例配置:
ddns-update-style interim;
ignore client-updates;
allow booting;
allow bootp;
subnet 192.168.1.0 netmask 255.255.255.0
{
option routers 192.168.1.1;
option subnet-mask 255.255.255.0;
option time-offset -18000;
default-lease-time 21600;
max-lease-time 43200;
range dynamic-bootp 192.168.1.240 192.168.1.250;
filename "pxelinux.0";
next-server 192.168.1.180;
}
按照上面的原理,默认的 DHCP Server 是需要配置的,无非是我们配置 IP 的时候所需要的 IP 地址段、子网掩码、网关地址、租期等。如果想使用 PXE,则需要配置 next-server,指向 PXE 服务器的地址,另外要配置初始启动文件 filename。
2. ICMP协议
ping的工作原理,ping是基于ICMP协议来工作的,ICMP全称为: Internet Control Message Protocol,就是互联网控制报文协议。
ICMP报文是封装在IP包里面的。如图所示
2.1 查询报文类型
ICMP主动发起的,对于ping的主动请求,进行网络抓包,称为ICMP ECHO REQUEST. 同理主动请求的回复称为ICMP ECHO REPLY. 比起原生的ICMP多了标识符和序号两个字段。
2.2 差错报文类型
- 终点不可达 3
- 网络不可达 0
- 主机不可达 1
- 协议不可达 2
- 端口不可达 3
- 需要进行分片但设置了不分片
- 源抑制 4
源站放慢了速度
- 超时 11
超过了网络包的生存时间,但是还没到
- 重定向 5
换下次发送使用的路由器
2.3 Ping的使用 查询报文类型的使用
下图描述了ping的整个发送和接收过程。
假定主机 A 的 IP 地址是 192.168.1.1,主机 B 的 IP 地址是 192.168.1.2,它们都在同一个子网。那当你在主机 A 上运行“ping 192.168.1.2”后,会发生什么呢?
ping 命令执行的时候,源主机首先会构建一个 ICMP 请求数据包,ICMP 数据包内包含多个字段。最重要的是两个,第一个是类型字段,对于请求数据包而言该字段为 8;另外一个是顺序号,主要用于区分连续 ping 的时候发出的多个数据包。每发出一个请求数据包,顺序号会自动加 1。为了能够计算往返时间 RTT,它会在报文的数据部分插入发送时间。
然后,由 ICMP 协议将这个数据包连同地址 192.168.1.2 一起交给 IP 层。IP 层将以 192.168.1.2 作为目的地址,本机 IP 地址作为源地址,加上一些其他控制信息,构建一个 IP 数据包。
接下来,需要加入 MAC 头。如果在本节 ARP 映射表中查找出 IP 地址 192.168.1.2 所对应的 MAC 地址,则可以直接使用;如果没有,则需要发送 ARP 协议查询 MAC 地址,获得 MAC 地址后,由数据链路层构建一个数据帧,目的地址是 IP 层传过来的 MAC 地址,源地址则是本机的 MAC 地址;还要附加上一些控制信息,依据以太网的介质访问规则,将它们传送出去。
主机 B 收到这个数据帧后,先检查它的目的 MAC 地址,并和本机的 MAC 地址对比,如符合,则接收,否则就丢弃。接收后检查该数据帧,将 IP 数据包从帧中提取出来,交给本机的 IP 层。同样,IP 层检查后,将有用的信息提取后交给 ICMP 协议。
主机 B 会构建一个 ICMP 应答包,应答数据包的类型字段为 0,顺序号为接收到的请求数据包中的顺序号,然后再发送出去给主机 A。
在规定的时候间内,源主机如果没有接到 ICMP 的应答包,则说明目标主机不可达;如果接收到了 ICMP 应答包,则说明目标主机可达。此时,源主机会检查,用当前时刻减去该数据包最初从源主机上发出的时刻,就是 ICMP 数据包的时间延迟。
2.4 traceroute 差错报文类型的使用
traceroute会故意设置特殊的TTL,来追踪去往目的地沿途经过的路由器。Traceroute 的参数指向某个目的 IP 地址,它会发送一个 UDP 的数据包。将 TTL 设置成 1,也就是说一旦遇到一个路由器或者一个关卡,就表示它“牺牲”了。
如果中间的路由器不止一个,当然碰到第一个就“牺牲”。于是,返回一个 ICMP 包,也就是网络差错包,类型是时间超时。那大军前行就带一顿饭,试一试走多远会被饿死,然后找个哨探回来报告,那我就知道大军只带一顿饭能走多远了。接下来,将 TTL 设置为 2。第一关过了,第二关就“牺牲”了,那我就知道第二关有多远。如此反复,直到到达目的主机。这样,Traceroute 就拿到了所有的路由器 IP。当然,有的路由器压根不会回这个 ICMP。这也是 Traceroute 一个公网的地址,看不到中间路由的原因。
怎么知道 UDP 有没有到达目的主机呢?Traceroute 程序会发送一份 UDP 数据报给目的主机,但它会选择一个不可能的值作为 UDP 端口号(大于 30000)。当该数据报到达时,将使目的主机的 UDP 模块产生一份“端口不可达”错误 ICMP 报文。如果数据报没有到达,则可能是超时。
Traceroute 还有一个作用是故意设置不分片,从而确定路径的 MTU。
要做的工作首先是发送分组,并设置“不分片”标志。发送的第一个分组的长度正好与出口 MTU 相等。如果中间遇到窄的关口会被卡住,会发送 ICMP 网络差错包,类型为“需要进行分片但设置了不分片位”。其实,这是人家故意的好吧,每次收到 ICMP“不能分片”差错时就减小分组的长度,直到到达目标主机。
3. 跨网关访问
一旦配置了IP地址和网关,往往就能够指定目标地址进行访问了。在跨网关访问的时候,会牵扯到MAC地址和IP地址的变化。
在 MAC 头里面,先是目标 MAC 地址,然后是源 MAC 地址,然后有一个协议类型,用来说明里面是 IP 协议。IP 头里面的版本号,目前主流的还是 IPv4,服务类型 TOS 在第三节讲 ip addr 命令的时候讲过,TTL 在第 7 节讲 ICMP 协议的时候讲过。另外,还有 8 位标识协议。这里到了下一层的协议,也就是,是 TCP 还是 UDP。最重要的就是源 IP 和目标 IP。先是源 IP 地址,然后是目标 IP 地址。
在任何一台机器上,当要访问另外一个IP地址的时候,都会先判断这个目标地址和当前机器的IP地址是否在同一个网段当中。使用CIDR和子网掩码来进行判断。
如果不是同一个网段的,就需要将请求发往默认网关gateway. Gateway的地址一定和源IP地址是一个网段的。例如192.168.1.0/24这个网段,Gateway往往是192.168.1.1/24或者192.168.1.2/24
如何发往默认网关呢?网关不是和源 IP 地址是一个网段的么?这个过程就和发往同一个网段的其他机器是一样的:将源地址和目标 IP 地址放入 IP 头中,通过 ARP 获得网关的 MAC 地址,将源 MAC 和网关的 MAC 放入 MAC 头中,发送出去。网关所在的端口,例如 192.168.1.1/24 将网络包收进来,然后接下来怎么做,就完全看网关的了。
网关往往是一个路由器,是个三层转发设备(就是把MAC头和IP头都取下来,然后根据里面的内容,看看接下来把包往哪里转发的设备)
3.1 静态路由
在路由器上配置一条条规则,通过网关进行转发
3.1.1 转发网关
只改变MAC地址,不改变IP的网关
服务器 A 要访问服务器 B。首先,服务器 A 会思考,192.168.4.101 和我不是一个网段的,因而需要先发给网关。那网关是谁呢?已经静态配置好了,网关是 192.168.1.1。网关的 MAC 地址是多少呢?发送 ARP 获取网关的 MAC 地址,然后发送包。包的内容是这样的:
- 源MAC: server A MAC addr
- 目标MAC: 192.168.1.1 这个网口的MAC
- 源IP: 192.168.1.101
- 目标IP: 192.168.4.101
包到达 192.168.1.1 这个网口,发现 MAC 一致,将包收进来,开始思考往哪里转发。在路由器 A 中配置了静态路由之后,要想访问 192.168.4.0/24,要从 192.168.56.1 这个口出去,下一跳为 192.168.56.2。
于是,路由器 A 思考的时候,匹配上了这条路由,要从 192.168.56.1 这个口发出去,发给 192.168.56.2,那 192.168.56.2 的 MAC 地址是多少呢?路由器 A 发送 ARP 获取 192.168.56.2 的 MAC 地址,然后发送包。包的内容是这样的:
- 源MAC: 192.168.56.1 MAC addr
- 目标MAC: 192.168.56.2 这个网口的MAC
- 源IP: 192.168.1.101
- 目标IP: 192.168.4.101
包到达 192.168.56.2 这个网口,发现 MAC 一致,将包收进来,开始思考往哪里转发。在路由器 B 中配置了静态路由,要想访问 192.168.4.0/24,要从 192.168.4.1 这个口出去,没有下一跳了。
于是,路由器 B 思考的时候,匹配上了这条路由,要从 192.168.4.1 这个口发出去,发给 192.168.4.101。那 192.168.4.101 的 MAC 地址是多少呢?路由器 B 发送 ARP 获取 192.168.4.101 的 MAC 地址,然后发送包。包的内容是这样的:
- 源MAC: 192.168.4.1 MAC addr
- 目标MAC: 192.168.4.101 这个网口的MAC
- 源IP: 192.168.1.101
- 目标IP: 192.168.4.101
包到达服务器B,由上述过程可以看出,MAC地址一直都是变的,而IP地址一直都不会变。
3.1.2 NAT网关 (Network Address Translation)
NAT网关出现的原因是IP冲突,如下图所示,各个局域网在设值的时候IP是一样的情况。
这里遇见的第一个问题是,局域网之间没有商量过,各定各的网段,因而 IP 段冲突了。最左面源地址是 192.168.1.101,最右面目标地址也是 192.168.1.101,如果单从 IP 地址上看,简直是自己访问自己,其实是源的 192.168.1.101 要访问目标的 192.168.1.101。
解决这个问题,就是在中间的局域网中使用另外的地址,来进行区分。有点像身份证和护照的关系。
首先,目标服务器 B 在国际上要有一个国际的身份,我们给它一个 192.168.56.2。在网关 B 上,我们记下来,国际身份 192.168.56.2 对应国内身份 192.168.1.101。凡是要访问 192.168.56.2,都转成 192.168.1.101。
于是,源服务器 A 要访问目标服务器 B,要指定的目标地址为 192.168.56.2。这是它的国际身份。服务器 A 想,192.168.56.2 和我不是一个网段的,因而需要发给网关,网关是谁?已经静态配置好了,网关是 192.168.1.1,网关的 MAC 地址是多少?发送 ARP 获取网关的 MAC 地址,然后发送包。包的内容是这样的:
- 源MAC: 服务器A 的 MAC addr
- 目标MAC: 192.168.1.1 这个网口的MAC
- 源IP: 192.168.1.101
- 目标IP: 192.168.56.2
包到达 192.168.1.1 这个网口,发现 MAC 一致,将包收进来,开始思考往哪里转发。
在路由器 A 中配置了静态路由:要想访问 192.168.56.2/24,要从 192.168.56.1 这个口出去,没有下一跳了。
于是,路由器 A 思考的时候,匹配上了这条路由,要从 192.168.56.1 这个口发出去,发给 192.168.56.2。那 192.168.56.2 的 MAC 地址是多少呢?路由器 A 发送 ARP 获取 192.168.56.2 的 MAC 地址。
当网络包发送到中间的局域网的时候,服务器 A 也需要有个国际身份,因而在国际上,源 IP 地址也不能用 192.168.1.101,需要改成 192.168.56.1。发送包的内容是这样的:
- 源MAC: 192.168.1.1 MAC addr
- 目标MAC: 192.168.1.101 这个网口的MAC
- 源IP: 192.168.56.1
- 目标IP: 192.168.1.101
从服务器B接收的包可以看出,源IP是服务器A的国际身份,发送包回去的时候,也是发给这个国际身份,由路由器A做NAT,转换为国内身份。
3.2 动态路由
3.2.1 路由表
路由器是一台网络设备,有多张网卡,当一个入口的网络包送到路由器时,它会根据一个本地的转发信息库,来决定如何正确地转发流量。这个转发信息库通常被称为路由表。
一张路由表会有多个路由规则,每一天规则至少包含这三项信息:
- 目的网络
- 出口设备
- 下一跳网关
3.2.2 策略路由
根据多个参数来配置路由,这样来使得不同来源的包走不同的路由。
家里的网络呢,就是普通的家用网段 192.168.1.x/24。家里有两个租户,分别把线连到路由器上。IP 地址为 192.168.1.101/24 和 192.168.1.102/24,网关都是 192.168.1.1/24,网关在路由器上。两个运营商都要为这个网关配置一个公网的 IP 地址。如果你去查看你们家路由器里的网段,基本就是我图中画的样子。
运行商里面也有一个 IP 地址,在运营商网络里面的网关。不同的运营商方法不一样,有的是 /32 的,也即一个一对一连接。例如,运营商 1 给路由器分配的地址是 183.134.189.34/32,而运营商网络里面的网关是 183.134.188.1/32。有的是 /30 的,也就是分了一个特别小的网段。运营商 2 给路由器分配的地址是 60.190.27.190/30,运营商网络里面的网关是 60.190.27.189/30。
$ ip route list table main
60.190.27.189/30 dev eth3 proto kernel scope link src 60.190.27.190
183.134.188.1 dev eth2 proto kernel scope link src 183.134.189.34
192.168.1.0/24 dev eth1 proto kernel scope link src 192.168.1.1
127.0.0.0/8 dev lo scope link
default via 183.134.188.1 dev eth2
- 如果去运营商二,就走 eth3;
- 如果去运营商一呢,就走 eth2;
- 如果访问内网,就走 eth1;
- 如果所有的规则都匹配不上,默认走运营商一,也即走快的网络。
如何让IP默认走运营商2?
添加一个新的table, chao
# echo 200 chao >> /etc/iproute2/rt_tables
添加一条规则:
# ip rule add from 192.168.1.101 table chao
# ip rule ls
0: from all lookup local
32765: from 10.0.0.10 lookup chao
32766: from all lookup main
32767: from all lookup default
设定规则为:从 192.168.1.101 来的包都查看个 chao 这个新的路由表。
在chao路由表中添加规则:
# ip route add default via 60.190.27.189 dev eth3 table chao
# ip route flush cache
3.2.3 动态路由算法
寻找最短路径,主要使用Bellman-Ford算法和Dijkstra算法。
- 距离矢量路由算法
这种算法的基本思路是,每个路由器都保存一个路由表,包含多行,每行对应网络中的一个路由器,每一行包含两部分信息,一个是要到目标路由器,从那条线出去,另一个是到目标路由器的距离。
由此可以看出,每个路由器都是知道全局信息的。那这个信息如何更新呢?每个路由器都知道自己和邻居之间的距离,每过几秒,每个路由器都将自己所知的到达所有的路由器的距离告知邻居,每个路由器也能从邻居那里得到相似的信息。
每个路由器根据新收集的信息,计算和其他路由器的距离,比如自己的一个邻居距离目标路由器的距离是 M,而自己距离邻居是 x,则自己距离目标路由器是 x+M。
存在的问题1: 好消息传得快,坏消息传得慢 如果有个路由器加入了这个网络,它的邻居就能很快发现它,然后将消息广播出去。要不了多久,整个网络就都知道了。但是一旦一个路由器挂了,挂的消息是没有广播的。当每个路由器发现原来的道路到不了这个路由器的时候,感觉不到它已经挂了,而是试图通过其他的路径访问,直到试过了所有的路径,才发现这个路由器是真的挂了。
原来的网络包括两个节点,B 和 C。A 加入了网络,它的邻居 B 很快就发现 A 启动起来了。于是它将自己和 A 的距离设为 1,同样 C 也发现 A 起来了,将自己和 A 的距离设置为 2。但是如果 A 挂掉,情况就不妙了。B 本来和 A 是邻居,发现连不上 A 了,但是 C 还是能够连上,只不过距离远了点,是 2,于是将自己的距离设置为 3。殊不知 C 的距离 2 其实是基于原来自己的距离为 1 计算出来的。C 发现自己也连不上 A,并且发现 B 设置为 3,于是自己改成距离 4。依次类推,数越来越大,直到超过一个阈值,我们才能判定 A 真的挂了。
存在的问题2: 每次发送的时候,要发送整个全局路由表
当网络规模比较小的时候,全局路由表比较小(15跳以内);如果网络规模比较大,就不适用了。
(不相关的分割线: 感觉很像区块链在做的事情,行为上的类似,目的上的不同)
- 链路状态路由算法
Link state routing
这种算法的基本思路是:当一个路由器启动的时候,首先是发现邻居,向邻居 say hello,邻居都回复。然后计算和邻居的距离,发送一个 echo,要求马上返回,除以二就是距离。然后将自己和邻居之间的链路状态包广播出去,发送到整个网络的每个路由器。这样每个路由器都能够收到它和邻居之间的关系的信息。因而,每个路由器都能在自己本地构建一个完整的图,然后针对这个图使用 Dijkstra 算法,找到两点之间的最短路径。
不像距离距离矢量路由协议那样,更新时发送整个路由表。链路状态路由协议只广播更新的或改变的网络拓扑,这使得更新信息更小,节省了带宽和 CPU 利用率。而且一旦一个路由器挂了,它的邻居都会广播这个消息,可以使得坏消息迅速收敛。
3.2.4 动态路由协议
- 基于链路状态路由算法的OSPF
OSPF - Open Shortest Path First 开放式最短路径优先)就是基于链路状态路由协议,广泛应用在数据中心中的协议。由于主要用在数据中心内部,用于路由决策,因而称为内部网关协议Interior Gateway Protocol
内部网关协议的重点就是找到最短的路径。在一个组织内部,路径最短往往最优。当然有时候 OSPF 可以发现多个最短的路径,可以在这多个路径中进行负载均衡,这常常被称为等价路由。
- 基于距离矢量路由算法的BGP(Border Gateway Protocol)
外部政策的问题,每个数据中心都会设值自己的Policy:
- 哪些外部IP可以让内部知道
- 哪些内部IP可以让外部知道
每个都为一个自治系统AS, Autonomous System,自治系统分为几种类型:
- Stub AS:对外只有一个连接。这类 AS 不会传输其他 AS 的包。例如,个人或者小公司的网络。
- Multihomed AS:可能有多个连接连到其他的 AS,但是大多拒绝帮其他的 AS 传输包。例如一些大公司的网络。
- Transit AS:有多个连接连到其他的 AS,并且可以帮助其他的 AS 传输包。例如主干网。
每个自治系统都有*** 边界路由器***,来和外界进行联系。
BGP协议使用的是路径矢量路由协议,(path-vector protocol)。它是距离矢量路由协议的升级版。
前面说了距离矢量路由协议的缺点。其中一个是收敛慢。在 BGP 里面,除了下一跳 hop 之外,还包括了自治系统 AS 的路径,从而可以避免坏消息传的慢的问题,也即上面所描述的,B 知道 C 原来能够到达 A,是因为通过自己,一旦自己都到达不了 A 了,就不用假设 C 还能到达 A 了。