procfs_core/
net.rs

1// Don't throw clippy warnings for manual string stripping.
2// The suggested fix with `strip_prefix` removes support for Rust 1.33 and 1.38
3#![allow(clippy::manual_strip)]
4
5//! Information about the networking layer.
6//!
7//! This module corresponds to the `/proc/net` directory and contains various information about the
8//! networking layer.
9use crate::ProcResult;
10use crate::{build_internal_error, expect, from_iter, from_str};
11use std::collections::HashMap;
12
13use bitflags::bitflags;
14use std::io::BufRead;
15use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
16use std::{path::PathBuf, str::FromStr};
17
18#[cfg(feature = "serde1")]
19use serde::{Deserialize, Serialize};
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
23pub enum TcpState {
24    Established = 1,
25    SynSent,
26    SynRecv,
27    FinWait1,
28    FinWait2,
29    TimeWait,
30    Close,
31    CloseWait,
32    LastAck,
33    Listen,
34    Closing,
35    NewSynRecv,
36}
37
38impl TcpState {
39    pub fn from_u8(num: u8) -> Option<TcpState> {
40        match num {
41            0x01 => Some(TcpState::Established),
42            0x02 => Some(TcpState::SynSent),
43            0x03 => Some(TcpState::SynRecv),
44            0x04 => Some(TcpState::FinWait1),
45            0x05 => Some(TcpState::FinWait2),
46            0x06 => Some(TcpState::TimeWait),
47            0x07 => Some(TcpState::Close),
48            0x08 => Some(TcpState::CloseWait),
49            0x09 => Some(TcpState::LastAck),
50            0x0A => Some(TcpState::Listen),
51            0x0B => Some(TcpState::Closing),
52            0x0C => Some(TcpState::NewSynRecv),
53            _ => None,
54        }
55    }
56
57    pub fn to_u8(&self) -> u8 {
58        match self {
59            TcpState::Established => 0x01,
60            TcpState::SynSent => 0x02,
61            TcpState::SynRecv => 0x03,
62            TcpState::FinWait1 => 0x04,
63            TcpState::FinWait2 => 0x05,
64            TcpState::TimeWait => 0x06,
65            TcpState::Close => 0x07,
66            TcpState::CloseWait => 0x08,
67            TcpState::LastAck => 0x09,
68            TcpState::Listen => 0x0A,
69            TcpState::Closing => 0x0B,
70            TcpState::NewSynRecv => 0x0C,
71        }
72    }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
76#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
77pub enum UdpState {
78    Established = 1,
79    Close = 7,
80}
81
82impl UdpState {
83    pub fn from_u8(num: u8) -> Option<UdpState> {
84        match num {
85            0x01 => Some(UdpState::Established),
86            0x07 => Some(UdpState::Close),
87            _ => None,
88        }
89    }
90
91    pub fn to_u8(&self) -> u8 {
92        match self {
93            UdpState::Established => 0x01,
94            UdpState::Close => 0x07,
95        }
96    }
97}
98
99#[derive(Debug, Clone, PartialEq, Eq)]
100#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
101pub enum UnixState {
102    UNCONNECTED = 1,
103    CONNECTING = 2,
104    CONNECTED = 3,
105    DISCONNECTING = 4,
106}
107
108impl UnixState {
109    pub fn from_u8(num: u8) -> Option<UnixState> {
110        match num {
111            0x01 => Some(UnixState::UNCONNECTED),
112            0x02 => Some(UnixState::CONNECTING),
113            0x03 => Some(UnixState::CONNECTED),
114            0x04 => Some(UnixState::DISCONNECTING),
115            _ => None,
116        }
117    }
118
119    pub fn to_u8(&self) -> u8 {
120        match self {
121            UnixState::UNCONNECTED => 0x01,
122            UnixState::CONNECTING => 0x02,
123            UnixState::CONNECTED => 0x03,
124            UnixState::DISCONNECTING => 0x04,
125        }
126    }
127}
128
129/// An entry in the TCP socket table
130#[derive(Debug, Clone)]
131#[non_exhaustive]
132#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
133pub struct TcpNetEntry {
134    pub local_address: SocketAddr,
135    pub remote_address: SocketAddr,
136    pub state: TcpState,
137    pub rx_queue: u32,
138    pub tx_queue: u32,
139    pub uid: u32,
140    pub inode: u64,
141}
142
143/// An entry in the UDP socket table
144#[derive(Debug, Clone)]
145#[non_exhaustive]
146#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
147pub struct UdpNetEntry {
148    pub local_address: SocketAddr,
149    pub remote_address: SocketAddr,
150    pub state: UdpState,
151    pub rx_queue: u32,
152    pub tx_queue: u32,
153    pub uid: u32,
154    pub inode: u64,
155}
156
157/// An entry in the Unix socket table
158#[derive(Debug, Clone)]
159#[non_exhaustive]
160#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
161pub struct UnixNetEntry {
162    /// The number of users of the socket
163    pub ref_count: u32,
164    /// The socket type.
165    ///
166    /// Possible values are `SOCK_STREAM`, `SOCK_DGRAM`, or `SOCK_SEQPACKET`.  These constants can
167    /// be found in the libc crate.
168    pub socket_type: u16,
169    /// The state of the socket
170    pub state: UnixState,
171    /// The inode number of the socket
172    pub inode: u64,
173    /// The bound pathname (if any) of the socket.
174    ///
175    /// Sockets in the abstract namespace are included, and are shown with a path that commences
176    /// with the '@' character.
177    pub path: Option<PathBuf>,
178}
179
180/// Parses an address in the form 00010203:1234
181///
182/// Also supports IPv6
183fn parse_addressport_str(s: &str, little_endian: bool) -> ProcResult<SocketAddr> {
184    let mut las = s.split(':');
185    let ip_part = expect!(las.next(), "ip_part");
186    let port = expect!(las.next(), "port");
187    let port = from_str!(u16, port, 16);
188
189    use std::convert::TryInto;
190
191    let read_u32 = if little_endian {
192        u32::from_le_bytes
193    } else {
194        u32::from_be_bytes
195    };
196
197    if ip_part.len() == 8 {
198        let bytes = expect!(hex::decode(ip_part));
199        let ip_u32 = read_u32(bytes[..4].try_into().unwrap());
200
201        let ip = Ipv4Addr::from(ip_u32);
202
203        Ok(SocketAddr::V4(SocketAddrV4::new(ip, port)))
204    } else if ip_part.len() == 32 {
205        let bytes = expect!(hex::decode(ip_part));
206
207        let ip_a = read_u32(bytes[0..4].try_into().unwrap());
208        let ip_b = read_u32(bytes[4..8].try_into().unwrap());
209        let ip_c = read_u32(bytes[8..12].try_into().unwrap());
210        let ip_d = read_u32(bytes[12..16].try_into().unwrap());
211
212        let ip = Ipv6Addr::new(
213            ((ip_a >> 16) & 0xffff) as u16,
214            (ip_a & 0xffff) as u16,
215            ((ip_b >> 16) & 0xffff) as u16,
216            (ip_b & 0xffff) as u16,
217            ((ip_c >> 16) & 0xffff) as u16,
218            (ip_c & 0xffff) as u16,
219            ((ip_d >> 16) & 0xffff) as u16,
220            (ip_d & 0xffff) as u16,
221        );
222
223        Ok(SocketAddr::V6(SocketAddrV6::new(ip, port, 0, 0)))
224    } else {
225        Err(build_internal_error!(format!(
226            "Unable to parse {:?} as an address:port",
227            s
228        )))
229    }
230}
231
232/// TCP socket entries.
233#[derive(Debug, Clone)]
234#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
235pub struct TcpNetEntries(pub Vec<TcpNetEntry>);
236
237impl super::FromBufReadSI for TcpNetEntries {
238    fn from_buf_read<R: BufRead>(r: R, system_info: &crate::SystemInfo) -> ProcResult<Self> {
239        let mut vec = Vec::new();
240
241        // first line is a header we need to skip
242        for line in r.lines().skip(1) {
243            let line = line?;
244            let mut s = line.split_whitespace();
245            s.next();
246            let local_address = expect!(s.next(), "tcp::local_address");
247            let rem_address = expect!(s.next(), "tcp::rem_address");
248            let state = expect!(s.next(), "tcp::st");
249            let mut tx_rx_queue = expect!(s.next(), "tcp::tx_queue:rx_queue").splitn(2, ':');
250            let tx_queue = from_str!(u32, expect!(tx_rx_queue.next(), "tcp::tx_queue"), 16);
251            let rx_queue = from_str!(u32, expect!(tx_rx_queue.next(), "tcp::rx_queue"), 16);
252            s.next(); // skip tr and tm->when
253            s.next(); // skip retrnsmt
254            let uid = from_str!(u32, expect!(s.next(), "tcp::uid"));
255            s.next(); // skip timeout
256            let inode = expect!(s.next(), "tcp::inode");
257
258            vec.push(TcpNetEntry {
259                local_address: parse_addressport_str(local_address, system_info.is_little_endian())?,
260                remote_address: parse_addressport_str(rem_address, system_info.is_little_endian())?,
261                rx_queue,
262                tx_queue,
263                state: expect!(TcpState::from_u8(from_str!(u8, state, 16))),
264                uid,
265                inode: from_str!(u64, inode),
266            });
267        }
268
269        Ok(TcpNetEntries(vec))
270    }
271}
272
273/// UDP socket entries.
274#[derive(Debug, Clone)]
275#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
276pub struct UdpNetEntries(pub Vec<UdpNetEntry>);
277
278impl super::FromBufReadSI for UdpNetEntries {
279    fn from_buf_read<R: BufRead>(r: R, system_info: &crate::SystemInfo) -> ProcResult<Self> {
280        let mut vec = Vec::new();
281
282        // first line is a header we need to skip
283        for line in r.lines().skip(1) {
284            let line = line?;
285            let mut s = line.split_whitespace();
286            s.next();
287            let local_address = expect!(s.next(), "udp::local_address");
288            let rem_address = expect!(s.next(), "udp::rem_address");
289            let state = expect!(s.next(), "udp::st");
290            let mut tx_rx_queue = expect!(s.next(), "udp::tx_queue:rx_queue").splitn(2, ':');
291            let tx_queue: u32 = from_str!(u32, expect!(tx_rx_queue.next(), "udp::tx_queue"), 16);
292            let rx_queue: u32 = from_str!(u32, expect!(tx_rx_queue.next(), "udp::rx_queue"), 16);
293            s.next(); // skip tr and tm->when
294            s.next(); // skip retrnsmt
295            let uid = from_str!(u32, expect!(s.next(), "udp::uid"));
296            s.next(); // skip timeout
297            let inode = expect!(s.next(), "udp::inode");
298
299            vec.push(UdpNetEntry {
300                local_address: parse_addressport_str(local_address, system_info.is_little_endian())?,
301                remote_address: parse_addressport_str(rem_address, system_info.is_little_endian())?,
302                rx_queue,
303                tx_queue,
304                state: expect!(UdpState::from_u8(from_str!(u8, state, 16))),
305                uid,
306                inode: from_str!(u64, inode),
307            });
308        }
309
310        Ok(UdpNetEntries(vec))
311    }
312}
313
314/// Unix socket entries.
315#[derive(Debug, Clone)]
316#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
317pub struct UnixNetEntries(pub Vec<UnixNetEntry>);
318
319impl super::FromBufRead for UnixNetEntries {
320    fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
321        let mut vec = Vec::new();
322
323        // first line is a header we need to skip
324        for line in r.lines().skip(1) {
325            let line = line?;
326            let mut s = line.split_whitespace();
327            s.next(); // skip table slot number
328            let ref_count = from_str!(u32, expect!(s.next()), 16);
329            s.next(); // skip protocol, always zero
330            s.next(); // skip internal kernel flags
331            let socket_type = from_str!(u16, expect!(s.next()), 16);
332            let state = from_str!(u8, expect!(s.next()), 16);
333            let inode = from_str!(u64, expect!(s.next()));
334            let path = s.next().map(PathBuf::from);
335
336            vec.push(UnixNetEntry {
337                ref_count,
338                socket_type,
339                inode,
340                state: expect!(UnixState::from_u8(state)),
341                path,
342            });
343        }
344
345        Ok(UnixNetEntries(vec))
346    }
347}
348
349/// An entry in the ARP table
350#[derive(Debug, Clone)]
351#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
352pub struct ARPEntry {
353    /// IPv4 address
354    pub ip_address: Ipv4Addr,
355    /// Hardware type
356    ///
357    /// This will almost always be ETHER (or maybe INFINIBAND)
358    pub hw_type: ARPHardware,
359    /// Internal kernel flags
360    pub flags: ARPFlags,
361    /// MAC Address
362    pub hw_address: Option<[u8; 6]>,
363    /// Device name
364    pub device: String,
365}
366
367bitflags! {
368    /// Hardware type for an ARP table entry.
369    // source: include/uapi/linux/if_arp.h
370    #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
371    #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
372    pub struct ARPHardware: u32 {
373        /// NET/ROM pseudo
374        const NETROM = 0;
375        /// Ethernet
376        const ETHER = 1;
377        /// Experimental ethernet
378        const EETHER = 2;
379        /// AX.25 Level 2
380        const AX25 = 3;
381        /// PROnet token ring
382        const PRONET = 4;
383        /// Chaosnet
384        const CHAOS = 5;
385        /// IEEE 802.2 Ethernet/TR/TB
386        const IEEE802 = 6;
387        /// Arcnet
388        const ARCNET = 7;
389        /// APPLEtalk
390        const APPLETLK = 8;
391        /// Frame Relay DLCI
392        const DLCI = 15;
393        /// ATM
394        const ATM = 19;
395        /// Metricom STRIP
396        const METRICOM = 23;
397        //// IEEE 1394 IPv4 - RFC 2734
398        const IEEE1394 = 24;
399        /// EUI-64
400        const EUI64 = 27;
401        /// InfiniBand
402        const INFINIBAND = 32;
403    }
404}
405
406bitflags! {
407    /// Flags for ARP entries
408    // source: include/uapi/linux/if_arp.h
409    #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
410    #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
411    pub struct ARPFlags: u32 {
412            /// Completed entry
413            const COM = 0x02;
414            /// Permanent entry
415            const PERM = 0x04;
416            /// Publish entry
417            const PUBL = 0x08;
418            /// Has requested trailers
419            const USETRAILERS = 0x10;
420            /// Want to use a netmask (only for proxy entries)
421            const NETMASK = 0x20;
422            // Don't answer this address
423            const DONTPUB = 0x40;
424    }
425}
426
427/// ARP table entries.
428#[derive(Debug, Clone)]
429#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
430pub struct ArpEntries(pub Vec<ARPEntry>);
431
432impl super::FromBufRead for ArpEntries {
433    fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
434        let mut vec = Vec::new();
435
436        // First line is a header we need to skip
437        for line in r.lines().skip(1) {
438            // Check if there might have been an IO error.
439            let line = line?;
440            let mut line = line.split_whitespace();
441            let ip_address = expect!(Ipv4Addr::from_str(expect!(line.next())));
442            let hw = from_str!(u32, &expect!(line.next())[2..], 16);
443            let hw = ARPHardware::from_bits_truncate(hw);
444            let flags = from_str!(u32, &expect!(line.next())[2..], 16);
445            let flags = ARPFlags::from_bits_truncate(flags);
446
447            let mac = expect!(line.next());
448            let mut mac: Vec<Result<u8, _>> = mac.split(':').map(|s| Ok(from_str!(u8, s, 16))).collect();
449
450            let mac = if mac.len() == 6 {
451                let mac_block_f = mac.pop().unwrap()?;
452                let mac_block_e = mac.pop().unwrap()?;
453                let mac_block_d = mac.pop().unwrap()?;
454                let mac_block_c = mac.pop().unwrap()?;
455                let mac_block_b = mac.pop().unwrap()?;
456                let mac_block_a = mac.pop().unwrap()?;
457                if mac_block_a == 0
458                    && mac_block_b == 0
459                    && mac_block_c == 0
460                    && mac_block_d == 0
461                    && mac_block_e == 0
462                    && mac_block_f == 0
463                {
464                    None
465                } else {
466                    Some([
467                        mac_block_a,
468                        mac_block_b,
469                        mac_block_c,
470                        mac_block_d,
471                        mac_block_e,
472                        mac_block_f,
473                    ])
474                }
475            } else {
476                None
477            };
478
479            // mask is always "*"
480            let _mask = expect!(line.next());
481            let dev = expect!(line.next());
482
483            vec.push(ARPEntry {
484                ip_address,
485                hw_type: hw,
486                flags,
487                hw_address: mac,
488                device: dev.to_string(),
489            })
490        }
491
492        Ok(ArpEntries(vec))
493    }
494}
495
496/// General statistics for a network interface/device
497///
498/// For an example, see the [interface_stats.rs](https://github.com/eminence/procfs/tree/master/examples)
499/// example in the source repo.
500#[derive(Debug, Clone)]
501#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
502pub struct DeviceStatus {
503    /// Name of the interface
504    pub name: String,
505    /// Total bytes received
506    pub recv_bytes: u64,
507    /// Total packets received
508    pub recv_packets: u64,
509    /// Bad packets received
510    pub recv_errs: u64,
511    /// Packets dropped
512    pub recv_drop: u64,
513    /// Fifo overrun
514    pub recv_fifo: u64,
515    /// Frame alignment errors
516    pub recv_frame: u64,
517    /// Number of compressed packets received
518    pub recv_compressed: u64,
519    /// Number of multicast packets received
520    pub recv_multicast: u64,
521
522    /// Total bytes transmitted
523    pub sent_bytes: u64,
524    /// Total packets transmitted
525    pub sent_packets: u64,
526    /// Number of transmission errors
527    pub sent_errs: u64,
528    /// Number of packets dropped during transmission
529    pub sent_drop: u64,
530    pub sent_fifo: u64,
531    /// Number of collisions
532    pub sent_colls: u64,
533    /// Number of packets not sent due to carrier errors
534    pub sent_carrier: u64,
535    /// Number of compressed packets transmitted
536    pub sent_compressed: u64,
537}
538
539impl DeviceStatus {
540    fn from_str(s: &str) -> ProcResult<DeviceStatus> {
541        let mut split = s.split_whitespace();
542        let name: String = expect!(from_iter(&mut split));
543        let recv_bytes = expect!(from_iter(&mut split));
544        let recv_packets = expect!(from_iter(&mut split));
545        let recv_errs = expect!(from_iter(&mut split));
546        let recv_drop = expect!(from_iter(&mut split));
547        let recv_fifo = expect!(from_iter(&mut split));
548        let recv_frame = expect!(from_iter(&mut split));
549        let recv_compressed = expect!(from_iter(&mut split));
550        let recv_multicast = expect!(from_iter(&mut split));
551        let sent_bytes = expect!(from_iter(&mut split));
552        let sent_packets = expect!(from_iter(&mut split));
553        let sent_errs = expect!(from_iter(&mut split));
554        let sent_drop = expect!(from_iter(&mut split));
555        let sent_fifo = expect!(from_iter(&mut split));
556        let sent_colls = expect!(from_iter(&mut split));
557        let sent_carrier = expect!(from_iter(&mut split));
558        let sent_compressed = expect!(from_iter(&mut split));
559
560        Ok(DeviceStatus {
561            name: name.trim_end_matches(':').to_owned(),
562            recv_bytes,
563            recv_packets,
564            recv_errs,
565            recv_drop,
566            recv_fifo,
567            recv_frame,
568            recv_compressed,
569            recv_multicast,
570            sent_bytes,
571            sent_packets,
572            sent_errs,
573            sent_drop,
574            sent_fifo,
575            sent_colls,
576            sent_carrier,
577            sent_compressed,
578        })
579    }
580}
581
582/// Device status information for all network interfaces.
583#[derive(Debug, Clone)]
584#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
585pub struct InterfaceDeviceStatus(pub HashMap<String, DeviceStatus>);
586
587impl super::FromBufRead for InterfaceDeviceStatus {
588    fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
589        let mut map = HashMap::new();
590        // the first two lines are headers, so skip them
591        for line in r.lines().skip(2) {
592            let dev = DeviceStatus::from_str(&line?)?;
593            map.insert(dev.name.clone(), dev);
594        }
595
596        Ok(InterfaceDeviceStatus(map))
597    }
598}
599
600/// An entry in the ipv4 route table
601#[derive(Debug, Clone)]
602#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
603pub struct RouteEntry {
604    /// Interface to which packets for this route will be sent
605    pub iface: String,
606    /// The destination network or destination host
607    pub destination: Ipv4Addr,
608    pub gateway: Ipv4Addr,
609    pub flags: u16,
610    /// Number of references to this route
611    pub refcnt: u16,
612    /// Count of lookups for the route
613    pub in_use: u16,
614    /// The 'distance' to the target (usually counted in hops)
615    pub metrics: u32,
616    pub mask: Ipv4Addr,
617    /// Default maximum transmission unit for TCP connections over this route
618    pub mtu: u32,
619    /// Default window size for TCP connections over this route
620    pub window: u32,
621    /// Initial RTT (Round Trip Time)
622    pub irtt: u32,
623}
624
625/// A set of ipv4 routes.
626#[derive(Debug, Clone)]
627#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
628pub struct RouteEntries(pub Vec<RouteEntry>);
629
630impl super::FromBufRead for RouteEntries {
631    fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
632        let mut vec = Vec::new();
633
634        // First line is a header we need to skip
635        for line in r.lines().skip(1) {
636            // Check if there might have been an IO error.
637            let line = line?;
638            let mut line = line.split_whitespace();
639            // network interface name, e.g. eth0
640            let iface = expect!(line.next());
641            let destination = from_str!(u32, expect!(line.next()), 16).to_ne_bytes().into();
642            let gateway = from_str!(u32, expect!(line.next()), 16).to_ne_bytes().into();
643            let flags = from_str!(u16, expect!(line.next()), 16);
644            let refcnt = from_str!(u16, expect!(line.next()), 10);
645            let in_use = from_str!(u16, expect!(line.next()), 10);
646            let metrics = from_str!(u32, expect!(line.next()), 10);
647            let mask = from_str!(u32, expect!(line.next()), 16).to_ne_bytes().into();
648            let mtu = from_str!(u32, expect!(line.next()), 10);
649            let window = from_str!(u32, expect!(line.next()), 10);
650            let irtt = from_str!(u32, expect!(line.next()), 10);
651            vec.push(RouteEntry {
652                iface: iface.to_string(),
653                destination,
654                gateway,
655                flags,
656                refcnt,
657                in_use,
658                metrics,
659                mask,
660                mtu,
661                window,
662                irtt,
663            });
664        }
665
666        Ok(RouteEntries(vec))
667    }
668}
669
670#[derive(Debug, Clone, PartialEq, Eq)]
671#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
672/// The indication of whether this entity is acting as an IP gateway in respect
673/// to the forwarding of datagrams received by, but not addressed to, this
674/// entity.  IP gateways forward datagrams.  IP hosts do not (except those
675/// source-routed via the host).
676///
677/// Note that for some managed nodes, this object may take on only a subset of
678/// the values possible. Accordingly, it is appropriate for an agent to return a
679/// `badValue` response if a management station attempts to change this object
680/// to an inappropriate value.
681pub enum IpForwarding {
682    /// Acting as a gateway
683    Forwarding = 1,
684    /// Not acting as a gateway
685    NotForwarding = 2,
686}
687
688impl IpForwarding {
689    pub fn from_u8(num: u8) -> Option<IpForwarding> {
690        match num {
691            1 => Some(IpForwarding::Forwarding),
692            2 => Some(IpForwarding::NotForwarding),
693            _ => None,
694        }
695    }
696
697    pub fn to_u8(&self) -> u8 {
698        match self {
699            IpForwarding::Forwarding => 1,
700            IpForwarding::NotForwarding => 2,
701        }
702    }
703}
704
705#[derive(Debug, Clone, PartialEq, Eq)]
706#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
707/// The algorithm used to determine the timeout value used for retransmitting
708/// unacknowledged octets.
709pub enum TcpRtoAlgorithm {
710    /// None of the following
711    Other = 1,
712    /// A constant rto
713    Constant = 2,
714    /// MIL-STD-1778, [Appendix B](https://datatracker.ietf.org/doc/html/rfc1213#appendix-B)
715    Rsre = 3,
716    /// Van Jacobson's algorithm
717    ///
718    /// Reference: Jacobson, V., "Congestion Avoidance and Control", SIGCOMM 1988, Stanford, California.
719    Vanj = 4,
720}
721
722impl TcpRtoAlgorithm {
723    pub fn from_u8(num: u8) -> Option<TcpRtoAlgorithm> {
724        match num {
725            1 => Some(TcpRtoAlgorithm::Other),
726            2 => Some(TcpRtoAlgorithm::Constant),
727            3 => Some(TcpRtoAlgorithm::Rsre),
728            4 => Some(TcpRtoAlgorithm::Vanj),
729            _ => None,
730        }
731    }
732
733    pub fn to_u8(&self) -> u8 {
734        match self {
735            TcpRtoAlgorithm::Other => 1,
736            TcpRtoAlgorithm::Constant => 2,
737            TcpRtoAlgorithm::Rsre => 3,
738            TcpRtoAlgorithm::Vanj => 4,
739        }
740    }
741}
742
743/// This struct holds the data needed for the IP, ICMP, TCP, and UDP management
744/// information bases for an SNMP agent.
745///
746/// For more details, see [RFC1213](https://datatracker.ietf.org/doc/html/rfc1213)
747/// and [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html)
748#[derive(Debug, Clone)]
749#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
750pub struct Snmp {
751    pub ip_forwarding: IpForwarding,
752    /// The default value inserted into the Time-To-Live field of the IP header
753    /// of datagrams originated at this entity, whenever a TTL value is not
754    /// supplied by the transport layer protocol.
755    pub ip_default_ttl: u32,
756    /// The total number of input datagrams received from interfaces, including
757    /// those received in error.
758    pub ip_in_receives: u64,
759    /// The number of input datagrams discarded due to errors in their IP
760    /// headers.
761    pub ip_in_hdr_errors: u64,
762    /// The number of input datagrams discarded because the IP address in their
763    /// IP header's destination field was not a valid address to be received at
764    /// this entity.
765    pub ip_in_addr_errors: u64,
766    /// The number of input datagrams for which this entity was not their final
767    /// IP destination, as a result of which an attempt was made to find a
768    /// route to forward them to that final destination.
769    pub ip_forw_datagrams: u64,
770    /// The number of locally-addressed datagrams received successfully but
771    /// discarded because of an unknown or unsupported protocol.
772    pub ip_in_unknown_protos: u64,
773    /// The number of input IP datagrams for which no problems were encountered
774    /// to prevent their continued processing, but which were discarded
775    /// (e.g., for lack of buffer space).
776    pub ip_in_discards: u64,
777    /// The total number of input datagrams successfully delivered to IP
778    /// user-protocols (including ICMP).
779    ///
780    /// Note that this counter does not include any datagrams discarded while
781    /// awaiting re-assembly.
782    pub ip_in_delivers: u64,
783    /// The total number of IP datagrams which local IP user-protocols
784    /// (including ICMP) supplied to IP in requests for transmission.
785    ///
786    /// Note that this counter does not include any datagrams counted in
787    /// ipForwDatagrams.
788    pub ip_out_requests: u64,
789    /// The number of output IP datagrams for which no problem was encountered
790    /// to prevent their transmission to their destination, but which were
791    /// discarded (e.g., for lack of buffer space).
792    ///
793    /// Note that this counter would include datagrams counted in
794    /// `IpForwDatagrams` if any such packets met this (discretionary) discard
795    /// criterion.
796    pub ip_out_discards: u64,
797    /// The number of IP datagrams discarded because no route could be found to
798    /// transmit them to their destination.
799    ///
800    /// Note that this counter includes any packets counted in `IpForwDatagrams`
801    /// which meet this `no-route' criterion.
802    ///
803    /// Note that this includes any datagarms which a host cannot route because
804    /// all of its default gateways are down.
805    pub ip_out_no_routes: u64,
806    /// The maximum number of seconds which received fragments are held while
807    /// they are awaiting reassembly at this entity.
808    pub ip_reasm_timeout: u64,
809    /// The number of IP fragments received which needed to be reassembled at
810    /// this entity.
811    pub ip_reasm_reqds: u64,
812    /// The number of IP datagrams successfully re-assembled.
813    pub ip_reasm_oks: u64,
814    /// The number of failures detected by the IP re-assembly algorithm
815    /// (for whatever reason: timed out, errors, etc).
816    ///
817    /// Note that this is not necessarily a count of discarded IP fragments
818    /// since some algorithms (notably the algorithm in [RFC 815](https://datatracker.ietf.org/doc/html/rfc815))
819    /// can lose track of the number of fragments by combining them as they are
820    /// received.
821    pub ip_reasm_fails: u64,
822    /// The number of IP datagrams that have been successfully fragmented at
823    /// this entity.
824    pub ip_frag_oks: u64,
825    /// The number of IP datagrams that have been discarded because they needed
826    /// to be fragmented at this entity but could not be, e.g., because their
827    /// `Don't Fragment` flag was set.
828    pub ip_frag_fails: u64,
829    /// The number of IP datagram fragments that have been generated as a result
830    /// of fragmentation at this entity.
831    pub ip_frag_creates: u64,
832
833    /// The total number of ICMP messages which the entity received.
834    ///
835    /// Note that this counter includes all those counted by `icmp_in_errors`.
836    pub icmp_in_msgs: u64,
837    /// The number of ICMP messages which the entity received but determined as
838    /// having ICMP-specific errors (bad ICMP checksums, bad length, etc.
839    pub icmp_in_errors: u64,
840    /// This counter indicates the checksum of the ICMP packet is wrong.
841    ///
842    /// Non RFC1213 field
843    pub icmp_in_csum_errors: u64,
844    /// The number of ICMP Destination Unreachable messages received.
845    pub icmp_in_dest_unreachs: u64,
846    /// The number of ICMP Time Exceeded messages received.
847    pub icmp_in_time_excds: u64,
848    /// The number of ICMP Parameter Problem messages received.
849    pub icmp_in_parm_probs: u64,
850    /// The number of ICMP Source Quench messages received.
851    pub icmp_in_src_quenchs: u64,
852    /// The number of ICMP Redirect messages received.
853    pub icmp_in_redirects: u64,
854    /// The number of ICMP Echo (request) messages received.
855    pub icmp_in_echos: u64,
856    /// The number of ICMP Echo Reply messages received.
857    pub icmp_in_echo_reps: u64,
858    /// The number of ICMP Timestamp (request) messages received.
859    pub icmp_in_timestamps: u64,
860    /// The number of ICMP Timestamp Reply messages received.
861    pub icmp_in_timestamp_reps: u64,
862    /// The number of ICMP Address Mask Request messages received.
863    pub icmp_in_addr_masks: u64,
864    /// The number of ICMP Address Mask Reply messages received.
865    pub icmp_in_addr_mask_reps: u64,
866    /// The total number of ICMP messages which this entity attempted to send.
867    ///
868    /// Note that this counter includes all those counted by `icmp_out_errors`.
869    pub icmp_out_msgs: u64,
870    /// The number of ICMP messages which this entity did not send due to
871    /// problems discovered within ICMP such as a lack of buffers.  This value
872    /// should not include errors discovered outside the ICMP layer such as the
873    /// inability of IP to route the resultant datagram.  In some
874    /// implementations there may be no types of error which contribute to this
875    /// counter's value.
876    pub icmp_out_errors: u64,
877    /// The number of ICMP Destination Unreachable messages sent.
878    pub icmp_out_dest_unreachs: u64,
879    /// The number of ICMP Time Exceeded messages sent.
880    pub icmp_out_time_excds: u64,
881    /// The number of ICMP Parameter Problem messages sent.
882    pub icmp_out_parm_probs: u64,
883    /// The number of ICMP Source Quench messages sent.
884    pub icmp_out_src_quenchs: u64,
885    /// The number of ICMP Redirect messages sent.  For a host, this object will
886    /// always be zero, since hosts do not send redirects.
887    pub icmp_out_redirects: u64,
888    /// The number of ICMP Echo (request) messages sent.
889    pub icmp_out_echos: u64,
890    /// The number of ICMP Echo Reply messages sent.
891    pub icmp_out_echo_reps: u64,
892    /// The number of ICMP Timestamp (request) messages sent.
893    pub icmp_out_timestamps: u64,
894    /// The number of ICMP Timestamp Reply messages sent.
895    pub icmp_out_timestamp_reps: u64,
896    /// The number of ICMP Address Mask Request messages sent.
897    pub icmp_out_addr_masks: u64,
898    /// The number of ICMP Address Mask Reply messages sent.
899    pub icmp_out_addr_mask_reps: u64,
900
901    // ignore ICMP numeric types
902    pub tcp_rto_algorithm: TcpRtoAlgorithm,
903    /// The minimum value permitted by a TCP implementation for the
904    /// retransmission timeout, measured in milliseconds.  More refined
905    /// semantics for objects of this type depend upon the algorithm used to
906    /// determine the retransmission timeout.  In particular, when the timeout
907    /// algorithm is rsre(3), an object of this type has the semantics of the
908    /// LBOUND quantity described in [RFC 793](https://datatracker.ietf.org/doc/html/rfc793).
909    pub tcp_rto_min: u64,
910    /// The maximum value permitted by a TCP implementation for the
911    /// retransmission timeout, measured in milliseconds.  More refined
912    /// semantics for objects of this type depend upon the algorithm used to
913    /// determine the retransmission timeout.  In particular, when the timeout
914    /// algorithm is rsre(3), an object of this type has the semantics of the
915    /// UBOUND quantity described in [RFC 793](https://datatracker.ietf.org/doc/html/rfc793).
916    pub tcp_rto_max: u64,
917    /// The limit on the total number of TCP connections the entity can support.
918    /// In entities where the maximum number of connections is dynamic, this
919    /// object should contain the value -1.
920    pub tcp_max_conn: i64,
921    /// The number of times TCP connections have made a direct transition to the
922    /// SYN-SENT state from the CLOSED state.
923    pub tcp_active_opens: u64,
924    /// The number of times TCP connections have made a direct transition to the
925    /// SYN-RCVD state from the LISTEN state.
926    pub tcp_passive_opens: u64,
927    /// The number of times TCP connections have made a direct transition to the
928    /// CLOSED state from either the SYN-SENT state or the SYN-RCVD state, plus
929    /// the number of times TCP connections have made a direct transition to the
930    /// LISTEN state from the SYN-RCVD state.
931    pub tcp_attempt_fails: u64,
932    /// The number of times TCP connections have made a direct transition to the
933    /// CLOSED state from either the ESTABLISHED state or the CLOSE-WAIT state.
934    pub tcp_estab_resets: u64,
935    /// The number of TCP connections for which the current state is either
936    /// ESTABLISHED or CLOSE-WAIT.
937    pub tcp_curr_estab: u64,
938    /// The total number of segments received, including those received in
939    /// error.  This count includes segments received on currently established
940    /// connections.
941    pub tcp_in_segs: u64,
942    /// The total number of segments sent, including those on current
943    /// connections but excluding those containing only retransmitted octets.
944    pub tcp_out_segs: u64,
945    /// The total number of segments retransmitted - that is, the number of TCP
946    /// segments transmitted containing one or more previously transmitted octets.
947    pub tcp_retrans_segs: u64,
948    /// The total number of segments received in error (e.g., bad TCP checksums).
949    pub tcp_in_errs: u64,
950    /// The number of TCP segments sent containing the RST flag.
951    pub tcp_out_rsts: u64,
952    /// [To be documented.]
953    ///
954    /// Non RFC1213 field
955    pub tcp_in_csum_errors: u64,
956
957    /// The total number of UDP datagrams delivered to UDP users.
958    pub udp_in_datagrams: u64,
959    /// The total number of received UDP datagrams for which there was no
960    /// application at the destination port.
961    pub udp_no_ports: u64,
962    /// The number of received UDP datagrams that could not be delivered for
963    /// reasons other than the lack of an application at the destination port.
964    pub udp_in_errors: u64,
965    /// The total number of UDP datagrams sent from this entity.
966    pub udp_out_datagrams: u64,
967    /// [To be documented.]
968    ///
969    /// Non RFC1213 field
970    pub udp_rcvbuf_errors: u64,
971    /// [To be documented.]
972    ///
973    /// Non RFC1213 field
974    pub udp_sndbuf_errors: u64,
975    /// [To be documented.]
976    ///
977    /// Non RFC1213 field
978    pub udp_in_csum_errors: u64,
979    /// [To be documented.]
980    ///
981    /// Non RFC1213 field
982    pub udp_ignored_multi: u64,
983
984    /// [To be documented.]
985    ///
986    /// Non RFC1213 field
987    pub udp_lite_in_datagrams: u64,
988    /// [To be documented.]
989    ///
990    /// Non RFC1213 field
991    pub udp_lite_no_ports: u64,
992    /// [To be documented.]
993    ///
994    /// Non RFC1213 field
995    pub udp_lite_in_errors: u64,
996    /// [To be documented.]
997    ///
998    /// Non RFC1213 field
999    pub udp_lite_out_datagrams: u64,
1000    /// [To be documented.]
1001    ///
1002    /// Non RFC1213 field
1003    pub udp_lite_rcvbuf_errors: u64,
1004    /// [To be documented.]
1005    ///
1006    /// Non RFC1213 field
1007    pub udp_lite_sndbuf_errors: u64,
1008    /// [To be documented.]
1009    ///
1010    /// Non RFC1213 field
1011    pub udp_lite_in_csum_errors: u64,
1012    /// [To be documented.]
1013    ///
1014    /// Non RFC1213 field
1015    pub udp_lite_ignored_multi: u64,
1016}
1017
1018/// A /proc/net/snmp section
1019///
1020/// A section represents two lines, 1x header and 1x data
1021/// Each line has a prefix [ip, icmp, icmpmsg, tcp, udp, udplite]
1022/// Eg.
1023///     Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs InErrs OutRsts InCsumErrors
1024///     Tcp: 1 200 120000 -1 177 14 0 6 4 11155 10083 18 0 94 0
1025#[derive(Debug)]
1026struct SnmpSection {
1027    prefix: String,
1028    values: HashMap<String, String>,
1029}
1030
1031impl<'a> SnmpSection {
1032    fn new(hdr: String, data: String) -> ProcResult<Self> {
1033        let mut hdr = hdr.trim_end().split_whitespace();
1034        let mut data = data.trim_end().split_whitespace();
1035        let prefix = expect!(hdr.next()).to_owned();
1036        expect!(data.next());
1037        let mut values = HashMap::new();
1038
1039        for hdr in hdr {
1040            values.insert(hdr.to_owned(), expect!(data.next()).to_owned());
1041        }
1042
1043        Ok(Self { prefix, values })
1044    }
1045}
1046
1047/// An iterator over the /proc/net/snmp sections using `BufRead`.
1048#[derive(Debug)]
1049struct SnmpSections<B> {
1050    buf: B,
1051}
1052
1053impl<B: BufRead> Iterator for SnmpSections<B> {
1054    type Item = ProcResult<SnmpSection>;
1055
1056    fn next(&mut self) -> Option<Self::Item> {
1057        let mut hdr = String::new();
1058        match self.buf.read_line(&mut hdr) {
1059            Ok(0) => None,
1060            Ok(_n) => {
1061                let mut data = String::new();
1062                match self.buf.read_line(&mut data) {
1063                    Ok(_n) => Some(SnmpSection::new(hdr, data)),
1064                    Err(e) => Some(Err(e.into())),
1065                }
1066            }
1067            Err(e) => Some(Err(e.into())),
1068        }
1069    }
1070}
1071
1072impl super::FromBufRead for Snmp {
1073    fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
1074        let mut map = HashMap::new();
1075        let sections = SnmpSections { buf: r };
1076        for section in sections.flatten() {
1077            let p = &section.prefix;
1078            for (hdr, v) in &section.values {
1079                map.insert(format!("{p}{hdr}"), v.to_owned());
1080            }
1081        }
1082
1083        let snmp = Snmp {
1084            // Ip
1085            ip_forwarding: expect!(IpForwarding::from_u8(from_str!(
1086                u8,
1087                &expect!(map.remove("Ip:Forwarding"))
1088            ))),
1089            ip_default_ttl: from_str!(u32, &expect!(map.remove("Ip:DefaultTTL"))),
1090            ip_in_receives: from_str!(u64, &expect!(map.remove("Ip:InReceives"))),
1091            ip_in_hdr_errors: from_str!(u64, &expect!(map.remove("Ip:InHdrErrors"))),
1092            ip_in_addr_errors: from_str!(u64, &expect!(map.remove("Ip:InAddrErrors"))),
1093            ip_forw_datagrams: from_str!(u64, &expect!(map.remove("Ip:ForwDatagrams"))),
1094            ip_in_unknown_protos: from_str!(u64, &expect!(map.remove("Ip:InUnknownProtos"))),
1095            ip_in_discards: from_str!(u64, &expect!(map.remove("Ip:InDiscards"))),
1096            ip_in_delivers: from_str!(u64, &expect!(map.remove("Ip:InDelivers"))),
1097            ip_out_requests: from_str!(u64, &expect!(map.remove("Ip:OutRequests"))),
1098            ip_out_discards: from_str!(u64, &expect!(map.remove("Ip:OutDiscards"))),
1099            ip_out_no_routes: from_str!(u64, &expect!(map.remove("Ip:OutNoRoutes"))),
1100            ip_reasm_timeout: from_str!(u64, &expect!(map.remove("Ip:ReasmTimeout"))),
1101            ip_reasm_reqds: from_str!(u64, &expect!(map.remove("Ip:ReasmReqds"))),
1102            ip_reasm_oks: from_str!(u64, &expect!(map.remove("Ip:ReasmOKs"))),
1103            ip_reasm_fails: from_str!(u64, &expect!(map.remove("Ip:ReasmFails"))),
1104            ip_frag_oks: from_str!(u64, &expect!(map.remove("Ip:FragOKs"))),
1105            ip_frag_fails: from_str!(u64, &expect!(map.remove("Ip:FragFails"))),
1106            ip_frag_creates: from_str!(u64, &expect!(map.remove("Ip:FragCreates"))),
1107            //ip_out_transmits: from_str!(u64, &expect!(map.remove("Ip:OutTransmits"))),
1108            // Icmp
1109            icmp_in_msgs: from_str!(u64, &expect!(map.remove("Icmp:InMsgs"))),
1110            icmp_in_errors: from_str!(u64, &expect!(map.remove("Icmp:InErrors"))),
1111            icmp_in_csum_errors: from_str!(u64, &expect!(map.remove("Icmp:InCsumErrors"))),
1112            icmp_in_dest_unreachs: from_str!(u64, &expect!(map.remove("Icmp:InDestUnreachs"))),
1113            icmp_in_time_excds: from_str!(u64, &expect!(map.remove("Icmp:InTimeExcds"))),
1114            icmp_in_parm_probs: from_str!(u64, &expect!(map.remove("Icmp:InParmProbs"))),
1115            icmp_in_src_quenchs: from_str!(u64, &expect!(map.remove("Icmp:InSrcQuenchs"))),
1116            icmp_in_redirects: from_str!(u64, &expect!(map.remove("Icmp:InRedirects"))),
1117            icmp_in_echos: from_str!(u64, &expect!(map.remove("Icmp:InEchos"))),
1118            icmp_in_echo_reps: from_str!(u64, &expect!(map.remove("Icmp:InEchoReps"))),
1119            icmp_in_timestamps: from_str!(u64, &expect!(map.remove("Icmp:InTimestamps"))),
1120            icmp_in_timestamp_reps: from_str!(u64, &expect!(map.remove("Icmp:InTimestampReps"))),
1121            icmp_in_addr_masks: from_str!(u64, &expect!(map.remove("Icmp:InAddrMasks"))),
1122            icmp_in_addr_mask_reps: from_str!(u64, &expect!(map.remove("Icmp:InAddrMaskReps"))),
1123            icmp_out_msgs: from_str!(u64, &expect!(map.remove("Icmp:OutMsgs"))),
1124            icmp_out_errors: from_str!(u64, &expect!(map.remove("Icmp:OutErrors"))),
1125            //icmp_out_rate_limit_global: from_str!(u64, &expect!(map.remove("Icmp:OutRateLimitGlobal"))),
1126            //icmp_out_rate_limit_host: from_str!(u64, &expect!(map.remove("Icmp:OutRateLimitHost"))),
1127            icmp_out_dest_unreachs: from_str!(u64, &expect!(map.remove("Icmp:OutDestUnreachs"))),
1128            icmp_out_time_excds: from_str!(u64, &expect!(map.remove("Icmp:OutTimeExcds"))),
1129            icmp_out_parm_probs: from_str!(u64, &expect!(map.remove("Icmp:OutParmProbs"))),
1130            icmp_out_src_quenchs: from_str!(u64, &expect!(map.remove("Icmp:OutSrcQuenchs"))),
1131            icmp_out_redirects: from_str!(u64, &expect!(map.remove("Icmp:OutRedirects"))),
1132            icmp_out_echos: from_str!(u64, &expect!(map.remove("Icmp:OutEchos"))),
1133            icmp_out_echo_reps: from_str!(u64, &expect!(map.remove("Icmp:OutEchoReps"))),
1134            icmp_out_timestamps: from_str!(u64, &expect!(map.remove("Icmp:OutTimestamps"))),
1135            icmp_out_timestamp_reps: from_str!(u64, &expect!(map.remove("Icmp:OutTimestampReps"))),
1136            icmp_out_addr_masks: from_str!(u64, &expect!(map.remove("Icmp:OutAddrMasks"))),
1137            icmp_out_addr_mask_reps: from_str!(u64, &expect!(map.remove("Icmp:OutAddrMaskReps"))),
1138            // Tcp
1139            tcp_rto_algorithm: expect!(TcpRtoAlgorithm::from_u8(from_str!(
1140                u8,
1141                &expect!(map.remove("Tcp:RtoAlgorithm"))
1142            ))),
1143            tcp_rto_min: from_str!(u64, &expect!(map.remove("Tcp:RtoMin"))),
1144            tcp_rto_max: from_str!(u64, &expect!(map.remove("Tcp:RtoMax"))),
1145            tcp_max_conn: from_str!(i64, &expect!(map.remove("Tcp:MaxConn"))),
1146            tcp_active_opens: from_str!(u64, &expect!(map.remove("Tcp:ActiveOpens"))),
1147            tcp_passive_opens: from_str!(u64, &expect!(map.remove("Tcp:PassiveOpens"))),
1148            tcp_attempt_fails: from_str!(u64, &expect!(map.remove("Tcp:AttemptFails"))),
1149            tcp_estab_resets: from_str!(u64, &expect!(map.remove("Tcp:EstabResets"))),
1150            tcp_curr_estab: from_str!(u64, &expect!(map.remove("Tcp:CurrEstab"))),
1151            tcp_in_segs: from_str!(u64, &expect!(map.remove("Tcp:InSegs"))),
1152            tcp_out_segs: from_str!(u64, &expect!(map.remove("Tcp:OutSegs"))),
1153            tcp_retrans_segs: from_str!(u64, &expect!(map.remove("Tcp:RetransSegs"))),
1154            tcp_in_errs: from_str!(u64, &expect!(map.remove("Tcp:InErrs"))),
1155            tcp_out_rsts: from_str!(u64, &expect!(map.remove("Tcp:OutRsts"))),
1156            tcp_in_csum_errors: from_str!(u64, &expect!(map.remove("Tcp:InCsumErrors"))),
1157            // Udp
1158            udp_in_datagrams: from_str!(u64, &expect!(map.remove("Udp:InDatagrams"))),
1159            udp_no_ports: from_str!(u64, &expect!(map.remove("Udp:NoPorts"))),
1160            udp_in_errors: from_str!(u64, &expect!(map.remove("Udp:InErrors"))),
1161            udp_out_datagrams: from_str!(u64, &expect!(map.remove("Udp:OutDatagrams"))),
1162            udp_rcvbuf_errors: from_str!(u64, &expect!(map.remove("Udp:RcvbufErrors"))),
1163            udp_sndbuf_errors: from_str!(u64, &expect!(map.remove("Udp:SndbufErrors"))),
1164            udp_in_csum_errors: from_str!(u64, &expect!(map.remove("Udp:InCsumErrors"))),
1165            udp_ignored_multi: from_str!(u64, &expect!(map.remove("Udp:IgnoredMulti"))),
1166            //udp_mem_errors: from_str!(u64, &expect!(map.remove("Udp:MemErrors"))),
1167            // UdpLite
1168            udp_lite_in_datagrams: from_str!(u64, &expect!(map.remove("UdpLite:InDatagrams"))),
1169            udp_lite_no_ports: from_str!(u64, &expect!(map.remove("UdpLite:NoPorts"))),
1170            udp_lite_in_errors: from_str!(u64, &expect!(map.remove("UdpLite:InErrors"))),
1171            udp_lite_out_datagrams: from_str!(u64, &expect!(map.remove("UdpLite:OutDatagrams"))),
1172            udp_lite_rcvbuf_errors: from_str!(u64, &expect!(map.remove("UdpLite:RcvbufErrors"))),
1173            udp_lite_sndbuf_errors: from_str!(u64, &expect!(map.remove("UdpLite:SndbufErrors"))),
1174            udp_lite_in_csum_errors: from_str!(u64, &expect!(map.remove("UdpLite:InCsumErrors"))),
1175            udp_lite_ignored_multi: from_str!(u64, &expect!(map.remove("UdpLite:IgnoredMulti"))),
1176            //udp_lite_mem_errors: from_str!(u64, &expect!(map.remove("UdpLite:MemErrors"))),
1177        };
1178
1179        Ok(snmp)
1180    }
1181}
1182
1183/// This struct holds the data needed for the IP, ICMP, TCP, and UDP management
1184/// information bases for an SNMP agent.
1185///
1186/// Note that this struct is only for IPv6
1187///
1188/// For more details, see [RFC1213](https://datatracker.ietf.org/doc/html/rfc1213)
1189/// and [SNMP counter](https://docs.kernel.org/networking/snmp_counter.html)
1190#[derive(Debug, Clone)]
1191#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
1192pub struct Snmp6 {
1193    /// The total number of input datagrams received from interfaces, including
1194    /// those received in error.
1195    pub ip_in_receives: u64,
1196    /// The number of input datagrams discarded due to errors in their IP
1197    /// headers.
1198    pub ip_in_hdr_errors: u64,
1199    /// [To be documented.]
1200    ///
1201    /// Non RFC1213 field
1202    pub ip_in_too_big_errors: u64,
1203    /// [To be documented.]
1204    ///
1205    /// Non RFC1213 field
1206    pub ip_in_no_routes: u64,
1207    /// The number of input datagrams discarded because the IP address in their
1208    /// IP header's destination field was not a valid address to be received at
1209    /// this entity.
1210    pub ip_in_addr_errors: u64,
1211    /// The number of locally-addressed datagrams received successfully but
1212    /// discarded because of an unknown or unsupported protocol.
1213    pub ip_in_unknown_protos: u64,
1214    /// [To be documented.]
1215    ///
1216    /// Non RFC1213 field
1217    pub ip_in_truncated_pkts: u64,
1218    /// The number of input IP datagrams for which no problems were encountered
1219    /// to prevent their continued processing, but which were discarded
1220    /// (e.g., for lack of buffer space).
1221    pub ip_in_discards: u64,
1222    /// The total number of input datagrams successfully delivered to IP
1223    /// user-protocols (including ICMP).
1224    ///
1225    /// Note that this counter does not include any datagrams discarded while
1226    /// awaiting re-assembly.
1227    pub ip_in_delivers: u64,
1228    /// [To be documented.]
1229    ///
1230    /// Non RFC1213 field
1231    pub ip_out_forw_datagrams: u64,
1232    /// The total number of IP datagrams which local IP user-protocols
1233    /// (including ICMP) supplied to IP in requests for transmission.
1234    ///
1235    /// Note that this counter does not include any datagrams counted in
1236    /// ipForwDatagrams.
1237    pub ip_out_requests: u64,
1238    /// The number of output IP datagrams for which no problem was encountered
1239    /// to prevent their transmission to their destination, but which were
1240    /// discarded (e.g., for lack of buffer space).
1241    ///
1242    /// Note that this counter would include datagrams counted in
1243    /// `IpForwDatagrams` if any such packets met this (discretionary) discard
1244    /// criterion.
1245    pub ip_out_discards: u64,
1246    /// The number of IP datagrams discarded because no route could be found to
1247    /// transmit them to their destination.
1248    ///
1249    /// Note that this counter includes any packets counted in `IpForwDatagrams`
1250    /// which meet this `no-route' criterion.
1251    ///
1252    /// Note that this includes any datagarms which a host cannot route because
1253    /// all of its default gateways are down.
1254    pub ip_out_no_routes: u64,
1255    /// The maximum number of seconds which received fragments are held while
1256    /// they are awaiting reassembly at this entity.
1257    pub ip_reasm_timeout: u64,
1258    /// The number of IP fragments received which needed to be reassembled at
1259    /// this entity.
1260    pub ip_reasm_reqds: u64,
1261    /// The number of IP datagrams successfully re-assembled.
1262    pub ip_reasm_oks: u64,
1263    /// The number of failures detected by the IP re-assembly algorithm
1264    /// (for whatever reason: timed out, errors, etc).
1265    ///
1266    /// Note that this is not necessarily a count of discarded IP fragments
1267    /// since some algorithms (notably the algorithm in [RFC 815](https://datatracker.ietf.org/doc/html/rfc815))
1268    /// can lose track of the number of fragments by combining them as they are
1269    /// received.
1270    pub ip_reasm_fails: u64,
1271    /// The number of IP datagrams that have been successfully fragmented at
1272    /// this entity.
1273    pub ip_frag_oks: u64,
1274    /// The number of IP datagrams that have been discarded because they needed
1275    /// to be fragmented at this entity but could not be, e.g., because their
1276    /// `Don't Fragment` flag was set.
1277    pub ip_frag_fails: u64,
1278    /// The number of IP datagram fragments that have been generated as a result
1279    /// of fragmentation at this entity.
1280    pub ip_frag_creates: u64,
1281    /// [To be documented.]
1282    ///
1283    /// Non RFC1213 field
1284    pub ip_in_mcast_pkts: u64,
1285    /// [To be documented.]
1286    ///
1287    /// Non RFC1213 field
1288    pub ip_out_mcast_pkts: u64,
1289    /// [To be documented.]
1290    ///
1291    /// Non RFC1213 field
1292    pub ip_in_octets: u64,
1293    /// [To be documented.]
1294    ///
1295    /// Non RFC1213 field
1296    pub ip_out_octets: u64,
1297    /// [To be documented.]
1298    ///
1299    /// Non RFC1213 field
1300    pub ip_in_mcast_octets: u64,
1301    /// [To be documented.]
1302    ///
1303    /// Non RFC1213 field
1304    pub ip_out_mcast_octets: u64,
1305    /// [To be documented.]
1306    ///
1307    /// Non RFC1213 field
1308    pub ip_in_bcast_octets: u64,
1309    /// [To be documented.]
1310    ///
1311    /// Non RFC1213 field
1312    pub ip_out_bcast_octets: u64,
1313    /// [To be documented.]
1314    ///
1315    /// Non RFC1213 field
1316    pub ip_in_no_ect_pkts: u64,
1317    /// [To be documented.]
1318    ///
1319    /// Non RFC1213 field
1320    pub ip_in_ect1_pkts: u64,
1321    /// [To be documented.]
1322    ///
1323    /// Non RFC1213 field
1324    pub ip_in_ect0_pkts: u64,
1325    /// [To be documented.]
1326    ///
1327    /// Non RFC1213 field
1328    pub ip_in_ce_pkts: u64,
1329
1330    /// The total number of ICMP messages which the entity received.
1331    ///
1332    /// Note that this counter includes all those counted by `icmp_in_errors`.
1333    pub icmp_in_msgs: u64,
1334    /// The number of ICMP messages which the entity received but determined as
1335    /// having ICMP-specific errors (bad ICMP checksums, bad length, etc.
1336    pub icmp_in_errors: u64,
1337    /// The total number of ICMP messages which this entity attempted to send.
1338    ///
1339    /// Note that this counter includes all those counted by `icmp_out_errors`.
1340    pub icmp_out_msgs: u64,
1341    /// The number of ICMP messages which this entity did not send due to
1342    /// problems discovered within ICMP such as a lack of buffers.  This value
1343    /// should not include errors discovered outside the ICMP layer such as the
1344    /// inability of IP to route the resultant datagram.  In some
1345    /// implementations there may be no types of error which contribute to this
1346    /// counter's value.
1347    pub icmp_out_errors: u64,
1348    /// This counter indicates the checksum of the ICMP packet is wrong.
1349    ///
1350    /// Non RFC1213 field
1351    pub icmp_in_csum_errors: u64,
1352    /// The number of ICMP Destination Unreachable messages received.
1353    pub icmp_in_dest_unreachs: u64,
1354    /// [To be documented.]
1355    ///
1356    /// Non RFC1213 field
1357    pub icmp_in_pkt_too_bigs: u64,
1358    /// The number of ICMP Time Exceeded messages received.
1359    pub icmp_in_time_excds: u64,
1360    /// The number of ICMP Parameter Problem messages received.
1361    pub icmp_in_parm_problem: u64,
1362    /// The number of ICMP Echo (request) messages received.
1363    pub icmp_in_echos: u64,
1364    /// The number of ICMP Echo Reply messages received.
1365    pub icmp_in_echo_replies: u64,
1366    /// [To be documented.]
1367    ///
1368    /// Non RFC1213 field
1369    pub icmp_in_group_memb_queries: u64,
1370    /// [To be documented.]
1371    ///
1372    /// Non RFC1213 field
1373    pub icmp_in_group_memb_responses: u64,
1374    /// [To be documented.]
1375    ///
1376    /// Non RFC1213 field
1377    pub icmp_in_group_memb_reductions: u64,
1378    /// [To be documented.]
1379    ///
1380    /// Non RFC1213 field
1381    pub icmp_in_router_solicits: u64,
1382    /// [To be documented.]
1383    ///
1384    /// Non RFC1213 field
1385    pub icmp_in_router_advertisements: u64,
1386    /// [To be documented.]
1387    ///
1388    /// Non RFC1213 field
1389    pub icmp_in_neighbor_solicits: u64,
1390    /// [To be documented.]
1391    ///
1392    /// Non RFC1213 field
1393    pub icmp_in_neighbor_advertisements: u64,
1394    /// [To be documented.]
1395    ///
1396    /// Non RFC1213 field
1397    pub icmp_in_redirects: u64,
1398    /// [To be documented.]
1399    ///
1400    /// Non RFC1213 field
1401    pub icmp_in_mldv2_reports: u64,
1402    /// The number of ICMP Destination Unreachable messages sent.
1403    pub icmp_out_dest_unreachs: u64,
1404    /// [To be documented.]
1405    ///
1406    /// Non RFC1213 field
1407    pub icmp_out_pkt_too_bigs: u64,
1408    /// The number of ICMP Time Exceeded messages sent.
1409    pub icmp_out_time_excds: u64,
1410    /// The number of ICMP Parameter Problem messages sent.
1411    pub icmp_out_parm_problems: u64,
1412    /// The number of ICMP Echo (request) messages sent.
1413    pub icmp_out_echos: u64,
1414    /// The number of ICMP Echo Reply messages sent.
1415    pub icmp_out_echo_replies: u64,
1416    /// [To be documented.]
1417    ///
1418    /// Non RFC1213 field
1419    pub icmp_out_group_memb_queries: u64,
1420    /// [To be documented.]
1421    ///
1422    /// Non RFC1213 field
1423    pub icmp_out_group_memb_responses: u64,
1424    /// [To be documented.]
1425    ///
1426    /// Non RFC1213 field
1427    pub icmp_out_group_memb_reductions: u64,
1428    /// [To be documented.]
1429    ///
1430    /// Non RFC1213 field
1431    pub icmp_out_router_solicits: u64,
1432    /// [To be documented.]
1433    ///
1434    /// Non RFC1213 field
1435    pub icmp_out_router_advertisements: u64,
1436    /// [To be documented.]
1437    ///
1438    /// Non RFC1213 field
1439    pub icmp_out_neighbor_solicits: u64,
1440    /// [To be documented.]
1441    ///
1442    /// Non RFC1213 field
1443    pub icmp_out_neighbor_advertisements: u64,
1444    /// [To be documented.]
1445    ///
1446    /// Non RFC1213 field
1447    pub icmp_out_redirects: u64,
1448    /// [To be documented.]
1449    ///
1450    /// Non RFC1213 field
1451    pub icmp_out_mldv2_reports: u64,
1452    //
1453    // ignore ICMP numeric types
1454    //
1455    /// The total number of UDP datagrams delivered to UDP users.
1456    pub udp_in_datagrams: u64,
1457    /// The total number of received UDP datagrams for which there was no
1458    /// application at the destination port.
1459    pub udp_no_ports: u64,
1460    /// The number of received UDP datagrams that could not be delivered for
1461    /// reasons other than the lack of an application at the destination port.
1462    pub udp_in_errors: u64,
1463    /// The total number of UDP datagrams sent from this entity.
1464    pub udp_out_datagrams: u64,
1465    /// [To be documented.]
1466    ///
1467    /// Non RFC1213 field
1468    pub udp_rcvbuf_errors: u64,
1469    /// [To be documented.]
1470    ///
1471    /// Non RFC1213 field
1472    pub udp_sndbuf_errors: u64,
1473    /// [To be documented.]
1474    ///
1475    /// Non RFC1213 field
1476    pub udp_in_csum_errors: u64,
1477    /// [To be documented.]
1478    ///
1479    /// Non RFC1213 field
1480    pub udp_ignored_multi: u64,
1481
1482    /// [To be documented.]
1483    ///
1484    /// Non RFC1213 field
1485    pub udp_lite_in_datagrams: u64,
1486    /// [To be documented.]
1487    ///
1488    /// Non RFC1213 field
1489    pub udp_lite_no_ports: u64,
1490    /// [To be documented.]
1491    ///
1492    /// Non RFC1213 field
1493    pub udp_lite_in_errors: u64,
1494    /// [To be documented.]
1495    ///
1496    /// Non RFC1213 field
1497    pub udp_lite_out_datagrams: u64,
1498    /// [To be documented.]
1499    ///
1500    /// Non RFC1213 field
1501    pub udp_lite_rcvbuf_errors: u64,
1502    /// [To be documented.]
1503    ///
1504    /// Non RFC1213 field
1505    pub udp_lite_sndbuf_errors: u64,
1506    /// [To be documented.]
1507    ///
1508    /// Non RFC1213 field
1509    pub udp_lite_in_csum_errors: u64,
1510}
1511
1512impl super::FromBufRead for Snmp6 {
1513    fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
1514        let mut map = HashMap::new();
1515
1516        for line in r.lines() {
1517            let line = expect!(line);
1518            if line.is_empty() {
1519                continue;
1520            }
1521            let mut s = line.split_whitespace();
1522            let field = expect!(s.next(), "no field");
1523            if field.starts_with("Icmp6InType") || field.starts_with("Icmp6OutType") {
1524                continue;
1525            }
1526            let value = from_str!(u64, expect!(s.next(), "no value"));
1527            map.insert(field.to_string(), value);
1528        }
1529
1530        let snmp6 = Snmp6 {
1531            ip_in_receives: expect!(map.remove("Ip6InReceives")),
1532            ip_in_hdr_errors: expect!(map.remove("Ip6InHdrErrors")),
1533            ip_in_too_big_errors: expect!(map.remove("Ip6InTooBigErrors")),
1534            ip_in_no_routes: expect!(map.remove("Ip6InNoRoutes")),
1535            ip_in_addr_errors: expect!(map.remove("Ip6InAddrErrors")),
1536            ip_in_unknown_protos: expect!(map.remove("Ip6InUnknownProtos")),
1537            ip_in_truncated_pkts: expect!(map.remove("Ip6InTruncatedPkts")),
1538            ip_in_discards: expect!(map.remove("Ip6InDiscards")),
1539            ip_in_delivers: expect!(map.remove("Ip6InDelivers")),
1540            ip_out_forw_datagrams: expect!(map.remove("Ip6OutForwDatagrams")),
1541            ip_out_requests: expect!(map.remove("Ip6OutRequests")),
1542            ip_out_discards: expect!(map.remove("Ip6OutDiscards")),
1543            ip_out_no_routes: expect!(map.remove("Ip6OutNoRoutes")),
1544            ip_reasm_timeout: expect!(map.remove("Ip6ReasmTimeout")),
1545            ip_reasm_reqds: expect!(map.remove("Ip6ReasmReqds")),
1546            ip_reasm_oks: expect!(map.remove("Ip6ReasmOKs")),
1547            ip_reasm_fails: expect!(map.remove("Ip6ReasmFails")),
1548            ip_frag_oks: expect!(map.remove("Ip6FragOKs")),
1549            ip_frag_fails: expect!(map.remove("Ip6FragFails")),
1550            ip_frag_creates: expect!(map.remove("Ip6FragCreates")),
1551            ip_in_mcast_pkts: expect!(map.remove("Ip6InMcastPkts")),
1552            ip_out_mcast_pkts: expect!(map.remove("Ip6OutMcastPkts")),
1553            ip_in_octets: expect!(map.remove("Ip6InOctets")),
1554            ip_out_octets: expect!(map.remove("Ip6OutOctets")),
1555            ip_in_mcast_octets: expect!(map.remove("Ip6InMcastOctets")),
1556            ip_out_mcast_octets: expect!(map.remove("Ip6OutMcastOctets")),
1557            ip_in_bcast_octets: expect!(map.remove("Ip6InBcastOctets")),
1558            ip_out_bcast_octets: expect!(map.remove("Ip6OutBcastOctets")),
1559            ip_in_no_ect_pkts: expect!(map.remove("Ip6InNoECTPkts")),
1560            ip_in_ect1_pkts: expect!(map.remove("Ip6InECT1Pkts")),
1561            ip_in_ect0_pkts: expect!(map.remove("Ip6InECT0Pkts")),
1562            ip_in_ce_pkts: expect!(map.remove("Ip6InCEPkts")),
1563
1564            icmp_in_msgs: expect!(map.remove("Icmp6InMsgs")),
1565            icmp_in_errors: expect!(map.remove("Icmp6InErrors")),
1566            icmp_out_msgs: expect!(map.remove("Icmp6OutMsgs")),
1567            icmp_out_errors: expect!(map.remove("Icmp6OutErrors")),
1568            icmp_in_csum_errors: expect!(map.remove("Icmp6InCsumErrors")),
1569            icmp_in_dest_unreachs: expect!(map.remove("Icmp6InDestUnreachs")),
1570            icmp_in_pkt_too_bigs: expect!(map.remove("Icmp6InPktTooBigs")),
1571            icmp_in_time_excds: expect!(map.remove("Icmp6InTimeExcds")),
1572            icmp_in_parm_problem: expect!(map.remove("Icmp6InParmProblems")),
1573            icmp_in_echos: expect!(map.remove("Icmp6InEchos")),
1574            icmp_in_echo_replies: expect!(map.remove("Icmp6InEchoReplies")),
1575            icmp_in_group_memb_queries: expect!(map.remove("Icmp6InGroupMembQueries")),
1576            icmp_in_group_memb_responses: expect!(map.remove("Icmp6InGroupMembResponses")),
1577            icmp_in_group_memb_reductions: expect!(map.remove("Icmp6InGroupMembReductions")),
1578            icmp_in_router_solicits: expect!(map.remove("Icmp6InRouterSolicits")),
1579            icmp_in_router_advertisements: expect!(map.remove("Icmp6InRouterAdvertisements")),
1580            icmp_in_neighbor_solicits: expect!(map.remove("Icmp6InNeighborSolicits")),
1581            icmp_in_neighbor_advertisements: expect!(map.remove("Icmp6InNeighborAdvertisements")),
1582            icmp_in_redirects: expect!(map.remove("Icmp6InRedirects")),
1583            icmp_in_mldv2_reports: expect!(map.remove("Icmp6InMLDv2Reports")),
1584            icmp_out_dest_unreachs: expect!(map.remove("Icmp6OutDestUnreachs")),
1585            icmp_out_pkt_too_bigs: expect!(map.remove("Icmp6OutPktTooBigs")),
1586            icmp_out_time_excds: expect!(map.remove("Icmp6OutTimeExcds")),
1587            icmp_out_parm_problems: expect!(map.remove("Icmp6OutParmProblems")),
1588            icmp_out_echos: expect!(map.remove("Icmp6OutEchos")),
1589            icmp_out_echo_replies: expect!(map.remove("Icmp6OutEchoReplies")),
1590            icmp_out_group_memb_queries: expect!(map.remove("Icmp6OutGroupMembQueries")),
1591            icmp_out_group_memb_responses: expect!(map.remove("Icmp6OutGroupMembResponses")),
1592            icmp_out_group_memb_reductions: expect!(map.remove("Icmp6OutGroupMembReductions")),
1593            icmp_out_router_solicits: expect!(map.remove("Icmp6OutRouterSolicits")),
1594            icmp_out_router_advertisements: expect!(map.remove("Icmp6OutRouterAdvertisements")),
1595            icmp_out_neighbor_solicits: expect!(map.remove("Icmp6OutNeighborSolicits")),
1596            icmp_out_neighbor_advertisements: expect!(map.remove("Icmp6OutNeighborAdvertisements")),
1597            icmp_out_redirects: expect!(map.remove("Icmp6OutRedirects")),
1598            icmp_out_mldv2_reports: expect!(map.remove("Icmp6OutMLDv2Reports")),
1599
1600            //
1601            // ignore ICMP numeric types
1602            //
1603            udp_in_datagrams: expect!(map.remove("Udp6InDatagrams")),
1604            udp_no_ports: expect!(map.remove("Udp6NoPorts")),
1605            udp_in_errors: expect!(map.remove("Udp6InErrors")),
1606            udp_out_datagrams: expect!(map.remove("Udp6OutDatagrams")),
1607            udp_rcvbuf_errors: expect!(map.remove("Udp6RcvbufErrors")),
1608            udp_sndbuf_errors: expect!(map.remove("Udp6SndbufErrors")),
1609            udp_in_csum_errors: expect!(map.remove("Udp6InCsumErrors")),
1610            udp_ignored_multi: expect!(map.remove("Udp6IgnoredMulti")),
1611
1612            udp_lite_in_datagrams: expect!(map.remove("UdpLite6InDatagrams")),
1613            udp_lite_no_ports: expect!(map.remove("UdpLite6NoPorts")),
1614            udp_lite_in_errors: expect!(map.remove("UdpLite6InErrors")),
1615            udp_lite_out_datagrams: expect!(map.remove("UdpLite6OutDatagrams")),
1616            udp_lite_rcvbuf_errors: expect!(map.remove("UdpLite6RcvbufErrors")),
1617            udp_lite_sndbuf_errors: expect!(map.remove("UdpLite6SndbufErrors")),
1618            udp_lite_in_csum_errors: expect!(map.remove("UdpLite6InCsumErrors")),
1619        };
1620
1621        if cfg!(test) {
1622            assert!(map.is_empty(), "snmp6 map is not empty: {:#?}", map);
1623        }
1624
1625        Ok(snmp6)
1626    }
1627}
1628
1629#[cfg(test)]
1630mod tests {
1631    use super::*;
1632    use std::net::IpAddr;
1633
1634    #[test]
1635    fn test_parse_ipaddr() {
1636        use std::str::FromStr;
1637
1638        let addr = parse_addressport_str("0100007F:1234", true).unwrap();
1639        assert_eq!(addr.port(), 0x1234);
1640        match addr.ip() {
1641            IpAddr::V4(addr) => assert_eq!(addr, Ipv4Addr::new(127, 0, 0, 1)),
1642            _ => panic!("Not IPv4"),
1643        }
1644
1645        // When you connect to [2a00:1450:4001:814::200e]:80 (ipv6.google.com) the entry with
1646        // 5014002A14080140000000000E200000:0050 remote endpoint is created in /proc/net/tcp6
1647        // on Linux 4.19.
1648        let addr = parse_addressport_str("5014002A14080140000000000E200000:0050", true).unwrap();
1649        assert_eq!(addr.port(), 80);
1650        match addr.ip() {
1651            IpAddr::V6(addr) => assert_eq!(addr, Ipv6Addr::from_str("2a00:1450:4001:814::200e").unwrap()),
1652            _ => panic!("Not IPv6"),
1653        }
1654
1655        // IPv6 test case from https://stackoverflow.com/questions/41940483/parse-ipv6-addresses-from-proc-net-tcp6-python-2-7/41948004#41948004
1656        let addr = parse_addressport_str("B80D01200000000067452301EFCDAB89:0", true).unwrap();
1657        assert_eq!(addr.port(), 0);
1658        match addr.ip() {
1659            IpAddr::V6(addr) => assert_eq!(addr, Ipv6Addr::from_str("2001:db8::123:4567:89ab:cdef").unwrap()),
1660            _ => panic!("Not IPv6"),
1661        }
1662
1663        let addr = parse_addressport_str("1234:1234", true);
1664        assert!(addr.is_err());
1665    }
1666
1667    #[test]
1668    fn test_tcpstate_from() {
1669        assert_eq!(TcpState::from_u8(0xA).unwrap(), TcpState::Listen);
1670    }
1671
1672    #[test]
1673    fn test_snmp_debian_6_8_12() {
1674        // Sample from Debian 6.8.12-1
1675        let data = r#"Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates OutTransmits
1676Ip: 1 64 58881328 0 1 0 0 0 58879082 12449667 9745 1855 0 4 2 0 0 0 0 12449667
1677Icmp: InMsgs InErrors InCsumErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrors OutRateLimitGlobal OutRateLimitHost OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps
1678Icmp: 16667 83 0 16667 0 0 0 0 0 0 0 0 0 0 21854 0 2 81 21854 0 0 0 0 0 0 0 0 0 0
1679IcmpMsg: InType3 OutType3
1680IcmpMsg: 16667 21854
1681Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs InErrs OutRsts InCsumErrors
1682Tcp: 1 200 120000 -1 88170 33742 29003 4952 9 5129401 4676076 3246 60 40857 0
1683Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti MemErrors
1684Udp: 48327329 21522 6981741 9605045 6981727 9497 14 478236 0
1685UdpLite: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti MemErrors
1686UdpLite: 0 0 0 0 0 0 0 0 0
1687"#;
1688
1689        let r = std::io::Cursor::new(data.as_bytes());
1690        use crate::FromRead;
1691        let res = Snmp::from_read(r).unwrap();
1692        assert_eq!(res.ip_forwarding, IpForwarding::Forwarding);
1693        assert_eq!(res.ip_in_receives, 58881328);
1694        assert_eq!(res.ip_in_delivers, 58879082);
1695        assert_eq!(res.ip_out_requests, 12449667);
1696        assert_eq!(res.ip_out_no_routes, 1855);
1697        assert_eq!(res.tcp_rto_algorithm, TcpRtoAlgorithm::Other);
1698        assert_eq!(res.tcp_rto_min, 200);
1699        assert_eq!(res.tcp_rto_max, 120000);
1700        assert_eq!(res.tcp_max_conn, -1);
1701        assert_eq!(res.tcp_curr_estab, 9);
1702        assert_eq!(res.tcp_in_segs, 5129401);
1703        assert_eq!(res.tcp_out_segs, 4676076);
1704        assert_eq!(res.udp_in_datagrams, 48327329);
1705        assert_eq!(res.udp_in_csum_errors, 14);
1706        assert_eq!(res.udp_no_ports, 21522);
1707        assert_eq!(res.udp_out_datagrams, 9605045);
1708        println!("{res:?}");
1709    }
1710
1711    #[test]
1712    fn test_snmp_missing_icmp_msg() {
1713        // https://github.com/eminence/procfs/issues/310
1714        let data = r#"Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates OutTransmits
1715Ip: 2 64 12063 0 1 0 0 0 11952 8953 0 0 0 0 0 0 0 0 0 8953
1716Icmp: InMsgs InErrors InCsumErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrors OutRateLimitGlobal OutRateLimitHost OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps
1717Icmp: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1718Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs InErrs OutRsts InCsumErrors
1719Tcp: 1 200 120000 -1 177 14 0 6 4 11155 10083 18 0 94 0
1720Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti MemErrors
1721Udp: 2772 0 0 1890 0 0 0 745 0
1722UdpLite: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti MemErrors
1723UdpLite: 0 0 0 0 0 0 0 0 0
1724"#;
1725
1726        let r = std::io::Cursor::new(data.as_bytes());
1727        use crate::FromRead;
1728        let res = Snmp::from_read(r).unwrap();
1729        println!("{res:?}");
1730    }
1731}