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