家用路由器被入侵了

某天上网浏览网页,半天都返回不了结果,OS X 虽然不怎么样,Firefox/Chrome 浏览个网页应该没什么大问题。
最初以为是浏览器的问题,直接重启,无效;重启 Mac,继续无效。好吧,既然重启都不生效,索性咱们从专业的角度来分析分析问题。
抓包。结果令人惊讶。

我的机器主动给路由器(192.168.2.1)发送了大量的 RST 包,在没有任何主动 SYN 的情况下。第一反应就是受到中间人攻击了。
登到路由器后台一看,果真有几个不认识的客户加到了这个 SSID 里面。
后续的重启改密码就不说了,虽然用的是 WPA2 的加密方式,但是密码太弱智了,花几分钟暴力破解一下应该都能出来,引以为戒。

后续留几个连接大家可以参考参考。
如何通过 Sequence Number Predictionl 的方式来进行 MITM 攻击,手法跟我遇到的类似。

出现 TCP rst 的几种可能:
1.1. Where do resets come from? (No, the stork does not bring them.)
1.2. RST flag at end of TCP transmission 
1.3. What causes a TCP/IP reset (RST) flag to be sent? 

2.1. Why TCP Reset sent after receive [FIN,ACK] Packet? 
2.2. TCP option SO_LINGER (zero) – when it's required

3.1. Windows sends RST after SYN-ACK on a TCP connection
3.2. RST after SYN-ACK
3.3. Why on receiving SYN/ACK, a RST packet is sent with certain sites
3.4. why kernel sent RST to a remote TCP server after the machine receiving a SYN/ACK packet?
3.5. why do Client send a RST packet during TCP handshake? [closed]
3.6. Why would a client send a RST packet as reply to a SYN,ACK? 

4.1. TCP: Server sends [RST, ACK] immediately after receiving [SYN] from Client
4.2. Why do I see a RST, ACK packet instead of a RST packet? 

不要乱用 TCP ENC flag

前段时间处理了一个 case,现象很简单,同网络环境下的机器,绝大多数的机器都无法 curl 访问 example.com,仅有少部分的可以 curl 访问,并且他们的 mtr 的路径一模一样,机器的配置应该也有一样。
对比一下,可以访问的:
$ curl -IL "http://example.com:80/rest"
-v
* About to connect() to example.com port 80 (#0)
*   Trying example.com… connected
* Connected to example.com (example.com) port 80 (#0)
> HEAD /rest HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.15.3
> zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: example.com
> Accept: */*
>
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Server: nginx
Server: nginx
< Date: Fri, 24 Oct 2014 07:13:23 GMT
Date: Fri, 24 Oct 2014 07:13:23 GMT
< Content-Type: application/json;charset=UTF-8
Content-Type: application/json;charset=UTF-8
< Content-Length: 0
Content-Length: 0
< Connection: keep-alive
Connection: keep-alive
< s-rt: 2
s-rt: 2

<
* Connection #0 to host example.com left intact
* Closing connection #0

访问超时的:
curl -IL "http://example.com:80/rest" -v
* About to connect() to example.com port 80 (#0)
*   Trying example.com… Connection timed out
* couldn't connect to host
* Closing connection #0
curl: (7) couldn't connect to host

最初以为是 example.com(泛指) 的问题,后来联系了下我们的服务方,确认没有问题。当时想到一种可能是否是由于 SNAT 出口的 timestamps, tcp recycle 的时间戳问题引起的,到 nat 机器上看了下,并不存在这个问题。
直接抓包,惊奇的发现,根据经验,第一次的 TCP 连接,9bit 的 Flags 区域只应该有 SYN 标识位是被设置的,但是连不上的机器的 flags 竟然是 SYN, ECN, CWR 三个 bit 都被设置了。

看了下 wiki 对 ECN 的解释,以及 stackexchange 的实践,原因就很明显了,这条经过的 routing 上肯定有不支持 ECN 的 router,收到带有 ECN flag 的包之后就直接丢弃了,
可以访问的那部分机器由于 /proc/sys/net/ipv4/tcp_ecn 的默认值 2,可以接受带有 ECN 的包,但是不会主动发送,其余的机器由于明确的设置为了 1,这些包到从目的地返回源的过程中被中间 router 丢弃了。
2000 的统计显示,全球有 8% 的网络不可达跟 ECN 有关系。十多年过去了,理论上讲,应该有所改善。

ipvs 频繁 add/remove 后端 rs 问题追查

这个问题我们三年前就遇到过,限于当时的资源,没能分析出具体的原因。欠下的债肯定是要还的,最近又遇到了,不过解决起来倒是很快。
看 messages,发现有不少类似的 log:
Mar  6 15:00:11 lvs-1 Keepalived_healthcheckers[107592]: TCP connection to [192.168.90.5]:80 failed !!!
Mar  6 15:00:11 lvs-1 Keepalived_healthcheckers[107592]: Removing service [192.168.90.5]:80 from VS [111.111.111.4]:80
Mar  6 15:00:17 lvs-1 Keepalived_healthcheckers[107592]: TCP connection to [192.168.90.5]:80 success.
Mar  6 15:00:17 lvs-1 Keepalived_healthcheckers[107592]: Adding service [192.168.90.5]:80 to VS [111.111.111.4]:80
Mar  6 20:00:16 lvs-1 Keepalived_healthcheckers[107592]: TCP connection to [192.168.90.5]:80 failed !!!
Mar  6 20:00:16 lvs-1 Keepalived_healthcheckers[107592]: Removing service [192.168.90.5]:80 from VS [111.111.111.4]:80
Mar  6 20:00:22 lvs-1 Keepalived_healthcheckers[107592]: TCP connection to [192.168.90.5]:80 success.
Mar  6 20:00:22 lvs-1 Keepalived_healthcheckers[107592]: Adding service [192.168.90.5]:80 to VS [111.111.111.4]:80

结合监控图,发现出现问题的时候都是在整点,并且正点的时候系统的 load 确实比较大,把历史的 sar 数据调出来发现正点的 IO 利用率都是在 100%,这一联想就只有 logrotate 在正点做 rotate 然后 zip 压缩了。模拟了一下发现可以复现该问题,剩下的就是解决问题了。

除了上面这个,我们还发现在非整点也会出现类似的 log
Mar 10 17:10:23 lvs-2 sshd[54526]: Accepted publickey for jaseywang from 192.168.10.254 port 56532 ssh2
Mar 10 17:15:19 lvs-2 Keepalived_healthcheckers[34943]: MD5 digest error to [192.168.90.5]:80 url[/], MD5SUM [f9943d27e3420479271984e01dfe85dd].
Mar 10 17:15:19 lvs-2 Keepalived_healthcheckers[34943]: Removing service [192.168.90.5]:80 from VS [111.111.111.4]:80
Mar 10 17:15:22 lvs-2 Keepalived_healthcheckers[34943]: MD5 digest success to [192.168.90.5]:80 url(1).
Mar 10 17:15:28 lvs-2 Keepalived_healthcheckers[34943]: Remote Web server [192.168.90.5]:80 succeed on service.


抓包发现 LVS 向 RS 发送 GET / HTTP/1.0 的请求, RS 却返回了 HTTP/1.1 500 Internal Server Error 的非 200 错误码,所以 LVS remove 掉这些 RS 确实是正常的逻辑:

同样从监控中看到,出问题的时间段由于频繁的切换导致了前端几乎不可用,在 5 点多这一时段 error log 的比平常大了 100 多倍,达到了 500M:

从上面的信息,全量分析了出问题阶段所有 err log 的类型,发现了惊人的答案,99% 以上的问题都源于 "too many open file" 的错误,/proc 一看,发现 Nginx 的确实变成了 1024:
# cat /proc/3148/limits
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            10485760             unlimited            bytes      
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             514947               514947               processes
Max open files            1024                 4096                 files     
Max locked memory         65536                65536                bytes     
Max address space         unlimited            unlimited            bytes      
Max file locks            unlimited            unlimited            locks     
Max pending signals       514947               514947               signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0                    
Max realtime timeout      unlimited            unlimited            us      

root 用户的没有问题:
# ulimit  -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 514947
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 512000
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 512000
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

不过为什么突然由原来的系统级别的数字变成了 1024,实在值得奇怪,该问题很早之前在我们一台 Nginx 的机器上同样遇到,记得当时 restart 了 Nginx 依然没有效果,不得已只能 reboot 机器恢复。Nginx limit 变成 1024 的问题,其他用户也遇到类似的问题:
* http://serverfault.com/questions/516802/too-many-open-files-with-nginx-cant-seem-to-raise-limit
* http://anothersysadmin.wordpress.com/2013/08/09/nginx-and-the-too-many-open-files-limit/

目前还无法得知为什么,但是找到了部分原因,要避免此类问题倒是很简单,强行指定 worker_rlimit_nofile 就好了,谨慎起见,把 /proc/xxx/ 下面重要的文件都给监控上。

谈谈开源软件的选择

11/05/2014 update:
博客写完之后根据往常直接同步到了 weibo 上,结果反响比较大,一小部分是敢跳出来喊支持的(毕竟软件作者也在 weibo 上,站队要正确),非常难能可贵,一半是完全中庸的态度,另外一半是大喷特喷,或者说是满嘴跑火车更恰当些。
写这篇博客的目的有二:

  1. 回答题目的问题,如何选择可能靠谱的开源产品
  2. 毫不避讳,结合上面的几点,谈谈最近用的 tcpcopy 遇到的诸多问题,证明 tcpcopy 还有很大的提升空间

对于那些满嘴跑火车的同学,建议你,仅仅是建议而已:

  1. 把全文好好看完
  2. 多看看外面的世界,别把自己限死在狭小的空间里面

还想继续乱喷的,理解了@ayanamist 说的话咱们再继续。

很意外的是,最近两天收到了几封邮件,让满嘴跑火车的同学失望的是,他们不是来继续喷的,相反,他们都表达了非常正面的观点。邮件的结尾都差不多,让我加个 QQ 交个朋友,交朋友本是件好事,但是,我这篇博客已经说的很清楚,相比 QQ(基本不用),我更偏向使用 gmail、gtalk、twitter 交流。所以,善意的提醒下,RTFB(read the friendly blog:)还是很重要的。如果有什么想交流,about/ 页面有我的联系方式,非常欢迎。


先说点背景,对一款开源软件评估,也就是最终用还是不用,无外乎从这几个方面入手。
1. 技术层面,这也是最基础的一层,即这款工具、软件是否符合你的需求,比如是否需要跨平台,是否对性能有很高的要求,是否对安全有很高的要求等等。
2. 文档,现在不少的开源软件动不动就冒出高性能(high performance),简单易用(easy to use),比竞争对手更强大的字眼,试问连个详细的文档说明都没有,跑的个 demo 连背景环境配置都没有,你敢用吗?
3. 代码整洁,有注释,尤其是后者,github 上不少代码都是从头写到尾,一行注释都没有,这个不但给自己挖坟,也给用户带来了非常多的不便。
4. 更新频率,如果最近一次更新是 5 年前,是不是要考虑下有没有用的必要。当然这个并不绝对,不少最新的稳定版本都是 3 年前甚至 5 年前的,对应的开发版本倒是蛮活跃的。另外,如果是刚刚仅仅出来了几个月的新产品,还是慎重考虑一下。
5. 社区,这个是重中之重。
5.1. 用户数量,github 某个项目如果没几个人 star,是不是需要慎重考虑下。
5.2. 参与程度,半年才提一个 issue,mail list, irc 几天都没条消息的还是暂时不要考虑了。
5.3. 用户参与方式,使用 QQ、旺旺这种娱乐软件参与开源社区确实是件荒唐的事情,这类参与方式无法将信息公开的被搜索引擎收录,并且基本上将用户群体限制在了一个非常狭小的中文社区里面。mail list、google group 之类的比他们好用更重要的是开放的多。
5.4. commit 的人数,如果常年就一人更新,想想万一哪天他不在了,你们线上的服务怎么办?
5.5. 开源软件许可,这个一般自己用都没什么大问题。

Continue reading

服务器网卡收包性能测试

之前写过不少跟网络相关的 benchmark,比如:
* 《网络质量评估
* 《10G(82599EB) 网卡测试优化(总)

上面的更多的是放在带宽使用率上,即如何尽可能的打满,但是都遗漏一个重要的细节,那就是 packet/s,这个论坛的作者一语中的:
– how many packets/sec you have. In fact, network throughput mostly depends on packets/sec, not bytes/sec

可能对于大部分的应用来说,根本无需关注每秒钟到达的包的数量甚至大小,但是对于某些应用来说,这些指标是至关重要的。

要对包(大小、数量、时间)方面的指标做 benchmark,iperf, netperf 之类的就没法用了,他们只能测试带宽,经过一天的使用比较发现下面的一些工具能基本满足需求。要快速的上手的话还是需要对 tcp/ip 有比较深入的了解,比如 ip header,tcp header 之类的,再比如最小的 frame 应该是 64B,这也是众多网络设备厂商做包转发测试的基础,不明白的看下面两张(1, 2)图就清楚了。

跟网络厂商一样,我们也重点关注小包(0-75)的性能,至于小包如何定义,没有严格的标注,我上面的 0-75 也仅仅是从 iptraf 拿到的一个范围,最终还是要根据业务来定。
一般而言,frame(fps) 是针对 data-link(Ethernet) 而言,packet(pps) 是针对 IP 层而言,segment 是针对 TCP 层而言。

Continue reading

访问 VIP 缓慢的调查

将某个 https 的域名迁移到 lvs,部署完成,绑定 hosts 测试过程中发现 GET 请求非常的缓慢,一个 90KB 的图片耗费的时间长达几十秒甚至超过 1min,这个在未迁移前是从未出现过的现象。
为了一步一步调查,绑定了 hosts 直接访问 rs,结果跟未迁移前效果一样,但是直接访问 VIP 则十分的缓慢。看到这么多的现象,第一反应是难道经过了 VIP 之后 ssl 的处理速度会急剧下降?直接用浏览器自带的开发工具观察好了,发现包含 DNS 查询之前的响应还是很快的,从 Connecting 开始之后的每一步当然包括 SSL 了,其 RT 都是原先的 100 倍以上,Waiting 以及 Receiving 的时间更是长的惊人。
为了方便后续的分析,找了个简单的脚本,可以通过 curl 更方面的观察:

# cat curl-format.txt
    time_namelookup:  %{time_namelookup}\n
       time_connect:  %{time_connect}\n
    time_appconnect:  %{time_appconnect}\n
   time_pretransfer:  %{time_pretransfer}\n
      time_redirect:  %{time_redirect}\n
 time_starttransfer:  %{time_starttransfer}\n
                    ———-\n
         time_total:  %{time_total}\n

curl -w "@curl-format.txt" -o /dev/null -s http://wordpress.com/
连续几次的测试结果跟上面观察的到的一样。当时的第一反应是,虽然怎么都讲不通,经过了 VIP 之后质量还不到原来的 1%,但观察到的结果就是,这个 SSL 把个 VIP 搞的这么慢,为了确认确实是 https 的问题,又找了几个 http 的资源测试了一下,结果,奇怪的事情发生了,也极其的缓慢。
看来最初的判断是错误的,所有的走 VIP 的 GET 都变的极其缓慢了,那为什么之前比这个量大的多的 POST 没有任何异常了?最大的区别就是发送请求 body 的大小了,前者一个静态资源都是几十上百 K,而后者仅仅是返回一个 json 的纯文本。
直接抓包,最初仅仅是在客户端分别抓经过 VIP 跟不经过 VIP 的包,除了每一个 segment 的返回时间差距很大之外,其他的并无差异。后来经我们主管提醒,需要在服务器上也同时抓包,于是一个在原来的 Nginx 上抓,一个在 VIP 上抓,对比相面四张图应该就能看出原因所在了。



上面两张是不走 VIP 的情况,可以看到一个 90K 的文件经过 12 个 TCP segment 就重组了,看 length 这一栏也可以得出,平均一个 segment 都在8K 左右。



上面两张是走 VIP 的,所有的 segment 都变成了 1514(14B 的二层加上 MTU),导致的结果是分了 60 多个 segment。
看到这里立马就联想 2.6.32 ipvs 对 gro 的那个 bug: 2.6.32 – 2.6.36 的内核上在使用 ipvs 的情况下需要关闭 gro。这个我之前曾经单独写过一篇博客。只不过当年没有深入的研究下开启 gro 会有什么具体的影响,这次算是经历过了。
由于配置管理的问题,导致我们一个集群上部分网卡的 gro off 操作没有生效,这个其实是主因。
既然说到 gro/lro,那干脆把 tso, gso 的也一并实践一下。
tso, gso 在 Ubuntu 以及 RedHat 上默认是开启。
gro 在 Ubuntu 上默认关闭,在 Redhat 上默认开启。
lro 的在这两个发行版本上默认均是关闭。

可以看看我下面的两个抓包,第一张是 tso, gso 均关闭的情况

下面一张是单独开启 tso 或者 gso 的情况

现象是不是很明显。所以开启 tso/gso 对于绝大多数的应用来说还是很有必要的,能节约不少资源。

ref:

http://wiki.wireshark.org/CaptureSetup/Offloading