2013-05-14 2 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()을 만듭니다. args (AF_PACKET, ETH_P_ARP)를 사용하여 모든 인터페이스에서 bind()해도 상관 없습니다. 따라서 모든 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 ===================== 

내 소켓은 2 패킷의 4 얻었다 (my_host 요청을하고 my_host 응답 놓친). tcpdump -n -p -i eth0 arp 모든 4 개의 패킷을 표시합니다.

ETH_P_ARP를 socket() 및 bind()에서 ETH_P_ALL로 변경하면 4 개의 패킷 모두가 소켓 (IP 및 기타)과 함께 이동합니다.

왜? 그것을 고치는 방법?

추신. 일부 메일 링리스트의 주소를 알려주십시오. 여기서 나는이 동작에 대해 물어볼 수 있습니다.

+1

에서

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

이 질문에 대한 소스를 추가하십시오. pastebin이 사라지거나 데이터를 얻을 경우이 질문은 쓸모 없게됩니다. 감사. – sarnold

+0

사용하기 전에 s_ll 구조체를 정리할 때 더 잘 작동합니까? 또는 정적으로 만듭니다. –

+0

아니요. 게다가, man 7 패킷은 bind()가 sll_protocol, sll_ifindex 필드만을 사용한다고 말합니다. – user2380785

답변

3

늦은 답변의 종류는 재미 있지만이 항목으로 실험했습니다. 어쩌면 일부 googler/duckduckgoer가 도움이 될 수 있습니다.

내 제안은 ETH_P_ALL을 사용하여 모든 패킷을 수신 한 다음 Linux 소켓 필터로 필터링하여 응용 프로그램이 요청 된 ARP 패킷 만 수신하도록하는 것입니다.

여기 내 코드입니다. 라지 변화는 또한 I bind()는 불필요한 제거하고, 상기 캡쳐 된 패킷 크기와 비교하여 하나의 오프가 보정 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"); 
} 

표시된다. 그것은 필자가 없습니다까지 지금, 내가 예제 코드는 패킷을 호스트의 IP 주소로 수신하는 이유에 대한 좋은 설명을 찾을 수 없습니다 패킷 소켓 커널 소스를 읽는 <

짧은해야 할 때 비교 <= sizeof(struct ethernet) + sizeof(struct arp)했다. OP와 인터넷상의 많은 예들이 확인하기 때문에, 레벨이 ETH_P_ALL이면, 나가는 패킷들은 소켓에 의해 수신된다. 나는 그것이 단지 구현 선택이라고 생각한다. 대부분의 응용 프로그램에서 선호하는 동작 일 수 있습니다. 기존 프로토콜을 스누핑하는 대신 프로토콜을 구현하는 사람들.

참고 커널 소스에 대해 말하면서, 왜 lsf/bpf 필터가 byteorder 수정없이 2 바이트 ETH_P_ARP를 줄 때 작동하는지 모르겠습니다. 나는 그것 때문에 커널이 라인이 될 것 같아요 : http://lxr.linux.no/#linux+v3.11/net/core/filter.c#L317

관련 문제