xref: /cloud-hypervisor/net_util/src/lib.rs (revision 3f8cd52ffd74627242cb7e8ea1c2bdedadf6741a)
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 serde::{Deserialize, Serialize};
18 use std::io::Error as IoError;
19 use std::os::raw::c_uint;
20 use std::os::unix::io::{FromRawFd, RawFd};
21 use std::{io, mem, net};
22 use thiserror::Error;
23 
24 use virtio_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, Serialize, Deserialize)]
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::<[u8; 4], net_gen::inn::in_addr>(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