gpt4 book ai didi

c - AF_PACKET 套接字上的 Bind() 首先失败......然后自行修复?

转载 作者:太空宇宙 更新时间:2023-11-04 12:19:03 25 4
gpt4 key购买 nike

我想不通。当我运行我的代码时......即使我绑定(bind)成功,我也会看到来自所有以太网类型和所有接口(interface)的数据。运行几分钟后……它会自行修复。然后我只从一个特定的接口(interface)看到,并且只有当 Ether 类型匹配时。目标是循环遍历所有接口(interface)以查找特定的 MAC 地址。当返回正确的响应时...我们会退出 for 循环,并根据需要配置所有内容。

// Copyright (c) 2017 Keith M. Bradley
//
//
// History:
// 13 May 2017 Keith M. Bradley Creation
// all rights reserved.
//

/* ----------------------- Standard includes --------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <pthread.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <netpacket/packet.h>
#include <netdb.h>



#define SIGNAL_THREAD_KILL 0xFF
#define SIGNAL_THREAD_RESET 0xFE

// Ethernet II protocol to use (0x88b5 ... experimental #1).
#define eType 0x88b5

#define msg_Hello "MikiePLC"
#define msg_Reply "IOM_1.0"
#define msg_Ack "ackMikiePLC"


void* PLCThread(void* arg)
{
// get our pointer to the PLC struct
PLC *myPLC = arg;
// get and save our thread ID
myPLC->tid = pthread_self();
// thread index number?



//------------------------------------------------------------------------------------------------------------------
// locals
uint8_t i; // used as an index or loop counts.
uint8_t j; // used as 2nd index or loop counts.

int rtn; // temp store or function return values.


//------------------------------------------------------------------------------------------------------------------
// create Ethernet buffers and variables.

char* outBuff = NULL; // character buffer for sending out on Ethernet.
size_t outBuffSz = 1540;

char* inBuff = NULL; // character buffer for receiving in on Ethernet.
size_t inBuffSz = 1540;

int fd; // file descriptor for socket.

int flags; // socket flags used bt fcntl().

struct
ifreq ifr; // used to get and set interface parameters.

struct
sockaddr_ll IOM_sa_flt; // socket address struct, used to filter received Ethernet frames from the remote IO module ... used by bind().

struct
sockaddr_ll IOM_sa_rcv; // socket address struct, used to store addr details of received frame ... used by recvfrom().
socklen_t IOM_sa_len; // IOM_sa_rcv length.

fd_set myfds; // used by select().

struct
timeval rcv_tm_out; // time out for select() to declare communications failed.



//------------------------------------------------------------------------------------------------------------------
// initialize Ethernet buffers and variables.

// allocate memory for the Ethernet sending message buffer.
outBuff = malloc(outBuffSz);
if (outBuff == NULL)
printf("\nNATIVE-PLCThread: Could not allocate outBuff memory.");
memset(outBuff, '\0', outBuffSz);

// allocate memory for the Ethernet recevied message buffer.
inBuff = malloc(inBuffSz);
if (inBuff == NULL)
printf("\nNATIVE-PLCThread: Could not allocate inBuff memory.");

// clear the sockaddr_ll structs.
// (send was already cleared ... it is inside the PLC typdef).
memset(&IOM_sa_rcv, 0, sizeof(IOM_sa_rcv));
memset(&IOM_sa_flt, 0, sizeof(IOM_sa_flt));

// set receiving sockaddr_ll struct size.
IOM_sa_len = sizeof(IOM_sa_rcv);

// setup the sending, receiving, and filtering sockaddr_ll's.
myPLC->IOM_sa_snd.sll_family = AF_PACKET;
myPLC->IOM_sa_snd.sll_protocol = htons(eType);

IOM_sa_rcv.sll_family = AF_PACKET;
IOM_sa_rcv.sll_protocol = htons(eType);

IOM_sa_flt.sll_family = AF_PACKET;
IOM_sa_flt.sll_protocol = htons(eType);



//------------------------------------------------------------------------------------------------------------------
// open our socket in dgram mode and setup the socket's features.
fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
if (fd == -1)
{
fprintf(stderr, "%s\n", strerror(errno));
printf("\nNATIVE-PLCThread: socket() failed !! - ");
}

// get the socket file descriptor flags.
flags = fcntl(fd, F_GETFL, 0);

// if succesful, set to non-blocking.
if (flags != -1)
fcntl(fd, F_SETFL, flags | O_NONBLOCK);




if (fd != -1) // valid socket file descriptor means ok to proceed with IOM_Addr_search.
{
// IOM_MAC_search
// if MAC_Addr is configured,
// loop to find which interface has the IOM (I/O Module).
//
// begin for loop ----------------------------------------------------------------------------------------------
for (i = 1; 1; i++)
{
// we need to test for thread kill signal.
if((myPLC->ThreadCtrl == SIGNAL_THREAD_KILL) || (myPLC->ThreadCtrl == SIGNAL_THREAD_RESET)) break;

// if the user cleared the MAC addr while we were searching ... give up and run the engine.
if (myPLC->MAC_is_Valid != 0xa5) break;

// clear the ifreq struct.
memset(&ifr, 0, sizeof(ifr));

// i is our 'for' loop counter and our current interface index.
ifr.ifr_ifindex = i;

// does the interface exist?
if (ioctl(fd, SIOCGIFNAME, &ifr) == -1)
{
// if not, we ran past top of network interfaces.
printf("\nNATIVE-PLCThread: IOM_MAC_search MAC address not found after searching all interfaces !!!\n");
printf("\n_________________________________________________________________________________________\n");
sleep(10);
i = 0;
continue;
}
// don't mess with loopback interface.
if (strcmp(ifr.ifr_name,"lo") == 0) continue;

// store the ifname using the pointer.
strncpy (myPLC->ifName, ifr.ifr_name, sizeof(ifr.ifr_name) - 1);
myPLC->ifName[IFNAMSIZ - 1] = '\0';

// update the interface index in all sockaddr structs.
myPLC->IOM_sa_snd.sll_ifindex = i;
IOM_sa_rcv.sll_ifindex = i;
IOM_sa_flt.sll_ifindex = i;

// is the interface up?
ioctl(fd, SIOCGIFFLAGS, &ifr);
if ((ifr.ifr_flags & IFF_UP) == 0)
{
printf("\nNATIVE-PLCThread: IOM_Addr_search interface %s (index %d) is down.\n", myPLC->ifName, i);
continue;
}

// bind it.
if (bind(fd, (struct sockaddr*)&IOM_sa_flt, sizeof(IOM_sa_flt)) == -1)
{
fprintf(stderr, "%s\n", strerror(errno));
printf("\nNATIVE-PLCThread: IOM_Addr_search bind() failed !!!\n");
continue;
}

// pause and flush? (didn't help at all)
sleep(2);
recvfrom(fd, inBuff, inBuffSz, 0, (struct sockaddr *)&IOM_sa_rcv, &IOM_sa_len);

// fill outBuff with the hello message.
strcpy(outBuff, msg_Hello);

// send hello msg to the IOM with configured IOM_MAC_address.
if (sendto(fd, outBuff, sizeof(msg_Hello), 0, (struct sockaddr *)&(myPLC->IOM_sa_snd), sizeof (myPLC->IOM_sa_snd)) == -1)
{
fprintf(stderr, "%s\n", strerror(errno));
printf("\nNATIVE-PLCThread: IOM_Addr_search sendto() failed on interface %s (index %d) !!!\n", myPLC->ifName, i);
continue;
}

// setup for the select() time out loop.
rcv_tm_out.tv_sec = 0;
rcv_tm_out.tv_usec = 50000;

// begin while loop ------------------------------------------------------------------------------------------
//
// select() time out loop.
// wait for valid response from IOM_MAC_address (discard any ETHERNET 2 messages from other MAC's).
//
while ((rcv_tm_out.tv_sec != 0) || (rcv_tm_out.tv_usec != 0))
{
// create the file descriptor set for use by select().
FD_ZERO(&myfds);
FD_SET(fd, &myfds);

// select() to sleep until received frame is ready, or the maximum length of time it would taked to get a response is exceeded.
rtn = select(fd + 1, &myfds, NULL, NULL, &rcv_tm_out);
if (rtn < 0)
{
fprintf(stderr, "%s\n", strerror(errno));
printf("\nNATIVE-PLCThread: IOM_Addr_search select() returned <0 on interface %s (index %d).\n", myPLC->ifName, i);
break;
}
// did we time out? ... then goto the next interface to search.
else if (rtn == 0)
{
printf("\nNATIVE-PLCThread: IOM_Addr_search select() timed out (returned 0) on interface %s (index %d).\n", myPLC->ifName, i);
break;
}
else // select() returned > 0.
{
if (FD_ISSET(fd, &myfds))
{
// our socket is ready for reading ... 1st clear the buffer and the sock addr.
memset(inBuff, '\0', inBuffSz);
for (j = 0; j < 6; j++)
IOM_sa_rcv.sll_addr[j] = 0;
rtn = recvfrom(fd, inBuff, inBuffSz, 0, (struct sockaddr *)&IOM_sa_rcv, &IOM_sa_len);
if(rtn < 0)
{
if (errno == EAGAIN)
printf("\nNATIVE-PLCThread: IOM_Addr_search recvfrom() returned EAGAIN.\n");
else if (errno == EWOULDBLOCK)
printf("\nNATIVE-PLCThread: IOM_Addr_search recvfrom() returned EWOULDBLOCK.\n");
else
{
fprintf(stderr, "%s\n", strerror(errno));
printf("\nNATIVE-PLCThread: IOM_Addr_search recvfrom() returned unrecoverable error.\n");
}
break;
}
else if (rtn == 0)
printf("\nNATIVE-PLCThread: IOM_Addr_search a_file_descriptor_is_set yet recvfrom() returned zero.\n");
else // recvfrom() returned > 0.
{
printf("\nNATIVE-PLCThread: IOM_Addr_search recvfrom() returned %d bytes on %s (index %d) MAC %02x:%02x:%02x:%02x:%02x:%02x rcv_tm_out.tv_sec = %d.%d\n",
rtn,
myPLC->ifName,
i,
IOM_sa_rcv.sll_addr[0],
IOM_sa_rcv.sll_addr[1],
IOM_sa_rcv.sll_addr[2],
IOM_sa_rcv.sll_addr[3],
IOM_sa_rcv.sll_addr[4],
IOM_sa_rcv.sll_addr[5],
(int)rcv_tm_out.tv_sec,
(int)rcv_tm_out.tv_usec);
// check the IOM_sa_rcv.MAC_Addr ... is it who we want to talk to? ... if not discard.
for (j = 0; j < 6; ++j)
if ((myPLC->IOM_sa_snd.sll_addr[j]) == (IOM_sa_rcv.sll_addr[j])) continue;

// MAC addr matches?
if (j > 50) // set to 50 to debug ... should be 5.
{
printf("\nMAC Addr from our IOM.\n");
// parse the received response to our hello msg.
if (strcmp(inBuff, msg_Reply) == 0)
{
// fill outBuff with the Ack message.
strcpy(outBuff, msg_Ack);
// send ack message to the IOM with configured IOM_MAC_address.
if (sendto(fd, outBuff, sizeof("ackMikiePLC"), 0, (struct sockaddr *)&(myPLC->IOM_sa_snd), sizeof (myPLC->IOM_sa_snd)) == -1)
{
fprintf(stderr, "%s\n", strerror(errno));
printf("\nNATIVE-PLCThread: IOM_Addr_search sendto() failed on interface %s (index %d) !!!\n", myPLC->ifName, i);
continue;
}
else
{
// declare ComStatus ok.
myPLC->ComStatus = 0xa5;
break; // we have a winner !!!
}
}
else
{
// declare ComStatus still NOT ok.
myPLC->ComStatus = 0x5a;
continue;
}
}
else
{
printf("\nMAC Addr from a stranger (discarded)!!!\n");
break;
}
}// END recvfrom() returned > 0.
}// END if (FD_ISSET(fd, &myfds))
else printf("\nNATIVE-PLCThread: IOM_Addr_search select() returned > 0 yet our only file descriptor was not set !!!\n");
}// END select() returned > 0.
}// END while loop -------------------------------------------------------------------------------------------
if (myPLC->ComStatus == 0xa5) break; // search is done ... break out of for loop.
}// END for loop -----------------------------------------------------------------------------------------------
}// END "valid socket fd means ok to proceed" ----------------------------------------------------------------------
else printf("\nNATIVE-PLCThread: IOM_Addr_search socket() previously failed ... search cannot proceed.\n");





// MAIN ENGINE LOOP !!!---------------------------------------------------------------------------------------------
//
// Loop for the life of this Sedona PLC object (unless Enable is false).
//
while((myPLC->ThreadCtrl != SIGNAL_THREAD_KILL) && (myPLC->ThreadCtrl != SIGNAL_THREAD_RESET))
{

}



CleanExit: //--------------------------------------------------------------------------------------------------------

close(fd);
free(outBuff);
free(inBuff);
free(myPLC);
pthread_exit(NULL);
}

这是启动时的打印示例:

NATIVE-PLCThread:IOM_Addr_search recvfrom() 在 eth0(索引 2)MAC 00:1e:c9:7d:c4:36 rcv_tm_out.tv_sec = 0.49997 上返回 104 个字节

来自陌生人的 MAC 地址!!!

NATIVE-PLCThread:IOM_Addr_search recvfrom() 在 enp1s0(索引 3)MAC 00:1e:c9:7d:c4:36 rcv_tm_out.tv_sec = 0.49998 上返回 152 个字节

来自陌生人的 MAC 地址!!!

NATIVE-PLCThread:搜索所有接口(interface)后未找到 IOM_MAC_search MAC 地址!!!


我应该在 eth0 上看到“select() 超时”,因为以太类型 0x88b5 没有任何响应。

最佳答案

我想我看到了问题。

我用 ETH_P_ALL 创建了套接字。我假设我可以像文档所说的那样在绑定(bind)中更具体。

到目前为止的初步测试没有重现这个问题。

我看到很多消息来源说可以做我最初做的事情...所以这可能是 Linux 或驱动程序中的错误?

关于c - AF_PACKET 套接字上的 Bind() 首先失败......然后自行修复?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46551158/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com