1use crate::condition::Protocol;
31use crate::constants::*;
32use crate::engine::WfpEngine;
33use crate::errors::{WfpError, WfpResult};
34use crate::filter::{Action, FilterRule};
35use crate::layer;
36use std::net::IpAddr;
37use std::ptr;
38use windows::core::{GUID, PWSTR};
39use windows::Win32::Foundation::ERROR_SUCCESS;
40use windows::Win32::NetworkManagement::WindowsFilteringPlatform::{
41 FwpmFilterAdd0, FwpmFilterDeleteById0, FwpmFreeMemory0, FwpmGetAppIdFromFileName0,
42 FWPM_FILTER0, FWPM_FILTER_CONDITION0, FWPM_FILTER_FLAGS, FWP_ACTION_BLOCK, FWP_ACTION_PERMIT,
43 FWP_ACTION_TYPE, FWP_BYTE_BLOB, FWP_BYTE_BLOB_TYPE, FWP_CONDITION_VALUE0, FWP_MATCH_EQUAL,
44 FWP_UINT16, FWP_UINT64, FWP_UINT8, FWP_V4_ADDR_AND_MASK, FWP_V4_ADDR_MASK,
45 FWP_V6_ADDR_AND_MASK, FWP_V6_ADDR_MASK,
46};
47
48pub struct FilterBuilder;
70
71impl FilterBuilder {
72 fn translate_action(action: Action) -> FWP_ACTION_TYPE {
74 match action {
75 Action::Permit => FWP_ACTION_PERMIT,
76 Action::Block => FWP_ACTION_BLOCK,
77 }
78 }
79
80 fn translate_protocol(protocol: Protocol) -> u8 {
82 protocol.as_u8()
83 }
84
85 fn prefix_to_v4_mask(prefix_len: u8) -> u32 {
89 if prefix_len == 0 {
90 0
91 } else if prefix_len >= 32 {
92 0xFFFFFFFF
93 } else {
94 u32::MAX << (32 - prefix_len)
95 }
96 }
97
98 pub fn add_filter(engine: &WfpEngine, rule: &FilterRule) -> WfpResult<u64> {
120 let is_ipv6 = rule
122 .remote_ip
123 .as_ref()
124 .map(|ip_mask| matches!(ip_mask.addr, IpAddr::V6(_)))
125 .unwrap_or(false);
126
127 let layer_key = layer::select_layer(rule.direction, is_ipv6);
128 let action = Self::translate_action(rule.action);
129
130 let name_wide: Vec<u16> = rule.name.encode_utf16().chain(std::iter::once(0)).collect();
132
133 let weight_value: u64 = rule.weight;
135
136 let app_id_blob: Option<(*mut FWP_BYTE_BLOB, bool)> =
138 rule.app_path.as_ref().and_then(|app_path| unsafe {
139 let path_str = app_path.to_string_lossy().to_string();
140 let path_wide: Vec<u16> =
141 path_str.encode_utf16().chain(std::iter::once(0)).collect();
142 let pwstr = PWSTR(path_wide.as_ptr() as *mut u16);
143
144 let mut blob_ptr: *mut FWP_BYTE_BLOB = ptr::null_mut();
145 let result = FwpmGetAppIdFromFileName0(pwstr, &mut blob_ptr);
146
147 if result == ERROR_SUCCESS.0 {
148 Some((blob_ptr, true))
149 } else {
150 None
151 }
152 });
153
154 let remote_v4_mask: Option<FWP_V4_ADDR_AND_MASK> =
155 rule.remote_ip.as_ref().and_then(|remote_ip| {
156 if let IpAddr::V4(ipv4) = remote_ip.addr {
157 Some(FWP_V4_ADDR_AND_MASK {
158 addr: u32::from_be_bytes(ipv4.octets()),
159 mask: Self::prefix_to_v4_mask(remote_ip.prefix_len),
160 })
161 } else {
162 None
163 }
164 });
165
166 let remote_v6_mask: Option<FWP_V6_ADDR_AND_MASK> =
167 rule.remote_ip.as_ref().and_then(|remote_ip| {
168 if let IpAddr::V6(ipv6) = remote_ip.addr {
169 Some(FWP_V6_ADDR_AND_MASK {
170 addr: ipv6.octets(),
171 prefixLength: remote_ip.prefix_len,
172 })
173 } else {
174 None
175 }
176 });
177
178 let local_v4_mask: Option<FWP_V4_ADDR_AND_MASK> =
179 rule.local_ip.as_ref().and_then(|local_ip| {
180 if let IpAddr::V4(ipv4) = local_ip.addr {
181 Some(FWP_V4_ADDR_AND_MASK {
182 addr: u32::from_be_bytes(ipv4.octets()),
183 mask: Self::prefix_to_v4_mask(local_ip.prefix_len),
184 })
185 } else {
186 None
187 }
188 });
189
190 let local_v6_mask: Option<FWP_V6_ADDR_AND_MASK> =
191 rule.local_ip.as_ref().and_then(|local_ip| {
192 if let IpAddr::V6(ipv6) = local_ip.addr {
193 Some(FWP_V6_ADDR_AND_MASK {
194 addr: ipv6.octets(),
195 prefixLength: local_ip.prefix_len,
196 })
197 } else {
198 None
199 }
200 });
201
202 let mut conditions = Vec::new();
204
205 if let Some((blob_ptr, _)) = app_id_blob {
207 conditions.push(FWPM_FILTER_CONDITION0 {
208 fieldKey: CONDITION_ALE_APP_ID,
209 matchType: FWP_MATCH_EQUAL,
210 conditionValue: FWP_CONDITION_VALUE0 {
211 r#type: FWP_BYTE_BLOB_TYPE,
212 Anonymous: windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_CONDITION_VALUE0_0 {
213 byteBlob: blob_ptr,
214 },
215 },
216 });
217 }
218
219 if let Some(remote_port) = rule.remote_port {
221 conditions.push(FWPM_FILTER_CONDITION0 {
222 fieldKey: CONDITION_IP_REMOTE_PORT,
223 matchType: FWP_MATCH_EQUAL,
224 conditionValue: FWP_CONDITION_VALUE0 {
225 r#type: FWP_UINT16,
226 Anonymous: windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_CONDITION_VALUE0_0 {
227 uint16: remote_port,
228 },
229 },
230 });
231 }
232
233 if let Some(local_port) = rule.local_port {
235 conditions.push(FWPM_FILTER_CONDITION0 {
236 fieldKey: CONDITION_IP_LOCAL_PORT,
237 matchType: FWP_MATCH_EQUAL,
238 conditionValue: FWP_CONDITION_VALUE0 {
239 r#type: FWP_UINT16,
240 Anonymous: windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_CONDITION_VALUE0_0 {
241 uint16: local_port,
242 },
243 },
244 });
245 }
246
247 if let Some(protocol) = rule.protocol {
249 conditions.push(FWPM_FILTER_CONDITION0 {
250 fieldKey: CONDITION_IP_PROTOCOL,
251 matchType: FWP_MATCH_EQUAL,
252 conditionValue: FWP_CONDITION_VALUE0 {
253 r#type: FWP_UINT8,
254 Anonymous: windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_CONDITION_VALUE0_0 {
255 uint8: Self::translate_protocol(protocol),
256 },
257 },
258 });
259 }
260
261 if let Some(ref mask) = remote_v4_mask {
263 conditions.push(FWPM_FILTER_CONDITION0 {
264 fieldKey: CONDITION_IP_REMOTE_ADDRESS,
265 matchType: FWP_MATCH_EQUAL,
266 conditionValue: FWP_CONDITION_VALUE0 {
267 r#type: FWP_V4_ADDR_MASK,
268 Anonymous: windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_CONDITION_VALUE0_0 {
269 v4AddrMask: mask as *const _ as *mut _,
270 },
271 },
272 });
273 }
274
275 if let Some(ref mask) = remote_v6_mask {
277 conditions.push(FWPM_FILTER_CONDITION0 {
278 fieldKey: CONDITION_IP_REMOTE_ADDRESS,
279 matchType: FWP_MATCH_EQUAL,
280 conditionValue: FWP_CONDITION_VALUE0 {
281 r#type: FWP_V6_ADDR_MASK,
282 Anonymous: windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_CONDITION_VALUE0_0 {
283 v6AddrMask: mask as *const _ as *mut _,
284 },
285 },
286 });
287 }
288
289 if let Some(ref mask) = local_v4_mask {
291 conditions.push(FWPM_FILTER_CONDITION0 {
292 fieldKey: CONDITION_IP_LOCAL_ADDRESS,
293 matchType: FWP_MATCH_EQUAL,
294 conditionValue: FWP_CONDITION_VALUE0 {
295 r#type: FWP_V4_ADDR_MASK,
296 Anonymous: windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_CONDITION_VALUE0_0 {
297 v4AddrMask: mask as *const _ as *mut _,
298 },
299 },
300 });
301 }
302
303 if let Some(ref mask) = local_v6_mask {
305 conditions.push(FWPM_FILTER_CONDITION0 {
306 fieldKey: CONDITION_IP_LOCAL_ADDRESS,
307 matchType: FWP_MATCH_EQUAL,
308 conditionValue: FWP_CONDITION_VALUE0 {
309 r#type: FWP_V6_ADDR_MASK,
310 Anonymous: windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_CONDITION_VALUE0_0 {
311 v6AddrMask: mask as *const _ as *mut _,
312 },
313 },
314 });
315 }
316
317 let filter = FWPM_FILTER0 {
319 filterKey: GUID::zeroed(),
320 displayData:
321 windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWPM_DISPLAY_DATA0 {
322 name: PWSTR(name_wide.as_ptr() as *mut u16),
323 description: PWSTR::null(),
324 },
325 flags: FWPM_FILTER_FLAGS(0),
326 providerKey: &TRUSTY_PROVIDER_GUID as *const _ as *mut _,
327 providerData:
328 windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_BYTE_BLOB {
329 size: 0,
330 data: ptr::null_mut(),
331 },
332 layerKey: layer_key,
333 subLayerKey: TRUSTY_SUBLAYER_GUID,
334 weight: windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_VALUE0 {
335 r#type: FWP_UINT64,
336 Anonymous:
337 windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_VALUE0_0 {
338 uint64: &weight_value as *const u64 as *mut u64,
339 },
340 },
341 numFilterConditions: conditions.len() as u32,
342 filterCondition: if conditions.is_empty() {
343 ptr::null_mut()
344 } else {
345 conditions.as_ptr() as *mut _
346 },
347 action: windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWPM_ACTION0 {
348 r#type: action,
349 Anonymous: Default::default(),
350 },
351 Anonymous: Default::default(),
352 reserved: ptr::null_mut(),
353 filterId: 0,
354 effectiveWeight: Default::default(),
355 };
356
357 let mut filter_id: u64 = 0;
358
359 unsafe {
360 let result = FwpmFilterAdd0(engine.handle(), &filter, None, Some(&mut filter_id));
361
362 if let Some((mut blob_ptr, needs_free)) = app_id_blob {
364 if needs_free && !blob_ptr.is_null() {
365 FwpmFreeMemory0(&mut blob_ptr as *mut _ as *mut *mut _);
366 }
367 }
368
369 if result != ERROR_SUCCESS.0 {
370 return Err(WfpError::FilterAddFailed(format!(
371 "Failed to add filter '{}': error code {}",
372 rule.name, result
373 )));
374 }
375 }
376
377 Ok(filter_id)
378 }
379
380 pub fn delete_filter(engine: &WfpEngine, filter_id: u64) -> WfpResult<()> {
405 unsafe {
406 let result = FwpmFilterDeleteById0(engine.handle(), filter_id);
407
408 if result != ERROR_SUCCESS.0 {
409 return Err(WfpError::FilterDeleteFailed(format!(
410 "Failed to delete filter ID {}: error code {}",
411 filter_id, result
412 )));
413 }
414 }
415
416 Ok(())
417 }
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423 use crate::condition::Protocol;
424 use crate::filter::{Action, FilterRule};
425
426 #[test]
427 fn test_action_translation() {
428 assert_eq!(
429 FilterBuilder::translate_action(Action::Permit),
430 FWP_ACTION_PERMIT
431 );
432 assert_eq!(
433 FilterBuilder::translate_action(Action::Block),
434 FWP_ACTION_BLOCK
435 );
436 }
437
438 #[test]
439 fn test_protocol_translation() {
440 assert_eq!(FilterBuilder::translate_protocol(Protocol::Hopopt), 0);
441 assert_eq!(FilterBuilder::translate_protocol(Protocol::Icmp), 1);
442 assert_eq!(FilterBuilder::translate_protocol(Protocol::Igmp), 2);
443 assert_eq!(FilterBuilder::translate_protocol(Protocol::Tcp), 6);
444 assert_eq!(FilterBuilder::translate_protocol(Protocol::Udp), 17);
445 assert_eq!(FilterBuilder::translate_protocol(Protocol::Gre), 47);
446 assert_eq!(FilterBuilder::translate_protocol(Protocol::Esp), 50);
447 assert_eq!(FilterBuilder::translate_protocol(Protocol::Ah), 51);
448 assert_eq!(FilterBuilder::translate_protocol(Protocol::Icmpv6), 58);
449 }
450
451 #[test]
452 fn test_prefix_to_v4_mask() {
453 assert_eq!(FilterBuilder::prefix_to_v4_mask(0), 0x00000000);
454 assert_eq!(FilterBuilder::prefix_to_v4_mask(8), 0xFF000000);
455 assert_eq!(FilterBuilder::prefix_to_v4_mask(16), 0xFFFF0000);
456 assert_eq!(FilterBuilder::prefix_to_v4_mask(24), 0xFFFFFF00);
457 assert_eq!(FilterBuilder::prefix_to_v4_mask(32), 0xFFFFFFFF);
458 }
459
460 #[test]
461 fn test_prefix_to_v4_mask_all_values() {
462 assert_eq!(FilterBuilder::prefix_to_v4_mask(1), 0x80000000);
463 assert_eq!(FilterBuilder::prefix_to_v4_mask(4), 0xF0000000);
464 assert_eq!(FilterBuilder::prefix_to_v4_mask(12), 0xFFF00000);
465 assert_eq!(FilterBuilder::prefix_to_v4_mask(20), 0xFFFFF000);
466 assert_eq!(FilterBuilder::prefix_to_v4_mask(28), 0xFFFFFFF0);
467 assert_eq!(FilterBuilder::prefix_to_v4_mask(31), 0xFFFFFFFE);
468 }
469
470 #[test]
471 fn test_prefix_to_v4_mask_overflow() {
472 assert_eq!(FilterBuilder::prefix_to_v4_mask(33), 0xFFFFFFFF);
474 assert_eq!(FilterBuilder::prefix_to_v4_mask(255), 0xFFFFFFFF);
475 }
476
477 #[test]
478 #[ignore] fn test_add_simple_filter() {
480 let engine = WfpEngine::new().expect("Failed to create engine");
481 crate::initialize_wfp(&engine).expect("Failed to initialize WFP");
482
483 let rule = FilterRule::allow_all_outbound();
484 let result = FilterBuilder::add_filter(&engine, &rule);
485
486 assert!(result.is_ok(), "Failed to add filter: {:?}", result);
487 }
488
489 #[test]
490 #[ignore] fn test_add_and_delete_filter() {
492 let engine = WfpEngine::new().expect("Failed to create engine");
493 crate::initialize_wfp(&engine).expect("Failed to initialize WFP");
494
495 let rule = FilterRule::allow_all_outbound();
496
497 let filter_id = FilterBuilder::add_filter(&engine, &rule).expect("Failed to add filter");
498 let result = FilterBuilder::delete_filter(&engine, filter_id);
499
500 assert!(result.is_ok(), "Failed to delete filter: {:?}", result);
501 }
502}