diff --git a/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/gen.go b/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/gen.go index 91bda5cc0f..7d1284a7e0 100644 --- a/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/gen.go +++ b/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/gen.go @@ -8,24 +8,15 @@ package main #include #include -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; @@ -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) { @@ -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 { @@ -248,36 +254,43 @@ 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 { @@ -285,12 +298,15 @@ func cntNetstackResponse(s *NetstackResponse, cnt *uint) [0]C.NetstackResponseRe } 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() {} diff --git a/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/impl.go b/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/impl.go index 61df21e72c..3fc6abcadf 100644 --- a/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/impl.go +++ b/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/impl.go @@ -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" @@ -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)}, @@ -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() @@ -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 } @@ -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 +} diff --git a/nym-vpn-core/crates/nym-gateway-probe/src/lib.rs b/nym-vpn-core/crates/nym-gateway-probe/src/lib.rs index 9864db2f00..315a368116 100644 --- a/nym-vpn-core/crates/nym-gateway-probe/src/lib.rs +++ b/nym-vpn-core/crates/nym-gateway-probe/src/lib.rs @@ -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; } } diff --git a/nym-vpn-core/crates/nym-gateway-probe/src/netstack.rs b/nym-vpn-core/crates/nym-gateway-probe/src/netstack.rs index 6a701ca658..32c0d2195e 100644 --- a/nym-vpn-core/crates/nym-gateway-probe/src/netstack.rs +++ b/nym-vpn-core/crates/nym-gateway-probe/src/netstack.rs @@ -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 { @@ -30,6 +31,7 @@ impl Default for NetstackRequest { num_ping: 3, send_timeout_sec: 1, recv_timeout_sec: 2, + download_timeout_sec: 180, } } } @@ -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] diff --git a/nym-vpn-core/crates/nym-gateway-probe/src/types.rs b/nym-vpn-core/crates/nym-gateway-probe/src/types.rs index 5b277d7c78..1820b1638e 100644 --- a/nym-vpn-core/crates/nym-gateway-probe/src/types.rs +++ b/nym-vpn-core/crates/nym-gateway-probe/src/types.rs @@ -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)]