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.
create_sockaddr(ip_addr: net::Ipv4Addr) -> net_gen::sockaddr65 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
create_inet_socket(addr: IpAddr) -> Result<net::UdpSocket>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
create_unix_socket() -> Result<net::UdpSocket>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
vnet_hdr_len() -> usize107 fn vnet_hdr_len() -> usize {
108 std::mem::size_of::<virtio_net_hdr_v1>()
109 }
110
register_listener( epoll_fd: RawFd, fd: RawFd, ev_type: epoll::Events, data: u64, ) -> std::result::Result<(), io::Error>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
unregister_listener( epoll_fd: RawFd, fd: RawFd, ev_type: epoll::Events, data: u64, ) -> std::result::Result<(), io::Error>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
build_net_config_space( config: &mut VirtioNetConfig, mac: MacAddr, num_queues: usize, mtu: Option<u16>, avail_features: &mut u64, )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
build_net_config_space_with_mq( config: &mut VirtioNetConfig, num_queues: usize, mtu: Option<u16>, avail_features: &mut u64, )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
virtio_features_to_tap_offload(features: u64) -> c_uint170 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]
test_create_sockaddr()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