# Network Layer

## ICMP

**互联网控制消息协议**（**I**nternet **C**ontrol **M**essage **P**rotocol，**ICMP**）用于网际协议（IP）中发送控制消息，提供可能发生在通信环境中的各种问题反馈。通过这些信息，使管理者可以对所发生的问题作出诊断，然后采取适当的措施解决。

![](https://3232244687-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYZow-MmROshIrkwdtE%2F-LfKdxcn5EdmB27E0Vek%2F-LfKePGxFw8rbwuLVKWJ%2Fimage.png?alt=media\&token=f5f2b811-92a0-4c10-9d76-acfb67e7dbc7)

ICMP 有很多类型，最常用的类型是**主动请求**为 8，**主动请求的应答**为 0。

### 查询报文类型

主动发起的类型。比如 **ping** 就是查询报文，是一种主动请求，并且获得主动应答的 ICMP 协议。

ping 的主动请求，称为 **ICMP ECHO REQUEST，**&#x4E3B;动请求的回复，称为 **ICMP ECHO REPLY**。

![](https://3232244687-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYZow-MmROshIrkwdtE%2F-LfKdxcn5EdmB27E0Vek%2F-LfKhz__2QHorDPaxVgH%2Fimage.png?alt=media\&token=ea19900f-dc2a-4dbf-b9bb-5fda8884139d)

### 差错报文类型

异常情况发起，报告发生了不好的事。比如：**终点不可达为3，源抑制为4，超时为11，重定向为5。**

**Traceroute** 故意设置特殊的 TTL，来追踪去往目的地时沿途经过的路由器。

Traceroute 的参数指向某个目的 IP 地址，它会发送一个 UDP 的数据包。将 TTL 设置成 1，然后依次累加，直到到达目的主机，所以就能拿到所有的路由器 IP。

## IP

IP 地址被点分割为四个部分，每部分 8bit，所以 IP一共是 32 位。IPV6 有 128 位，比如 fe80::f816:3eff:fec7:7975/64 。

每个 IP 地址又包括了**主机号**和**网络号**两部分。相同网络号的主机组成一个**子网**；不同子网再通过路由器连接，组成一个庞大的网络。

### 五类 IP

| 类别   |   |         |          |          |   |           |
| ---- | - | ------- | -------- | -------- | - | --------- |
|  A 类 | 0 | 网络号(7位) |          |          |   | 主机号(24位)  |
| B 类  | 1 | 0       | 网络号(14位) |          |   | 主机号(16位)  |
| C 类  | 1 | 1       | 0        | 网络号(21位) |   | 主机号(8位)   |
| D 类  | 1 | 1       | 1        | 0        |   | 多播组号(28位) |
| E 类  | 1 | 1       | 1        | 1        | 0 | 留待后用(27位) |

![A、B、C 类所能包含的主机数](https://3232244687-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYZow-MmROshIrkwdtE%2F-LfFCppZsFpcF6AGr_Tc%2F-LfFE8ni7oxPwPvYnj4z%2Fimage.png?alt=media\&token=ab88de89-fa4d-422b-977b-026e04f679d8)

### **CIDR**

**无类别域间路由**（Classless Inter-Domain Routing、**CIDR**）将 32 位的IP 地址一分为二，前面是**网络号**，后面是**主机号**。X.X.X.X/A，意思是 32 位中，前 A 位是网络号，后 32 - A 位是主机号。10.126.6.255 是**广播地址**（一旦发送这个地址，10.126.6 网络里面的所有机器都可以收到），255.255.255.0 是**子网掩码**。将子网掩码与 IP 地址进行 AND 计算，就得到网络号。

例如，求 16.158.165.91/22 的第一个地址、子网掩码、广播地址。可以这么拆解：16.158.<101001>.<01>.91。第一个地址是16.158.<101001>.<00>.1，即16.158.164.1。子网掩码是 255.255.<111111>.<00>.0，即255.255.252.0。广播地址为 16.158.<101001>.<11>.255，即16.158.167.255。

### **查看 IP**

在 Windows 中通过 `ipconfig` 查看 IP，在 Linux 中通过 `ifconfig` （net-tools）或`ip iddr`（iproute2）查看。

```bash
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    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
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UNKNOWN qlen 1000
    link/ether 00:16:3e:0e:33:97 brd ff:ff:ff:ff:ff:ff
    inet 10.126.6.109/24 brd 10.126.6.255 scope global dynamic eth0
       valid_lft 292567783sec preferred_lft 292567783sec
```

ip addr 会显示所有的网卡，网卡可以有 IP 地址，也可以没有。上面输出 10.126.6.109 就是一个 IP 地址。

在 IP 地址的后面有个 scope，对于 eth0 这张网卡来讲，是 global，说明这张网卡是可以对外的，可以接收来自各个地方的包。对于 lo 来讲，是 host，说明这张网卡仅仅可以供本机相互通信。

在IP地址的上一行是 link/ether 00:16:3e:0e:33:97 brd ff:ff:ff:ff:ff:ff，这个被称为**MAC地址**，是一个网卡的物理地址，用十六进制，6个byte表示。MAC地址的通信范围比较小，局限在一个子网里面。

第一行的 \<BROADCAST,MULTICAST,UP,LOWER\_UP> 是 **net\_device flags**，**网络设备的状态标识**。UP 表示网卡处于启动的状态；BROADCAST 表示这个网卡有广播地址，可以发送广播包；MULTICAST 表示网卡可以发送多播包；LOWER\_UP 表示L1是启动的，也即网线插着呢。

mtu 1500 表示最大传输单元MTU为1500，这是以太网的默认值。

qdisc pfifo\_fast 表示排队规则为 pfifo\_fast。qdisc 全称是 **queueing discipline。**

* 最简单的 qdisc 是 pfifo，它不对进入的数据包做任何的处理，数据包采用先入先出的方式通过队列。
* pfifo\_fast 包括三个波段（band）。在每个波段里面，使用先进先出规则。
* 三个波段（band）的优先级不同，band 0 \~ 2 优先级依次降低。比如若 band 0 里面有数据包，系统就不会处理 band 1 里面的数据包。
* 数据包是按照服务类型（**Type of Service，TOS**）被分配到三个波段（band）里面的。TOS 是 IP 头里面的一个字段，代表了当前的包是高优先级的，还是低优先级的。

### IP 头

![](https://3232244687-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYZow-MmROshIrkwdtE%2F-LfMzNdCqvWuBdiDPmvk%2F-LfN-CpjEwxFeVmwOixo%2Fimage.png?alt=media\&token=891723fa-0574-42cb-aa3e-0de65192cca6)

* 版本号：主流还是 IPv4
* TOS 见上文
* TTL 见 ICMP
* 协议：指 TCP 还是 UDP

## 网关

在进行网卡配置的时候，除了IP地址，还需要配置一个 **Gateway** 的东西，这个就是**网关**。Gateway 的地址一定是和源 IP 地址是一个网段的。

**Linux默认的逻辑是，如果是一个跨网段的调用，它便不会直接将包发送到网络上，而是企图将包发送到网关。**&#x4E5F;就是说如果你配置了网关，Linux 会获取网关的 MAC 地址，然后将包发送出去。

### 转发网关

不改变 IP 地址的网关。包到达网关后，源 MAC 变成网关出口的 MAC，目标 MAC 变成下一跳网关的 MAC 或目标 IP 的 MAC；源 IP 和目标 IP 不变。

![](https://3232244687-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYZow-MmROshIrkwdtE%2F-LfNJdvJQswNAME1f6gh%2F-LfNLPeH-4R-P2zOUz-f%2Fimage.png?alt=media\&token=e1f98cab-adb9-4470-9f91-5aca68be07f4)

如图 A 访问 192.168.56.2，A 配置了网关 192.168.1.1，通过 ARP 获取其 MAC：

* 源 MAC：服务器 A 的 MAC
* 目标 MAC：192.168.1.1 这个网口的 MAC
* 源 IP：192.168.1.101
* 目标 IP：192.168.56.2

路由器 A 配置了静态路由，要想访问192.168.56.2，要从 192.168.56.1 这个口出去，没有下一跳：

* 源 MAC：192.168.56.1 的 MAC 地址
* 目标 MAC：192.168.56.2 的 MAC 地址
* 源 IP：192.168.1.101
* 目标 IP：192.168.56.2

### NAT 网关

**NAT** （**N**etwork **A**ddress **T**ranslation）**网关**：改变 IP 地址（源和目标）的网关。MAC 地址的行为和转发网关是一致的；源 IP 和目标 IP 通过配置的 NAT 修改。NAT 可以分为三类：

* 静态 NAT，内网 IP 与公网 IP 是一对一的映射关系。
* 动态 NAT，内网 IP 从公网 IP 池中动态选一个映射。
* 网络地址端口转换 NAPT（Network Address and Port Translation），内网 IP 映射到公网 IP 的不同端口，多个内网 IP共享同一个公网 IP。又可以分为三类：
  * SNAT，仅替换源 IP 或端口。主要用于内网多个 IP 共享一个公网 IP 来访问外网资源。
  * DNAT，仅替换目标 IP 或端口。主要用于公网 IP 通过不同端口号来访问内网资源，屏蔽真实 IP。
  * 双向地址转换。主要用于公网 IP 与内网 IP 一一对应，在虚拟环境中为虚拟机分配浮动的公网 IP。

![](https://3232244687-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYZow-MmROshIrkwdtE%2F-LfNJdvJQswNAME1f6gh%2F-LfNJnmfYYuk32FAPVwO%2Fimage.png?alt=media\&token=302d8284-7f21-4a17-b4bc-438f5a2f64e4)

如图，A 访问 192.168.56.2。A 配置了网关 192.168.1.1，通过 ARP 获取其 MAC 地址：

* 源 MAC：A的 MAC
* 目标 MAC：192.168.1.1 的 MAC
* 源IP：192.168.1.101
* 目标IP：192.168.56.2

包到达路由器 A 后，路由器 A 配置了静态路由：要想访问 192.168.56.2/24，要从 192.168.56.1这个口出去，没有下一跳。路由器 A 发送 ARP 获取 192.168.56.2 的 MAC：

* 源 MAC：192.168.56.1 的 MAC 地址
* 目标 MAC：192.168.56.2 的 MAC 地址
* 源 IP：192.168.56.1
* 目标 IP：192.168.56.2

路由器 B 配置了 NAT 网关：

* 源 MAC：192.168.1.1 的 MAC 地址
* 目标 MAC：192.168.1.101 的 MAC 地址
* 源 IP：192.168.56.1
* 目标 IP：192.168.1.101

关于 NAT 的具体应用可见[操作系统-NAT](../../linux/network-diagnosis#nat)。

## 路由

网关往往是一个路由器，是一个三层转发的设备，它有五个网口或者网卡，分别连着五个局域网。每个口的 IP 地址都和局域网的 IP 地址相同的网段，每个口都是对应局域网的网关。

任何一个想发往其他局域网的包，都会到达其中一个口，拿下 MAC 头和 IP 头，根据路由算法，选择另一个口，加上 IP 头和 MAC 头再发出去。

路由种类：

* **静态路由**：路由项（routing entry）手动配置。
* **动态路由**：根据路由协议算法生成动态路由表，随网络运行状况的变化而变化。本质是最短路径算法，比如 Bellman-Ford、Dijkstra。

### 配置路由

路由规则包括：

* 目的网络
* 出口设备
* 下一跳网关

通过 route 命令和 ip route 命令都可以进行查询或者配置。核心思想：根据目的 IP 地址来配置路由。

例如`ip route add 10.176.48.0/20 via 10.173.32.1 dev eth0`意思是 10.176.48.0/20 这个目标网络，要从 eth0 端口出去，经过 10.173.32.1。

除了可以根据目标 IP 来配置路由外，还可以根据多个参数配置路由，称为**策略路由**。可以配置多个路由表，根据源 IP、入口设备、TOS 等选择路由表。

```bash
# 从 192.168.1.0/24 来的使用表 10
ip rule add from 192.168.1.0/24 table 10 

# 从 192.168.2.0/24 来的使用表 20
ip rule add from 192.168.2.0/24 table 20

# 下一跳有两个地方，权重 1:2
ip route add default scope global nexthop via 100.100.100.1 weight 1 nexthop via 200.200.200.1 weight 2
```

### 距离矢量路由算法

**距离矢量路由**（**distance vector routing**）是基于 Bellman-Ford 算法。

基本思路：每个路由器都保存一个路由表，每行对应网络中的一个路由器，每一行包含两部分信息，一个是要到目标路由器，从那条线出去，另一个是到目标路由器的距离。

每个路由器都知道自己和邻居之间的距离，每个路由器都将自己所知的到达所有的路由器的距离告知邻居，每个路由器也能从邻居那里得到相似的信息。

问题：

* 添加路由器很快能被广播，但是路由器下线很久才能被知道。
* 每次发送都需要发送整个全局路由表。所以适用于小型网络（小于 15跳）。

### 链路状态路由算法

**链路状态路由**（**link state routing**）是基于 Dijkstra 算法。

基本思路：当一个路由器启动的时，发现邻居，发送一个 echo，立即返回，除以二就是距离。然后将自己和邻居之间的链路状态包广播出去，发送到整个网络的每个路由器。这样每个路由器都能够收到它和邻居之间的关系的信息。因而，每个路由器都能在自己本地构建一个完整的图，然后针对这个图使用 Dijkstra 算法，找到两点之间的最短路径。

优点：

* 每次广播都只发送改变的网络拓扑。
* 路由器下线，它的邻居都会广播这个消息，很快就能被知道。

### OSPF

**OSPF**（**Open Shortest Path First**，**开放式最短路径优先**）是一个基于链路状态路由协议，多用在数据中心内部，用于路由决策，因而称为**内部网关协议**（**Interior Gateway Protocol**，简称**IGP**）。

有时候 OSPF 可以发现多个最短的路径，可以在这多个路径中进行负载均衡，这常常被称为**等价路由**。

### BGP

**边界网关协议**（**B**order **G**ateway **P**rotocol，**BGP**）是基于距离矢量路由算法。

BGP 分为两类，eBGP 和 iBGP。

BGP 协议使用的算法是**路径矢量路由协议**（path-vector protocol）。它是距离矢量路由协议的升级版。
