6

I have a linux system with two physical interfaces. I need to intercept (read) incoming packets over one interface, read (or process) the data and send it out over the other interface as it is - just like a middleman. I am able to extract all the header fields and payload data from the packets but I am not able to put it back on the wire again. How do I send the packet on its way through the other interface?

// All #includes 

struct sockaddr_in source,dest;
int i,j,k;
int main()
{
    int saddr_size , data_size;
    struct sockaddr_in saddr;
    unsigned char *buffer=malloc(65535);


    int sock_raw = socket( AF_PACKET , SOCK_RAW , htons(ETH_P_ALL)) ;

     if(sock_raw < 0)
        perror("setsockopt");

    setsockopt(sock_raw , SOL_SOCKET , SO_BINDTODEVICE , "eth0" , strlen("eth0")+ 1 );

    if(sock_raw < 0)
    {
        perror("Socket Error");
        return 1;
    }

    while(1)
    {
        saddr_size = sizeof (struct sockaddr);
        //Receive a packet
        data_size = recvfrom(sock_raw , buffer , 65536 , 0 ,(struct sockaddr *) &saddr , (socklen_t*)&saddr_size);

        if(data_size <0 )
        {
            printf("Recvfrom error , failed to get packets\n");
            return 1;
        }
        else{
        printf("Received %d bytes\n",data_size);

        //Huge code to process the packet

        //Send it out through "eth1" here 

        }
    }
    close(sock_raw);
    return 0;
}

Just assume only UDP or ICMP packets if it makes it easier to explain (using a simple "sendto" function maybe)- I can handle the sorting. Do not worry about the intended destination, I only want to put the packets back on the wire - delivery is not important.

Edit 1:

If I do this it gives me a runtime error saying "Invalid argument". It doesn't matter if I'm sending the buffer or even "Hello World".

bytes_sent=sendto(sock_raw, buffer, 65536, 0,(struct sockaddr *) &saddr ,saddr_size);
if (bytes_sent < 0) {
    perror("sendto");
    exit(1);
    }

Edit 2 : Let me make it simpler- I have two pipes A and B. Balls roll in from A and I receive them. I just want to put them in pipe B and send them on their way. Ethernet bridges work in a similar way - just sending all packets over all interfaces involved. I would have definitely used a bridge if I didn't have to get some basic information from the packet headers. And I'm not good at modifying the kernel bridge drivers.

Edit 3 : I'll try one last time with a different question. If I have received a complete raw packet with source/destination addresses included in the headers and all, how do I simply send it ANYWHERE (i don't care where) using sendto ? Should I add any information to the "struct sockaddr" in the sendto call , or can I simply use the same one I did in the recvfrom call ?

Community
  • 1
  • 1
anon_16
  • 329
  • 2
  • 3
  • 11
  • `I only want to put the packets back on the wire - delivery is not important...`... what is that suppose to mean , BTW? – Sourav Ghosh Nov 13 '14 at 12:01
  • I just want to send the packets out, I don't care whether they reach their destination or not. Using wireshark I can check whether they have been sent out. – anon_16 Nov 13 '14 at 12:06
  • if you are able to process the recived data over raw interface, put the data into a seperate buffer and `sendto()` the buffer to the other destination address. [Assuming two i/fs are in diff n/w..]. – Sourav Ghosh Nov 13 '14 at 12:10
  • why _just like a middleman_ sending needs to be on `raw` socket? same destination address is required[not clear from your question]? – Sourav Ghosh Nov 13 '14 at 12:30
  • Even if I open a fresh socket with the destination address that I extract from the IP header, I still get the "Invalid Argument" error. It doesn't have to be a raw socket - you can suggest any changes that seem right. – anon_16 Nov 13 '14 at 12:41
  • I suppose you could simply activate bridging/routing between the two interfaces and then make the middleman just read. To make this work it can be not as simple as reprinting the packet to another interface because of sub-IP protocols like ARP that is responsible for the managing the data link layer. – Havenard Nov 15 '14 at 02:16

2 Answers2

8

I finally got it to work ! I was able to send packets received on eth0 to eth1 unchanged.

Corrections:

  1. "write" instead of "sendto" , although it isn't advisable.
  2. A fresh socket for sending only.
  3. It seems the SO_BINDTODEVICE using setsockopt doesn't work properly so I had to use a bind function to bind the socket to the interface.
  4. struct sockaddr_in to struct sockaddr_ll

I put this together in a hurry so there may be some redundant lines in there. I don't know if daddr is even neccessry but it works so there it is. Here's the full code :

// All #includes 

int main()
{
    int saddr_size , data_size, daddr_size, bytes_sent;
    struct sockaddr_ll saddr, daddr;
    unsigned char *buffer=malloc(65535);

    int sock_raw = socket( AF_PACKET , SOCK_RAW , htons(ETH_P_ALL)) ; //For receiving
    int sock = socket( PF_PACKET , SOCK_RAW , IPPROTO_RAW) ;            //For sending

    memset(&saddr, 0, sizeof(struct sockaddr_ll));
    saddr.sll_family = AF_PACKET;
    saddr.sll_protocol = htons(ETH_P_ALL);
    saddr.sll_ifindex = if_nametoindex("eth0");
    if (bind(sock_raw, (struct sockaddr*) &saddr, sizeof(saddr)) < 0) {
        perror("bind failed\n");
        close(sock_raw);
    }

    memset(&daddr, 0, sizeof(struct sockaddr_ll));
    daddr.sll_family = AF_PACKET;
    daddr.sll_protocol = htons(ETH_P_ALL);
    daddr.sll_ifindex = if_nametoindex("eth1");
    if (bind(sock, (struct sockaddr*) &daddr, sizeof(daddr)) < 0) {
      perror("bind failed\n");
      close(sock);
    }
    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth1");
    if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
        perror("bind to eth1");
        }

    while(1)
    {
        saddr_size = sizeof (struct sockaddr);
        daddr_size = sizeof (struct sockaddr);
        //Receive a packet
        data_size = recvfrom(sock_raw , buffer , 65536 , 0 ,(struct sockaddr *) &saddr , (socklen_t*)&saddr_size);

        if(data_size <0 )
        {
            printf("Recvfrom error , failed to get packets\n");
            return 1;
        }
        else{
        printf("Received %d bytes\n",data_size);

        //Huge code to process the packet (optional)

        //Send the same packet out
        bytes_sent=write(sock,buffer,data_size);
        printf("Sent %d bytes\n",bytes_sent);
         if (bytes_sent < 0) {
            perror("sendto");
            exit(1);
         }

        }
    }
    close(sock_raw);
    return 0;
}

Thanks to all those who responded.

anon_16
  • 329
  • 2
  • 3
  • 11
  • can you add the //Huge code to process the packet (optional) I am trying to do the same thing but didn't know how to analyze and edit the packet. – Mohd Alomar May 05 '18 at 20:17
0

Regarding edit 2: If setting up a bridge device is an option, why can't you just use a sniffing tool (tcpdump, wireshark, your own creation based on libpcap) on the bridge device and not have to care about forwarding the packets yourself?

CRABOLO
  • 8,605
  • 39
  • 41
  • 68
ua2b
  • 182
  • 3
  • Because at a later point I want to selectively forward only those packets that I choose in my program. The bridge would forward all packets indiscriminately. Right now I just want to CATCH and then THROW the packets out. – anon_16 Nov 15 '14 at 05:00