1、问题描述

在设备使用linux操作系统(湖南麒麟)时,当外界通过网络发组播包给该设备对应网卡时,有可能会出现网卡通过socket无法接收网络包的情况。此时在linux下通过tcpdump能够抓到网卡接收到的网络包,只是该网络包未传递给上层应用。

2、解决办法

出现这种情况时,通常是linux系统下默认对网卡设置了过滤条件,导致底层能收到包,但被上层过滤。可以通过关闭网卡过滤条件进行解决。

在/etc/sysctl.conf内

设置

net.ipv4.conf.all.rp_filter=0

net.ipv4.conf.default.rp_filter=0

net.ipv4.conf.XXXX(网卡名称).rp_filter=0

添加后重启系统生效。

3、补充说明

如果要通过组播接收,首先需要设置网卡的路由信息,否则网卡底层也无法接收到组播包,可以通过在/etc/rc.local中添加网卡的路由信息,格式如下:

route add -net 224.X.X.X netmask 255.X.X.X dev XXX(网卡名称)

其实掩码255一般不太常用,chatgpt上面有解释

4、测试代码

可以使用下面代码进行测试,验证是否收到数据

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <net/if.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <time.h>

#define IP_SIZE     16   //不要修改
#define UDP_ADDR    "224.0.11.10"      //组播地址跟据需要修改
#define UDP_PORT    6000				    //端口号跟据需要修改
#define ETHX        "enp16s0"  			//不同网卡名需要修改
#define N           2080





#define COLOR_NONE  "\033[0m"
#define RED         "\033[1;31;40m"

#define errlog(errmsg) do{perror(errmsg);\
                          printf("%s--%s--%d\n",\
                                  __FILE__, __func__, __LINE__);\
                          exit(1);\
                         }while(0)

int get_local_ip(const char *eth_inf, char *ip)
{
    int sd;
    struct sockaddr_in sin;
    struct ifreq ifr;
 
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sd)
    {
        printf("socket error: %s\n", strerror(errno));
        return -1;
    }
 
    strncpy(ifr.ifr_name, eth_inf, IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;
 
    // if error: No such device
    if (ioctl(sd, SIOCGIFADDR, &ifr) < 0)
    {
        printf("ioctl error: %s\n", strerror(errno));
        close(sd);
        return -1;
    }
 
    memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
    snprintf(ip, IP_SIZE, "%s", inet_ntoa(sin.sin_addr));
 
    close(sd);
    return 0;
}



int main()
{
    int sockfd;
    struct sockaddr_in groupcastaddr, addr;
    socklen_t addrlen = sizeof(groupcastaddr);
    char buf[N] = {};
    int i,j = 0;
    char ip[IP_SIZE];	
    unsigned int Num = 0; 
    unsigned char Slot = 0; 
    unsigned int Count =0;
	int time_interval=0;
	time_t time1 = time(NULL);;
	time_t time2 = time(NULL);

    //第一步:创建套接字
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        errlog("fail to socket");
    }

    //第二步:填充组播网络信息结构体
    //inet_addr:将点分十进制ip地址转化为网络字节序的整型数据
    //htons:将主机字节序转化为网络字节序
    //atoi:将数字型字符串转化为整型数据
    groupcastaddr.sin_family = AF_INET;
    groupcastaddr.sin_addr.s_addr = inet_addr(UDP_ADDR); //224-239
    groupcastaddr.sin_port = htons(UDP_PORT);                                                                                                                              
    //第三步:将套接字与服务器网络信息结构体绑定
    if(bind(sockfd, (struct sockaddr *)&groupcastaddr, addrlen) < 0)
    {
        errlog("fail to bind");
    }

    get_local_ip(ETHX, ip);
    printf("local %s ip: %s\n", ETHX, ip);
    //加入多播组,允许数据链路层处理指定数据包
    struct ip_mreq mreq;
	struct in_addr inaddr = {0};
    inaddr.s_addr = inet_addr(ip);
    mreq.imr_interface.s_addr = inaddr.s_addr;
    mreq.imr_multiaddr.s_addr = inet_addr(UDP_ADDR);
    //mreq.imr_interface.s_addr = htonl(INADDR_ANY);
	//mreq.imr_interface.s_addr = inet_addr("192.160.82.99");

    if(setsockopt(sockfd, IPPROTO_IP,IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
    {
        errlog("fail to setsockopt");
    }

    ssize_t bytes;
    while(1)
    {
		if((bytes = recvfrom(sockfd, buf, N, 0,(struct sockaddr *)&addr, &addrlen)) < 0)
        {
            errlog("fail to recvfrom");
        }
		else
		{

			time1 = time(NULL);
			//printf("%d \n",time1);//第N个包时刻
			time_interval = time1 - time2; // 时间差
			//printf("%d \n",time_interval);
			time2 = time1;//第N-1个包的时刻

            Count++;
            printf("########################################\
##########################################\n");
            printf("ip: %s, port: %d   Count:%d    ",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),Count);
			if( (time_interval < 6) )
			{
			  printf(RED"Rcv_Packet_Interval: %d \n"COLOR_NONE,time_interval);
			}
			else
			{
				printf(RED"Rcv_Packet_Interval: ERROR ERROR ERROR \n"COLOR_NONE);
			}
         for(i=0;i<bytes;i++)
             {
           	
              	printf("%02x ",buf[i]);

			 }

        }
    }

    close(sockfd);

    return 0;
}     

Logo

更多推荐