先让我们从一个工程中遇到的实际问题开始,先上log:
D/WMT_ENV ( 57): get sysparaethaddr
W/BestCast( 57): httpSimplePostfailed
W/BestCast( 57): bestcastLoginfailed
E/hostapd ( 100): random: Cannot readfrom /dev/random: Try again
I/hostapd ( 100): random: Only 0/20bytes of strong random data available from /dev/random
I/hostapd ( 100): random: Not enoughentropy pool available for secure operations
I/hostapd ( 100): WPA: Note[ 22.722265] RTL871X: set group key to hw: alg:2(WEP40-1 WEP104-5TKIP-2 AES-4) keyid:1
nough entropy in random pool to proceed -reject first 4-way handshake
[ 22.738445] RTL871X: sendeapol packet
D/WMT_ENV ( 57): get sysparabestId
E/WMT_ENV ( 57): MEMGETENV getbestId error: -1 I/O error
D/WMT_ENV ( 57): get sysparaclientId
E/WMT_ENV ( 57): MEMGETENV getclientId error: -1 I/O error
D/WMT_ENV ( 57): get sysparaclientName
E/WMT_ENV ( 57): MEMGETENV getclientName error: -1 I/O error
D/WMT_ENV ( 57): get sysparaethaddr
W/BestCast( 57): httpSimplePostfailed
W/BestCast( 57): bestcastLoginfailed
以上是hostapd在接受一个wifi终端的接入鉴权时的一段log,该段log显示,在开始四步握手鉴权时,需要获取随机数,而此时/dev/random却未能够提供足够的随机数熵(entropy),导致鉴权不能正常进行。
那为什么/dev/random不能提供足够的随机数呢,为了解析这个,得从他们的根源分析起。
/dev/random和/dev/urandom是unix系统提供的产生随机数的设备,很多应用都需要使用random设备提供的随机数,比如ssh keys, SSL keys, TCP/IP sequence numbers等等
而random设备的random pool是从基于中断的IRQS里面取值,IRQS跟一些特殊的硬件绑定,基于这些硬件的interrupts将会提供给random设备。
以下模拟一个从/dev/random取值但是/dev/random取不到足够值的情况,这时候取值的进程将会等待,直到得到足够的random 值。
rubbitxiao@szmce15:~$ time dd if=/dev/random of=1.dmp bs=1024k count=100 &
[1] 25398
这时dd的进程将会hang住等待足够的random值
rubbitxiao@szmce15:~$ ps
PID TTY TIME CMD
24315 pts/11 00:00:00 bash
25398 pts/11 00:00:00 bash
25399 pts/11 00:00:00 dd
25400 pts/11 00:00:00 ps
用strace跟踪dd进程在做什么
rubbitxiao@szmce15:~$ sudo strace -p 25399
[sudo] password for rubbitxiao:
Process 25399 attached - interrupt to quit
read(0, "\256Yi\314\266\351\1\366", 1048576) = 8
write(1, "\256Yi\314\266\351\1\366", 8) = 8
read(0, "wYyV\264\362K\23", 1048576) = 8
write(1, "wYyV\264\362K\23", 8) = 8
read(0, "Cm\220>uy\260\376", 1048576) = 8
write(1, "Cm\220>uy\260\376", 8) = 8
read(0, "\365\217\302yk\177\234\244", 1048576) = 8
write(1, "\365\217\302yk\177\234\244", 8) = 8
read(0, "\24,\226l\216\203E\322", 1048576) = 8
write(1, "\24,\226l\216\203E\322", 8) = 8
read(0, "t\273\27\237\r\243\2164", 1048576) = 8
write(1, "t\273\27\237\r\243\2164", 8) = 8
read(0, "\232x\n \337M\313/", 1048576) = 8
write(1, "\232x\n \337M\313/", 8) = 8
read(0, "\227\251\212\264o\30~\327", 1048576) = 8
write(1, "\227\251\212\264o\30~\327", 8) = 8
read(0, "y\21\20\213cAS\260", 1048576) = 8
write(1, "y\21\20\213cAS\260", 8) = 8
read(0, "p\355\356\303\36\35\350\206\323", 1048576) = 9
write(1, "p\355\356\303\36\35\350\206\323", 9) = 9
read(0, "&\1b\32\262L\3\33\10", 1048576) = 9
write(1, "&\1b\32\262L\3\33\10", 9) = 9
read(0, "O\30\372\374\t-7\36", 1048576) = 8
write(1, "O\30\372\374\t-7\36", 8) = 8
read(0, "]\277\22\364\260\217\254>", 1048576) = 8
write(1, "]\277\22\364\260\217\254>", 8) = 8
read(0, "R,\0227\307\300\275}", 1048576) = 8
write(1, "R,\0227\307\300\275}", 8) = 8
read(0, "p^\356V&7\223w\271", 1048576) = 9
write(1, "p^\356V&7\223w\271", 9) = 9
read(0, "t\267\325_\7\227\303\313", 1048576) = 8
write(1, "t\267\325_\7\227\303\313", 8) = 8
read(0, "\216DA_\340\211\ts", 1048576) = 8
write(1, "\216DA_\340\211\ts", 8) = 8
read(0, "jl\366D\1\25o\315<", 1048576) = 9
write(1, "jl\366D\1\25o\315<", 9) = 9
read(0, "\375\266\253\36\234\255I\n", 1048576) = 8
write(1, "\375\266\253\36\234\255I\n", 8) = 8
read(0, "h\216j\3046\315>{", 1048576) = 8
write(1, "h\216j\3046\315>{", 8) = 8
read(0, "\270\267\33S\314\354= ", 1048576) = 8
write(1, "\270\267\33S\314\354= ", 8) = 8
read(0, ");\361\356\363\316_\242", 1048576) = 8
... ...
write(1, "V\261\373h\267\0104+", 8) = 8
read(0, "\4\327\335S\304\24\243\362", 1048576) = 8
write(1, "\4\327\335S\304\24\243\362", 8) = 8
read(0, "0\0b\27\363\\\217\"", 1048576) = 8
write(1, "0\0b\27\363\\\217\"", 8) = 8
close(0) = 0
close(1) = 0
write(2, "0+100 records in\n0+100 records o"..., 350+100 records in
0+100 records out
) = 35
write(2, "807 bytes (807 B) copied", 24807 bytes (807 B) copied) = 24
write(2, ", 1407.68 s, 0.0 kB/s\n", 22, 1407.68 s, 0.0 kB/s
) = 22
close(2) = 0
exit_group(0) = ?
Process 25399 detached
real 23m27.695s
user 0m0.012s
sys 0m0.000s
[1]+ Done time dd if=/dev/random of=1.dmp bs=1024k count=100
以上可以看出,从/dev/random读取(100*1024K个)随机数,由于中间会阻塞(dd hang),所以总计花了23分钟27秒才完成。为什么会花费这么长的时间,因为它的随机数的提供是依赖与外部中断事件的,如果没有足够多中断事件,就会阻塞,其实为了加速/dev/random提供随机数的速度,你可以通过操作设备的外设,让其产生大量的中断(如网络传输数据,按键,移动鼠标等)。
是否有足够的熵来用于产生随机数,可以通过如下命令来查看:
cat /proc/sys/kernel/random/entropy_avail
rubbitxiao@szmce15:~$ cat /proc/sys/kernel/random/entropy_avail
277
接下来我们看/dev/urandom,从它那里取同样多的随机数,
rubbitxiao@szmce15:~$
rubbitxiao@szmce15:~$ time dd if=/dev/urandom of=1.dmp bs=1024k count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 6.38387 s, 16.4 MB/s
real 0m6.385s
user 0m0.000s
sys 0m6.364s
rubbitxiao@szmce15:~$
却只需要花费6.385秒,同样的机器上,/dev/urandom不受interrupts的限制,即使没有足够的interrupt它也能通过 random number generator产生足够的输出值,所以它不会导致dd hang
至此可以看出/dev/urandom与/dev/random的区别,前者不受外部中断的影响,照样可以产生随机数,而后者则受系统外部中断的影响,所以如果取较多随机数,可能会导致应用会hang住。在我们开篇的那个log,就是属于这个问题。