【问题标题】:How to ping localhost using raw socket?如何使用原始套接字 ping localhost?
【发布时间】:2013-10-17 11:40:15
【问题描述】:

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

【问题讨论】:

  • 我相信,您需要 root 权限才能使用原始套接字。尝试 sudo'ing 你的程序。
  • 感谢您的回答,但是我在目标机器上使用了archlinux的root帐户,在这种情况下应该没有任何权限问题。
  • 如果我理解你是正确的,你改变了数据包头中的IP。如果是这样,您需要重新计算新数据包的校验和。如果数据包的校验和不正确,目标机器可以直接丢弃它。
  • 感谢您的回答,但我知道。我重新计算校验和,它在 tcpdump 中显示为正确。此外,根据 tcpdump,对于我的和标准的 ping 数据包,所有以太网和 ip 级别的标头(包括校验和)都是相同的。所以我认为问题在于 sendto(),而不是外部流量内容。
  • 你找出问题所在了吗?

标签: c linux sockets raw-sockets loopback


【解决方案1】:

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

我认为struct sockaddr_ll.sll_protocolSendTo() 中设置为 ETH_P_ALL 可能会弄乱您的数据包。使用错误的协议可以解释为什么它们没有传递给适当的协议处理程序。

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

男人 7 包:

当你发送数据包时,指定 sll_family 就足够了, sll_addr、sll_halen、sll_ifindex。其他字段应为 0。 sll_hatype 和 sll_pkttype 在收到的数据包上为您设置 信息。对于绑定只有 sll_protocol 和 sll_ifindex 用过。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-10
    • 2010-09-16
    • 1970-01-01
    • 2016-11-13
    • 2011-09-22
    相关资源
    最近更新 更多