tcpreplay重播tcpdump抓到的数据包,socket应用程序看不到。

问题稍微有点复杂,请大神耐心看完。
我的主机在工作日的某个时间段会收到UDP广播数据包,我用C语言做了个简单的应用程序来接收这些UDP广播数据包。
因为只有在某个特定时间才能接收数据包,为了测试程序,我先用tcpdump把数据包保存下来,然后用tcpreplay工具重放这些抓到的数据包。分条总结一下如下:

# 1. 用tcpdump抓包
tcpdump -i enp0s25 -s 1600 -w /home/user/log/capture.pcap

# 2. 用tcpreplay重放这些抓到的数据包
tcpreplay -i p1p2 --pktlen /home/user/log/capture.pcap

ifconfig命令的输出如下:

enp0s25: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500

   inet 192.168.3.6  netmask 255.255.255.0  broadcast 192.168.3.255
   inet6 2400:2410:2a41:2e00:1d6f:2fde:2436:608f  prefixlen 64  scopeid 0x0<global>
   inet6 fe80::555d:f7f2:a114:51df  prefixlen 64  scopeid 0x20<link>
   ether 98:90:96:a7:6f:f3  txqueuelen 1000  (Ethernet)
   RX packets 45031  bytes 50053845 (47.7 MiB)
   RX errors 0  dropped 0  overruns 0  frame 0
   TX packets 14129  bytes 1784865 (1.7 MiB)
   TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
   device interrupt 20  memory 0xf9100000-f9120000  

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536

   inet 127.0.0.1  netmask 255.0.0.0
   inet6 ::1  prefixlen 128  scopeid 0x10<host>
   loop  txqueuelen 1  (Local Loopback)
   RX packets 0  bytes 0 (0.0 B)
   RX errors 0  dropped 0  overruns 0  frame 0
   TX packets 0  bytes 0 (0.0 B)
   TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

p1p1: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500

   ether 00:02:c9:5a:77:62  txqueuelen 1000  (Ethernet)
   RX packets 0  bytes 0 (0.0 B)
   RX errors 218  dropped 207  overruns 0  frame 0
   TX packets 0  bytes 0 (0.0 B)
   TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

p1p2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500

   inet6 fe80::2acf:23a9:3c1a:89a0  prefixlen 64  scopeid 0x20<link>
   ether 00:02:c9:5a:77:63  txqueuelen 1000  (Ethernet)
   RX packets 0  bytes 0 (0.0 B)
   RX errors 0  dropped 0  overruns 0  frame 0
   TX packets 329583  bytes 50655424 (48.3 MiB)
   TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

p4p1: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500

   ether 00:0f:53:29:e9:30  txqueuelen 1000  (Ethernet)
   RX packets 0  bytes 0 (0.0 B)
   RX errors 0  dropped 0  overruns 0  frame 0
   TX packets 0  bytes 0 (0.0 B)
   TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
   device interrupt 26  

p4p2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500

   ether 00:0f:53:29:e9:31  txqueuelen 1000  (Ethernet)
   RX packets 329583  bytes 51973756 (49.5 MiB)
   RX errors 0  dropped 0  overruns 0  frame 0
   TX packets 0  bytes 0 (0.0 B)
   TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
   device interrupt 35

我的机器上装有3张网卡,其中有两张是光卡,每张光卡都有两个port,p1p1和p1p2属于同一张卡, p4p1和p4p2属于同一张卡。我的接线方式是:
p1p2的光纤接到了p4p2上,所以这样就可以通过p1p2把数据包发送到p4p2上了。

结下泪用tcpdump命令查看p4p2上的数据包,发现确实有数据输出

tcpdump -i p4p2 -nnn

18:05:21.062023 IP 10.10.31.31.3131 > 239.239.31.31.3131: UDP, length 97

18:05:21.062035 IP 10.10.31.31.3131 > 239.239.31.31.3131: UDP, length 97

问题是我的应用程序却无法监听到数据包,我的应用程序的部分代码如下:

#pragma pack(1)

#include <stdio.h>
#include <string.h>
#include <float.h>
#include <stdlib.h>
#include <ctype.h>
#include <signal.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/socket.h>

#define MAXBUFSIZE 65536 // Max UDP Packet size is 64 Kbyte

int main() {

int sock, status, socklen;
uint8_t buffer[MAXBUFSIZE];
struct sockaddr_in saddr;
struct ip_mreq imreq;

memset(&saddr, 0x00, sizeof(saddr));
memset(&imreq, 0x00, sizeof(imreq));
memset(buffer, 0x00, MAXBUFSIZE);

// open a UDP socket
sock = socket(AF_INET, SOCK_DGRAM, 0);
if ( sock < 0 ) {
    perror("Error creating socket"); 
    return(0);
} 

imreq.imr_multiaddr.s_addr = inet_addr("239.239.31.31");
imreq.imr_interface.s_addr = INADDR_ANY; // use DEFAULT interface

// JOIN multicast group on default interface
setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void *)&imreq, sizeof(struct ip_mreq));

saddr.sin_family = AF_INET;
saddr.sin_port = htons(3131); 
saddr.sin_addr.s_addr = htonl(INADDR_ANY); 
status = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));

if ( status < 0 ) {
    perror("Error binding socket to interface");
    return(0);
}

socklen = sizeof(struct sockaddr_in);

// receive 500 packets from socket
int m = 0;
for (; m < 500; m++) {
    status = recvfrom(sock, buffer, MAXBUFSIZE, 0, (struct sockaddr *)&saddr, &socklen);
    if(status < 0) {
        perror("receive error!\n");
    } else if (status > 0) {
        // DO sth usefully here.
    }                
}

// shutdown socket
shutdown(sock, 2);
// close socket
close(sock);

return 0;

}

运行程序后,程序会停在recvfrom方法上。为什么tcpdump能看到,而我的应用程序却看不到数据包呢?
求指教。

阅读 9.1k
2 个回答

你可以用tcpdump的-e参数来看看数据包的源和目的mac地址,我没用过多播,不确定多播这种情况下目的mac地址是不是广播地址,如果目的mac地址是你enp0s25的mac地址的话,你这样子操作肯定不行,p4p2收到数据包后发现目的mac地址不是自己,会丢弃该包。

你这里tcpdump能在p4p2上面看到数据包,是因为tcpdump会开启网卡的混杂模式,于是能看到网卡收到的所有数据包,但由于数据包的目的mac地址不是当前网卡,协议栈收到网卡驱动的数据包之后会将数据包丢弃掉。

建议在和enp0s25同网段的其它机器上用tcpreplay试试,看enp0s25能不能收到相应的数据包,或者在当前机器上用tcpreplay试试-i enp0s25,看从这个网卡出去后能不能被交换机再发送回来。

另外你这里没有过滤数据包,capture.pcap里面应该包含这个网卡收到的和发送出去的所有数据包,我没用过tcpreplay,不确定tcpreplay有没有区分请求和应答包,建议过滤一下,只包含自己需要的数据包,避免不必要的麻烦。

新手上路,请多包涵

问题解决了。
原因在于数据包被操作系统过滤掉了,所以系统调用socket无法看到数据包。需要作如下操作让数据包通过操作系统。

# stop firewall
systemctl stop firewalled # CentOS 7.3. other versions, please search how to stop firewall.

# set route
route add -net 239.0.0.0 netmask 255.0.0.0 dev p4p2

# close rp_filter
sysctl net.ipv4.conf.all.rp_filter=0
sysctl net.ipv4.conf.default.rp_filter=0
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题