第 34 章 高级网络

如果发现翻译错误,请直接 发起PR修改

34.1. 简介

本章涵盖了一些高级网络主题。

阅读完本章后,您将了解:

  • 网关和路由的基础知识。

  • 如何设置 USB 网络共享。

  • 如何设置 IEEE® 802.11 和 Bluetooth® 设备。

  • 如何将 FreeBSD 配置为桥接模式。

  • 如何设置网络 PXE 引导。

  • 如何在 FreeBSD 中启用和利用 Common Address Redundancy Protocol (CARP) 的功能。

  • 如何在 FreeBSD 上配置多个 VLAN。

  • 配置蓝牙耳机。

在阅读本章之前,你应该:

34.2. 网关和路由

路由 是一种机制,允许系统找到到另一个系统的网络路径。路由 是一对定义好的地址,表示“目标(destination)”和“网关(gateway)”。路由指示当尝试到达指定的目标时,通过指定的网关发送数据包。目标有三种类型:单个主机、子网和“默认”。如果没有其他路由适用,将使用“默认路由”。网关也有三种类型:单个主机、接口(也称为链路)和以太网硬件(MAC)地址。已知的路由存储在路由表中。

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

34.2.1. 路由基础知识

要查看 FreeBSD 系统的路由表,请使用 netstat(1) 命令。

% 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) 路由。当本地系统需要与远程主机建立连接时,它会检查路由表以确定是否存在已知路径。如果远程主机与表中的条目匹配,系统会检查是否可以使用该条目中指定的接口进行连接。

如果目标与任何条目都不匹配,或者所有已知路径都失败,则系统将使用默认路由的条目。对于局域网上的主机, 默认路由中的 网关(Gateway) 字段设置为直接连接到互联网的系统。在阅读此条目时,请验证 Flags 列是否指示网关可用(UG)。

如果一台机器本身作为外部世界的网关,那么它的默认路由将是互联网服务提供商(ISP)的网关机器。

本地主机

第二条路由是 localhost(本地主机) 路由。在 Netif 列中指定的接口为 lo0 ,也被称为环回设备。这表示所有发送到该目的地的流量都应该是内部流量,而不是通过网络发送出去。

MAC 地址

0:e0: 开头的地址是 MAC 地址。在 FreeBSD 中,它会自动识别本地以太网上的任何主机(例如 test0),并在以太网接口(re0)上添加一个用于该主机的路由。这种类型的路由有一个超时时间,在 Expire 列中显示,如果主机在特定时间内没有响应,路由将被自动删除。这些主机是使用路由信息协议(RIP)进行识别的,该协议根据最短路径确定计算到本地主机的路由。

子网

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

本地网络主机和本地子网的路由由一个名为 routed(8) 的守护进程自动配置。如果该进程未运行,则只会存在由管理员静态定义的路由。

主机

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

两个 host2 行表示使用 ifconfig(8) 创建的别名。在 lo0 接口之后的 符号表示除了环回地址之外,还设置了一个别名。这样的路由只会出现在支持别名的主机上,而本地网络上的所有其他主机将会有一个 link#1 行用于这样的路由。

224

最后一行(目标子网 224 )处理多播。

每个路由的各种属性可以在 Flags 列中看到。 常见的路由表标志 总结了一些这些标志及其含义:

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

U

路由已激活(上行)。

H

路由目的地是一个单独的主机。

G

将任何东西发送到这个目的地,然后由这个网关决定从哪里发送。

S

此路由是静态配置的。

C

为机器连接克隆一个基于此路由的新路由。这种类型的路由通常用于本地网络。

W

该路由是基于本地区域网络(克隆)路由进行自动配置的。

L

路由涉及对以太网(链路)硬件的引用。

在 FreeBSD 系统上,可以通过在 /etc/rc.conf 文件中指定默认网关的 IP 地址来定义默认路由:

defaultrouter="10.20.30.1"

还可以使用 route 命令手动添加路由:

# route add default 10.20.30.1

请注意,手动添加的路由在重新启动后将不会保留。有关手动操作网络路由表的更多信息,请参阅 route(8)

34.2.2. 使用静态路由配置路由器

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

为了使系统在接口之间转发数据包,FreeBSD 必须配置为路由器。互联网标准和良好的工程实践阻止了 FreeBSD 项目默认启用此功能,但可以通过将以下行添加到 [/etc/rc.conf] 来配置在启动时启动:

gateway_enable="YES"          # Set to YES if this host will be a gateway

要立即启用路由功能,请将 sysctl(8) 变量 net.inet.ip.forwarding 设置为 1 。要停止路由功能,请将此变量重置为 0

路由器的路由表需要额外的路由,以便知道如何到达其他网络。路由可以通过手动添加静态路由或使用路由协议自动学习来添加。静态路由适用于小型网络,本节描述了如何为小型网络添加静态路由条目。

对于大型网络来说,静态路由很快就变得不可扩展。 FreeBSD 附带了标准的 BSD 路由守护程序 routed(8),它提供了 RIP 协议的版本 1 和 2 以及 IRDP 协议。可以使用 net/quagga 包或端口安装 BGP 和 OSPF 路由协议的支持。

考虑以下网络:

static routes

在这种情况下,RouterA 是一台充当互联网路由器的 FreeBSD 机器。它设置了一个默认路由到 10.0.0.1 ,使其能够与外部世界连接。RouterB 已经配置为使用 192.168.1.1 作为其默认网关。

在添加任何静态路由之前,RouterA 上的路由表如下所示:

% 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 网络的路由。以下命令将使用 192.168.1.2 作为下一跳,将 Internal Net 2 网络添加到 RouterA 的路由表中:

# route add -net 192.168.2.0/24 192.168.1.2

现在,RouterA 可以访问 192.168.2.0/24 网络上的任何主机。然而,如果 FreeBSD 系统重新启动,路由信息将不会持久化。如果需要静态路由持久化,将其添加到 /etc/rc.conf 文件中。

# Add Internal Net 2 as a persistent static route
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/24192.168.1.0/24 网络的静态路由的示例:

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)

34.2.4. 多播考虑因素

FreeBSD 原生支持多播应用程序和多播路由。在 FreeBSD 上运行多播应用程序不需要任何特殊配置。支持多播路由需要将以下选项编译到自定义内核中:

options MROUTING

可以使用 net/mrouted 包或 port 安装多播路由守护程序 mrouted。该守护程序实现了 DVMRP 多播路由协议,并通过编辑 /usr/local/etc/mrouted.conf 来配置以设置隧道和 DVMRP。安装 mrouted 还会安装 map-mbone 和 mrinfo,以及它们的相关手册页。请参考这些手册页获取配置示例。

在许多多播安装中,DVMRP 协议大多被 PIM 协议取代。有关更多信息,请参阅 pim(4)

34.3. 虚拟主机

FreeBSD 的常见用途之一是虚拟站点托管,其中一个服务器在网络上表现为多个服务器。这是通过将多个网络地址分配给单个接口来实现的。

一个给定的网络接口有一个“真实”的地址,可以有任意数量的“别名”地址。这些别名通常是通过在 /etc/rc.conf 中添加别名条目来添加的,如下面的示例所示:

# sysrc ifconfig_fxp0_alias0="inet xxx.xxx.xxx.xxx netmask xxx.xxx.xxx.xxx"

别名条目必须以 alias0 开头,使用连续的数字,例如 alias0alias1 等等。配置过程将在第一个缺失的数字处停止。

别名网络掩码的计算非常重要。对于给定的接口,必须有一个地址能正确表示网络的网络掩码。任何其他落在该网络内的地址都必须具有全为 1 的网络掩码,可以表示为 255.255.255.2550xffffffff

例如,考虑 fxp0 接口连接到两个网络的情况:10.1.1.0 的子网掩码为 255.255.255.0202.0.75.16 的子网掩码为 255.255.255.240。系统需要配置为出现在范围 10.1.1.110.1.1.5202.0.75.17202.0.75.20 中。给定网络范围中只有第一个地址应具有真实的子网掩码。其余的地址(10.1.1.210.1.1.5202.0.75.18202.0.75.20)必须配置为子网掩码 255.255.255.255

以下是配置适配器以适应此场景的 /etc/rc.conf 条目:

# sysrc ifconfig_fxp0="inet 10.1.1.1 netmask 255.255.255.0"
# sysrc ifconfig_fxp0_alias0="inet 10.1.1.2 netmask 255.255.255.255"
# sysrc ifconfig_fxp0_alias1="inet 10.1.1.3 netmask 255.255.255.255"
# sysrc ifconfig_fxp0_alias2="inet 10.1.1.4 netmask 255.255.255.255"
# sysrc ifconfig_fxp0_alias3="inet 10.1.1.5 netmask 255.255.255.255"
# sysrc ifconfig_fxp0_alias4="inet 202.0.75.17 netmask 255.255.255.240"
# sysrc ifconfig_fxp0_alias5="inet 202.0.75.18 netmask 255.255.255.255"
# sysrc ifconfig_fxp0_alias6="inet 202.0.75.19 netmask 255.255.255.255"
# sysrc ifconfig_fxp0_alias7="inet 202.0.75.20 netmask 255.255.255.255"

用空格分隔的 IP 地址范围列表更简单地表达这个概念。第一个地址将使用指定的子网掩码,而其他地址将使用子网掩码 255.255.255.255

# sysrc ifconfig_fxp0_aliases="inet 10.1.1.1-5/24 inet 202.0.75.17-20/28"

34.4. 无线高级认证

FreeBSD 支持多种连接无线网络的方式。本节介绍如何进行高级身份验证以连接无线网络。

要建立连接并进行基本身份验证以连接到无线网络,可以参考网络章节中的 Connection and Authentication to a Wireless Network 部分来了解如何操作。

34.4.1. WPA 与 EAP-TLS

使用 WPA 的第二种方式是通过 802.1X 后端认证服务器。在这种情况下,WPA 被称为 WPA 企业版,以区别于较不安全的 WPA 个人版。WPA 企业版中的认证是基于可扩展认证协议(EAP)的。

EAP 不带有加密方法。相反,EAP 被嵌入在一个加密隧道中。有许多 EAP 认证方法,但 EAP-TLS、EAP-TTLS 和 EAP-PEAP 是最常见的。

EAP 与传输层安全性(EAP-TLS)是一种得到广泛支持的无线认证协议,因为它是第一个被 Wi-Fi 联盟 认证的 EAP 方法。EAP-TLS 需要三个证书来运行:安装在所有设备上的证书颁发机构(CA)的证书,用于认证服务器的服务器证书,以及每个无线客户端的一个客户端证书。在这种 EAP 方法中,认证服务器和无线客户端通过展示各自的证书来相互认证,然后验证这些证书是由组织的 CA 签名的。

与以前一样,配置是通过 /etc/wpa_supplicant.conf 进行的:

network={
  ssid="freebsdap" (1)
  proto=RSN  (2)
  key_mgmt=WPA-EAP (3)
  eap=TLS (4)
  identity="loader" (5)
  ca_cert="/etc/certs/cacert.pem" (6)
  client_cert="/etc/certs/clientcert.pem" (7)
  private_key="/etc/certs/clientkey.pem" (8)
  private_key_passwd="freebsdmallclient" (9)
}
1 这个字段表示网络名称( SSID )。 <.> 这个示例使用了 RSN IEEE® 802.11i 协议,也被称为 WPA2。 <.> key_mgmt 行指定了要使用的密钥管理协议。在这个示例中,使用的是使用 EAP 认证的 WPA。 <.> 这个字段表示连接的 EAP 方法。 <.> identity 字段包含了 EAP 的身份字符串。 <.> ca_cert 字段指定了 CA 证书文件的路径名。这个文件用于验证服务器证书。 <.> client_cert 行给出了客户端证书文件的路径名。这个证书是每个无线客户端的独特证书。 <.> private_key 字段是客户端证书私钥文件的路径名。 <.> private_key_passwd 字段包含了私钥的密码短语。

然后,将以下行添加到 /etc/rc.conf 文件中:

wlans_ath0="wlan0"
ifconfig_wlan0="WPA DHCP"

下一步是启动界面:

# service netif start
Starting wpa_supplicant.
DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7
DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15
DHCPACK from 192.168.0.20
bound to 192.168.0.254 -- renewal in 300 seconds.
wlan0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
      ether 00:11:95:d5:43:62
      inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255
      media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g
      status: associated
      ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac
      country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF
      AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan
      bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS
      wme burst roaming MANUAL

还可以使用 wpa_supplicant(8)ifconfig(8) 手动启动接口。

34.4.2. WPA 与 EAP-TTLS

使用 EAP-TLS,认证服务器和客户端都需要证书。使用 EAP-TTLS,客户端证书是可选的。这种方法类似于一个 Web 服务器,即使访问者没有客户端证书,也会创建一个安全的 SSL 隧道。EAP-TTLS 使用加密的 TLS 隧道来安全传输认证数据。

所需的配置可以添加到 /etc/wpa_supplicant.conf 文件中:

network={
  ssid="freebsdap"
  proto=RSN
  key_mgmt=WPA-EAP
  eap=TTLS (1)
  identity="test" (2)
  password="test" (3)
  ca_cert="/etc/certs/cacert.pem" (4)
  phase2="auth=MD5" (5)
}
1 这个字段指定了连接的 EAP 方法。 <.> identity 字段包含了在加密的 TLS 隧道内进行 EAP 身份验证的身份字符串。<.> password 字段包含了 EAP 身份验证的密码。 <.> ca_cert 字段指示 CA 证书文件的路径名。这个文件用于验证服务器证书。 <.> 这个字段指定了在加密的 TLS 隧道中使用的身份验证方法。在这个例子中,使用了 EAP 和 MD5-Challenge 。“内部身份验证”阶段通常被称为“第二阶段”。

接下来,将以下行添加到 /etc/rc.conf 文件中:

wlans_ath0="wlan0"
ifconfig_wlan0="WPA DHCP"

下一步是启动界面:

# service netif start
Starting wpa_supplicant.
DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7
DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15
DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 21
DHCPACK from 192.168.0.20
bound to 192.168.0.254 -- renewal in 300 seconds.
wlan0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
      ether 00:11:95:d5:43:62
      inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255
      media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g
      status: associated
      ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac
      country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF
      AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan
      bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS
      wme burst roaming MANUAL

34.4.3. WPA 与 EAP-PEAP

PEAPv0/EAP-MSCHAPv2 是最常见的 PEAP 方法。在本章中,术语 PEAP 用于指代该方法。

Protected EAP(PEAP)是作为 EAP-TTLS 的替代方案而设计的,在 EAP-TLS 之后是最常用的 EAP 标准。在一个混合操作系统的网络中,PEAP 应该是仅次于 EAP-TLS 的最受支持的标准。

PEAP 与 EAP-TTLS 类似,它使用服务器端证书来通过在客户端和认证服务器之间创建加密的 TLS 隧道来验证客户端,从而保护后续的认证信息交换。 PEAP 认证与 EAP-TTLS 不同之处在于,它以明文方式广播用户名,只有密码在加密的 TLS 隧道中发送。而 EAP-TTLS 将同时使用 TLS 隧道传输用户名和密码。

将以下行添加到 /etc/wpa_supplicant.conf 以配置 EAP-PEAP 相关设置:

network={
  ssid="freebsdap"
  proto=RSN
  key_mgmt=WPA-EAP
  eap=PEAP (1)
  identity="test" (2)
  password="test" (3)
  ca_cert="/etc/certs/cacert.pem" (4)
  phase1="peaplabel=0" (5)
  phase2="auth=MSCHAPV2" (6)
}
1 这个字段指定了连接的 EAP 方法。 <.> identity 字段包含了在加密的 TLS 隧道内进行 EAP 认证的身份字符串。 <.> password 字段包含了 EAP 认证的密码。 <.> ca_cert 字段指示了 CA 证书文件的路径名。这个文件用于验证服务器证书。 <.> 这个字段包含了第一阶段认证的参数,即 TLS 隧道。根据使用的认证服务器,指定一个特定的标签进行认证。大多数情况下,标签将是"client EAP encryption",可以通过使用 peaplabel = 0 来设置。更多信息可以在 wpa_supplicant.conf(5) 中找到。 <.> 这个字段指定了在加密的 TLS 隧道中使用的认证协议。在 PEAP 的情况下,它是 auth = MSCHAPV2

将以下内容添加到 [/etc/rc.conf] 文件中:

wlans_ath0="wlan0"
ifconfig_wlan0="WPA DHCP"

然后,启动界面:

# service netif start
Starting wpa_supplicant.
DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 7
DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 15
DHCPREQUEST on wlan0 to 255.255.255.255 port 67 interval 21
DHCPACK from 192.168.0.20
bound to 192.168.0.254 -- renewal in 300 seconds.
wlan0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
      ether 00:11:95:d5:43:62
      inet 192.168.0.254 netmask 0xffffff00 broadcast 192.168.0.255
      media: IEEE 802.11 Wireless Ethernet DS/11Mbps mode 11g
      status: associated
      ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac
      country US ecm authmode WPA2/802.11i privacy ON deftxkey UNDEF
      AES-CCM 3:128-bit txpower 21.5 bmiss 7 scanvalid 450 bgscan
      bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS
      wme burst roaming MANUAL

34.5. 无线自组网模式

IBSS 模式,也称为自组网模式,旨在用于点对点连接。例如,要在机器 A 和 B 之间建立一个自组网,选择两个 IP 地址和一个 SSID。

A 上:

# ifconfig wlan0 create wlandev ath0 wlanmode adhoc
# ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap
# ifconfig wlan0
  wlan0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
	  ether 00:11:95:c3:0d:ac
	  inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255
	  media: IEEE 802.11 Wireless Ethernet autoselect mode 11g <adhoc>
	  status: running
	  ssid freebsdap channel 2 (2417 Mhz 11g) bssid 02:11:95:c3:0d:ac
	  country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60
	  protmode CTS wme burst

adhoc 参数表示接口正在以 IBSS 模式运行。

B 现在应该能够检测到 A

# ifconfig wlan0 create wlandev ath0 wlanmode adhoc
# ifconfig wlan0 up scan
  SSID/MESH ID    BSSID              CHAN RATE   S:N     INT CAPS
  freebsdap       02:11:95:c3:0d:ac    2   54M -64:-96  100 IS   WME

输出中的 I 确认了 A 处于自组网模式。现在,将 B 配置为不同的 IP 地址:

# ifconfig wlan0 inet 192.168.0.2 netmask 255.255.255.0 ssid freebsdap
# ifconfig wlan0
  wlan0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
	  ether 00:11:95:d5:43:62
	  inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255
	  media: IEEE 802.11 Wireless Ethernet autoselect mode 11g <adhoc>
	  status: running
	  ssid freebsdap channel 2 (2417 Mhz 11g) bssid 02:11:95:c3:0d:ac
	  country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60
	  protmode CTS wme burst

现在,AB 都准备好交换信息了。

34.5.1. FreeBSD 主机访问点

FreeBSD 可以充当访问点(AP),从而无需购买硬件 AP 或运行自组网。当 FreeBSD 机器充当网关连接到另一个网络(如互联网)时,这一点尤其有用。

34.5.1.1. 基本设置

在将 FreeBSD 机器配置为 AP 之前,必须为无线网卡配置适当的网络支持和所使用的安全协议。有关更多详细信息,请参阅 [network-wireless-basic]

目前,用于 Windows® 驱动程序的 NDIS 驱动程序包装器不支持 AP 操作。只有原生的 FreeBSD 无线驱动程序支持 AP 模式。

一旦加载了无线网络支持,检查无线设备是否支持主机模式接入点模式,也称为 hostap 模式:

# ifconfig wlan0 create wlandev ath0
# ifconfig wlan0 list caps
drivercaps=6f85edc1<STA,FF,TURBOP,IBSS,HOSTAP,AHDEMO,TXPMGT,SHSLOT,SHPREAMBLE,MONITOR,MBSS,WPA1,WPA2,BURST,WME,WDS,BGSCAN,TXFRAG>
cryptocaps=1f<WEP,TKIP,AES,AES_CCM,TKIPMIC>

此输出显示了该卡的功能。HOSTAP 一词确认了该无线网卡可以充当 AP 。还列出了各种支持的加密算法:WEP、TKIP 和 AES。这些信息表明可以在 AP 上使用哪些安全协议。

在创建网络伪设备时,无线设备只能被设置为 hostap 模式,因此必须先销毁先前创建的设备:

# ifconfig wlan0 destroy

在设置其他参数之前,使用正确的选项重新生成。

# ifconfig wlan0 create wlandev ath0 wlanmode hostap
# ifconfig wlan0 inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap mode 11g channel 1

再次使用 ifconfig(8) 命令查看 wlan0 接口的状态:

# ifconfig wlan0
  wlan0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
	  ether 00:11:95:c3:0d:ac
	  inet 192.168.0.1 netmask 0xffffff00 broadcast 192.168.0.255
	  media: IEEE 802.11 Wireless Ethernet autoselect mode 11g <hostap>
	  status: running
	  ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac
	  country US ecm authmode OPEN privacy OFF txpower 21.5 scanvalid 60
	  protmode CTS wme burst dtimperiod 1 -dfs

hostap 参数表示接口正在以基于主机的访问点模式运行。

可以通过在 /etc/rc.conf 文件中添加以下行来在启动时自动进行接口配置:

wlans_ath0="wlan0"
create_args_wlan0="wlanmode hostap"
ifconfig_wlan0="inet 192.168.0.1 netmask 255.255.255.0 ssid freebsdap mode 11g channel 1"

34.5.1.2. 无身份验证或加密的基于主机的接入点

虽然不建议在没有任何身份验证或加密的情况下运行 AP,但这是检查 AP 是否工作的简单方法。这种配置对于调试客户端问题也很重要。

一旦配置了 AP,从另一台无线机器上发起扫描以找到 AP:

# ifconfig wlan0 create wlandev ath0
# ifconfig wlan0 up scan
SSID/MESH ID    BSSID              CHAN RATE   S:N     INT CAPS
freebsdap       00:11:95:c3:0d:ac    1   54M -66:-96  100 ES   WME

客户端机器找到了 AP 并且可以与其关联:

# ifconfig wlan0 inet 192.168.0.2 netmask 255.255.255.0 ssid freebsdap
# ifconfig wlan0
  wlan0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
	  ether 00:11:95:d5:43:62
	  inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255
	  media: IEEE 802.11 Wireless Ethernet OFDM/54Mbps mode 11g
	  status: associated
	  ssid freebsdap channel 1 (2412 Mhz 11g) bssid 00:11:95:c3:0d:ac
	  country US ecm authmode OPEN privacy OFF txpower 21.5 bmiss 7
	  scanvalid 60 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7
	  roam:rate 5 protmode CTS wme burst

34.5.1.3. WPA2 基于主机的接入点

本节重点介绍使用 WPA2 安全协议设置 FreeBSD 访问点。有关 WPA 和基于 WPA 的无线客户端配置的更多详细信息,请参阅 [network-wireless-wpa]

hostapd[8] 守护进程用于处理 WPA2 启用的访问点上的客户端身份验证和密钥管理。

在充当 AP 的 FreeBSD 机器上执行以下配置操作。一旦 AP 正确工作,可以在启动时通过在 /etc/rc.conf 文件中添加以下行来自动启动 hostapd(8)

hostapd_enable="YES"

在尝试配置 hostapd(8) 之前,请先配置 基本设置 中介绍的基本设置。

34.5.1.3.1. WPA2-PSK 是一种 Wi-Fi 网络安全协议,它使用预共享密钥(PSK)来进行身份验证和加密。

WPA2-PSK 适用于无法或不希望使用后端认证服务器的小型网络。

配置是在 /etc/hostapd.conf 中完成的:

interface=wlan0                  (1)
debug=1                          (2)
ctrl_interface=/var/run/hostapd  (3)
ctrl_interface_group=wheel       (4)
ssid=freebsdap                   (5)
wpa=2                            (6)
wpa_passphrase=freebsdmall       (7)
wpa_key_mgmt=WPA-PSK             (8)
wpa_pairwise=CCMP                (9)
1 用于访问点的无线接口。 <.> 在执行 hostapd(8) 期间使用的详细程度。值为 1 表示最低级别。 <.> hostapd(8) 用于存储与外部程序(如 hostapd_cli(8))通信的域套接字文件的路径名。在此示例中使用默认值。 <.> 允许访问控制接口文件的组。 <.> 将出现在无线扫描中的无线网络名称或 SSID。 <.> 启用 WPA 并指定所需的 WPA 身份验证协议。值为 2 配置 AP 为 WPA2,推荐使用。仅在需要过时的 WPA 时设置为 1。 <.> 用于 WPA 身份验证的 ASCII 密码。 <.> 要使用的密钥管理协议。此示例设置为 WPA-PSK 。 <.> 访问点接受的加密算法。在此示例中,仅接受 CCMP (AES)密码。 CCMP 是 TKIP 的替代方案,在可能的情况下强烈推荐使用。仅当有无法使用 CCMP 的设备时才应允许使用 TKIP。

下一步是启动 hostapd(8)

# service hostapd forcestart
# ifconfig wlan0
wlan0: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500
	ether 04:f0:21:16:8e:10
	inet6 fe80::6f0:21ff:fe16:8e10%wlan0 prefixlen 64 scopeid 0x9
	nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
	media: IEEE 802.11 Wireless Ethernet autoselect mode 11na <hostap>
	status: running
	ssid No5ignal channel 36 (5180 MHz 11a ht/40+) bssid 04:f0:21:16:8e:10
	country US ecm authmode WPA2/802.11i privacy MIXED deftxkey 2
	AES-CCM 2:128-bit AES-CCM 3:128-bit txpower 17 mcastrate 6 mgmtrate 6
	scanvalid 60 ampdulimit 64k ampdudensity 8 shortgi wme burst
	dtimperiod 1 -dfs
	groups: wlan

一旦 AP 运行起来,客户端就可以与其关联。更多详细信息请参见 [network-wireless-wpa]。可以使用 ifconfig wlan0 list sta 命令查看与 AP 关联的设备。

34.6. USB 网络共享

许多手机提供通过 USB 共享数据连接的选项(通常称为“网络共享”)。此功能使用 RNDIS、CDC 或自定义的 Apple® iPhone®/iPad® 协议之一。

  • Android™ 设备通常使用 urndis(4) 驱动程序。

  • Apple® 设备使用 ipheth(4) 驱动程序。

  • 较旧的设备通常使用 cdce(4) 驱动程序。

在连接设备之前,将适当的驱动程序加载到内核中:

# kldload if_urndis
# kldload if_cdce
# kldload if_ipheth

一旦设备连接上,ue_0_ 将可用于像普通网络设备一样使用。请确保设备上已启用“USB 网络共享”选项。

要使此更改永久生效并在启动时将驱动程序作为模块加载,将以下适当的行放置在 /boot/loader.conf 中:

if_urndis_load="YES"
if_cdce_load="YES"
if_ipheth_load="YES"

34.7. 蓝牙

蓝牙是一种无线技术,用于在 2.4 GHz 无线频段中创建个人网络,其范围为 10 米。网络通常是由便携设备(如手机、手持设备和笔记本电脑)自动形成的。与 Wi-Fi 无线技术不同,蓝牙提供更高级的服务配置文件,例如类似 FTP 的文件服务器、文件推送、语音传输、串行线仿真等。

本节描述了在 FreeBSD 系统上使用 USB 蓝牙适配器的方法。然后,它介绍了各种蓝牙协议和实用工具。

34.7.1. 加载蓝牙支持

在 FreeBSD 中,蓝牙堆栈是使用 netgraph(4) 框架实现的。ng_ubt(4) 支持各种各样的蓝牙 USB 适配器。基于 Broadcom BCM2033 的蓝牙设备由 ubtbcmfw(4)ng_ubt(4) 驱动程序支持。3Com 蓝牙 PC 卡 3CRWB60-A 由 ng_bt3c(4) 驱动程序支持。基于串口和 UART 的蓝牙设备由 sio(4)ng_h4(4)hcseriald(8) 驱动程序支持。

在连接设备之前,确定设备使用的是上述驱动程序中的哪一个,然后加载该驱动程序。例如,如果设备使用的是 ng_ubt(4) 驱动程序:

# kldload ng_ubt

如果在系统启动期间将蓝牙设备连接到系统上,可以通过将驱动程序添加到 /boot/loader.conf 来配置系统在引导时加载模块。

ng_ubt_load="YES"

一旦驱动程序加载完成,插入 USB dongle。如果驱动程序加载成功,类似以下内容的输出将出现在控制台和 /var/log/messages 文件中:

ubt0: vendor 0x0a12 product 0x0001, rev 1.10/5.25, addr 2
ubt0: Interface 0 endpoints: interrupt=0x81, bulk-in=0x82, bulk-out=0x2
ubt0: Interface 1 (alt.config 5) endpoints: isoc-in=0x83, isoc-out=0x3,
      wMaxPacketSize=49, nframes=6, buffer size=294

要启动和停止蓝牙堆栈,请使用其启动脚本。在拔下设备之前停止堆栈是一个好主意。启动蓝牙堆栈可能需要启动 hcsecd(8) 。启动堆栈时,输出应类似于以下内容:

# service bluetooth start ubt0
BD_ADDR: 00:02:72:00:d4:1a
Features: 0xff 0xff 0xf 00 00 00 00 00
<3-Slot> <5-Slot> <Encryption> <Slot offset>
<Timing accuracy> <Switch> <Hold mode> <Sniff mode>
<Park mode> <RSSI> <Channel quality> <SCO link>
<HV2 packets> <HV3 packets> <u-law log> <A-law log> <CVSD>
<Paging scheme> <Power control> <Transparent SCO data>
Max. ACL packet size: 192 bytes
Number of ACL packets: 8
Max. SCO packet size: 64 bytes
Number of SCO packets: 8

34.7.2. 查找其他蓝牙设备

主机控制器接口(HCI)提供了一种统一的方法来访问蓝牙基带功能。在 FreeBSD 中,为每个蓝牙设备创建一个 netgraph HCI 节点。有关更多详细信息,请参阅 ng_hci(4)

发现 RF 附近的蓝牙设备是最常见的任务之一。这个操作被称为“查询”。查询和其他 HCI 相关的操作是使用 hccontrol(8) 完成的。下面的示例显示了如何查找在范围内的蓝牙设备。设备列表应该在几秒钟内显示出来。请注意,只有在远程设备设置为“可发现”模式时,它才会回答查询。

% hccontrol -n ubt0hci inquiry
Inquiry result, num_responses=1
Inquiry result #0
       BD_ADDR: 00:80:37:29:19:a4
       Page Scan Rep. Mode: 0x1
       Page Scan Period Mode: 00
       Page Scan Mode: 00
       Class: 52:02:04
       Clock offset: 0x78ef
Inquiry complete. Status: No error [00]

BD_ADDR 是蓝牙设备的唯一地址,类似于网络卡的 MAC 地址。这个地址在与设备进行进一步通信时是必需的,并且可以为 BD_ADDR 分配一个可读的名称。已知的蓝牙主机的信息包含在 /etc/bluetooth/hosts 文件中。以下示例显示了如何获取分配给远程设备的可读名称:

% hccontrol -n ubt0hci remote_name_request 00:80:37:29:19:a4
BD_ADDR: 00:80:37:29:19:a4
Name: Pav's T39

如果对远程蓝牙设备进行查询,将会以"your.host.name (ubt0)"的形式找到计算机。本地设备的名称可以随时更改。

远程设备可以在 /etc/bluetooth/hosts 中分配别名。有关 /etc/bluetooth/hosts 文件的更多信息可以在 bluetooth.hosts(5) 中找到。

蓝牙系统提供了两个蓝牙设备之间的点对点连接,或者在多个蓝牙设备之间共享的点对多点连接。以下示例展示了如何创建与远程设备的连接:

% hccontrol -n ubt0hci create_connection BT_ADDR

create_connection 函数接受 BT_ADDR 和主机别名作为参数,在 /etc/bluetooth/hosts 文件中定义。

以下示例显示了如何获取本地设备的活动基带连接列表:

% hccontrol -n ubt0hci read_connection_list
Remote BD_ADDR    Handle Type Mode Role Encrypt Pending Queue State
00:80:37:29:19:a4     41  ACL    0 MAST    NONE       0     0 OPEN

当需要终止基带连接时,连接句柄(connection handle) 非常有用,尽管通常不需要手动执行此操作。堆栈会自动终止不活动的基带连接。

# hccontrol -n ubt0hci disconnect 41
Connection handle: 41
Reason: Connection terminated by local host [0x16]

输入 hccontrol help 以获取可用 HCI 命令的完整列表。大多数 HCI 命令不需要超级用户权限。

34.7.3. 设备配对

默认情况下,蓝牙通信不需要进行身份验证,任何设备都可以与其他设备进行通信。蓝牙设备(如手机)可以选择要求进行身份验证以提供特定服务。蓝牙身份验证通常使用一个最长为 16 个字符的 ASCII 字符串作为 PIN 码。用户需要在两台设备上输入相同的 PIN 码。用户输入 PIN 码后,两台设备将生成一个 链接密钥。之后,链接密钥可以存储在设备或持久存储中。下次,两台设备将使用先前生成的链接密钥。这个过程称为 配对。请注意,如果链接密钥被任一设备丢失,配对必须重新进行。

hcsecd(8) 守护进程负责处理蓝牙认证请求。默认的配置文件是 /etc/bluetooth/hcsecd.conf。下面是一个示例部分,其中手机的 PIN 码设置为 1234

device {
        bdaddr  00:80:37:29:19:a4;
        name    "Pav's T39";
        key     nokey;
        pin     "1234";
      }

对于 PIN 码,唯一的限制是长度。一些设备,如蓝牙耳机,可能有一个固定的内置 PIN 码。-d 开关强制 hcsecd(8) 保持在前台,以便可以清楚地看到发生了什么。将远程设备设置为接收配对并启动与远程设备的蓝牙连接。远程设备应指示接受配对并请求 PIN 码。输入与 hcsecd.conf 中列出的相同的 PIN 码。现在计算机和远程设备已配对。或者,可以在远程设备上启动配对。

可以将以下行添加到 /etc/rc.conf 中,以配置 hcsecd(8) 在系统启动时自动启动:

hcsecd_enable="YES"

以下是 hcsecd(8) 守护进程输出的示例:

hcsecd[16484]: Got Link_Key_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4
hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', link key doesn't exist
hcsecd[16484]: Sending Link_Key_Negative_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4
hcsecd[16484]: Got PIN_Code_Request event from 'ubt0hci', remote bdaddr 0:80:37:29:19:a4
hcsecd[16484]: Found matching entry, remote bdaddr 0:80:37:29:19:a4, name 'Pav's T39', PIN code exists
hcsecd[16484]: Sending PIN_Code_Reply to 'ubt0hci' for remote bdaddr 0:80:37:29:19:a4

34.7.4. 使用 PPP 配置文件的网络访问

可以使用拨号网络(DUN)配置文件将手机配置为无线调制解调器,以连接到拨号上网服务器。它还可以用于配置计算机接收来自手机的数据呼叫。

使用 PPP 配置文件进行网络访问可以为单个蓝牙设备或多个蓝牙设备提供局域网访问。它还可以使用 PPP 网络通过串行电缆仿真实现 PC 与 PC 之间的连接。

在 FreeBSD 中,这些配置文件是通过 ppp(8)rfcomm_pppd(8) 包装器实现的,该包装器将蓝牙连接转换为 PPP 可以使用的内容。在使用配置文件之前,必须在 /etc/ppp/ppp.conf 中创建一个新的 PPP 标签。请参考 rfcomm_pppd(8) 中的示例。

在这个例子中,使用 rfcomm_pppd(8) 来打开与远程设备的连接,其 BD_ADDR00:80:37:29:19:a4 ,在一个 DUNRFCOMM 通道上。

# rfcomm_pppd -a 00:80:37:29:19:a4 -c -C dun -l rfcomm-dialup

实际的通道号将通过使用 SDP 协议从远程设备获取。可以手动指定 RFCOMM 通道,在这种情况下,rfcomm_pppd(8) 将不执行 SDP 查询。使用 sdpcontrol(8) 来查找远程设备上的 RFCOMM 通道。

为了使用 PPPLAN 服务提供网络访问,必须运行 sdpd(8) 并在 /etc/ppp/ppp.conf 中创建一个新的 LAN 客户端条目。请参考 rfcomm_pppd(8) 中的示例。最后,在有效的 RFCOMM 通道号上启动 RFCOMMPPP 服务器。RFCOMMPPP 服务器将自动在本地 SDP 守护进程中注册蓝牙 LAN 服务。下面的示例显示了如何启动 RFCOMMPPP 服务器。

# rfcomm_pppd -s -C 7 -l rfcomm-server

34.7.5. 蓝牙协议

本节提供了各种蓝牙协议的概述,包括它们的功能和相关工具。

34.7.5.1. 逻辑链路控制和适配协议(L2CAP)

逻辑链路控制和适配协议(L2CAP)为上层协议提供面向连接和无连接的数据服务。L2CAP 允许更高级的协议和应用程序传输和接收最长为 64 千字节的 L2CAP 数据包。

L2CAP 基于 通道(channels) 概念。通道是在基带连接之上的逻辑连接,每个通道以多对一的方式绑定到单个协议上。多个通道可以绑定到同一个协议,但一个通道不能绑定到多个协议。在通道上接收到的每个 L2CAP 数据包都会被传递给相应的高层协议。多个通道可以共享同一个基带连接。

在 FreeBSD 中,为每个蓝牙设备创建一个 netgraph L2CAP 节点。该节点通常连接到下游的蓝牙 HCI 节点和上游的蓝牙套接字节点。 L2CAP 节点的默认名称为"devicel2cap"。有关更多详细信息,请参阅 ng_l2cap(4)

一个有用的命令是 l2ping(8),它可以用来 ping 其他设备。一些蓝牙实现可能不会返回所有发送给它们的数据,所以在下面的示例中 0 bytes 是正常的。

# l2ping -a 00:80:37:29:19:a4
0 bytes from 0:80:37:29:19:a4 seq_no=0 time=48.633 ms result=0
0 bytes from 0:80:37:29:19:a4 seq_no=1 time=37.551 ms result=0
0 bytes from 0:80:37:29:19:a4 seq_no=2 time=28.324 ms result=0
0 bytes from 0:80:37:29:19:a4 seq_no=3 time=46.150 ms result=0

l2control(8) 实用程序用于对 L2CAP 节点执行各种操作。此示例演示了如何获取本地设备的逻辑连接(通道)列表和基带连接列表。

% l2control -a 00:02:72:00:d4:1a read_channel_list
L2CAP channels:
Remote BD_ADDR     SCID/ DCID   PSM  IMTU/ OMTU State
00:07:e0:00:0b:ca    66/   64     3   132/  672 OPEN
% l2control -a 00:02:72:00:d4:1a read_connection_list
L2CAP connections:
Remote BD_ADDR    Handle Flags Pending State
00:07:e0:00:0b:ca     41 O           0 OPEN

另一个诊断工具是 btsockstat(1)。它类似于 netstat(1),但用于蓝牙网络相关的数据结构。下面的示例显示了与上面的 l2control(8) 相同的逻辑连接。

% btsockstat
Active L2CAP sockets
PCB      Recv-Q Send-Q Local address/PSM       Foreign address   CID   State
c2afe900      0      0 00:02:72:00:d4:1a/3     00:07:e0:00:0b:ca 66    OPEN
Active RFCOMM sessions
L2PCB    PCB      Flag MTU   Out-Q DLCs State
c2afe900 c2b53380 1    127   0     Yes  OPEN
Active RFCOMM sockets
PCB      Recv-Q Send-Q Local address     Foreign address   Chan DLCI State
c2e8bc80      0    250 00:02:72:00:d4:1a 00:07:e0:00:0b:ca 3    6    OPEN

34.7.5.2. 无线电频率通信(RFCOMM)

RFCOMM 协议通过 L2CAP 协议提供串口的仿真。RFCOMM 是一个简单的传输协议,还提供了模拟 RS-232(EIATIA-232-E)串口的 9 个电路的附加功能。它支持两个蓝牙设备之间的最多 60 个同时连接(RFCOMM 通道)。

对于 RFCOMM 而言,一个完整的通信路径涉及到两个在通信端点上运行的应用程序,它们之间有一个通信段。RFCOMM 旨在覆盖利用设备串口的应用程序。通信段是从一个设备直接连接到另一个设备的蓝牙链路。

RFCOMM 只关注直接连接情况下设备之间的连接,或者设备与网络情况下的调制解调器之间的连接。RFCOMM 可以支持其他配置,例如通过蓝牙无线技术在一侧进行通信并在另一侧提供有线接口的模块。

在 FreeBSD 中,RFCOMM 是在蓝牙套接字层实现的。

34.7.5.3. 服务发现协议(SDP)

服务发现协议(SDP)提供了客户端应用程序发现由服务器应用程序提供的服务的存在以及这些服务的属性的方法。服务的属性包括提供的服务类型或类别以及利用该服务所需的机制或协议信息。

SDP 涉及 SDP 服务器和 SDP 客户端之间的通信。服务器维护一个描述与服务器相关的服务特性的服务记录列表。每个服务记录包含有关单个服务的信息。客户端可以通过发出 SDP 请求从 SDP 服务器维护的服务记录中检索信息。如果客户端或与客户端相关的应用程序决定使用某个服务,则必须打开与服务提供者的单独连接以利用该服务。SDP 提供了一种发现服务及其属性的机制,但不提供利用这些服务的机制。

通常,SDP 客户端根据服务的某些期望特征来搜索服务。然而,有时候希望在没有关于服务的任何先前信息的情况下,发现 SDP 服务器的服务记录描述了哪些类型的服务。这个寻找任何提供的服务的过程被称为 浏览

蓝牙 SDP 服务器(sdpd(8))和命令行客户端(sdpcontrol(8))已包含在标准的 FreeBSD 安装中。以下示例展示了如何执行 SDP 浏览查询。

% sdpcontrol -a 00:01:03:fc:6e:ec browse
Record Handle: 00000000
Service Class ID List:
        Service Discovery Server (0x1000)
Protocol Descriptor List:
        L2CAP (0x0100)
                Protocol specific parameter #1: u/int/uuid16 1
                Protocol specific parameter #2: u/int/uuid16 1

Record Handle: 0x00000001
Service Class ID List:
        Browse Group Descriptor (0x1001)

Record Handle: 0x00000002
Service Class ID List:
        LAN Access Using PPP (0x1102)
Protocol Descriptor List:
        L2CAP (0x0100)
        RFCOMM (0x0003)
                Protocol specific parameter #1: u/int8/bool 1
Bluetooth Profile Descriptor List:
        LAN Access Using PPP (0x1102) ver. 1.0

请注意,每个服务都有一系列属性,例如 RFCOMM 通道。根据服务的不同,用户可能需要注意其中一些属性。某些蓝牙实现不支持服务浏览,并可能返回一个空列表。在这种情况下,可以搜索特定的服务。下面的示例演示了如何搜索 OBEX 对象推送(OPUSH)服务:

% sdpcontrol -a 00:01:03:fc:6e:ec search OPUSH

在 FreeBSD 上为蓝牙客户端提供服务是通过 sdpd(8) 服务器完成的。可以将以下行添加到 /etc/rc.conf 文件中:

sdpd_enable="YES"

然后可以使用以下命令启动 sdpd(8) 守护进程:

# service sdpd start

希望为远程客户端提供蓝牙服务的本地服务器应用程序将在本地 SDP 守护进程中注册该服务。这样的应用程序示例是 rfcomm_pppd(8) 。一旦启动,它将在本地 SDP 守护进程中注册蓝牙局域网服务。

可以通过在本地控制通道上发出 SDP 浏览查询来获取注册到本地 SDP 服务器的服务列表。

# sdpcontrol -l browse

34.7.5.4. OBEX 对象推送(OPUSH)

Object Exchange(OBEX)是一种广泛使用的协议,用于移动设备之间的简单文件传输。它主要用于红外通信,用于笔记本电脑或个人数字助理(PDA)之间的通用文件传输,以及在手机和其他具有个人信息管理(PIM)应用程序的设备之间发送名片或日历条目。

OBEX 服务器和客户端是由 obexapp 实现的,可以使用 comms/obexapp[] 软件包或 port 进行安装。

OBEX 客户端用于从 OBEX 服务器推送或拉取对象。一个示例对象可以是名片或约会。OBEX 客户端可以通过 SDP 从远程设备获取 RFCOMM 通道号。这可以通过指定服务名称而不是 RFCOMM 通道号来完成。支持的服务名称有:IrMCFTRNOPUSH。还可以将 RFCOMM 通道指定为数字。下面是一个 OBEX 会话的示例,其中从手机中提取设备信息对象,并将一个新对象,名片,推送到手机的目录中。

% obexapp -a 00:80:37:29:19:a4 -C IrMC
obex> get telecom/devinfo.txt devinfo-t39.txt
Success, response: OK, Success (0x20)
obex> put new.vcf
Success, response: OK, Success (0x20)
obex> di
Success, response: OK, Success (0x20)

为了提供 OPUSH 服务,必须运行 sdpd(8) 并创建一个根文件夹,用于存储所有传入的对象。根文件夹的默认路径是 /var/spool/obex。最后,在有效的 RFCOMM 通道号上启动 OBEX 服务器。OBEX 服务器将自动在本地 SDP 守护进程中注册 OPUSH 服务。下面的示例显示了如何启动 OBEX 服务器。

# obexapp -s -C 10

34.7.5.5. 串口配置文件(SPP)

串口配置文件(SPP)允许蓝牙设备执行串行电缆仿真。该配置文件允许传统应用程序通过虚拟串口抽象将蓝牙用作电缆替代品。

在 FreeBSD 中,rfcomm_sppd(1) 实现了 SPP ,并且使用伪终端作为虚拟串口抽象。下面的示例展示了如何连接到远程设备的串口服务。rfcomm_sppd(1) 不需要指定 RFCOMM 通道,它可以通过 SDP 从远程设备获取。如果要覆盖这个行为,可以在命令行上指定一个 RFCOMM 通道。

# rfcomm_sppd -a 00:07:E0:00:0B:CA -t
rfcomm_sppd[94692]: Starting on /dev/pts/6...
/dev/pts/6

一旦连接成功,伪终端可以被用作串口。

# cu -l /dev/pts/6

伪终端会被打印到标准输出,并可以被包装脚本读取。

PTS=`rfcomm_sppd -a 00:07:E0:00:0B:CA -t`
cu -l $PTS

34.7.6. 故障排除

默认情况下,当 FreeBSD 接受新连接时,它会尝试执行角色切换并成为主设备。一些不支持角色切换的较旧的蓝牙设备将无法连接。由于角色切换是在建立新连接时执行的,因此无法询问远程设备是否支持角色切换。然而,有一个 HCI 选项可以在本地禁用角色切换:

# hccontrol -n ubt0hci write_node_role_switch 0

要显示蓝牙数据包,请使用第三方软件包 hcidump,可以使用 comms/hcidump 软件包或 port 进行安装。该实用程序类似于 tcpdump(1),可用于在终端上显示蓝牙数据包的内容,并将蓝牙数据包转储到文件中。

34.8. 桥接

有时候,将一个网络(比如以太网段)划分为网络段,而无需创建 IP 子网并使用路由器将这些网络段连接起来,是非常有用的。以这种方式连接两个网络的设备被称为“桥接器”。

桥接器通过学习每个网络接口上设备的 MAC 地址来工作。只有当源 MAC 地址和目标 MAC 地址位于不同的网络上时,它才会在网络之间转发流量。在许多方面,桥接器类似于只有很少端口的以太网交换机。可以配置具有多个网络接口的 FreeBSD 系统来充当桥接器。

桥接在以下情况下非常有用:

连接网络

桥接的基本操作是连接两个或多个网络段。使用主机级桥接而不是网络设备有许多原因,例如布线限制或防火墙。桥接还可以将以 hostap 模式运行的无线接口连接到有线网络,并充当访问点。

过滤/流量整形防火墙

当需要防火墙功能而不需要路由或网络地址转换(NAT)时,可以使用桥接。

一个例子是一个通过 DSL 或 ISDN 连接到 ISP 的小公司。ISP 提供了 13 个公共 IP 地址,网络上有 10 台计算机。在这种情况下,由于子网划分问题,使用基于路由器的防火墙是困难的。而基于桥接的防火墙可以在没有任何 IP 地址问题的情况下进行配置。

网络分流器

桥接器可以连接两个网络段,以便使用桥接接口上的 bpf(4)tcpdump(1) 来检查它们之间传递的所有以太网帧,或者通过将所有帧的副本发送到一个额外的接口,即监控端口。

二层虚拟专用网络(Layer 2 VPN)

通过将网络桥接到 EtherIP 隧道或基于 tap(4) 的解决方案(如 OpenVPN),可以通过 IP 链接将两个以太网网络连接起来。

二层冗余

一个网络可以通过多个链路连接在一起,并使用生成树协议(STP)来阻塞冗余路径。

本节介绍了如何使用 if_bridge(4) 将 FreeBSD 系统配置为桥接模式。还提供了 netgraph 桥接驱动程序的配置方法,详见 ng_bridge(4)

数据包过滤可以与任何钩入 pfil(9) 框架的防火墙软件包一起使用。桥接可以与 altq(4)dummynet(4) 一起用作流量整形器。

34.8.1. 启用桥接功能

在 FreeBSD 中,if_bridge(4) 是一个内核模块,当创建一个桥接接口时,ifconfig(8) 会自动加载它。也可以通过在自定义内核配置文件中添加 device if_bridge 来编译桥接支持到自定义内核中。

使用接口克隆来创建桥接。要创建桥接接口:

# ifconfig bridge create
bridge0
# ifconfig bridge0
bridge0: flags=8802<BROADCAST,SIMPLEX,MULTICAST> metric 0 mtu 1500
        ether 96:3d:4b:f1:79:7a
        id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15
        maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200
        root id 00:00:00:00:00:00 priority 0 ifcost 0 port 0

当创建一个桥接接口时,它会自动分配一个随机生成的以太网地址。maxaddrtimeout 参数控制桥接将保留多少个 MAC 地址在其转发表中,以及每个条目在最后一次被检测到后多少秒后被删除。其他参数控制 STP 的操作方式。

接下来,指定要添加为桥接成员的网络接口。为了使桥接转发数据包,所有成员接口和桥接都需要处于启动状态:

# ifconfig bridge0 addm fxp0 addm fxp1 up
# ifconfig fxp0 up
# ifconfig fxp1 up

桥接器现在可以在 fxp0fxp1 之间转发以太网帧。在 /etc/rc.conf 中添加以下行,以便在启动时创建桥接器:

cloned_interfaces="bridge0"
ifconfig_bridge0="addm fxp0 addm fxp1 up"
ifconfig_fxp0="up"
ifconfig_fxp1="up"

如果桥接主机需要一个 IP 地址,请在桥接接口上设置,而不是在成员接口上设置。该地址可以静态设置或通过 DHCP 设置。以下示例设置了一个静态 IP 地址:

# ifconfig bridge0 inet 192.168.0.1/24

还可以将 IPv6 地址分配给桥接口。要使更改永久生效,请将寻址信息添加到 /etc/rc.conf 文件中。

当启用数据包过滤时,桥接的数据包将通过过滤器从桥接接口的原始接口进入,并通过适当的接口出去。任何一个阶段都可以被禁用。当数据包流向很重要时,最好在成员接口上设置防火墙,而不是在桥接本身上设置。

该桥接器具有多个可配置的设置,用于传递非 IP 和 IP 数据包,并使用 ipfw(8) 进行第二层防火墙设置。有关更多信息,请参阅 if_bridge(4)

34.8.2. 启用生成树

为了使以太网网络正常运行,两个设备之间只能存在一条活动路径。 STP 协议可以检测到环路,并将冗余链路置于阻塞状态。如果其中一条活动链路失败,STP 会计算出一棵不同的树,并启用一条被阻塞的路径,以恢复网络中所有节点的连接。

快速生成树协议(RSTP 或 802.1w)与传统的生成树协议(STP)向后兼容。RSTP 提供更快的收敛速度,并与相邻交换机交换信息,以便快速转换到转发模式,而不会创建环路。FreeBSD 支持 RSTP 和 STP 作为操作模式,其中 RSTP 是默认模式。

可以使用 ifconfig(8) 在成员接口上启用 STP。对于具有 fxp0fxp1 作为当前接口的桥接,可以使用以下命令启用 STP :

# ifconfig bridge0 stp fxp0 stp fxp1
bridge0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        ether d6:cf:d5:a0:94:6d
        id 00:01:02:4b:d4:50 priority 32768 hellotime 2 fwddelay 15
        maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200
        root id 00:01:02:4b:d4:50 priority 32768 ifcost 0 port 0
        member: fxp0 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP>
                port 3 priority 128 path cost 200000 proto rstp
                role designated state forwarding
        member: fxp1 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP>
                port 4 priority 128 path cost 200000 proto rstp
                role designated state forwarding

这个桥的生成树 ID 是 00:01:02:4b:d4:50,优先级是 32768。由于根 ID 相同,这表明这是生成树的根桥。

网络上的另一座桥梁也启用了 STP :

bridge0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        ether 96:3d:4b:f1:79:7a
        id 00:13:d4:9a:06:7a priority 32768 hellotime 2 fwddelay 15
        maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200
        root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4
        member: fxp0 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP>
                port 4 priority 128 path cost 200000 proto rstp
                role root state forwarding
        member: fxp1 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP>
                port 5 priority 128 path cost 200000 proto rstp
                role designated state forwarding

该行 root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4 显示根桥为 00:01:02:4b:d4:50,从该桥到根桥的路径成本为 400000。到达根桥的路径通过 port 4,即 fxp0

34.8.3. 桥接接口参数

几个 ifconfig 参数是专门用于桥接接口的。本节概述了这些参数的一些常见用法。完整的可用参数列表在 ifconfig(8) 中有描述。

private

私有接口不会将任何流量转发到其他被指定为私有接口的端口。流量会被无条件地阻塞,因此不会转发任何以太网帧,包括 ARP 数据包。如果需要选择性地阻止流量,应该使用防火墙。

span

一个 SPAN 端口会传输桥接器接收到的每个以太网帧的副本。一个桥接器上配置的 SPAN 端口数量是无限的,但是如果一个接口被指定为 SPAN 端口,它就不能再被用作常规的桥接端口。这对于在连接到桥接器的 SPAN 端口之一的另一台主机上被动地监听桥接网络非常有用。例如,要将所有帧的副本发送到名为 fxp4 的接口上。

# ifconfig bridge0 span fxp4
sticky

如果桥接成员接口被标记为粘性(sticky),动态学习的地址条目将被视为转发缓存中的静态条目。粘性条目不会因为地址在不同接口上出现而被清除或替换。这提供了静态地址条目的好处,而无需预先填充转发表。在桥接的特定段上学习的客户端不能漫游到另一个段。

使用粘性地址的一个例子是将桥接器与 VLAN 结合使用,以便在不浪费 IP 地址空间的情况下隔离客户网络。假设 CustomerA 位于 vlan100CustomerB 位于 vlan101,桥接器的地址为 192.168.0.1

# ifconfig bridge0 addm vlan100 sticky vlan100 addm vlan101 sticky vlan101
# ifconfig bridge0 inet 192.168.0.1/24

在这个例子中,两个客户端都将 192.168.0.1 视为它们的默认网关。由于桥接缓存是粘性的,一个主机无法伪造另一个客户的 MAC 地址以拦截其流量。

可以使用防火墙或私有接口来阻止 VLAN 之间的任何通信,就像在这个例子中所看到的那样:

# ifconfig bridge0 private vlan100 private vlan101

客户之间完全隔离,可以分配整个 /24 地址范围而无需进行子网划分。

接口后面的唯一源 MAC 地址数量可以被限制。一旦达到限制,具有未知源地址的数据包将被丢弃,直到现有的主机缓存条目过期或被删除。

以下示例将 vlan100CustomerA 的以太网设备的最大数量设置为 10 :

# ifconfig bridge0 ifmaxaddr vlan100 10

桥接接口还支持监控模式,在 bpf(4) 处理后丢弃数据包,不再进行进一步处理或转发。这可以用于将两个或多个接口的输入多路复用到单个 bpf(4) 流中。这对于重构通过两个独立接口传输 RX/TX 信号的网络监听器的流量非常有用。例如,要将四个网络接口的输入作为一个流进行读取:

# ifconfig bridge0 addm fxp0 addm fxp1 addm fxp2 addm fxp3 monitor up
# tcpdump -i bridge0

34.8.4. SNMP 监控

桥接接口和 STP 参数可以通过 bsnmpd(1) 进行监控,该工具包含在 FreeBSD 基本系统中。导出的桥接 MIB 符合 IETF 标准,因此可以使用任何 SNMP 客户端或监控软件来检索数据。

要在桥接器上启用监控,请取消注释 /etc/snmpd.config 中的此行,即删除开头的 # 符号:

begemotSnmpdModulePath."bridge" = "/usr/lib/snmp_bridge.so"

其他配置设置,如社区名称和访问列表,可能需要在此文件中进行修改。有关更多信息,请参阅 bsnmpd(1)snmp_bridge(3)。保存这些编辑后,将此行添加到 /etc/rc.conf

bsnmpd_enable="YES"

然后,启动 bsnmpd(1)

# service bsnmpd start

以下示例使用 Net-SNMP 软件(net-mgmt/net-snmp)从客户端系统查询桥接。也可以使用 net-mgmt/bsnmptools port。从运行 Net-SNMP 的 SNMP 客户端,在 $HOME/.snmp/snmp.conf 中添加以下行以导入桥接 MIB 定义:

mibdirs +/usr/share/snmp/mibs
mibs +BRIDGE-MIB:RSTP-MIB:BEGEMOT-MIB:BEGEMOT-BRIDGE-MIB

使用 IETF BRIDGE-MIB(RFC4188)监控单个桥接设备:

% snmpwalk -v 2c -c public bridge1.example.com mib-2.dot1dBridge
BRIDGE-MIB::dot1dBaseBridgeAddress.0 = STRING: 66:fb:9b:6e:5c:44
BRIDGE-MIB::dot1dBaseNumPorts.0 = INTEGER: 1 ports
BRIDGE-MIB::dot1dStpTimeSinceTopologyChange.0 = Timeticks: (189959) 0:31:39.59 centi-seconds
BRIDGE-MIB::dot1dStpTopChanges.0 = Counter32: 2
BRIDGE-MIB::dot1dStpDesignatedRoot.0 = Hex-STRING: 80 00 00 01 02 4B D4 50
...
BRIDGE-MIB::dot1dStpPortState.3 = INTEGER: forwarding(5)
BRIDGE-MIB::dot1dStpPortEnable.3 = INTEGER: enabled(1)
BRIDGE-MIB::dot1dStpPortPathCost.3 = INTEGER: 200000
BRIDGE-MIB::dot1dStpPortDesignatedRoot.3 = Hex-STRING: 80 00 00 01 02 4B D4 50
BRIDGE-MIB::dot1dStpPortDesignatedCost.3 = INTEGER: 0
BRIDGE-MIB::dot1dStpPortDesignatedBridge.3 = Hex-STRING: 80 00 00 01 02 4B D4 50
BRIDGE-MIB::dot1dStpPortDesignatedPort.3 = Hex-STRING: 03 80
BRIDGE-MIB::dot1dStpPortForwardTransitions.3 = Counter32: 1
RSTP-MIB::dot1dStpVersion.0 = INTEGER: rstp(2)

dot1dStpTopChanges.0 的值为 2,表示 STP 桥接拓扑已经发生了两次变化。拓扑变化意味着网络中的一个或多个链路发生了变化或故障,并重新计算了新的树形结构。dot1dStpTimeSinceTopologyChange.0 的值将显示这一事件发生的时间。

要监控多个桥接口,可以使用私有的 BEGEMOT-BRIDGE-MIB。

% snmpwalk -v 2c -c public bridge1.example.com
enterprises.fokus.begemot.begemotBridge
BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge0" = STRING: bridge0
BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge2" = STRING: bridge2
BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge0" = STRING: e:ce:3b:5a:9e:13
BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge2" = STRING: 12:5e:4d:74:d:fc
BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge0" = INTEGER: 1
BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge2" = INTEGER: 1
...
BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge0" = Timeticks: (116927) 0:19:29.27 centi-seconds
BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge2" = Timeticks: (82773) 0:13:47.73 centi-seconds
BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge0" = Counter32: 1
BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge2" = Counter32: 1
BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge0" = Hex-STRING: 80 00 00 40 95 30 5E 31
BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge2" = Hex-STRING: 80 00 00 50 8B B8 C6 A9

要通过 mib-2.dot1dBridge 子树更改被监视的桥接口:

% snmpset -v 2c -c private bridge1.example.com
BEGEMOT-BRIDGE-MIB::begemotBridgeDefaultBridgeIf.0 s bridge2

34.9. 链路聚合和故障转移

FreeBSD 提供了 lagg(4) 接口,可以将多个网络接口聚合成一个虚拟接口,以实现故障转移和链路聚合。故障转移允许流量在至少一个聚合网络接口建立连接时继续传输。链路聚合在支持 LACP 的交换机上效果最佳,因为该协议可以在双向传输流量的同时响应单个链路的故障。

lagg 接口支持的聚合协议决定了哪些端口用于出站流量,以及特定端口是否接受入站流量。lagg(4) 支持以下协议:

故障转移(failover)

这种模式只通过主端口发送和接收流量。如果主端口不可用,将使用下一个活动端口。添加到虚拟接口的第一个接口是主端口,随后添加的接口将用作故障转移设备。如果故障转移到非主端口,原始端口在再次可用时将恢复为主端口。

负载均衡(loadbalance)

这提供了一个静态设置,并且不与对等方协商聚合或交换帧以监视链路。如果交换机支持 LACP,则应使用该协议。

lacp

IEEE® 802.3ad 链路聚合控制协议(LACP)与对等方协商一组可聚合的链路,形成一个或多个链路聚合组(LAG)。每个 LAG 由相同速度的端口组成,设置为全双工操作,并且流量在 LAG 中的端口之间进行平衡,以获得最大总速度。通常,只有一个包含所有端口的 LAG 。在物理连接发生变化时,LACP 将迅速收敛到新的配置。

LACP 根据哈希协议头信息平衡传出流量,并从任何活动端口接受传入流量。哈希包括以太网源地址和目的地址,如果可用,还包括 VLAN 标签以及 IPv4 或 IPv6 源地址和目的地址。

roundrobin

这种模式通过循环调度器将出站流量分发到所有活动端口,并接受来自任何活动端口的入站流量。由于这种模式违反了以太网帧的顺序,因此应谨慎使用。

broadcast

该模式将出站流量发送到 lagg 接口上配置的所有端口,并在任何端口上接收帧。

34.9.1. 配置示例

本节演示了如何配置 Cisco® 交换机和 FreeBSD 系统以实现 LACP 负载均衡。然后,它展示了如何配置两个以故障转移模式工作的以太网接口,以及如何在以太网接口和无线接口之间配置故障转移模式。

例 1. 使用 Cisco® 交换机进行 LACP 聚合

这个例子将一个 FreeBSD 机器上的两个 fxp(4) 以太网接口连接到 Cisco® 交换机的前两个以太网端口,作为一个负载均衡和容错链接。可以添加更多接口以增加吞吐量和容错性。请根据本地配置替换示例中显示的 Cisco® 端口名称、以太网设备、通道组号和 IP 地址的名称。

以太网链路上的帧排序是强制性的,任何两个站点之间的流量总是通过同一物理链路传输,这限制了最大速度为一个接口的速度。传输算法尝试使用尽可能多的信息来区分不同的流量,并在可用接口之间平衡流量。

在 Cisco® 交换机上,将 FastEthernet0/1FastEthernet0/2 接口添加到通道组 1 中:

interface FastEthernet0/1
 channel-group 1 mode active
 channel-protocol lacp
!
interface FastEthernet0/2
 channel-group 1 mode active
 channel-protocol lacp

在 FreeBSD 系统上,使用物理接口 fxp0fxp1 创建 lagg(4) 接口,并使用 IP 地址 10.0.0.3/24 将接口启动:

# ifconfig fxp0 up
# ifconfig fxp1 up
# ifconfig lagg0 create
# ifconfig lagg0 up laggproto lacp laggport fxp0 laggport fxp1 10.0.0.3/24

接下来,验证虚拟接口的状态:

# ifconfig lagg0
lagg0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=8<VLAN_MTU>
        ether 00:05:5d:71:8d:b8
        inet 10.0.0.3 netmask 0xffffff00 broadcast 10.0.0.255
        media: Ethernet autoselect
        status: active
        laggproto lacp
        laggport: fxp1 flags=1c<ACTIVE,COLLECTING,DISTRIBUTING>
        laggport: fxp0 flags=1c<ACTIVE,COLLECTING,DISTRIBUTING>

标记为“ ACTIVE ”的端口是与远程交换机协商的链路聚合组(LAG)的一部分。通过这些活动端口传输和接收流量。在上述命令中添加 -v 以查看 LAG 标识符。

查看 Cisco® 交换机上的端口状态:

switch# show lacp neighbor
Flags:  S - Device is requesting Slow LACPDUs
        F - Device is requesting Fast LACPDUs
        A - Device is in Active mode       P - Device is in Passive mode

Channel group 1 neighbors

Partner's information:

                  LACP port                        Oper    Port     Port
Port      Flags   Priority  Dev ID         Age     Key     Number   State
Fa0/1     SA      32768     0005.5d71.8db8  29s    0x146   0x3      0x3D
Fa0/2     SA      32768     0005.5d71.8db8  29s    0x146   0x4      0x3D

要获取更详细的信息,请输入 show lacp neighbor detail

要在重新启动后保留此配置,请将以下条目添加到 FreeBSD 系统上的 /etc/rc.conf 文件中:

ifconfig_fxp0="up"
ifconfig_fxp1="up"
cloned_interfaces="lagg0"
ifconfig_lagg0="laggproto lacp laggport fxp0 laggport fxp1 10.0.0.3/24"
例 2. 故障转移模式

如果主接口丢失连接,可以使用故障转移模式切换到备用接口。要配置故障转移,请确保底层物理接口正常工作,然后创建 lagg(4) 接口。在这个例子中,fxp0 是主接口,fxp1 是备用接口,虚拟接口被分配了一个 IP 地址为 10.0.0.15/24

# ifconfig fxp0 up
# ifconfig fxp1 up
# ifconfig lagg0 create
# ifconfig lagg0 up laggproto failover laggport fxp0 laggport fxp1 10.0.0.15/24

虚拟接口应该类似于这样:

# ifconfig lagg0
lagg0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=8<VLAN_MTU>
        ether 00:05:5d:71:8d:b8
        inet 10.0.0.15 netmask 0xffffff00 broadcast 10.0.0.255
        media: Ethernet autoselect
        status: active
        laggproto failover
        laggport: fxp1 flags=0<>
        laggport: fxp0 flags=5<MASTER,ACTIVE>

流量将在 fxp0 上进行传输和接收。如果 fxp0 上的连接丢失,fxp1 将成为活动连接。如果主接口上的连接恢复,它将再次成为活动连接。

要在重新启动后保留此配置,请将以下条目添加到 /etc/rc.conf 文件中:

ifconfig_fxp0="up"
ifconfig_fxp1="up"
cloned_interfaces="lagg0"
ifconfig_lagg0="laggproto failover laggport fxp0 laggport fxp1 10.0.0.15/24"
例 3. 以太网和无线接口之间的故障转移模式

对于笔记本电脑用户来说,通常希望将无线设备配置为次要设备,仅在以太网连接不可用时使用。通过使用 lagg(4) ,可以配置故障转移,优先选择以太网连接,以提高性能和安全性,同时保持通过无线连接传输数据的能力。

这是通过使用无线接口的 MAC 地址覆盖以太网接口的 MAC 地址来实现的。

理论上,以太网或无线网络的 MAC 地址都可以更改以匹配另一个。然而,一些常用的无线接口不支持覆盖 MAC 地址。因此,我们建议为此目的覆盖以太网的 MAC 地址。

如果在 GENERIC 或自定义内核中没有加载无线接口的驱动程序,并且计算机正在运行 FreeBSD 12.1 ,则可以通过在 /boot/loader.conf 文件中添加 driver_load ="YES" 来加载相应的 .ko 文件,并重新启动计算机。另一种更好的方法是通过将驱动程序添加到 /etc/rc.conf 文件中的 kld_list(详见 rc.conf(5))并重新启动来加载驱动程序。这是必需的,因为否则在设置 lagg(4) 接口时驱动程序尚未加载。

在这个例子中,以太网接口 re0 是主接口,无线接口 wlan0 是备份接口。wlan0 接口是从 ath0 物理无线接口创建的,并且以太网接口将配置无线接口的 MAC 地址。首先,将无线接口启动(将 FR 替换为您自己的两个字母的国家代码),但不设置 IP 地址。将 wlan0 替换为与系统的无线接口名称匹配的名称:

# ifconfig wlan0 create wlandev ath0 country FR ssid my_router up

现在您可以确定无线接口的 MAC 地址:

# ifconfig wlan0
wlan0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
	ether b8:ee:65:5b:32:59
	groups: wlan
	ssid Bbox-A3BD2403 channel 6 (2437 MHz 11g ht/20) bssid 00:37:b7:56:4b:60
	regdomain ETSI country FR indoor ecm authmode WPA2/802.11i privacy ON
	deftxkey UNDEF AES-CCM 2:128-bit txpower 30 bmiss 7 scanvalid 60
	protmode CTS ampdulimit 64k ampdudensity 8 shortgi -stbctx stbcrx
	-ldpc wme burst roaming MANUAL
	media: IEEE 802.11 Wireless Ethernet MCS mode 11ng
	status: associated
	nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>

ether 行将包含指定接口的 MAC 地址。现在,将以太网接口的 MAC 地址更改为匹配的地址:

# ifconfig re0 ether b8:ee:65:5b:32:59

确保 re0 接口处于启动状态,然后使用 re0 作为主接口创建 lagg(4) 接口,并设置故障转移至 wlan0

# ifconfig re0 up
# ifconfig lagg0 create
# ifconfig lagg0 up laggproto failover laggport re0 laggport wlan0

虚拟接口应该类似于这样:

# ifconfig lagg0
lagg0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=8<VLAN_MTU>
        ether b8:ee:65:5b:32:59
        laggproto failover lagghash l2,l3,l4
        laggport: re0 flags=5<MASTER,ACTIVE>
        laggport: wlan0 flags=0<>
        groups: lagg
        media: Ethernet autoselect
        status: active

然后,启动 DHCP 客户端以获取 IP 地址:

# dhclient lagg0

要在重新启动后保留此配置,请将以下条目添加到 /etc/rc.conf 文件中:

ifconfig_re0="ether b8:ee:65:5b:32:59"
wlans_ath0="wlan0"
ifconfig_wlan0="WPA"
create_args_wlan0="country FR"
cloned_interfaces="lagg0"
ifconfig_lagg0="up laggproto failover laggport re0 laggport wlan0 DHCP"

34.10. 使用 PXE 进行无盘操作

Intel® Preboot eXecution Environment (PXE) 允许操作系统通过网络引导。例如,FreeBSD 系统可以通过网络引导并在没有本地磁盘的情况下运行,使用从 NFS 服务器挂载的文件系统。PXE 支持通常在 BIOS 中可用。要在机器启动时使用 PXE,在 BIOS 设置中选择 从网络引导(Boot from network) 选项或在系统初始化期间键入功能键。

为了提供操作系统在网络上启动所需的文件,PXE 设置还需要正确配置的 DHCP、TFTP 和 NFS 服务器,其中:

  • 初始参数,如 IP 地址、可执行的引导文件名和位置、服务器名称和根路径,是从 DHCP 服务器获取的。

  • 操作系统加载程序文件使用 TFTP 进行引导。

  • 文件系统使用 NFS 加载。

当计算机进行 PXE 引导时,它通过 DHCP 接收关于获取初始引导加载程序文件的信息。主机计算机接收到这些信息后,通过 TFTP 下载引导加载程序,然后执行引导加载程序。在 FreeBSD 中,引导加载程序文件是 /boot/pxeboot。在 /boot/pxeboot 执行后,FreeBSD 内核被加载,随后进行 FreeBSD 的引导过程,如 FreeBSD引导过程 中所述。

对于基于 UEFI PXE 的引导,要使用的实际引导加载程序文件是 /boot/loader.efi 。请参阅下面的章节 调试 PXE 问题,了解如何使用 /boot/loader.efi

本节介绍了如何在 FreeBSD 系统上配置这些服务,以便其他系统可以通过 PXE 引导进入 FreeBSD。有关更多信息,请参阅 diskless(8)

如上所述,提供这些服务的系统是不安全的。它应该存在于网络的受保护区域,并且其他主机不应信任它。

34.10.1. 设置 PXE 环境

本节中显示的步骤配置了内置的 NFS 和 TFTP 服务器。下一节演示了如何安装和配置 DHCP 服务器。在本示例中,用于 PXE 用户的文件的目录是 /b/tftpboot/FreeBSD/install。重要的是,该目录存在,并且在 /etc/inetd.conf/usr/local/etc/dhcpd.conf 中设置了相同的目录名。

下面的命令示例假设使用 sh(1) shell。csh(1)tcsh(1) 用户需要启动一个 sh(1) shell 或者根据 csh(1) 语法调整命令。

  1. 创建根目录,该目录将包含一个要进行 NFS 挂载的 FreeBSD 安装。

    # export NFSROOTDIR=/b/tftpboot/FreeBSD/install
    # mkdir -p ${NFSROOTDIR}
  2. 通过将以下行添加到 /etc/rc.conf 来启用 NFS 服务器:

    nfs_server_enable="YES"
  3. 通过将以下内容添加到 /etc/exports,将无磁盘根目录通过 NFS 导出:

    /b -ro -alldirs -maproot=root
  4. 启动 NFS 服务器:

    # service nfsd start
  5. 通过将以下行添加到 /etc/rc.conf 来启用 inetd(8)

    inetd_enable="YES"
  6. 请取消注释 /etc/inetd.conf 中的以下行,确保它不以 # 符号开头:

    tftp dgram udp wait root /usr/libexec/tftpd tftpd blocksize 1468 -l -s /b/tftpboot

    指定的 tftp 块大小,例如 1468 字节,替换了默认大小 512 字节。某些 PXE 版本要求使用 TCP 版本的 TFTP 。在这种情况下,取消注释包含 stream tcp 的第二个 tftp 行。

  7. 启动 inetd(8)

    # service inetd start
  8. 将基本系统安装到 ${NFSROOTDIR} 中,可以通过解压官方存档或重新构建 FreeBSD 内核和用户空间来完成(有关更详细的说明,请参阅 “从源代码更新 FreeBSD” ,但在运行 make installkernelmake installworld 命令时不要忘记添加 DESTDIR =${NFSROOTDIR}

  9. 测试 TFTP 服务器是否正常工作,并且能够通过 PXE 下载引导加载程序:

    # tftp localhost
    tftp> get FreeBSD/install/boot/pxeboot
    Received 264951 bytes in 0.1 seconds
  10. 编辑 ${NFSROOTDIR}/etc/fstab 并创建一个条目以通过 NFS 挂载根文件系统:

    # Device                                         Mountpoint    FSType   Options  Dump Pass
    myhost.example.com:/b/tftpboot/FreeBSD/install       /         nfs      ro        0    0

    myhost.example.com 替换为 NFS 服务器的主机名或 IP 地址。在这个例子中,根文件系统以只读方式挂载,以防止 NFS 客户端可能删除根文件系统的内容。

  11. 为通过 PXE 引导的客户机在 PXE 环境中设置根密码:

    # chroot ${NFSROOTDIR}
    # passwd
  12. 如有需要,通过编辑 ${NFSROOTDIR}/etc/ssh/sshd_config 文件并启用 PermitRootLogin 选项,可以为使用 PXE 引导的客户机启用 ssh(1) PermitRootLogin。该选项在 sshd_config(5) 中有详细说明。

  13. ${NFSROOTDIR} 中进行任何其他所需的 PXE 环境自定义。这些自定义可能包括安装软件包或使用 vipw(8) 编辑密码文件。

当从 NFS 根卷引导时,/etc/rc 检测到 NFS 引导并运行 /etc/rc.initdiskless。在这种情况下,/etc/var 需要是内存支持的文件系统,以便这些目录可写,但 NFS 根目录是只读的。

# chroot ${NFSROOTDIR}
# mkdir -p conf/base
# tar -c -v -f conf/base/etc.cpio.gz --format cpio --gzip etc
# tar -c -v -f conf/base/var.cpio.gz --format cpio --gzip var

当系统启动时,将创建并挂载 /etc/var 的内存文件系统,并将 cpio.gz 文件的内容复制到其中。默认情况下,这些文件系统的最大容量为 5 兆字节。如果您的存档不适合,通常是 /var 安装了二进制包的情况下,可以通过在 ${NFSROOTDIR}/conf/base/etc/md_size${NFSROOTDIR}/conf/base/var/md_size 文件中放入所需的 512 字节扇区数(例如,5 兆字节为 10240 扇区)来请求更大的大小,分别用于 /etc/var 文件系统。

34.10.2. 配置 DHCP 服务器

DHCP 服务器不需要与 TFTP 和 NFS 服务器位于同一台机器上,但它需要在网络中可访问。

DHCP 不是 FreeBSD 基本系统的一部分,但可以使用 net/isc-dhcp44-server 端口或 port 进行安装。

安装完成后,编辑配置文件 /usr/local/etc/dhcpd.conf。根据以下示例配置 next-serverfilenameroot-path 设置:

subnet 192.168.0.0 netmask 255.255.255.0 {
   range 192.168.0.2 192.168.0.3 ;
   option subnet-mask 255.255.255.0 ;
   option routers 192.168.0.1 ;
   option broadcast-address 192.168.0.255 ;
   option domain-name-servers 192.168.35.35, 192.168.35.36 ;
   option domain-name "example.com";

   # IP address of TFTP server
   next-server 192.168.0.1 ;

   # path of boot loader obtained via tftp
   filename "FreeBSD/install/boot/pxeboot" ;

   # pxeboot boot loader will try to NFS mount this directory for root FS
   option root-path "192.168.0.1:/b/tftpboot/FreeBSD/install/" ;

}

next-server 指令用于指定 TFTP 服务器的 IP 地址。

filename 指令定义了路径为 /boot/pxeboot 的文件名。使用的是相对路径,这意味着路径中不包括 /b/tftpboot

root-path 选项定义了 NFS 根文件系统的路径。

在保存编辑后,通过将以下行添加到 /etc/rc.conf 来在启动时启用 DHCP :

dhcpd_enable="YES"

然后启动 DHCP 服务:

# service isc-dhcpd start

34.10.3. 调试 PXE 问题

一旦所有服务都配置并启动完成, PXE 客户端应该能够通过网络自动加载 FreeBSD 。如果某个特定的客户端无法连接,当该客户端机器启动时,进入 BIOS 配置菜单并确认其设置为从网络启动。

本节介绍了一些故障排除技巧,用于确定配置问题的根源,以防止任何客户端无法进行 PXE 引导。

  1. 使用 net/wireshark 包或 port 来调试 PXE 引导过程中涉及的网络流量,如下图所示。

    pxe nfs
    图 1. PXE 引导过程与 NFS 根目录挂载
    1. 客户端广播一个 DHCPDISCOVER 消息。

    2. DHCP 服务器响应 IP 地址、next-server、filename 和 root-path 的值。

    3. 客户端向 next-server 发送一个 TFTP 请求,请求检索文件名。

    4. TFTP 服务器响应并向客户端发送文件名。

    5. 客户端执行文件名为 pxeboot(8),然后加载内核。当内核执行时,通过 root-path 指定的根文件系统将通过 NFS 挂载。

  2. 在 TFTP 服务器上,读取 /var/log/xferlog 以确保从正确的位置检索到 pxeboot。要测试此示例配置:

    # tftp 192.168.0.1
    tftp> get FreeBSD/install/boot/pxeboot
    Received 264951 bytes in 0.1 seconds

    tftpd(8)tftp(1) 中的 BUGS 部分记录了 TFTP 的一些限制。

  3. 确保可以通过 NFS 挂载根文件系统。要测试此示例配置,请执行以下操作:

    # mount -t nfs 192.168.0.1:/b/tftpboot/FreeBSD/install /mnt
  4. 对于基于 UEFI PXE 的引导,请将 boot/pxeboot 文件替换为 boot/loader.efi 文件:

# chroot ${NFSROOTDIR}
# mv boot/pxeboot boot/pxeboot.original
# cp boot/loader.efi boot/pxeboot

34.11. 通用地址冗余协议(CARP)

通用地址冗余协议(CARP)允许多个主机共享相同的 IP 地址和虚拟主机 ID(VHID),以提供一个或多个服务的高可用性。这意味着一个或多个主机可能会失败,而其他主机将自动接管,使用户无法看到服务中断。

除了共享的 IP 地址外,每个主机还有自己的 IP 地址用于管理和配置。共享同一个 IP 地址的所有机器具有相同的 VHID 。每个虚拟 IP 地址的 VHID 必须在网络接口的广播域中是唯一的。

在 FreeBSD 中,使用 CARP 实现高可用性是内置的,尽管根据 FreeBSD 的版本不同,配置步骤可能略有不同。本节提供了适用于 FreeBSD 10 版本之前、以及等于或之后版本的相同示例配置。

这个示例配置了三个主机的故障转移支持,每个主机都有唯一的 IP 地址,但提供相同的网页内容。它有两个不同的主服务器,分别命名为 hosta.example.orghostb.example.org ,还有一个共享备份命名为 hostc.example.org

这些机器使用循环轮询 DNS 配置进行负载均衡。主备机器的配置完全相同,只是主机名和管理 IP 地址不同。这些服务器必须具有相同的配置并运行相同的服务。当发生故障切换时,只有当备份服务器可以访问相同的内容时,才能正确地回答共享 IP 地址上的服务请求。备份机器还有两个额外的 CARP 接口,分别用于主内容服务器的每个 IP 地址。当发生故障时,备份服务器将接管失败的主机器的 IP 地址。

34.11.1. 在 FreeBSD 10 及更高版本上使用 CARP

通过在 /boot/loader.conf 中为 carp.ko 内核模块添加条目,启用 CARP 的启动时支持。

carp_load="YES"

立即加载模块而无需重新启动:

# kldload carp

对于喜欢使用自定义内核的用户,请在自定义内核配置文件中包含以下行,并按照 配置 FreeBSD 内核 中描述的方式编译内核。

device	carp

主机名、管理 IP 地址和子网掩码、共享 IP 地址以及 VHID 都是通过向 /etc/rc.conf 添加条目来设置的。以下示例适用于 hosta.example.org

hostname="hosta.example.org"
ifconfig_em0="inet 192.168.1.3 netmask 255.255.255.0"
ifconfig_em0_alias0="inet vhid 1 pass testpass alias 192.168.1.50/32"

下一组条目是针对 hostb.example.org 的。由于它代表了第二个主机,所以它使用了不同的共享 IP 地址和 VHID 。然而,使用 pass 指定的密码必须相同,因为 CARP 只会监听和接受来自具有正确密码的机器的广告。

hostname="hostb.example.org"
ifconfig_em0="inet 192.168.1.4 netmask 255.255.255.0"
ifconfig_em0_alias0="inet vhid 2 pass testpass alias 192.168.1.51/32"

第三台机器 hostc.example.org 配置为处理来自任一主机的故障转移。该机器配置了两个 CARPVHID,一个用于处理每个主机的虚拟 IP 地址。 CARP 广告偏差 advskew 被设置为确保备份主机比主机广告晚,因为 advskew 控制多个备份服务器时的优先顺序。

hostname="hostc.example.org"
ifconfig_em0="inet 192.168.1.5 netmask 255.255.255.0"
ifconfig_em0_alias0="inet vhid 1 advskew 100 pass testpass alias 192.168.1.50/32"
ifconfig_em0_alias1="inet vhid 2 advskew 100 pass testpass alias 192.168.1.51/32"

配置了两个 CARPVHID 意味着 hostc.example.org 将会注意到主服务器中的任何一个不可用。如果主服务器在备份服务器之前无法进行广告宣传,备份服务器将会接管共享 IP 地址,直到主服务器再次可用。

如果原始主服务器再次可用,hostc.example.org 不会自动将虚拟 IP 地址释放给它。要实现这一点,必须启用抢占功能。该功能默认情况下是禁用的,可以通过 sysctl(8) 变量 net.inet.carp.preempt 来控制。管理员可以强制备份服务器将 IP 地址返回给主服务器:

# ifconfig em0 vhid 1 state backup

配置完成后,要么重新启动网络,要么重新启动每个系统。高可用性现在已启用。

CARP 功能可以通过 sysctl(8) 变量来控制,这些变量在 carp(4) 手册页中有详细说明。还可以使用 devd(8) 来触发 CARP 事件的其他操作。

34.11.2. 在 FreeBSD 9 及更早版本上使用 CARP

这些版本的 FreeBSD 的配置与前一节中描述的类似,只是首先必须创建一个 CARP 设备并在配置中引用它。

通过在 /boot/loader.conf 中加载 if_carp.ko 内核模块,启用 CARP 的启动时支持。

if_carp_load="YES"

立即加载模块而无需重新启动:

# kldload carp

对于喜欢使用自定义内核的用户,请在自定义内核配置文件中包含以下行,并按照 配置 FreeBSD 内核 中描述的方式编译内核。

device	carp

接下来,在每台主机上创建一个 CARP 设备:

# ifconfig carp0 create

通过在 /etc/rc.conf 中添加所需的行,设置主机名、管理 IP 地址、共享 IP 地址和 VHID。由于使用虚拟 CARP 设备而不是别名,所以使用实际的子网掩码 /24 而不是 /32。以下是 hosta.example.org 的条目:

hostname="hosta.example.org"
ifconfig_fxp0="inet 192.168.1.3 netmask 255.255.255.0"
cloned_interfaces="carp0"
ifconfig_carp0="vhid 1 pass testpass 192.168.1.50/24"

hostb.example.org 上:

hostname="hostb.example.org"
ifconfig_fxp0="inet 192.168.1.4 netmask 255.255.255.0"
cloned_interfaces="carp0"
ifconfig_carp0="vhid 2 pass testpass 192.168.1.51/24"

第三台机器 hostc.example.org 配置为处理来自任一主机的故障转移:

hostname="hostc.example.org"
ifconfig_fxp0="inet 192.168.1.5 netmask 255.255.255.0"
cloned_interfaces="carp0 carp1"
ifconfig_carp0="vhid 1 advskew 100 pass testpass 192.168.1.50/24"
ifconfig_carp1="vhid 2 advskew 100 pass testpass 192.168.1.51/24"

GENERIC FreeBSD 内核中禁用了抢占。如果使用自定义内核启用了抢占,hostc.example.org 可能无法将 IP 地址释放回原始内容服务器。管理员可以使用以下命令强制备份服务器将 IP 地址返回给主服务器:

# ifconfig carp0 down && ifconfig carp0 up

这应该在与正确主机对应的 carp 接口上完成。

配置完成后,要么重新启动网络,要么重新启动每个系统。高可用性现在已启用。

34.12. 虚拟局域网(VLANs)

VLAN(虚拟局域网)是一种将网络虚拟分割成许多不同子网络的方法,也被称为分段。每个分段都有自己的广播域,并与其他 VLAN 隔离开来。

在 FreeBSD 上,VLAN 必须由网络适配器驱动程序支持。要查看哪些驱动程序支持 VLAN ,请参考 vlan(4) 手册页。

配置 VLAN 时,需要了解一些信息。首先,是哪个网络接口?其次, VLAN 标签是什么?

要在运行时配置 VLAN ,使用网卡 em0 和 VLAN 标签 5 ,命令如下:

# ifconfig em0.5 create vlan 5 vlandev em0 inet 192.168.20.20/24

看到接口名称包括了网卡驱动名称和 VLAN 标签,用句点分隔吗?这是一种最佳实践,可以在一个机器上存在多个 VLAN 时,方便维护 VLAN 配置。

要在启动时配置 VLAN ,必须更新 /etc/rc.conf 文件。要复制上述配置,需要添加以下内容:

vlans_em0="5"
ifconfig_em0_5="inet 192.168.20.20/24"

可以通过简单地将标签添加到 vlans_em0 字段,并添加一行额外的配置网络的代码来添加其他 VLAN。

为了在关联的硬件发生更改时只需要更新少量配置变量,给接口分配一个符号名称是很有用的。例如,安全摄像头需要在 em0 上运行 VLAN 1 。如果以后将 em0 卡替换为使用 ixgb(4) 驱动程序的卡,所有对 em0.1 的引用都不需要更改为 ixgb0.1

要配置 VLAN 5,在网卡 em0 上分配接口名称 cameras,并为接口分配 IP 地址 192.168.20.20 ,使用 24 位前缀,使用以下命令:

# ifconfig em0.5 create vlan 5 vlandev em0 name cameras inet 192.168.20.20/24

对于名为 video 的接口,请使用以下内容:

# ifconfig video.5 create vlan 5 vlandev video name cameras inet 192.168.20.20/24

要在启动时应用更改,请将以下行添加到 /etc/rc.conf 文件中:

vlans_video="cameras"
create_args_cameras="vlan 5"
ifconfig_cameras="inet 192.168.20.20/24"

上次修改时间: September 18, 2024 by fiercex