diff options
author | Mohit Mayank <mohit.ritanil@gmail.com> | 2023-11-01 11:13:32 -0700 |
---|---|---|
committer | Facebook GitHub Bot <facebook-github-bot@users.noreply.github.com> | 2023-11-01 11:13:32 -0700 |
commit | 7fc6f7f7e1bdee0716a49bf2ab4a44e1d3623dc7 (patch) | |
tree | 70acf57393024262b716998f709a0ea982058412 | |
parent | 6b64f9f0c0ac00c4e1b493a2499b26c443b744ce (diff) |
Add support for recording ethtool stats (#8204)
Summary:
The PR is broken into modular commits, so it might be easier to review the PR commit-by-commit.
Commit: [Add crate for reading NIC stats using ethtool](https://github.com/facebookincubator/below/commit/965795c0c1b2675cc22cf781a80f5e06cdbea50a)
- Adds the library support to read and parse `ethtool` stats using `ioctl`
- Parses some common stats into fields while others are stored as raw stats
Commit: [Add model to represent ethtool stats](https://github.com/facebookincubator/below/commit/66dbb26ab612d3f87131aabbbf6bd33112234c90)
- Adds the model struct and parsing logic to convert `ethtool` stats into a sub-model in existing `network.interface` model
Commit: [Add render configs for ethtool fields](https://github.com/facebookincubator/below/commit/9401fed6922f1c843d1ae0ed3e1949cf37b26fad)
- Updates and adds the required commands for dumping new fields and model
- Adds the parsing for rendering the new fields and model
Commit: [Add ability for printing raw_stats in OpenMetrics format](https://github.com/facebookincubator/below/commit/7d6349efa2d1d72795d3e6a18417384e44113fb2)
- Modifies the printing logic of `OpenMetrics` format to handle a map type
Pull Request resolved: https://github.com/facebookincubator/below/pull/8204
Reviewed By: brianc118
Differential Revision: D50714625
Pulled By: dschatzberg
fbshipit-source-id: 156f3054944d86fe489bd9ca5bfb3023d1e02c26
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | below/config/src/lib.rs | 2 | ||||
-rw-r--r-- | below/dump/src/command.rs | 106 | ||||
-rw-r--r-- | below/dump/src/ethtool.rs | 110 | ||||
-rw-r--r-- | below/dump/src/lib.rs | 39 | ||||
-rw-r--r-- | below/dump/src/test.rs | 160 | ||||
-rw-r--r-- | below/ethtool/Cargo.toml | 15 | ||||
-rw-r--r-- | below/ethtool/src/errors.rs | 34 | ||||
-rw-r--r-- | below/ethtool/src/ethtool_sys.rs | 4866 | ||||
-rw-r--r-- | below/ethtool/src/lib.rs | 134 | ||||
-rw-r--r-- | below/ethtool/src/reader.rs | 325 | ||||
-rw-r--r-- | below/ethtool/src/test.rs | 81 | ||||
-rw-r--r-- | below/ethtool/src/types.rs | 27 | ||||
-rw-r--r-- | below/model/Cargo.toml | 1 | ||||
-rw-r--r-- | below/model/src/cgroup.rs | 6 | ||||
-rw-r--r-- | below/model/src/collector.rs | 14 | ||||
-rw-r--r-- | below/model/src/common_field_ids.rs | 13 | ||||
-rw-r--r-- | below/model/src/lib.rs | 41 | ||||
-rw-r--r-- | below/model/src/network.rs | 474 | ||||
-rw-r--r-- | below/model/src/sample.rs | 1 | ||||
-rw-r--r-- | below/model/src/sample_model.rs | 72 | ||||
-rw-r--r-- | below/render/src/default_configs.rs | 73 | ||||
-rw-r--r-- | below/render/src/lib.rs | 20 | ||||
-rw-r--r-- | below/src/main.rs | 2 |
24 files changed, 6525 insertions, 92 deletions
@@ -7,6 +7,7 @@ members = [ "below/common", "below/config", "below/dump", + "below/ethtool", "below/gpu_stats", "below/model", "below/procfs", diff --git a/below/config/src/lib.rs b/below/config/src/lib.rs index 11cde69e..a713cd25 100644 --- a/below/config/src/lib.rs +++ b/below/config/src/lib.rs @@ -45,6 +45,7 @@ pub struct BelowConfig { pub enable_btrfs_stats: bool, pub btrfs_samples: u64, pub btrfs_min_pct: f64, + pub enable_ethtool_stats: bool, } impl Default for BelowConfig { @@ -59,6 +60,7 @@ impl Default for BelowConfig { enable_btrfs_stats: false, btrfs_samples: btrfs::DEFAULT_SAMPLES, btrfs_min_pct: btrfs::DEFAULT_MIN_PCT, + enable_ethtool_stats: false, } } } diff --git a/below/dump/src/command.rs b/below/dump/src/command.rs index 1eba0621..b633e72c 100644 --- a/below/dump/src/command.rs +++ b/below/dump/src/command.rs @@ -25,6 +25,7 @@ use model::SingleCgroupModelFieldId; use model::SingleDiskModelFieldId; use model::SingleNetModelFieldId; use model::SingleProcessModelFieldId; +use model::SingleQueueModelFieldId; use model::SystemModelFieldId; use once_cell::sync::Lazy; use regex::Regex; @@ -664,11 +665,13 @@ pub enum IfaceAggField { Rate, Rx, Tx, + Ethtool, } impl AggField<SingleNetModelFieldId> for IfaceAggField { - fn expand(&self, _detail: bool) -> Vec<SingleNetModelFieldId> { + fn expand(&self, detail: bool) -> Vec<SingleNetModelFieldId> { use model::SingleNetModelFieldId::*; + match self { Self::Rate => vec![ RxBytesPerSec, @@ -703,6 +706,13 @@ impl AggField<SingleNetModelFieldId> for IfaceAggField { TxPackets, TxWindowErrors, ], + Self::Ethtool => { + let mut fields = vec![TxTimeoutPerSec]; + if detail { + fields.push(RawStats); + } + fields + } } } } @@ -717,6 +727,7 @@ pub static DEFAULT_IFACE_FIELDS: &[IfaceOptionField] = &[ DumpOptionField::Agg(IfaceAggField::Rate), DumpOptionField::Agg(IfaceAggField::Rx), DumpOptionField::Agg(IfaceAggField::Tx), + DumpOptionField::Agg(IfaceAggField::Ethtool), DumpOptionField::Unit(DumpField::Common(CommonField::Timestamp)), ]; @@ -739,7 +750,9 @@ static IFACE_LONG_ABOUT: Lazy<String> = Lazy::new(|| { * tx: includes [{agg_tx_fields}]. -* --detail: no effect. +* ethtool: includes [{agg_ethtool_fields}]. + +* --detail: includes `raw_stats` field. * --default: includes [{default_fields}]. @@ -763,6 +776,7 @@ $ below dump iface -b "08:30:00" -e "08:30:30" -s interface -F eth* -O json agg_rate_fields = join(IfaceAggField::Rate.expand(false)), agg_rx_fields = join(IfaceAggField::Rx.expand(false)), agg_tx_fields = join(IfaceAggField::Tx.expand(false)), + agg_ethtool_fields = join(IfaceAggField::Ethtool.expand(false)), default_fields = join(DEFAULT_IFACE_FIELDS.to_owned()), ) }); @@ -941,6 +955,83 @@ $ below dump transport -b "08:30:00" -e "08:30:30" -f tcp udp -O json ) }); +/// Represents the ethtool queue sub-model of the network model. +#[derive( + Clone, + Debug, + PartialEq, + below_derive::EnumFromStr, + below_derive::EnumToString +)] +pub enum EthtoolQueueAggField { + Queue, +} + +impl AggField<SingleQueueModelFieldId> for EthtoolQueueAggField { + fn expand(&self, detail: bool) -> Vec<SingleQueueModelFieldId> { + use model::SingleQueueModelFieldId::*; + let mut fields = vec![ + Interface, + QueueId, + RxBytesPerSec, + TxBytesPerSec, + RxCountPerSec, + TxCountPerSec, + TxMissedTx, + TxUnmaskInterrupt, + ]; + if detail { + fields.push(RawStats); + } + match self { + Self::Queue => fields, + } + } +} + +pub type EthtoolQueueOptionField = DumpOptionField<SingleQueueModelFieldId, EthtoolQueueAggField>; + +pub static DEFAULT_ETHTOOL_QUEUE_FIELDS: &[EthtoolQueueOptionField] = &[ + DumpOptionField::Unit(DumpField::Common(CommonField::Datetime)), + DumpOptionField::Agg(EthtoolQueueAggField::Queue), + DumpOptionField::Unit(DumpField::Common(CommonField::Timestamp)), +]; + +const ETHTOOL_QUEUE_ABOUT: &str = "Dump network interface queue stats"; + +/// Generated about message for Ethtool Queue dump so supported fields are up-to-date. +static ETHTOOL_QUEUE_LONG_ABOUT: Lazy<String> = Lazy::new(|| { + format!( + r#"{about} + +********************** Available fields ********************** + +{common_fields}, and expanded fields below. + +********************** Aggregated fields ********************** + +* queue: includes [{agg_queue_fields}]. + +* --detail: includes `raw_stats` field. + +* --default: includes [{default_fields}]. + +* --everything: includes everything (equivalent to --default --detail). + +********************** Example Commands ********************** + +Example: + +$ below dump ethtool-queue -b "08:30:00" -e "08:30:30" -O json + +"#, + about = ETHTOOL_QUEUE_ABOUT, + common_fields = join(enum_iterator::all::<CommonField>()), + agg_queue_fields = join(EthtoolQueueAggField::Queue.expand(false)), + default_fields = join(DEFAULT_ETHTOOL_QUEUE_FIELDS.to_owned()), + ) +}); + make_option! (OutputFormat { "raw": Raw, "csv": Csv, @@ -1113,4 +1204,15 @@ pub enum DumpCommand { #[clap(long, short, conflicts_with("fields"))] pattern: Option<String>, }, + #[clap(about = ETHTOOL_QUEUE_ABOUT, long_about = ETHTOOL_QUEUE_LONG_ABOUT.as_str())] + EthtoolQueue { + /// Select which fields to display and in what order. + #[clap(short, long, num_args = 1..)] + fields: Option<Vec<EthtoolQueueOptionField>>, + #[clap(flatten)] + opts: GeneralOpt, + /// Saved pattern in the dumprc file under [ethtool] section. + #[clap(long, short, conflicts_with("fields"))] + pattern: Option<String>, + }, } diff --git a/below/dump/src/ethtool.rs b/below/dump/src/ethtool.rs new file mode 100644 index 00000000..9bf37ad3 --- /dev/null +++ b/below/dump/src/ethtool.rs @@ -0,0 +1,110 @@ +use super::*; + +pub struct EthtoolQueue { + opts: GeneralOpt, + fields: Vec<EthtoolQueueField>, +} + +impl EthtoolQueue { + pub fn new(opts: &GeneralOpt, fields: Vec<EthtoolQueueField>) -> Self { + Self { + opts: opts.to_owned(), + fields, + } + } +} + +impl Dumper for EthtoolQueue { + fn dump_model( + &self, + ctx: &CommonFieldContext, + model: &model::Model, + output: &mut dyn Write, + round: &mut usize, + comma_flag: bool, + ) -> Result<IterExecResult> { + let mut queues = Vec::new(); + for nic in model.network.interfaces.values() { + for queue in &nic.queues { + queues.push(queue); + } + } + + // Return if we filtered everything. + if queues.is_empty() { + return Ok(IterExecResult::Skip); + } + + let mut json_output = json!([]); + + queues + .into_iter() + .map(|queue| { + match self.opts.output_format { + Some(OutputFormat::Raw) | None => write!( + output, + "{}", + print::dump_raw( + &self.fields, + ctx, + queue, + *round, + self.opts.repeat_title, + self.opts.disable_title, + self.opts.raw + ) + )?, + Some(OutputFormat::Csv) => write!( + output, + "{}", + print::dump_csv( + &self.fields, + ctx, + queue, + *round, + self.opts.disable_title, + self.opts.raw + ) + )?, + Some(OutputFormat::Tsv) => write!( + output, + "{}", + print::dump_tsv( + &self.fields, + ctx, + queue, + *round, + self.opts.disable_title, + self.opts.raw + ) + )?, + Some(OutputFormat::KeyVal) => write!( + output, + "{}", + print::dump_kv(&self.fields, ctx, queue, self.opts.raw) + )?, + Some(OutputFormat::Json) => { + let par = print::dump_json(&self.fields, ctx, queue, self.opts.raw); + json_output.as_array_mut().unwrap().push(par); + } + Some(OutputFormat::OpenMetrics) => write!( + output, + "{}", + print::dump_openmetrics(&self.fields, ctx, queue) + )?, + } + *round += 1; + Ok(()) + }) + .collect::<Result<Vec<_>>>()?; + + match (self.opts.output_format, comma_flag) { + (Some(OutputFormat::Json), true) => write!(output, ",{}", json_output)?, + (Some(OutputFormat::Json), false) => write!(output, "{}", json_output)?, + (Some(OutputFormat::OpenMetrics), _) => (), + _ => write!(output, "\n")?, + }; + + Ok(IterExecResult::Success) + } +} diff --git a/below/dump/src/lib.rs b/below/dump/src/lib.rs index 541cc1b8..86f3ac01 100644 --- a/below/dump/src/lib.rs +++ b/below/dump/src/lib.rs @@ -46,6 +46,7 @@ pub mod btrfs; pub mod cgroup; pub mod command; pub mod disk; +pub mod ethtool; pub mod iface; pub mod network; pub mod print; @@ -115,6 +116,7 @@ pub type NetworkField = DumpField<model::NetworkModelFieldId>; pub type IfaceField = DumpField<model::SingleNetModelFieldId>; // Essentially the same as NetworkField pub type TransportField = DumpField<model::NetworkModelFieldId>; +pub type EthtoolQueueField = DumpField<model::SingleQueueModelFieldId>; fn get_advance( logger: slog::Logger, @@ -520,5 +522,42 @@ pub fn run( errs, ) } + DumpCommand::EthtoolQueue { + fields, + opts, + pattern, + } => { + let (time_begin, time_end, advance) = + get_advance(logger, dir, host, port, snapshot, &opts)?; + let default = opts.everything || opts.default; + let detail = opts.everything || opts.detail; + let fields = if let Some(pattern_key) = pattern { + parse_pattern(filename, pattern_key, "ethtool_queue") + } else { + fields + }; + let fields = expand_fields( + match fields.as_ref() { + Some(fields) if !default => fields, + _ => command::DEFAULT_ETHTOOL_QUEUE_FIELDS, + }, + detail, + ); + let ethtool = ethtool::EthtoolQueue::new(&opts, fields); + let mut output: Box<dyn Write> = match opts.output.as_ref() { + Some(file_path) => Box::new(File::create(file_path)?), + None => Box::new(io::stdout()), + }; + dump_timeseries( + advance, + time_begin, + time_end, + ðtool, + output.as_mut(), + opts.output_format, + opts.br, + errs, + ) + } } } diff --git a/below/dump/src/test.rs b/below/dump/src/test.rs index 5fcdd8d7..43e5ba67 100644 --- a/below/dump/src/test.rs +++ b/below/dump/src/test.rs @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::collections::BTreeMap; +use std::time::Duration; + use command::expand_fields; use command::GeneralOpt; use command::OutputFormat; @@ -669,6 +672,8 @@ fn test_dump_iface_titles() { "TX Heartbeat Errors", "TX Packets", "TX Window Errors", + "TX Timeout", + "Raw Stats", ]; assert_eq!(titles, expected_titles); } @@ -895,6 +900,161 @@ fn test_dump_transport_titles() { } #[test] +fn test_queue_titles() { + let titles = expand_fields(command::DEFAULT_ETHTOOL_QUEUE_FIELDS, true) + .iter() + .filter_map(|dump_field| match dump_field { + DumpField::Common(_) => None, + DumpField::FieldId(field_id) => Some(field_id.to_string()), + }) + .collect::<Vec<_>>(); + let expected_titles = vec![ + "interface", + "queue_id", + "rx_bytes_per_sec", + "tx_bytes_per_sec", + "rx_count_per_sec", + "tx_count_per_sec", + "tx_missed_tx", + "tx_unmask_interrupt", + "raw_stats", + ]; + assert_eq!(titles, expected_titles); +} + +#[test] +fn test_dump_queue_content() { + let eth0_queue_models = vec![ + model::SingleQueueModel { + interface: "eth0".to_string(), + queue_id: 0, + rx_bytes_per_sec: Some(10), + tx_bytes_per_sec: Some(20), + rx_count_per_sec: Some(100), + tx_count_per_sec: Some(200), + tx_missed_tx: Some(50), + tx_unmask_interrupt: Some(5), + raw_stats: BTreeMap::from([("stat1".to_string(), 1000), ("stat2".to_string(), 2000)]), + }, + model::SingleQueueModel { + interface: "eth0".to_string(), + queue_id: 1, + rx_bytes_per_sec: Some(20), + tx_bytes_per_sec: Some(10), + rx_count_per_sec: Some(200), + tx_count_per_sec: Some(100), + tx_missed_tx: Some(5), + tx_unmask_interrupt: Some(50), + raw_stats: BTreeMap::from([("stat1".to_string(), 2000), ("stat2".to_string(), 1000)]), + }, + ]; + let lo_queue_models = vec![model::SingleQueueModel { + interface: "lo".to_string(), + queue_id: 1, + rx_bytes_per_sec: Some(20), + tx_bytes_per_sec: Some(10), + rx_count_per_sec: Some(200), + tx_count_per_sec: Some(100), + tx_missed_tx: Some(5), + tx_unmask_interrupt: Some(50), + raw_stats: BTreeMap::from([("stat1".to_string(), 2000), ("stat2".to_string(), 1000)]), + }]; + + let eth0_model = model::SingleNetModel { + interface: "eth0".to_string(), + queues: eth0_queue_models.clone(), + ..Default::default() + }; + let lo_model = model::SingleNetModel { + interface: "lo".to_string(), + queues: lo_queue_models.clone(), + ..Default::default() + }; + let network = model::NetworkModel { + interfaces: BTreeMap::from([ + ("eth0".to_string(), eth0_model), + ("lo".to_string(), lo_model), + ]), + ..Default::default() + }; + let model = model::Model { + time_elapsed: Duration::from_secs(60 * 10), + timestamp: SystemTime::now(), + system: model::SystemModel::default(), + cgroup: model::CgroupModel::default(), + process: model::ProcessModel::default(), + network, + gpu: None, + }; + + let mut opts: GeneralOpt = Default::default(); + let fields = command::expand_fields(command::DEFAULT_ETHTOOL_QUEUE_FIELDS, true); + + opts.output_format = Some(OutputFormat::Json); + let queue_dumper = ethtool::EthtoolQueue::new(&opts, fields.clone()); + + let mut queue_content: Vec<u8> = Vec::new(); + let mut round = 0; + let ctx = CommonFieldContext { + timestamp: 0, + hostname: "h".to_string(), + }; + + let result = queue_dumper + .dump_model(&ctx, &model, &mut queue_content, &mut round, false) + .expect("Failed to dump queue model"); + assert!(result == tmain::IterExecResult::Success); + + // verify json correctness + assert!(!queue_content.is_empty()); + let jval: Value = + serde_json::from_slice(&queue_content).expect("Fail parse json of queue dump"); + + let expected_json = json!([ + { + "Datetime": "1969-12-31 16:00:00", + "Interface": "eth0", + "Queue": "0", + "RawStats": "stat1=1000, stat2=2000", + "RxBytes": "10 B/s", + "RxCount": "100/s", + "Timestamp": "0", + "TxBytes": "20 B/s", + "TxCount": "200/s", + "TxMissedTx": "50", + "TxUnmaskInterrupt": "5" + }, + { + "Datetime": "1969-12-31 16:00:00", + "Interface": "eth0", + "Queue": "1", + "RawStats": "stat1=2000, stat2=1000", + "RxBytes": "20 B/s", + "RxCount": "200/s", + "Timestamp": "0", + "TxBytes": "10 B/s", + "TxCount": "100/s", + "TxMissedTx": "5", + "TxUnmaskInterrupt": "50" + }, + { + "Datetime": "1969-12-31 16:00:00", + "Interface": "lo", + "Queue": "1", + "RawStats": "stat1=2000, stat2=1000", + "RxBytes": "20 B/s", + "RxCount": "200/s", + "Timestamp": "0", + "TxBytes": "10 B/s", + "TxCount": "100/s", + "TxMissedTx": "5", + "TxUnmaskInterrupt": "50" + } + ]); + assert_eq!(jval, expected_json); +} + +#[test] // Test correctness of disk decoration // This test will also test JSON correctness. fn test_dump_disk_content() { diff --git a/below/ethtool/Cargo.toml b/below/ethtool/Cargo.toml new file mode 100644 index 00000000..29b68595 --- /dev/null +++ b/below/ethtool/Cargo.toml @@ -0,0 +1,15 @@ +# @generated by autocargo + +[package] +name = "below-ethtool" +version = "0.7.1" +authors = ["Daniel Xu <dlxu@fb.com>", "Facebook"] +edition = "2021" +description = "Model crate for below" +repository = "https://github.com/facebookincubator/below" +license = "Apache-2.0" + +[dependencies] +nix = "0.25" +serde = { version = "1.0.185", features = ["derive", "rc"] } +thiserror = "1.0.43" diff --git a/below/ethtool/src/errors.rs b/below/ethtool/src/errors.rs new file mode 100644 index 00000000..2eee8fb8 --- /dev/null +++ b/below/ethtool/src/errors.rs @@ -0,0 +1,34 @@ +use std::alloc; + +use nix::errno::Errno; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum EthtoolError { + #[error("Failed to open a socket, error={0:}")] + SocketError(Errno), + + #[error("Failed to read interface names, error={0:}")] + IfNamesReadError(Errno), + + #[error("Failed to initialize struct, error={0:}")] + CStructInitError(#[from] alloc::LayoutError), + + #[error("Failed to read data from struct pointer")] + CStructReadError(), + + #[error("Failed to read number of stats using ETHTOOL_GSSET_INFO, error={0:}")] + GSSetInfoReadError(Errno), + + #[error("Failed to read names of stats using ETHTOOL_GSTRINGS, error={0:}")] + GStringsReadError(Errno), + + #[error("Failed to read values of stats using ETHTOOL_GSTATS, error={0:}")] + GStatsReadError(Errno), + + #[error("Failed to parse stats, error={0:}")] + ParseError(String), + + #[error("Failed to allocate memory")] + AllocationFailure(), +} diff --git a/below/ethtool/src/ethtool_sys.rs b/below/ethtool/src/ethtool_sys.rs new file mode 100644 index 00000000..a8e9da8f --- /dev/null +++ b/below/ethtool/src/ethtool_sys.rs @@ -0,0 +1,4866 @@ +/* automatically generated by rust-bindgen 0.68.1 */ +#![allow(dead_code, unused_variables)] + +#[repr(C)] +#[derive(Default)] +pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]); +impl<T> __IncompleteArrayField<T> { + #[inline] + pub const fn new() -> Self { + __IncompleteArrayField(::std::marker::PhantomData, []) + } + #[inline] + pub fn as_ptr(&self) -> *const T { + self as *const _ as *const T + } + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + self as *mut _ as *mut T + } + #[inline] + pub unsafe fn as_slice(&self, len: usize) -> &[T] { + ::std::slice::from_raw_parts(self.as_ptr(), len) + } + #[inline] + pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { + ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) + } +} +impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + fmt.write_str("__IncompleteArrayField") + } +} +pub const __BITS_PER_LONG: u32 = 64; +pub const __FD_SETSIZE: u32 = 1024; +pub const ETH_ALEN: u32 = 6; +pub const ETH_TLEN: u32 = 2; +pub const ETH_HLEN: u32 = 14; +pub const ETH_ZLEN: u32 = 60; +pub const ETH_DATA_LEN: u32 = 1500; +pub const ETH_FRAME_LEN: u32 = 1514; +pub const ETH_FCS_LEN: u32 = 4; +pub const ETH_MIN_MTU: u32 = 68; +pub const ETH_MAX_MTU: u32 = 65535; +pub const ETH_P_LOOP: u32 = 96; +pub const ETH_P_PUP: u32 = 512; +pub const ETH_P_PUPAT: u32 = 513; +pub const ETH_P_TSN: u32 = 8944; +pub const ETH_P_ERSPAN2: u32 = 8939; +pub const ETH_P_IP: u32 = 2048; +pub const ETH_P_X25: u32 = 2053; +pub const ETH_P_ARP: u32 = 2054; +pub const ETH_P_BPQ: u32 = 2303; +pub const ETH_P_IEEEPUP: u32 = 2560; +pub const ETH_P_IEEEPUPAT: u32 = 2561; +pub const ETH_P_BATMAN: u32 = 17157; +pub const ETH_P_DEC: u32 = 24576; +pub const ETH_P_DNA_DL: u32 = 24577; +pub const ETH_P_DNA_RC: u32 = 24578; +pub const ETH_P_DNA_RT: u32 = 24579; +pub const ETH_P_LAT: u32 = 24580; +pub const ETH_P_DIAG: u32 = 24581; +pub const ETH_P_CUST: u32 = 24582; +pub const ETH_P_SCA: u32 = 24583; +pub const ETH_P_TEB: u32 = 25944; +pub const ETH_P_RARP: u32 = 32821; +pub const ETH_P_ATALK: u32 = 32923; +pub const ETH_P_AARP: u32 = 33011; +pub const ETH_P_8021Q: u32 = 33024; +pub const ETH_P_ERSPAN: u32 = 35006; +pub const ETH_P_IPX: u32 = 33079; +pub const ETH_P_IPV6: u32 = 34525; +pub const ETH_P_PAUSE: u32 = 34824; +pub const ETH_P_SLOW: u32 = 34825; +pub const ETH_P_WCCP: u32 = 34878; +pub const ETH_P_MPLS_UC: u32 = 34887; +pub const ETH_P_MPLS_MC: u32 = 34888; +pub const ETH_P_ATMMPOA: u32 = 34892; +pub const ETH_P_PPP_DISC: u32 = 34915; +pub const ETH_P_PPP_SES: u32 = 34916; +pub const ETH_P_LINK_CTL: u32 = 34924; +pub const ETH_P_ATMFATE: u32 = 34948; +pub const ETH_P_PAE: u32 = 34958; +pub const ETH_P_AOE: u32 = 34978; +pub const ETH_P_8021AD: u32 = 34984; +pub const ETH_P_802_EX1: u32 = 34997; +pub const ETH_P_PREAUTH: u32 = 35015; +pub const ETH_P_TIPC: u32 = 35018; +pub const ETH_P_LLDP: u32 = 35020; +pub const ETH_P_MRP: u32 = 35043; +pub const ETH_P_MACSEC: u32 = 35045; +pub const ETH_P_8021AH: u32 = 35047; +pub const ETH_P_MVRP: u32 = 35061; +pub const ETH_P_1588: u32 = 35063; +pub const ETH_P_NCSI: u32 = 35064; +pub const ETH_P_PRP: u32 = 35067; +pub const ETH_P_CFM: u32 = 35074; +pub const ETH_P_FCOE: u32 = 35078; +pub const ETH_P_IBOE: u32 = 35093; +pub const ETH_P_TDLS: u32 = 35085; +pub const ETH_P_FIP: u32 = 35092; +pub const ETH_P_80221: u32 = 35095; +pub const ETH_P_HSR: u32 = 35119; +pub const ETH_P_NSH: u32 = 35151; +pub const ETH_P_LOOPBACK: u32 = 36864; +pub const ETH_P_QINQ1: u32 = 37120; +pub const ETH_P_QINQ2: u32 = 37376; +pub const ETH_P_QINQ3: u32 = 37632; +pub const ETH_P_EDSA: u32 = 56026; +pub const ETH_P_DSA_8021Q: u32 = 56027; +pub const ETH_P_IFE: u32 = 60734; +pub const ETH_P_AF_IUCV: u32 = 64507; +pub const ETH_P_802_3_MIN: u32 = 1536; +pub const ETH_P_802_3: u32 = 1; +pub const ETH_P_AX25: u32 = 2; +pub const ETH_P_ALL: u32 = 3; +pub const ETH_P_802_2: u32 = 4; +pub const ETH_P_SNAP: u32 = 5; +pub const ETH_P_DDCMP: u32 = 6; +pub const ETH_P_WAN_PPP: u32 = 7; +pub const ETH_P_PPP_MP: u32 = 8; +pub const ETH_P_LOCALTALK: u32 = 9; +pub const ETH_P_CAN: u32 = 12; +pub const ETH_P_CANFD: u32 = 13; +pub const ETH_P_PPPTALK: u32 = 16; +pub const ETH_P_TR_802_2: u32 = 17; +pub const ETH_P_MOBITEX: u32 = 21; +pub const ETH_P_CONTROL: u32 = 22; +pub const ETH_P_IRDA: u32 = 23; +pub const ETH_P_ECONET: u32 = 24; +pub const ETH_P_HDLC: u32 = 25; +pub const ETH_P_ARCNET: u32 = 26; +pub const ETH_P_DSA: u32 = 27; +pub const ETH_P_TRAILER: u32 = 28; +pub const ETH_P_PHONET: u32 = 245; +pub const ETH_P_IEEE802154: u32 = 246; +pub const ETH_P_CAIF: u32 = 247; +pub const ETH_P_XDSA: u32 = 248; +pub const ETH_P_MAP: u32 = 249; +pub const __UAPI_DEF_ETHHDR: u32 = 1; +pub const _LIBC_LIMITS_H_: u32 = 1; +pub const _FEATURES_H: u32 = 1; +pub const _DEFAULT_SOURCE: u32 = 1; +pub const __USE_ISOC11: u32 = 1; +pub const __USE_ISOC99: u32 = 1; +pub const __USE_ISOC95: u32 = 1; +pub const __USE_POSIX_IMPLICITLY: u32 = 1; +pub const _POSIX_SOURCE: u32 = 1; +pub const _POSIX_C_SOURCE: u32 = 200809; +pub const __USE_POSIX: u32 = 1; +pub const __USE_POSIX2: u32 = 1; +pub const __USE_POSIX199309: u32 = 1; +pub const __USE_POSIX199506: u32 = 1; +pub const __USE_XOPEN2K: u32 = 1; +pub const __USE_XOPEN2K8: u32 = 1; +pub const _ATFILE_SOURCE: u32 = 1; +pub const __USE_MISC: u32 = 1; +pub const __USE_ATFILE: u32 = 1; +pub const __USE_FORTIFY_LEVEL: u32 = 0; +pub const __GLIBC_USE_DEPRECATED_GETS: u32 = 0; +pub const _STDC_PREDEF_H: u32 = 1; +pub const __STDC_IEC_559__: u32 = 1; +pub const __STDC_IEC_559_COMPLEX__: u32 = 1; +pub const __STDC_ISO_10646__: u32 = 201706; +pub const __GNU_LIBRARY__: u32 = 6; +pub const __GLIBC__: u32 = 2; +pub const __GLIBC_MINOR__: u32 = 28; +pub const _SYS_CDEFS_H: u32 = 1; +pub const __glibc_c99_flexarr_available: u32 = 1; +pub const __WORDSIZE: u32 = 64; +pub const __WORDSIZE_TIME64_COMPAT32: u32 = 1; +pub const __SYSCALL_WORDSIZE: u32 = 64; +pub const __HAVE_GENERIC_SELECTION: u32 = 1; +pub const __GLIBC_USE_LIB_EXT2: u32 = 0; +pub const __GLIBC_USE_IEC_60559_BFP_EXT: u32 = 0; +pub const __GLIBC_USE_IEC_60559_FUNCS_EXT: u32 = 0; +pub const __GLIBC_USE_IEC_60559_TYPES_EXT: u32 = 0; +pub const MB_LEN_MAX: u32 = 16; +pub const _BITS_POSIX1_LIM_H: u32 = 1; +pub const _POSIX_AIO_LISTIO_MAX: u32 = 2; +pub const _POSIX_AIO_MAX: u32 = 1; +pub const _POSIX_ARG_MAX: u32 = 4096; +pub const _POSIX_CHILD_MAX: u32 = 25; +pub const _POSIX_DELAYTIMER_MAX: u32 = 32; +pub const _POSIX_HOST_NAME_MAX: u32 = 255; +pub const _POSIX_LINK_MAX: u32 = 8; +pub const _POSIX_LOGIN_NAME_MAX: u32 = 9; +pub const _POSIX_MAX_CANON: u32 = 255; +pub const _POSIX_MAX_INPUT: u32 = 255; +pub const _POSIX_MQ_OPEN_MAX: u32 = 8; +p |