The program got misc ethernet traffic from some source, change ip and should redirect it to localhost(for example, it should be used for ssh connection) and send answers from localhost back. I used following code:
int bind_if(int raw , char *device , int protocol) {
struct sockaddr_ll sll;
struct ifreq ifr; bzero(&sll , sizeof(sll));
bzero(&ifr , sizeof(ifr));
strncpy((char *)ifr.ifr_name ,device , IFNAMSIZ);
//copy device name to ifr
if((ioctl(raw , SIOCGIFINDEX , &ifr)) == -1)
{
perror("Unable to find interface index");
return -1;
}
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_protocol = htons(protocol);
if((bind(raw , (struct sockaddr *)&sll , sizeof(sll))) ==-1)
{
perror("bind: ");
return -1;
}
return 0;
}
int _create_input_socket(int *s, char* interface)
{
struct packet_mreq mreq;
struct ifreq if_idx;
int sockopt;
int ifnumber = 0;
if ((*s = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 1)
{
perror("cannot create socket");
return -1;
}
memset(&if_idx, 0, sizeof(struct ifreq));
strncpy(if_idx.ifr_name, interface, IFNAMSIZ-1);
if (ioctl(*s, SIOCGIFINDEX, &if_idx) < 0)
{
perror("SIOCGIFINDEX for interface failed");
close(*s);
return -1;
}
ifnumber = if_idx.ifr_ifindex;
/* Get the current flags that the device might have */
if (ioctl(*s, SIOCGIFFLAGS, &if_idx) <0)
{
perror("SIOCGIFFLAGS failed");
close(*s);
return -1;
}
/* Set the old flags plus the IFF_PROMISC flag */
if_idx.ifr_flags |= IFF_PROMISC;
if (ioctl(*s, SIOCSIFFLAGS, &if_idx) == -1)
{
perror("SIOCSIFFLAGS while adding IFF_PROMISC failed");
close(*s);
}
int flags = fcntl(*s, F_GETFL, 0);
int ret = fcntl(*s, F_SETFL, flags | O_NONBLOCK);
printf("ret = %d\n", ret);
if(ret < 0)
{
perror("fcntl for O_NONBLOCK failed");
close(*s);
return -1;
}
if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)) == -1) {
perror("setsockopt SO_REUSEADDR failed");
close(*s);
return -1;
}
if( bind_if(*s, interface, ETH_P_ALL) == -1 )
{
close(*s);
return -1;
}
if (setsockopt(*s, SOL_SOCKET, SO_BINDTODEVICE, interface, IFNAMSIZ-1) == -1) {
perror("SO_BINDTODEVICE");
close(*s);
return -1;
}
memset(&mreq,0,sizeof(mreq));
mreq.mr_ifindex = ifnumber;
mreq.mr_type = PACKET_MR_PROMISC;
mreq.mr_alen = 6;
if (setsockopt(*s,SOL_PACKET,PACKET_ADD_MEMBERSHIP,
(void*)&mreq,(socklen_t)sizeof(mreq)) < 0)
perror("setsockopt(PACKET_ADD_MEMBERSHIP)");
printf("create socket %d for iface %s(%d)\n", *s, interface, ifnumber);
return ifnumber;
}
void SendTo(int sock, int ifindex, const unsigned char*buffer, int buffer_size)
{
struct sockaddr_ll socket_address;
memset(&socket_address, 0, sizeof(socket_address));
socket_address.sll_ifindex = ifindex;
socket_address.sll_halen = ETH_ALEN;
socket_address.sll_family = htons(PF_PACKET);
socket_address.sll_protocol = htons(ETH_P_ALL);
socket_address.sll_hatype = ARPHRD_ETHER;
memcpy(socket_address.sll_addr, buffer, 6);
if (sendto(sock, buffer, buffer_size, 0, (struct sockaddr*)&socket_address, sizeof(socket_address)) < 0)
printf("Send failed due error %d\n", errno);
}
Somewhere in main():
ifindex_lo = _create_input_socket(&socket_loopback, "lo");
...
SendTo(socket_loopback, ifindex_lo, buffer, size);
I used tcpdump on loopback interface to check how it works, when I send ping through program:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes
00:10:24.269431 IP localhost.localdomain > localhost.localdomain: ICMP echo request, id 29046, seq 1, length 64
00:10:25.269125 IP localhost.localdomain > localhost.localdomain: ICMP echo request, id 29046, seq 2, length 64
And when I perform real ping from linux command:
00:43:49.228192 IP localhost.localdomain > localhost.localdomain: ICMP echo request, id 535, seq 1, length 64
00:43:49.228219 IP localhost.localdomain > localhost.localdomain: ICMP echo reply, id 535, seq 1, length 64
00:43:50.227183 IP localhost.localdomain > localhost.localdomain: ICMP echo request, id 535, seq 2, length 64
00:43:50.227203 IP localhost.localdomain > localhost.localdomain: ICMP echo reply, id 535, seq 2, length 64
I checked packets - its looks the same except icmp sequence numbers. The same situation is with all other traffic: system does not reply to traffic generated by my program, but make reply by traffic generated by other sources. I am confused for such behavior of sendto on loopback interface. Anyone have idea how avoid that?