-1

Question:

How to I create a two-threaded bridge in code (not using brctl addbr br0 or some similar networking method, but writing the code) with one that takes Ethernet frames from ETH1 and puts them on ETH2, while the other takes them from ETH2 and puts them on ETH1 in a way that the two processes don't continuously read each other's Ethernet Frames?

Details:

I am trying to create a device that secures a connection between to systems (for a quite a few legacy systems that we cannot update) and in order to do that, we need to stick this device on the Ethernet port of the legacy system with another Ethernet port that is network facing. Basically a hardware man-in-the-middle.

I can do almost everything I need with a standard Ethernet bridge and some fancy IPTables pre and post routing rules, but I wanted to try to implement some code that did this. I have tried this in C and in Python, and I just end up creating an infinite loop of Ethernet frames. For example in C, I can open a socket to ETH1 for reading in promiscuous mode:

    int open_eth_read_socket(char *interface_name) {
        int socket_fd;
        struct ifreq if_options;
        struct timeval timeout;
        timeout.tv_sec = 10;
        timeout.tv_usec = 0;
        // Get socket
        socket_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
        if(socket_fd < 0) exit(EXIT_ERROR);
        // Zero ifreq
        memset(&if_options, 0, sizeof(struct ifreq));
        // Put interface_name in
        strcpy(if_options.ifr_name, interface_name);
        // Set timout
        int ret = setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO,
                       (char *)&timeout,sizeof(timeout));
        if(ret < 0) exit(EXIT_ERROR);
        // Bind this socket to the specified interface.
        ret = setsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE,
                   interface_name, strlen(interface_name));
        if(ret < 0) exit(EXIT_ERROR);
        // Get flags
        if(ioctl(socket_fd, SIOCGIFFLAGS, &ifopts) < 0) exit(EXIT_ERROR);
        // Add promisc flag
        ifopts.ifr_flags |= IFF_PROMISC;
        // Set flags
        if(ioctl(socket_fd, SIOCSIFFLAGS, &ifopts) < 0) exit(EXIT_ERROR);
        return socket_fd;
    }

I then open a socket to ETH2 for writing, and I can have program logic in between. The ETH1->ETH2 works perfectly as does the ETH2->ETH1 so long as both threads (pthreads in C, threading.Thread in Python) are not running at the same time. If they are, so that the process acts like a bridge, all hell breaks lose.

The ETH1->ETH2 thread reads the Ethernet frames from ETH1 and writes them to ETH2. When the ETH2->ETH1 thread goes to read from the ETH2 Ethernet socket, it reads the frame the the other thread wrote, and an infinite loop begins.

The Python version looks similar, but I've tried using pcapy and dpkt, but neither works. I came across a Scapy bridge question that seems to have experienced a similar issue, but the answers there don't seem to explain why this is happening or really how to fix it, and the code that Bogdan Paun is nice, but glosses over how to fix this if one wanted to learn.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Gabe
  • 131
  • 1
  • 13

1 Answers1

0

For those who are interested, you need to check if the Ethernet frame is of type socket.PACKET_OUTGOING. In Python:

frame, inf = sock.recvfrom(constants.MAX_SOCKET_READ_BYTES)
if inf[2] == socket.PACKET_OUTGOING:
    continue # Or whatever to skip

In c (From Raw Socket promiscuous mode not sniffing what I write):

unsigned char buf[1500];
struct sockaddr_ll addr;
socklen_t addr_len = sizeof(addr);
n = recvfrom(fd, buf, 2000, 0, (struct sockaddr*)&addr, &addr_len);
if (addr.sll_pkttype == PACKET_OUTGOING)  {
    // Skip it...
}

Reading outgoing Ethernet frames (or IP packets for that matter) does not take them off the stack, so discarding / ignoring them does not affect outgoing traffic.

Gabe
  • 131
  • 1
  • 13