ubuntu18-recvfrom接收不到广播报文异常分析

目录

前言

一、UDP广播接收程序

二、异常原因分析

总结


前言

在ubuntu18.04系统中,编写udp接收程序发现接收不到广播报文,使用抓包工具tcpdump可以抓取到广播报文,在此对该现象分析解析如下文所示。

一、UDP广播接收程序

UDP广播接收程序如下所示,广播发送程序使用上位机向255.255.255.255地址11100端口发送广播报文。

cpp 复制代码
int CreateUdpSocket(int iUdpPort)
{
    int flag = 1;
    int err;
    int iSockFd = 0;
    short sPort = (short)iUdpPort;
    struct sockaddr_in tSockAddr;
    
	
    iSockFd = socket(AF_INET, SOCK_DGRAM, 0);
    if (iSockFd <= 0)
    {
        printf("[%s]Create local socket Error.\n", __FUNCTION__);
        return -1;
    }

    tSockAddr.sin_family      = AF_INET;
    tSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    tSockAddr.sin_port        = htons(sPort);
	
    err = setsockopt(iSockFd, SOL_SOCKET, SO_REUSEADDR, (char*)&flag, sizeof(int));
    if(err != 0)
    {
        printf("ERR: setsockopt  socket error. err = %d,errno = %d[%s] port %d\n",err, errno, strerror(errno), iUdpPort);
        close(iSockFd);
        return -2;
    }
    if (bind(iSockFd, (struct sockaddr*)&tSockAddr, sizeof(struct sockaddr_in)) < 0)
    {
        printf("ERR:  bind error.\n");
        close(iSockFd);
        return -3;
    }
    
    return iSockFd;
}

void *StartBroadCastThread(void *arg)
{
    char acInterface[] = "eth0";
    int ifindex = 0;
    int iSocket = -1;
    int iLen = 10000;
    int iRet = 0;
    int iBroadcastIface = -1;
    int iEthIndex = 0;
    int iEthNum = 0;
    int i;
    unsigned char *pcData = NULL;
    struct sockaddr_in tSockAddr;
    int iSockAddrLen = sizeof(tSockAddr);
    int iRecvLen = 0;
    short sSendLen = 0;
    PT_MSG_INFO ptMsg = NULL;
    T_MSG_BROADCAST_DEV_INFO *ptBroadCastInfo = NULL;
    //T_ETHNAME_INFO atEthNameInfo[MAX_ETH_NUM];
 //   T_DEV_NET_INFO *ptNetInfo = NULL;
    char acBroadcastIface[24];
    char acTmpBuf[512];
    int iSessionId = 0;
    T_ETHNAME_INFO atEthNameInfo[4];
    
    iSocket = CreateUdpSocket(MSG_TEST_BROADCAST_PORT);
    if (iSocket < 0)
    {
        printf("socket udp failed\n");
        return NULL;
    }
    
    pcData = (char *)malloc(iLen);
    if (NULL == pcData)
    {
        printf("[%s] malloc failed\n", __FUNCTION__);
        close(iSocket);
        return NULL;
    }
    
    //ptHead = (PT_MSG_HEAD)pcBuf;
    while (1)
    {
        iRecvLen = recvfrom(iSocket, pcData, iLen, 0, 
                                   (struct sockaddr *)&tSockAddr, (socklen_t*)&iSockAddrLen);
        if(iRecvLen <= 0)
        {
            printf("[%s] recvfrom fail, .\n", __FUNCTION__);
            close(iSocket);
            free(pcData);
            return NULL;
        }
        if (iRecvLen < 4)
        {
            printf("iRecvLen = %d\n", iRecvLen);
            continue;
        }
        printf("[%s]recv len %d, magic %d, cmd %d\n", __FUNCTION__, iRecvLen, pcData[0], pcData[1]);
        if ((pcData[0] == MSG_MAGIC_FLAG) && (pcData[1] == MSG_CLI2SERV_BROADCAST_QUERY))
        {
        	  printf("[%s]%d\n", __FUNCTION__, __LINE__);
        	  memset(pcData, 0, iLen);
            ptMsg = (PT_MSG_INFO)pcData;
            ptBroadCastInfo = (T_MSG_BROADCAST_DEV_INFO *)ptMsg->acMsgData;
           /* srand((int)time(0));
            iSessionId = (int)(300000.0*rand()/(RAND_MAX+1.0));
            memset(ptBroadCastInfo, 0, sizeof(T_MSG_BROADCAST_DEV_INFO));
            
            ptBroadCastInfo->iSessionId = htonl(iSessionId);*/
            

            iRet =  ABI_GetDevInfo((char *)ptBroadCastInfo, sizeof(T_MSG_BROADCAST_DEV_INFO));
            
            iRet = COMM_GetNetWorkCardNum(&iEthNum, atEthNameInfo);
            if (iRet < 0)
            {
                printf("Get None NetWork Card\n");
                continue;
            }

            for (i = 0; i < iEthNum; i++)
            {
                sSendLen = sizeof(T_MSG_BROADCAST_DEV_INFO);
                ptMsg->tMsgHead.magic = MSG_MAGIC_FLAG;
                ptMsg->tMsgHead.cmd = MSG_SERV2CLI_BROADCAST_QUERY_RESP;
                ptMsg->tMsgHead.sLen = htons(sSendLen);
                COMM_GetIpAddr(atEthNameInfo[i].acName, ptBroadCastInfo->acIpAddr, sizeof(ptBroadCastInfo->acIpAddr));
                read_interface(atEthNameInfo[i].acName, &ifindex, NULL, NULL);
                send_broadcast(ifindex, MSG_TEST_BROADCAST_PORT, ntohs(tSockAddr.sin_port), pcData, sizeof(T_MSG_HEAD) + sSendLen);
            }   
        }
    }
    
    close(iSocket);
    if (pcData)
    {
        free(pcData);
        pcData = NULL;
    }
    
    return NULL;
}

使用tcpdump抓包工具可以抓到广播报文:

但是通过recvfrom函数则接收不到该报文。

二、异常原因分析

考虑到ubuntu可以自带防火墙,但是通过查询ubuntu系统的防火墙功能,发现并没有开启,通过查询相关资料,发现可能跟Linux内核网络参数的设置有关,涉及到反向路径过滤,在/etc/sysctl.conf中有反向路径过滤参数可以设置,两项参数如下所示:

cpp 复制代码
# Uncomment the next two lines to enable Spoof protection (reverse-path filter)
# Turn on Source Address Verification in all interfaces to
# prevent some spoofing attacks
#net.ipv4.conf.default.rp_filter=0
#net.ipv4.conf.all.rp_filter=0

两项参数解释如下:

  1. net.ipv4.conf.default.rp_filter=0: 这个设置表示在默认的网络接口上关闭反向路径过滤。反向路径过滤用于验证从系统收到的数据包是否通过与内核期望的相同的网络接口返回。将其设置为0表示禁用反向路径过滤。

  2. net.ipv4.conf.all.rp_filter=0: 这个设置表示在所有网络接口上关闭反向路径过滤。与前一个设置不同,这个设置应用于系统上的所有网络接口。

由于上位机软件发送该广播报文时并没有绑定源地址,且此时ubuntu系统是默认开启源地址验证,则该报文则被系统方向过滤掉了。鉴于该种情况,在ubuntu系统中关闭反向路径过滤即可,如下:

重启ubuntu系统,在此运行程序时发现recvform可以接收到该广播报文。


总结

在ubuntu系统如果碰到recvform接收不到广播报文的情况,可以参考本文内容解决。

相关推荐
wowocpp10 小时前
ubuntu 22.04 server 安装 anaconda3
linux·运维·ubuntu
海绵波波10711 小时前
Webserver(4.8)UDP、广播、组播
单片机·网络协议·udp
wowocpp12 小时前
ubuntu 22.04 防火墙 ufw
linux·运维·ubuntu
橘色的喵13 小时前
Linux编程:DMA增加UDP 数据传输吞吐量并降低延迟
linux·udp·dma·网络驱动·低延迟·吞吐量·nic
188_djh20 小时前
# Python基础到实战一飞冲天(一)--linux基础(一)
linux·开发语言·python·ubuntu·centos·os·operationsystem
winds~20 小时前
ubuntu中安装matplotcpp绘图
linux·运维·ubuntu
UsamaBinLaden21 小时前
Ubuntu和Debian系列的Release默认shell解释器变更
linux·ubuntu·debian
C++忠实粉丝1 天前
计算机网络socket编程(1)_UDP网络编程实现echo server
linux·服务器·网络·c++·网络协议·计算机网络·udp
黑风风1 天前
Ubuntu 22 安装 Apache Doris 3.0.3 笔记
笔记·ubuntu·apache
重生之我是数学王子2 天前
网络编程 UDP编程 Linux环境 C语言实现
linux·c语言·开发语言·网络·网络协议·udp