# 34.2.网关和路由

*路由* 是使系统能够找到通往另一个系统的网络路径的机制。*路由* 是一对定义的地址，分别表示“目标”和“网关”。路由表明，当尝试连接到指定的目标时，应该通过指定的网关发送数据包。有三种目标类型：单个主机、子网和“默认”。“默认路由”在没有其他路由适用时使用。网关也有三种类型：单个主机、接口（也叫做链路）和以太网硬件（MAC）地址。已知的路由会存储在路由表中。

本节提供了路由基础的概述。然后展示了如何将 FreeBSD 系统配置为路由器，并提供了一些故障排除技巧。

## 34.2.1. 路由基础

要查看 FreeBSD 系统的路由表，可以使用 [netstat(1)](https://man.freebsd.org/cgi/man.cgi?query=netstat\&sektion=1\&format=html)：

```sh
% netstat -r
Routing tables

Internet:
Destination      Gateway            Flags     Refs     Use     Netif Expire
default          outside-gw         UGS        37      418       em0
localhost        localhost          UH          0      181       lo0
test0            0:e0:b5:36:cf:4f   UHLW        5    63288       re0     77
10.20.30.255     link#1             UHLW        1     2421
example.com      link#1             UC          0        0
host1            0:e0:a8:37:8:1e    UHLW        3     4601       lo0
host2            0:e0:a8:37:8:1e    UHLW        0        5       lo0 =>
host2.example.com link#1            UC          0        0
224              link#1             UC          0        0
```

这个例子中的条目如下：

**default**\
路由表中的第一个条目指定了 `default` 路由。当本地系统需要连接到远程主机时，它会检查路由表以确定是否有已知的路径。如果远程主机匹配表中的某个条目，系统将检查是否可以通过该条目中指定的接口连接。

如果目标不匹配任何条目，或者所有已知路径失败，系统将使用默认路由的条目。对于本地局域网上的主机，默认路由中的 `Gateway` 字段设置为具有直接互联网连接的系统。在读取此条目时，确保 `Flags` 列指示网关是可用的（`UG`）。

对于作为外部世界网关的机器，默认路由将是连接到互联网服务提供商（ISP）的网关机器。

**localhost**\
第二个路由是 `localhost` 路由。`Netif` 列中为 `localhost` 指定的接口是 **lo0**，也称为回环设备。这表明所有发送到此目标的流量应保持在内部，而不是通过网络发送。

**MAC 地址**\
以 `0:e0:` 开头的地址是 MAC 地址。FreeBSD 会自动识别本地以太网中的任何主机（例如示例中的 `test0`），并为该主机在以太网接口 **re0** 上添加一条路由。这种类型的路由有一个超时，如 `Expire` 列所示，如果主机在特定时间内没有响应，该路由将自动删除。这些主机是通过路由信息协议（RIP）识别的，RIP 根据最短路径确定到本地主机的路由。

**子网**\
FreeBSD 会自动为本地子网添加子网路由。在这个例子中，`10.20.30.255` 是 `10.20.30` 子网的广播地址，`example.com` 是与该子网关联的域名。`link#1` 表示机器的第一个以太网卡。

本地网络主机和本地子网的路由是由一个名为 [routed(8)](https://man.freebsd.org/cgi/man.cgi?query=routed\&sektion=8\&format=html) 的守护进程自动配置的。如果它未运行，则只有管理员静态定义的路由存在。

**主机**`host1` 行通过以太网地址引用主机。由于它是发送主机，FreeBSD 知道要使用回环接口 (**lo0**)，而不是以太网接口。

两条 `host2` 行代表使用 [ifconfig(8)](https://man.freebsd.org/cgi/man.cgi?query=ifconfig\&sektion=8\&format=html) 创建的别名。**lo0** 接口后的 `⇒` 符号表示除了回环地址外，还设置了一个别名。此类路由仅出现在支持别名的主机上，本地网络上的所有其他主机将对这些路由显示 `link#1` 行。

**224**\
最后一行（目标子网 `224`）处理多播。

可以在 `Flags` 列中查看每个路由的各种属性。[常见的路由表标志](https://docs.freebsd.org/en/books/handbook/advanced-networking/#routeflags) 总结了这些标志及其含义。

**表 1. 常见的路由表标志**

| 标志 | 目的                                 |
| -- | ---------------------------------- |
| U  | 路由是活动的（启用）。                        |
| H  | 路由目标是单个主机。                         |
| G  | 将此目的地的任何流量转发到此网关，由网关决定如何进一步转发。     |
| S  | 该路由是静态配置的。                         |
| C  | 基于此路由克隆一个新路由，用于机器的连接。此类路由通常用于本地网络。 |
| W  | 该路由是根据本地网络（克隆）路由自动配置的。             |
| L  | 路由涉及到以太网（链路）硬件的引用。                 |

在 FreeBSD 系统中，可以通过在 **/etc/rc.conf** 中指定默认网关的 IP 地址来定义默认路由：

```sh
defaultrouter="10.20.30.1"
```

也可以使用 `route` 手动添加路由：

```sh
# route add default 10.20.30.1
```

请注意，手动添加的路由在重启后不会保留。有关手动操作网络路由表的更多信息，请参阅 [route(8)](https://man.freebsd.org/cgi/man.cgi?query=route\&sektion=8\&format=html)。

## 34.2.2. 配置带静态路由的路由器

如果 FreeBSD 系统是双网卡系统，它可以配置为网络的默认网关或路由器。双网卡系统是指至少连接到两个不同网络的主机。通常，每个网络连接到一个独立的网络接口，尽管可以使用 IP 别名将多个地址绑定到一个物理接口，并使每个地址处于不同的子网。

为了让系统在接口之间转发数据包，FreeBSD 必须被配置为路由器。互联网标准和良好的工程实践不允许 FreeBSD 项目默认启用此功能，但可以通过在 **/etc/rc.conf** 中添加以下行来配置在启动时启用此功能：

```sh
gateway_enable="YES"          # 如果此主机是网关，则设置为 YES
```

要立即启用路由，请将 [sysctl(8)](https://man.freebsd.org/cgi/man.cgi?query=sysctl\&sektion=8\&format=html) 变量 `net.inet.ip.forwarding` 设置为 `1`。要停止路由，请将此变量重置为 `0`。

路由器的路由表需要添加额外的路由，以便它知道如何到达其他网络。路由可以通过静态路由手动添加，也可以通过路由协议自动学习。静态路由适用于小型网络，本节将介绍如何为小型网络添加静态路由条目。

> **注意**
>
> 对于大型网络，静态路由很快变得不可扩展。FreeBSD 提供了标准的 BSD 路由守护进程 [routed(8)](https://man.freebsd.org/cgi/man.cgi?query=routed\&sektion=8\&format=html)，它提供了 RIP 协议（版本 1 和 2）和 IRDP 协议的支持。通过安装包或 Port [net/quagga](https://cgit.freebsd.org/ports/tree/net/quagga/)，可以支持 BGP 和 OSPF 路由协议。

考虑以下网络配置：

![静态路由](https://docs.freebsd.org/images/books/handbook/advanced-networking/static-routes.png)

在此示例中，`RouterA` 是一台 FreeBSD 机器，充当通向外部互联网的路由器。它的默认路由设置为 `10.0.0.1`，允许它与外部世界连接。`RouterB` 已配置使用 `192.168.1.1` 作为其默认网关。

在添加任何静态路由之前，`RouterA` 的路由表如下所示：

```sh
% netstat -nr
Routing tables

Internet:
Destination        Gateway            Flags    Refs      Use  Netif  Expire
default            10.0.0.1           UGS         0    49378    xl0
127.0.0.1          127.0.0.1          UH          0        6    lo0
10.0.0.0/24        link#1             UC          0        0    xl0
192.168.1.0/24     link#2             UC          0        0    xl1
```

当前的路由表中，`RouterA` 没有到 `192.168.2.0/24` 网络的路由。以下命令将 `Internal Net 2` 网络添加到 `RouterA` 的路由表，并使用 `192.168.1.2` 作为下一跳：

```sh
# route add -net 192.168.2.0/24 192.168.1.2
```

现在，`RouterA` 可以访问 `192.168.2.0/24` 网络上的任何主机。但是，如果 FreeBSD 系统重新启动，路由信息将不会保留。如果需要使静态路由持久化，可以将其添加到 **/etc/rc.conf** 中：

```sh
# 将 Internal Net 2 添加为持久静态路由
static_routes="internalnet2"
route_internalnet2="-net 192.168.2.0/24 192.168.1.2"
```

`static_routes` 配置变量是一个由空格分隔的字符串列表，每个字符串引用一个路由名称。变量 `route_internalnet2` 包含该路由名称的静态路由。

使用多个字符串在 `static_routes` 中创建多个静态路由。以下示例显示了如何为 `192.168.0.0/24` 和 `192.168.1.0/24` 网络添加静态路由：

```sh
static_routes="net1 net2"
route_net1="-net 192.168.0.0/24 192.168.0.1"
route_net2="-net 192.168.1.0/24 192.168.1.1"
```

## 34.2.3. 故障排除

当一个地址空间被分配给一个网络时，服务提供商会配置他们的路由表，以确保所有流量都被发送到该站点的链接。但是，外部站点如何知道将其数据包发送到网络的 ISP 呢？

有一个系统跟踪所有分配的地址空间，并定义它们与互联网主干网（或承载互联网流量的主干线路）连接的节点。每台主干机器都保存一份主路由表，该表将特定网络的流量指向一个特定的主干承运商，从那里通过一系列服务提供商直到到达特定网络。

服务提供商的任务是向主干站点通告他们是连接点，因此也是流量的进入路径。这个过程被称为路由传播。

有时，路由传播会出现问题，导致某些站点无法连接。最有用的命令之一用于查找路由问题的断点是 `traceroute`，尤其是在 `ping` 失败时非常有用。

使用 `traceroute` 时，需要提供远程主机的地址。输出将显示路径上的网关主机，最终要么到达目标主机，要么因连接问题而终止。有关更多信息，请参阅 [traceroute(8)](https://man.freebsd.org/cgi/man.cgi?query=traceroute\&sektion=8\&format=html)。

## 34.2.4. 多播考虑事项

FreeBSD 本身支持多播应用和多播路由。多播应用在 FreeBSD 上运行时无需进行特殊配置。要支持多播路由，必须将以下选项编译到自定义内核中：

```ini
options MROUTING
```

多播路由守护进程 `mrouted` 可以通过 [net/mrouted](https://cgit.freebsd.org/ports/tree/net/mrouted/) 软件包和 Ports 进行安装。该守护进程实现了 DVMRP 多播路由协议，并通过编辑 **/usr/local/etc/mrouted.conf** 来配置隧道和 DVMRP。安装 `mrouted` 时，还会安装 `map-mbone` 和 `mrinfo`，以及它们相关的 man 页面。请参考这些文档以获得配置示例。

> **注意**
>
> DVMRP 已经在许多多播安装中被 PIM 协议取代。有关更多信息，请参阅 [pim(4)](https://man.freebsd.org/cgi/man.cgi?query=pim\&sektion=4\&format=html)。
