2012-04-09 1 views
1

C에서 리눅스에서 PROMISC 인터페이스를 설정하고 원시 소켓을 사용하면 read()를 통해 인터페이스에서 들어오는 패킷을 읽을 수있다.PF_PACKET에서 읽음 read()가 패킷을 놓친다

그러나 모든 패킷을 가져 오지는 않습니다. Read() 블록은 "긴"시간 (< 1 초이지만 패킷은 초당 수백 개로 흐릅니다)을 사용하여 파일 디스크립터에서 다음 사용 가능한 데이터를 읽습니다.

누락되었거나 근본적으로 잘못된 것이 있어야합니다.

"libpcap 사용"은 유효한 응답자가 아닙니다. 나는 (libpcap의 패킷을 놓치지 않음)

가 FD를 초기화 자신의 코드를 보았고, 그 차이를 찾을 수 없습니다 :

if ((fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) { 
    perror("socket(PF_PACKET) failed"); 
    return 1; 
} 

memset(&ifr, 0, sizeof(ifr)); 
strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1); 

if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { 
    perror("ioctl(SIOCGIFINDEX) failed"); 
    return 1; 
} 

memset(&sll, 0, sizeof(sll)); 
sll.sll_family = AF_PACKET; 
sll.sll_ifindex = ifr.ifr_ifindex; 
sll.sll_protocol = htons(ETH_P_ALL); 

if ((ifr.ifr_flags | IFF_UP | IFF_BROADCAST | IFF_RUNNING) != ifr.ifr_flags) { 
    ifr.ifr_flags |= IFF_UP | IFF_BROADCAST | IFF_RUNNING; 
    if(ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { 
     perror("ioctl(SIOCSIFFLAGS) failed"); 
     return 1; 
    } 
} 

if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) < 0) { 
    perror("bind(ETH_P_ALL) failed"); 
    return 1; 
} 

if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) 
{ 
    perror("ioctl(SIOCGIFHWADDR) failed"); 
    return 1; 
} 

if (ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211 && 
     ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211_PRISM && 
     ifr.ifr_hwaddr.sa_family != ARPHRD_IEEE80211_FULL) 
{ 
    if (ifr.ifr_hwaddr.sa_family == 1) 
     fprintf(stderr, "\nARP linktype is set to 1 (Ethernet) "); 
    else 
     fprintf(stderr, "\nUnsupported hardware link type %4d ", 
       ifr.ifr_hwaddr.sa_family); 

    fprintf(stderr, "- expected ARPHRD_IEEE80211,\nARPHRD_IEEE80211_" 
      "FULL or ARPHRD_IEEE80211_PRISM instead. Make\n" 
      "sure RFMON is enabled: run 'airmon-ng start %s" 
      " <#>'\nSysfs injection support was not found " 
      "either.\n\n", iface); 
    return 1; 
} 

memset(&mr, 0, sizeof(mr)); 
mr.mr_ifindex = sll.sll_ifindex; 
mr.mr_type = PACKET_MR_PROMISC; 

if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) { 
    perror("setsockop(PACKET_MR_PROMISC) failed"); 
    return 1; 
} 

읽기 :

while (caplen > 0) { 
      if ((caplen = read(fd, p, read_size)) < 0) { 
       perror("read failed"); 
       break; 
      } 
      p += caplen; 
      read_size -= caplen; 
     } 
    } 

답변

0

의 libpcap 1.0 이상 아마 또한 메모리를 사용 매핑 된 인터페이스이지만 질문에있는 코드는 그렇지 않습니다. 이것은 차이를 만들 수 있습니다.

Linux 소스의 Documentation/networking/packet_mmap.txt 파일을 참조하십시오.하지만 적어도 TPACKET_V1 및 TPACKET_V2를 사용하면 메모리 매핑 된 버퍼의 모든 슬롯이 가능한 가장 큰 패킷을 저장할만큼 커야합니다 (예를 들어 패킷에 802.11의 라디오 태그 메타 데이터 헤더 또는 어댑터가 TCP 세그먼트 화 또는 리 어셈블리를 수행하는 경우와 같이 추가 헤더가있는 경우 생각보다 클 수 있습니다).

또한 libpcap이 사용하는 것처럼 큰 소켓 버퍼 (또는 메모리 매핑 링 버퍼)를 제공하고 있는지 확인하십시오.

+0

나는 또한 zerocopy (= mmap 사용)로 시도했지만 문제는 동일했습니다. 코드의 다른 부분으로 인해 처음부터 다시 작성 했으므로 제대로 작동합니다. 그것은 확실히 위의 부분에 있으며 어떤 일이 벌어지고 있는지를 이해하기 위해 그것을 나눌 것입니다. sll 초기화에서 빠진 매개 변수 일 수 있습니다. 그 사이에 내가 손을 부러 뜨 렸어. – user1321333