Skip to content

Commit 1f6daa7

Browse files
committed
Bits and bobs to make everything work
1 parent fbcc9e4 commit 1f6daa7

File tree

15 files changed

+150
-66
lines changed

15 files changed

+150
-66
lines changed

common/credential-verification/src/ecash/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,4 +253,8 @@ impl traits::EcashManager for MockEcashManager {
253253
}
254254

255255
fn async_verify(&self, _ticket: ClientTicket) {}
256+
257+
fn is_mock(&self) -> bool {
258+
true
259+
}
256260
}

common/credential-verification/src/ecash/traits.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,10 @@ pub trait EcashManager {
2020
aggregated_verification_key: &VerificationKeyAuth,
2121
) -> Result<(), EcashTicketError>;
2222
fn async_verify(&self, ticket: ClientTicket);
23+
24+
/// Returns true if this is a mock ecash manager (for local testing).
25+
/// Default implementation returns false.
26+
fn is_mock(&self) -> bool {
27+
false
28+
}
2329
}

common/wireguard/src/lib.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ impl WireguardGatewayData {
159159
pub struct WireguardData {
160160
pub inner: WireguardGatewayData,
161161
pub peer_rx: Receiver<PeerControlRequest>,
162+
pub use_userspace: bool,
162163
}
163164

164165
/// Start wireguard device
@@ -170,6 +171,7 @@ pub async fn start_wireguard(
170171
upgrade_mode_status: nym_credential_verification::upgrade_mode::UpgradeModeStatus,
171172
shutdown_token: nym_task::ShutdownToken,
172173
wireguard_data: WireguardData,
174+
use_userspace: bool,
173175
) -> Result<std::sync::Arc<WgApiWrapper>, Box<dyn std::error::Error + Send + Sync + 'static>> {
174176
use base64::{Engine, prelude::BASE64_STANDARD};
175177
use defguard_wireguard_rs::{InterfaceConfiguration, WireguardInterfaceApi};
@@ -181,7 +183,8 @@ pub async fn start_wireguard(
181183
use tracing::info;
182184

183185
let ifname = String::from(WG_TUN_BASE_NAME);
184-
let wg_api = defguard_wireguard_rs::WGApi::new(ifname.clone(), false)?;
186+
info!("Initializing WireGuard interface '{}' with use_userspace={}", ifname, use_userspace);
187+
let wg_api = defguard_wireguard_rs::WGApi::new(ifname.clone(), use_userspace)?;
185188
let mut peer_bandwidth_managers = HashMap::with_capacity(peers.len());
186189

187190
for peer in peers.iter() {
@@ -212,7 +215,13 @@ pub async fn start_wireguard(
212215
interface_config.address, interface_config.port
213216
);
214217

215-
wg_api.configure_interface(&interface_config)?;
218+
info!("Configuring WireGuard interface...");
219+
wg_api.configure_interface(&interface_config).map_err(|e| {
220+
log::error!("Failed to configure WireGuard interface: {:?}", e);
221+
e
222+
})?;
223+
224+
info!("Adding IPv6 address to interface...");
216225
std::process::Command::new("ip")
217226
.args([
218227
"-6",
@@ -226,7 +235,11 @@ pub async fn start_wireguard(
226235
"dev",
227236
(&ifname),
228237
])
229-
.output()?;
238+
.output()
239+
.map_err(|e| {
240+
log::error!("Failed to add IPv6 address: {:?}", e);
241+
e
242+
})?;
230243

231244
// Use a dummy peer to create routing rule for the entire network space
232245
let mut catch_all_peer = Peer::new(Key::new([0; 32]));

docker/localnet/Dockerfile.localnet

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,27 @@ RUN cargo build --release --locked \
1515
-p nym-network-requester \
1616
-p nym-socks5-client
1717

18-
# Install runtime dependencies
18+
# Install runtime dependencies including Go for wireguard-go
1919
RUN apt update && apt install -y \
2020
python3 \
2121
python3-pip \
2222
netcat-openbsd \
2323
jq \
2424
iproute2 \
25+
net-tools \
26+
wireguard-tools \
27+
golang-go \
28+
git \
2529
&& rm -rf /var/lib/apt/lists/*
2630

31+
# Install wireguard-go (userspace WireGuard implementation)
32+
RUN git clone https://git.zx2c4.com/wireguard-go && \
33+
cd wireguard-go && \
34+
make && \
35+
cp wireguard-go /usr/local/bin/ && \
36+
cd .. && \
37+
rm -rf wireguard-go
38+
2739
# Install Python dependencies for build_topology.py
2840
RUN pip3 install --break-system-packages base58
2941

docker/localnet/localnet.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ start_mixnode() {
222222
start_gateway() {
223223
log_info "Starting $GATEWAY_CONTAINER..."
224224

225-
container run \
225+
container run \
226226
--name "$GATEWAY_CONTAINER" \
227227
-m 2G \
228228
--network "$NETWORK_NAME" \
@@ -256,14 +256,15 @@ start_gateway() {
256256
--lp-use-mock-ecash true \
257257
--output=json \
258258
--wireguard-enabled true \
259+
--wireguard-userspace true \
259260
--bonding-information-output="/localnet/gateway.json";
260261
261262
echo "Waiting for network.json...";
262263
while [ ! -f /localnet/network.json ]; do
263264
sleep 2;
264265
done;
265266
echo "Starting gateway with LP listener (mock ecash)...";
266-
exec nym-node run --id gateway-localnet --unsafe-disable-replay-protection --local
267+
exec nym-node run --id gateway-localnet --unsafe-disable-replay-protection --local --wireguard-enabled true --wireguard-userspace true --lp-use-mock-ecash true
267268
'
268269

269270
log_success "$GATEWAY_CONTAINER started"

gateway/src/node/lp_listener/handler.rs

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -367,9 +367,14 @@ impl LpConnectionHandler {
367367
GatewayError::LpProtocolError(format!("Failed to serialize response: {}", e))
368368
})?;
369369

370-
// Create LP packet with response
371-
let packet = session.create_data_packet(data).map_err(|e| {
372-
GatewayError::LpProtocolError(format!("Failed to create data packet: {}", e))
370+
// Encrypt data first (this increments Noise internal counter)
371+
let encrypted_message = session.encrypt_data(&data).map_err(|e| {
372+
GatewayError::LpProtocolError(format!("Failed to encrypt data: {}", e))
373+
})?;
374+
375+
// Create LP packet with encrypted message (this increments LP protocol counter)
376+
let packet = session.next_packet(encrypted_message).map_err(|e| {
377+
GatewayError::LpProtocolError(format!("Failed to create packet: {}", e))
373378
})?;
374379

375380
// Send the packet
@@ -473,28 +478,6 @@ impl LpConnectionHandler {
473478
}
474479
}
475480

476-
// Extension trait for LpSession to create packets
477-
// This would ideally be part of nym-lp
478-
trait LpSessionExt {
479-
fn create_data_packet(&self, data: Vec<u8>) -> Result<LpPacket, nym_lp::LpError>;
480-
}
481-
482-
impl LpSessionExt for LpSession {
483-
fn create_data_packet(&self, data: Vec<u8>) -> Result<LpPacket, nym_lp::LpError> {
484-
use nym_lp::packet::LpHeader;
485-
486-
let header = LpHeader {
487-
protocol_version: 1,
488-
session_id: self.id(),
489-
counter: 0, // TODO: Use actual counter from session
490-
};
491-
492-
let message = LpMessage::EncryptedData(data);
493-
494-
Ok(LpPacket::new(header, message))
495-
}
496-
}
497-
498481
#[cfg(test)]
499482
mod tests {
500483
use super::*;

gateway/src/node/lp_listener/registration.rs

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,26 @@ async fn credential_storage_preparation(
5959
ecash_verifier: Arc<dyn EcashManager + Send + Sync>,
6060
client_id: i64,
6161
) -> Result<PersistedBandwidth, GatewayError> {
62-
ecash_verifier
62+
// Check if bandwidth entry already exists (idempotent)
63+
let existing_bandwidth = ecash_verifier
6364
.storage()
64-
.create_bandwidth_entry(client_id)
65+
.get_available_bandwidth(client_id)
6566
.await?;
67+
68+
// Only create if it doesn't exist
69+
if existing_bandwidth.is_none() {
70+
ecash_verifier
71+
.storage()
72+
.create_bandwidth_entry(client_id)
73+
.await?;
74+
}
75+
6676
let bandwidth = ecash_verifier
6777
.storage()
6878
.get_available_bandwidth(client_id)
6979
.await?
7080
.ok_or_else(|| {
71-
GatewayError::InternalError("bandwidth entry should have just been created".to_string())
81+
GatewayError::InternalError("bandwidth entry should exist".to_string())
7282
})?;
7383
Ok(bandwidth)
7484
}
@@ -96,18 +106,30 @@ async fn credential_verification(
96106
// Track credential verification attempts
97107
inc!("lp_credential_verification_attempts");
98108

99-
match verifier.verify().await {
100-
Ok(allocated) => {
101-
inc!("lp_credential_verification_success");
102-
// Track allocated bandwidth
103-
inc_by!("lp_bandwidth_allocated_bytes_total", allocated);
104-
Ok(allocated)
105-
}
106-
Err(e) => {
107-
inc!("lp_credential_verification_failed");
108-
Err(e.into())
109+
// For mock ecash mode (local testing), skip cryptographic verification
110+
// and just return a dummy bandwidth value since we don't have blockchain access
111+
let allocated = if ecash_verifier.is_mock() {
112+
// Return a reasonable test bandwidth value (e.g., 1GB in bytes)
113+
const MOCK_BANDWIDTH: i64 = 1024 * 1024 * 1024;
114+
inc!("lp_credential_verification_success");
115+
inc_by!("lp_bandwidth_allocated_bytes_total", MOCK_BANDWIDTH);
116+
Ok::<i64, GatewayError>(MOCK_BANDWIDTH)
117+
} else {
118+
match verifier.verify().await {
119+
Ok(allocated) => {
120+
inc!("lp_credential_verification_success");
121+
// Track allocated bandwidth
122+
inc_by!("lp_bandwidth_allocated_bytes_total", allocated);
123+
Ok(allocated)
124+
}
125+
Err(e) => {
126+
inc!("lp_credential_verification_failed");
127+
Err(e.into())
128+
}
109129
}
110-
}
130+
}?;
131+
132+
Ok(allocated)
111133
}
112134

113135
/// Process an LP registration request
@@ -320,7 +342,22 @@ async fn register_wg_peer(
320342
];
321343
peer.persistent_keepalive_interval = Some(25);
322344

323-
// Send to WireGuard peer controller and track latency
345+
// Store peer in database FIRST (before adding to controller)
346+
// This ensures bandwidth storage exists when controller's generate_bandwidth_manager() is called
347+
let client_id = state
348+
.storage
349+
.insert_wireguard_peer(&peer, ticket_type.into())
350+
.await
351+
.map_err(|e| {
352+
error!("Failed to store WireGuard peer in database: {}", e);
353+
GatewayError::InternalError(format!("Failed to store peer: {}", e))
354+
})?;
355+
356+
// Create bandwidth entry for the client
357+
// This must happen BEFORE AddPeer because generate_bandwidth_manager() expects it to exist
358+
credential_storage_preparation(state.ecash_verifier.clone(), client_id).await?;
359+
360+
// Now send to WireGuard peer controller and track latency
324361
let controller_start = std::time::Instant::now();
325362
let (tx, rx) = oneshot::channel();
326363
wg_controller
@@ -348,16 +385,6 @@ async fn register_wg_peer(
348385

349386
result?;
350387

351-
// Store bandwidth allocation and get client_id
352-
let client_id = state
353-
.storage
354-
.insert_wireguard_peer(&peer, ticket_type.into())
355-
.await
356-
.map_err(|e| {
357-
error!("Failed to store WireGuard peer in database: {}", e);
358-
GatewayError::InternalError(format!("Failed to store peer: {}", e))
359-
})?;
360-
361388
// Get gateway's actual WireGuard public key
362389
let gateway_pubkey = *wg_data.keypair().public_key();
363390

gateway/src/node/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,13 +624,15 @@ impl GatewayTasksBuilder {
624624
wireguard_data.inner.config().announced_metadata_port,
625625
);
626626

627+
let use_userspace = wireguard_data.use_userspace;
627628
let wg_handle = nym_wireguard::start_wireguard(
628629
ecash_manager,
629630
self.metrics.clone(),
630631
all_peers,
631632
self.upgrade_mode_state.upgrade_mode_status(),
632633
self.shutdown_tracker.clone_shutdown_token(),
633634
wireguard_data,
635+
use_userspace,
634636
)
635637
.await?;
636638

nym-gateway-probe/src/bandwidth_helpers.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use nym_sdk::bandwidth::BandwidthImporter;
1111
use nym_sdk::mixnet::{DisconnectedMixnetClient, EphemeralCredentialStorage};
1212
use nym_validator_client::nyxd::error::NyxdError;
1313
use std::time::Duration;
14+
use time::OffsetDateTime;
1415
use tracing::{error, info};
1516

1617
pub(crate) async fn import_bandwidth(
@@ -237,6 +238,11 @@ pub(crate) fn create_dummy_credential(
237238
0, 0, 0, 0, 0, 1,
238239
];
239240

240-
CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES)
241-
.expect("Failed to deserialize test credential - this is a bug in the test harness")
241+
let mut credential = CredentialSpendingData::try_from_bytes(&CREDENTIAL_BYTES)
242+
.expect("Failed to deserialize test credential - this is a bug in the test harness");
243+
244+
// Update spend_date to today to pass validation
245+
credential.spend_date = OffsetDateTime::now_utc().date();
246+
247+
credential
242248
}

nym-node/src/cli/helpers.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,14 @@ pub(crate) struct WireguardArgs {
293293
env = NYMNODE_WG_PRIVATE_NETWORK_PREFIX_ARG
294294
)]
295295
pub(crate) wireguard_private_network_prefix: Option<u8>,
296+
297+
/// Use userspace implementation of WireGuard (wireguard-go) instead of kernel module.
298+
/// Useful in containerized environments without kernel WireGuard support.
299+
#[clap(
300+
long,
301+
env = NYMNODE_WG_USERSPACE_ARG
302+
)]
303+
pub(crate) wireguard_userspace: Option<bool>,
296304
}
297305

298306
impl WireguardArgs {
@@ -321,6 +329,10 @@ impl WireguardArgs {
321329
section.private_network_prefix_v4 = private_network_prefix
322330
}
323331

332+
if let Some(userspace) = self.wireguard_userspace {
333+
section.use_userspace = userspace
334+
}
335+
324336
section
325337
}
326338
}

0 commit comments

Comments
 (0)