From 46b564db930332efac6aa022db94906987a2b0a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Papp?= Date: Thu, 25 Apr 2024 15:03:55 +0200 Subject: [PATCH] Add naive speed test --- client/internal/relay/speed.go | 143 +++++++++++++++++++++++++++++ client/internal/relay/turn_test.go | 121 ++++++++++++++++++++++-- 2 files changed, 254 insertions(+), 10 deletions(-) create mode 100644 client/internal/relay/speed.go diff --git a/client/internal/relay/speed.go b/client/internal/relay/speed.go new file mode 100644 index 000000000..24d1979a9 --- /dev/null +++ b/client/internal/relay/speed.go @@ -0,0 +1,143 @@ +package relay + +import ( + "fmt" + "io" + "net" + "os" + "time" + + log "github.com/sirupsen/logrus" +) + +const ( + bufferSize = 800 + testFile = "/tmp/1MB" +) + +type Speed struct { +} + +func NewSpeed() *Speed { + return &Speed{} +} + +func (s *Speed) ReceiveFileFromAddr(remoteAddr net.Addr) error { + pc, err := net.ListenPacket("udp4", "0.0.0.0:0") + if err != nil { + log.Errorf("failed to lisen: %s", err.Error()) + return err + } + defer pc.Close() + + log.Debugf("--- sending initial message to: %s", remoteAddr.String()) + _, err = pc.WriteTo([]byte("hey, I am the receiver"), remoteAddr) + if err != nil { + log.Errorf("failed to send initial msg: %s", err.Error()) + return err + } + + return s.receiveFile(pc) +} + +func (s *Speed) ReceiveFileFromPC(pc net.PacketConn) error { + return s.receiveFile(pc) +} + +func (s *Speed) receiveFile(pc net.PacketConn) error { + log.Debugf("--- start to receive file...") + file, err := os.OpenFile(fmt.Sprintf("%s.cp", testFile), os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Errorf("failed to open file: %s", err.Error()) + return err + } + _ = file.Truncate(0) + defer file.Close() + + buffer := make([]byte, bufferSize) + for { + n, addr, err := pc.ReadFrom(buffer) + if err != nil { + log.Errorf("failed to read from connection: %s", err.Error()) + return err + } + + n, err = file.Write(buffer[:n]) + if err != nil { + log.Errorf("failed to write to file: %s", err.Error()) + return err + } + + _, err = pc.WriteTo([]byte("ack"), addr) + if err != nil { + log.Errorf("failed to send ack: %s", err.Error()) + } + + log.Debugf("received %d bytes from %s", n, addr) + } +} + +func (s *Speed) SendFileToPC(relayConn net.PacketConn) error { + buf := make([]byte, bufferSize) + log.Debugf("--- wait for initial message") + n, rAddr, err := relayConn.ReadFrom(buf) + if err != nil { + log.Errorf("failed to read from connection: %s", err.Error()) + return err + } + log.Errorf("received initial msg %d bytes (%s), addr %s", n, string(buf[:n]), rAddr.String()) + return s.sendFile(relayConn, rAddr) +} + +func (s *Speed) SendFileToAddr(addr net.Addr) error { + pc, err := net.ListenPacket("udp4", "0.0.0.0:0") + if err != nil { + log.Errorf("failed to lisen: %s", err.Error()) + return err + } + defer pc.Close() + + return s.sendFile(pc, addr) +} + +func (s *Speed) sendFile(conn net.PacketConn, rAddr net.Addr) error { + log.Debugf("--- start to send file...") + file, err := os.Open(testFile) + if err != nil { + // Handle error + return nil + } + defer file.Close() + + buf := make([]byte, bufferSize) + start := time.Now() + sent := 0 + + for { + n, err := file.Read(buf) + if err != nil && err != io.EOF { + return err + } + if n == 0 { + break + } + + n, err = conn.WriteTo(buf[:n], rAddr) + if err != nil { + log.Errorf("failed to write to connection: %s", err.Error()) + return err + } + sent += n + log.Debugf("sent %d bytes, (%d) to %s", n, sent, rAddr.String()) + + // wait for ack + _, _, err = conn.ReadFrom(make([]byte, bufferSize)) + if err != nil { + log.Errorf("failed to read from connection: %s", err.Error()) + return err + } + } + elapsed := time.Since(start) + log.Infof("sent %d bytes, troughtput: %f MB/s", sent, float64(sent)/1024/1024/elapsed.Seconds()) + return nil +} diff --git a/client/internal/relay/turn_test.go b/client/internal/relay/turn_test.go index 7752b8edd..3b6f3e9fa 100644 --- a/client/internal/relay/turn_test.go +++ b/client/internal/relay/turn_test.go @@ -2,35 +2,136 @@ package relay import ( "os" + "sync" "testing" "github.com/pion/stun/v2" + log "github.com/sirupsen/logrus" "github.com/netbirdio/netbird/util" ) +const ( + userName = "1714092678" + password = "8PEprGKo+UARpYpQOulNz3H24dI=" +) + func TestMain(m *testing.M) { _ = util.InitLog("trace", "console") code := m.Run() os.Exit(code) } -func TestNewPermanentTurn(t *testing.T) { - turnURI, err := stun.ParseURI("turns:turn.netbird.io:443?transport=tcp") +func TestMyTurnUpload(t *testing.T) { + turnURI, err := stun.ParseURI("turn:api.stage.netbird.io:3478?transport=udp") if err != nil { - t.Errorf("failed to parse stun url: %v", err) + t.Fatalf("failed to parse stun url: %v", err) } - turnURI.Username = "1713006060" - turnURI.Password = "pO5Pfx15luZ92mW+FHPa6/LtJ7Y=" + turnURI.Username = userName + turnURI.Password = password - stunURI, err := stun.ParseURI("stun:stun.netbird.io:5555") + stunURI, err := stun.ParseURI("stun:api.stage.netbird.io:3478") if err != nil { - t.Errorf("failed to parse stun url: %v", err) + t.Fatalf("failed to parse stun url: %v", err) } - turnRelay := NewPermanentTurn(stunURI, turnURI) - err = turnRelay.Open() + turnRelayA := NewPermanentTurn(stunURI, turnURI) + err = turnRelayA.Open() if err != nil { - t.Errorf("failed to open turn relay: %v", err) + t.Fatalf("failed to open turn relay: %v", err) + } + defer turnRelayA.Close() + + turnRelayB := NewPermanentTurn(stunURI, turnURI) + peerBAddr, err := turnRelayB.discoverPublicIPByStun() + if err != nil { + t.Fatalf("failed to discover public ip: %v", err) } + err = turnRelayA.PunchHole(peerBAddr) + if err != nil { + t.Fatalf("failed to punch hole: %v", err) + } + + // at this point, the relayed side should be established + + wg := sync.WaitGroup{} + wg.Add(2) + + speedB := NewSpeed() + go func() { + err := speedB.ReceiveFileFromAddr(turnRelayA.relayConn.LocalAddr()) + if err != nil { + log.Errorf("failed to receive file: %v", err) + } + wg.Done() + }() + + speedA := NewSpeed() + go func() { + err := speedA.SendFileToPC(turnRelayA.relayConn) + if err != nil { + log.Errorf("failed to send file: %v", err) + } + log.Debugf("file sent") + wg.Done() + }() + + wg.Wait() +} + +func TestMyTurnDownload(t *testing.T) { + turnURI, err := stun.ParseURI("turn:api.stage.netbird.io:3478?transport=udp") + if err != nil { + t.Fatalf("failed to parse stun url: %v", err) + } + turnURI.Username = "1714016034" + turnURI.Password = "oDpL6tDu0d+xcO3rQnHoEvbcS/Q=" + + stunURI, err := stun.ParseURI("stun:api.stage.netbird.io:3478") + if err != nil { + t.Fatalf("failed to parse stun url: %v", err) + } + turnRelayA := NewPermanentTurn(stunURI, turnURI) + err = turnRelayA.Open() + if err != nil { + t.Fatalf("failed to open turn relay: %v", err) + } + defer turnRelayA.Close() + + turnRelayB := NewPermanentTurn(stunURI, turnURI) + peerBAddr, err := turnRelayB.discoverPublicIPByStun() + if err != nil { + t.Fatalf("failed to discover public ip: %v", err) + } + + err = turnRelayA.PunchHole(peerBAddr) + if err != nil { + t.Fatalf("failed to punch hole: %v", err) + } + + // at this point, the relayed side should be established + + wg := sync.WaitGroup{} + wg.Add(2) + + speedB := NewSpeed() + go func() { + err := speedB.SendFileToAddr(turnRelayA.relayConn.LocalAddr()) + if err != nil { + log.Errorf("failed to receive file: %v", err) + } + wg.Done() + }() + + speedA := NewSpeed() + go func() { + err := speedA.ReceiveFileFromPC(turnRelayA.relayConn) + if err != nil { + log.Errorf("failed to send file: %v", err) + } + log.Debugf("file sent") + wg.Done() + }() + + wg.Wait() }