更新
- 01-27ter:添加了rp_filter信息
- 01-27bis:请注意,9.04 盒子在不同的界面上工作。
- 01-27:增加接口配置信息和报文分析。
原帖
我有两个极其相似的硬件配置(SuperMicro 1U 系统,带有双 Xeon CPU 和两个板载以太网端口),一个运行 Ubuntu 8.04(Linux 2.6.24-26-server),一个运行 Ubuntu 9.04(Linux 2.6.28- 17个服务器)。它们都将 eth1 连接到同一个网络,在该网络上,各种其他服务器正在向各种端口发送广播 UDP 数据包。在两台主机上,在 eth1 上使用 tcpdump,我可以看到这些广播 UDP 数据包到达。
然而,虽然在 8.04 机器上我可以让一个简单的程序很好地收听它们,但在 9.04 机器上,一个相同的程序永远不会接收到它们。作为一个高级概述,这里是一个示例 Haskell 程序,它适用于一个但不能适用于另一个(在两者上使用相同版本的 GHC):
import Network.Socket
port = 5515
main :: IO ()
main = do
do sock <- socket AF_INET Datagram defaultProtocol
bindSocket sock $ SockAddrInet (fromIntegral port) iNADDR_ANY
loop sock
where
loop sock =
do msg <- recv sock 2048
print msg
loop sock
如果这个问题在 GHC 中发生了一些非常奇怪的事情(尽管两者的构建相同),我编写了一个 C 程序来做同样的事情:
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#define BUFLEN 512
#define NPACK 10
#define PORT 5515
void diep(char *s)
{
perror(s);
exit(1);
}
void showb(int s) {
int val, len, retval;
len = sizeof(val);
retval = getsockopt(s, SOL_SOCKET, SO_BROADCAST, &val, &len);
printf("showb retval=%d val=%d\n", retval, val);
}
int main(int argc, char **argv)
{
struct sockaddr_in si_me, si_other;
int s, i, slen=sizeof(si_other);
char buf[BUFLEN];
if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
diep("socket");
showb(s);
i = 1;
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i))==-1)
diep("setsockopt");
showb(s);
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(PORT);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s, &si_me, sizeof(si_me))==-1)
diep("bind");
puts("Listening.");
for (i=0; i<NPACK; i++) {
if (recvfrom(s, buf, BUFLEN, 0, &si_other, &slen)==-1)
diep("recvfrom()");
printf("Received packet from %s:%d\nData: %s\n\n",
inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port), buf);
}
close(s);
return 0;
}
您会注意到,在这种情况下,只是为了好玩,我还打开了套接字上的 SO_BROADCAST 标志,并确认它已打开,尽管它对程序的行为没有影响,这是相同的. 即使我将构建在 8.04 上的二进制文件复制到 9.04 机器上,反之亦然,在所有情况下,在 8.04 机器上运行的程序都会看到 UDP 广播数据包,而 9.04 机器却看不到。
我究竟做错了什么?
更新 01-27:
这是工作(8.04)主机的 ip link 和 ip ether 的输出:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1362 qdisc pfifo_fast qlen 1000
link/ether 00:30:48:d3:4b:06 brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 100
link/ether 00:30:48:d3:4b:07 brd ff:ff:ff:ff:ff:ff
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1362 qdisc pfifo_fast qlen 1000
link/ether 00:30:48:d3:4b:06 brd ff:ff:ff:ff:ff:ff
inet 192.168.228.130/28 brd 192.168.228.143 scope global eth0
inet6 fe80::230:48ff:fed3:4b06/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 100
link/ether 00:30:48:d3:4b:07 brd ff:ff:ff:ff:ff:ff
inet 172.40.4.130/24 brd 172.40.4.255 scope global eth1
inet6 fe80::230:48ff:fed3:4b07/64 scope link
valid_lft forever preferred_lft forever
对于非工作(9.04)服务器:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1362 qdisc pfifo_fast state UP qlen 1000
link/ether 00:30:48:d9:38:da brd ff:ff:ff:ff:ff:ff
3: eth2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 00:1b:21:36:19:fd brd ff:ff:ff:ff:ff:ff
4: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 100
link/ether 00:30:48:d9:38:db brd ff:ff:ff:ff:ff:ff
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1362 qdisc pfifo_fast state UP qlen 1000
link/ether 00:30:48:d9:38:da brd ff:ff:ff:ff:ff:ff
inet 192.168.228.132/28 brd 192.168.228.143 scope global eth0
inet6 fe80::230:48ff:fed9:38da/64 scope link
valid_lft forever preferred_lft forever
3: eth2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 00:1b:21:36:19:fd brd ff:ff:ff:ff:ff:ff
4: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 100
link/ether 00:30:48:d9:38:db brd ff:ff:ff:ff:ff:ff
inet 172.40.4.132/24 brd 172.40.4.255 scope global eth1
inet6 fe80::230:48ff:fed9:38db/64 scope link
valid_lft forever preferred_lft forever
请注意,对于这两种情况,eth1 是广播到达的端口。
这是程序未接收到的示例广播数据包的完整解码(来自非工作 9.04 服务器上的 tshark):
Frame 193555 (271 bytes on wire, 271 bytes captured)
Arrival Time: Jan 25, 2010 08:00:00.535345000
[Time delta from previous captured frame: 0.001508000 seconds]
[Time delta from previous displayed frame: 0.000000000 seconds]
[Time since reference or first frame: 6590.956186000 seconds]
Frame Number: 193555
Frame Length: 271 bytes
Capture Length: 271 bytes
[Frame is marked: False]
[Protocols in frame: eth:ip:udp:data]
Ethernet II, Src: Cisco_aa:c0:28 (00:d0:bb:aa:c0:28), Dst: Broadcast (ff:ff:ff:ff:ff:ff)
Destination: Broadcast (ff:ff:ff:ff:ff:ff)
Address: Broadcast (ff:ff:ff:ff:ff:ff)
.... ...1 .... .... .... .... = IG bit: Group address (multicast/broadcast)
.... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
Source: Cisco_aa:c0:28 (00:d0:bb:aa:c0:28)
Address: Cisco_aa:c0:28 (00:d0:bb:aa:c0:28)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
.... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
Type: IP (0x0800)
Internet Protocol, Src: 192.166.1.120 (192.166.1.120), Dst: 255.255.255.255 (255.255.255.255)
Version: 4
Header length: 20 bytes
Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00)
0000 00.. = Differentiated Services Codepoint: Default (0x00)
.... ..0. = ECN-Capable Transport (ECT): 0
.... ...0 = ECN-CE: 0
Total Length: 257
Identification: 0xfad3 (64211)
Flags: 0x04 (Don't Fragment)
0... = Reserved bit: Not set
.1.. = Don't fragment: Set
..0. = More fragments: Not set
Fragment offset: 0
Time to live: 252
Protocol: UDP (0x11)
Header checksum: 0xc0f9 [correct]
[Good: True]
[Bad : False]
Source: 192.166.1.120 (192.166.1.120)
Destination: 255.255.255.255 (255.255.255.255)
User Datagram Protocol, Src Port: 56172 (56172), Dst Port: 5515 (5515)
Source port: 56172 (56172)
Destination port: 5515 (5515)
Length: 237
Checksum: 0x01ba [correct]
[Good Checksum: True]
[Bad Checksum: False]
Data (229 bytes)
0000 41 37 30 33 34 30 38 30 30 30 30 30 30 31 31 30 A703408000000110
0010 4b 52 53 50 49 4f 50 4b 32 49 4b 52 34 32 30 31 KRSPIOPK2IKR4201
0020 45 32 32 32 35 33 30 30 32 31 30 30 30 30 30 30 E222530021000000
0030 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
0040 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
0050 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
0060 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
0070 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
0080 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
0090 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
00a0 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
00b0 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
00c0 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
00d0 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
00e0 30 30 30 30 ff 0000.
Data: 413730333430383030303030303131304B525350494F504B...
我已经将此与来自工作 8.04 服务器上的转储中的相同数据包进行了比较,并且数据包本身是相同的;唯一的区别在于帧号(在 pcap 文件中)和接收数据包的时间(1.224 毫秒的差异,考虑到两台主机使用相同的 NTP 服务器,这似乎很高,但并非完全不合理)。
更新 01-27bis
我做了进一步的实验,在 8.04 主机上生成我自己的广播数据包并将它们发送到 9.04 主机,当 8.04 主机发送它们并且它们到达 eth0 或 eth1 时,9.04 主机接收数据包就好了。
更新 01-27ter
sp 3; sysctl -a 2>/dev/null | grep '\.rp_filter' | sort
8.04主机上的输出为:
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 0
net.ipv4.conf.eth1.rp_filter = 0
net.ipv4.conf.lo.rp_filter = 1
在 9.04 主机上是:
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.eth1.rp_filter = 1
net.ipv4.conf.eth2.rp_filter = 1
net.ipv4.conf.lo.rp_filter = 0