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 ?
I finally got it to work ! I was able to send packets received on eth0 to eth1 unchanged.
Corrections:
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.