Skip to content

Commit d989a02

Browse files
committed
Bits and bobs to make everything work
1 parent 4e75bf9 commit d989a02

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
@@ -156,6 +156,7 @@ impl WireguardGatewayData {
156156
pub struct WireguardData {
157157
pub inner: WireguardGatewayData,
158158
pub peer_rx: Receiver<PeerControlRequest>,
159+
pub use_userspace: bool,
159160
}
160161

161162
/// Start wireguard device
@@ -166,6 +167,7 @@ pub async fn start_wireguard(
166167
peers: Vec<Peer>,
167168
shutdown_token: nym_task::ShutdownToken,
168169
wireguard_data: WireguardData,
170+
use_userspace: bool,
169171
) -> Result<std::sync::Arc<WgApiWrapper>, Box<dyn std::error::Error + Send + Sync + 'static>> {
170172
use base64::{Engine, prelude::BASE64_STANDARD};
171173
use defguard_wireguard_rs::{InterfaceConfiguration, WireguardInterfaceApi};
@@ -177,7 +179,8 @@ pub async fn start_wireguard(
177179
use tracing::info;
178180

179181
let ifname = String::from(WG_TUN_BASE_NAME);
180-
let wg_api = defguard_wireguard_rs::WGApi::new(ifname.clone(), false)?;
182+
info!("Initializing WireGuard interface '{}' with use_userspace={}", ifname, use_userspace);
183+
let wg_api = defguard_wireguard_rs::WGApi::new(ifname.clone(), use_userspace)?;
181184
let mut peer_bandwidth_managers = HashMap::with_capacity(peers.len());
182185

183186
for peer in peers.iter() {
@@ -208,7 +211,13 @@ pub async fn start_wireguard(
208211
interface_config.address, interface_config.port
209212
);
210213

211-
wg_api.configure_interface(&interface_config)?;
214+
info!("Configuring WireGuard interface...");
215+
wg_api.configure_interface(&interface_config).map_err(|e| {
216+
log::error!("Failed to configure WireGuard interface: {:?}", e);
217+
e
218+
})?;
219+
220+
info!("Adding IPv6 address to interface...");
212221
std::process::Command::new("ip")
213222
.args([
214223
"-6",
@@ -222,7 +231,11 @@ pub async fn start_wireguard(
222231
"dev",
223232
(&ifname),
224233
])
225-
.output()?;
234+
.output()
235+
.map_err(|e| {
236+
log::error!("Failed to add IPv6 address: {:?}", e);
237+
e
238+
})?;
226239

227240
// Use a dummy peer to create routing rule for the entire network space
228241
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
@@ -565,12 +565,14 @@ impl GatewayTasksBuilder {
565565
wireguard_data.inner.config().announced_metadata_port,
566566
);
567567

568+
let use_userspace = wireguard_data.use_userspace;
568569
let wg_handle = nym_wireguard::start_wireguard(
569570
ecash_manager,
570571
self.metrics.clone(),
571572
all_peers,
572573
self.shutdown_tracker.clone_shutdown_token(),
573574
wireguard_data,
575+
use_userspace,
574576
)
575577
.await?;
576578

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
@@ -291,6 +291,14 @@ pub(crate) struct WireguardArgs {
291291
env = NYMNODE_WG_PRIVATE_NETWORK_PREFIX_ARG
292292
)]
293293
pub(crate) wireguard_private_network_prefix: Option<u8>,
294+
295+
/// Use userspace implementation of WireGuard (wireguard-go) instead of kernel module.
296+
/// Useful in containerized environments without kernel WireGuard support.
297+
#[clap(
298+
long,
299+
env = NYMNODE_WG_USERSPACE_ARG
300+
)]
301+
pub(crate) wireguard_userspace: Option<bool>,
294302
}
295303

296304
impl WireguardArgs {
@@ -319,6 +327,10 @@ impl WireguardArgs {
319327
section.private_network_prefix_v4 = private_network_prefix
320328
}
321329

330+
if let Some(userspace) = self.wireguard_userspace {
331+
section.use_userspace = userspace
332+
}
333+
322334
section
323335
}
324336
}

0 commit comments

Comments
 (0)