2013-05-14 23 views
2
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <errno.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/ioctl.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <linux/if_ether.h> 
#include <net/if.h> 
#include <netpacket/packet.h> 

struct ethernet { 
    unsigned char dest[6]; 
    unsigned char source[6]; 
    uint16_t eth_type; 
}; 

struct arp { 
    uint16_t htype; 
    uint16_t ptype; 
    unsigned char hlen; 
    unsigned char plen; 
    uint16_t oper; 
    /* addresses */ 
    unsigned char sender_ha[6]; 
    unsigned char sender_pa[4]; 
    unsigned char target_ha[6]; 
    unsigned char target_pa[4]; 
}; 

#define ETH_HDR_LEN 14 
#define BUFF_SIZE 2048 

#define ARP_PROTO 0x0806 

static void dump_arp(struct arp *arp_hdr); 

int main(void) 
{ 
    int sock, err; 
    void *buffer = NULL; 
    ssize_t recvd_size; 
    struct sockaddr_ll s_ll; 
    struct ethernet *eth_hdr = NULL; 
    struct arp *arp_hdr = NULL; 

    if((sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP))) == -1) 
    // if((sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) 
    { 
     perror("socket(): "); 
     exit(-1); 
    } 
    s_ll.sll_family = AF_PACKET; 
    s_ll.sll_protocol = htons(ETH_P_ARP); 
    //s_ll.sll_protocol = htons(ETH_P_ARP); 
    s_ll.sll_ifindex = 0; // all ifaces 
    //s_ll.sll_ifindex = 2 // eth0 
    if((err = bind(sock, (struct sockaddr *)&s_ll, sizeof(s_ll))) == -1) 
    { 
     perror("bind(): "); 
     exit(-1); 
    } 

    buffer = malloc(BUFF_SIZE); 
    while(1) 
    { 
     if((recvd_size = recv(sock, buffer, BUFF_SIZE, 0)) == -1) 
     { 
      perror("recv(): "); 
      free(buffer); 
      close(sock); 
      exit(-1); 
     } 
     if(recvd_size <= (sizeof(struct ethernet) + sizeof(struct arp))) 
     { 
      printf("Short packet. Packet len: %d\n", recvd_size); 
      continue; 
     } 
     eth_hdr = (struct ethernet *)buffer; 
     if(ntohs(eth_hdr->eth_type) != ARP_PROTO) 
      continue; 
     arp_hdr = (struct arp *)(buffer+ETH_HDR_LEN); 
     dump_arp(arp_hdr); 
    } 
    free(buffer); 
    close(sock); 
} 

static void 
dump_arp(struct arp *arp_hdr) 
{ 
    uint16_t htype = ntohs(arp_hdr->htype); 
    uint16_t ptype = ntohs(arp_hdr->ptype); 
    uint16_t oper = ntohs(arp_hdr->oper); 
    switch(htype) 
    { 
     case 0x0001: 
      printf("ARP HTYPE: Ethernet(0x%04X)\n", htype); 
      break; 
     default: 
      printf("ARP HYPE: 0x%04X\n", htype); 
      break; 
    } 
    switch(ptype) 
    { 
     case 0x0800: 
      printf("ARP PTYPE: IPv4(0x%04X)\n", ptype); 
      break; 
     default: 
      printf("ARP PTYPE: 0x%04X\n", ptype); 
      break; 
    } 
    printf("ARP HLEN: %d\n", arp_hdr->hlen); 
    printf("ARP PLEN: %d\n", arp_hdr->plen); 
    switch(oper) 
    { 
     case 0x0001: 
      printf("ARP OPER: Request(0x%04X)\n", oper); 
      break; 
     case 0x0002: 
      printf("ARP OPER: Response(0x%04X)\n", oper); 
      break; 
     default: 
      printf("ARP OPER: 0x%04X\n", oper); 
      break; 
    } 
    printf("ARP Sender HA: %02X:%02X:%02X:%02X:%02X:%02X\n", 
      arp_hdr->sender_ha[0],arp_hdr->sender_ha[1],arp_hdr->sender_ha[2], 
      arp_hdr->sender_ha[3], arp_hdr->sender_ha[4], arp_hdr->sender_ha[5]); 
    printf("ARP Sender PA: %d.%d.%d.%d\n", arp_hdr->sender_pa[0], 
      arp_hdr->sender_pa[1], arp_hdr->sender_pa[2], arp_hdr->sender_pa[3]); 
    printf("ARP Target HA: %02X:%02X:%02X:%02X:%02X:%02X\n", 
      arp_hdr->target_ha[0],arp_hdr->target_ha[1],arp_hdr->target_ha[2], 
      arp_hdr->target_ha[3], arp_hdr->target_ha[4], arp_hdr->target_ha[5]); 
    printf("ARP Target PA: %d.%d.%d.%d\n", arp_hdr->target_pa[0], 
      arp_hdr->target_pa[1], arp_hdr->target_pa[2], arp_hdr->target_pa[3]); 
    printf("ARP DONE =====================\n"); 
} 

我使用AF_PACKET,SOCK_RAW,ETH_P_ARP args創建socket()。並將其綁定()在所有接口(無關緊要)與參數(AF_PACKET,ETH_P_ARP)。所以,所有的ARP數據包都必須通過這個套接字。丟失SOCK_RAW插座中的ARP數據包

我的主人:192.168.1.2遠程主機:192.168.1.7,主機不包含192.167.1.7的ARP記錄。

程序輸出,當我做平192.168.1.7:

 
... 
ARP OPER: Response(0x0002) 
ARP Sender HA: 50:67:F0:94:70:F5 
ARP Sender PA: 192.168.1.7 
ARP Target HA: 00:22:15:A2:D0:C5 
ARP Target PA: 192.168.1.2 
ARP DONE ===================== 
... 
ARP OPER: Request(0x0001) 
ARP Sender HA: 50:67:F0:94:70:F5 
ARP Sender PA: 192.168.1.7 
ARP Target HA: 00:00:00:00:00:00 
ARP Target PA: 192.168.1.2 
ARP DONE ===================== 

我的插座有4只2包的(my_host轉變請求和響應my_host轉變錯過)。 tcpdump -n -p -i eth0 arp顯示全部4個數據包。

如果我改變ETH_P_ARP在插座ETH_P_ALL()和bind(),那麼所有4個數據包的去插座(帶IP等)。

爲什麼?如何解決這個問題?

PS。請告訴我一些郵件列表的地址,我可以詢問這種行爲。

+1

請添加源問題。如果pastebin消失或收穫您的數據,這個問題就變得毫無用處。謝謝。 – sarnold 2013-05-14 19:03:58

+0

當你在使用前清理s_ll結構時,它會更好嗎?或者使其成爲靜態。 – 2013-05-14 20:30:06

+0

沒有。此外,man 7數據包說bind()只使用sll_protocol,sll_ifindex字段。 – user2380785 2013-05-15 09:30:50

回答

3

後期答案的種類,但我這一個有趣的實驗。也許一些Google員工/ duckduckgoer可以受益。

我的建議是使用ETH_P_ALL接收所有數據包,但隨後一個Linux插座濾網過濾了他們,讓應用程序只接收到請求的ARP報文。

這是我的代碼。的變化拉爾標記有CHANGE評論

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <errno.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/ioctl.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <linux/if_ether.h> 
#include <net/if.h> 
#include <netpacket/packet.h> 
#include <linux/filter.h> // CHANGE: include lsf 

struct ethernet { 
    unsigned char dest[6]; 
    unsigned char source[6]; 
    uint16_t eth_type; 
}; 

struct arp { 
    uint16_t htype; 
    uint16_t ptype; 
    unsigned char hlen; 
    unsigned char plen; 
    uint16_t oper; 
    /* addresses */ 
    unsigned char sender_ha[6]; 
    unsigned char sender_pa[4]; 
    unsigned char target_ha[6]; 
    unsigned char target_pa[4]; 
}; 

#define ETH_HDR_LEN 14 
#define BUFF_SIZE 2048 

/* CHANGE 
    Linux socket filters use the Berkeley packet filter syntax. 
    This was adapted from BSDs "man 4 bpf" example for RARP. 
*/ 
struct sock_filter arpfilter[] = { 
    BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), /* Skip 12 bytes */ 
    BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETH_P_ARP, 0, 1), /* if eth type != ARP 
                 skip next instr. */ 
    BPF_STMT(BPF_RET+BPF_K, sizeof(struct arp) + 
       sizeof(struct ethernet)), 
    BPF_STMT(BPF_RET+BPF_K, 0), /* Return, either the ARP packet or nil */ 
}; 

static void dump_arp(struct arp *arp_hdr); 

int main(void) 
{ 
    int sock; 
    void *buffer = NULL; 
    ssize_t recvd_size; 
    struct ethernet *eth_hdr = NULL; 
    struct arp *arp_hdr = NULL; 
    struct sock_filter *filter; 
    struct sock_fprog fprog; 

    if((sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) 
    { 
     perror("socket(): "); 
     exit(-1); 
    } 

    /* CHANGE prepare linux packet filter */ 
    if ((filter = malloc(sizeof(arpfilter))) == NULL) { 
     perror("malloc"); 
     close(sock); 
     exit(1); 
    } 
    memcpy(filter, &arpfilter, sizeof(arpfilter)); 
    fprog.filter = filter; 
    fprog.len = sizeof(arpfilter)/sizeof(struct sock_filter); 

    /* CHANGE add filter */ 
    if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) == -1) { 
     perror("setsockopt"); 
     close(sock); 
     exit(1); 
    } 

    buffer = malloc(BUFF_SIZE); 
    while(1) 
    { 
     if((recvd_size = recv(sock, buffer, BUFF_SIZE, 0)) < 0) 
     { 
      perror("recv(): "); 
      free(buffer); 
      close(sock); 
      exit(-1); 
     } 
     if((size_t)recvd_size < (sizeof(struct ethernet) + sizeof(struct arp))) 
     { 
      printf("Short packet. Packet len: %ld\n", recvd_size); 
      continue; 
     } 
     eth_hdr = (struct ethernet *)buffer; 
     if(ntohs(eth_hdr->eth_type) != ETH_P_ARP) { 
      printf("Received wrong ethernet type: %X\n", eth_hdr->eth_type); 
      exit(1); 
     }   
     arp_hdr = (struct arp *)(buffer+ETH_HDR_LEN); 
     dump_arp(arp_hdr); 
    } 
    free(buffer); 
    close(sock); 
} 

static void 
dump_arp(struct arp *arp_hdr) 
{ 
    uint16_t htype = ntohs(arp_hdr->htype); 
    uint16_t ptype = ntohs(arp_hdr->ptype); 
    uint16_t oper = ntohs(arp_hdr->oper); 
    switch(htype) 
    { 
     case 0x0001: 
      printf("ARP HTYPE: Ethernet(0x%04X)\n", htype); 
      break; 
     default: 
      printf("ARP HYPE: 0x%04X\n", htype); 
      break; 
    } 
    switch(ptype) 
    { 
     case 0x0800: 
      printf("ARP PTYPE: IPv4(0x%04X)\n", ptype); 
      break; 
     default: 
      printf("ARP PTYPE: 0x%04X\n", ptype); 
      break; 
    } 
    printf("ARP HLEN: %d\n", arp_hdr->hlen); 
    printf("ARP PLEN: %d\n", arp_hdr->plen); 
    switch(oper) 
    { 
     case 0x0001: 
      printf("ARP OPER: Request(0x%04X)\n", oper); 
      break; 
     case 0x0002: 
      printf("ARP OPER: Response(0x%04X)\n", oper); 
      break; 
     default: 
      printf("ARP OPER: 0x%04X\n", oper); 
      break; 
    } 
    printf("ARP Sender HA: %02X:%02X:%02X:%02X:%02X:%02X\n", 
      arp_hdr->sender_ha[0],arp_hdr->sender_ha[1],arp_hdr->sender_ha[2], 
      arp_hdr->sender_ha[3], arp_hdr->sender_ha[4], arp_hdr->sender_ha[5]); 
    printf("ARP Sender PA: %d.%d.%d.%d\n", arp_hdr->sender_pa[0], 
      arp_hdr->sender_pa[1], arp_hdr->sender_pa[2], arp_hdr->sender_pa[3]); 
    printf("ARP Target HA: %02X:%02X:%02X:%02X:%02X:%02X\n", 
      arp_hdr->target_ha[0],arp_hdr->target_ha[1],arp_hdr->target_ha[2], 
      arp_hdr->target_ha[3], arp_hdr->target_ha[4], arp_hdr->target_ha[5]); 
    printf("ARP Target PA: %d.%d.%d.%d\n", arp_hdr->target_pa[0], 
      arp_hdr->target_pa[1], arp_hdr->target_pa[2], arp_hdr->target_pa[3]); 
    printf("ARP DONE =====================\n"); 
} 

我還除去bind()是不必要的,在所捕獲的數據包大小的比較校正由一個關斷。比較是<= sizeof(struct ethernet) + sizeof(struct arp)當它應該是<

閱讀的短報文插座內核源代碼,這我不是了,現在,我沒有找到爲什麼示例代碼只接收發給主機的IP數據包一個很好的解釋。由於OP和Internet上的許多示例都證實,當級別爲ETH_P_ALL時,輸出數據包也由套接字接收。我想這只是一個實現選擇。這可能是大多數應用程序的最佳行爲,例如那些實施協議,而不是窺探現有協議。

注意說到內核源代碼,我不確定爲什麼lsf/bpf過濾器工作時,我給它的兩個字節ETH_P_ARP沒有字節順序修改。我想可能是因爲在內核這些行:

case BPF_S_ANC_PROTOCOL: 
A = ntohs(skb->protocol); 

http://lxr.linux.no/#linux+v3.11/net/core/filter.c#L317