Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perform file downloads with netstack in the WG gateway probe #1329

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 77 additions & 61 deletions nym-vpn-core/crates/nym-gateway-probe/netstack_ping/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,15 @@ package main
#include <stdint.h>
#include <stdlib.h>

typedef struct ListRef {
const void *ptr;
uintptr_t len;
} ListRef;

typedef struct StringRef {
const uint8_t *ptr;
uintptr_t len;
} StringRef;

typedef struct NetstackResponseRef {
bool can_handshake;
uint16_t sent_ips;
uint16_t received_ips;
uint16_t sent_hosts;
uint16_t received_hosts;
bool can_resolve_dns;
} NetstackResponseRef;
typedef struct ListRef {
const void *ptr;
uintptr_t len;
} ListRef;

typedef struct NetstackRequestRef {
struct StringRef wg_ip;
Expand All @@ -38,8 +29,21 @@ typedef struct NetstackRequestRef {
uint8_t num_ping;
uint64_t send_timeout_sec;
uint64_t recv_timeout_sec;
uint64_t download_timeout_sec;
} NetstackRequestRef;

typedef struct NetstackResponseRef {
bool can_handshake;
uint16_t sent_ips;
uint16_t received_ips;
uint16_t sent_hosts;
uint16_t received_hosts;
bool can_resolve_dns;
struct StringRef downloaded_file;
uint64_t download_duration;
struct StringRef download_err;
} NetstackResponseRef;

// hack from: https://stackoverflow.com/a/69904977
__attribute__((weak))
inline void NetstackCall_ping_cb(const void *f_ptr, struct NetstackResponseRef resp, const void *slot) {
Expand Down Expand Up @@ -215,30 +219,32 @@ func refC_float(p *float32, _ *[]byte) C.float { return C.float(*p) }
func refC_double(p *float64, _ *[]byte) C.double { return C.double(*p) }

type NetstackRequest struct {
wg_ip string
private_key string
public_key string
endpoint string
dns string
ping_hosts []string
ping_ips []string
num_ping uint8
send_timeout_sec uint64
recv_timeout_sec uint64
wg_ip string
private_key string
public_key string
endpoint string
dns string
ping_hosts []string
ping_ips []string
num_ping uint8
send_timeout_sec uint64
recv_timeout_sec uint64
download_timeout_sec uint64
}

func newNetstackRequest(p C.NetstackRequestRef) NetstackRequest {
return NetstackRequest{
wg_ip: newString(p.wg_ip),
private_key: newString(p.private_key),
public_key: newString(p.public_key),
endpoint: newString(p.endpoint),
dns: newString(p.dns),
ping_hosts: new_list_mapper(newString)(p.ping_hosts),
ping_ips: new_list_mapper(newString)(p.ping_ips),
num_ping: newC_uint8_t(p.num_ping),
send_timeout_sec: newC_uint64_t(p.send_timeout_sec),
recv_timeout_sec: newC_uint64_t(p.recv_timeout_sec),
wg_ip: newString(p.wg_ip),
private_key: newString(p.private_key),
public_key: newString(p.public_key),
endpoint: newString(p.endpoint),
dns: newString(p.dns),
ping_hosts: new_list_mapper(newString)(p.ping_hosts),
ping_ips: new_list_mapper(newString)(p.ping_ips),
num_ping: newC_uint8_t(p.num_ping),
send_timeout_sec: newC_uint64_t(p.send_timeout_sec),
recv_timeout_sec: newC_uint64_t(p.recv_timeout_sec),
download_timeout_sec: newC_uint64_t(p.download_timeout_sec),
}
}
func cntNetstackRequest(s *NetstackRequest, cnt *uint) [0]C.NetstackRequestRef {
Expand All @@ -248,49 +254,59 @@ func cntNetstackRequest(s *NetstackRequest, cnt *uint) [0]C.NetstackRequestRef {
}
func refNetstackRequest(p *NetstackRequest, buffer *[]byte) C.NetstackRequestRef {
return C.NetstackRequestRef{
wg_ip: refString(&p.wg_ip, buffer),
private_key: refString(&p.private_key, buffer),
public_key: refString(&p.public_key, buffer),
endpoint: refString(&p.endpoint, buffer),
dns: refString(&p.dns, buffer),
ping_hosts: ref_list_mapper(refString)(&p.ping_hosts, buffer),
ping_ips: ref_list_mapper(refString)(&p.ping_ips, buffer),
num_ping: refC_uint8_t(&p.num_ping, buffer),
send_timeout_sec: refC_uint64_t(&p.send_timeout_sec, buffer),
recv_timeout_sec: refC_uint64_t(&p.recv_timeout_sec, buffer),
wg_ip: refString(&p.wg_ip, buffer),
private_key: refString(&p.private_key, buffer),
public_key: refString(&p.public_key, buffer),
endpoint: refString(&p.endpoint, buffer),
dns: refString(&p.dns, buffer),
ping_hosts: ref_list_mapper(refString)(&p.ping_hosts, buffer),
ping_ips: ref_list_mapper(refString)(&p.ping_ips, buffer),
num_ping: refC_uint8_t(&p.num_ping, buffer),
send_timeout_sec: refC_uint64_t(&p.send_timeout_sec, buffer),
recv_timeout_sec: refC_uint64_t(&p.recv_timeout_sec, buffer),
download_timeout_sec: refC_uint64_t(&p.download_timeout_sec, buffer),
}
}

type NetstackResponse struct {
can_handshake bool
sent_ips uint16
received_ips uint16
sent_hosts uint16
received_hosts uint16
can_resolve_dns bool
can_handshake bool
sent_ips uint16
received_ips uint16
sent_hosts uint16
received_hosts uint16
can_resolve_dns bool
downloaded_file string
download_duration uint64
download_err string
}

func newNetstackResponse(p C.NetstackResponseRef) NetstackResponse {
return NetstackResponse{
can_handshake: newC_bool(p.can_handshake),
sent_ips: newC_uint16_t(p.sent_ips),
received_ips: newC_uint16_t(p.received_ips),
sent_hosts: newC_uint16_t(p.sent_hosts),
received_hosts: newC_uint16_t(p.received_hosts),
can_resolve_dns: newC_bool(p.can_resolve_dns),
can_handshake: newC_bool(p.can_handshake),
sent_ips: newC_uint16_t(p.sent_ips),
received_ips: newC_uint16_t(p.received_ips),
sent_hosts: newC_uint16_t(p.sent_hosts),
received_hosts: newC_uint16_t(p.received_hosts),
can_resolve_dns: newC_bool(p.can_resolve_dns),
downloaded_file: newString(p.downloaded_file),
download_duration: newC_uint64_t(p.download_duration),
download_err: newString(p.download_err),
}
}
func cntNetstackResponse(s *NetstackResponse, cnt *uint) [0]C.NetstackResponseRef {
return [0]C.NetstackResponseRef{}
}
func refNetstackResponse(p *NetstackResponse, buffer *[]byte) C.NetstackResponseRef {
return C.NetstackResponseRef{
can_handshake: refC_bool(&p.can_handshake, buffer),
sent_ips: refC_uint16_t(&p.sent_ips, buffer),
received_ips: refC_uint16_t(&p.received_ips, buffer),
sent_hosts: refC_uint16_t(&p.sent_hosts, buffer),
received_hosts: refC_uint16_t(&p.received_hosts, buffer),
can_resolve_dns: refC_bool(&p.can_resolve_dns, buffer),
can_handshake: refC_bool(&p.can_handshake, buffer),
sent_ips: refC_uint16_t(&p.sent_ips, buffer),
received_ips: refC_uint16_t(&p.received_ips, buffer),
sent_hosts: refC_uint16_t(&p.sent_hosts, buffer),
received_hosts: refC_uint16_t(&p.received_hosts, buffer),
can_resolve_dns: refC_bool(&p.can_resolve_dns, buffer),
downloaded_file: refString(&p.downloaded_file, buffer),
download_duration: refC_uint64_t(&p.download_duration, buffer),
download_err: refString(&p.download_err, buffer),
}
}
func main() {}
74 changes: 73 additions & 1 deletion nym-vpn-core/crates/nym-gateway-probe/netstack_ping/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ package main

import (
"bytes"
"context"
"fmt"
"io"
"log"
"net"
"net/http"
"net/netip"
"strings"
"time"

"math/rand"

"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"golang.zx2c4.com/wireguard/conn"
Expand All @@ -21,6 +27,17 @@ func init() {
NetstackCallImpl = Netstack{}
}

var fileUrls = []string{
"https://hil-speed.hetzner.com/100MB.bin",
"https://nbg1-speed.hetzner.com/100MB.bin",
"https://fsn1-speed.hetzner.com/100MB.bin",
"https://ash-speed.hetzner.com/100MB.bin",
"https://hel1-speed.hetzner.com/100MB.bin",
"https://proof.ovh.net/files/100Mb.dat",
"http://cachefly.cachefly.net/100mb.test",
"https://sin-speed.hetzner.com/100MB.bin",
}

func (Netstack) ping(req NetstackRequest) NetstackResponse {
tun, tnet, err := netstack.CreateNetTUN(
[]netip.Addr{netip.MustParseAddr(req.wg_ip)},
Expand All @@ -42,7 +59,7 @@ func (Netstack) ping(req NetstackRequest) NetstackResponse {
ipc.WriteString(req.endpoint)
ipc.WriteString("\nallowed_ip=0.0.0.0/0\n")

response := NetstackResponse{false, 0, 0, 0, 0, false}
response := NetstackResponse{false, 0, 0, 0, 0, false, "", 0, ""}

dev.IpcSet(ipc.String())
err = dev.Up()
Expand Down Expand Up @@ -81,6 +98,26 @@ func (Netstack) ping(req NetstackRequest) NetstackResponse {
}
}

randomIndex := rand.Intn(len(fileUrls))
fileURL := fileUrls[randomIndex]

// Download the file
fileContent, downloadDuration, err := downloadFile(fileURL, req.download_timeout_sec, tnet)
if err != nil {
log.Printf("Failed to download file: %v\n", err)
} else {
log.Printf("Downloaded file content length: %.2f MB\n", float64(len(fileContent))/1024/1024)
log.Printf("Download duration: %v\n", downloadDuration)
}

response.download_duration = uint64(downloadDuration.Seconds())
response.downloaded_file = fileURL
if err != nil {
response.download_err = err.Error()
} else {
response.download_err = ""
}

return response
}

Expand Down Expand Up @@ -134,3 +171,38 @@ func sendPing(address string, seq uint8, send_timeout_secs uint64, recieve_timou
}
}
}

func downloadFile(url string, timeoutSecs uint64, tnet *netstack.Net) ([]byte, time.Duration, error) {
transport := &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return tnet.Dial(network, addr)
},
}

client := &http.Client{
Transport: transport,
Timeout: time.Second * time.Duration(timeoutSecs),
}

start := time.Now() // Start timing

resp, err := client.Get(url)
if err != nil {
return nil, 0, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, 0, fmt.Errorf("failed to download file: %s", resp.Status)
}

var buf bytes.Buffer
_, err = io.Copy(&buf, resp.Body)
if err != nil {
return nil, 0, err
}

duration := time.Since(start) // Calculate duration

return buf.Bytes(), duration, nil
}
3 changes: 3 additions & 0 deletions nym-vpn-core/crates/nym-gateway-probe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ async fn wg_probe(
netstack_response.received_hosts as f32 / netstack_response.sent_hosts as f32;
wg_outcome.ping_ips_performance =
netstack_response.received_ips as f32 / netstack_response.sent_ips as f32;
wg_outcome.download_duration = netstack_response.download_duration;
wg_outcome.downloaded_file = netstack_response.downloaded_file;
wg_outcome.download_err = netstack_response.download_err;
}
}

Expand Down
5 changes: 5 additions & 0 deletions nym-vpn-core/crates/nym-gateway-probe/src/netstack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub struct NetstackRequest {
pub num_ping: u8,
pub send_timeout_sec: u64,
pub recv_timeout_sec: u64,
pub download_timeout_sec: u64,
}

impl Default for NetstackRequest {
Expand All @@ -30,6 +31,7 @@ impl Default for NetstackRequest {
num_ping: 3,
send_timeout_sec: 1,
recv_timeout_sec: 2,
download_timeout_sec: 180,
}
}
}
Expand All @@ -42,6 +44,9 @@ pub struct NetstackResponse {
pub sent_hosts: u16,
pub received_hosts: u16,
pub can_resolve_dns: bool,
pub downloaded_file: String,
pub download_duration: u64,
pub download_err: String,
}

#[rust2go::r2g]
Expand Down
3 changes: 3 additions & 0 deletions nym-vpn-core/crates/nym-gateway-probe/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub struct WgProbeResults {
pub can_resolve_dns: bool,
pub ping_hosts_performance: f32,
pub ping_ips_performance: f32,
pub download_duration: u64,
pub downloaded_file: String,
pub download_err: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down
Loading