0

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?

  • I believe, you need root privileges to use raw sockets. Try sudo'ing your program. – KBart Oct 17 '13 at 12:10
  • Thanks for answer, but I used root account for archlinux on target machine, there shouldn't been any problems with priveleges in that case. – user2890160 Oct 17 '13 at 12:16
  • If I understand you correct, you change IP in the packet header. If so, you need to recalculate checksum for the new packet. If checksum of a packet is incorrect, target machine can just drop it. – fycth Oct 17 '13 at 13:24
  • Thanks for answer, but I know it. I recalculate checksum and it is shown as correct in tcpdump. Furthermore, according tcpdump, all ethernet and ip level headers including checksums are the same for my and standard ping packets. So I supposed that problem is about sendto(), not external traffic content. – user2890160 Oct 17 '13 at 13:45
  • Did you figure out what was problem ? – saeed hardan Nov 04 '19 at 13:10

1 Answers1

0

Try examining the complete packets, including ethernet header ( -e to tcpdump) and make sure those are correct.

I think struct sockaddr_ll.sll_protocol set to ETH_P_ALL in SendTo()might be messing up your packets. Having the wrong protocol would explain why they are not delivered to the appropriate protocol handler.

Try removing everything except the struct sockaddr_ll.sll_family and struct sockaddr_ll.sll_ifindex from the SendTo(). In case that does not work, manually set .sll_protocol as the type you are sending out. The man-page could be interpreted as saying that sll_protocol has no influence when the socket is SOCK_RAW but I think it might.

man 7 packet:

When you send packets it is enough to specify sll_family, sll_addr, sll_halen, sll_ifindex. The other fields should be 0. sll_hatype and sll_pkttype are set on received packets for your information. For bind only sll_protocol and sll_ifindex are used.

You could post all of the code you are using, but it is already rather long. It would be best, if you put together a very short program, that exhibits the same symptoms and post that.

thuovila
  • 1,960
  • 13
  • 21