2013-10-17 41 views
0

程序從某些來源獲得misc以太網流量,更改ip並將其重定向到本地主機(例如,它應該用於ssh連接)並從本地主機發回答案。我用下面的代碼:如何使用原始套接字ping本地主機?

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); 

我使用環回接口上的tcpdump檢查它是如何工作的,當我通過程序發送Ping:

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序列號相同。所有其他流量也是如此:系統不會回覆我的程序生成的流量,但會根據其他來源生成的流量進行回覆。我對loopback接口上sendto的這種行爲感到困惑。任何人都知道如何避免這一點?

+0

我相信,你需要root權限才能使用原始套接字。嘗試sudo'ing您的程序。 – KBart

+0

感謝您的回答,但我在目標機器上使用了archlinux的root帳戶,在這種情況下,應該沒有任何priveleges問題。 – user2890160

+0

如果我理解你是正確的,你可以在數據包頭中更改IP。如果是這樣,您需要重新計算新數據包的校驗和。如果數據包的校驗和不正確,目標機器可以放棄它。 – fycth

回答

0

嘗試檢查完整的數據包,包括以太網報頭(-e到tcpdump)並確保它們是正確的。

我認爲struct sockaddr_ll.sll_protocolSendTo()設置爲ETH_P_ALL可能會搞亂你的數據包。使用錯誤的協議會解釋爲什麼它們沒有交付給適當的協議處理程序。

嘗試從SendTo()中刪除除struct sockaddr_ll.sll_familystruct sockaddr_ll.sll_ifindex之外的所有內容。如果不起作用,請手動將.sll_protocol設置爲您發送的類型。這個手冊頁可能被解釋爲說sll_protocol在套接字是SOCK_RAW時沒有影響,但我認爲它可能。

人7包:

當你發送的數據包就足以說明sll_family, sll_addr,sll_halen,sll_ifindex。其他字段應該爲0. sll_hatype和sll_pkttype在接收到的數據包上爲您的 信息設置。對於綁定,只有sll_protocol和sll_ifindex使用 。

你可以發佈你正在使用的所有代碼,但它已經很長了。最好是,如果你把一個很短的程序放在一起,表現出相同的症狀併發布。

相關問題