I have two identical network devices. Both are hardwired to the IP 192.168.137.128
. Both devices receive UDP packets sent to their IP and respond with UDP broadcast packets sent to 192.168.137.255
. I also have a Linux (Debian) machine with 3 NICs, where eth0
is configured to have IP 192.168.137.1
and connected to device 1, eth2
is at 192.168.137.17
m and eth1
connects to the world. The two identical subnets are not connected.
So the problem is to
- chose the correct NIC to send outgoing packets to
192.168.137.128
depending on the sender IP - respond to broadcast packets
The best I've managed to do is use SO_BINDTODEVICE
, which requires me to know the actual NIC device name and more importantly requires me to run the programme as root.
So apart from this issue, the following programme does what I want:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <time.h>
#define sockopt_t int
#define OUTPORT 51001
#define INPORT 51002
int main(int argc, char *argv[])
{
int sockfd[2];
unsigned char buf[1666];
struct sockaddr_in sendaddr[2];
struct sockaddr_in recvaddr[2];
int numbytes;
socklen_t addr_len;
int broadcast=1;
int ret;
const char * ip_addrs[] = {"192.168.137.255", "192.168.137.255"};
const char * devs[] = {"eth0", "eth2"};
size_t i;
for (i=0; i < 2; ++i) {
if ((sockfd[i] = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket");
exit(1);
}
if ((setsockopt(sockfd[i], SOL_SOCKET, SO_BROADCAST,
&broadcast, sizeof broadcast)) == -1) {
perror("setsockopt - SO_SOCKET ");
exit(1);
}
printf("Socket %lu created\n", i);
// /*
ret = setsockopt(sockfd[i], SOL_SOCKET, SO_BINDTODEVICE, devs[i], sizeof(devs[i]));
printf("setsockopt %lu returned %i\n", i, ret);
if (ret==-1) {
perror("socket");
}
//*/
memset(&sendaddr[i], 0, sizeof sendaddr[i]);
sendaddr[i].sin_family = AF_INET;
sendaddr[i].sin_port = htons(OUTPORT);
sendaddr[i].sin_addr.s_addr = inet_addr("192.168.137.128");
memset(&recvaddr[i], 0, sizeof recvaddr[i]);
recvaddr[i].sin_family = AF_INET;
recvaddr[i].sin_port = htons(INPORT);
recvaddr[i].sin_addr.s_addr = inet_addr(ip_addrs[i]);
if (bind(sockfd[i], (struct sockaddr*)&recvaddr[i], sizeof recvaddr[i]) == -1) {
perror("bind");
exit(1);
}
}
close(sockfd[0]);
close(sockfd[1]);
exit(0);
}
Note that if I insert the actual NIC IPs in ip_addrs
, outgoing packets will be correctly routed, but the socket will not receive the broadcast packets.
A proposed solution was to put a router between one device and the PC's NIC, and use NAT. This would, however, introduce latency which is not desirable here.