首页 > 解决方案 > 将字段内容从一个公共结构复制到另一个

问题描述

我正在修改一些由另一方编写的代码。目的是模拟 UniFi 安全网关以在 UniFi 控制器软件中获取报告。我计划在带有 2 个运行 CentOS 8 的 NIC 的 Zotac Mini PC 上运行仿真。所以我不必担心额外的 NAT,我将 2 个 NIC 设置为桥接器。

每个网络设备的结构定义为:

#[derive(PartialEq, Clone, Debug)]
pub(crate) struct UnixNetworkDevice {
    name: String,
    mac: MacAddr,
    interface: NetworkInterface,
    statistics: UnixNetworkDeviceStatistics,
}

当为 eth0 设备填写结构时,您会得到这个(wan_device开头只是我的标签,所以我知道我在看什么):

wan_device Some(UnixNetworkDevice {
    name: "eth0",
    mac: 00:01:2e:80:3e:1d,
    interface: NetworkInterface {
        name: "eth0",
        index: 2,
        mac: Some(00:01:2e:80:3e:1d),
        ips: [],
        flags: 69699
    },
    statistics: UnixNetworkDeviceStatistics {
        collisions: 0,
        multicast: 497,
        rx_bytes: 359185,
        rx_compressed: 0,
        rx_crc_errors: 0,
        rx_dropped: 26,
        rx_errors: 0,
        rx_fifo_errors: 0,
        rx_frame_errors: 0,
        rx_length_errors: 0,
        rx_missed_errors: 0,
        rx_nohandler: 0,
        rx_over_errors: 0,
        rx_packets: 1587,
        tx_aborted_errors: 0,
        tx_bytes: 74695,
        tx_carrier_errors: 0,
        tx_compressed: 0,
        tx_dropped: 0,
        tx_errors: 0,
        tx_fifo_errors: 0,
        tx_heartbeat_errors: 0,
        tx_packets: 474,
        tx_window_errors: 0
    }
})

我还从 Bridge 设备中提取相同的信息:

bri_device Some(UnixNetworkDevice {
    name: "bri0",
    mac: 00:01:2e:80:3e:1d,
    interface: NetworkInterface {
        name: "bri0",
        index: 5,
        mac: Some(00:01:2e:80:3e:1d),
        ips: [V4(Ipv4Network { addr: 192.168.113.2, prefix: 24 }),
              V6(Ipv6Network { addr: fe80::c8ee:a0ff:fe3a:3096, prefix: 64 })],
        flags: 69699
    },
    statistics: UnixNetworkDeviceStatistics {
        collisions: 0,
        multicast: 0,
        rx_bytes: 275467,
        rx_compressed: 0,
        rx_crc_errors: 0,
        rx_dropped: 0,
        rx_errors: 0,
        rx_fifo_errors: 0,
        rx_frame_errors: 0,
        rx_length_errors: 0,
        rx_missed_errors: 0,
        rx_nohandler: 0,
        rx_over_errors: 0,
        rx_packets: 1371,
        tx_aborted_errors: 0,
        tx_bytes: 68355,
        tx_carrier_errors: 0,
        tx_compressed: 0,
        tx_dropped: 0,
        tx_errors: 0,
        tx_fifo_errors: 0,
        tx_heartbeat_errors: 0,
        tx_packets: 345,
        tx_window_errors: 0
    }
})

wan_device不显示任何 IP 地址,因为它已分配给网桥。

现在,当它尝试执行 a clone()of the时,由于结构中的空白部分而wan_device失败。ips我想将结构的 IPS 部分从复制bri_devicewan_device. 我希望这是可能的。

这是一个可以做成项目的 MRE,包括它后面的 Cargo.toml 文件。

这是相同 ZIP 文件的链接: https ://www.dropbox.com/s/7pfxbmv49ra1jxs/Test-OpnFi.zip?dl=0

#[macro_use]
extern crate log;

extern crate lazy_static;
extern crate clap;
extern crate regex;
extern crate simple_logger;

/// Network interface for inform
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug, Default)]
pub struct OpnFiInformNetworkInterface {
    pub drops: usize,
    pub enabled: bool,
    pub full_duplex: bool,
    pub gateways: Vec<String>,
    pub ip: String,
    pub latency: usize,
    pub mac: String,
    pub name: String,
    pub nameservers: Vec<String>,
    pub netmask: String,
    pub num_port: usize,
    pub rx_bytes: usize,
    pub rx_dropped: usize,
    pub rx_errors: usize,
    pub rx_multicast: usize,
    pub rx_packets: usize,
    pub speed: usize,
    pub speedtest_lastrun: usize,
    pub speedtest_ping: usize,
    pub speedtest_status: String,
    pub tx_bytes: usize,
    pub tx_dropped: usize,
    pub tx_errors: usize,
    pub tx_packets: usize,
    pub up: bool,
    pub uptime: usize,
    pub xput_down: usize,
    pub xput_up: usize,
}
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
pub(crate) struct Config {
    pub inform_url: String,
    pub capability: Vec<String>,
    pub cfgversion: String,
    pub selfrun_guest_mode: String,
    pub led_enabled: bool,
    pub stun_url: String,
    pub mgmt_url: String,
    pub authkey: String,
    pub use_aes_gcm: bool,
    pub report_crash: bool,
}

use pnet::{
    datalink::{interfaces, NetworkInterface},
    util::MacAddr,
};
use std::str::FromStr;
use std::{fs, io, path};

#[derive(PartialEq, Clone, Debug)]
pub(crate) struct UnixNetworkDevice {
    name: String,
    mac: MacAddr,
    interface: NetworkInterface,
    statistics: UnixNetworkDeviceStatistics,
}

impl UnixNetworkDevice {
    pub fn new(name: &String) -> io::Result<UnixNetworkDevice> {
        let device_path = path::Path::new("/sys/class/net").join(name);
        if !device_path.as_path().is_dir() {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!("Unable to locate UnixNetworkDevice {}", name),
            ));
        }

    let mac_string = fs::read_to_string(device_path.join("address"))?;
        if mac_string.trim().len() < 15 {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "Mac address is empty.",
            ));
        }
    let mac = MacAddr::from_str(mac_string.trim())
            .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;

        let interface = interfaces().into_iter().filter(|i| i.name == *name).next();
        if interface.is_none() {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!("Unable to locate NetworkInterface {}", name),
            ));
        }
    let interface = interface.unwrap();

        let statistics = UnixNetworkDeviceStatistics::new(name);

        Ok(UnixNetworkDevice {
            name: name.clone(),
            mac,
            interface,
            statistics,
        })
    }

    pub fn name(&self) -> String {
        self.name.clone()
    }

    pub fn mac(&self) -> MacAddr {
        self.mac
    }

    pub fn interface(&self) -> NetworkInterface {
        self.interface.clone()
    }

    pub fn statistics(&self) -> UnixNetworkDeviceStatistics {
        self.statistics.clone()
    }
}

// ===== Statistics =====

#[derive(PartialOrd, PartialEq, Clone, Debug)]
pub(crate) struct UnixNetworkDeviceStatistics {
    pub collisions: usize,
    pub multicast: usize,
    pub rx_bytes: usize,
    pub rx_compressed: usize,
    pub rx_crc_errors: usize,
    pub rx_dropped: usize,
    pub rx_errors: usize,
    pub rx_fifo_errors: usize,
    pub rx_frame_errors: usize,
    pub rx_length_errors: usize,
    pub rx_missed_errors: usize,
    pub rx_nohandler: usize,
    pub rx_over_errors: usize,
    pub rx_packets: usize,
    pub tx_aborted_errors: usize,
    pub tx_bytes: usize,
    pub tx_carrier_errors: usize,
    pub tx_compressed: usize,
    pub tx_dropped: usize,
    pub tx_errors: usize,
    pub tx_fifo_errors: usize,
    pub tx_heartbeat_errors: usize,
    pub tx_packets: usize,
    pub tx_window_errors: usize,
}

impl UnixNetworkDeviceStatistics {
    pub fn new(device_name: &String) -> UnixNetworkDeviceStatistics {
        let read_value = |statistic_name: &str| -> io::Result<usize> {
            let stat_path = path::Path::new("/sys/class/net")
                .join(device_name)
                .join("statistics")
                .join(statistic_name);
            let value = fs::read_to_string(stat_path.as_path())?;
            usize::from_str(value.trim())
                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
        };

        UnixNetworkDeviceStatistics {
            collisions: read_value("collisions").unwrap_or_default(),
        multicast: read_value("multicast").unwrap_or_default(),
            rx_bytes: read_value("rx_bytes").unwrap_or_default(),
            rx_compressed: read_value("rx_compressed").unwrap_or_default(),
            rx_crc_errors: read_value("rx_crc_errors").unwrap_or_default(),
            rx_dropped: read_value("rx_dropped").unwrap_or_default(),
            rx_errors: read_value("rx_errors").unwrap_or_default(),
            rx_fifo_errors: read_value("rx_fifo_errors").unwrap_or_default(),
            rx_frame_errors: read_value("rx_frame_errors").unwrap_or_default(),
            rx_length_errors: read_value("rx_length_errors").unwrap_or_default(),
            rx_missed_errors: read_value("rx_missed_errors").unwrap_or_default(),
            rx_nohandler: read_value("rx_nohandler").unwrap_or_default(),
            rx_over_errors: read_value("rx_over_errors").unwrap_or_default(),
            rx_packets: read_value("rx_packets").unwrap_or_default(),
            tx_aborted_errors: read_value("tx_aborted_errors").unwrap_or_default(),
            tx_bytes: read_value("tx_bytes").unwrap_or_default(),
            tx_carrier_errors: read_value("tx_carrier_errors").unwrap_or_default(),
            tx_compressed: read_value("tx_compressed").unwrap_or_default(),
            tx_dropped: read_value("tx_dropped").unwrap_or_default(),
            tx_errors: read_value("tx_errors").unwrap_or_default(),
            tx_fifo_errors: read_value("tx_fifo_errors").unwrap_or_default(),
            tx_heartbeat_errors: read_value("tx_heartbeat_errors").unwrap_or_default(),
            tx_packets: read_value("tx_packets").unwrap_or_default(),
            tx_window_errors: read_value("tx_window_errors").unwrap_or_default(),
        }
    }
}

impl From<UnixNetworkDevice> for OpnFiInformNetworkInterface {
    fn from(value: UnixNetworkDevice) -> Self {
        let interface = value.interface();
        let stats = value.statistics();
        let ip = interface
            .ips
            .iter()
            .filter(|ip| ip.is_ipv4())
            .next()
            .unwrap();
        Self {
            drops: stats.rx_dropped + stats.tx_dropped,
            enabled: true,
            full_duplex: true,
            gateways: vec![],
            ip: ip.ip().to_string(),
            latency: 1,
            mac: value.mac().to_string(),
            name: value.name().to_string(),
            nameservers: vec![],
            netmask: ip.mask().to_string(),
            num_port: interface.index as usize,
            rx_bytes: stats.rx_bytes,
            rx_dropped: stats.rx_dropped,
            rx_errors: stats.rx_errors,
            rx_multicast: 0,
            rx_packets: stats.rx_packets,
            speed: 1000,
            speedtest_lastrun: 0,
            speedtest_ping: 0,
            speedtest_status: "Idle".to_string(),
            tx_bytes: stats.tx_bytes,
            tx_dropped: stats.tx_dropped,
            tx_errors: stats.tx_errors,
            tx_packets: stats.tx_packets,
            up: true,
            uptime: 0,
            xput_down: 0,
            xput_up: 0,
        }
    }
}

fn main() {

    let matches = clap::App::new("OpnFi Device")
        .version("0.1.0")
        .author("James Parks <jrjparks@zathera.com>")
        .about("Emulates a UniFi device")
        .arg(
            clap::Arg::with_name("config")
                .short("c")
                .long("config")
                .value_name("FILE")
                .help("Sets a config file path to use")
                .takes_value(true),
        )
    .arg(
            clap::Arg::with_name("controller")
                .long("controller")
                .value_name("FILE")
                .help("FQDN or IP Address of UniFi Controller")
                .takes_value(true),
        )
    .arg(
            clap::Arg::with_name("wan")
                .short("w")
                .long("wan")
                .value_name("NIC")
                .help("Set the nic to report for WAN")
                .takes_value(true)
                .default_value("eth0"),
        )
    .arg(
            clap::Arg::with_name("lan")
                .short("l")
                .long("lan")
                .value_name("NIC")
                .help("Set the nic to report for LAN")
                .takes_value(true)
                .default_value("eth1"),
        )
    .arg(
            clap::Arg::with_name("bri")
                .short("b")
                .long("bri")
                .value_name("NIC")
                .help("Set the nic to report for BRIDGE")
                .takes_value(true)
                .default_value("bri0"),
        )
    .get_matches();

    let _wan_device = match matches.value_of("wan") {
        Some(wan_name) => {
            info!("Using {} as WAN device.", wan_name);
            UnixNetworkDevice::new(&wan_name.to_string()).ok()
        }
        None => None,
    };
    let _lan_device = match matches.value_of("lan") {
        Some(lan_name) => {
            info!("Using {} as LAN device.", lan_name);
            UnixNetworkDevice::new(&lan_name.to_string()).ok()
        }
        None => None,
    };
    let _bri_device = match matches.value_of("bri") {
        Some(bri_name) => {
            info!("Using {} as BRIDGE device.", bri_name);
            UnixNetworkDevice::new(&bri_name.to_string()).ok()
        }
        None => None,
    };

println!("wan_device {:?}", _wan_device);
println!("lan_device {:?}", _lan_device);
println!("bri_device {:?}", _bri_device);

            // Interfaces

println!("Wan_Interface");

            let _wan_interface: Option<OpnFiInformNetworkInterface> = match &mut _wan_device.as_ref()
            {
                Some(wan) => Some(wan.clone().into()),
                _ => None,
            };

println!("Lan_Interface");

            let _lan_interface: Option<OpnFiInformNetworkInterface> = match &mut _lan_device.as_ref()
            {
                Some(lan) => Some(lan.clone().into()),
                _ => None,
            };

println!("Bri_Interface");

            let _bri_interface: Option<OpnFiInformNetworkInterface> = match &mut _bri_device.as_ref()
            {
                Some(bri) => Some(bri.clone().into()),
                _ => None,
            };
}
[package]
name = "test_opnfi"
version = "0.0.1"
authors = ["Mike Schaffner <mcschaffner@gmail.com>"]
edition = "2018"
license = "Apache-2.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
lazy_static = "1.4.0"
toml = "0.5.5"
regex = "1.3.1"
rand = "0.7.2"
net2 = "0.2.33"
byteorder = "1.3.2"
serde = "1.0.103"
serde_json = "1.0.42"
sysinfo = "0.9.6"
reqwest = "0.9"
hex = "0.4.0"
pnet = "0.23.0"
clap = "2.33"
ctrlc = "3.1.3"
log = "0.4.8"
simple_logger = "1.3.0"

标签: rust

解决方案


您的问题涉及许多特定于生锈的问题。类型、类型的大小、生存时间、可选值等等。

请阅读 Shepmaster 的评论并更新您的问题。给你一个可能有帮助的方向:https ://gist.github.com/rust-play/2289bbcf50caabe7bc0fb397e073e581

// #[derive(Clone)] dosn't work because the unknown size of `value`
struct A {
    value: [isize],
}

#[derive(Clone)]
struct B<'a> {
    value: Option<&'a [isize]>,
}

fn main() {
    //let a: A = A { value: [1] };
    //let a1 = a.clone();
    let b: B = B { value: None };
    let b1 = b.clone();
}

推荐阅读