使用zypper升级系统的时候,可能下载失败,下面总结了两种原因。
1)网络丢包率太高(可以用ping命令查看),这种情况好处理,重试就行了。
我在使用Tumbleweed,没找到大陆的mirror,用的是官方的源。可以使用release版,选用就近的源,这种问题应该就没有了。
2)curl做DNS查询获得的是两个ipv6地址,没有ipv4地址。只要这个情况不消除,重试是没有用的。
从我拿到的证据来看,是路由器的DNS代理有问题。我试过有效的方法:
- 使用proxychains给zypper增加代理,比如"sudo proxychains zypper patch"。我用的时候每次都能用,就是DNS查询性能不够好。
- "sudo vi /etc/resolv.conf"编辑resolv.conf,注释掉"nameserver 192.168.0.1",把ISP的域名服务器放在最前面,另外在下面添加"nameserver 8.8.8.8"备用(这是Google提供的DNS服务器)。用了比较久了,没再出问题(不包括重试就好的情况)。
如果zypper能像curl跟wget一样强制使用ipv4,那也不会有问题。
花费了很多时间以后才找到问题,除了调试工具不熟悉之外,有以下几个原因:
- 发现问题以后再去做试验,这个时候拿到的试验数据跟之前出错的时候很可能是不一样的,这样会产生误导。再想遇到一次一模一样的错误原因,并不容易。
- 一直不相信DNS查询会被自己的路由器弄坏。
- curl不够严谨,查询ipv4地址的时候,如果拿到的是ipv6地址,欣然接受,没有任何抱怨。
- curl给出的错误信息过于简陋,有误导性质,比如,ipv4无法访问也不说,只说ipv6地址无法访问。
- 浏览器上访问opensuse.org很顺畅,是因为我在代理模式里添加了,但是我忘了,以至于我认为访问ipv4地址一定能拿到数据。
下面是一些过程记录,可略过。
------
刚开始的时候,我在使用mirrors.ustc.edu.cn,那时候就遇见过一次这样的问题:
> zypper lp Download (curl) error for 'http://mirrors.ustc.edu.cn/opensuse/distribution/12.3/repo/oss/content': Error code: Connection failed Error message: Failed to connect to 2001:da8:d800:95::110: Network is unreachable在浏览器能访问mirrors.ustc.edu.cn,当时是把mirrors.ustc.edu.cn替换成了mirrors4.ustc.edu.cn,这个域名只支持ipv4,没有问题。
------
后来使用Tumbleweed,升级的时候,如果有几十个升级包,通常是会因为下载错误中断的。马上重试不好使,要不以后再试,要不用proxychains解决,这样用了一段时间。proxychains无法缓存DNS查询结果,速度不快,所以就看看有没有别的方法来解决这个问题。
> sudo zypper update ... Retrieving: typelib-1_0-Gcr-3-3.8.2-3.12.x86_64.rpm .................................................................................................................[error] Download (curl) error for 'http://download.opensuse.org/repositories/openSUSE:/Tumbleweed/standard/x86_64/typelib-1_0-Gcr-3-3.8.2-3.12.x86_64.rpm': Error code: Connection failed Error message: Failed to connect to 2001:67c:2178:8::13: Network is unreachable
先测试了一下download.opensuse.org域名:
> ping -c 1 download.opensuse.org PING download.opensuse.org (195.135.221.134) 56(84) bytes of data. 64 bytes from ftp.opensuse.org (195.135.221.134): icmp_seq=1 ttl=46 time=337 ms
> nslookup download.opensuse.org Non-authoritative answer: Name: download.opensuse.org Address: 195.135.221.134"dig download.opensuse.org"返回类似的结果。后来发现"host download.opensuse.org"可以返回ipv4跟ipv6地址。
ipv4地址是对的,能在浏览器打开,内容正确。
看看curl查看head的具体调用过程:
> strace -o ~/strace-curl-1.txt curl --head http://download.opensuse.org/repositories/openSUSE:/Tumbleweed/standard/x86_64/typelib-1_0-Gcr-3-3.8.2-3.12.x86_64.rpm curl: (7) Failed to connect to 2001:67c:2178:8::13: Network is unreachable
后来打开Wireshark,重试curl查看head,这时候已经没问题了。
> strace -o ~/strace-curl-2.txt curl --head http://download.opensuse.org/repositories/openSUSE:/Tumbleweed/standard/x86_64/typelib-1_0-Gcr-3-3.8.2-3.12.x86_64.rpm HTTP/1.1 302 Found ...Wireshark里边有一段DNS解析的记录:
| No. | Time | Source | Destination | Protocol | Length | Info |
| 1 | 0.000000000 | 192.168.0.2 | 192.168.0.1 | DNS | 81 | Standard query 0xb2b6 A download.opensuse.org |
| 2 | 0.000026000 | 192.168.0.2 | 192.168.0.1 | DNS | 81 | Standard query 0xbac0 AAAA download.opensuse.org |
| 3 | 0.229767000 | 192.168.0.1 | 192.168.0.2 | DNS | 109 | Standard query response 0xbac0 AAAA 2001:67c:2178:8::13 |
| 4 | 0.367153000 | 192.168.0.1 | 192.168.0.2 | DNS | 97 | Standard query response 0xb2b6 A 195.135.221.134 |
把strace-curl-*.txt里边的"0x[0-9a-f]+"替换为空,然后用diff命令生成了一个差异文件,关键的一个区别是在:
< socket(PF_INET6, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_TCP) = 3
< connect(3, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "2001:67c:2178:8::13", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = -1 ENETUNREACH (Network is unreachable)
> socket(PF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_TCP) = 3
> connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("195.135.221.134")}, 16) = -1 EINPROGRESS (Operation now in progress)
没看出出错原因。后来试过同样的curl查看head,又不能用了,用Wireshark抓包,没有DNS查询请求,应该是使用了缓存下来的ipv6地址。
到了这一步,不想再纠结具体的原因,估计是curl不知道不能用ipv6(网络不支持ipv6)。curl可以强制使用ipv4/6,选项是--ipv4/6。
> curl --ipv6 --head http://download.opensuse.org/repositories/openSUSE:/Tumbleweed/standard/x86_64/typelib-1_0-Gcr-3-3.8.2-3.12.x86_64.rpm curl: (7) Failed to connect to 2001:67c:2178:8::13: Network is unreachable为了让zypper产生这样的效果,在"vi ~/.curlrc"加入"--ipv4",保存退出,在"/root/.curlrc"做同样的修改。只测试了curl,没有测试zypper。
------
昨天以为改了.curlrc就好了,今天升级的时候测试了一下,还不行,没想到zypper只是使用libcurl,不使用.curlrc配置文件。
看看"disable aaaa lookup",看到很多类似的问题,没有好的解决办法。后来禁用了ipv6,目前使用的方法是在/etc/sysctl.conf文件末尾添加如下几行(具体可参考Disable IPv6 on Ubuntu to Speed Up Browsing - Author: Brian Yang - www.techairlines.com):
net.ipv6.conf.all.disable_ipv6 = 1 net.ipv6.conf.default.disable_ipv6 = 1 net.ipv6.conf.lo.disable_ipv6 = 1重启系统,可以用"cat /proc/sys/net/ipv6/conf/all/disable_ipv6" | "ip a" | "/sbin/ifconfig"查看ipv6是否生效。
用"sudo /usr/sbin/tcpdump port 53"查看DNS查询,nslookup跟dig只有A查询,wget / curl / zypper还是有AAAA查询,不知道在使用的时候会不会检查是否禁用了ipv6。这次升级了很多安装包,没有出现中断,以后再看看。
从"man resolv.conf"来看,DNS查询是glibc在控制,暂时没法禁用AAAA查询。顺便改了一个配置:默认情况下,A查询跟AAAA查询是并发进行的,某些服务器无法处理这种情况,可以在"/etc/resolv.conf"添加"options single-request"来保证顺序。
删除~/.curlrc,用改进的strace命令重新试了一次:
> strace -fx -o ~/strace-curl-3.txt curl --head http://download.opensuse.org/repositories/openSUSE:/Tumbleweed/standard/x86_64/typelib-1_0-Gcr-3-3.8.2-3.12.x86_64.rpm HTTP/1.1 404 Not Found ...从strace-curl-3.txt来看,里面有两个进程的调用记录。进程4856尝试用UDP方式连接195.135.221.134跟2001:67c:2178:8::13,其中一个成功,另一个失败。在进程4856退出之后,进程4855有了一个到195.135.221.134的TCP请求。相关记录如下:
Pre[-]
4856 open("/etc/gai.conf", O_RDONLY|O_CLOEXEC) = 3
...
4856 socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
4856 connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("195.135.221.134")}, 16) = 0
4856 close(3) = 0
4856 socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3
4856 connect(3, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "2001:67c:2178:8::13", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = -1 ENETUNREACH (Network is unreachable)
4856 close(3) = 0
...
4856 +++ exited with 0 +++
4855 <... futex resumed> ) = 0
4855 socket(PF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_TCP) = 3
4855 setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0
4855 setsockopt(3, SOL_TCP, TCP_KEEPIDLE, [60], 4) = 0
4855 setsockopt(3, SOL_TCP, TCP_KEEPINTVL, [60], 4) = 0
4855 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
4855 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
4855 connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("195.135.221.134")}, 16) = -1 EINPROGRESS (Operation now in progress)
getaddrinfo函数可以返回多个结果,包括ipv4跟ipv6。在"man getaddrinfo"里面发现一段话:
Normally, the application should try using the addresses in the order in which they are returned. The sorting function used within getaddrinfo() is defined in RFC 3484; the order can be tweaked for a particular system by editing /etc/gai.conf (available since glibc 2.5).
RFC3484有下面一段话:
Well-behaved applications SHOULD iterate through the list of addresses returned from getaddrinfo() until they find a working address.
从这两段解释来看,curl上面的行为符合要求,可是之前的怎么就错了呢?现在没有证据,如果下次还出现这样的错误,那再看看。
> sudo vi /etc/gai.conf
启用注释掉的"precedence ::ffff:0:0/96 100"。
作用:"For sites which prefer IPv4 connections change the last line to precedence ::ffff:0:0/96 100"。这样的话,getaddrinfo函数会把ipv4地址排在前面返回,但是应用程序可能按照自己的意愿来使用这些地址。
------
下载了curl源码,在curl-7.30.0/lib目录查询"grep SOCK_DGRAM *.c",有几个文件包含SOCK_DGRAM,其中有hostip6.c的Curl_ipv6works函数:
Pre[-]
/*
* Curl_ipv6works() returns TRUE if ipv6 seems to work.
*/
bool Curl_ipv6works(void)
{
/* the nature of most system is that IPv6 status doesn't come and go
during a program's lifetime so we only probe the first time and then we
have the info kept for fast re-use */
static int ipv6_works = -1;
if(-1 == ipv6_works) {
/* probe to see if we have a working IPv6 stack */
curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
if(s == CURL_SOCKET_BAD)
/* an ipv6 address was requested but we can't get/use one */
ipv6_works = 0;
else {
ipv6_works = 1;
Curl_closesocket(NULL, s);
}
}
return (ipv6_works>0)?TRUE:FALSE;
}
4855 open("/home/u1/.curlrc", O_RDONLY) = -1 ENOENT (No such file or directory)
...
4855 socket(PF_INET6, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_IP) = 3
4855 close(3) = 0
"socket(PF_INET6, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_IP)"对应的就是"socket(PF_INET6, SOCK_DGRAM, 0)"。通过"man -K IPPROTO_IP"跟"less /usr/include/netinet/in.h"就能看到"IPPROTO_IP = 0, /* Dummy protocol for TCP. */";SOCK_CLOEXEC可以忽略。在上面的设置都生效的情况下,Curl_ipv6works得到的结果是ipv6可能好使。
源码里有两个常见的宏:ENABLE_IPV6 / CURLRES_IPV6,如果启用了,那就会查询ipv6地址,不论系统有没有禁用ipv6。curl是系统自带的,这两个宏应该是启用了,因为:version.c的version_info里面使用了ENABLE_IPV6;版本信息里面有IPv6;curl做了ipv6地址查询。
> curl --version curl 7.28.1 (x86_64-suse-linux-gnu) libcurl/7.28.1 OpenSSL/1.0.1e zlib/1.2.7 libidn/1.25 libssh2/1.4.3 Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP如果从源码编译安装,那可以禁用ipv6(configure有--disable-ipv6选项),从而禁用AAAA查询,这样应该能解决问题。我还没这么试过,如果以后再出现这样的问题,并且无法解决,那就试试。
清空DNS缓存,重新测试了一次,同时打开了Wireshark:
> sudo /sbin/service nscd restart > strace -fx -o ~/strace-curl-5.txt curl --head download.opensuse.org HTTP/1.1 200 OK ...从strace-curl-5.txt来看,有两次到/var/run/nscd/socket的请求,应该就是两次DNS查询。现在应该是做了ip地址重排序,实际结果跟以前一样。其他的没啥区别。
从Wireshark来看,DNS查询确实是有序了(A查询获得响应之后才有AAAA查询)。
connect.c有以下调用过程:Curl_is_connected > trynextip > singleipconnect > Curl_socket。
从这个逻辑来看,如果有正确的ip地址,那curl应该不会出错。
再看strace-curl-1.txt,发现一个之前忽略的地方:有两次到2001:67c:2178:8::13的SOCK_STREAM连接。相关记录如下:
Pre[-]
socket(PF_INET6, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_TCP) = 3
setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0
setsockopt(3, SOL_TCP, TCP_KEEPIDLE, [60], 4) = 0
setsockopt(3, SOL_TCP, TCP_KEEPINTVL, [60], 4) = 0
fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
connect(3, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "2001:67c:2178:8::13", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = -1 ENETUNREACH (Network is unreachable)
...
getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
close(3) = 0
socket(PF_INET6, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_TCP) = 3
setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0
setsockopt(3, SOL_TCP, TCP_KEEPIDLE, [60], 4) = 0
setsockopt(3, SOL_TCP, TCP_KEEPINTVL, [60], 4) = 0
fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
connect(3, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "2001:67c:2178:8::13", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = -1 ENETUNREACH (Network is unreachable)
getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
close(3) = 0
> strace -o ~/strace-curl-1-1.txt curl --ipv6 --head download.opensuse.org > strace -fx -o ~/strace-curl-1-2.txt curl --ipv6 --head download.opensuse.orgstrace记录显示只有一次到2001:67c:2178:8::13的SOCK_STREAM连接,也就是说,如果一个ip连接失败,那是不会重试的。
我只能猜测上面两次connect是因为拿到了两个ipv6地址,并且没有ipv4地址,但是无法确定是哪部分出错。
------
这几天都没有遇到问题,直到安装一个软件包的时候出错。
已经开着tcpdump,只抓取DNS查询:
15:58:07.985780 IP 192.168.0.2.52211 > 192.168.0.1.domain: 49645+ A? download.opensuse.org. (39) 15:58:07.986996 IP 192.168.0.1.domain > 192.168.0.2.52211: 49645 1/0/0 A 195.135.221.134 (55) 15:58:07.987175 IP 192.168.0.2.56832 > 192.168.0.1.domain: 20860+ AAAA? download.opensuse.org. (39) 15:58:10.577528 IP 192.168.0.1.domain > 192.168.0.2.56832: 20860 1/0/0 AAAA 2001:67c:2178:8::13 (67)没问题。
看看zypper有没有可能指定使用ipv6:
> grep CURLOPT_URL -r libzypp-12.9.0-1.1.1.src/libzypp-12.9.0/zypp/ libzypp-12.9.0-1.1.1.src/libzypp-12.9.0/zypp/media/MediaCurl.cc: CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL, urlBuffer.c_str() ); libzypp-12.9.0-1.1.1.src/libzypp-12.9.0/zypp/media/MediaCurl.cc: CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL, urlBuffer.c_str() ); libzypp-12.9.0-1.1.1.src/libzypp-12.9.0/zypp/media/MediaMultiCurl.cc: curl_easy_setopt(_curl, CURLOPT_URL, _urlbuf.c_str());看起来设置的都是url,不是直接指定ip地址,所以,还得从curl解决,试试编译安装curl,禁用ipv6。
configure选项可以用"./configure --help"查看。
> sudo zypper install libidn-devel libssh2-devel > ./configure --prefix=/usr/ --disable-ipv6 --enable-threaded-resolver --with-libidn --with-libssh2 > make > sudo make install
下面提到的这两个选项都是为了开启AsynchDNS。如果开启--enable-ares,那AAAA查询是没了,但是不使用DNS缓存。如果开启--enable-threaded-resolver,那还是有AAAA查询,用起来还是这个更顺畅,系统自带的curl用的是这个resolver。
如果要开启--enable-ares,那需要安装libcares-devel,先从源安装试试,不行的话,可以参考http://curl.haxx.se/dev/readme-ares.html,从源码编译安装。
> ./configure --prefix=/usr/ ; make ; sudo make install
测试编译安装的curl的时候,有时候会下载超时,用Wireshark抓包发现一些有问题的TCP包(retransmission/dup),可能是网络问题。
如果从源安装新版本curl,那编译安装的会被覆盖,需要重新编译安装。
如果不想用编译安装的curl了,那可以用"sudo zypper install --force libcurl4 curl"强制从源下载安装。
就先这样,如果再出错,那应该会提示ipv4地址的错误了。
------
有一天关电脑之前测试了一下,没想到运气这么好,直接出错,没有开strace跟Wireshark。不过,确实不报ipv6地址的错误了。
> zypper lp Download (curl) error for 'http://download.opensuse.org/distribution/openSUSE-current/repo/non-oss/media.1/media': Error code: Connection failed Error message: Failed connect to download.opensuse.org:80; Operation now in progress重试就好了。
> man connect
EINPROGRESS
The socket is nonblocking and the connection cannot be completed immediately. It is possible to select(2) or poll(2) for completion by selecting the socket for writing. After select(2) indicates writability, use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error codes listed here, explaining the reason for the failure).
第二天继续看。EINPROGRESS是指连接正在建立,正在建立报什么错?
用strace试出来一样的错,是因为建立连接超时,之前一直以为所有的超时都会报Timeout这样的错,所以,看到EINPROGRESS的时候就没怀疑超时,先看了一阵源码,没找到问题才用strace。片段如下:
Pre[-]
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0
setsockopt(3, SOL_TCP, TCP_KEEPIDLE, [60], 4) = 0
setsockopt(3, SOL_TCP, TCP_KEEPINTVL, [60], 4) = 0
fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("195.135.221.134")}, 16) = -1 EINPROGRESS (Operation now in progress)
poll([{fd=3, events=POLLOUT|POLLWRNORM}], 1, 0) = 0 (Timeout)
poll([{fd=3, events=POLLOUT}], 1, 1000) = 0 (Timeout)
...
poll([{fd=3, events=POLLOUT}], 1, 1000) = 0 (Timeout)
poll([{fd=3, events=POLLOUT|POLLWRNORM}], 1, 0) = 0 (Timeout)
poll([{fd=3, events=POLLOUT}], 1, 1000) = 1 ([{fd=3, revents=POLLOUT|POLLERR|POLLHUP}])
poll([{fd=3, events=POLLOUT|POLLWRNORM}], 1, 0) = 1 ([{fd=3, revents=POLLOUT|POLLWRNORM|POLLERR|POLLHUP}])
getsockopt(3, SOL_SOCKET, SO_ERROR, [110], [4]) = 0
...
socket(PF_INET, SOCK_STREAM, IPPROTO_UDP) = -1 EPROTONOSUPPORT (Protocol not supported)
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 4
setsockopt(4, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0
setsockopt(4, SOL_TCP, TCP_KEEPIDLE, [60], 4) = 0
setsockopt(4, SOL_TCP, TCP_KEEPINTVL, [60], 4) = 0
fcntl(4, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
connect(4, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("195.135.221.134")}, 16) = -1 EINPROGRESS (Operation now in progress)
close(3) = 0
poll([{fd=4, events=POLLOUT}], 1, 1000) = 0 (Timeout)
poll([{fd=4, events=POLLOUT|POLLWRNORM}], 1, 0) = 0 (Timeout)
...
poll([{fd=4, events=POLLOUT}], 1, 1000) = 0 (Timeout)
poll([{fd=4, events=POLLOUT|POLLWRNORM}], 1, 0) = 0 (Timeout)
close(4) = 0
Wireshark里边有一些建立TCP连接的数据包,只有SYN请求包,没有ACK响应包,就像下面这样:
| No. | Time | Source | Destination | Protocol | Length | Info |
| 1 | 0.000000000 | 192.168.0.2 | 195.135.221.134 | TCP | 74 | 52845 > http [SYN] Seq=0 Win=14600 Len=0 MSS=1460 SACK_PERM=1 TSval=17433696 TSecr=0 WS=128 |
| 2 | 32.031970000 | 192.168.0.2 | 195.135.221.134 | TCP | 74 | [TCP Retransmission] 52845 > http [SYN] Seq=0 Win=14600 Len=0 MSS=1460 SACK_PERM=1 TSval=17465728 TSecr=0 WS=128 |
> ping -c 36 download.opensuse.org --- download.opensuse.org ping statistics --- 36 packets transmitted, 26 received, 27% packet loss, time 36548ms rtt min/avg/max/mdev = 383.255/445.445/486.358/36.185 ms丢包率很高。
在目前的配置下,这个问题跟是否启用ipv6无关,已经还原到系统自带的curl。后来测试出来一样的错误,这次是ipv4地址访问超时,ipv6地址无法访问,报的错一样,重试就好了。
------
过了比较久以后,终于遇到一次跟最开始一样的错。
Download (curl) error for 'http://download.opensuse.org/repositories/openSUSE:/Tumbleweed/standard/repodata/repomd.xml': Error code: Connection failed Error message: Failed to connect to 2001:67c:2178:8::13: Network is unreachable重试不好使,哪怕隔了比较久也一样。打开/etc/resolv.conf里边的"options single-request"也一样,只不过是DNS查询有序了。
> sudo /usr/sbin/tcpdump port 53
> sudo /sbin/service nscd restart
> curl --head download.opensuse.org
curl: (7) Failed to connect to 2001:67c:2178:8::13: Network is unreachable
tcpdump截获的数据:
22:50:45.925896 IP 192.168.0.2.58627 > 192.168.0.1.domain: 38640+ A? download.opensuse.org. (39) 22:50:45.925907 IP 192.168.0.2.58627 > 192.168.0.1.domain: 21267+ AAAA? download.opensuse.org. (39) 22:50:45.927134 IP 192.168.0.1.domain > 192.168.0.2.58627: 38640 1/0/0 AAAA 2001:67c:2178:8::13 (67) 22:50:46.337089 IP 192.168.0.1.domain > 192.168.0.2.58627: 21267 1/0/0 AAAA 2001:67c:2178:8::13 (67)还真是两个ipv6地址,之前通过一点证据有过这样的猜测,但是一直都不相信会出这样的问题,牛头不对马嘴的响应。
> host download.opensuse.org
download.opensuse.org has address 195.135.221.134
download.opensuse.org has IPv6 address 2001:67c:2178:8::13
> sudo /sbin/service nscd restart
> curl -4 --head download.opensuse.org
HTTP/1.1 200 OK
tcpdump截获的数据:
22:51:56.844343 IP 192.168.0.2.36634 > 192.168.0.1.domain: 48717+ A? download.opensuse.org. (39) 22:51:56.845506 IP 192.168.0.1.domain > 192.168.0.2.36634: 48717 1/0/0 AAAA 2001:67c:2178:8::13 (67) 22:51:57.844422 IP 192.168.0.2.36568 > jhdns4.jhptt.zj.cn.domain: 48717+ A? download.opensuse.org. (39) 22:51:58.066126 IP jhdns4.jhptt.zj.cn.domain > 192.168.0.2.36568: 48717 1/0/0 A 195.135.221.134 (55) 22:51:58.066408 IP 192.168.0.2.58356 > 192.168.0.1.domain: 41852+ AAAA? download.opensuse.org. (39) 22:51:58.105402 IP 192.168.0.1.domain > 192.168.0.2.58356: 41852 1/0/0 AAAA 2001:67c:2178:8::13 (67)curl跟host都得到了两个ipv6地址,但是host跟"curl -4"额外使用/etc/resolv.conf里边的第二个nameserver(来自ISP)得到了ipv4地址。
如果路由器代理到正确的DNS服务器,那应该是没问题,如果有问题,那很可能是路由器的问题。看看路由器DNS相关的配置,没发现什么问题,明天再看看。
今天已经没有问题了,继续看看路由器DNS相关的配置,发现“DHCP服务器设置”里面有个“DNS代理”选项,默认是启用了的。经过试验,不启用的话,路由器会转告客户端用ISP的DNS服务器解析,但是有很多重复的请求,导致速度很慢。
宽带路由器的DNS代理功能 - cisco.chinaitlab.com提到DNS代理的实现可能不够完美,有可能出错。从目前遇到的情况来看,这个很有可能。
运行"sudo vi /etc/resolv.conf"编辑resolv.conf,注释掉"nameserver 192.168.0.1",把ISP的域名服务器放在最前面,另外在下面添加"nameserver 8.8.8.8"备用(这是Google提供的DNS服务器)。
参考资料:
- > strace --help
- > curl --help
- > man curl
- List_of_DNS_record_types - en.wikipedia.org
- Re: openSUSE community - IPv6 and DNS - Author: Per Jessen - opensuse.14.x6.nabble.com
- How to disable DNS AAAA queries? - Author: Humberto Jucá - answers.launchpad.net
- > man tcpdump
- > man resolv.conf
- DNS resolver library doesn't seem to be working reliably - bugzilla.redhat.com
- getaddrinfo() with AI_ADDRCONFIG doesn't suppress AAAA DNS queries on IPv4-only networks - bugzilla.redhat.com
- https://fedoraproject.org/wiki/Networking/NameResolution/ADDRCONFIG
- Slow dns fix! - Author: ghost_ryder35 - forums.fedoraforum.org
- > man gai.conf
- > man getaddrinfo
- > less /etc/gai.conf
- RFC 3484 - Default Address Selection for Internet Protocol version 6 (IPv6) - tools.ietf.org
- Linux: Prefer IPv4 over IPv6 in dual-stack environment (and prevent problems when only IPv4 exists) - Author: SF-Alpha - sf-alpha.bjgang.org
- curl install - curl.haxx.se
- curl example - curl.haxx.se
=文章版本=
2013062020130621 curl的配置对zypper无效,增加其他方法
20130622 增加curl部分源码分析、出错原因分析
20130627 增加编译安装curl
20130630 根据做的测试,修改配置部分
20130717 根据之前做的测试,增加了一种可能有效的配置
20130731 更新上次配置的使用情况;增加了一部分总结
20131004 根据之前的测试及最近的使用情况,结论已经基本确定
。。。真有相应号召的。。。你怎么没研究下 zypper 相关的代码呢,它也是开源的呢 github.com/openSUSE/zypper。希望你能造福全国人民。
ReplyDelete