该程序从某个来源获得了杂项以太网流量,更改 ip 并将其重定向到 localhost(例如,它应该用于 ssh 连接)并从 localhost 发送回答案。我使用了以下代码:
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);
}
main() 中的某处:
ifindex_lo = _create_input_socket(&socket_loopback, "lo");
...
SendTo(socket_loopback, ifindex_lo, buffer, size);
当我通过程序发送 ping 时,我在环回接口上使用了 tcpdump 来检查它是如何工作的:
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
当我从 linux 命令执行真正的 ping 操作时:
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
我检查了数据包——除了 icmp 序列号外,它看起来都一样。其他所有流量也是同样的情况:系统不回复我的程序产生的流量,而是回复其他来源产生的流量。我对环回接口上 sendto 的这种行为感到困惑。任何人都知道如何避免这种情况?