目录
前言
在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
两项参数解释如下:
-
net.ipv4.conf.default.rp_filter=0
: 这个设置表示在默认的网络接口上关闭反向路径过滤。反向路径过滤用于验证从系统收到的数据包是否通过与内核期望的相同的网络接口返回。将其设置为0表示禁用反向路径过滤。 -
net.ipv4.conf.all.rp_filter=0
: 这个设置表示在所有网络接口上关闭反向路径过滤。与前一个设置不同,这个设置应用于系统上的所有网络接口。
由于上位机软件发送该广播报文时并没有绑定源地址,且此时ubuntu系统是默认开启源地址验证,则该报文则被系统方向过滤掉了。鉴于该种情况,在ubuntu系统中关闭反向路径过滤即可,如下:
重启ubuntu系统,在此运行程序时发现recvform可以接收到该广播报文。
总结
在ubuntu系统如果碰到recvform接收不到广播报文的情况,可以参考本文内容解决。