void send_etherframe_from_local_to_vxlan( struct vxlan_instance *instance, struct ether_header *ether, size_t len ) { assert( vxlan != NULL ); assert( instance != NULL ); assert( ether != NULL ); assert( len > 0 ); if ( !instance->activated || !vxlan->active ) { return; } struct vxlanhdr vhdr; memset( &vhdr, 0, sizeof( vhdr ) ); vhdr.flags = VXLAN_VALIDFLAG; memcpy( vhdr.vni, instance->vni, VXLAN_VNISIZE ); struct iovec iov[ 2 ]; memset( iov, 0, sizeof( iov ) ); iov[ 0 ].iov_base = &vhdr; iov[ 0 ].iov_len = sizeof( vhdr ); iov[ 1 ].iov_base = ether; iov[ 1 ].iov_len = len; struct msghdr mhdr; memset( &mhdr, 0, sizeof( mhdr ) ); mhdr.msg_iov = iov; mhdr.msg_iovlen = 2; mhdr.msg_controllen = 0; char buf[ 256 ]; struct fdb_entry *entry = fdb_search_entry( instance->fdb, ether->ether_dhost ); if ( entry == NULL ) { mhdr.msg_name = &instance->addr; mhdr.msg_namelen = sizeof( instance->addr ); if ( sendmsg( instance->udp_sock, &mhdr, 0 ) < 0 ) { char *error_string = safe_strerror_r( errno, buf, sizeof( buf ) ); warn( "Failed to send a vxlan message ( errno = %s [%d] ).", error_string, errno ); } } else { entry->vtep_addr.sin_port = htons( instance->port ); mhdr.msg_name = &entry->vtep_addr; mhdr.msg_namelen = sizeof( entry->vtep_addr ); if ( sendmsg( instance->udp_sock, &mhdr, 0 ) < 0 ) { char *error_string = safe_strerror_r( errno, buf, sizeof( buf ) ); warn( "Failed to send a vxlan message ( errno = %s [%d] ).", error_string, errno ); } } }
static bool set_ipv4_multicast_leave( int socket, struct in_addr maddr, char *ifname ) { assert( ifname != NULL ); if ( !IN_MULTICAST( ntohl( maddr.s_addr ) ) ) { return false; } unsigned int n_users = 0; delete_user_from_multicast_group( maddr, &n_users ); if ( n_users > 0 ) { // Still used. return true; } struct ip_mreq mreq; memset( &mreq, 0, sizeof( mreq ) ); mreq.imr_multiaddr = maddr; if ( !getifaddr( ifname, &mreq.imr_interface ) ) { return false; } int ret = setsockopt( socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, ( char * ) &mreq, sizeof( mreq ) ); if ( ret < 0 ) { char buf[ 256 ]; char *error_string = safe_strerror_r( errno, buf, sizeof( buf ) ); error( "Failed to send a leave group ( socket = %d, ret = %d, errno = %s [%d] ).", socket, ret, error_string, errno ); return false; } return true; }
bool set_device_flags( const char *name, short int flags ) { assert( name != NULL ); int fd = socket( AF_INET, SOCK_DGRAM, 0 ); struct ifreq ifr; memset( &ifr, 0, sizeof( ifr ) ); strncpy( ifr.ifr_name, name, IFNAMSIZ ); ifr.ifr_name[ IFNAMSIZ - 1 ] = '\0'; ifr.ifr_flags = flags; int ret = ioctl( fd, SIOCSIFFLAGS, &ifr ); if ( ret == -1 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to set interface flags ( device = %s, flags = %#x, ret = %d, errno = %s [%d] ).", ifr.ifr_name, ifr.ifr_flags, ret, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); close( fd ); return false; } close( fd ); return true; }
static bool set_ipv4_multicast_join_and_iface( int socket, struct in_addr maddr, char *ifname ) { assert( ifname != NULL ); if ( !IN_MULTICAST( ntohl( maddr.s_addr ) ) ) { return false; } unsigned int n_users = 0; add_user_into_multicast_group( maddr, &n_users ); if ( n_users > 1 ) { // Already joined. return true; } else if ( n_users != 1 ) { return false; } struct ip_mreq mreq; memset( &mreq, 0, sizeof( mreq ) ); mreq.imr_multiaddr = maddr; if ( !getifaddr( ifname, &mreq.imr_interface ) ) { return false; } char buf[ 256 ]; int ret = setsockopt( socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, ( char * ) &mreq, sizeof( mreq ) ); if ( ret < 0 ) { char *error_string = safe_strerror_r( errno, buf, sizeof( buf ) ); error( "Failed to send a membership report ( socket = %d, ret = %d, errno = %s [%d] ).", socket, ret, error_string, errno ); return false; } ret = setsockopt( socket, IPPROTO_IP, IP_MULTICAST_IF, ( char * ) &mreq.imr_interface, sizeof( mreq.imr_interface ) ); if ( ret < 0 ) { char *error_string = safe_strerror_r( errno, buf, sizeof( buf ) ); error( "Failed to set a network interface ( socket = %d, ret = %d, errno = %s [%d] ).", socket, ret, error_string, errno ); return false; } 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 flush_send_queue( int fd, void *user_data ) { UNUSED( fd ); ether_device *device = user_data; assert( device != NULL ); debug( "Flushing send queue ( device = %s, queue length = %u ).", device->name, get_packet_buffers_length( device->send_queue ) ); set_writable_safe( device->fd, false ); struct sockaddr_ll sll; memset( &sll, 0, sizeof( sll ) ); sll.sll_ifindex = device->ifindex; int count = 0; buffer *buf = NULL; while ( ( buf = peek_packet_buffer( device->send_queue ) ) != NULL && count < 256 ) { #if WITH_PCAP if( pcap_sendpacket( device->pcap, buf->data, ( int ) buf->length ) < 0 ) { error( "Failed to send a message to ethernet device ( device = %s, pcap_err = %s ).", device->name, pcap_geterr( device->pcap ) ); } size_t length = buf->length; #else ssize_t length = sendto( device->fd, buf->data, buf->length, MSG_DONTWAIT, ( struct sockaddr * ) &sll, sizeof( sll ) ); if ( length < 0 ) { if ( ( errno == EINTR ) || ( errno == EAGAIN ) || ( errno == EWOULDBLOCK ) ) { break; } char error_string[ ERROR_STRING_SIZE ]; error( "Failed to send a message to ethernet device ( device = %s, errno = %s [%d] ).", device->name, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); return; } #endif if ( ( size_t ) length < buf->length ) { remove_front_buffer( buf, ( size_t ) length ); break; } buf = dequeue_packet_buffer( device->send_queue ); mark_packet_buffer_as_used( device->send_queue, buf ); count++; } if ( get_packet_buffers_length( device->send_queue ) > 0 ) { set_writable_safe( device->fd, true ); } }
bool try_lock( pthread_mutex_t *mutex ) { assert( mutex != NULL ); int ret = pthread_mutex_trylock( mutex ); if ( ret != 0 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to try lock mutex ( mutex = %p, ret = %s [%d] ).", mutex, safe_strerror_r( ret, error_string, sizeof( error_string ) ), ret ); return false; } return true; }
static void enable_promiscuous( ether_device *device ) { int fd = socket( AF_INET, SOCK_DGRAM, 0 ); if ( fd < 0 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to open a socket ( ret = %d, errno = %s [%d] ).", fd, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); return; } struct ifreq ifr; memset( &ifr, 0, sizeof( ifr ) ); strncpy( ifr.ifr_name, device->name, IFNAMSIZ ); ifr.ifr_name[ IFNAMSIZ - 1 ] = '\0'; int ret = ioctl( fd, SIOCGIFFLAGS, &ifr ); if ( ret == -1 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to get interface flags ( device = %s, ret = %d, errno = %s [%d] ).", ifr.ifr_name, ret, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); close( fd ); return; } ifr.ifr_flags |= IFF_PROMISC; ret = ioctl( fd, SIOCSIFFLAGS, &ifr ); if ( ret == -1 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to set interface flags ( device = %s, flags = %#x, ret = %d, errno = %s [%d] ).", ifr.ifr_name, ifr.ifr_flags, ret, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); close( fd ); return; } close( fd ); }
static bool set_ipv4_multicast_loop( int socket, int stat ) { assert( socket >= 0 ); int ret = setsockopt( socket, IPPROTO_IP, IP_MULTICAST_LOOP, ( char * ) &stat, sizeof( stat ) ); if ( ret < 0 ) { char buf[ 256 ]; char *error_string = safe_strerror_r( errno, buf, sizeof( buf ) ); error( "Failed to turn off multicast loopback ( socket = %d, ret = %d, errno = %s [%d] ).", socket, ret, error_string, errno ); return false; } return true; }
static bool set_ipv4_multicast_ttl( int socket, int ttl ) { assert( socket >= 0 ); int ret = setsockopt( socket, IPPROTO_IP, IP_MULTICAST_TTL, ( char * ) &ttl, sizeof( ttl ) ); if ( ret < 0 ) { char buf[ 256 ]; char *error_string = safe_strerror_r( errno, buf, sizeof( buf ) ); error( "Failed to set TTL value to %d ( socket = %d, ret = %d, errno = %s [%d] ).", ttl, socket, ret, error_string, errno ); return false; } return true; }
bool init_net( struct vxlan *_vxlan ) { assert( _vxlan != NULL ); vxlan = _vxlan; vxlan->udp_sock = socket( AF_INET, SOCK_DGRAM, 0 ); if ( vxlan->udp_sock < 0 ) { char buf[ 256 ]; char *error_string = safe_strerror_r( errno, buf, sizeof( buf ) ); error( "Failed to create a socket for IPv4 ( ret = %d, errno = %s [%d] ).", vxlan->udp_sock, error_string, errno ); goto error; } bool ret = bind_ipv4_inaddrany( vxlan->udp_sock, vxlan->port ); if ( !ret ) { goto error; } ret = set_ipv4_multicast_loop( vxlan->udp_sock, 0 ); if ( !ret ) { goto error; } ret = set_ipv4_multicast_ttl( vxlan->udp_sock, VXLAN_MCAST_TTL ); if ( !ret ) { goto error; } ret = update_interface_state(); if ( !ret ) { goto error; } ret = init_multicast_group_table(); if ( !ret ) { goto error; } return true; error: if ( vxlan->udp_sock >= 0 ) { close( vxlan->udp_sock ); } return false; }
bool init_mutex( pthread_mutex_t *mutex ) { assert( mutex != NULL ); pthread_mutexattr_t attr; pthread_mutexattr_init( &attr ); pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE_NP ); int ret = pthread_mutex_init( mutex, &attr ); if ( ret != 0 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to initialize mutex ( mutex = %p, ret = %s [%d] ).", mutex, safe_strerror_r( ret, error_string, sizeof( error_string ) ), ret ); return false; } return true; }
void send_etherframe_from_vxlan_to_local( struct vxlan_instance *instance, struct ether_header *ether, size_t len ) { assert( vxlan != NULL ); assert( instance != NULL ); assert( ether != NULL ); assert( len > 0 ); if ( !instance->activated || !vxlan->active ) { return; } ssize_t ret = write( instance->tap_sock, ether, len ); if ( ret != ( ssize_t ) len ) { char buf[ 256 ]; char *error_string = safe_strerror_r( errno, buf, sizeof( buf ) ); warn( "Failed to write an Ethernet frame to a tap interface ( socket = %d, " "len = %u, ret = %d, errno = %s [%d] ).", instance->tap_sock, len, ret, error_string, errno ); } }
static bool bind_ipv4_inaddrany( int socket, uint16_t port ) { assert( socket >= 0 ); assert( port > 0 ); struct sockaddr_in saddr_in; memset( &saddr_in, 0, sizeof( saddr_in ) ); saddr_in.sin_family = AF_INET; saddr_in.sin_port = htons( port ); saddr_in.sin_addr.s_addr = INADDR_ANY; int ret = bind( socket, ( struct sockaddr * ) &saddr_in, sizeof( saddr_in ) ); if ( ret < 0 ) { char buf[ 256 ]; char *error_string = safe_strerror_r( errno, buf, sizeof( buf ) ); error( "Failed to bind a socket ( fd = %d, errno = %s [%d] ).", socket, error_string, errno ); return false; } return true; }
ether_device * create_ether_device( const char *name, const size_t max_send_queue, const size_t max_recv_queue ) { assert( name != NULL ); assert( strlen( name ) > 0 ); assert( max_send_queue > 0 ); assert( max_recv_queue > 0 ); int nfd = socket( PF_INET, SOCK_DGRAM, 0 ); if ( nfd < 0 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to open a socket ( ret = %d, errno = %s [%d] ).", nfd, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); return NULL; } struct ifreq ifr; memset( &ifr, 0, sizeof( ifr ) ); strncpy( ifr.ifr_name, name, IFNAMSIZ ); ifr.ifr_name[ IFNAMSIZ - 1 ] = '\0'; int ret = ioctl( nfd, SIOCGIFINDEX, &ifr ); if ( ret == -1 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to retrieve an interface index of %s ( ret = %d, errno = %s [%d] ).", name, ret, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); close( nfd ); return NULL; } int ifindex = ifr.ifr_ifindex; ret = ioctl( nfd, SIOCGIFMTU, &ifr ); if ( ret == -1 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to retrieve MTU of %s ( ret = %d, errno = %s [%d] ).", name, ret, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); close( nfd ); return NULL; } int mtu = ifr.ifr_mtu; ret = ioctl( nfd, SIOCGIFHWADDR, &ifr ); if ( ret == -1 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to retrieve hardware address of %s ( ret = %d, error = %s [%d] ).", name, ret, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); close( nfd ); return NULL; } close( nfd ); size_t device_mtu = ( size_t ) mtu + MAX_L2_HEADER_LENGTH; #ifdef WITH_PCAP char errbuf[ PCAP_ERRBUF_SIZE ]; pcap_t *handle = pcap_open_live( name, ( int ) device_mtu, 1, 100, errbuf ); if( handle == NULL ) { error( "Failed to open %s ( %s ).", name, errbuf ); return NULL; } if ( pcap_setnonblock( handle, 1, errbuf ) == -1 ) { warn( "Failed to setnonblock %s ( %s ).", name, errbuf ); } int fd = pcap_get_selectable_fd( handle ); #else // WITH_PCAP int fd = socket( PF_PACKET, SOCK_RAW, htons( ETH_P_ALL ) ); if ( fd < 0 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to open a socket ( ret = %d, errno = %s [%d] ).", fd, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); return NULL; } struct sockaddr_ll sll; memset( &sll, 0, sizeof( sll ) ); sll.sll_family = AF_PACKET; sll.sll_protocol = htons( ETH_P_ALL ); sll.sll_ifindex = ifindex; ret = bind( fd, ( struct sockaddr * ) &sll, sizeof( sll ) ); if ( ret < 0 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to bind ( fd = %d, ret = %d, errno = %s [%d] ).", fd, ret, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); close( fd ); return NULL; } int val = TPACKET_V2; ret = setsockopt( fd, SOL_PACKET, PACKET_VERSION, &val, sizeof( val ) ); if ( ret < 0 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to set PACKET_VERSION to %d ( fd = %d, ret = %d, errno = %s [%d] ).", val, fd, ret, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); close( fd ); return NULL; } val = 1; ret = setsockopt( fd, SOL_PACKET, PACKET_AUXDATA, &val, sizeof( val ) ); if ( ret < 0 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to set PACKET_AUXDATA to %d ( fd = %d, ret = %d, errno = %s [%d] ).", val, fd, ret, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); close( fd ); return NULL; } while ( 1 ) { char buf; ssize_t length = recv( fd, &buf, 1, MSG_DONTWAIT ); if ( length <= 0 ) { break; } } #endif // WITH_PCAP ether_device *device = xmalloc( sizeof( ether_device ) ); memset( device, 0, sizeof( ether_device ) ); strncpy( device->name, name, IFNAMSIZ ); device->name[ IFNAMSIZ - 1 ] = '\0'; #if WITH_PCAP device->pcap = handle; #endif device->fd = fd; device->ifindex = ifindex; memcpy( device->hw_addr, ifr.ifr_hwaddr.sa_data, ETH_ADDRLEN ); device->status.can_retrieve_link_status = true; device->status.can_retrieve_pause = true; device->mtu = device_mtu; device->recv_buffer = alloc_buffer_with_length( device->mtu ); device->send_queue = create_packet_buffers( ( unsigned int ) max_send_queue, device->mtu ); device->recv_queue = create_packet_buffers( ( unsigned int ) max_recv_queue, device->mtu ); short int flags = get_device_flags( device->name ); device->original_flags = flags; set_fd_handler_safe( fd, receive_frame, device, flush_send_queue, device ); set_readable_safe( fd, true ); enable_promiscuous( device ); up_ether_device( device ); update_device_status( device ); time_now( &device->created_at ); return device; }
void * receiver_main( void *args ) { assert( args != NULL ); info( "Receiver thread is started ( pid = %u, tid = %u).", getpid(), receiver_thread ); receiver_options *options = args; ethdev *dev = options->dev; assert( dev->fd >= 0 ); packet_buffer trash; while ( running ) { notify_distributor(); fd_set fds; FD_ZERO( &fds ); FD_SET( dev->fd, &fds ); struct timespec timeout = { 0, 1000000 }; int ret = pselect( dev->fd + 1, &fds, NULL, NULL, &timeout, NULL ); if ( ret < 0 ) { if ( errno == EINTR ) { continue; } char buf[ 256 ]; char *error_string = safe_strerror_r( errno, buf, sizeof( buf ) ); error( "Failed to select ( errno = %s [%d] ).", error_string, errno ); break; } else if ( ret == 0 ) { continue; } if ( !FD_ISSET( dev->fd, &fds ) ) { continue; } packet_buffer *packet = peek( free_packet_buffers ); if ( packet == NULL ) { // TODO: implement a counter to remember # of packet losses packet = &trash; } int err = 0; ssize_t length = recv_from_ethdev( dev, packet->data, sizeof( packet->data ), &err ); if ( packet == &trash ) { continue; } if ( length < ( sizeof( struct iphdr ) + sizeof( struct udphdr ) + sizeof( struct vxlanhdr ) ) || length > PACKET_SIZE ) { continue; } packet->ip = ( struct iphdr * ) packet->data; if ( packet->ip->protocol != IPPROTO_UDP ) { continue; } packet->udp = ( struct udphdr * ) ( ( char * ) packet->ip + ( packet->ip->ihl * 4 ) ); if ( ntohs( packet->udp->dest ) != options->port ) { continue; } packet->vxlan = ( struct vxlanhdr * ) ( ( char * ) packet->udp + sizeof( struct udphdr ) ); packet->length = ( size_t ) length; dequeue( free_packet_buffers ); enqueue( received_packets, packet ); } running = false; info( "Receiver thread is terminated ( pid = %u, tid = %u ).", getpid(), receiver_thread ); free( options ); return NULL; }
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 ); }
bool update_device_status( ether_device *device ) { assert( device != NULL ); assert( strlen( device->name ) > 0 ); int fd = socket( PF_INET, SOCK_DGRAM, 0 ); if ( fd < 0 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to open a socket ( ret = %d, errno = %s [%d] ).", fd, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); return false; } struct ifreq ifr; memset( &ifr, 0, sizeof( ifr ) ); strncpy( ifr.ifr_name, device->name, IFNAMSIZ ); ifr.ifr_name[ IFNAMSIZ - 1 ] = '\0'; struct ethtool_value ev; memset( &ev, 0, sizeof( struct ethtool_value ) ); ev.cmd = ETHTOOL_GLINK; ifr.ifr_data = ( char * ) &ev; int ret = ioctl( fd, SIOCETHTOOL, &ifr ); if ( ret == -1 ) { char error_string[ ERROR_STRING_SIZE ]; error( "Failed to retrieve link status of %s ( ret = %d, error = %s [%d] ).", device->name, ret, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); warn( "Assuming link is up ( device = %s ).", device->name ); device->status.up = true; } else { if ( ev.data > 0 ) { device->status.up = true; } else { device->status.up = false; } } struct ethtool_cmd ec; memset( &ec, 0, sizeof( struct ethtool_cmd ) ); if ( device->status.can_retrieve_link_status ) { ec.cmd = ETHTOOL_GSET; ifr.ifr_data = ( char * ) &ec; ret = ioctl( fd, SIOCETHTOOL, &ifr ); if ( ret == -1 ) { char error_string[ ERROR_STRING_SIZE ]; warn( "Failed to retrieve statuses of %s ( ret = %d, error = %s [%d] ).", device->name, ret, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); warn( "Assuming 100Mbps/Full Duplex/Twisted Pair link ( device = %s ).", device->name ); device->status.can_retrieve_link_status = false; } } if ( !device->status.can_retrieve_link_status ) { ethtool_cmd_speed_set( &ec, SPEED_100 ); ec.duplex = DUPLEX_FULL; ec.port = PORT_TP; ec.advertising = ADVERTISED_100baseT_Full | ADVERTISED_TP; ec.supported = SUPPORTED_100baseT_Full | SUPPORTED_TP; } struct ethtool_pauseparam ep; memset( &ep, 0, sizeof( struct ethtool_pauseparam ) ); if ( device->status.can_retrieve_pause ) { ep.cmd = ETHTOOL_GPAUSEPARAM; ifr.ifr_data = ( char * ) &ep; ret = ioctl( fd, SIOCETHTOOL, &ifr ); if ( ret == -1 ) { char error_string[ ERROR_STRING_SIZE ]; warn( "Failed to retrieve pause parameters of %s ( ret = %d, error = %s [%d] ).", device->name, ret, safe_strerror_r( errno, error_string, sizeof( error_string ) ), errno ); warn( "Assuming pause is disabled ( device = %s ).", device->name ); device->status.can_retrieve_pause = false; } } close( fd ); device->status.curr = make_current_ofp_port_features( ethtool_cmd_speed( &ec ), ec.duplex, ec.port, ec.autoneg, ep.rx_pause, ep.tx_pause ); device->status.advertised = make_supported_ofp_port_features( ec.advertising ); device->status.supported = make_supported_ofp_port_features( ec.supported ); device->status.peer = device->status.curr; // FIMXE: how to know the correct value? device->status.curr_speed = 0; // Since we set curr flags, this field might be meaningless. device->status.max_speed = 0; // Since we set supported flags, this field might be meaningless. return true; }