首页 WLAN 笔记
文章
取消

WLAN 笔记

本文主要记录 Linux 与无线网络相关的一些知识和操作笔记,包括:如何编译无线网卡的驱动、如何利用 dkms 在内核更新时自动编译驱动模块、以及 hostapd、wpa_supplicant、iw 等实用工具的用法。

内核模块

在 Linux 中,驱动一般都是通过内核模块的形式提供。内核模块分为 内置模块可加载模块:内置模块是静态编译进内核的,无需通过 modprobeinsmodrmmod 来加载、卸载,核心模块一般会内置到内核;而大部分驱动,都是可加载模块,即 *.ko kernel object 内核对象文件。

kernel object 和 object 都是对象文件,但是 kernel object 不能被链接或者直接运行,需要使用 insmodmodprobe 来加载它,让它成为内核的一部分,扩展内核的功能。不需要时,可以使用 rmmodmodprobe 来卸载它,回收内存以及相应的资源。

默认情况下,Linux 发行版已经自带了大部分硬件的驱动模块,一般放在 /usr/lib/modules/$(uname -r)/kernel 目录下,以 *.ko*.ko.gz 等形式存放,因此可以通过 find 命令,查看当前系统是否已自带指定模块。

驱动编译

当我们插入一张无线网卡时(假设为 USB 无线网卡),如果系统已自带驱动,那么使用 ip addr 就可以看到对应的接口,否则说明系统没有自带相应驱动,需自行编译。

我们先使用 lsusb 命令查看网卡的型号,这是 RPi3B 的输出:

1
2
3
4
5
$ lsusb
Bus 001 Device 004: ID 0b95:1790 ASIX Electronics Corp. AX88179 Gigabit Ethernet
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. SMC9514 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

其中第一个就是我插入的 USB 转 RJ45 千兆网卡,可以看到对应的网卡型号为 AX88179,可以通过 Google 来找到对应的网卡驱动(一般官网都会提供的,不过实际上,这块网卡的驱动 ArchLinux 已经自带了,所以一插上去我就能看到对应的 eth1 接口)。

然后下载对应的驱动源码包,解压,一般都有 makefile 文件,熟悉编译安装的同学应该很容易知道,其实就是使用 make 编译。编译之前,需要安装一些依赖和头文件,make、gcc 这些不必多说,必备;此外我们还需要获取 linux 内核头文件,在 ArchLinux 中,安装与内核版本对应的 linux-headers 包就可以了,注意必须是与 linux 包相同版本的;如果版本不对应,建议先更新系统,再安装。

接下来,查看 readme.txt 或其他说明文件,一般情况下,直接 make 就可以了。如果没有报错,会在当前目录下生成 modname.ko;使用 modinfo modname.ko 可以查看这个模块的信息;使用 insmod modname.ko 加载此模块;如果一切顺利,此时使用 ip addr 即可看到新网卡;不需要时,可以使用 rmmod modname 卸载模块。

如果 insmod 时出错,提示符号找不到;可先使用 modprobe mac80211 插入 mac80211 模块,因为无线网卡的驱动依赖这个模块,而 insmod 不会处理依赖。可以使用 modinfo 来查看模块的依赖信息。


如果 lsusb 或 lspci 没有显示型号怎么办?

这问题还是挺常见的,比如 360wifi3,只有一个 MediaTek。像这种怎么获取网卡的具体型号呢?看到 lsusb 输出行的 ID 字段没?如果是 lspci,带上参数 -nn 才会显示 ID。假设 ID 为 0bda:8153,你只要打开 Google,搜索此 ID 一般就能够找到对应的网卡型号。

这个 ID 到底是什么意思呢?它分为两个部分,0bda8153,前者是厂商 ID(Vendor ID),后者是设备 ID(Device ID)或产品 ID(Product ID)。每个硬件都有其对应的 VID、PID:VID 需要向相关机构申请,是全球唯一的,每个厂商的 VID 都是不同的,而 PID 则由厂商自己决定,可以利用 VID、PID 查找对应的硬件型号。注意,VID 和 PID 并不是 Linux 独有的概念,它是硬件的概念,在 Windows 上同样存在,使用设备管理器可以看到对应的 VID、PID。

https://devicehunt.com:输入 VID 和 PID 即可查找硬件型号等信息,支持 PCI 和 USB 两种总线类型。

自动加载

前面介绍了如何使用 insmod 来装载驱动,但你可能也发现,当我们插入网卡时,系统并不会自动加载我们编译的驱动模块,需要手动 insmod 来加载。能不能像其它自带驱动一样,让系统自动检测硬件,并载入对应的驱动模块呢?当然是可以的,步骤如下:

  • modname.ko 文件放到 /usr/lib/modules/$(uname -r)/kernel/drivers 的相应子目录下(自行分类),当然实际上放在哪并没有要求,但分类总是好的,别人一看就知道这是什么类型的驱动,如果实在不知道放哪,也可以放到 /usr/lib/modules/$(uname -r)/extra 目录;

  • 使用 depmod -a 重新建立内核模块的依赖关系,它会更新 /usr/lib/modules/$(uname -r)/modules.* 相关文件;这些信息会被 modprobe 使用,用来分析和解决模块的依赖关系。

现在我们再插入网卡,udev 守护进程就会根据 VendorID:DeviceID 来加载驱动模块了;不过当我们拔出网卡时,udev 并不会自动卸载模块,需要我们手动卸载;但通常没必要这样做,让 udev 自动管理即可。

除了借助 udev,也可使用 modprobe modname 来加载模块,卸载模块则用 modprobe -r modname


系统如何知道一个硬件要使用哪个驱动模块?

其实就是根据 VID/PID 来识别的。驱动模块通常会定义一些 alias,使用 modinfo modname | grep alias 可以看到相关的 VID、PID,它表示这个驱动可以用在哪些硬件。当新硬件插入时,udev 会先获取硬件的 VID/PID,然后查找系统中的 alias 文件(/usr/lib/modules/$(uname -r)/modules.alias),就知道该使用哪个驱动了。

如何将一个驱动强制用于某个硬件设备?

/sys/module/<modname>/drivers/<bus:modname>/new_id 文件中添加对应硬件的 VID、PID 就可以了,如 echo VID PID >/path/to/new_id。但是,你必须确定该驱动能够支持此硬件,否则强加进去也是没用的,甚至还可能会导致内核崩溃。

dkms 用法

更新内核后,如何让手动添加的驱动能够正常使用?

内核模块和内核版本是紧密相关的,一个内核模块只能在某个内核版本运行。当我们更新内核版本后,必须在新内核环境中,重新编译相关的内核模块。

当我们使用经常更新内核版本的发行版时,会非常麻烦。比如 ArchLinux,时不时就会更新内核版本。我可不想每次更新内核都去重新编译内核模块,怎么办呢?

答案是 dkms(Dynamic Kernel Module Support,动态内核模块支持),我们可以将模块源码交给 dkms 管理。内核更新时,dkms 会自动编译适用于新版本的内核模块。

首先安装 dkms:pacman -S dkms,然后运行 dkms 查看使用帮助:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ dkms
Usage: /usr/bin/dkms [action] [options]
  [action]  = { add | remove | build | install | uninstall | match | autoinstall
               | mkdriverdisk | mktarball | ldtarball | mkrpm | mkkmp | mkdeb | status }
  [options] = [-m module] [-v module-version] [-k kernel-version] [-a arch]
              [-d distro] [-c dkms.conf-location] [-q] [--force] [--all]
              [--templatekernel=kernel] [--directive='cli-directive=cli-value']
              [--config=kernel-.config-location] [--archive=tarball-location]
              [--kernelsourcedir=source-location] [--no-prepare-kernel] [--no-initrd]
              [--binaries-only] [--source-only] [-r release (SuSE)] [--verbose]
              [--size] [--spec=specfile] [--media=floppy|iso|tar] [--legacy-postinst=0|1]
              [--no-depmod] [-j number] [--version]
Error! No action was specified.

常用的 action 有:

  • add:将模块添加到构建系统
  • remove:将模块从构建系统移除
  • status:查看构建系统中的模块状态
  • build:构建内核模块(make)
  • install:安装内核模块到相关目录
  • uninstall:取消安装内核模块(变为 built 状态)

常用的 options 有:

  • -m module:指定模块名称
  • -v module-version:指定模块版本
  • -k kernel-version:指定内核版本和 architecture
  • -c dkms.conf-location:指定 dkms.conf 文件
  • --all:所有相关的内核版本和 architecture
  • --verbose:输出详细信息

这里以 rtl8812au 无线网卡驱动为例,README.md 说明如下:

1
2
3
4
5
6
7
8
DRV_NAME=rtl8812au
DRV_VERSION=4.3.20
mkdir /usr/src/${DRV_NAME}-${DRV_VERSION}
cp -af . /usr/src/${DRV_NAME}-${DRV_VERSION}/ # 将源码和相关文件copy到此目录
dkms add -m ${DRV_NAME} -v ${DRV_VERSION}
dkms build -m ${DRV_NAME} -v ${DRV_VERSION}
dkms install -m ${DRV_NAME} -v ${DRV_VERSION}
dkms status -m ${DRV_NAME} -v ${DRV_VERSION}

首先,将想要被 dkms 管理的内核模块源码目录复制到 /usr/src 目录下,源码目录的命名是有规定的,即 $name-$version,前面是模块名,后面是模块版本。后面的 -m-v 参数指定的就是这两个东西。当然要被 dkms 管理还没这么简单,你需要在 /usr/src/$name-$version/ 目录下创建 dkms.conf 配置文件,告诉 dkms 一些必要的信息,这是 rtl8812au 的 dkms.conf 文件内容:

1
2
3
4
5
6
7
PACKAGE_NAME="rtl8812au"
PACKAGE_VERSION="4.3.14"
CLEAN="make clean"
MAKE[0]="make KVER=$kernelver"
BUILT_MODULE_NAME[0]="rtl8812au"
DEST_MODULE_LOCATION[0]="/kernel/drivers/net"
AUTOINSTALL="yes"
  • PACKAGE_NAME:模板名称
  • PACKAGE_VERSION:模块版本
  • CLEAN:清理命令,如 make clean
  • MAKE[0]:编译模块的命令,如 make
  • BUILT_MODULE_NAME[0]:生成的内核模块名
  • DEST_MODULE_LOCATION[0]:安装路径,相对于 /usr/lib/modules/$(uname -r)
  • AUTOINSTALL:是否自动安装该模块,具体我也不太清楚,请参见相关 man 文档

在 dkms.conf 中,可以引用一些预定义的变量,比如上面的 $kernelver:当前内核版本。

dkms.conf 是一个包含变量定义的 shell 脚本,变量名应全部大写,可以执行一些 shell 语句,如:

1
2
3
4
5
6
7
8
9
10
PACKAGE_NAME="@PKGBASE@"
PACKAGE_VERSION="@PKGVER@"
PROCS_NUM=`nproc`
[ $PROCS_NUM -gt 16 ] && PROCS_NUM=16
MAKE="'make' -j$PROCS_NUM KVER=${kernelver}"
CLEAN="'make' clean"
BUILT_MODULE_NAME[0]="rtl8192eu"
DEST_MODULE_LOCATION[0]="/extra"
AUTOINSTALL="yes"
REMAKE_INITRD="yes"

如果需要让 dkms 不再管理相关模块,可以使用 dkms remove 模块名/模块版本 --all

另外,dkms.conf 中的模块名要和 make 编译出来的保持一致,否则 dkms build 可能报错。

认证与加密

安全协议

  • WEP:Wired Equivalent Privacy(有线等效加密),不安全,不建议使用。
  • WPA:Wi-Fi Protected Access(WiFi 保护访问)第一版,有漏洞,不是很建议。
  • WPA2:Wi-Fi Protected Access(WiFi 保护访问)第二版,目前来说,建议使用。

WPA/WPA2 分为个人版、企业版:

  • WPA/WPA2 个人版:使用 pre-shared key(PSK,预共享密钥),较简单
  • WPA/WPA2 企业版:需要一台额外的认证服务器,大型企业使用,比较复杂

加密方式

  • TKIP:WPA 中的加密方式,使用 RC4 加密算法,不安全。
  • CCMP:WPA2 中的加密方式,使用 AES 加密算法,更安全。

  • 在 WPA 中,TKIP 为默认方式,CCMP 为可选方式;
  • 在 WPA2 中,CCMP 为默认方式,TKIP 为可选方式。

WiFi 联盟要求 IEEE 802.11n 使用 WPA2 和 CCMP,TKIP 不可用于 802.11n 的传输,它仅支持传统的 802.11a、802.11b、802.11g 传输,最高速率为 54Mbps。

无线网络标准

主要有三个:802.11a、802.11b/g/n、802.11ac

  • 802.11a:标准吞吐量为 54Mbps,工作在 5GHz 频段。
  • 802.11b:标准吞吐量为 11Mbps,工作在 2.4GHz 频段。
  • 802.11g:标准吞吐量为 54Mbps,工作在 2.4GHz 频段,兼容 802.11b。
  • 802.11n:标准吞吐量为 300Mbps,工作在 2.4GHz/5GHz 频段,兼容 802.11a/b/g。
  • 802.11ac:标准吞吐量为 1Gbps,工作在 5GHz,兼容 802.11a/b/g/n(需降为 2.4GHz)。

主要讨论 802.11n、802.11ac,一般来说,802.11n 工作在 2.4GHz 频段,802.11ac 工作在 5GHz 频段。

802.11n 是在 802.11g 的基础上改良的,802.11ac 则是在 802.11a 的基础上改良的。因此,在 hostapd.conf 中,802.11n 的配置是 hw_mode=gieee80211n=1、802.11ac 的配置是 hw_mode=aieee80211ac=1

部分 802.11ac 无线网卡支持“双频”工作模式,所谓双频是指同时支持 802.11n、802.11ac 两种标准,即这种网卡可以发射/接收 2.4G 的 WiFi,也可以发射/接收 5.8G 的 WiFi,但是同时只能工作在一种模式下,不可同时开启。因此所谓的 dual-band 双频 AP,只是有两张物理网卡而已,一个处理 2.4G、一个处理 5.8G。


802.11n:信道选择

2.4G 频段有 13 个左右交叠的信道(中国),其中只能找出 3 个相互不重合的信道(具体请参考最后一节),最常用的就是 1、6、11 这三个,当然也可以使用其他没有重叠的组合,但是由于一些国家法律不允许使用 12 或 13 信道,所以这个组合是兼容性最好的。如下图所示:

WiFi 信道选择图解

  • 如果选择 1、6、11 信道,只会被同信道的设备干扰;
  • 如果选择其他信道,会同时被多个信道的设备干扰。

802.11n:频宽选择

在路由器设置页面,我们可以选择 20MHz 和 40MHz 两种频宽(有些还支持 20/40MHz 自动模式)。可以将频宽理解为传输数据的道路宽度,因此理论上,40MHz 的传输速度会比 20MHz 快很多。但由于 2.4GHz 下有很多干扰源(很多设备都工作在此频段,比如无线蓝牙,无线键鼠,微波炉等),因此不可避免的存在大量干扰,所以实际体验中,40MHz 可能还不如 20MHz 稳定&快速。

简单来说,如果你周围的 WiFi 很多,请使用 20MHz 频宽;如果周围没有什么 WiFi,则可以选择 40MHz 频宽,速度快一些。如果路由器支持自动频宽选择,也可以试试选择自动模式。

注意,40MHz 频宽不是所有设备都支持的,如果路由器支持,但客户端不支持,则会自动回退到 20MHz,只有两者都支持 40MHz 才会正常工作。

hostapd 在开启 40MHz 支持时,会在启动时扫描附近是否已经有使用 40MHz 的 WiFi,如果有则会自动降为 20MHz。在 ArchLinux 的 hostapd 中,打了 noscan 的补丁,因此可以在 hostapd.conf 中加入 noscan=1 告诉 hostapd 不要扫描附近的 WiFi 频宽,具体的配置如下:

1
2
3
4
5
6
# 启用 802.11n 支持
ieee80211n=1
# 启用 20MHz/40MHz 支持
ht_capab=[HT40+][SHORT-GI-40]
# patch,不扫描周围的 WiFi
noscan=1

hostapd 中的国家/地区代码有什么用?

因为每个国家的 WiFi 允许的频段和信道可能不同,因此需要设置国家/地区代码。

AP 与 STA 模式

  • AP 模式:Access Point 无线接入点,发送 WiFi 信号的模式,如无线路由。
  • STA 模式:Station 无线终端,接收 WiFi 信号的模式,如笔记本,手机等。

  • AP 模式需要的软件包:hostapd
  • STA 模式需要的软件包:wpa_supplicant
  • 查看/管理 无线网卡 的工具:iw

hostapd 配置

802.11b/g/n with WPA2-PSK and CCMP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface=wlan0       # the interface used by the AP
driver=nl80211        # use nl80211 driver (default: hostap)
hw_mode=g             # g simply means 2.4GHz band
channel=10            # the channel to use
country_code=FR       # the country code
ieee80211d=1          # limit the frequencies used to those allowed in the country
ieee80211h=1          # same to "ieee80211d=1"
ieee80211n=1          # 802.11n support
wmm_enabled=1         # QoS support

ssid=somename         # the name of the AP
auth_algs=1           # 1=wpa, 2=wep, 3=both
wpa=2                 # WPA2 only
wpa_key_mgmt=WPA-PSK  
rsn_pairwise=CCMP
wpa_passphrase=somepassword

802.11a/n/ac with WPA2-PSK and CCMP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface=wlan0       # the interface used by the AP
driver=nl80211        # use nl80211 driver (default: hostap)
hw_mode=a             # a simply means 5GHz
channel=0             # the channel to use, 0 means the AP will search for the channel with the least interferences 
country_code=FR       # the country code
ieee80211d=1          # limit the frequencies used to those allowed in the country
ieee80211h=1          # same to "ieee80211d=1"
ieee80211n=1          # 802.11n support
ieee80211ac=1         # 802.11ac support
wmm_enabled=1         # QoS support

ssid=somename         # the name of the AP
auth_algs=1           # 1=wpa, 2=wep, 3=both
wpa=2                 # WPA2 only
wpa_key_mgmt=WPA-PSK 
rsn_pairwise=CCMP
wpa_passphrase=somepassword

802.11b/g/n triple AP(单网卡,多 SSID/AP,需驱动支持)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
interface=wlan0       # the interface used by the AP
driver=nl80211        # use nl80211 driver (default: hostap)
hw_mode=g             # g simply means 2.4GHz
channel=10            # the channel to use
country_code=FR       # the country code
ieee80211d=1          # limit the frequencies used to those allowed in the country
ieee80211h=1          # same to "ieee80211d=1"
ieee80211n=1          # 802.11n support
wmm_enabled=1         # QoS support

# First AP
ssid=test1            # the name of the AP
auth_algs=1           # 1=wpa, 2=wep, 3=both
wpa=2                 # WPA2 only
wpa_key_mgmt=WPA-PSK 
rsn_pairwise=CCMP
wpa_passphrase=somepassword

# Seconf AP
bss=wlan1             # the name of the new interface hostapd will create to handle this AP 
ssid=test2            # the name of the AP
auth_algs=1           # 1=wpa, 2=wep, 3=both
wpa=1                 # WPA1 only
wpa_key_mgmt=WPA-PSK 
wpa_passphrase=someotherpassword

# Third AP
bss=wlan2             # the name of the new interface hostapd will create to handle this AP 
ssid=test3
# since there is no encryption defined, none will be used

其它配置:

1
2
3
4
5
6
7
8
9
# 加入桥接网络
bridge=br0

# 不广播/隐藏 SSID
ignore_broadcast_ssid=1

# 20MHz/40MHz 频宽 (2.4G)
ht_capab=[HT40-][HT40+][SHORT-GI-20][SHORT-GI-40]
noscan=1    # archlinux的patch功能,前面有提到

RPI3B 板载 WiFi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface=wlan0
driver=nl80211
bridge=br0

hw_mode=g
channel=11
ieee80211d=1
ieee80211h=1
country_code=CN
ieee80211n=1
wmm_enabled=1

ssid=RPi3B
ignore_broadcast_ssid=0
auth_algs=1
wpa=2
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
wpa_passphrase=somepassword

Tenda U12 2.4GHz 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface=wlan1
driver=nl80211
bridge=br0

hw_mode=g
channel=1
ieee80211d=1
ieee80211h=1
country_code=CN
ieee80211n=1
ht_capab=[HT40-][HT40+][SHORT-GI-20][SHORT-GI-40][RX-STBC1][DSSS_CCK-40]
noscan=1    # archlinux的patch功能,前面有提到
wmm_enabled=1

ssid=U12-2.4G
ignore_broadcast_ssid=0
auth_algs=1
wpa=2
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
wpa_passphrase=somepassword

Tenda U12 5.8GHz 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface=wlan1
driver=nl80211
bridge=br0

hw_mode=a
channel=149
ieee80211d=1
ieee80211h=1
country_code=CN
ieee80211n=1
ieee80211ac=1
vht_capab=[SHORT-GI-80][MAX-MPDU-11454][TX-STBC-2BY1][RX-STBC-1][MAX-A-MPDU-LEN-EXP7]
noscan=1    # archlinux的patch功能,前面有提到
wmm_enabled=1

ssid=U12-5.8G
ignore_broadcast_ssid=0
auth_algs=1
wpa=2
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
wpa_passphrase=somepassword

STA 配置

安装 wpa_supplicant 软件包就可以了,hostapd 用于创建 AP,wpa_supplicant 用于连接 AP。它们都需要运行一个守护进程,守护进程的名字同软件包的名字。

wpa_supplicant 软件包的 3 个主要命令:

  • wpa_cli:wpa_supplicant 的命令行接口,交互配置工具,调试时常用。
  • wpa_passphrase:生成 network 配置段的工具,默认使用密文保存密码。
  • wpa_supplicant:核心守护进程,配置文件目录 /etc/wpa_supplicant/

使用 iw 扫描可用 wifi

连接 WiFi 前,一般先 scan 下附近可用的 WiFi,可以使用 iw 来扫描。

扫描前,先使用 ip link set wlan0 up 启用接口,然后 iw dev wlan0 scan


使用 wpa_supplicant 连接 wifi

这是一个极简的 /etc/wpa_supplicant/wpa_supplicant.conf 配置文件:

1
2
3
4
5
6
7
8
9
10
11
ctrl_interface=/run/wpa_supplicant
update_config=1
country=CN

network={
    # 因为这个 AP 的 SSID 隐藏了,所以设置 scan_ssid=1
    scan_ssid=1
    ssid="TL"
    # psk="somepassword" # 也可以使用明文密码,wpa_passphrase 生成的是密文
    psk=794086310d3cb629fb9b2217658a3a3fd685cce88ce51984b57111fb463b8ac6
}

network 段可以使用 wpa_passphrase 来生成:wpa_passphrase <SSID> <密码>

启动:wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf

如果一切顺利,现在使用 dhcpcd wlan0 就可以接入网络了,当然也可以配置静态 IP。


使用 netctl 连接 wifi

在 ArchLinux 中,也可以使用 netctl 来连接 wifi,省去 wpa_supplicant 的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Description='A simple WPA encrypted wireless connection'
Interface=wlan0
Connection=wireless

Security=wpa
IP=dhcp

ESSID='MyNetwork'
# Prepend hexadecimal keys with \"
# If your key starts with ", write it as '""<key>"'
# See also: the section on special quoting rules in netctl.profile(5)
Key='WirelessKey'
# Uncomment this if your ssid is hidden
#Hidden=yes
# Set a priority for automatic profile selection
#Priority=10

iw 命令用法

  • iw dev:查看接口信息(简短)
  • iw dev wlan0 info:查看指定接口信息(简短)

  • iw list:查看设备信息(详细)
  • iw phy:查看设备信息(详细)
  • iw phy phy0 info:查看指定设备信息(详细)
  • iw phy phy0 channels:查看指定设备的信道信息

  • iw dev wlan0 scan:查看附近的 WiFi 信息
  • iw dev wlan0 connect param...:连接到指定 WiFi(仅支持 WEP)
  • iw dev wlan0 disconnect:断开当前连接的 WiFi

  • iw dev wlan0 link:查看当前连接的 WiFi 信息
  • iw dev wlan0 station dump [-v]:查看指定接口连接的 WiFi 信息

因为 iw dev wlan0 scan 的信息非常多,因此这里提供一个简单的解析脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#!/bin/bash

if [ -z "$1" ]; then
    echo "Usage: $0 <NIC>"
    exit 1
fi

awk_script=$(cat << 'EOF'
$1 == "BSS" {
    split($2, arr, "(");
    MAC = arr["1"];
    results[MAC]["enc"] = "Open";
}

$1 == "freq:" {
    results[MAC]["freq"] = $2;
}

$1 == "signal:" {
    results[MAC]["signal"] = $2 " " $3;
}

$1 == "SSID:" {
    if (length($2) == 0)
        SSID = "<HIDDEN>";
    else
        SSID = $2;
    results[MAC]["ssid"] = SSID;
}

/^\s*HT[0-9]{2}/ {
    results[MAC]["bandwidth"] = $1;
}

/^\s*\* primary channel: [0-9]+/ {
    results[MAC]["channel"] = $4;
}

$1 == "WEP:" {
    enc = results[MAC]["enc"];
    if (enc == "Open")
        results[MAC]["enc"] = "WEP";
}

$1 == "WPA:" {
    enc = results[MAC]["enc"];
    if (enc == "Open" || enc == "WEP")
        results[MAC]["enc"] = "WPA";
}

$1 == "RSN:" {
    enc = results[MAC]["enc"];
    if (enc == "Open" || enc == "WEP" || enc == "WPA")
        results[MAC]["enc"] = "WPA2";
}

END {
    print("       MAC                     SSID   Frequency     Signal     BandWidth   Channel   Security");
    for (mac in results)
        printf("%17s   %15s   %4s MHz   %11s   %9s   %5s   %8s\n",
               mac, results[mac]["ssid"], results[mac]["freq"],
               results[mac]["signal"], results[mac]["bandwidth"],
               results[mac]["channel"], results[mac]["enc"]);
}
EOF
)

if [ "$(ip link set dev $1 up &> /dev/null; echo $?)" -ne 0 ]; then
    echo "Cannot find device '$1'"
    exit 1
fi

echo -e "\e[1m$(awk "$awk_script" < /dev/null)\e[0m"
iw dev $1 scan 2> /dev/null | awk "$awk_script" | tail +2 | sort -nrk5

用法很简单,将 wifi-scan 放在 PATH 路径下,然后 wifi-scan wlan0 就可以了。

其他无线知识

  • 有线网络:全双工,可以同时发送和接收数据,互不影响,相当于有两条道路。
  • 无线网络:半双工,同一时间只能发送或接收,两个状态之间需要不断的切换。

一条网线同一时间只能与两个设备相连,这很容易理解。而一条无线同一时间也只能与两个设备相连,即 AP 和 Sta 之间是一对一连接的。那为什么多个 Sta 连接到同一个 AP 却可以同时上网呢?

其实是 AP 与多个 Sta 之间不断切换造成的假象,比如第一个 Sta 通信 10ms,然后立即切换到第二个 Sta,与之通信 10ms,以此类推。这和单核心计算机上运行多个程序是一样的,都是不断切换来实现的。

基于这个特点,将无线 AP 当作无线的交换机是不太可取的,特别是设备多,网络负载比较重时,性能下降明显。当然,家用或设备不多的情况下,拿来当无线交换机还是可以的。


在无线传输领域,存在 MIMO 这样一个技术,MIMO 即 multiple-input and multiple-output,多输入多输出。MIMO 在 WLAN、3G、4G 中被广泛使用。

802.11n 在速率上的提升很大程度上都归功于 MIMO 技术,那 MIMO 究竟是什么东西呢?其实就是多天线,MIMO 不要求 AP 和 Sta 都有多天线,但是如果 AP 和 Sta 的天线数量相同(或 AP 端更多),效果最好。

MIMO 的每条天线都可以同时发送数据或同时接收数据,将传输流从一条变成了多条,因此传输速率将翻倍。假设一条天线的速率是 150Mbps,那么两条天线的速率就是 150*2=300Mbps,三条天线的速率就是 150*3=450Mbps,四条天线的速率就是 150*4=600Mbps。802.11n 最多允许 4 条天线,即最大 600Mbps。

两个设备之间的传输速率取决于天线少的一方,假设 AP 有 4 条天线,频宽为 40MHz(则单天线速率为 150Mbps),如果 Sta 也有 4 条天线,那么协商速率就是 600Mbps,如果只有两条,那么就是 300Mbps,一条的话就只有 150Mbps 了。


上一段提到,只有在 40MHz 的频宽下,单天线才有 150Mbps(这里仅针对 802.11n 讨论)。如果频宽为 20MHz,那么单天线速率只有 72.2 Mbps,双天线速率就是 144Mbps 了。

这也是为啥高通手机连接 802.11n 的速率大多是 72Mbps(单天线),144Mbps(双天线)。因为高通手机默认关闭了 40MHz 的频宽,需要修改 /system/etc/wifi/WCNSS_qcom_cfg.ini 配置文件(需要 root 权限),将 gChannelBondingMode24GHz 改为 1,启用 40MHz 频宽支持。


802.11n 时代的 MIMO 属于 SU-MIMO(Single User MIMO,单用户 MIMO),所谓单用户是指即使你有多个天线,一个 AP 同时也只能与一个 Sta 进行通信。这和开头说的一条网线同时只能与两个设备连接是一样的,本质没有改变,只不过“这根网线”的传输速率增加了。

既然有 SU-MIMO,就有 MU-MIMO(多用户 MIMO),这是 802.11ac wave2 中提出的,意思是:同一个 AP 同一时间内可以与多个支持 MU-MIMO 的 Sta 进行通信(目前仅支持下行链路,即 AP -> Sta 方向)。多用户 MIMO 听起来是很美好的,但因为不支持上行链路,且多条天线之间可能会造成干扰,可能会导致速率比单用户 MIMO 低。


802.11n 支持的频宽(band-width)有 20MHz、40MHz,而 802.11ac 支持的频宽有 20MHz、40MHz、80MHz、160MHz。频宽越大,传输速率就越大,但因为“传输的道路”变大了,所以可能被邻边的信道干扰,在 WiFi 多的环境中,一味的提高频宽可能没有效果,甚至还会降低原本的速率。

802.11n 最多可以有 4 条天线

  • 单天线时,20MHz 频宽的速率为:72.2Mbps
  • 单天线时,40MHz 频宽的速率为:150Mbps

802.11ac 最多可以有 8 条天线

  • 单天线时,20MHz 频宽的速率为:87.6Mbps
  • 单天线时,40MHz 频宽的速率为:200Mbps
  • 单天线时,80MHz 频宽的速率为:433.3Mbps
  • 单天线时,160MHz 频宽的速率为:866.7Mbps(wave2)

对于 802.11n,中国允许使用的信道为:1-13。但 12、13 可能兼容性不好,部分无线网卡不支持 12、13 信道,搜不到信号。因此实际上常用的为 1-11。在 20MHz 频宽下,建议使用 1、6、11 信道。在 40MHz 频宽下,建议使用 1、11 信道。

对于 802.11ac,建议使用 80MHz 频宽的信道,充分发挥速度优势。下表是每个频宽对应的信道列表。PS:中国允许使用的信道都是只支持 20MHz 频宽的,建议 802.11ac 网络不要选择 CN,用 US 比较好。

网上说 CN 地区建议使用 149 信道,干扰较少,速度比较快,比较稳定。

802.11ac 相关信道对应的频宽

本文由作者按照 CC BY 4.0 进行授权

C语言 进程间通信 管道

Zig MIPS 软浮点问题