I am trying to communicate with an external device using unicast UDP.
SETUP: My machine and the external device are both running versions of Linux. The external device is connected to NIC#2 in my machine, or eno2. The external device is sending unicast packets to port 25000 and IP of my eno2 NIC 192.24.38.1. I have also updated the ARP table on the remote device to add my machines IP and HW address.
WHAT I HAVE DONE: Using the simple code below, I never receive any data.
int socket_desc;
struct ifreq ifr;
struct sockaddr_in server_addr, client_addr;
char server_message[2000], client_message[2000];
int client_struct_length = sizeof(client_addr);
// Clean buffers:
memset(server_message, '\0', sizeof(server_message));
memset(client_message, '\0', sizeof(client_message));
// Create UDP socket:
socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (socket_desc < 0) {
printf("Error while creating socket\n");
return -1;
}
printf("Socket created successfully\n");
if (setsockopt(socket_desc, SOL_SOCKET, SO_BINDTODEVICE, "eno2", strlen("eno2") + 1) < 0)
{
std::cout << "Error setting SO_BINDTODEVICE" << std::endl;
exit(-1);
}
// Set port and IP:
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(25000);
server_addr.sin_addr.s_addr = inet_addr("192.24.38.1");
// Bind to the set port and IP:
if (bind(socket_desc, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
printf("Couldn't bind to the port\n");
return -1;
}
printf("Done with binding\n");
printf("Listening for incoming messages...\n\n");
// Receive client's message:
if (recvfrom(socket_desc, client_message, sizeof(client_message), 0,
(struct sockaddr*)&client_addr, (socklen_t*)&client_struct_length) < 0) {
printf("Couldn't receive\n");
return -1;
}
printf("Received message from IP: %s and port: %i\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
printf("Msg from client: %s\n", client_message);
THE ISSUE: The only way I can see these packets is if I put the NIC in promiscuous mode and with the following code:
struct ifreq ifr;
struct sockaddr_in servaddr;
int saddr_size, data_size;
struct sockaddr saddr;
thisSocket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (thisSocket < 0)
{
//Print the error with proper message
perror("Socket Error");
}
if (setsockopt(thisSocket, SOL_SOCKET, SO_BINDTODEVICE, "eno2", strlen("eno2") + 1) < 0)
{
std::cout << "Error setting SO_BINDTODEVICE" << std::endl;
exit(-1);
}
memset(&ifr, 0, sizeof(ifr));
strncpy((char*)ifr.ifr_name, "eno2", IFNAMSIZ);
if ((ioctl(thisSocket, SIOCGIFHWADDR, &ifr)) == -1)
{
std::cout << "Error getting Interface hw address!" << std::endl;
exit(-1);
}
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 18000;
saddr_size = sizeof saddr;
while (1)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(thisSocket, &fds);
int status = select(thisSocket + 1, &fds, NULL, NULL, &timeout);
if (status > 0) {
//Receive a packet
data_size = recvfrom(thisSocket, &RX_Buffer[0], RX_Buffer.size(), 0, &saddr, (socklen_t*)&saddr_size);
if (data_size < 0)
{
std::cout << "Recvfrom error , failed to get packets" << std::endl;
}
else {
//Now process the packet
ProcessPacket(RX_Buffer, data_size);
}
}
}
This is not ideal because I have to ‘sniff’ all traffic and parse the raw packet IP header to look for the packets I want. There is a lot of multicast traffic that I do not care about. I would like to bind to an address so I only receive packets that are intended for my machine. What am I doing wrong?
Local machine
~# ifconfig
eno2 Link encap:Ethernet HWaddr 00:72:2F:30:6E:C9
inet addr:192.24.38.1 Bcast:192.24.255.255 Mask:255.255.0.0
inet6 addr: fe80::280:2fff:fe30:6ec4/64 Scope:Link
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1
Remote Machine
~# ifconfig
eth0 Link encap:Ethernet HWaddr 00:28:12:00:7A:C9
inet addr:192.24.34.1 Bcast:0.0.0.0 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
– m.s. May 01 '23 at 18:37