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