0

该程序从某个来源获得了杂项以太网流量,更改 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 的这种行为感到困惑。任何人都知道如何避免这种情况?

4

1 回答 1

0

尝试检查完整的数据包,包括以太网标头(-e 到 tcpdump)并确保它们是正确的。

我认为struct sockaddr_ll.sll_protocol设置为 ETH_P_ALLSendTo()可能会弄乱你的数据包。拥有错误的协议可以解释为什么它们没有被传递给适当的协议处理程序。

尝试从. struct sockaddr_ll.sll_family_ 如果这不起作用,请手动将 .sll_protocol 设置为您要发送的类型。手册页可以解释为当套接字为 SOCK_RAW 时 sll_protocol 没有影响,但我认为它可能。struct sockaddr_ll.sll_ifindexSendTo()

男人7包:

当您发送数据包时,指定 sll_family、sll_addr、sll_halen、sll_ifindex 就足够了。其他字段应为 0。 sll_hatype 和 sll_pkttype 在收到的数据包上设置以供您参考。对于绑定,仅使用 sll_protocol 和 sll_ifindex。

您可以发布您正在使用的所有代码,但它已经相当长了。如果您编写一个非常短的程序,展示相同的症状并发布,那将是最好的。

于 2013-10-18T09:49:45.990 回答