xref: /cloud-hypervisor/net_util/src/lib.rs (revision 7d7bfb2034001d4cb15df2ddc56d2d350c8da30f)
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 // SAFETY: 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_inet_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 create_unix_socket() -> Result<net::UdpSocket> {
92     // This is safe since we check the return value.
93     let sock = unsafe { libc::socket(libc::AF_UNIX, libc::SOCK_DGRAM, 0) };
94     if sock < 0 {
95         return Err(Error::CreateSocket(IoError::last_os_error()));
96     }
97 
98     // This is safe; nothing else will use or hold onto the raw sock fd.
99     Ok(unsafe { net::UdpSocket::from_raw_fd(sock) })
100 }
101 
102 fn vnet_hdr_len() -> usize {
103     std::mem::size_of::<virtio_net_hdr_v1>()
104 }
105 
106 pub fn register_listener(
107     epoll_fd: RawFd,
108     fd: RawFd,
109     ev_type: epoll::Events,
110     data: u64,
111 ) -> std::result::Result<(), io::Error> {
112     epoll::ctl(
113         epoll_fd,
114         epoll::ControlOptions::EPOLL_CTL_ADD,
115         fd,
116         epoll::Event::new(ev_type, data),
117     )
118 }
119 
120 pub fn unregister_listener(
121     epoll_fd: RawFd,
122     fd: RawFd,
123     ev_type: epoll::Events,
124     data: u64,
125 ) -> std::result::Result<(), io::Error> {
126     epoll::ctl(
127         epoll_fd,
128         epoll::ControlOptions::EPOLL_CTL_DEL,
129         fd,
130         epoll::Event::new(ev_type, data),
131     )
132 }
133 
134 pub fn build_net_config_space(
135     config: &mut VirtioNetConfig,
136     mac: MacAddr,
137     num_queues: usize,
138     avail_features: &mut u64,
139 ) {
140     config.mac.copy_from_slice(mac.get_bytes());
141     *avail_features |= 1 << VIRTIO_NET_F_MAC;
142 
143     build_net_config_space_with_mq(config, num_queues, avail_features);
144 }
145 
146 pub fn build_net_config_space_with_mq(
147     config: &mut VirtioNetConfig,
148     num_queues: usize,
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 }
159 
160 pub fn virtio_features_to_tap_offload(features: u64) -> c_uint {
161     let mut tap_offloads: c_uint = 0;
162     if features & (1 << VIRTIO_NET_F_GUEST_CSUM) != 0 {
163         tap_offloads |= net_gen::TUN_F_CSUM;
164     }
165     if features & (1 << VIRTIO_NET_F_GUEST_TSO4) != 0 {
166         tap_offloads |= net_gen::TUN_F_TSO4;
167     }
168     if features & (1 << VIRTIO_NET_F_GUEST_TSO6) != 0 {
169         tap_offloads |= net_gen::TUN_F_TSO6;
170     }
171     if features & (1 << VIRTIO_NET_F_GUEST_ECN) != 0 {
172         tap_offloads |= net_gen::TUN_F_TSO_ECN;
173     }
174     if features & (1 << VIRTIO_NET_F_GUEST_UFO) != 0 {
175         tap_offloads |= net_gen::TUN_F_UFO;
176     }
177 
178     tap_offloads
179 }
180 
181 #[cfg(test)]
182 mod tests {
183     use super::*;
184 
185     #[test]
186     fn test_create_sockaddr() {
187         let addr: net::Ipv4Addr = "10.0.0.1".parse().unwrap();
188         let sockaddr = create_sockaddr(addr);
189 
190         assert_eq!(sockaddr.sa_family, net_gen::AF_INET as u16);
191 
192         let data = &sockaddr.sa_data[..];
193 
194         // The first two bytes should represent the port, which is 0.
195         assert_eq!(data[0], 0);
196         assert_eq!(data[1], 0);
197 
198         // The next four bytes should represent the actual IPv4 address, in network order.
199         assert_eq!(data[2], 10);
200         assert_eq!(data[3], 0);
201         assert_eq!(data[4], 0);
202         assert_eq!(data[5], 1);
203     }
204 }
205