summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKalle Lindström <karl.lindstrom@mullvad.net>2025-03-26 12:19:28 +0100
committerDavid Göransson <david.goransson@mullvad.net>2025-03-31 12:13:18 +0200
commit9649d08f34ceaeac8a39f5c1760e2698f7da6659 (patch)
tree65d63b124e30e2c2b40a027866ba784f1705febe
parentc93339781c1fec5b9db5cb81cfcbb478c8311c18 (diff)
downloadmullvadvpn-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.
-rw-r--r--ci/ios/test-router/raas/Cargo.lock10
-rw-r--r--ci/ios/test-router/raas/Cargo.toml2
-rw-r--r--ci/ios/test-router/raas/src/block_list/rule.rs53
-rw-r--r--ci/ios/test-router/raas/src/capture/mod.rs3
-rw-r--r--ci/ios/test-router/raas/src/capture/parse.rs4
-rw-r--r--ci/ios/test-router/raas/src/web/routes.rs10
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,