Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 161a8a7d4d | |||
| 25aa04686e | |||
| 9b6355b256 | |||
| 65272d7bf6 | |||
| 810dce5ee8 | |||
| 58ec878256 | |||
| a5c1e4abf0 | |||
| 3a1003c564 | |||
| 1cdd8f6c08 | |||
| 808e3f0562 | |||
| f0dade3c5b | |||
| 0a3c2b3cca | |||
| ac66906980 | |||
| afd9f823d8 | |||
| d818448848 | |||
| a9a1ba2847 | |||
| 2708c0ce10 | |||
| bb3e9b3d4e | |||
| e624f42ad5 | |||
| 7da83397dd | |||
| 26d0b4b159 | |||
| b74490dc50 | |||
| 8113095ff5 | |||
| 8339d6ab49 | |||
| f037b2ae68 | |||
| 2a4c1d96a4 | |||
| ed04ddf1c4 | |||
| 34b5d66df6 | |||
| 0a1a5c25f7 | |||
| 6bdba7046f | |||
| 428d91a536 | |||
| 88e0eaafcb | |||
| dd19cabf15 |
Generated
+44
-5
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"] }
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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"]
|
||||
@@ -0,0 +1,7 @@
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux;
|
||||
|
||||
pub mod tun_task_channel;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use linux::tun_device;
|
||||
+66
-82
@@ -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),
|
||||
@@ -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"]
|
||||
|
||||
@@ -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;
|
||||
|
||||
+21
-23
@@ -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;
|
||||
+1
-1
@@ -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(),
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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,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>)>);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 },
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -81,3 +81,8 @@ cpucycles = [
|
||||
"opentelemetry",
|
||||
"nym-bin-common/tracing",
|
||||
]
|
||||
|
||||
[package.metadata.deb]
|
||||
name = "nym-mixnode"
|
||||
maintainer-scripts = "debian"
|
||||
systemd-units = { enable = false }
|
||||
|
||||
@@ -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'
|
||||
@@ -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
|
||||
@@ -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]
|
||||
|
||||
@@ -31,6 +31,12 @@ pub enum NymNodeError {
|
||||
source: WireguardError,
|
||||
},
|
||||
|
||||
#[error(transparent)]
|
||||
KeyRecoveryError {
|
||||
#[from]
|
||||
source: nym_crypto::asymmetric::encryption::KeyRecoveryError,
|
||||
},
|
||||
|
||||
#[error("unimplemented")]
|
||||
Unimplemented,
|
||||
}
|
||||
|
||||
+2
-5
@@ -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(®istration_in_progress),
|
||||
binding_port: 8080,
|
||||
free_private_network_ips,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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(())
|
||||
|
||||
Reference in New Issue
Block a user