Exemplo n.º 1
0
static void
receive_frame( int fd, void *user_data ) {
  UNUSED( fd );

  ether_device *device = user_data;
  assert( device != NULL );

  if ( !device->status.up ) {
    return;
  }

  if ( get_max_packet_buffers_length( device->recv_queue ) <= get_packet_buffers_length( device->recv_queue ) ) {
    warn( "Receive queue is full ( device = %s, usage = %u/%u ).", device->name,
          get_packet_buffers_length( device->recv_queue ), get_max_packet_buffers_length( device->recv_queue ) );
    return;
  }

  unsigned int count = 0;
  const unsigned int max_queue_length = get_max_packet_buffers_length( device->recv_queue );
  const unsigned int max_loop_count = max_queue_length < 256 ? max_queue_length : 256;
  while ( count < max_loop_count ) {
    buffer *frame = get_buffer_from_free_buffers( device->recv_queue );
    if ( frame == NULL ) {
      warn( "Failed to retrieve a receive buffer ( device = %s, queue usage = %u/%u ).", device->name,
            get_packet_buffers_length( device->recv_queue ), max_queue_length );
      frame = device->recv_buffer; // Use recv_buffer as a trash.
    }
    append_back_buffer( frame, device->mtu );

    ssize_t length = recv( device->fd, frame->data, frame->length, MSG_DONTWAIT );
    assert( length != 0 );
    if ( length < 0 ) {
      if ( frame != device->recv_buffer ) {
        mark_packet_buffer_as_used( device->recv_queue, frame );
      }
      if ( ( errno == EINTR ) || ( errno == EAGAIN ) || ( errno == EWOULDBLOCK ) || ( errno == ENETDOWN ) ) {
        break;
      }
      char error_string[ ERROR_STRING_SIZE ];
      error( "Receive error ( device = %s, errno = %s [%d] ).",
             device->name, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno );
      break;
    }
    if ( frame != device->recv_buffer ) {
      frame->length = ( size_t ) length;
      enqueue_packet_buffer( device->recv_queue, frame );
    }
    else {
      reset_buffer( frame );
    }
    count++;
  }

  handle_received_frames( device );
}
Exemplo n.º 2
0
static void
receive_frame( int fd, void *user_data ) {
    UNUSED( fd );

    ether_device *device = user_data;
    assert( device != NULL );

    if ( !device->status.up ) {
        return;
    }

    if ( get_max_packet_buffers_length( device->recv_queue ) <= get_packet_buffers_length( device->recv_queue ) ) {
        warn( "Receive queue is full ( device = %s, usage = %u/%u ).", device->name,
              get_packet_buffers_length( device->recv_queue ), get_max_packet_buffers_length( device->recv_queue ) );
        return;
    }

    unsigned int count = 0;
    const unsigned int max_queue_length = get_max_packet_buffers_length( device->recv_queue );
    const unsigned int max_loop_count = max_queue_length < 256 ? max_queue_length : 256;
    while ( count < max_loop_count ) {
        buffer *frame = get_buffer_from_free_buffers( device->recv_queue );
        if ( frame == NULL ) {
            warn( "Failed to retrieve a receive buffer ( device = %s, queue usage = %u/%u ).", device->name,
                  get_packet_buffers_length( device->recv_queue ), max_queue_length );
            frame = device->recv_buffer; // Use recv_buffer as a trash.
        }
        reset_buffer( frame );
        append_back_buffer( frame, device->mtu );

#if WITH_PCAP
        size_t length = 0;
        struct pcap_pkthdr *header = NULL;
        const u_char *packet = NULL;
        int ret = pcap_next_ex( device->pcap, &header, &packet );
        if ( ret == 1 ) {
            length = header->caplen;
            if ( length > frame->length ) {
                append_back_buffer( frame, length - frame->length );
            }
            memcpy( frame->data, packet, length );
        }
        else {
            if ( frame != device->recv_buffer ) {
                mark_packet_buffer_as_used( device->recv_queue, frame );
            }
            if ( ret == -1 ) {
                error( "Receive error ( device = %s, pcap_err = %s ).",
                       device->name, pcap_geterr( device->pcap ) );
            }
            break;
        }
#else // WITH_PCAP
        struct msghdr msg;
        msg.msg_name = NULL;
        msg.msg_namelen = 0;

        struct iovec iovec;
        size_t headroom_length = sizeof( vlantag_header_t );
        char *head = ( char * ) frame->data + headroom_length;
        iovec.iov_base = head;
        iovec.iov_len = frame->length - headroom_length;
        msg.msg_iov = &iovec;
        msg.msg_iovlen = 1;

        char cmsg_buf[ CMSG_SPACE( sizeof( struct tpacket_auxdata ) ) ];
        msg.msg_control = cmsg_buf;
        msg.msg_controllen = sizeof( cmsg_buf );
        msg.msg_flags = 0;

        ssize_t length = recvmsg( device->fd, &msg, MSG_DONTWAIT );
        assert( length != 0 );
        if ( length < 0 ) {
            if ( frame != device->recv_buffer ) {
                mark_packet_buffer_as_used( device->recv_queue, frame );
            }
            if ( ( errno == EINTR ) || ( errno == EAGAIN ) || ( errno == EWOULDBLOCK ) || ( errno == ENETDOWN ) ) {
                break;
            }
            char error_string[ ERROR_STRING_SIZE ];
            error( "Receive error ( device = %s, errno = %s [%d] ).",
                   device->name, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno );
            break;
        }

        for ( struct cmsghdr *cmsg = CMSG_FIRSTHDR( &msg ); cmsg != NULL; cmsg = CMSG_NXTHDR( &msg, cmsg ) ) {
            if ( cmsg->cmsg_len < CMSG_LEN( sizeof( struct tpacket_auxdata ) ) ) {
                continue;
            }
            if ( cmsg->cmsg_level != SOL_PACKET || cmsg->cmsg_type != PACKET_AUXDATA ) {
                continue;
            }
            struct tpacket_auxdata *auxdata = ( struct tpacket_auxdata * ) CMSG_DATA( cmsg );
            if ( auxdata->tp_vlan_tci == 0 ) {
                continue;
            }
            head -= sizeof( vlantag_header_t );
            if ( ( void * ) head < frame->data ) {
                append_front_buffer( frame, sizeof( vlantag_header_t ) );
                head = frame->data;
            }
            length += ( ssize_t ) sizeof( vlantag_header_t );
            memmove( head, head + sizeof( vlantag_header_t ), ETH_ADDRLEN * 2 );
            uint16_t *eth_type = ( uint16_t * ) ( head + ETH_ADDRLEN * 2 );
            *eth_type = htons( ETH_ETHTYPE_TPID );
            uint16_t *tci = ++eth_type;
            *tci = htons( auxdata->tp_vlan_tci );
        }
        frame->data = head;
#endif
        if ( frame != device->recv_buffer ) {
            frame->length = ( size_t ) length;
            enqueue_packet_buffer( device->recv_queue, frame );
        }
        count++;
    }

    handle_received_frames( device );
}