Compare commits

..

33 Commits

Author SHA1 Message Date
Jon Häggblad 161a8a7d4d Create tun_common subdir 2023-11-20 10:19:47 +01:00
Jon Häggblad 25aa04686e new structure compiles 2023-11-20 10:06:34 +01:00
Jon Häggblad 9b6355b256 wip 2023-11-20 08:55:26 +01:00
Jon Häggblad 65272d7bf6 wip: extract crates 2023-11-18 15:29:55 +01:00
Jon Häggblad 810dce5ee8 Use common interface request response 2023-11-18 14:17:27 +01:00
Jon Häggblad 58ec878256 wip 2023-11-17 16:21:40 +01:00
Bogdan-Ștefan Neacşu a5c1e4abf0 Expose the same pub key that's used for wg (#4157) 2023-11-17 13:04:22 +00:00
Jon Häggblad 3a1003c564 Create TaggedPacket (#4156)
* Create TaggedPacket

* Fix bug passing the correct data
2023-11-17 12:30:15 +01:00
Jon Häggblad 1cdd8f6c08 Rework error handling in tun device (#4146)
* Rework error handling in tun device

* Extract out timeout constants

* Experiment with timeouts

* Update error msg

* try_send in one direction as hotfix for deadlock

* Downgrade some log from info to debug

* Update comment

* rustfmt
2023-11-17 09:52:05 +01:00
Jon Häggblad 808e3f0562 Merge pull request #4154 from nymtech/jon/clippy
Fix clippy for latest rustc
2023-11-17 09:19:02 +01:00
Jon Häggblad f0dade3c5b Fix clippy in ephemera 2023-11-17 09:15:42 +01:00
Jon Häggblad 0a3c2b3cca Upgrade to safer-ffi 0.1.4 for clippy 2023-11-17 09:06:12 +01:00
Jon Häggblad ac66906980 IPR: add exit policy (#4127)
* Copy over request_filter

* Comment out stuff we don't need

* Delete unused allowed_hosts

* Delete unused code in request_filter

* Setup request filter

* Handle address checks

* rustfmt

* Tweak errors

* clippy

* allow dead code for non-linux

* inline log_msg

* Add ParsedPacket type
2023-11-16 14:13:13 +01:00
Gala afd9f823d8 Merge pull request #4151 from nymtech/feat/explorer-vpnsite-buttom
Feat/explorer vpnsite buttom
2023-11-16 13:08:06 +01:00
serinko d818448848 DOC: hotfix 2023-11-16 12:07:20 +00:00
Gala a9a1ba2847 please lint.. 2023-11-16 12:50:10 +01:00
Drazen Urch 2708c0ce10 Feature/deb package (#4153)
* Add debian scaffolding, allow specifying home_dir in env

* Run as nym user
2023-11-16 12:35:02 +01:00
Gala bb3e9b3d4e remove non used variable 2023-11-16 12:33:43 +01:00
Gala e624f42ad5 fixing build 2023-11-16 12:03:23 +01:00
mx 7da83397dd Merge pull request #4152 from nymtech/feature/docs/sort-info
DOCs: Operators - create tables to clarify Smoosh progress
2023-11-16 10:18:00 +00:00
serinko 26d0b4b159 create tables to clarify Smoosh progress 2023-11-16 11:12:40 +01:00
mx b74490dc50 Merge pull request #4150 from nymtech/feature/ts-sdk-fixes
adding SURBs info to mixfetch
2023-11-15 17:19:59 +00:00
Jędrzej Stuczyński 8113095ff5 remove needless borrow (#4149) 2023-11-15 16:34:32 +01:00
Gala 8339d6ab49 nymvpn link on footer 2023-11-15 16:20:14 +01:00
Gala f037b2ae68 adding nymvpn link to explorer 2023-11-15 15:58:31 +01:00
Zane Schepke 2a4c1d96a4 Update README.md 2023-11-15 09:37:21 -05:00
Lorexia ed04ddf1c4 adding SURBs info to mixfetch 2023-11-15 15:35:10 +01:00
Zane Schepke 34b5d66df6 Update README.md 2023-11-15 09:35:04 -05:00
Tommy Verrall 0a1a5c25f7 Merge pull request #4148 from nymtech/chore/add-update-cost-params
Add update cost params to the NYM-CLI
2023-11-15 13:58:07 +00:00
Jędrzej Stuczyński 6bdba7046f Bugfix/prerelease versionbump (#4145)
* prerelease updating rc suffix

* added post-run summary

* updated error message
2023-11-15 13:58:21 +01:00
Tommy Verrall 428d91a536 fmt 2023-11-15 12:37:01 +00:00
Tommy Verrall 88e0eaafcb update args to pass through correctly 2023-11-15 12:32:23 +00:00
Tommy Verrall dd19cabf15 adding the cost parameter update to the nym-cli 2023-11-15 12:30:02 +00:00
49 changed files with 918 additions and 285 deletions
Generated
+44 -5
View File
@@ -6657,22 +6657,40 @@ dependencies = [
"thiserror",
]
[[package]]
name = "nym-ip-packet-requests"
version = "0.1.0"
dependencies = [
"bincode",
"bytes",
"nym-service-providers-common",
"nym-sphinx",
"serde",
]
[[package]]
name = "nym-ip-packet-router"
version = "0.1.0"
dependencies = [
"bincode",
"bytes",
"etherparse",
"futures",
"log",
"nym-bin-common",
"nym-client-core",
"nym-config",
"nym-exit-policy",
"nym-ip-packet-requests",
"nym-network-requester",
"nym-sdk",
"nym-service-providers-common",
"nym-sphinx",
"nym-task",
"nym-tun",
"nym-wireguard",
"nym-wireguard-types",
"reqwest",
"serde",
"serde_json",
"tap",
@@ -6917,6 +6935,7 @@ dependencies = [
"nym-crypto",
"nym-node-requests",
"nym-task",
"nym-wireguard",
"nym-wireguard-types",
"rand 0.7.3",
"serde",
@@ -7459,6 +7478,19 @@ dependencies = [
"wasm-utils",
]
[[package]]
name = "nym-tun"
version = "0.1.0"
dependencies = [
"boringtun",
"etherparse",
"log",
"nym-wireguard-types",
"thiserror",
"tokio",
"tokio-tun",
]
[[package]]
name = "nym-types"
version = "1.0.0"
@@ -7578,6 +7610,7 @@ version = "0.1.0"
dependencies = [
"async-recursion",
"base64 0.21.4",
"bincode",
"boringtun",
"bytes",
"dashmap",
@@ -7586,14 +7619,15 @@ dependencies = [
"ip_network",
"ip_network_table",
"log",
"nym-sphinx",
"nym-task",
"nym-tun",
"nym-wireguard-types",
"rand 0.8.5",
"serde",
"tap",
"thiserror",
"tokio",
"tokio-stream",
"tokio-tun",
]
@@ -7603,14 +7637,19 @@ version = "0.1.0"
dependencies = [
"base64 0.21.4",
"boringtun",
"bytes",
"dashmap",
"hmac 0.12.1",
"ip_network",
"ip_network_table",
"log",
"nym-crypto",
"rand 0.7.3",
"serde",
"serde_json",
"sha2 0.10.8",
"thiserror",
"tokio",
"utoipa",
"x25519-dalek 2.0.0",
]
@@ -9440,9 +9479,9 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "safer-ffi"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9c1d19b288ca9898cd421c7b105fb7269918a7f8e9253a991e228981ca421ad"
checksum = "395ace5aff9629c7268ca8255aceb945525b2cb644015f3caec5131a6a537c11"
dependencies = [
"inventory",
"libc",
@@ -9457,9 +9496,9 @@ dependencies = [
[[package]]
name = "safer_ffi-proc_macros"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d7a04caa3ca2224f5ea4ddd850e2629c3b36b2b83621f87a8303bf41020110"
checksum = "9255504d5467bae9e07d58b8de446ba6739b29bf72e1fa35b2387e30d29dcbfe"
dependencies = [
"macro_rules_attribute",
"prettyplease",
+2
View File
@@ -49,6 +49,7 @@ members = [
"common/exit-policy",
"common/http-api-client",
"common/inclusion-probability",
"common/ip-packet-requests",
"common/ledger",
"common/mixnode-common",
"common/network-defaults",
@@ -74,6 +75,7 @@ members = [
"common/store-cipher",
"common/task",
"common/topology",
"common/tun",
"common/types",
"common/wasm/client-core",
"common/wasm/storage",
@@ -4,6 +4,7 @@
use clap::{Args, Subcommand};
pub mod update_config;
pub mod update_cost_params;
pub mod vesting_update_config;
#[derive(Debug, Args)]
@@ -20,7 +21,5 @@ pub enum MixnetOperatorsMixnodeSettingsCommands {
/// Update mixnode configuration for a mixnode bonded with locked tokens
VestingUpdateConfig(vesting_update_config::Args),
/// Update mixnode cost parameters
UpdateCostParameters,
/// Update mixnode cost parameters for a mixnode bonded with locked tokens
VestingUpdateCostParameters,
UpdateCostParameters(update_cost_params::Args),
}
@@ -0,0 +1,48 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use cosmwasm_std::Uint128;
use log::info;
use nym_mixnet_contract_common::{MixNodeCostParams, Percent};
use nym_validator_client::nyxd::contract_traits::MixnetSigningClient;
use nym_validator_client::nyxd::CosmWasmCoin;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(
long,
help = "input your profit margin as follows; (so it would be 10, rather than 0.1)"
)]
pub profit_margin_percent: Option<u8>,
#[clap(
long,
help = "operating cost in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub interval_operating_cost: Option<u128>,
}
pub async fn update_cost_params(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
let cost_params = MixNodeCostParams {
profit_margin_percent: Percent::from_percentage_value(
args.profit_margin_percent.unwrap_or(10) as u64,
)
.unwrap(),
interval_operating_cost: CosmWasmCoin {
denom: denom.into(),
amount: Uint128::new(args.interval_operating_cost.unwrap_or(40_000_000)),
},
};
info!("Starting mixnode params updating!");
let res = client
.update_mixnode_cost_params(cost_params, None)
.await
.expect("failed to update cost params");
info!("Cost params result: {:?}", res)
}
+10 -2
View File
@@ -25,12 +25,20 @@ pub const DEFAULT_CONFIG_FILENAME: &str = "config.toml";
#[cfg(feature = "dirs")]
pub fn must_get_home() -> PathBuf {
dirs::home_dir().expect("Failed to evaluate $HOME value")
if let Some(home_dir) = std::env::var_os("NYM_HOME_DIR") {
home_dir.into()
} else {
dirs::home_dir().expect("Failed to evaluate $HOME value")
}
}
#[cfg(feature = "dirs")]
pub fn may_get_home() -> Option<PathBuf> {
dirs::home_dir()
if let Some(home_dir) = std::env::var_os("NYM_HOME_DIR") {
Some(home_dir.into())
} else {
dirs::home_dir()
}
}
pub trait NymConfigTemplate: Serialize {
+18
View File
@@ -0,0 +1,18 @@
[package]
name = "nym-ip-packet-requests"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bincode = "1.3.3"
bytes = "1.5.0"
nym-service-providers-common = { path = "../../service-providers/common" }
nym-sphinx = { path = "../nymsphinx" }
serde = { workspace = true, features = ["derive"] }
+33
View File
@@ -0,0 +1,33 @@
use nym_service_providers_common::interface;
pub type IpPacketRouterRequest = interface::Request<TaggedIpPacket>;
pub type IpPacketRouterResponse = interface::Response<IpPacket>;
#[derive(serde::Serialize, serde::Deserialize)]
pub struct TaggedIpPacket {
pub packet: bytes::Bytes,
pub return_address: nym_sphinx::addressing::clients::Recipient,
pub return_mix_hops: Option<u8>,
pub return_mix_delays: Option<f64>,
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct IpPacket {
pub packet: bytes::Bytes,
}
impl TaggedIpPacket {
pub fn from_message(
message: &nym_sphinx::receiver::ReconstructedMessage,
) -> Result<Self, bincode::Error> {
use bincode::Options;
make_bincode_serializer().deserialize(&message.message)
}
}
fn make_bincode_serializer() -> impl bincode::Options {
use bincode::Options;
bincode::DefaultOptions::new()
.with_big_endian()
.with_varint_encoding()
}
+26
View File
@@ -0,0 +1,26 @@
[package]
name = "nym-tun"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
thiserror.workspace = true
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util", "time", "sync", "macros"] }
etherparse = "0.13.0"
log.workspace = true
# TODO: remove
boringtun = { workspace = true }
nym-wireguard-types = { path = "../wireguard-types", optional = true }
[target.'cfg(target_os = "linux")'.dependencies]
tokio-tun = "0.9.0"
[features]
wireguard = ["nym-wireguard-types"]
+7
View File
@@ -0,0 +1,7 @@
#[cfg(target_os = "linux")]
mod linux;
pub mod tun_task_channel;
#[cfg(target_os = "linux")]
pub use linux::tun_device;
@@ -1,7 +1,6 @@
use std::{
collections::HashMap,
net::{IpAddr, Ipv4Addr},
sync::Arc,
time::Duration,
};
@@ -11,36 +10,42 @@ use tokio::{
time::timeout,
};
use crate::{
active_peers::PeerEventSenderError,
event::Event,
tun_task_channel::{
tun_task_channel, tun_task_response_channel, TunTaskPayload, TunTaskResponseRx,
TunTaskResponseSendError, TunTaskResponseTx, TunTaskRx, TunTaskTx,
},
udp_listener::PeersByIp,
use crate::tun_task_channel::{
tun_task_channel, tun_task_response_channel, TunTaskPayload, TunTaskResponseRx,
TunTaskResponseSendError, TunTaskResponseTx, TunTaskRx, TunTaskTx,
};
#[cfg(feature = "wireguard")]
use nym_wireguard_types::tun_common::{
active_peers::{PeerEventSenderError, PeersByIp},
event::Event,
};
#[cfg(feature = "wireguard")]
const MUTEX_LOCK_TIMEOUT_MS: u64 = 200;
const TUN_WRITE_TIMEOUT_MS: u64 = 1000;
#[derive(thiserror::Error, Debug)]
pub enum TunDeviceError {
#[error("iface: timeout writing to tun device, dropping packet")]
#[error("timeout writing to tun device, dropping packet")]
TunWriteTimeout,
#[error("iface: failed forwarding packet to peer: {source}")]
#[error("error writing to tun device: {source}")]
TunWriteError { source: std::io::Error },
#[cfg(feature = "wireguard")]
#[error("failed forwarding packet to peer: {source}")]
ForwardToPeerFailed {
#[from]
source: PeerEventSenderError,
},
#[error("iface: failed to forward responding packet with tag: {source}")]
#[error("failed to forward responding packet with tag: {source}")]
ForwardNatResponseFailed {
#[from]
source: TunTaskResponseSendError,
},
#[error("iface: error writing to tun device: {source}")]
TunWriteError { source: std::io::Error },
#[error("unable to parse destination address from packet")]
UnableToParseDstAdddress,
@@ -78,10 +83,10 @@ fn setup_tokio_tun_device(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> t
pub struct TunDevice {
// The TUN device that we read/write to, to send/receive packets
tun: Option<tokio_tun::Tun>,
tun: tokio_tun::Tun,
// Incoming data that we should send
tun_task_rx: Option<TunTaskRx>,
tun_task_rx: TunTaskRx,
// And when we get replies, this is where we should send it
tun_task_response_tx: TunTaskResponseTx,
@@ -91,6 +96,7 @@ pub struct TunDevice {
pub enum RoutingMode {
// The routing table, as how wireguard does it
#[cfg(feature = "wireguard")]
AllowedIps(AllowedIpsInner),
// This is an alternative to the routing table, where we just match outgoing source IP with
@@ -105,20 +111,26 @@ impl RoutingMode {
})
}
pub fn new_allowed_ips(peers_by_ip: Arc<tokio::sync::Mutex<PeersByIp>>) -> Self {
#[cfg(feature = "wireguard")]
pub fn new_allowed_ips(peers_by_ip: std::sync::Arc<tokio::sync::Mutex<PeersByIp>>) -> Self {
RoutingMode::AllowedIps(AllowedIpsInner { peers_by_ip })
}
}
#[cfg(feature = "wireguard")]
pub struct AllowedIpsInner {
peers_by_ip: Arc<tokio::sync::Mutex<PeersByIp>>,
peers_by_ip: std::sync::Arc<tokio::sync::Mutex<PeersByIp>>,
}
#[cfg(feature = "wireguard")]
impl AllowedIpsInner {
async fn lock(&self) -> Result<tokio::sync::MutexGuard<PeersByIp>, TunDeviceError> {
timeout(Duration::from_millis(200), self.peers_by_ip.as_ref().lock())
.await
.map_err(|_| TunDeviceError::FailedToLockPeer)
timeout(
Duration::from_millis(MUTEX_LOCK_TIMEOUT_MS),
self.peers_by_ip.as_ref().lock(),
)
.await
.map_err(|_| TunDeviceError::FailedToLockPeer)
}
}
@@ -152,9 +164,9 @@ impl TunDevice {
let (tun_task_response_tx, tun_task_response_rx) = tun_task_response_channel();
let tun_device = TunDevice {
tun_task_rx: Some(tun_task_rx),
tun_task_rx,
tun_task_response_tx,
tun: Some(tun),
tun,
routing_mode,
};
@@ -163,27 +175,29 @@ impl TunDevice {
// Send outbound packets out on the wild internet
async fn handle_tun_write(&mut self, data: TunTaskPayload) -> Result<(), TunDeviceError> {
{
let (tag, ref packet) = data;
let dst_addr = boringtun::noise::Tunn::dst_address(packet)
.ok_or_else(|| TunDeviceError::UnableToParseDstAdddress)?;
let (tag, packet) = data;
let dst_addr = boringtun::noise::Tunn::dst_address(&packet)
.ok_or_else(|| TunDeviceError::UnableToParseDstAdddress)?;
let src_addr = parse_src_address(packet)?;
log::info!(
"iface: write Packet({src_addr} -> {dst_addr}, {} bytes)",
packet.len()
);
let src_addr = parse_src_address(&packet)?;
log::debug!(
"iface: write Packet({src_addr} -> {dst_addr}, {} bytes)",
packet.len()
);
// TODO: expire old entries
if let RoutingMode::Nat(nat_table) = &mut self.routing_mode {
nat_table.nat_table.insert(src_addr, tag);
}
// TODO: expire old entries
#[allow(irrefutable_let_patterns)]
if let RoutingMode::Nat(nat_table) = &mut self.routing_mode {
nat_table.nat_table.insert(src_addr, tag);
}
// timeout(Duration::from_millis(1000), self.tun.write_all(&data.1))
// .await
// .map_err(|_| TunDeviceError::TunWriteTimeout)?
// .map_err(|err| TunDeviceError::TunWriteError { source: err })
timeout(
Duration::from_millis(TUN_WRITE_TIMEOUT_MS),
self.tun.write_all(&packet),
)
.await
.map_err(|_| TunDeviceError::TunWriteTimeout)?
.map_err(|err| TunDeviceError::TunWriteError { source: err })
}
// Receive reponse packets from the wild internet
@@ -191,7 +205,7 @@ impl TunDevice {
let dst_addr = boringtun::noise::Tunn::dst_address(packet)
.ok_or(TunDeviceError::UnableToParseDstAdddress)?;
let src_addr = parse_src_address(packet)?;
log::info!(
log::debug!(
"iface: read Packet({src_addr} -> {dst_addr}, {} bytes)",
packet.len(),
);
@@ -200,10 +214,11 @@ impl TunDevice {
match self.routing_mode {
// This is how wireguard does it, by consulting the AllowedIPs table.
#[cfg(feature = "wireguard")]
RoutingMode::AllowedIps(ref peers_by_ip) => {
let peers = peers_by_ip.lock().await?;
if let Some(peer_tx) = peers.longest_match(dst_addr).map(|(_, tx)| tx) {
log::info!("Forward packet to wg tunnel");
log::debug!("Forward packet to wg tunnel");
return peer_tx
.send(Event::Ip(packet.to_vec().into()))
.await
@@ -214,11 +229,10 @@ impl TunDevice {
// But we can also do it by consulting the NAT table.
RoutingMode::Nat(ref nat_table) => {
if let Some(tag) = nat_table.nat_table.get(&dst_addr) {
log::info!("Forward packet with NAT tag: {tag}");
log::debug!("Forward packet with NAT tag: {tag}");
return self
.tun_task_response_tx
.send((*tag, packet.to_vec()))
.await
.try_send((*tag, packet.to_vec()))
.map_err(|err| err.into());
}
}
@@ -231,37 +245,10 @@ impl TunDevice {
pub async fn run(mut self) {
let mut buf = [0u8; 65535];
let tun_task_rx_stream =
tokio_stream::wrappers::ReceiverStream::new(self.tun_task_rx.take().unwrap().0);
use futures::StreamExt;
let tun_task_rx_stream = tun_task_rx_stream.map(|data| {
//{
// let (tag, ref packet) = data;
// let dst_addr = boringtun::noise::Tunn::dst_address(packet).unwrap();
// // .ok_or_else(|| TunDeviceError::UnableToParseDstAdddress)?;
// let src_addr = parse_src_address(packet).unwrap();
// log::info!(
// "iface: write Packet({src_addr} -> {dst_addr}, {} bytes)",
// packet.len()
// );
// // TODO: expire old entries
// // if let RoutingMode::Nat(nat_table) = &mut self.routing_mode {
// // nat_table.nat_table.insert(src_addr, tag);
// // }
//}
// data.1
4
});
let (mut tun_read, tun_write) = tokio::io::split(self.tun);
loop {
tokio::select! {
// Reading from the TUN device
// len = self.tun.read(&mut buf) => match len {
len = tun_read.read(&mut buf) => match len {
len = self.tun.read(&mut buf) => match len {
Ok(len) => {
let packet = &buf[..len];
if let Err(err) = self.handle_tun_read(packet).await {
@@ -274,14 +261,11 @@ impl TunDevice {
}
},
// Writing to the TUN device
//Some(data) = self.tun_task_rx.recv() => {
// if let Err(err) = self.handle_tun_write(data).await {
// log::error!("ifcae: handle_tun_write failed: {err}");
// }
//}
// res = self.tun.send_all(&mut tun_task_rx_stream) => {
// log::error!("finished");
// }
Some(data) = self.tun_task_rx.recv() => {
if let Err(err) = self.handle_tun_write(data).await {
log::error!("ifcae: handle_tun_write failed: {err}");
}
}
}
}
// log::info!("TUN device shutting down");
@@ -1,25 +1,24 @@
use std::time::Duration;
use tokio::{
sync::mpsc::{self, error::SendError},
time::{error::Elapsed, timeout},
use tokio::sync::mpsc::{
self,
error::{SendError, SendTimeoutError, TrySendError},
};
pub(crate) type TunTaskPayload = (u64, Vec<u8>);
#[derive(Clone)]
pub struct TunTaskTx(mpsc::Sender<TunTaskPayload>);
pub(crate) struct TunTaskRx(pub(crate) mpsc::Receiver<TunTaskPayload>);
pub(crate) struct TunTaskRxStream(pub(crate) tokio_stream::wrappers::ReceiverStream<TunTaskPayload>);
pub(crate) struct TunTaskRx(mpsc::Receiver<TunTaskPayload>);
impl TunTaskTx {
pub async fn send(
&self,
data: TunTaskPayload,
) -> Result<(), tokio::sync::mpsc::error::SendError<TunTaskPayload>> {
pub async fn send(&self, data: TunTaskPayload) -> Result<(), SendError<TunTaskPayload>> {
self.0.send(data).await
}
pub fn try_send(&self, data: TunTaskPayload) -> Result<(), TrySendError<TunTaskPayload>> {
self.0.try_send(data)
}
}
impl TunTaskRx {
@@ -29,28 +28,42 @@ impl TunTaskRx {
}
pub(crate) fn tun_task_channel() -> (TunTaskTx, TunTaskRx) {
let (tun_task_tx, tun_task_rx) = tokio::sync::mpsc::channel(16);
let (tun_task_tx, tun_task_rx) = tokio::sync::mpsc::channel(128);
(TunTaskTx(tun_task_tx), TunTaskRx(tun_task_rx))
}
const TUN_TASK_RESPONSE_SEND_TIMEOUT_MS: u64 = 1_000;
// Send responses back from the tun device back to the PacketRelayer
pub(crate) struct TunTaskResponseTx(mpsc::Sender<TunTaskPayload>);
pub struct TunTaskResponseRx(mpsc::Receiver<TunTaskPayload>);
#[derive(thiserror::Error, Debug)]
pub enum TunTaskResponseSendError {
#[error("failed to send: timeout")]
Timeout(#[from] Elapsed),
#[error("failed to send tun response: {0}")]
SendTimeoutError(#[from] SendTimeoutError<TunTaskPayload>),
#[error("failed to send: {0}")]
#[error("failed to send tun response: {0}")]
SendError(#[from] SendError<TunTaskPayload>),
#[error("failed to send tun response: {0}")]
TrySendError(#[from] TrySendError<TunTaskPayload>),
}
impl TunTaskResponseTx {
#[allow(unused)]
pub(crate) async fn send(&self, data: TunTaskPayload) -> Result<(), TunTaskResponseSendError> {
timeout(Duration::from_millis(1000), self.0.send(data))
.await?
.map_err(|err| err.into())
Ok(self
.0
.send_timeout(
data,
Duration::from_millis(TUN_TASK_RESPONSE_SEND_TIMEOUT_MS),
)
.await?)
}
pub(crate) fn try_send(&self, data: TunTaskPayload) -> Result<(), TunTaskResponseSendError> {
Ok(self.0.try_send(data)?)
}
}
@@ -61,7 +74,7 @@ impl TunTaskResponseRx {
}
pub(crate) fn tun_task_response_channel() -> (TunTaskResponseTx, TunTaskResponseRx) {
let (tun_task_tx, tun_task_rx) = tokio::sync::mpsc::channel(16);
let (tun_task_tx, tun_task_rx) = tokio::sync::mpsc::channel(128);
(
TunTaskResponseTx(tun_task_tx),
TunTaskResponseRx(tun_task_rx),
+6 -1
View File
@@ -12,9 +12,14 @@ license.workspace = true
[dependencies]
base64 = { workspace = true }
bytes = "1.5.0"
dashmap = { workspace = true }
ip_network = "0.4.1"
ip_network_table = "0.2.0"
log = { workspace = true }
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["sync", "time"] }
nym-crypto = { path = "../crypto", features = ["asymmetric"] }
@@ -45,4 +50,4 @@ nym-crypto = { path = "../crypto", features = ["rand"]}
default = ["verify"]
openapi = ["utoipa", "serde_json"]
# this is moved to a separate feature as we really need clients to import it (especially, *cough*, wasm)
verify = ["hmac", "sha2"]
verify = ["hmac", "sha2"]
+1
View File
@@ -4,6 +4,7 @@
pub mod error;
pub mod public_key;
pub mod registration;
pub mod tun_common;
pub use error::Error;
pub use public_key::PeerPublicKey;
@@ -5,24 +5,24 @@ use dashmap::{
mapref::one::{Ref, RefMut},
DashMap,
};
use tokio::{
sync::mpsc::{self},
time::{error::Elapsed, timeout},
};
use tokio::sync::mpsc::{self};
use crate::event::Event;
use crate::tun_common::{event::Event, network_table::NetworkTable};
// Registered peers
pub type PeersByIp = NetworkTable<PeerEventSender>;
// Channels that are used to communicate with the various tunnels
#[derive(Clone)]
pub struct PeerEventSender(mpsc::Sender<Event>);
pub(crate) struct PeerEventReceiver(mpsc::Receiver<Event>);
pub struct PeerEventReceiver(mpsc::Receiver<Event>);
#[derive(thiserror::Error, Debug)]
pub enum PeerEventSenderError {
#[error("timeout")]
Timeout {
#[error("send failed: timeout: {source}")]
SendTimeoutError {
#[from]
source: Elapsed,
source: mpsc::error::SendTimeoutError<Event>,
},
#[error("send failed: {source}")]
@@ -33,20 +33,21 @@ pub enum PeerEventSenderError {
}
impl PeerEventSender {
pub(crate) async fn send(&self, event: Event) -> Result<(), PeerEventSenderError> {
timeout(Duration::from_millis(1000), self.0.send(event))
.await?
.map_err(|err| err.into())
pub async fn send(&self, event: Event) -> Result<(), PeerEventSenderError> {
Ok(self
.0
.send_timeout(event, Duration::from_millis(1000))
.await?)
}
}
impl PeerEventReceiver {
pub(crate) async fn recv(&mut self) -> Option<Event> {
pub async fn recv(&mut self) -> Option<Event> {
self.0.recv().await
}
}
pub(crate) fn peer_event_channel() -> (PeerEventSender, PeerEventReceiver) {
pub fn peer_event_channel() -> (PeerEventSender, PeerEventReceiver) {
let (tx, rx) = mpsc::channel(16);
(PeerEventSender(tx), PeerEventReceiver(rx))
}
@@ -55,20 +56,20 @@ pub(crate) type PeersByKey = DashMap<x25519::PublicKey, PeerEventSender>;
pub(crate) type PeersByAddr = DashMap<SocketAddr, PeerEventSender>;
#[derive(Default)]
pub(crate) struct ActivePeers {
pub struct ActivePeers {
active_peers: PeersByKey,
active_peers_by_addr: PeersByAddr,
}
impl ActivePeers {
pub(crate) fn remove(&self, public_key: &x25519::PublicKey) {
pub fn remove(&self, public_key: &x25519::PublicKey) {
log::info!("Removing peer: {public_key:?}");
self.active_peers.remove(public_key);
log::warn!("TODO: remove from peers_by_ip?");
log::warn!("TODO: remove from peers_by_addr");
}
pub(crate) fn insert(
pub fn insert(
&self,
public_key: x25519::PublicKey,
addr: SocketAddr,
@@ -78,17 +79,14 @@ impl ActivePeers {
self.active_peers_by_addr.insert(addr, peer_tx);
}
pub(crate) fn get_by_key_mut(
pub fn get_by_key_mut(
&self,
public_key: &x25519::PublicKey,
) -> Option<RefMut<'_, x25519::PublicKey, PeerEventSender>> {
self.active_peers.get_mut(public_key)
}
pub(crate) fn get_by_addr(
&self,
addr: &SocketAddr,
) -> Option<Ref<'_, SocketAddr, PeerEventSender>> {
pub fn get_by_addr(&self, addr: &SocketAddr) -> Option<Ref<'_, SocketAddr, PeerEventSender>> {
self.active_peers_by_addr.get(addr)
}
}
@@ -0,0 +1,3 @@
pub mod active_peers;
pub mod event;
pub mod network_table;
@@ -9,7 +9,7 @@ pub struct NetworkTable<T> {
}
impl<T> NetworkTable<T> {
pub(crate) fn new() -> Self {
pub fn new() -> Self {
Self {
ips: IpNetworkTable::new(),
}
+3 -1
View File
@@ -13,6 +13,7 @@ license.workspace = true
[dependencies]
async-recursion = "1.0.4"
base64 = "0.21.3"
bincode = "1.3.3"
# The latest version on crates.io at the time of writing this (6.0.0) has a
# version mismatch with x25519-dalek/curve25519-dalek that is resolved in the
# latest commit. So pick that for now.
@@ -27,12 +28,13 @@ ip_network_table = "0.2.0"
log.workspace = true
nym-task = { path = "../task" }
nym-wireguard-types = { path = "../wireguard-types" }
nym-sphinx = { path = "../nymsphinx" }
nym-tun = { path = "../tun" , features = ["wireguard"] }
rand.workspace = true
serde = { workspace = true, features = ["derive"] }
tap.workspace = true
thiserror.workspace = true
tokio = { workspace = true, features = ["rt-multi-thread", "net", "io-util"] }
tokio-stream = { version = "0.1.11" }
[target.'cfg(target_os = "linux")'.dependencies]
tokio-tun = "0.9.0"
+12 -8
View File
@@ -3,15 +3,15 @@
// #![warn(clippy::expect_used)]
// #![warn(clippy::unwrap_used)]
mod active_peers;
// mod active_peers;
mod error;
mod event;
mod network_table;
// mod event;
// mod network_table;
mod packet_relayer;
mod platform;
// mod platform;
mod registered_peers;
mod setup;
pub mod tun_task_channel;
pub mod setup;
// pub mod tun_task_channel;
mod udp_listener;
mod wg_tunnel;
@@ -20,7 +20,9 @@ use std::sync::Arc;
// Currently the module related to setting up the virtual network device is platform specific.
#[cfg(target_os = "linux")]
pub use platform::linux::tun_device;
use nym_tun::tun_device;
use nym_tun::tun_task_channel;
/// Start wireguard UDP listener and TUN device
///
@@ -36,7 +38,9 @@ pub async fn start_wireguard(
// We can optionally index peers by their IP like standard wireguard. If we don't then we do
// plain NAT where we match incoming destination IP with outgoing source IP.
let peers_by_ip = Arc::new(tokio::sync::Mutex::new(network_table::NetworkTable::new()));
use nym_wireguard_types::tun_common::network_table::NetworkTable;
let peers_by_ip = Arc::new(tokio::sync::Mutex::new(NetworkTable::new()));
// Alternative 1:
let routing_mode = tun_device::RoutingMode::new_allowed_ips(peers_by_ip.clone());
+3 -5
View File
@@ -3,11 +3,9 @@ use std::{collections::HashMap, sync::Arc};
use tap::TapFallible;
use tokio::sync::mpsc::{self};
use crate::{
active_peers::PeerEventSender,
event::Event,
tun_task_channel::{TunTaskResponseRx, TunTaskTx},
};
use crate::tun_task_channel::{TunTaskResponseRx, TunTaskTx};
use nym_wireguard_types::tun_common::{active_peers::PeerEventSender, event::Event};
#[derive(Clone)]
pub struct PacketRelaySender(pub(crate) mpsc::Sender<(u64, Vec<u8>)>);
+8 -7
View File
@@ -7,15 +7,19 @@ use boringtun::{
use futures::StreamExt;
use log::error;
use nym_task::TaskClient;
use nym_wireguard_types::{registration::GatewayClientRegistry, PeerPublicKey, WG_PORT};
use nym_wireguard_types::{
registration::GatewayClientRegistry,
tun_common::{
active_peers::{ActivePeers, PeersByIp},
event::Event,
},
PeerPublicKey, WG_PORT,
};
use tap::TapFallible;
use tokio::{net::UdpSocket, sync::Mutex};
use crate::{
active_peers::{ActivePeers, PeerEventSender},
error::WgError,
event::Event,
network_table::NetworkTable,
packet_relayer::PacketRelaySender,
registered_peers::{RegisteredPeer, RegisteredPeers},
setup::{self, WG_ADDRESS},
@@ -24,9 +28,6 @@ use crate::{
const MAX_PACKET: usize = 65535;
// Registered peers
pub(crate) type PeersByIp = NetworkTable<PeerEventSender>;
async fn add_test_peer(registered_peers: &mut RegisteredPeers) {
let peer_static_public = PeerPublicKey::new(setup::peer_static_public_key());
let peer_index = 0;
+6 -8
View File
@@ -7,18 +7,16 @@ use boringtun::{
};
use bytes::Bytes;
use log::{debug, error, info, warn};
use nym_wireguard_types::tun_common::{
active_peers::{peer_event_channel, PeerEventReceiver, PeerEventSender},
event::Event,
network_table::NetworkTable,
};
use rand::RngCore;
use tap::TapFallible;
use tokio::{net::UdpSocket, sync::broadcast, time::timeout};
use crate::{
active_peers::{peer_event_channel, PeerEventReceiver, PeerEventSender},
error::WgError,
event::Event,
network_table::NetworkTable,
packet_relayer::PacketRelaySender,
registered_peers::PeerIdx,
};
use crate::{error::WgError, packet_relayer::PacketRelaySender, registered_peers::PeerIdx};
const HANDSHAKE_MAX_RATE: u64 = 10;
+16 -11
View File
@@ -22,14 +22,17 @@ As we shared in our blog post article [*What does it take to build the wolds mos
### What are the changes?
Project smoosh will have three steps:
Project Smoosh will have four steps, please follow the table below to track the dynamic progress:
1. Combine the `nym-gateway` and `nym-network-requester` into one binary ✅
2. Create [Exit Gateway](../legal/exit-gateway.md): Take the `nym-gateway` binary including `nym-network-requester` combined in \#1 and switch from [`allowed.list`](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) to a new [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) ✅
3. Combine all the nodes in the Nym Mixnet into one binary, that is `nym-mixnode`, `nym-gateway` (entry and exit) and `nym-network-requester`.
| **Step** | **Status** |
| :--- | :--- |
| **1.** Combine the `nym-gateway` and `nym-network-requester` into one binary | ✅ done |
| **2.** Create [Exit Gateway](../legal/exit-gateway.md): Take the `nym-gateway` binary including `nym-network-requester` combined in \#1 and switch from [`allowed.list`](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) to a new [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) | ✅ done |
| **3.** Combine all the nodes in the Nym Mixnet into one binary, that is `nym-mixnode`, `nym-gateway` (entry and exit) and `nym-network-requester`. | 🛠️ in progress |
| **4.** Adjust reward scheme to incentivise and reward Exit Gateways as a part of `nym-node` binary, implementing [zkNym credentials](https://youtu.be/nLmdsZ1BsQg?t=1717). | 🛠️ in progress |
These three steps will be staggered over time - period of several months, and will be implemented one by one with enough time to take in feedback and fix bugs in between.
Generally, the software will be the same, just instead of multiple binaries, there will be one Nym Mixnet node binary. Delegations will remain on as they are now, per our token economics (staking, saturation etc)
These steps will be staggered over time - period of several months, and will be implemented one by one with enough time to take in feedback and fix bugs in between.
Generally, the software will be the same, just instead of multiple binaries, there will be one Nym Node (`nym-node`) binary. Delegations will remain on as they are now, per our token economics (staking, saturation etc)
### What does it mean for Nym nodes operators?
@@ -49,13 +52,15 @@ The operators running Gateways would have to “open” their nodes to a wider r
### How will the Exit policy be implemented?
The progression of exit policy on Gateways will have three steps:
Follow the dynamic progress of exit policy implementation on Gateways below:
1. By default the [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) filtering will be disabled and the current [`allowed.list`](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) filtering is going to continue be used. This is to prevent operators getting surprised by upgrading their Gateways (or Network Requesters) and suddenly be widely open to the internet. To enable the new exit policy, operators must use `--with-exit-policy` flag or modify the `config.toml` file. ✅
2. Relatively soon the exit policy will be part of the Gateway setup by default. To disable this exit policy, operators must use `--disable-exit-policy` flag.
3. Further down the line, it will be the only option. Then the `allowed.list` will be completely removed.
| **Step** | **Status** |
| :--- | :--- |
| **1.** By default the [exit policy](https://nymtech.net/.wellknown/network-requester/exit-policy.txt) filtering is disabled and the [`allowed.list`](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) filtering is going to continue be used. This is to prevent operators getting surprised by upgrading their Gateways (or Network Requesters) and suddenly be widely open to the internet. To enable the new exit policy, operators must use `--with-exit-policy` flag or modify the `config.toml` file. | ✅ done |
| **2.** The exit policy is part of the Gateway setup by default. To disable this exit policy, operators must use `--disable-exit-policy` flag. | 🛠️ in progress |
| **3.** The exit policy is the only option. The `allowed.list` is completely removed. | 🛠️ in progress |
Keep in mind this only relates to changes happening on Gateway and Network Requester side. Whether this will be optional or mandatory depends on the chosen [design](./smoosh-faq.md#what-does-it-mean-for-nym-nodes-operators).
Keep in mind the table above only relates to changes happening on Gateways. For the Project Smoosh progress refer to the [table above](./smoosh-faq.md#what-are-the-changes). Whether Exit Gateway functionality will be optional or mandatory part of every active Nym Node depends on the chosen [design](./smoosh-faq.md#what-does-it-mean-for-nym-nodes-operators).
### Can I run a Mix Node only?
@@ -29,15 +29,14 @@ Follow these steps to upgrade your Node binary and update its config file:
- if you run it as `systemd` service, run: `systemctl stop nym-<NODE>.service`
* Replace the existing `<NODE>` binary with the newest binary (which you can either [compile yourself](https://nymtech.net/docs/binaries/building-nym.html) or grab from our [releases page](https://github.com/nymtech/nym/releases)).
* Re-run `init` with the same values as you used initially for your `<NODE>` ([Mix Node](./mix-node-setup.md#initialising-your-mix-node), [Gateway](./gateway-setup.md#initialising-your-gateway)) . **This will just update the config file, it will not overwrite existing keys**.
* Restart your node process with the new binary.
- if you automatized (recommended) run:
* Restart your node process with the new binary:
- if your node is not automitized, just `run` your `<NODE>` with `./nym-<NODE> run --id <ID>`. Here are exact guidelines for [Mix Node](./mix-node-setup.md#running-your-mix-node) and [Gateway](./gateway-setup.md#running-your-gateway).
- if you automatized your node via systemd (recommended) run:
```sh
systemctl daemon-reload # to pickup the new unit file
systemctl start nym-<NODE>.service
journalctl -f -u <NODE>.service # to monitor log of you node
```
- if your node is not automitized, just `run` your `<NODE>` with `./nym-<NODE> run --id <ID>`. Here are exact guidelines for [Mix Node](./mix-node-setup.md#running-your-mix-node) and [Gateway](./gateway-setup.md#running-your-gateway).
If these steps are too difficult and you prefer to just run a script, you can use [ExploreNYM script](https://github.com/ExploreNYM/bash-tool) or one done by [Nym developers](https://gist.github.com/tommyv1987/4dca7cc175b70742c9ecb3d072eb8539).
+3 -1
View File
@@ -580,7 +580,9 @@ mod test {
}
#[tokio::test]
#[should_panic]
#[should_panic(
expected = "Received committed block which isn't last produced block, this is a bug!"
)]
async fn test_on_committed_with_invalid_pending_block() {
let (mut manager, _) = block_manager_with_defaults();
+8 -2
View File
@@ -1,8 +1,11 @@
import * as React from 'react';
import React from 'react';
import Box from '@mui/material/Box';
import { Typography } from '@mui/material';
import MuiLink from '@mui/material/Link';
import { Link } from 'react-router-dom';
import Typography from '@mui/material/Typography';
import { Socials } from './Socials';
import { useIsMobile } from '../hooks/useIsMobile';
import { NymVpnIcon } from '../icons/NymVpn';
export const Footer: FCWithChildren = () => {
const isMobile = useIsMobile();
@@ -31,6 +34,9 @@ export const Footer: FCWithChildren = () => {
mb: 2,
}}
>
<MuiLink component={Link} to="http://nymvpn.com" target="_blank" underline="none" marginRight={1}>
<NymVpnIcon />
</MuiLink>
<Socials isFooter />
</Box>
)}
+4
View File
@@ -22,6 +22,7 @@ import { useMainContext } from '../context/main';
import { MobileDrawerClose } from '../icons/MobileDrawerClose';
import { Socials } from './Socials';
import { Footer } from './Footer';
import { NymVpnIcon } from '../icons/NymVpn';
import { DarkLightSwitchDesktop } from './Switch';
import { NavOptionType } from '../context/nav';
@@ -341,6 +342,9 @@ export const Nav: FCWithChildren = ({ children }) => {
alignItems: 'center',
}}
>
<MuiLink component={Link} to="http://nymvpn.com" target="_blank" underline="none" marginRight={1}>
<NymVpnIcon />
</MuiLink>
<Socials />
<DarkLightSwitchDesktop defaultChecked />
</Box>
+56
View File
@@ -0,0 +1,56 @@
import * as React from 'react';
interface DiscordIconProps {
size?: { width: number; height: number };
}
export const NymVpnIcon: FCWithChildren<DiscordIconProps> = ({ size }) => (
<svg width={size?.width} height={size?.height} viewBox="0 0 170 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M19.6118 0.128906H19.5405V0.187854V20.7961L10.7849 0.164277L10.773 0.128906H10.7255H5.75959H0.187819H0.128418V0.187854V23.8142V23.8732H0.187819H5.75959H5.81899V23.8142V3.17063L14.6103 23.8378L14.6222 23.8732H14.6697H19.6118H25.1717H25.2311V23.8142V0.187854V0.128906H25.1717H19.6118Z"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M19.4121 0H25.3603V24H14.5297L14.4901 23.8819L5.94824 3.80121V24H0V0H10.8663L10.906 0.118132L19.4121 20.1621V0ZM19.5409 20.7951L10.7853 0.163225L10.7734 0.127854H0.128835V23.8721H5.81941V3.16958L14.6107 23.8368L14.6226 23.8721H25.2315V0.127854H19.5409V20.7951Z"
fill="white"
/>
<path
d="M89.8116 0.128906H79.1908H79.1314L79.1195 0.176068L73.6784 20.8904L68.2255 0.176068L68.2136 0.128906H68.1661H57.5215H57.4502V0.187854V23.8142V23.8732H57.5215H63.0814H63.1408V23.8142V3.33568L68.5225 23.826L68.5343 23.8732H68.5937H78.7394H78.7869L78.7988 23.826L84.1804 3.33568V23.8142V23.8732H84.2398H89.8116H89.871V23.8142V0.187854V0.128906H89.8116Z"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M79.0312 0H90.0003V24H84.052V4.33208L78.9242 23.856L78.9238 23.8572L78.8879 24H68.4342L68.3982 23.8572L68.3979 23.856L63.27 4.33208V24H57.3218V0H68.3146L68.3505 0.142699L68.3509 0.144015L73.6787 20.383L78.9949 0.144015L78.9953 0.142765L79.0312 0ZM73.6788 20.8894L68.2259 0.175015L68.214 0.127854H57.4506V23.8721H63.1412V3.33463L68.5229 23.825L68.5348 23.8721H78.7873L78.7992 23.825L84.1809 3.33463V23.8721H89.8714V0.127854H79.1318L79.1199 0.175015L73.6788 20.8894Z"
fill="white"
/>
<path
d="M48.2909 0.128906H48.2553L48.2434 0.152487L41.4836 11.8124L34.6882 0.152487L34.6763 0.128906H34.6407H28.2135H28.0947L28.1541 0.223225L38.6205 18.2142V23.8142V23.8732H38.6799H44.2517H44.3111V23.8142V18.2142L54.7775 0.223225L54.8369 0.128906H54.7181H48.2909Z"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M48.1757 0H55.0693L54.8879 0.288036L44.4399 18.2474V24H38.4917V18.2474L28.0437 0.288036L27.8623 0H34.756L34.8017 0.0907854L41.4833 11.5555L48.1299 0.0909153L48.1757 0ZM48.2434 0.151434L41.4836 11.8114L34.6882 0.151434L34.6763 0.127854H28.0948L28.1542 0.222173L38.6205 18.2131V23.8721H44.3111V18.2131L54.7775 0.222173L54.8369 0.127854H48.2553L48.2434 0.151434Z"
fill="white"
/>
<path
d="M169.238 0V24H166.422C166.006 24 165.654 23.9341 165.366 23.8023C165.088 23.6596 164.811 23.418 164.534 23.0776L153.542 8.76321C153.584 9.19149 153.611 9.60878 153.622 10.0151C153.643 10.4104 153.654 10.7838 153.654 11.1352V24H148.886V0H151.734C151.968 0 152.166 0.0109813 152.326 0.032944C152.486 0.0549066 152.63 0.0988326 152.758 0.164722C152.886 0.219629 153.008 0.30199 153.126 0.411805C153.243 0.521619 153.376 0.669869 153.526 0.856553L164.614 15.2697C164.56 14.8085 164.523 14.3638 164.502 13.9355C164.48 13.4962 164.47 13.0844 164.47 12.7001V0H169.238Z"
fill="#A8A6A6"
/>
<path
d="M134.206 11.7776C135.614 11.7776 136.627 11.4317 137.246 10.7399C137.865 10.048 138.174 9.08167 138.174 7.84077C138.174 7.29169 138.094 6.79204 137.934 6.3418C137.774 5.89156 137.529 5.50721 137.198 5.18874C136.878 4.8593 136.467 4.60673 135.966 4.43102C135.475 4.25532 134.889 4.16747 134.206 4.16747H131.39V11.7776H134.206ZM134.206 0C135.849 0 137.257 0.203157 138.43 0.609471C139.614 1.0048 140.585 1.55388 141.342 2.25669C142.11 2.95951 142.675 3.78861 143.038 4.74399C143.401 5.69938 143.582 6.73164 143.582 7.84077C143.582 9.03775 143.395 10.1359 143.022 11.1352C142.649 12.1345 142.078 12.9911 141.31 13.7049C140.542 14.4187 139.566 14.9787 138.382 15.385C137.209 15.7804 135.817 15.978 134.206 15.978H131.39V24H125.982V0H134.206Z"
fill="#A8A6A6"
/>
<path
d="M121.584 0L112.24 24H107.344L98 0H102.352C102.821 0 103.2 0.115305 103.488 0.345915C103.776 0.565545 103.995 0.851064 104.144 1.20247L108.656 14.0508C108.869 14.6108 109.077 15.2258 109.28 15.8957C109.483 16.5546 109.675 17.2464 109.856 17.9712C110.005 17.2464 110.171 16.5546 110.352 15.8957C110.544 15.2258 110.747 14.6108 110.96 14.0508L115.44 1.20247C115.557 0.894989 115.765 0.620452 116.064 0.378861C116.373 0.126287 116.752 0 117.2 0H121.584Z"
fill="#A8A6A6"
/>
</svg>
);
NymVpnIcon.defaultProps = {
size: { width: 80, height: 12 },
};
+2 -2
View File
@@ -229,14 +229,14 @@ impl<'a> HttpApiBuilder<'a> {
IpAddr::from(Ipv4Addr::new(10, 1, 0, 0)),
self.gateway_config.wireguard.private_network_prefix,
)?;
let wg_state = self.client_registry.map(|client_registry| {
let wg_state = self.client_registry.and_then(|client_registry| {
WireguardAppState::new(
self.sphinx_keypair,
client_registry,
Default::default(),
self.gateway_config.wireguard.bind_address.port(),
wireguard_private_network,
)
.ok()
});
let router = nym_node::http::NymNodeRouter::new(config, wg_state);
+5
View File
@@ -81,3 +81,8 @@ cpucycles = [
"opentelemetry",
"nym-bin-common/tracing",
]
[package.metadata.deb]
name = "nym-mixnode"
maintainer-scripts = "debian"
systemd-units = { enable = false }
+6
View File
@@ -0,0 +1,6 @@
#DEBHELPER#
useradd nym
mkdir -p /etc/nym
chown -R nym /etc/nym
su nym -c 'NYM_HOME_DIR=/etc/nym nym-mixnode init --host 0.0.0.0 --id nym-mixnode'
+11
View File
@@ -0,0 +1,11 @@
[Unit]
Description=Nym Mixnode
After=network-online.target
[Service]
ExecStart=/usr/bin/nym-mixnode run --id nym-mixnode
User=nym
Environment="NYM_HOME_DIR=/etc/nym"
[Install]
WantedBy=multi-user.target
+1
View File
@@ -50,6 +50,7 @@ nym-config = { path = "../common/config" }
nym-crypto = { path = "../common/crypto", features = ["asymmetric" ]}
nym-node-requests = { path = "nym-node-requests", default-features = false, features = ["openapi"]}
nym-task = { path = "../common/task" }
nym-wireguard = { path = "../common/wireguard" }
nym-wireguard-types = { path = "../common/wireguard-types", features = ["verify"] }
[dev-dependencies]
+6
View File
@@ -31,6 +31,12 @@ pub enum NymNodeError {
source: WireguardError,
},
#[error(transparent)]
KeyRecoveryError {
#[from]
source: nym_crypto::asymmetric::encryption::KeyRecoveryError,
},
#[error("unimplemented")]
Unimplemented,
}
@@ -31,10 +31,7 @@ async fn process_final_message(
}
};
if client
.verify(state.dh_keypair.private_key(), preshared_nonce)
.is_ok()
{
if client.verify(&state.private_key, preshared_nonce).is_ok() {
state.registration_in_progress.remove(&client.pub_key());
state.client_registry.insert(client.pub_key(), client);
@@ -104,7 +101,7 @@ pub(crate) async fn register_client(
// mark it as used, even though it's not final
*private_ip_ref = false;
let gateway_data = GatewayClient::new(
state.dh_keypair.private_key(),
&state.private_key,
remote_public,
*private_ip_ref.key(),
nonce,
@@ -8,8 +8,9 @@ use crate::wireguard::types::{GatewayClientRegistry, PendingRegistrations};
use axum::routing::{get, post};
use axum::Router;
use ipnetwork::IpNetwork;
use nym_crypto::asymmetric::encryption;
use nym_crypto::asymmetric::encryption::PrivateKey;
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
use nym_wireguard::setup;
use nym_wireguard_types::registration::PrivateIPs;
use std::sync::Arc;
@@ -24,15 +25,16 @@ pub struct WireguardAppState {
impl WireguardAppState {
pub fn new(
dh_keypair: Arc<encryption::KeyPair>,
client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16,
private_ip_network: IpNetwork,
) -> Self {
WireguardAppState {
) -> Result<Self, crate::error::NymNodeError> {
Ok(WireguardAppState {
inner: Some(WireguardAppStateInner {
dh_keypair,
private_key: Arc::new(PrivateKey::from_bytes(
setup::server_static_private_key().as_ref(),
)?),
client_registry,
registration_in_progress,
binding_port,
@@ -40,7 +42,7 @@ impl WireguardAppState {
private_ip_network.iter().map(|ip| (ip, true)).collect(),
),
}),
}
})
}
// #[allow(dead_code)]
@@ -79,7 +81,7 @@ macro_rules! get_state {
#[derive(Clone)]
pub(crate) struct WireguardAppStateInner {
dh_keypair: Arc<encryption::KeyPair>,
private_key: Arc<PrivateKey>,
client_registry: Arc<GatewayClientRegistry>,
registration_in_progress: Arc<PendingRegistrations>,
binding_port: u16,
@@ -112,6 +114,7 @@ mod test {
PeerPublicKey,
};
use nym_node_requests::routes::api::v1::gateway::client_interfaces::wireguard;
use nym_wireguard::setup::server_static_private_key;
use nym_wireguard_types::registration::HmacSha256;
use std::net::IpAddr;
use std::str::FromStr;
@@ -130,8 +133,15 @@ mod test {
// 6. Gateway verifies mac digest and nonce, and stores client's public key and socket address and port
let mut rng = rand::thread_rng();
let gateway_private_key =
encryption::PrivateKey::from_bytes(server_static_private_key().as_bytes()).unwrap();
let gateway_public_key = encryption::PublicKey::from(&gateway_private_key);
let gateway_key_pair = encryption::KeyPair::new(&mut rng);
let gateway_key_pair = encryption::KeyPair::from_bytes(
&gateway_private_key.to_bytes(),
&gateway_public_key.to_bytes(),
)
.unwrap();
let client_key_pair = encryption::KeyPair::new(&mut rng);
let gateway_static_public =
@@ -158,7 +168,7 @@ mod test {
let state = WireguardAppState {
inner: Some(WireguardAppStateInner {
client_registry: Arc::clone(&client_registry),
dh_keypair: Arc::new(gateway_key_pair),
private_key: Arc::new(gateway_private_key),
registration_in_progress: Arc::clone(&registration_in_progress),
binding_port: 8080,
free_private_network_ips,
+1 -1
View File
@@ -13,7 +13,7 @@ This is the application UI layer for the next NymVPN clients.
Some system libraries are required depending on the host platform.
Follow the instructions for your specific OS [here](https://tauri.app/v1/guides/getting-started/prerequisites)
To install run
To install:
```
yarn
+1 -1
View File
@@ -28,7 +28,7 @@ tokio = { workspace = true, features = ["sync", "time"] }
log = "0.4.17"
rand = "0.7.3"
safer-ffi = { version = "0.1.0-rc1" }
safer-ffi = { version = "0.1.4" }
[target.'cfg(target_os="android")'.dependencies]
jni = { version = "0.21", default-features = false }
@@ -16,6 +16,8 @@ in combination with their own configuration. If you are trying to access somethi
3. If you are using `mixFetch` in a web app with HTTPS you will need to use a gateway that has Secure Websockets to
avoid getting a [mixed content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content) error.
4. For now, mixfetch doesn't work with SURBS, altough this may change in the future.
Read [this article](https://blog.nymtech.net/mixfetch-like-the-fetch-api-but-via-the-mixnet-82acfd435c62) to learn more about mixFetch.
@@ -9,18 +9,25 @@ edition.workspace = true
license.workspace = true
[dependencies]
bincode = "1.3.3"
bytes = "1.5.0"
etherparse = "0.13.0"
futures = { workspace = true }
log = { workspace = true }
nym-bin-common = { path = "../../common/bin-common" }
nym-client-core = { path = "../../common/client-core" }
nym-config = { path = "../../common/config" }
nym-exit-policy = { path = "../../common/exit-policy" }
nym-ip-packet-requests = { path = "../../common/ip-packet-requests" }
nym-network-requester = { path = "../network-requester" }
nym-sdk = { path = "../../sdk/rust/nym-sdk" }
nym-service-providers-common = { path = "../common" }
nym-sphinx = { path = "../../common/nymsphinx" }
nym-task = { path = "../../common/task" }
nym-tun = { path = "../../common/tun" }
nym-wireguard = { path = "../../common/wireguard" }
nym-wireguard-types = { path = "../../common/wireguard-types" }
reqwest.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
tap.workspace = true
@@ -1,4 +1,7 @@
use std::net::SocketAddr;
pub use nym_client_core::error::ClientCoreError;
use nym_exit_policy::PolicyError;
#[derive(thiserror::Error, Debug)]
pub enum IpPacketRouterError {
@@ -27,9 +30,41 @@ pub enum IpPacketRouterError {
#[error("the entity wrapping the network requester has disconnected")]
DisconnectedParent,
#[error("failed to deserialize tagged packet: {source}")]
FailedToDeserializeTaggedPacket { source: bincode::Error },
#[error("failed to parse incoming packet: {source}")]
PacketParseFailed { source: etherparse::ReadError },
#[error("parsed packet is missing IP header")]
PacketMissingHeader,
PacketMissingIpHeader,
#[error("parsed packet is missing transport header")]
PacketMissingTransportHeader,
#[error("failed to send packet to tun device: {source}")]
FailedToSendPacketToTun {
source: tokio::sync::mpsc::error::TrySendError<(u64, Vec<u8>)>,
},
#[error("the provided socket address, '{addr}' is not covered by the exit policy!")]
AddressNotCoveredByExitPolicy { addr: SocketAddr },
#[error("failed filter check: '{addr}'")]
AddressFailedFilterCheck { addr: SocketAddr },
#[error("failed to apply the exit policy: {source}")]
ExitPolicyFailure {
#[from]
source: PolicyError,
},
#[error("the url provided for the upstream exit policy source is malformed: {source}")]
MalformedExitPolicyUpstreamUrl {
#[source]
source: reqwest::Error,
},
#[error("can't setup an exit policy without any upstream urls")]
NoUpstreamExitPolicy,
}
+121 -45
View File
@@ -1,4 +1,9 @@
use std::{net::IpAddr, path::Path};
#![cfg_attr(not(target_os = "linux"), allow(dead_code))]
use std::{
net::{IpAddr, SocketAddr},
path::Path,
};
use error::IpPacketRouterError;
use futures::{channel::oneshot, StreamExt};
@@ -12,7 +17,7 @@ use nym_sdk::{
};
use nym_sphinx::receiver::ReconstructedMessage;
use nym_task::{connections::TransmissionLane, TaskClient, TaskHandle};
use tap::TapFallible;
use request_filter::RequestFilter;
use crate::config::BaseClientConfig;
@@ -20,6 +25,7 @@ pub use crate::config::Config;
pub mod config;
pub mod error;
mod request_filter;
// The interface used to route traffic
pub const TUN_BASE_NAME: &str = "nymtun";
@@ -29,11 +35,16 @@ pub const TUN_DEVICE_NETMASK: &str = "255.255.255.0";
pub struct OnStartData {
// to add more fields as required
pub address: Recipient,
pub request_filter: RequestFilter,
}
impl OnStartData {
pub fn new(address: Recipient) -> Self {
Self { address }
pub fn new(address: Recipient, request_filter: RequestFilter) -> Self {
Self {
address,
request_filter,
}
}
}
@@ -128,20 +139,23 @@ impl IpPacketRouterBuilder {
let self_address = *mixnet_client.nym_address();
// Create the TUN device that we interact with the rest of the world with
let config = nym_wireguard::tun_device::TunDeviceConfig {
let config = nym_tun::tun_device::TunDeviceConfig {
base_name: TUN_BASE_NAME.to_string(),
ip: TUN_DEVICE_ADDRESS.parse().unwrap(),
netmask: TUN_DEVICE_NETMASK.parse().unwrap(),
};
let (tun, tun_task_tx, tun_task_response_rx) = nym_wireguard::tun_device::TunDevice::new(
nym_wireguard::tun_device::RoutingMode::new_nat(),
let (tun, tun_task_tx, tun_task_response_rx) = nym_tun::tun_device::TunDevice::new(
nym_tun::tun_device::RoutingMode::new_nat(),
config,
);
tun.start();
let request_filter = request_filter::RequestFilter::new(&self.config).await?;
request_filter.start_update_tasks().await;
let ip_packet_router_service = IpPacketRouter {
_config: self.config,
// tun,
request_filter: request_filter.clone(),
tun_task_tx,
tun_task_response_rx,
mixnet_client,
@@ -152,7 +166,10 @@ impl IpPacketRouterBuilder {
log::info!("All systems go. Press CTRL-C to stop the server.");
if let Some(on_start) = self.on_start {
if on_start.send(OnStartData::new(self_address)).is_err() {
if on_start
.send(OnStartData::new(self_address, request_filter))
.is_err()
{
// the parent has dropped the channel before receiving the response
return Err(IpPacketRouterError::DisconnectedParent);
}
@@ -165,9 +182,9 @@ impl IpPacketRouterBuilder {
#[allow(unused)]
struct IpPacketRouter {
_config: Config,
// tun: nym_wireguard::tun_device::TunDevice,
tun_task_tx: nym_wireguard::tun_task_channel::TunTaskTx,
tun_task_response_rx: nym_wireguard::tun_task_channel::TunTaskResponseRx,
request_filter: request_filter::RequestFilter,
tun_task_tx: nym_tun::tun_task_channel::TunTaskTx,
tun_task_response_rx: nym_tun::tun_task_channel::TunTaskResponseRx,
mixnet_client: nym_sdk::mixnet::MixnetClient,
task_handle: TaskHandle,
}
@@ -184,7 +201,9 @@ impl IpPacketRouter {
},
msg = self.mixnet_client.next() => {
if let Some(msg) = msg {
self.on_message(msg).await.ok();
if let Err(err) = self.on_message(msg).await {
log::error!("Error handling mixnet message: {err}");
};
} else {
log::trace!("IpPacketRouter [main loop]: stopping since channel closed");
break;
@@ -207,13 +226,9 @@ impl IpPacketRouter {
let packet_type = None;
let input_message = InputMessage::new_regular(recipient, packet, lane, packet_type);
self.mixnet_client
.send(input_message)
.await
.tap_err(|err| {
log::error!("IpPacketRouter [main loop]: failed to send packet to mixnet: {err}");
})
.ok();
if let Err(err) = self.mixnet_client.send(input_message).await {
log::error!("IpPacketRouter [main loop]: failed to send packet to mixnet: {err}");
};
} else {
log::error!("NYM_CLIENT_ADDR not set or invalid");
}
@@ -233,43 +248,104 @@ impl IpPacketRouter {
&mut self,
reconstructed: ReconstructedMessage,
) -> Result<(), IpPacketRouterError> {
log::info!("Received message: {:?}", reconstructed.sender_tag);
log::debug!(
"Received message with sender_tag: {:?}",
reconstructed.sender_tag
);
let headers = etherparse::SlicedPacket::from_ip(&reconstructed.message).map_err(|err| {
log::warn!("Received non-IP packet: {err}");
IpPacketRouterError::PacketParseFailed { source: err }
})?;
let tagged_packet = nym_ip_packet_requests::TaggedIpPacket::from_message(&reconstructed)
.map_err(|err| IpPacketRouterError::FailedToDeserializeTaggedPacket { source: err })?;
let (src_addr, dst_addr): (IpAddr, IpAddr) = match headers.ip {
Some(etherparse::InternetSlice::Ipv4(ipv4_header, _)) => (
ipv4_header.source_addr().into(),
ipv4_header.destination_addr().into(),
),
Some(etherparse::InternetSlice::Ipv6(ipv6_header, _)) => (
ipv6_header.source_addr().into(),
ipv6_header.destination_addr().into(),
),
None => {
log::warn!("Received non-IP packet");
return Err(IpPacketRouterError::PacketMissingHeader);
// We don't forward packets that we are not able to parse. BUT, there might be a good
// reason to still forward them.
//
// For example, if we are running in a mode where we are only supposed to forward
// packets to a specific destination, we might want to forward them anyway.
//
// TODO: look into this
let ParsedPacket {
packet_type,
src_addr,
dst_addr,
dst,
} = parse_packet(&tagged_packet.packet)?;
let dst_str = dst.map_or(dst_addr.to_string(), |dst| dst.to_string());
log::info!("Received packet: {packet_type}: {src_addr} -> {dst_str}");
// Filter check
if let Some(dst) = dst {
if !self.request_filter.check_address(&dst).await {
log::warn!("Failed filter check: {dst}");
// TODO: we could consider sending back a response here
return Err(IpPacketRouterError::AddressFailedFilterCheck { addr: dst });
}
};
log::info!("Received packet: {src_addr} -> {dst_addr}");
} else {
// TODO: we should also filter packets without port number
log::warn!("Ignoring filter check for packet without port number! TODO!");
}
// TODO: set the tag correctly. Can we just reuse sender_tag?
let peer_tag = 0;
self.tun_task_tx
.send((peer_tag, reconstructed.message))
.await
.tap_err(|err| {
log::error!("Failed to send packet to tun device: {err}");
})
.ok();
.try_send((peer_tag, tagged_packet.packet.into()))
.map_err(|err| IpPacketRouterError::FailedToSendPacketToTun { source: err })?;
Ok(())
}
}
struct ParsedPacket<'a> {
packet_type: &'a str,
src_addr: IpAddr,
dst_addr: IpAddr,
dst: Option<SocketAddr>,
}
fn parse_packet(packet: &[u8]) -> Result<ParsedPacket, IpPacketRouterError> {
let headers = etherparse::SlicedPacket::from_ip(packet).map_err(|err| {
log::warn!("Unable to parse incoming data as IP packet: {err}");
IpPacketRouterError::PacketParseFailed { source: err }
})?;
let (packet_type, dst_port) = match headers.transport {
Some(etherparse::TransportSlice::Udp(header)) => ("ipv4", Some(header.destination_port())),
Some(etherparse::TransportSlice::Tcp(header)) => ("ipv6", Some(header.destination_port())),
Some(etherparse::TransportSlice::Icmpv4(_)) => ("icmpv4", None),
Some(etherparse::TransportSlice::Icmpv6(_)) => ("icmpv6", None),
Some(etherparse::TransportSlice::Unknown(_)) => ("unknown", None),
None => {
log::warn!("Received packet missing transport header");
return Err(IpPacketRouterError::PacketMissingTransportHeader);
}
};
let (src_addr, dst_addr, dst) = match headers.ip {
Some(etherparse::InternetSlice::Ipv4(ipv4_header, _)) => {
let src_addr: IpAddr = ipv4_header.source_addr().into();
let dst_addr: IpAddr = ipv4_header.destination_addr().into();
let dst = dst_port.map(|port| SocketAddr::new(dst_addr, port));
(src_addr, dst_addr, dst)
}
Some(etherparse::InternetSlice::Ipv6(ipv6_header, _)) => {
let src_addr: IpAddr = ipv6_header.source_addr().into();
let dst_addr: IpAddr = ipv6_header.destination_addr().into();
let dst = dst_port.map(|port| SocketAddr::new(dst_addr, port));
(src_addr, dst_addr, dst)
}
None => {
log::warn!("Received packet missing IP header");
return Err(IpPacketRouterError::PacketMissingIpHeader);
}
};
Ok(ParsedPacket {
packet_type,
src_addr,
dst_addr,
dst,
})
}
// Helper function to create the mixnet client.
// This is NOT in the SDK since we don't want to expose any of the client-core config types.
// We could however consider moving it to a crate in common in the future.
@@ -0,0 +1,50 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::net::SocketAddr;
use crate::error::IpPacketRouterError;
use nym_exit_policy::client::get_exit_policy;
use nym_exit_policy::ExitPolicy;
use reqwest::IntoUrl;
use url::Url;
pub struct ExitPolicyRequestFilter {
upstream: Option<Url>,
policy: ExitPolicy,
}
impl ExitPolicyRequestFilter {
pub(crate) async fn new_upstream(url: impl IntoUrl) -> Result<Self, IpPacketRouterError> {
let url = url
.into_url()
.map_err(|source| IpPacketRouterError::MalformedExitPolicyUpstreamUrl { source })?;
Ok(ExitPolicyRequestFilter {
upstream: Some(url.clone()),
policy: get_exit_policy(url).await?,
})
}
#[allow(unused)]
pub(crate) fn new(policy: ExitPolicy) -> Self {
ExitPolicyRequestFilter {
upstream: None,
policy,
}
}
pub fn policy(&self) -> &ExitPolicy {
&self.policy
}
pub fn upstream(&self) -> Option<&Url> {
self.upstream.as_ref()
}
pub(crate) async fn check(&self, addr: &SocketAddr) -> Result<bool, IpPacketRouterError> {
self.policy
.allows_sockaddr(addr)
.ok_or(IpPacketRouterError::AddressNotCoveredByExitPolicy { addr: *addr })
}
}
@@ -0,0 +1,68 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::Config;
use crate::error::IpPacketRouterError;
use crate::request_filter::exit_policy::ExitPolicyRequestFilter;
use log::{info, warn};
use std::{net::SocketAddr, sync::Arc};
pub mod exit_policy;
enum RequestFilterInner {
ExitPolicy {
policy_filter: ExitPolicyRequestFilter,
},
}
#[derive(Clone)]
pub struct RequestFilter {
inner: Arc<RequestFilterInner>,
}
impl RequestFilter {
pub(crate) async fn new(config: &Config) -> Result<Self, IpPacketRouterError> {
info!("setting up ExitPolicy based request filter...");
Self::new_exit_policy_filter(config).await
}
pub fn current_exit_policy_filter(&self) -> Option<&ExitPolicyRequestFilter> {
match &*self.inner {
RequestFilterInner::ExitPolicy { policy_filter } => Some(policy_filter),
}
}
pub(crate) async fn start_update_tasks(&self) {
match &*self.inner {
RequestFilterInner::ExitPolicy { .. } => {
// nothing to do for the exit policy (yet; we might add a refresher at some point)
}
}
}
async fn new_exit_policy_filter(config: &Config) -> Result<Self, IpPacketRouterError> {
let upstream_url = config
.ip_packet_router
.upstream_exit_policy_url
.as_ref()
.ok_or(IpPacketRouterError::NoUpstreamExitPolicy)?;
let policy_filter = ExitPolicyRequestFilter::new_upstream(upstream_url.clone()).await?;
Ok(RequestFilter {
inner: Arc::new(RequestFilterInner::ExitPolicy { policy_filter }),
})
}
pub(crate) async fn check_address(&self, address: &SocketAddr) -> bool {
match &*self.inner {
RequestFilterInner::ExitPolicy { policy_filter } => {
match policy_filter.check(address).await {
Err(err) => {
warn!("failed to validate '{address}' against the exit policy: {err}");
false
}
Ok(res) => res,
}
}
}
}
}
+14 -13
View File
@@ -44,7 +44,7 @@ pub(crate) trait ReleasePackage: Sized {
Ok(())
}
fn update_nym_dependencies(&mut self, _: &HashSet<String>) -> anyhow::Result<()> {
fn update_nym_dependencies(&mut self, _: &HashSet<String>, _: bool) -> anyhow::Result<()> {
Ok(())
}
}
@@ -57,28 +57,29 @@ pub(crate) trait VersionBumpExt: Sized {
fn try_remove_prerelease(&self) -> anyhow::Result<Self>;
}
pub(crate) fn try_bump_raw_prerelease(raw: &str) -> anyhow::Result<Prerelease> {
// ugh that's disgusting
let (rc_prefix, pre_version) = raw
.split_once('.')
.context("the prerelease version does not contain a valid rc.X suffix")?;
let parsed_version: u32 = pre_version.parse()?;
let updated_version = parsed_version + 1;
Ok(format!("{rc_prefix}.{updated_version}").parse()?)
}
impl VersionBumpExt for Version {
fn try_bump_prerelease(&self) -> anyhow::Result<Self> {
if self.pre.is_empty() {
bail!("the current version ({self}) does not have pre-release data set - are you sure you followed the release process correctly?")
}
// ugh that's disgusting
let (rc_prefix, pre_version) = self
.pre
.as_str()
.split_once('.')
.context("the prerelease version does not contain a valid rc.X suffix")?;
let parsed_version: u32 = pre_version.parse()?;
let updated_version = parsed_version + 1;
let pre = format!("{rc_prefix}.{updated_version}").parse()?;
Ok(Version {
major: self.major,
minor: self.minor,
patch: self.patch,
pre,
pre: try_bump_raw_prerelease(self.pre.as_str())?,
build: self.build.clone(),
})
}
+48 -11
View File
@@ -1,6 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::helpers::try_bump_raw_prerelease;
use crate::json_types::DepsSet;
use crate::{json_types, ReleasePackage};
use anyhow::{bail, Context};
@@ -14,10 +15,18 @@ pub struct PackageJson {
inner: json_types::Package,
}
fn update_dependencies(deps: &mut DepsSet, names: &HashSet<String>) -> anyhow::Result<()> {
fn update_dependencies(
deps: &mut DepsSet,
names: &HashSet<String>,
pre_release: bool,
) -> anyhow::Result<()> {
for (package, version) in deps.iter_mut() {
if names.contains(package) {
let updated = try_bump_minor_version_req(version)?;
let updated = if pre_release {
try_bump_prerelease_version_req(version)?
} else {
try_bump_minor_version_req(version)?
};
println!("\t\t>>> updating '{package}' from {version} to {updated}");
*version = updated
@@ -52,21 +61,25 @@ impl ReleasePackage for PackageJson {
self.inner.version = version.to_string()
}
fn update_nym_dependencies(&mut self, names: &HashSet<String>) -> anyhow::Result<()> {
fn update_nym_dependencies(
&mut self,
names: &HashSet<String>,
pre_release: bool,
) -> anyhow::Result<()> {
println!("\t>>> updating @nymproject dependencies...");
update_dependencies(&mut self.inner.dependencies, names)?;
update_dependencies(&mut self.inner.dependencies, names, pre_release)?;
println!("\t>>> updating @nymproject peerDependencies...");
update_dependencies(&mut self.inner.peer_dependencies, names)?;
update_dependencies(&mut self.inner.peer_dependencies, names, pre_release)?;
println!("\t>>> updating @nymproject devDependencies...");
update_dependencies(&mut self.inner.dev_dependencies, names)?;
update_dependencies(&mut self.inner.dev_dependencies, names, pre_release)?;
println!("\t>>> updating @nymproject optionalDependencies...");
update_dependencies(&mut self.inner.optional_dependencies, names)?;
update_dependencies(&mut self.inner.optional_dependencies, names, pre_release)?;
println!("\t>>> updating @nymproject bundledDependencies...");
update_dependencies(&mut self.inner.bundled_dependencies, names)?;
update_dependencies(&mut self.inner.bundled_dependencies, names, pre_release)?;
Ok(())
}
@@ -101,9 +114,9 @@ pub(crate) fn find_package_path(dir: &Path) -> anyhow::Result<PathBuf> {
// expected structure: `>=X.Y.Z-rc.W || ^X`
fn try_bump_minor_version_req(raw_req: &str) -> anyhow::Result<String> {
let (req, major) = raw_req
.split_once("||")
.context("invalid version requirement")?;
let (req, major) = raw_req.split_once("||").context(format!(
"'{raw_req}' is not a valid semver version requirement - we expect '`>=X.Y.Z-rc.W || ^X`'"
))?;
let parsed_req = VersionReq::parse(req)?;
let parsed_major = VersionReq::parse(major)?;
if parsed_req.comparators.len() != 1 {
@@ -123,6 +136,30 @@ fn try_bump_minor_version_req(raw_req: &str) -> anyhow::Result<String> {
Ok(format!("{updated} || {parsed_major}"))
}
// expected structure: `>=X.Y.Z-rc.W || ^X`
fn try_bump_prerelease_version_req(raw_req: &str) -> anyhow::Result<String> {
let (req, major) = raw_req.split_once("||").context(format!(
"'{raw_req}' is not a valid semver version requirement - we expect '`>=X.Y.Z-rc.W || ^X`'"
))?;
let parsed_req = VersionReq::parse(req)?;
let parsed_major = VersionReq::parse(major)?;
if parsed_req.comparators.len() != 1 {
bail!("wrong number of version requirements present in {parsed_req}")
}
let updated = VersionReq {
comparators: vec![Comparator {
op: parsed_req.comparators[0].op,
major: parsed_req.comparators[0].major,
minor: parsed_req.comparators[0].minor,
patch: parsed_req.comparators[0].patch,
pre: try_bump_raw_prerelease(parsed_req.comparators[0].pre.as_str())?,
}],
};
Ok(format!("{updated} || {parsed_major}"))
}
#[cfg(test)]
mod tests {
use super::*;
+74 -15
View File
@@ -5,7 +5,7 @@ use crate::cargo::CargoPackage;
use crate::helpers::ReleasePackage;
use crate::json::PackageJson;
use clap::{Parser, Subcommand};
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::env;
use std::path::{Path, PathBuf};
@@ -18,6 +18,48 @@ fn default_root() -> PathBuf {
env::current_dir().unwrap()
}
struct Summary {
cargo_results: HashMap<String, anyhow::Result<()>>,
json_results: HashMap<String, anyhow::Result<()>>,
}
impl Summary {
fn new(
cargo_results: HashMap<String, anyhow::Result<()>>,
json_results: HashMap<String, anyhow::Result<()>>,
) -> Self {
Summary {
cargo_results,
json_results,
}
}
fn print(&self) {
let cargo_ok = self.cargo_results.values().filter(|p| p.is_ok()).count();
let json_ok = self.json_results.values().filter(|p| p.is_ok()).count();
println!("SUMMARY");
println!("inspected {} cargo packages", self.cargo_results.len());
println!("updated {cargo_ok} cargo packages");
for (package, res) in &self.cargo_results {
if let Err(err) = res {
println!(
"\t>>> ❌ FAILURE: cargo package '{package}' failed to get updated: {err}"
);
}
}
println!("inspected {} json packages", self.json_results.len());
println!("updated {json_ok} json packages");
for (package, res) in &self.json_results {
if let Err(err) = res {
println!("\t>>> ❌ FAILURE: json package '{package}' failed to get updated: {err}");
}
}
}
}
#[derive(Parser)]
struct Args {
#[arg(default_value=default_root().into_os_string())]
@@ -36,12 +78,13 @@ enum Commands {
/// It will also update the `@nymproject/...` dependencies from `">=X.Y.Z-rc.0 || ^X"` to `">=X.Y.(Z+1)-rc.0 || ^X"`
BumpVersion {
#[arg(long)]
/// If enabled, the packages will only have their rc version bumped and the dependencies won't get updated at all
/// If enabled, the packages will only have their rc version bumped and the dependencies
/// will get updated from `">=X.Y.Z-rc.W || ^X"` to `">=X.Y.Z-rc.(W+1) || ^X"`
pre_release: bool,
},
}
fn remove_suffix<Pkg: ReleasePackage>(root: &Path, path: impl AsRef<Path>) {
fn remove_suffix<Pkg: ReleasePackage>(root: &Path, path: impl AsRef<Path>) -> anyhow::Result<()> {
let path = root.join(path);
println!(
">>> [{}] UPDATING PACKAGE {}: ",
@@ -51,8 +94,10 @@ fn remove_suffix<Pkg: ReleasePackage>(root: &Path, path: impl AsRef<Path>) {
if let Err(err) = { remove_suffix_inner::<Pkg>(path) } {
println!("\t>>> ❌ FAILURE: {err}");
Err(err)
} else {
println!("\t>>> ✅ SUCCESS");
Ok(())
}
}
@@ -70,7 +115,7 @@ fn bump_version<Pkg: ReleasePackage>(
path: impl AsRef<Path>,
dependencies_to_update: &HashSet<String>,
pre_release: bool,
) {
) -> anyhow::Result<()> {
let path = root.join(path);
println!(
">>> [{}] UPDATING PACKAGE {}: ",
@@ -79,8 +124,10 @@ fn bump_version<Pkg: ReleasePackage>(
);
if let Err(err) = { bump_version_inner::<Pkg>(path, dependencies_to_update, pre_release) } {
println!("\t>>> ❌ FAILURE: {err}");
Err(err)
} else {
println!("\t>>> ✅ SUCCESS");
Ok(())
}
}
@@ -93,9 +140,7 @@ fn bump_version_inner<Pkg: ReleasePackage>(
let mut package = Pkg::open(path)?;
package.bump_version(pre_release)?;
if !pre_release {
package.update_nym_dependencies(dependencies_to_update)?;
}
package.update_nym_dependencies(dependencies_to_update, pre_release)?;
println!("\t>>> saving the package file...");
package.save_changes()
@@ -132,34 +177,46 @@ impl InternalPackages {
self.internal_js_dependencies.insert(name.into());
}
pub fn remove_suffix(&self) {
pub fn remove_suffix(&self) -> Summary {
let mut cargo_results = HashMap::new();
for cargo_package in &self.cargo {
remove_suffix::<CargoPackage>(&self.root, cargo_package);
let res = remove_suffix::<CargoPackage>(&self.root, cargo_package);
cargo_results.insert(cargo_package.clone(), res);
}
let mut json_results = HashMap::new();
for package_json in &self.json {
remove_suffix::<PackageJson>(&self.root, package_json);
let res = remove_suffix::<PackageJson>(&self.root, package_json);
json_results.insert(package_json.clone(), res);
}
Summary::new(cargo_results, json_results)
}
pub fn bump_version(&self, pre_release: bool) {
pub fn bump_version(&self, pre_release: bool) -> Summary {
let mut cargo_results = HashMap::new();
for cargo_package in &self.cargo {
bump_version::<CargoPackage>(
let res = bump_version::<CargoPackage>(
&self.root,
cargo_package,
&Default::default(),
pre_release,
);
cargo_results.insert(cargo_package.clone(), res);
}
let mut json_results = HashMap::new();
for package_json in &self.json {
bump_version::<PackageJson>(
let res = bump_version::<PackageJson>(
&self.root,
package_json,
&self.internal_js_dependencies,
pre_release,
);
json_results.insert(package_json.clone(), res);
}
Summary::new(cargo_results, json_results)
}
}
@@ -226,10 +283,12 @@ fn main() -> anyhow::Result<()> {
let args = Args::parse();
let packages = initialise_internal_packages(args.root);
match args.command {
let summary = match args.command {
Commands::RemoveSuffix => packages.remove_suffix(),
Commands::BumpVersion { pre_release } => packages.bump_version(pre_release),
}
};
summary.print();
Ok(())
}
@@ -13,6 +13,9 @@ pub(crate) async fn execute(
nym_cli_commands::validator::mixnet::operators::mixnode::settings::MixnetOperatorsMixnodeSettingsCommands::UpdateConfig(args) => {
nym_cli_commands::validator::mixnet::operators::mixnode::settings::update_config::update_config(args, create_signing_client(global_args, network_details)?).await
}
nym_cli_commands::validator::mixnet::operators::mixnode::settings::MixnetOperatorsMixnodeSettingsCommands::UpdateCostParameters(args) => {
nym_cli_commands::validator::mixnet::operators::mixnode::settings::update_cost_params::update_cost_params(args, create_signing_client(global_args, network_details)?).await
}
_ => unreachable!(),
}
Ok(())