sysinfo/windows/
network.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::network::refresh_networks_addresses;
4use crate::{IpNetwork, MacAddr, NetworkData};
5
6use std::collections::{HashMap, hash_map};
7
8use windows::Win32::NetworkManagement::IpHelper::{FreeMibTable, GetIfTable2, MIB_IF_TABLE2};
9use windows::Win32::NetworkManagement::Ndis::MediaConnectStateDisconnected;
10
11macro_rules! old_and_new {
12    ($ty_:expr, $name:ident, $old:ident, $new_val:expr) => {{
13        $ty_.$old = $ty_.$name;
14        $ty_.$name = $new_val;
15    }};
16}
17
18pub(crate) struct NetworksInner {
19    pub(crate) interfaces: HashMap<String, NetworkData>,
20}
21
22impl NetworksInner {
23    pub(crate) fn new() -> Self {
24        Self {
25            interfaces: HashMap::new(),
26        }
27    }
28
29    pub(crate) fn list(&self) -> &HashMap<String, NetworkData> {
30        &self.interfaces
31    }
32
33    pub(crate) fn refresh(&mut self, remove_not_listed_interfaces: bool) {
34        let mut table: *mut MIB_IF_TABLE2 = std::ptr::null_mut();
35
36        unsafe {
37            if GetIfTable2(&mut table).is_err() {
38                return;
39            }
40
41            for (_, data) in self.interfaces.iter_mut() {
42                data.inner.updated = false;
43            }
44
45            // In here, this is tricky: we have to filter out the software interfaces to only keep
46            // the hardware ones. To do so, we first check the connection potential speed (if 0, not
47            // interesting), then we check its state: if not open, not interesting either. And finally,
48            // we count the members of a same group: if there is more than 1, then it's software level.
49            let mut groups = HashMap::new();
50            let mut indexes = Vec::new();
51            let ptr = (*table).Table.as_ptr();
52            for i in 0..(*table).NumEntries {
53                let ptr = &*ptr.offset(i as _);
54                if (ptr.TransmitLinkSpeed == 0 && ptr.ReceiveLinkSpeed == 0)
55                    || ptr.MediaConnectState == MediaConnectStateDisconnected
56                    || ptr.PhysicalAddressLength == 0
57                {
58                    continue;
59                }
60                let id = vec![
61                    ptr.InterfaceGuid.data2,
62                    ptr.InterfaceGuid.data3,
63                    ptr.InterfaceGuid.data4[0] as _,
64                    ptr.InterfaceGuid.data4[1] as _,
65                    ptr.InterfaceGuid.data4[2] as _,
66                    ptr.InterfaceGuid.data4[3] as _,
67                    ptr.InterfaceGuid.data4[4] as _,
68                    ptr.InterfaceGuid.data4[5] as _,
69                    ptr.InterfaceGuid.data4[6] as _,
70                    ptr.InterfaceGuid.data4[7] as _,
71                ];
72                let entry = groups.entry(id.clone()).or_insert(0);
73                *entry += 1;
74                if *entry > 1 {
75                    continue;
76                }
77                indexes.push((i, id));
78            }
79            for (i, id) in indexes {
80                let ptr = &*ptr.offset(i as _);
81                if *groups.get(&id).unwrap_or(&0) > 1 {
82                    continue;
83                }
84                let mut pos = 0;
85                for x in ptr.Alias.iter() {
86                    if *x == 0 {
87                        break;
88                    }
89                    pos += 1;
90                }
91                let interface_name = match String::from_utf16(&ptr.Alias[..pos]) {
92                    Ok(s) => s,
93                    _ => continue,
94                };
95
96                let mtu = ptr.Mtu as u64;
97                match self.interfaces.entry(interface_name) {
98                    hash_map::Entry::Occupied(mut e) => {
99                        let interface = e.get_mut();
100                        let interface = &mut interface.inner;
101                        old_and_new!(interface, current_out, old_out, ptr.OutOctets);
102                        old_and_new!(interface, current_in, old_in, ptr.InOctets);
103                        old_and_new!(
104                            interface,
105                            packets_in,
106                            old_packets_in,
107                            ptr.InUcastPkts.saturating_add(ptr.InNUcastPkts)
108                        );
109                        old_and_new!(
110                            interface,
111                            packets_out,
112                            old_packets_out,
113                            ptr.OutUcastPkts.saturating_add(ptr.OutNUcastPkts)
114                        );
115                        old_and_new!(interface, errors_in, old_errors_in, ptr.InErrors);
116                        old_and_new!(interface, errors_out, old_errors_out, ptr.OutErrors);
117                        if interface.mtu != mtu {
118                            interface.mtu = mtu;
119                        }
120                        interface.updated = true;
121                    }
122                    hash_map::Entry::Vacant(e) => {
123                        let packets_in = ptr.InUcastPkts.saturating_add(ptr.InNUcastPkts);
124                        let packets_out = ptr.OutUcastPkts.saturating_add(ptr.OutNUcastPkts);
125
126                        e.insert(NetworkData {
127                            inner: NetworkDataInner {
128                                current_out: ptr.OutOctets,
129                                old_out: ptr.OutOctets,
130                                current_in: ptr.InOctets,
131                                old_in: ptr.InOctets,
132                                packets_in,
133                                old_packets_in: packets_in,
134                                packets_out,
135                                old_packets_out: packets_out,
136                                errors_in: ptr.InErrors,
137                                old_errors_in: ptr.InErrors,
138                                errors_out: ptr.OutErrors,
139                                old_errors_out: ptr.OutErrors,
140                                mac_addr: MacAddr::UNSPECIFIED,
141                                ip_networks: vec![],
142                                mtu,
143                                updated: true,
144                            },
145                        });
146                    }
147                }
148            }
149            FreeMibTable(table as _);
150        }
151        if remove_not_listed_interfaces {
152            // Remove interfaces which are gone.
153            self.interfaces.retain(|_, i| {
154                if !i.inner.updated {
155                    return false;
156                }
157                i.inner.updated = false;
158                true
159            });
160        }
161        // Refresh all interfaces' addresses.
162        refresh_networks_addresses(&mut self.interfaces);
163    }
164}
165
166pub(crate) struct NetworkDataInner {
167    current_out: u64,
168    old_out: u64,
169    current_in: u64,
170    old_in: u64,
171    packets_in: u64,
172    old_packets_in: u64,
173    packets_out: u64,
174    old_packets_out: u64,
175    errors_in: u64,
176    old_errors_in: u64,
177    errors_out: u64,
178    old_errors_out: u64,
179    updated: bool,
180    pub(crate) mac_addr: MacAddr,
181    pub(crate) ip_networks: Vec<IpNetwork>,
182    /// Interface Maximum Transfer Unit (MTU)
183    mtu: u64,
184}
185
186impl NetworkDataInner {
187    pub(crate) fn received(&self) -> u64 {
188        self.current_in.saturating_sub(self.old_in)
189    }
190
191    pub(crate) fn total_received(&self) -> u64 {
192        self.current_in
193    }
194
195    pub(crate) fn transmitted(&self) -> u64 {
196        self.current_out.saturating_sub(self.old_out)
197    }
198
199    pub(crate) fn total_transmitted(&self) -> u64 {
200        self.current_out
201    }
202
203    pub(crate) fn packets_received(&self) -> u64 {
204        self.packets_in.saturating_sub(self.old_packets_in)
205    }
206
207    pub(crate) fn total_packets_received(&self) -> u64 {
208        self.packets_in
209    }
210
211    pub(crate) fn packets_transmitted(&self) -> u64 {
212        self.packets_out.saturating_sub(self.old_packets_out)
213    }
214
215    pub(crate) fn total_packets_transmitted(&self) -> u64 {
216        self.packets_out
217    }
218
219    pub(crate) fn errors_on_received(&self) -> u64 {
220        self.errors_in.saturating_sub(self.old_errors_in)
221    }
222
223    pub(crate) fn total_errors_on_received(&self) -> u64 {
224        self.errors_in
225    }
226
227    pub(crate) fn errors_on_transmitted(&self) -> u64 {
228        self.errors_out.saturating_sub(self.old_errors_out)
229    }
230
231    pub(crate) fn total_errors_on_transmitted(&self) -> u64 {
232        self.errors_out
233    }
234
235    pub(crate) fn mac_address(&self) -> MacAddr {
236        self.mac_addr
237    }
238
239    pub(crate) fn ip_networks(&self) -> &[IpNetwork] {
240        &self.ip_networks
241    }
242
243    pub(crate) fn mtu(&self) -> u64 {
244        self.mtu
245    }
246}