diff options
| author | Kalle Lindström <karl.lindstrom@mullvad.net> | 2025-03-26 12:19:28 +0100 |
|---|---|---|
| committer | David Göransson <david.goransson@mullvad.net> | 2025-03-31 12:13:18 +0200 |
| commit | 9649d08f34ceaeac8a39f5c1760e2698f7da6659 (patch) | |
| tree | 65d63b124e30e2c2b40a027866ba784f1705febe /ci | |
| parent | c93339781c1fec5b9db5cb81cfcbb478c8311c18 (diff) | |
| download | mullvadvpn-9649d08f34ceaeac8a39f5c1760e2698f7da6659.tar.xz mullvadvpn-9649d08f34ceaeac8a39f5c1760e2698f7da6659.zip | |
Add ability to block IP by prefix
The REST API now accepts src and dst IP networks,
e.g. 45.83.223.0/24 which will block all addressess in
the matching subnet.
Diffstat (limited to 'ci')
| -rw-r--r-- | ci/ios/test-router/raas/Cargo.lock | 10 | ||||
| -rw-r--r-- | ci/ios/test-router/raas/Cargo.toml | 2 | ||||
| -rw-r--r-- | ci/ios/test-router/raas/src/block_list/rule.rs | 53 | ||||
| -rw-r--r-- | ci/ios/test-router/raas/src/capture/mod.rs | 3 | ||||
| -rw-r--r-- | ci/ios/test-router/raas/src/capture/parse.rs | 4 | ||||
| -rw-r--r-- | ci/ios/test-router/raas/src/web/routes.rs | 10 |
6 files changed, 52 insertions, 30 deletions
diff --git a/ci/ios/test-router/raas/Cargo.lock b/ci/ios/test-router/raas/Cargo.lock index ec9f29813b..04cc80d840 100644 --- a/ci/ios/test-router/raas/Cargo.lock +++ b/ci/ios/test-router/raas/Cargo.lock @@ -422,6 +422,15 @@ dependencies = [ ] [[package]] +name = "ipnetwork" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf370abdafd54d13e54a620e8c3e1145f28e46cc9d704bc6d94414559df41763" +dependencies = [ + "serde", +] + +[[package]] name = "is-terminal" version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -752,6 +761,7 @@ dependencies = [ "axum", "env_logger", "futures", + "ipnetwork", "log", "mnl", "nftnl", diff --git a/ci/ios/test-router/raas/Cargo.toml b/ci/ios/test-router/raas/Cargo.toml index 5bc0eacf6e..38b59f6480 100644 --- a/ci/ios/test-router/raas/Cargo.toml +++ b/ci/ios/test-router/raas/Cargo.toml @@ -29,4 +29,4 @@ serde = { version = "1.0.138", features = ["derive"] } serde_json = "1.0" uuid = { version = "1", features = [ "serde" ] } anyhow = "1" - +ipnetwork = { version = "0.21", features = [ "serde" ]} diff --git a/ci/ios/test-router/raas/src/block_list/rule.rs b/ci/ios/test-router/raas/src/block_list/rule.rs index 5bd876fe96..6604e9c3fa 100644 --- a/ci/ios/test-router/raas/src/block_list/rule.rs +++ b/ci/ios/test-router/raas/src/block_list/rule.rs @@ -2,7 +2,8 @@ use crate::web::routes::TransportProtocol; use mnl::mnl_sys::libc; use nftnl::{expr, nft_expr, nft_expr_payload, Chain, Rule}; -use std::{collections::BTreeSet, iter, net::IpAddr}; +use ipnetwork::IpNetwork; +use std::{collections::BTreeSet, iter}; #[derive(Clone, serde::Serialize)] pub enum BlockRule { @@ -17,8 +18,8 @@ pub enum BlockRule { #[derive(Clone, serde::Serialize)] pub struct Endpoints { - pub src: IpAddr, - pub dst: IpAddr, + pub src: IpNetwork, + pub dst: IpNetwork, } impl BlockRule { @@ -70,15 +71,15 @@ impl BlockRule { } } -fn check_l3proto(rule: &mut Rule<'_>, ip: IpAddr) { +fn check_l3proto(rule: &mut Rule<'_>, ip: IpNetwork) { rule.add_expr(&nft_expr!(meta nfproto)); rule.add_expr(&nft_expr!(cmp == l3proto(ip))); } -fn l3proto(addr: IpAddr) -> u8 { +fn l3proto(addr: IpNetwork) -> u8 { match addr { - IpAddr::V4(_) => libc::NFPROTO_IPV4 as u8, - IpAddr::V6(_) => libc::NFPROTO_IPV6 as u8, + IpNetwork::V4(_) => libc::NFPROTO_IPV4 as u8, + IpNetwork::V6(_) => libc::NFPROTO_IPV6 as u8, } } @@ -87,26 +88,38 @@ fn check_l4proto(rule: &mut Rule<'_>, protocol: TransportProtocol) { rule.add_expr(&nft_expr!(cmp == protocol.as_ipproto())); } -fn check_ip_addrs(rule: &mut Rule, src: IpAddr, dst: IpAddr) { +fn check_ip_addrs(rule: &mut Rule, src: IpNetwork, dst: IpNetwork) { // Add source checking rule.add_expr(match src { - IpAddr::V4(_) => &nft_expr!(payload ipv4 saddr), - IpAddr::V6(_) => &nft_expr!(payload ipv6 saddr), + IpNetwork::V4(_) => &nft_expr!(payload ipv4 saddr), + IpNetwork::V6(_) => &nft_expr!(payload ipv6 saddr), }); - match src { - IpAddr::V4(addr) => rule.add_expr(&nft_expr!(cmp == addr)), - IpAddr::V6(addr) => rule.add_expr(&nft_expr!(cmp == addr)), - }; + check_matches_prefix(rule, src); // Add destination check rule.add_expr(match dst { - IpAddr::V4(_) => &nft_expr!(payload ipv4 daddr), - IpAddr::V6(_) => &nft_expr!(payload ipv6 daddr), + IpNetwork::V4(_) => &nft_expr!(payload ipv4 daddr), + IpNetwork::V6(_) => &nft_expr!(payload ipv6 daddr), }); - match dst { - IpAddr::V4(addr) => rule.add_expr(&nft_expr!(cmp == addr)), - IpAddr::V6(addr) => rule.add_expr(&nft_expr!(cmp == addr)), - }; + check_matches_prefix(rule, dst); + + fn check_matches_prefix(rule: &mut Rule, network: IpNetwork) { + // Compute the bitwise AND of the incoming packet IP address and the mask, and then + // compare this value to the bitwise AND of the rule IP address and the mask. + // This will match when the network prefixes of the IP address to filter and rule + // IP address matches. E.g. the rule IP address/network 34.117.0.0/16 will match + // an incoming packet IP address 34.117.105.189. + match network { + IpNetwork::V4(addr) => { + rule.add_expr(&nft_expr!(bitwise mask addr.mask(), xor 0x0)); + rule.add_expr(&nft_expr!(cmp == addr.ip() & addr.mask())); + } + IpNetwork::V6(addr) => { + rule.add_expr(&nft_expr!(bitwise mask addr.mask(), xor 0x0)); + rule.add_expr(&nft_expr!(cmp == addr.ip() & addr.mask())); + } + }; + } } fn check_wireguard_traffic(rule: &mut Rule) { diff --git a/ci/ios/test-router/raas/src/capture/mod.rs b/ci/ios/test-router/raas/src/capture/mod.rs index 992da8395a..32f7296959 100644 --- a/ci/ios/test-router/raas/src/capture/mod.rs +++ b/ci/ios/test-router/raas/src/capture/mod.rs @@ -53,7 +53,7 @@ impl PacketCodec for CloneCodec { } } -const RAAS_TMP_DIR: &'static str = "raas"; +const RAAS_TMP_DIR: &str = "raas"; impl Capture { fn capture_file_path(label: &uuid::Uuid) -> PathBuf { @@ -94,6 +94,7 @@ impl Capture { let mut stream = capture.stream(CloneCodec).map_err(Error::CreateStream)?; let capture = tokio::spawn(async move { + #[allow(clippy::type_complexity)] let (pcap_tx, pcap_rx): (_, sync_mpsc::Receiver<(PacketHeader, Box<[u8]>)>) = sync_mpsc::channel(); tokio::task::spawn_blocking(move || { diff --git a/ci/ios/test-router/raas/src/capture/parse.rs b/ci/ios/test-router/raas/src/capture/parse.rs index 8da51d1f59..e8f96c280a 100644 --- a/ci/ios/test-router/raas/src/capture/parse.rs +++ b/ci/ios/test-router/raas/src/capture/parse.rs @@ -192,7 +192,7 @@ trait IpPacket: pnet_packet::Packet { } } -impl<'a> IpPacket for Ipv4Packet<'a> { +impl IpPacket for Ipv4Packet<'_> { fn source(&self) -> IpAddr { self.get_source().into() } @@ -206,7 +206,7 @@ impl<'a> IpPacket for Ipv4Packet<'a> { } } -impl<'a> IpPacket for Ipv6Packet<'a> { +impl IpPacket for Ipv6Packet<'_> { fn source(&self) -> IpAddr { self.get_source().into() } diff --git a/ci/ios/test-router/raas/src/web/routes.rs b/ci/ios/test-router/raas/src/web/routes.rs index 2b6432c5a2..43f83f1e34 100644 --- a/ci/ios/test-router/raas/src/web/routes.rs +++ b/ci/ios/test-router/raas/src/web/routes.rs @@ -3,12 +3,10 @@ use axum::{ http::StatusCode, response::IntoResponse, }; +use ipnetwork::IpNetwork; use mnl::mnl_sys::libc; +use std::collections::{BTreeMap, BTreeSet}; use std::sync::MutexGuard; -use std::{ - collections::{BTreeMap, BTreeSet}, - net::IpAddr, -}; use uuid::Uuid; use crate::block_list::{BlockList, BlockRule, Endpoints}; @@ -16,8 +14,8 @@ use crate::web; #[derive(serde::Deserialize, Clone)] pub struct NewRule { - pub src: IpAddr, - pub dst: IpAddr, + pub src: IpNetwork, + pub dst: IpNetwork, pub protocols: Option<BTreeSet<TransportProtocol>>, #[serde(default)] pub block_wireguard: bool, |
