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