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) `__ 驱动程序: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # kldload ng_ubt 如果在系统启动期间将蓝牙设备连接到系统,则可以通过将驱动程序添加到 **/boot/loader.conf**\ ,将系统配置为在引导时加载模块: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session ng_ubt_load="YES" 加载驱动程序后,插入 USB 蓝牙适配器。如果驱动程序加载成功,则类似于以下内容的输出应显示在控制台和 **/var/log/messages** 中: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 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) `__\ 。启动堆栈时,输出应类似于以下内容: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # 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> 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) `__\ 。 最常见的任务之一是发现射频邻近范围内的蓝牙设备。此操作称为 *轮询*\ 。轮询和其他与 HCI 相关的操作均使用 `hccontrol(8) `__ 完成。以下示例显示了如何找出哪些蓝牙设备在范围内。设备列表应在几秒钟内显示。请注意,远程设备只有在设置为 *可发现* 模式时才会回答查询。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session % 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** 中。下面的示例演示如何获取分配给远程设备的人类可读名称: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session % 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) `__ 中找到。 蓝牙系统在两个蓝牙设备之间提供点对点连接,或在多个蓝牙设备之间共享点对多点连接。下面的示例演示如何创建与远程设备的连接: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session % hccontrol -n ubt0hci create_connection BT_ADDR ``create_connection`` 接受 ``BT_ADDR`` 以及 ``/etc/bluetooth/hosts`` 中的主机别名 下面的示例演示如何获取本地设备的活动基带连接列表: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session % 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 当需要终止基带连接时,\ *连接柄* 很有用,尽管通常不需要手动执行此操作。堆栈将自动终止非活动基带连接。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # hccontrol -n ubt0hci disconnect 41 Connection handle: 41 Reason: Connection terminated by local host [0x16] 键入 ``hccontrol help`` 以获得可用 HCI 命令的完整清单。大多数 HCI 命令不需要超级用户权限。 34.7.3.设备配对 --------------- 默认情况下,蓝牙通信是不需要认证的,任何设备都可以与任何其他设备通信。蓝牙设备,如手机,可以选择要求认证以提供特定服务。蓝牙认证通常是通过 PIN 码完成的,这是一个长度不超过 16 个字符的 ASCII 字符串。用户需要在两个设备上输入相同的 *PIN 码*\ 。一旦用户输入了 PIN 码,两台设备将生成一个 *链接密钥*\ 。之后,链接密钥可以存储在设备中或持久性存储中。下一次,两台设备将使用先前生成的链接密钥。这个过程被称为 *配对*\ 。注意,如果任何一个设备丢失了链路密钥,必须重新配对。 `hcsecd(8) `__ 守护程序负责处理蓝牙认证请求。默认的配置文件是 **/etc/bluetooth/hcsecd.conf**\ 。下面是一个手机的例子部分,PIN 码设置为 ``1234``\ : .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 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) `__ 配置为在系统启动时自动启动: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session hcsecd_enable="YES" 以下是 `hcsecd(8) `__ 守护程序输出的示例: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 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 配置文件的网络访问可用于为单个蓝牙设备或多个蓝牙设备提供 LAN 访问。它还可以通过串行电缆仿真使用 PPP 网络提供 PC 到 PC 连接。 在 FreeBSD 中,这些配置文件是用 `ppp(8) `__ 和 `rfcomm_pppd(8) `__ 包装器实现的,它把蓝牙连接转换成 PPP 可以使用的东西。在使用配置文件之前,必须在 **/etc/ppp/ppp.conf** 中创建新的 PPP 标签。有关示例,请参考 `rfcomm_pppd(8) `__\ 。 在本例中,\ `rfcomm_pppd(8) `__ 用于在 DUNRFCOMM 通道上以 ``00:80:37:29:19:a4`` 的 ``BD_ADDR`` 打开到远端设备的连接: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # 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 服务器。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # rfcomm_pppd -s -C 7 -l rfcomm-server 34.7.5.蓝牙协议 --------------- 本节概述了各种蓝牙协议、其功能和相关实用程序。 34.7.5.1.逻辑链路控制和适配协议(L2CAP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 逻辑链路控制和适配协议(L2CAP)为上层协议提供面向连接和无连接的数据服务。L2CAP 允许更高级别的协议和应用程序发送和接收长度高达 64 KB 的 L2CAP 数据包。 L2CAP 基于 *信道* 的概念。信道是基带连接之上的逻辑连接,其中每个信道都以多对一的方式绑定到单个协议。多个信道可以绑定到同一协议,但一个信道不能绑定到多个协议。在信道上接收的每个 L2CAP 数据包都定向到相应的更高级别的协议。多个信道可以共享同一基带连接。 在 FreeBSD 中,为每个蓝牙设备创建一个 netgraph L2CAP 节点。此节点通常连接到下游蓝牙 HCI 节点和上游蓝牙插座节点。L2CAP 节点的默认名称是 ``devicel2cap``\ 。有关更多详细信息,请参阅 `ng_l2cap(4) `__\ 。 一个有用的命令是 `l2ping(8) `__\ ,它可以用来 ping 其他设备。一些蓝牙实现可能不会返回所有发送给它们的数据,所以下面例子中的 ``0 bytes`` 是正常的。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # 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 节点上执行各种操作。此示例演示如何获取本地设备的逻辑连接(信道)列表和基带连接列表: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session % 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) `__ 相同的逻辑连接。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session % 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 浏览查询。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session % 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)服务: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session % sdpcontrol -a 00:01:03:fc:6e:ec search OPUSH 在 FreeBSD 上向蓝牙客户端提供服务是通过 `sdpd(8) `__ 服务器完成的。以下行可以添加到 **/etc/rc.conf** 中: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session sdpd_enable="YES" 然后 `sdpd(8) `__ 守护程序可以启动: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # service sdpd start 要向远程客户端提供蓝牙服务的本地服务器应用程序将向本地 SDP 守护程序注册该服务。此类应用程序的一个例子是 `rfcomm_pppd(8) `__\ 。启动后,它将向本地 SDP 守护程序注册蓝牙 LAN 服务。 通过本地控制信道发出 SDP 浏览查询,可以获得在本地 SDP 服务器注册的服务列表: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # sdpcontrol -l browse 34.7.5.4.OBEX 对象推送(OPUSH) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 对象交换(OBEX)是一种广泛使用的协议,用于移动设备之间的简单文件传输。它的主要用途是红外通信,用于笔记本电脑或 PDA 之间的通用文件传输,以及用于在手机和其他具有个人信息管理器(PIM)应用程序的设备之间发送名片或日历条目。 OBEX 服务器和客户端由 obexapp 实现,可以使用软件包或 port 来安装 `comms/obexapp `__\ 。 OBE X 客户端被用来从 OBEX 服务器推送和/或拉取对象。例如是一张名片或一个约会的对象。OBEX 客户端可以通过 SDP 从远程设备获得 RFCOMM 频道号码。这可以通过指定服务名称而不是 RFCOMM 信道号码来实现。支持的服务名称是:IrMC、FTRN 和 OPUSH。也可以把 RFCOMM 信道指定为一个数字。下面是一个 OBEX 会话的示例,其中从移动电话中提取设备信息对象,并将一个新对象(名片)推入电话的目录。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session % 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/sline/obex**\ 。最后,在有效的 RFCOMM 信道号上启动 OBEX 服务器。OBEX 服务器将自动向本地 SDP 守护程序注册 OPUSH 服务。下面的示例显示了如何启动 OBEX 服务器。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # obexapp -s -C 10 34.7.5.5.串行端口配置文件(SPP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 串行端口配置文件(SPP)允许蓝牙设备执行串行电缆仿真。此配置文件允许传统应用程序通过虚拟串行端口抽象使用蓝牙作为电缆替代品。 在 FreeBSD 中,\ `rfcomm_sppd(1) `__ 实现了 SPP,伪 tty 被用作虚拟串行端口抽象。以下示例显示了如何连接到远程设备的串行端口服务。RFCOMM 信道不必指定,因为 `rfcomm_sppd(1) `__ 可以通过 SDP 从远程设备获取它。若要重写此值,请在命令行上指定 RFCOMM 信道。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # rfcomm_sppd -a 00:07:E0:00:0B:CA -t rfcomm_sppd[94692]: Starting on /dev/pts/6... /dev/pts/6 连接后,伪 tty 可用作串行端口: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # cu -l /dev/pts/6 伪 tty 打印在 stdout 上,可以通过包装脚本读取: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session PTS=`rfcomm_sppd -a 00:07:E0:00:0B:CA -t` cu -l $PTS 34.7.6.故障排除 --------------- 默认情况下,当 FreeBSD 接受新连接时,它会尝试执行角色切换并成为主节点。某些不支持角色切换的旧版蓝牙设备将无法连接。由于角色切换是在建立新连接时执行的,因此无法询问远程设备是否支持角色切换。但是,有一个 HCI 选项用于在本地端禁用角色切换: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # hccontrol -n ubt0hci write_node_role_switch 0 要显示蓝牙数据包,请使用第三方软件包 hcidump,该软件包可以使用软件包或 port 来安装 `comms/hcidump `__\ 。此实用程序类似于 `tcpdump(1) `__\ ,可用于在终端上显示蓝牙数据包的内容,并将蓝牙数据包转储到文件中。