2010-04-12 86 views
0

我嘗試將DHCP RENEW數據包發送到網絡並接收響應。我廣播了數據包,我可以看到它使用Wireshark成功發送。但是,我收到答覆時遇到了困難。我使用數據包套接字來捕獲數據包。我可以看到使用Wireshark對我的RENEW數據包有響應,但是我的函數'packet_receive_renew'有時會捕獲這些數據包,但有時它不能捕獲這些數據包。我使用FDSET設置文件描述符,但我的代碼中的「select」無法識別該文件描述符有新數據包,並且發生超時。我無法說清楚爲什麼它有時會捕獲數據包,有時卻不會。 有人有想法嗎? 在此先感謝。使用數據包套接字接收廣播數據包

這裏是接收函數。

int packet_receive_renew(struct client_info* info) 
{ 
    int fd; 
    struct sockaddr_ll sock, si_other; 
    struct sockaddr_in si_me; 
    fd_set rfds; 
    struct timeval tv; 
    time_t start, end; 
    int bcast = 1; 

    int ret = 0, try = 0; 
    char buf[1500] = {'\0'}; 
    uint8_t tmp[BUFLEN] = {'\0'}; 
    struct dhcp_packet pkt; 
    socklen_t slen = sizeof(si_other); 
    struct dhcps* new_dhcps; 

    memset((char *) &si_me, 0, sizeof(si_me)); 
    memset((char *) &si_other, 0, sizeof(si_other)); 
    memset(&pkt, 0, sizeof(struct dhcp_packet)); 

#define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68) 

    static const struct sock_filter filter_instr[] = { 
     /* check for udp */ 
     BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9), 
     BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 4),  /* L5, L1, is UDP? */ 
     /* skip IP header */ 
     BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),      /* L5: */ 
     /* check udp source and destination ports */ 
     BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0), 
     BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1), /* L3, L4 */ 
     /* returns */ 
     BPF_STMT(BPF_RET|BPF_K, 0x0fffffff),     /* L3: pass */ 
     BPF_STMT(BPF_RET|BPF_K, 0),        /* L4: reject */ 
    }; 

    static const struct sock_fprog filter_prog = { 
     .len = sizeof(filter_instr)/sizeof(filter_instr[0]), 
     /* casting const away: */ 
     .filter = (struct sock_filter *) filter_instr, 
    }; 

    printf("opening raw socket on ifindex %d\n", info->interf.if_index); 

    if (-1==(fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)))) 
    { 
     perror("packet_receive_renew::socket"); 
     return -1; 
    } 

    printf("got raw socket fd %d\n", fd); 

    /* Use only if standard ports are in use */ 
    /* Ignoring error (kernel may lack support for this) */ 
    if (-1==setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog))) 
     perror("packet_receive_renew::setsockopt"); 

    sock.sll_family = AF_PACKET; 
    sock.sll_protocol = htons(ETH_P_IP); 
    //sock.sll_pkttype = PACKET_BROADCAST; 
    sock.sll_ifindex = info->interf.if_index; 
    if (-1 == bind(fd, (struct sockaddr *) &sock, sizeof(sock))) { 
     perror("packet_receive_renew::bind"); 
     close(fd); 
     return -3; 
    } 

    if (-1 == setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast))) { 
     perror("packet_receive_renew::setsockopt"); 
     close(fd); 
     return -1; 
    } 

    FD_ZERO(&rfds); 
    FD_SET(fd, &rfds); 
    tv.tv_sec = TIMEOUT; 
    tv.tv_usec = 0; 
    ret = time(&start); 
    if (-1 == ret) { 
     perror("packet_receive_renew::time"); 
     close(fd); 
     return -1; 
    } 

    while(1) { 
     ret = select(fd + 1, &rfds, NULL, NULL, &tv); 
     time(&end); 
     if (TOTAL_PENDING <= (end - start)) { 
      fprintf(stderr, "End receiving\n"); 
      break; 
     } 
     if (-1 == ret) 
     { 
      perror("packet_receive_renew::select"); 
      close(fd); 
      return -4; 
     } 
     else if (ret) { 
      new_dhcps = (struct dhcps*)calloc(1, sizeof(struct dhcps)); 
      if (-1 == recvfrom(fd, buf, 1500, 0, (struct sockaddr*)&si_other, &slen)) { 
       perror("packet_receive_renew::recvfrom"); 
       close(fd); 
       return -4; 
      } 
      deref_packet((unsigned char*)buf, &pkt, info); 
      if (-1!=(ret=get_option_val(pkt.options, DHO_DHCP_SERVER_IDENTIFIER, tmp))) { 
       sprintf((char*)tmp, "%d.%d.%d.%d", tmp[0],tmp[1],tmp[2],tmp[3]); 
       fprintf(stderr, "Received renew from %s\n", tmp); 
      } 
      else 
      { 
       fprintf(stderr, "Couldnt get DHO_DHCP_SERVER_IDENTIFIER%s\n", tmp); 
       close(fd); 
       return -5; 
      } 
      new_dhcps->dhcps_addr = strdup((char*)tmp); 

      //add to list 
      if (info->dhcps_list) 
       info->dhcps_list->next = new_dhcps; 
      else 
       info->dhcps_list = new_dhcps; 
      new_dhcps->next = NULL; 
     } 
     else { 
      try++; 
      tv.tv_sec = TOTAL_PENDING - try * TIMEOUT; 
      tv.tv_usec = 0; 
      fprintf(stderr, "Timeout occured\n"); 
     } 
    } 
    close(fd); 
    printf("close fd:%d\n", fd); 
    return 0; 
} 

回答

0

我已經解決了這個問題。我認爲這是一個時間問題。我打開偵聽套接字並在發送消息之前綁定它。所以,它可以毫無問題地捕捉到消息。 謝謝。

相關問題