1月 012013
 

转自:http://blog.csdn.net/sherlockhua/article/details/5365873

1 引子

在上一篇关于如何将套接口绑定到网络接口上的文章中,我曾经以为采用 SO_DONTROUTE 套接口选项能够实现和SO_BINDTODEVICE 选项同样的功能。但是实践证明不是这样。那么,其原因到底是为什么呢? SO_DONTROUTE 套接口选项真正的作用是什么呢?本文将对此予以解答。

2 问题求解

在 socket(7) 中对 SO_DONTROUTE 选项的说明如下:

SO_DONTROUTE

Don’t send via a gateway, only send to directly connected hosts.

The same effect can be achieved  by  setting  the  MSG_DONTROUTE

flag  on  a socket send(2) operation. Expects an integer boolean

flag.

这段话的核心意思是 SO_DONTROUTE 选项将导致数据包不经由网关发送,而是发往直接相连的主机。该套接口选项合法的值是整数形式的布尔标志值。

上述说明看起来似乎很明了,但是我 google 到的一些资料又说这个套接口选项将会绕过( bypass )路由表发送数据包,而且连 W. Richard Stevens 也是这样说的:“此选项规定发出的分组将旁路底层协议的正常路由机制。例如,对于 IPv4,分组被指向适当的本地接口,也就是目的地址的网络和子网部分所确定的本地接口。如果本地接口不能由目的地址确定(例如,目的主机不再一个点对点链路的另一端上,也不在一个共享网络上),则返回 ENETUNREACH 错误。……此选项经常由路由守护进程( routed 和 gated )用来旁路路由表(路由表不正确的情况下),强制一个分组从某个特定接口发出。” [UNIX 网络编程(第 1 卷),清华大学出版社,第 157 页 ] 。我不得不承认,正是这里的最后一句话,直接导致我认为可以通过 SO_DONTROUTE 套接口选项完成和 SO_BINDTODEVICE 同样的功能。但是实际上却并不是这样的。

为了解决这个问题,我编写了一个测试程序 udpsend2.c 。该程序要求要有三个参数,其中,第二个参数是数据包目的地的 IP 地址,第三个参数是一个开关,当为 on 时开启 SO_DONTROUTE 选项,当为 off 时关闭 SO_DONTROUTE 选项,默认情况下该选项是打开的。如果您理解该程序有困难,请先阅读《 UNIX 网络编程(第 1 卷)》。

 

如下编译程序:

[root@cyc src]# gcc -g -o udpsend2 udpsend2.c

3 对比测试

首先介绍一下初始时刻测试环境的设置情况,如下所示:

[root@cyc src]# uname -a

Linux cyc 2.4.20-8 #4 Sat Jan 20 19:42:09 CST 2007 i686 i686 i386 GNU/Linux

[root@cyc src]# ifconfig

eth0      Link encap:Ethernet  HWaddr 00:0D:87:EA:E3:AF

inet addr:202.115.26.224  Bcast:202.115.26.255  Mask:255.255.255.0

UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

RX packets:40016 errors:0 dropped:0 overruns:0 frame:0

TX packets:85327 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:100

RX bytes:3063524 (2.9 Mb)  TX bytes:36469606 (34.7 Mb)

Interrupt:11 Base address:0xc000

[root@cyc src]# route

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

202.115.26.0    *               255.255.255.0   U     0      0        0 eth0

default         202.115.26.1    0.0.0.0         UG    0      0        0 eth0

这里,作者所在的子网为 202.115.26.0/24 ,默认网关为 202.115.26.1 ,用于发送数据包的主机为 202.115.26.224 。用于接收数据的两台远端主机的 IP 地址分别为 202.115.26.193 和 202.112.14.184 。 202.115.26.193 与发送主机 202.115.26.224 位于同一网段内,通过交换机相连;而 202.112.14.184 则与发送主机 202.115.26.224 之间没有直接相连,中间通过多个路由器连接。连接示意图如下所示:

 

3.1 同网段的测试

首先测试的是 202.115.26.193 。路由表设置如下所示:

[root@cyc src]# route

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

202.115.26.0    *               255.255.255.0   U     0      0        0 eth0

default         202.115.26.1    0.0.0.0          UG    0      0        0 eth0

如下启动程序:

[root@cyc src]# ./udpsend2 202.115.26.193 on

send 512 to remote end…

程序报告发送了 512 字节的数据给 202.115.26.193 ,尽管对方并没有真正接收数据包。如我们所知, UDP 是无连接的,它只负责将数据包发出去,至于对方有没有接收到, UDP 是不管的。

接下来,去掉路由表中的默认路由:

[root@cyc src]# route del default

[root@cyc src]# route

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

202.115.26.0    *               255.255.255.0   U     0      0        0 eth0

再次运行 udpsend2 ,结果如下:

[root@cyc src]# ./udpsend2 202.115.26.193 on

send 512 to remote end…

结论:当目的主机与发送主机处于同一个网段内时,无论内核是否设置了默认路由,数据包总能发送给目的主机(尽管目的主机可能并没有在等待接收数据包)。但是,子网必须要在路由表中。比如,如果上面将子网 202.115.26.0 所在的路由表项也删除的话,就会得到网络不可达的结果,这一点是很容易理解的。

3.2 不同网段测试

其次来测试当发送主机与目的主机位于不同网段时的情况,此时的目的主机为 202.112.14.184 。发送主机上的路由表设置如下,并且发送主机能够 ping 通目的主机。这表明虽然 202.112.14.184 与 202.115.26.224 之间虽然并没有直接相连,但是能够通过路由器转发数据包来完成通信:

[root@cyc src]# route

Kernel IP routing table

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface

202.115.26.0    *               255.255.255.0   U     0      0        0 eth0

default         202.115.26.1    0.0.0.0         UG    0      0        0 eth0

[root@cyc src]# ping 202.112.14.181

PING 202.112.14.181 (202.112.14.181) 56(84) bytes of data.

64 bytes from 202.112.14.181: icmp_seq=1 ttl=125 time=0.364 ms

运行程序 udpsend2 ,结果显示如下:

[root@cyc src]# ./udpsend2 202.112.14.181 on

sendto(): Network is unreachable

将默认路由(也即网关地址)从路由表中删除,再次运行程序:

[root@cyc src]# ./udpsend2 202.112.14.181 on

sendto(): Network is unreachable

结论:无论路由表中是否包含了默认路由(也即网关地址),当两主机之间没有直接相连时(即使能够通过中间路由器通信),设置了 SO_DONTROUTE 也将导致网络不可达错误。而网络不可达错误通常程序中有函数返回 ENETUNREACH 的结果。在本例中, ENETUNREACH 错误是由 sendto() 函数引发的。

4 总的结论

根据上面的测试结果,我认为: SO_DONTROUTE 套接口选项并没有完全绕过路由表,而只是绕过了路由表中网关(或者说默认路由)所在的表项。因此, Linux 的 socket(7) 中的说明是正确并且无歧义的,而《 UNIX 网络编程(第 1卷)》中的说法则带有一定的歧义性,容易给人造成误解。当然,这也可能是译者翻译不准确的缘故。总的来讲,当目的主机与发送方直接相连时,可以通过 SO_DONTROUTE 来实现从指定的网络接口发出数据包。但是,当接受者与发送方并没有直接相连时,就不能这样做了。而需要考虑通过 SO_BINDTODEVICE 选项或者 RAW 套接口或者 PACKET 套接口来实现。(注: RAW 套接口能实现网络层的直接数据访问,而 PACKET 套接口则能实现链路层的直接数据访问)。

 

相关参考: http://blog.csdn.net/namelcx/article/details/8063543

 Posted by at 下午 5:13
11月 262012
 

缘起:

有这么一些服务器软件(如: tokyotyrant、memcached),他们提供长连接的功能,简单说,server端基本不会主动关闭连接的;非长连接的时候也会存在这种问题,只是长连接时会表现的更明显一下。如果客户端抽风(或者一些恶意的目的),打开连接后,并不关闭而直接离开,这样,就会在server端残留大量的连接,最直接的表象就是: netstat -anpt 时很慢。

真实案例:

具体原因未明,来个命令:

sar -n SOCK :

我们发现在1:30 PM时,tcp连接数突然飙升到 40万, 没有做任何操作3:40 PM时,连接数就开始减少了。

 

原因:

连接2小时没有操作,探活机制发现对方已经消失,于是,关闭了连接; 看了一下tokyotyrant 和 memcached的源码,都做了KEEPALIVE 的设置:

只是都没有做平静时间的设置,于是就都参考系统的设置了。

 

教训:

1. 如果没有keepalive的机制,系统运行时间过长总会出现这样的问题的

 

学习:

关于keep-alive的一些参数设置,摘自: man tcp :

tcp_keepalive_intvl (integer; default: 75)
The number of seconds between TCP keep-alive probes.

如果探活时,对方没有反应则,间隔 tcp_keepalive_intvl 秒后重发探活数据包,避免因丢包产生的探活差错

tcp_keepalive_probes (integer; default: 9)
The maximum number of TCP keep-alive probes to send before giving up and killing the connection if no
response is obtained from the other end.

如果发送了 tcp_keepalive_probes 次探活数据包,对方都没有反应,则视为对方死掉,而不是探活1次失败就放弃

tcp_keepalive_time (integer; default: 7200)
The number of seconds a connection needs to be idle before TCP begins sending out keep-alive probes.
Keep-alives are only sent when the SO_KEEPALIVE socket option is enabled. The default value is 7200 sec-
onds (2 hours). An idle connection is terminated after approximately an additional 11 minutes (9 probes
an interval of 75 seconds apart) when keep-alive is enabled.

Note that underlying connection tracking mechanisms and application timeouts may be much shorter.

从连接平静开始,间隔 tcp_keepalive_time 秒开始探活,避免对方异常退出

编程实现:

使用setsockopt(…) 函数来实现:

参考资料: http://ez.analog.com/docs/DOC-1862

 

 

 

 Posted by at 下午 4:42
7月 062012
 

PTR (Pointer Recore),指针记录,是电子邮件系统中的一种数据类型,被互联网标准文件RFC1035所定义。与其相对应的是A记录、地址记录。二者组成邮件交换记录。
  A记录解析名字到地址,而PTR记录解析地址到名字。地址是指一个客户端的IP地址,名字是指一个客户的完全合格域名。
   PTR记录被用于电子邮件发送过程中的反向地址解析。当正向域名解析完成后,还应当向您的线路接入商(ISP)申请做反向地址解析,以减少被国外机构退信的可能性。
   因为反向地址解析是需要“钱”的,所以除了用于发邮件的IP做反向地址解析,提供web服务的IP一般是不做反向地址解析的,所以很难根据IP地址反向解析到域名

实例:
提供邮件服务的IP的反向地址解析:

提供web服务的IP的反向地址解析:

 Posted by at 下午 9:58
11月 082011
 

netstat是一个查看网络状况的非常给力的工具,这里介绍一个不常用的选项:
-o, –timers
Include information related to networking timers.

如果添加-o选项,结果为:

我们发现,多出了一些信息,那么多出来的这些信息有什么用吗?man了一下netstat,知道这里是定时器,此外也并没有什么有用的说明。

我想,既然是定时器,应该与 SYN_RECV 状态持续的时间有关系,怎么才能够证明呢?
首先,先祭出一个非常有用的工具: nc ; nc是个好东西,可以做server也能做client; 更加凑巧的是nc做server时,listen的backlog是为1的:

backlog为1 将非常有利于我们做这个实验; 关于backlog的说明:
backlog通常称为“积压值”;“积压值”说明的是TCP监听的端点已被TCP接受(established的状态)而等待应用层接受(使用-p参数时看到的pid列为’-‘)的最大连接数。
下面你讲看到,虽然nc设置的backlog为1,但是,排队的连接数却不是1,而是2;一般来讲:
积压值和最大排队的连接数有如下关系:

扯远了,赶快回来…
1. 使用nc做一个server
nc -l 10.71.6.21 12345
2. 从另一台机器上去连接该server
nc 10.71.6.21  12345 &  // 写四遍就行了

在server端可以使用netstat来观察如下:

至于括号中的数字,我们再观察一个图:

我每次查看那个不能被立即接受的连接,括号中的数字都是变的,而且和等待的时间长短是有关系的;第一列数字是从某个数字开始递减的,递减到0,则第二列数字加1;然后第一列数字继续从一个新的数值开始递减,就这样一直等下去,直到客户端超时。新的数字的取值按照指数避让算法计算出来的。
关于定时器的知识可以参考《tcp/ip详解》

关于SYN_RECV 状态:
当server收到syn后,server端的连接的状态变为SYN_RECV,这时候:
1. 可能server还没有回复了syn-ack
2. 也可能server已经回复了syn-ack
3. 还可能server不仅回复了syn-ack,而且还收到了client的ack(这种情况属于不符合规范的实现)

情况1: 如果状态持续时间太长,需要查看 netstat -sn| grep overflow 是否变化
情况2: 一般可能是client太远了
情况3: 应用进程可能忙不过来了,可以查看 netstat -anop | grep -i established | wc -l ,可能太多了

一般来讲,如果netstat -anop | grep -i established| awk ‘{if($7 == "-") print $0}’| wc -l 不是太大,就没有什么问题

 Posted by at 上午 10:04
6月 042011
 

我的foxmail有一个月不能收取yahoo邮件了,一直很忙,也没查是为什么,只以为是yahoo的问题。
今天抓包一下,发现欲连接的IP是: 192.168.100.210; 感觉奇怪; 更奇怪的是这个IP不是dns解析出来的,我更没有设置host,yahoo的pop3主机域名地址为: pop.mail.yahoo.com.cn ,dns解析为:
——————————————
>nslookup pop.mail.yahoo.com.cn
DNS request timed out.
    timeout was 2 seconds.
服务器:  UnKnown
Address:  219.239.26.42

非权威应答:
名称:    pop1.mail.vip.cnb.yahoo.com
Address:  203.209.228.241
——————————————-
当我设置host:
203.209.228.241        pop.mail.yahoo.com.cn

foxmail依然连接地址: 192.168.100.210

解决办法: 直接将yahoo的pop3地址设置为: 203.209.228.241  ; all is OK

==========================
至于foxmail错误地解析了域名地址,大概是曾经的一次意外的错误的解析被foxmail 给cache了吧? 但是我没有找见相应的设置,不去管它了, 没有意义

另外: 在写本文之前,我不能访问我的blog: http://phpor.net/
当我得知我同学可以访问时,我重新拨号,然后就好了; 呃…,好了就好了吧
Aliases:  pop.mail.yahoo.com.cn

 Posted by at 下午 9:36
4月 162011
 

socket_shutdown函数可以关闭一个socket的读或写,这种操作映射到tcp协议中又是什么情况呢,简单测试了一下:
1. 如果关闭读操作,则协议上没有任何动作,对方发来的数据网卡上还是接受的
2. 如果关闭写操作,则协议上表现为关闭连接,即: 发送finish报文

 Posted by at 上午 9:15
10月 142009
 

当我们确认某vip开放了端口p,但访问时好时坏,你们可能是应为vip后面的部分真实服务器到我们自己的机器没有回包路由,这时的检查办法可以如下:

for i in seq 1 100;do nmap vip -p port | grep filtered; done

检查100次,应该可以遍历所有的真实服务器了,如果出现filtered; 则基本是有问题的

 Posted by at 上午 2:54
5月 232009
 

IPSec 协议不是一个单独的协议,它给出了应用于IP层上网络数据安全的一整套体系结构,包括网络认证协议 Authentication Header(AH)、封装安全载荷协议Encapsulating Security Payload(ESP)、密钥管理协议Internet Key Exchange (IKE)和用于网络认证及加密的一些算法等。IPSec 规定了如何在对等层之间选择安全协议、确定安全算法和密钥交换,向上提供了访问控制、数据源认证、数据加密等网络安全服务。

  一、安全特性

  IPSec的安全特性主要有:

   ·不可否认性 "不可否认性"可以证实消息发送方是唯一可能的发送者,发送者不能否认发送过消息。"不可否认性"是采用公钥技术的一个特征,当使用公钥技术时,发送方用私钥产生一个数字签名随消息一起发送,接收方用发送者的公钥来验证数字签名。由于在理论上只有发送者才唯一拥有私钥,也只有发送者才可能产生该数字签名,所以只要数字签名通过验证,发送者就不能否认曾发送过该消息。但"不可否认性"不是基于认证的共享密钥技术的特征,因为在基于认证的共享密钥技术中,发送方和接收方掌握相同的密钥。
  ·反重播性 "反重播"确保每个IP包的唯一性,保证信息万一被截取复制后,不能再被重新利用、重新传输回目的地址。该特性可以防止攻击者截取破译信息后,再用相同的信息包冒取非法访问权(即使这种冒取行为发生在数月之后)。
  ·数据完整性 防止传输过程中数据被篡改,确保发出数据和接收数据的一致性。IPSec利用Hash函数为每个数据包产生一个加密检查和,接收方在打开包前先计算检查和,若包遭篡改导致检查和不相符,数据包即被丢弃。
  ·数据可靠性(加密) 在传输前,对数据进行加密,可以保证在传输过程中,即使数据包遭截取,信息也无法被读。该特性在IPSec中为可选项,与IPSec策略的具体设置相关。
  ·认证 数据源发送信任状,由接收方验证信任状的合法性,只有通过认证的系统才可以建立通信连接。

  二、基于电子证书的公钥认证

  一个架构良好的公钥体系,在信任状的传递中不造成任何信息外泄,能解决很多安全问题。IPSec与特定的公钥体系相结合,可以提供基于电子证书的认证。公钥证书认证在Windows 2000中,适用于对非Windows 2000主机、独立主机,非信任域成员的客户机、或者不运行Kerberos v5认证协议的主机进行身份认证。

  三、预置共享密钥认证

  IPSec也可以使用预置共享密钥进行认证。预共享意味着通信双方必须在IPSec策略设置中就共享的密钥达成一致。之后在安全协商过程中,信息在传输前使用共享密钥加密,接收端使用同样的密钥解密,如果接收方能够解密,即被认为可以通过认证。但在Windows 2000 IPSec策略中,这种认证方式被认为不够安全而一般不推荐使用。

  四、公钥加密

  IPSec的公钥加密用于身份认证和密钥交换。公钥加密,也被称为"不对称加密法",即加解密过程需要两把不同的密钥,一把用来产生数字签名和加密数据,另一把用来验证数字签名和对数据进行解密。

  使用公钥加密法,每个用户拥有一个密钥对,其中私钥仅为其个人所知,公钥则可分发给任意需要与之进行加密通信的人。例如:A想要发送加密信息给B,则A需要用B的公钥加密信息,之后只有B才能用他的私钥对该加密信息进行解密。虽然密钥对中两把钥匙彼此相关,但要想从其中一把来推导出另一把,以目前计算机的运算能力来看,这种做法几乎完全不现实。因此,在这种加密法中,公钥可以广为分发,而私钥则需要仔细地妥善保管。

  五、Hash函数和数据完整性

  Hash信息验证码HMAC(Hash message authentication codes)验证接收消息和发送消息的完全一致性(完整性)。这在数据交换中非常关键,尤其当传输媒介如公共网络中不提供安全保证时更显其重要性。

  HMAC结合hash算法和共享密钥提供完整性。Hash散列通常也被当成是数字签名,但这种说法不够准确,两者的区别在于:Hash散列使用共享密钥,而数字签名基于公钥技术。hash算法也称为消息摘要或单向转换。称它为单向转换是因为:

  1)双方必须在通信的两个端头处各自执行Hash函数计算;
  2)使用Hash函数很容易从消息计算出消息摘要,但其逆向反演过程以目前计算机的运算能力几乎不可实现。

  Hash散列本身就是所谓加密检查和或消息完整性编码MIC(Message Integrity Code),通信双方必须各自执行函数计算来验证消息。举例来说,发送方首先使用HMAC算法和共享密钥计算消息检查和,然后将计算结果A封装进数据包中一起发送;接收方再对所接收的消息执行HMAC计算得出结果B,并将B与A进行比较。如果消息在传输中遭篡改致使B与A不一致,接收方丢弃该数据包。

  有两种最常用的hash函数:

 

  ·HMAC-MD5 MD5(消息摘要5)基于RFC1321。MD5对MD4做了改进,计算速度比MD4稍慢,但安全性能得到了进一步改善。MD5在计算中使用了64个32位常数,最终生成一个128位的完整性检查和。
  ·HMAC-SHA 安全Hash算法定义在NIST FIPS 180-1,其算法以MD5为原型。 SHA在计算中使用了79个32位常数,最终产生一个160位完整性检查和。SHA检查和长度比MD5更长,因此安全性也更高。

  六、加密和数据可靠性

  IPSec使用的数据加密算法是DES–Data Encryption Standard(数据加密标准)。DES密钥长度为56位,在形式上是一个64位数。DES以64位(8字节)为分组对数据加密,每64位明文,经过16轮置换生成64位密文,其中每字节有1位用于奇偶校验,所以实际有效密钥长度是56位。 IPSec还支持3DES算法,3DES可提供更高的安全性,但相应地,计算速度更慢。

  七、密钥管理

  ·动态密钥更新

  IPSec策略使用"动态密钥更新"法来决定在一次通信中,新密钥产生的频率。动态密钥指在通信过程中,数据流被划分成一个个"数据块",每一个"数据块"都使用不同的密钥加密,这可以保证万一攻击者中途截取了部分通信数据流和相应的密钥后,也不会危及到所有其余的通信信息的安全。动态密钥更新服务由Internet密钥交换IKE(Internet Key Exchange)提供,详见IKE介绍部分。

  IPSec策略允许专家级用户自定义密钥生命周期。如果该值没有设置,则按缺省时间间隔自动生成新密钥。

  ·密钥长度

  密钥长度每增加一位,可能的密钥数就会增加一倍,相应地,破解密钥的难度也会随之成指数级加大。IPSec策略提供多种加密算法,可生成多种长度不等的密钥,用户可根据不同的安全需求加以选择。

  ·Diffie-Hellman算法

  要启动安全通讯,通信两端必须首先得到相同的共享密钥(主密钥),但共享密钥不能通过网络相互发送,因为这种做法极易泄密。

  Diffie-Hellman算法是用于密钥交换的最早最安全的算法之一。DH算法的基本工作原理是:通信双方公开或半公开交换一些准备用来生成密钥的"材料数据",在彼此交换过密钥生成"材料"后,两端可以各自生成出完全一样的共享密钥。在任何时候,双方都绝不交换真正的密钥。

  通信双方交换的密钥生成"材料",长度不等,"材料"长度越长,所生成的密钥强度也就越高,密钥破译就越困难。 除进行密钥交换外,IPSec还使用DH算法生成所有其他加密密钥。

 Posted by at 上午 7:27