0

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.17m 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.

zeeMonkeez
  • 5,057
  • 3
  • 33
  • 56
  • @Barmar can you please explain how this is a duplicate? I mentioned `SO_BINDTODEVICE` and that it's undesirable (because of privileges). Note that because I need to respond to bcast packets I cannot differentiate by IP. – zeeMonkeez Mar 16 '17 at 22:46
  • If you can't use `SO_BINDTODEVICE` then I don't think you can do what you want. – Barmar Mar 16 '17 at 22:51
  • 1
    Why do you have two NICs on the same subnet that aren't actually connected to the same network? It seems like a broken configuration. – Barmar Mar 16 '17 at 22:51
  • @Barmar it's two identical data acquisition devices whose network configuration is set in firmware. It is broken indeed. Ironically, it works under Windows, as binding the sockets to the actual IPs receives bcast packets. – zeeMonkeez Mar 16 '17 at 22:54
  • In case anyone else runs into the issue of having hardcoded identical network devices to connect to one Linux box ... I used [`iptables` and `ip route` to modify outgoing packets ensuring they go to the correct NIC](http://serverfault.com/a/389004/360987) and [`tc` for stateless NAT for incoming packets](http://unix.stackexchange.com/a/281800/221798). The complete solution for this setup is at [the CereLink project on GitHub](https://github.com/dashesy/CereLink/issues/73#issuecomment-287924332) – zeeMonkeez Mar 21 '17 at 02:05

0 Answers0