|
| 1 | +// SPDX-License-Identifier: Apache-2.0 |
| 2 | +// Copyright Open Network Fabric Authors |
| 3 | + |
| 4 | +#![doc = include_str!("../README.md")] |
| 5 | + |
| 6 | +pub mod nic; |
| 7 | +pub mod sysfs; |
| 8 | + |
| 9 | +use std::path::PathBuf; |
| 10 | + |
| 11 | +use sysfs::*; |
| 12 | +use tracing::{error, info}; |
| 13 | + |
| 14 | +use crate::nic::{BindToVfioPci, PciDriver, PciNic}; |
| 15 | + |
| 16 | +/// Errors which might occur during system dataplane system initialization |
| 17 | +#[derive(Debug, thiserror::Error)] |
| 18 | +pub enum InitErr { |
| 19 | + /// The path is not under a mounted sysfs and therefore does not qualify as a [`SysfsPath`]. |
| 20 | + #[error("path {0:?} is not under sysfs")] |
| 21 | + PathNotUnderSysfs(PathBuf), |
| 22 | + /// Some [`std::io::Error`] error occurred |
| 23 | + #[error(transparent)] |
| 24 | + IoError(#[from] std::io::Error), |
| 25 | + /// Invalid UTF-8 in a path under sysfs is an absolutely wild error case I expect to |
| 26 | + /// never see. |
| 27 | + /// |
| 28 | + /// The kernel just uses ascii byte strings for sysfs, so you should never see this |
| 29 | + /// error under healthy conditions. |
| 30 | + /// In the event of illegal UTF-8, something is likely deeply wrong with the kernel; |
| 31 | + /// likely memory corruption or some other security impacting issue. |
| 32 | + /// |
| 33 | + /// As such, the [`InnitErr::InvalidUtf8`] branch deliberately does not include |
| 34 | + /// any information about the offending string name or any derivative, even for logging |
| 35 | + /// or error reporting. |
| 36 | + /// At best you will just end up mangling the log with unknown/unprintable bytes. |
| 37 | + /// At worst, injecting arbitrary bytes into a system log may be what an attacker needs |
| 38 | + /// for lateral compromise of some other system. |
| 39 | + /// |
| 40 | + /// You should likely `panic!` if you reach this error case as there is no plausible |
| 41 | + /// recovery from this type of low level operating system malfunction. |
| 42 | + #[error("path under sysfs is not a valid UTF-8 string")] |
| 43 | + SysfsPathIsNotValidUtf8, |
| 44 | + #[error("missing needed driver {0}")] |
| 45 | + DriverMissing(PciDriver), |
| 46 | +} |
| 47 | + |
| 48 | +/// Process setup helper function. |
| 49 | +/// |
| 50 | +/// Fills out the [`SYSFS`] [`OnceLock`]. |
| 51 | +/// |
| 52 | +/// # Panics |
| 53 | +/// |
| 54 | +/// - Panics if this process is unable to read the list of mounted filesystems. |
| 55 | +/// - |
| 56 | +/// |
| 57 | +/// Panics if the [`SYSFS`] [`OnceLock`] cannot be initialized. |
| 58 | +fn setup() -> Result<(), InitErr> { |
| 59 | + tracing_subscriber::fmt() |
| 60 | + .with_ansi(false) |
| 61 | + .with_file(true) |
| 62 | + .with_level(true) |
| 63 | + .with_line_number(true) |
| 64 | + .init(); |
| 65 | + let sysfs_mounts: Vec<_> = procfs::mounts() |
| 66 | + .unwrap() // acceptable panic: if we can't find /sys then this process will never work |
| 67 | + .into_iter() |
| 68 | + .filter(|mount| mount.fs_vfstype == "sysfs") |
| 69 | + .collect(); |
| 70 | + let sysfs_path = if sysfs_mounts.is_empty() { |
| 71 | + panic!("sysfs is not mounted: unable to initialize dataplane"); |
| 72 | + } else if sysfs_mounts.len() > 1 { |
| 73 | + const MSG_PREFIX: &str = |
| 74 | + "suspicious configuration found: sysfs is mounted at more than one location."; |
| 75 | + let message = std::format_args!("{MSG_PREFIX}. Filesystems found at {sysfs_mounts:#?}"); |
| 76 | + error!("{message}"); |
| 77 | + panic!("{message}"); |
| 78 | + } else { |
| 79 | + sysfs_mounts[0].fs_file.clone() |
| 80 | + }; |
| 81 | + let sysfs_root = SysfsPath::new(&sysfs_path)?; |
| 82 | + info!("found sysfs filesystem at {sysfs_root}"); |
| 83 | + match SYSFS.set(sysfs_root) { |
| 84 | + Ok(()) => Ok(()), |
| 85 | + Err(_) => { |
| 86 | + unreachable!("called setup function twice in one process!"); |
| 87 | + } |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +fn main() -> Result<(), InitErr> { |
| 92 | + setup()?; |
| 93 | + // TODO: proper argument parsing |
| 94 | + // -- hack add a real command line parser |
| 95 | + let mut args = std::env::args().skip(1); |
| 96 | + // -- end hack |
| 97 | + // TODO: fix unwraps in the next PR. These can't be properly addressed before the arg parser is done. |
| 98 | + let address = hardware::pci::address::PciAddress::try_from(args.next().unwrap()).unwrap(); |
| 99 | + let mut device = PciNic::new(address)?; |
| 100 | + device.bind_to_vfio_pci() |
| 101 | +} |
0 commit comments