bool send_frame( ether_device *device, buffer *frame ) { assert( device != NULL ); assert( device->send_queue != NULL ); assert( frame != NULL ); assert( frame->length > 0 ); if ( get_max_packet_buffers_length( device->send_queue ) <= get_packet_buffers_length( device->send_queue ) ) { warn( "Send queue is full ( device = %s, usage = %u/%u ).", device->name, get_packet_buffers_length( device->send_queue ), get_max_packet_buffers_length( device->send_queue ) ); return false; } debug( "Enqueueing a frame to send queue ( frame = %p, device = %s, queue length = %d, fd = %d ).", frame, device->name, get_packet_buffers_length( device->send_queue ), device->fd ); buffer *copy = get_free_packet_buffer( device->send_queue ); assert( copy != NULL ); copy_buffer( copy, frame ); enqueue_packet_buffer( device->send_queue, copy ); if ( ( get_packet_buffers_length( device->send_queue ) > 0 ) && ( device->fd >= 0 ) ) { set_writable_safe( device->fd, true ); } return true; }
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 ); }
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 ); }