xref: /cloud-hypervisor/net_util/src/lib.rs (revision b440cb7d2330770cd415b63544a371d4caa2db3a)
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