1 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 // 4 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 // Use of this source code is governed by a BSD-style license that can be 6 // found in the THIRD-PARTY file. 7 8 // This is only used by the tests module from tap.rs, but we cannot use #[macro_use] unless the 9 // reference to lazy_static is declared at the root level of the importing crate. 10 #[cfg(test)] 11 #[macro_use] 12 extern crate lazy_static; 13 #[macro_use] 14 extern crate log; 15 16 mod ctrl_queue; 17 mod mac; 18 mod open_tap; 19 mod queue_pair; 20 mod tap; 21 22 use std::io::Error as IoError; 23 use std::os::raw::c_uint; 24 use std::os::unix::io::{FromRawFd, RawFd}; 25 use std::{io, mem, net}; 26 use versionize::{VersionMap, Versionize, VersionizeResult}; 27 use versionize_derive::Versionize; 28 use virtio_bindings::bindings::virtio_net::{ 29 virtio_net_hdr_v1, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, 30 VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, 31 VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MQ, 32 }; 33 use vm_memory::{bitmap::AtomicBitmap, ByteValued}; 34 35 type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>; 36 37 pub use ctrl_queue::{CtrlQueue, Error as CtrlQueueError}; 38 pub use mac::{MacAddr, MAC_ADDR_LEN}; 39 pub use open_tap::{open_tap, Error as OpenTapError}; 40 pub use queue_pair::{NetCounters, NetQueuePair, NetQueuePairError, RxVirtio, TxVirtio}; 41 pub use tap::{Error as TapError, Tap}; 42 43 #[derive(Debug)] 44 pub enum Error { 45 /// Failed to create a socket. 46 CreateSocket(IoError), 47 } 48 49 pub type Result<T> = std::result::Result<T, Error>; 50 51 #[repr(C, packed)] 52 #[derive(Copy, Clone, Debug, Default, Versionize)] 53 pub struct VirtioNetConfig { 54 pub mac: [u8; 6], 55 pub status: u16, 56 pub max_virtqueue_pairs: u16, 57 pub mtu: u16, 58 pub speed: u32, 59 pub duplex: u8, 60 } 61 62 // Safe because it only has data and has no implicit padding. 63 unsafe impl ByteValued for VirtioNetConfig {} 64 65 /// Create a sockaddr_in from an IPv4 address, and expose it as 66 /// an opaque sockaddr suitable for usage by socket ioctls. 67 fn create_sockaddr(ip_addr: net::Ipv4Addr) -> net_gen::sockaddr { 68 // IPv4 addresses big-endian (network order), but Ipv4Addr will give us 69 // a view of those bytes directly so we can avoid any endian trickiness. 70 let addr_in = net_gen::sockaddr_in { 71 sin_family: net_gen::AF_INET as u16, 72 sin_port: 0, 73 sin_addr: unsafe { mem::transmute(ip_addr.octets()) }, 74 __pad: [0; 8usize], 75 }; 76 77 unsafe { mem::transmute(addr_in) } 78 } 79 80 fn create_socket() -> Result<net::UdpSocket> { 81 // This is safe since we check the return value. 82 let sock = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) }; 83 if sock < 0 { 84 return Err(Error::CreateSocket(IoError::last_os_error())); 85 } 86 87 // This is safe; nothing else will use or hold onto the raw sock fd. 88 Ok(unsafe { net::UdpSocket::from_raw_fd(sock) }) 89 } 90 91 fn vnet_hdr_len() -> usize { 92 std::mem::size_of::<virtio_net_hdr_v1>() 93 } 94 95 pub fn register_listener( 96 epoll_fd: RawFd, 97 fd: RawFd, 98 ev_type: epoll::Events, 99 data: u64, 100 ) -> std::result::Result<(), io::Error> { 101 epoll::ctl( 102 epoll_fd, 103 epoll::ControlOptions::EPOLL_CTL_ADD, 104 fd, 105 epoll::Event::new(ev_type, data), 106 ) 107 } 108 109 pub fn unregister_listener( 110 epoll_fd: RawFd, 111 fd: RawFd, 112 ev_type: epoll::Events, 113 data: u64, 114 ) -> std::result::Result<(), io::Error> { 115 epoll::ctl( 116 epoll_fd, 117 epoll::ControlOptions::EPOLL_CTL_DEL, 118 fd, 119 epoll::Event::new(ev_type, data), 120 ) 121 } 122 123 pub fn build_net_config_space( 124 mut config: &mut VirtioNetConfig, 125 mac: MacAddr, 126 num_queues: usize, 127 avail_features: &mut u64, 128 ) { 129 config.mac.copy_from_slice(mac.get_bytes()); 130 *avail_features |= 1 << VIRTIO_NET_F_MAC; 131 132 build_net_config_space_with_mq(&mut config, num_queues, avail_features); 133 } 134 135 pub fn build_net_config_space_with_mq( 136 config: &mut VirtioNetConfig, 137 num_queues: usize, 138 avail_features: &mut u64, 139 ) { 140 let num_queue_pairs = (num_queues / 2) as u16; 141 if (num_queue_pairs >= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16) 142 && (num_queue_pairs <= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16) 143 { 144 config.max_virtqueue_pairs = num_queue_pairs; 145 *avail_features |= 1 << VIRTIO_NET_F_MQ; 146 } 147 } 148 149 pub fn virtio_features_to_tap_offload(features: u64) -> c_uint { 150 let mut tap_offloads: c_uint = 0; 151 if features & (1 << VIRTIO_NET_F_GUEST_CSUM) != 0 { 152 tap_offloads |= net_gen::TUN_F_CSUM; 153 } 154 if features & (1 << VIRTIO_NET_F_GUEST_TSO4) != 0 { 155 tap_offloads |= net_gen::TUN_F_TSO4; 156 } 157 if features & (1 << VIRTIO_NET_F_GUEST_TSO6) != 0 { 158 tap_offloads |= net_gen::TUN_F_TSO6; 159 } 160 if features & (1 << VIRTIO_NET_F_GUEST_ECN) != 0 { 161 tap_offloads |= net_gen::TUN_F_TSO_ECN; 162 } 163 if features & (1 << VIRTIO_NET_F_GUEST_UFO) != 0 { 164 tap_offloads |= net_gen::TUN_F_UFO; 165 } 166 167 tap_offloads 168 } 169 170 #[cfg(test)] 171 mod tests { 172 use super::*; 173 174 #[test] 175 fn test_create_sockaddr() { 176 let addr: net::Ipv4Addr = "10.0.0.1".parse().unwrap(); 177 let sockaddr = create_sockaddr(addr); 178 179 assert_eq!(sockaddr.sa_family, net_gen::AF_INET as u16); 180 181 let data = &sockaddr.sa_data[..]; 182 183 // The first two bytes should represent the port, which is 0. 184 assert_eq!(data[0], 0); 185 assert_eq!(data[1], 0); 186 187 // The next four bytes should represent the actual IPv4 address, in network order. 188 assert_eq!(data[2], 10); 189 assert_eq!(data[3], 0); 190 assert_eq!(data[4], 0); 191 assert_eq!(data[5], 1); 192 } 193 } 194