sysinfo/windows/
network.rs1use 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 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 self.interfaces.retain(|_, i| {
154 if !i.inner.updated {
155 return false;
156 }
157 i.inner.updated = false;
158 true
159 });
160 }
161 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 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}