summaryrefslogtreecommitdiffstats
path: root/extras
diff options
context:
space:
mode:
authorMatus Fabian <matfabia@cisco.com>2024-12-10 16:15:06 +0100
committerFlorin Coras <florin.coras@gmail.com>2025-01-09 10:59:16 +0000
commitc19cca931af3baf847d66bce9e5dd660861fc92e (patch)
treea36f0ba9f31c379984a3de4dfb54baa8808f5bbe /extras
parente89c97a3ee1538f3693fcd298831952d5df19ec2 (diff)
hsa: proxying UDP in HTTP/1.1
Type: feature Change-Id: Ic0ff9b9bfbad9fbc602fbcec0d8906cd21d63a2c Signed-off-by: Matus Fabian <matfabia@cisco.com>
Diffstat (limited to 'extras')
-rw-r--r--extras/hs-test/go.mod13
-rw-r--r--extras/hs-test/go.sum26
-rw-r--r--extras/hs-test/infra/connect_udp_client.go147
-rw-r--r--extras/hs-test/infra/suite_vpp_udp_proxy.go14
-rw-r--r--extras/hs-test/infra/utils.go1
-rw-r--r--extras/hs-test/proxy_test.go262
6 files changed, 446 insertions, 17 deletions
diff --git a/extras/hs-test/go.mod b/extras/hs-test/go.mod
index 01cb9000bdc..4cc24d510cc 100644
--- a/extras/hs-test/go.mod
+++ b/extras/hs-test/go.mod
@@ -9,6 +9,7 @@ require (
github.com/edwarnicke/exechelper v1.0.3
github.com/onsi/ginkgo/v2 v2.17.2
github.com/onsi/gomega v1.33.1
+ github.com/quic-go/quic-go v0.48.2
github.com/sirupsen/logrus v1.9.3
go.fd.io/govpp v0.10.0
gopkg.in/yaml.v3 v3.0.1
@@ -39,6 +40,7 @@ require (
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pkg/errors v0.9.1 // indirect
+ github.com/quic-go/qpack v0.5.1 // indirect
github.com/sasha-s/go-deadlock v0.3.1 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace // indirect
@@ -50,11 +52,14 @@ require (
go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
+ go.uber.org/mock v0.4.0 // indirect
+ golang.org/x/crypto v0.26.0 // indirect
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
- golang.org/x/net v0.26.0 // indirect
- golang.org/x/sync v0.7.0 // indirect
- golang.org/x/sys v0.21.0 // indirect
- golang.org/x/text v0.16.0 // indirect
+ golang.org/x/mod v0.18.0 // indirect
+ golang.org/x/net v0.28.0 // indirect
+ golang.org/x/sync v0.8.0 // indirect
+ golang.org/x/sys v0.23.0 // indirect
+ golang.org/x/text v0.17.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.22.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
diff --git a/extras/hs-test/go.sum b/extras/hs-test/go.sum
index fb555ad7abf..19a120b3007 100644
--- a/extras/hs-test/go.sum
+++ b/extras/hs-test/go.sum
@@ -127,6 +127,10 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
+github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
+github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
+github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
+github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -193,6 +197,8 @@ go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
+go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
+go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
@@ -200,32 +206,36 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/W
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
+golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
+golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
-golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
+golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
+golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
-golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
+golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
-golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
+golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
-golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
+golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/extras/hs-test/infra/connect_udp_client.go b/extras/hs-test/infra/connect_udp_client.go
new file mode 100644
index 00000000000..595f7e7f2e6
--- /dev/null
+++ b/extras/hs-test/infra/connect_udp_client.go
@@ -0,0 +1,147 @@
+package hst
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "net/url"
+ "time"
+
+ "github.com/quic-go/quic-go/http3"
+ "github.com/quic-go/quic-go/quicvarint"
+)
+
+type CapsuleParseError struct {
+ Err error
+}
+
+func (e *CapsuleParseError) Error() string {
+ return e.Err.Error()
+}
+
+type ConnectUdpClient struct {
+ log bool
+ suite *HstSuite
+ timeout time.Duration
+ Conn net.Conn
+}
+
+func (s *HstSuite) NewConnectUdpClient(timeout time.Duration, log bool) *ConnectUdpClient {
+ client := &ConnectUdpClient{log: log, suite: s, timeout: timeout}
+ return client
+}
+
+func writeConnectUdpReq(target string) []byte {
+ var b bytes.Buffer
+
+ fmt.Fprintf(&b, "GET %s HTTP/1.1\r\n", target)
+ u, _ := url.Parse(target)
+ fmt.Fprintf(&b, "Host: %s\r\n", u.Host)
+ fmt.Fprintf(&b, "User-Agent: hs-test\r\n")
+ fmt.Fprintf(&b, "Connection: Upgrade\r\n")
+ fmt.Fprintf(&b, "Upgrade: connect-udp\r\n")
+ fmt.Fprintf(&b, "Capsule-Protocol: ?1\r\n")
+ io.WriteString(&b, "\r\n")
+
+ return b.Bytes()
+}
+
+func (c *ConnectUdpClient) Dial(proxyAddress, targetUri string) error {
+ req := writeConnectUdpReq(targetUri)
+ conn, err := net.DialTimeout("tcp", proxyAddress, c.timeout)
+ if err != nil {
+ return err
+ }
+
+ if c.log {
+ c.suite.Log("* Connected to proxy")
+ }
+
+ conn.SetDeadline(time.Now().Add(time.Second * c.timeout))
+ _, err = conn.Write(req)
+ if err != nil {
+ return err
+ }
+
+ r := bufio.NewReader(conn)
+ resp, err := http.ReadResponse(r, nil)
+ if err != nil {
+ return err
+ }
+
+ if c.log {
+ c.suite.Log(DumpHttpResp(resp, true))
+ }
+
+ if resp.StatusCode != http.StatusSwitchingProtocols {
+ return errors.New("request failed: " + resp.Status)
+ }
+ if resp.Header.Get("Connection") != "upgrade" || resp.Header.Get("Upgrade") != "connect-udp" || resp.Header.Get("Capsule-Protocol") != "?1" {
+ return errors.New("invalid response")
+ }
+
+ if c.log {
+ c.suite.Log("* CONNECT-UDP tunnel established")
+ }
+ c.Conn = conn
+ return nil
+}
+
+func (c *ConnectUdpClient) Close() error {
+ return c.Conn.Close()
+}
+
+func (c *ConnectUdpClient) WriteCapsule(capsuleType http3.CapsuleType, payload []byte) error {
+ err := c.Conn.SetWriteDeadline(time.Now().Add(c.timeout))
+ if err != nil {
+ return err
+ }
+ var buf bytes.Buffer
+ err = http3.WriteCapsule(&buf, capsuleType, payload)
+ if err != nil {
+ return err
+ }
+ _, err = c.Conn.Write(buf.Bytes())
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (c *ConnectUdpClient) WriteDgramCapsule(payload []byte) error {
+ b := make([]byte, 0)
+ b = quicvarint.Append(b, 0)
+ b = append(b, payload...)
+ return c.WriteCapsule(0, b)
+}
+
+func (c *ConnectUdpClient) ReadDgramCapsule() ([]byte, error) {
+ err := c.Conn.SetReadDeadline(time.Now().Add(c.timeout))
+ if err != nil {
+ return nil, err
+ }
+ r := bufio.NewReader(c.Conn)
+ capsuleType, payloadReader, err := http3.ParseCapsule(r)
+ if err != nil {
+ return nil, err
+ }
+ if capsuleType != 0 {
+ return nil, &CapsuleParseError{errors.New("capsule type should be 0")}
+ }
+ b := make([]byte, 1024)
+ n, err := payloadReader.Read(b)
+ if err != nil {
+ return nil, err
+ }
+ if n < 3 {
+ return nil, &CapsuleParseError{errors.New("response payload too short")}
+ }
+ if b[0] != 0 {
+ return nil, &CapsuleParseError{errors.New("context id should be 0")}
+ }
+ return b[1:n], nil
+}
diff --git a/extras/hs-test/infra/suite_vpp_udp_proxy.go b/extras/hs-test/infra/suite_vpp_udp_proxy.go
index 2290aeec6a2..62bf3ddd466 100644
--- a/extras/hs-test/infra/suite_vpp_udp_proxy.go
+++ b/extras/hs-test/infra/suite_vpp_udp_proxy.go
@@ -5,6 +5,7 @@ import (
"net"
"reflect"
"runtime"
+ "strconv"
"strings"
"time"
@@ -15,6 +16,7 @@ type VppUdpProxySuite struct {
HstSuite
proxyPort int
serverPort int
+ MaxTimeout time.Duration
Interfaces struct {
Client *NetInterface
Server *NetInterface
@@ -42,13 +44,21 @@ func (s *VppUdpProxySuite) SetupSuite() {
s.Interfaces.Client = s.GetInterfaceByName("hstcln")
s.Interfaces.Server = s.GetInterfaceByName("hstsrv")
s.Containers.VppProxy = s.GetContainerByName("vpp")
+
+ if *IsVppDebug {
+ s.MaxTimeout = time.Second * 600
+ } else {
+ s.MaxTimeout = time.Second * 2
+ }
}
func (s *VppUdpProxySuite) SetupTest() {
s.HstSuite.SetupTest()
// VPP proxy
- vpp, err := s.Containers.VppProxy.newVppInstance(s.Containers.VppProxy.AllocatedCpus)
+ var memoryConfig Stanza
+ memoryConfig.NewStanza("memory").Append("main-heap-size 2G")
+ vpp, err := s.Containers.VppProxy.newVppInstance(s.Containers.VppProxy.AllocatedCpus, memoryConfig)
s.AssertNotNil(vpp, fmt.Sprint(err))
s.AssertNil(vpp.Start())
@@ -119,7 +129,7 @@ func (s *VppUdpProxySuite) StartEchoServer() *net.UDPConn {
}
}
}()
- s.Log("started")
+ s.Log("* started udp echo server " + s.ServerAddr() + ":" + strconv.Itoa(s.ServerPort()))
return conn
}
diff --git a/extras/hs-test/infra/utils.go b/extras/hs-test/infra/utils.go
index b690efc32ca..bd603f863fc 100644
--- a/extras/hs-test/infra/utils.go
+++ b/extras/hs-test/infra/utils.go
@@ -17,6 +17,7 @@ import (
const networkTopologyDir string = "topo-network/"
const containerTopologyDir string = "topo-containers/"
+const HttpCapsuleTypeDatagram = uint64(0)
type Stanza struct {
content string
diff --git a/extras/hs-test/proxy_test.go b/extras/hs-test/proxy_test.go
index d371de46cbb..ec90d24ba36 100644
--- a/extras/hs-test/proxy_test.go
+++ b/extras/hs-test/proxy_test.go
@@ -11,6 +11,7 @@ import (
"net/http"
"os"
"strconv"
+ "strings"
"sync"
"sync/atomic"
"time"
@@ -23,9 +24,10 @@ func init() {
RegisterVppProxyTests(VppProxyHttpGetTcpTest, VppProxyHttpGetTlsTest, VppProxyHttpPutTcpTest, VppProxyHttpPutTlsTest,
VppConnectProxyGetTest, VppConnectProxyPutTest)
RegisterVppProxySoloTests(VppProxyHttpGetTcpMTTest, VppProxyHttpPutTcpMTTest, VppProxyTcpIperfMTTest,
- VppProxyUdpIperfMTTest, VppConnectProxyStressTest, VppConnectProxyStressMTTest)
- RegisterVppUdpProxyTests(VppProxyUdpTest)
- RegisterVppUdpProxySoloTests(VppProxyUdpMigrationMTTest)
+ VppProxyUdpIperfMTTest, VppConnectProxyStressTest, VppConnectProxyStressMTTest, VppConnectProxyConnectionFailedMTTest)
+ RegisterVppUdpProxyTests(VppProxyUdpTest, VppConnectUdpProxyTest, VppConnectUdpInvalidCapsuleTest,
+ VppConnectUdpUnknownCapsuleTest, VppConnectUdpClientCloseTest)
+ RegisterVppUdpProxySoloTests(VppProxyUdpMigrationMTTest, VppConnectUdpStressMTTest, VppConnectUdpStressTest)
RegisterEnvoyProxyTests(EnvoyProxyHttpGetTcpTest, EnvoyProxyHttpPutTcpTest)
RegisterNginxProxyTests(NginxMirroringTest)
RegisterNginxProxySoloTests(MirrorMultiThreadTest)
@@ -185,6 +187,17 @@ func VppConnectProxyGetTest(s *VppProxySuite) {
s.CurlDownloadResourceViaTunnel(targetUri, proxyUri)
}
+func VppConnectProxyConnectionFailedMTTest(s *VppProxySuite) {
+ var proxyPort uint16 = 8080
+ s.SetupNginxServer()
+ configureVppProxy(s, "http", proxyPort)
+
+ targetUri := fmt.Sprintf("http://%s:%d/httpTestFile", s.ServerAddr(), s.ServerPort()+1)
+ proxyUri := fmt.Sprintf("http://%s:%d", s.VppProxyAddr(), proxyPort)
+ _, log := s.CurlRequestViaTunnel(targetUri, proxyUri)
+ s.AssertContains(log, "HTTP/1.1 502 Bad Gateway")
+}
+
func VppConnectProxyPutTest(s *VppProxySuite) {
var proxyPort uint16 = 8080
s.SetupNginxServer()
@@ -373,3 +386,246 @@ func VppProxyUdpMigrationMTTest(s *VppUdpProxySuite) {
s.Log(s.Containers.VppProxy.VppInstance.Vppctl("show session verbose 2"))
}
+
+func VppConnectUdpProxyTest(s *VppUdpProxySuite) {
+ remoteServerConn := s.StartEchoServer()
+ defer remoteServerConn.Close()
+
+ vppProxy := s.Containers.VppProxy.VppInstance
+ cmd := fmt.Sprintf("test proxy server fifo-size 512k server-uri http://%s/%d", s.VppProxyAddr(), s.ProxyPort())
+ s.Log(vppProxy.Vppctl(cmd))
+
+ proxyAddress := fmt.Sprintf("%s:%d", s.VppProxyAddr(), s.ProxyPort())
+ targetUri := fmt.Sprintf("http://%s:%d/.well-known/masque/udp/%s/%d/", s.VppProxyAddr(), s.ProxyPort(), s.ServerAddr(), s.ServerPort())
+ c := s.NewConnectUdpClient(s.MaxTimeout, true)
+ err := c.Dial(proxyAddress, targetUri)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer c.Close()
+
+ data := []byte("hello")
+
+ err = c.WriteDgramCapsule(data)
+ s.AssertNil(err, fmt.Sprint(err))
+ payload, err := c.ReadDgramCapsule()
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertEqual(data, payload)
+}
+
+func VppConnectUdpInvalidCapsuleTest(s *VppUdpProxySuite) {
+ remoteServerConn := s.StartEchoServer()
+ defer remoteServerConn.Close()
+
+ vppProxy := s.Containers.VppProxy.VppInstance
+ cmd := fmt.Sprintf("test proxy server fifo-size 512k server-uri http://%s/%d", s.VppProxyAddr(), s.ProxyPort())
+ s.Log(vppProxy.Vppctl(cmd))
+
+ proxyAddress := fmt.Sprintf("%s:%d", s.VppProxyAddr(), s.ProxyPort())
+ targetUri := fmt.Sprintf("http://%s:%d/.well-known/masque/udp/%s/%d/", s.VppProxyAddr(), s.ProxyPort(), s.ServerAddr(), s.ServerPort())
+ c := s.NewConnectUdpClient(s.MaxTimeout, true)
+ err := c.Dial(proxyAddress, targetUri)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer c.Close()
+
+ // Capsule length is set to 494878333 which exceed maximum allowed UDP payload length 65527 and connection must be aborted
+ capsule := []byte{
+ 0x00, // type
+ 0x9D, 0x7F, 0x3E, 0x7D, // length
+ 0x00, // context ID
+ 0x4B, 0x6E, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6F, 0x66, 0x20, 0x4E, 0x69, // some extra junk
+ }
+ n, err := c.Conn.Write(capsule)
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertEqual(n, len(capsule))
+ b := make([]byte, 1)
+ _, err = c.Conn.Read(b)
+ s.AssertMatchError(err, io.EOF, "connection not closed by proxy")
+}
+
+func VppConnectUdpUnknownCapsuleTest(s *VppUdpProxySuite) {
+ remoteServerConn := s.StartEchoServer()
+ defer remoteServerConn.Close()
+
+ vppProxy := s.Containers.VppProxy.VppInstance
+ cmd := fmt.Sprintf("test proxy server fifo-size 512k server-uri http://%s/%d", s.VppProxyAddr(), s.ProxyPort())
+ s.Log(vppProxy.Vppctl(cmd))
+
+ proxyAddress := fmt.Sprintf("%s:%d", s.VppProxyAddr(), s.ProxyPort())
+ targetUri := fmt.Sprintf("http://%s:%d/.well-known/masque/udp/%s/%d/", s.VppProxyAddr(), s.ProxyPort(), s.ServerAddr(), s.ServerPort())
+ c := s.NewConnectUdpClient(s.MaxTimeout, true)
+ err := c.Dial(proxyAddress, targetUri)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer c.Close()
+
+ // Send capsule with unknown type 0x40 which is outside range for standards (0x00 - 0x3f)
+ // Endpoint that receives capsule with unknown type must silently drop that capsule and skip over to parse the next capsule
+ err = c.WriteCapsule(0x4040, []byte("None shall pass"))
+ s.AssertNil(err, fmt.Sprint(err))
+
+ // Send valid capsule to verify that previous was dropped
+ data := []byte("hello")
+ err = c.WriteDgramCapsule(data)
+ s.AssertNil(err, fmt.Sprint(err))
+ payload, err := c.ReadDgramCapsule()
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertEqual(data, payload)
+}
+
+func VppConnectUdpClientCloseTest(s *VppUdpProxySuite) {
+ remoteServerConn := s.StartEchoServer()
+ defer remoteServerConn.Close()
+
+ vppProxy := s.Containers.VppProxy.VppInstance
+ cmd := fmt.Sprintf("test proxy server fifo-size 512k server-uri http://%s/%d", s.VppProxyAddr(), s.ProxyPort())
+ s.Log(vppProxy.Vppctl(cmd))
+
+ proxyAddress := fmt.Sprintf("%s:%d", s.VppProxyAddr(), s.ProxyPort())
+ targetUri := fmt.Sprintf("http://%s:%d/.well-known/masque/udp/%s/%d/", s.VppProxyAddr(), s.ProxyPort(), s.ServerAddr(), s.ServerPort())
+ c := s.NewConnectUdpClient(s.MaxTimeout, true)
+ err := c.Dial(proxyAddress, targetUri)
+ s.AssertNil(err, fmt.Sprint(err))
+
+ err = c.Close()
+ s.AssertNil(err, fmt.Sprint(err))
+ proxyClientConn := fmt.Sprintf("[T] %s:%d->%s", s.VppProxyAddr(), s.ProxyPort(), s.ClientAddr())
+ proxyTargetConn := fmt.Sprintf("[U] %s:", s.Interfaces.Server.Peer.Ip4AddressString())
+ for nTries := 0; nTries < 10; nTries++ {
+ o := vppProxy.Vppctl("show session verbose 2")
+ if !strings.Contains(o, proxyClientConn) {
+ break
+ }
+ time.Sleep(1 * time.Second)
+ }
+ sessions := vppProxy.Vppctl("show session verbose 2")
+ s.Log(sessions)
+ s.AssertNotContains(sessions, proxyClientConn, "client-proxy session not closed")
+ s.AssertNotContains(sessions, proxyTargetConn, "proxy-server session not closed")
+}
+
+func vppConnectUdpStressLoad(s *VppUdpProxySuite) {
+ var (
+ connectError, timeout, readError, writeError, invalidData, total atomic.Uint32
+ wg sync.WaitGroup
+ )
+
+ proxyAddress := fmt.Sprintf("%s:%d", s.VppProxyAddr(), s.ProxyPort())
+ targetUri := fmt.Sprintf("http://%s/.well-known/masque/udp/%s/%d/", proxyAddress, s.ServerAddr(), s.ServerPort())
+
+ // warm-up
+ warmUp := s.NewConnectUdpClient(s.MaxTimeout, false)
+ err := warmUp.Dial(proxyAddress, targetUri)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer warmUp.Close()
+ data := []byte("hello")
+ err = warmUp.WriteDgramCapsule(data)
+ s.AssertNil(err, fmt.Sprint(err))
+ payload, err := warmUp.ReadDgramCapsule()
+ s.AssertNil(err, fmt.Sprint(err))
+ s.AssertEqual(data, payload)
+ warmUp.Close()
+
+ stop := make(chan struct{})
+
+ s.Log("Running 30s test @ " + targetUri)
+ for i := 0; i < 1000; i++ {
+ wg.Add(1)
+ go func() {
+ var tot, timed, re, we uint32
+ defer wg.Done()
+ defer func() {
+ total.Add(tot)
+ timeout.Add(timed)
+ readError.Add(re)
+ writeError.Add(we)
+ }()
+ restart:
+ c := s.NewConnectUdpClient(s.MaxTimeout, false)
+ e := c.Dial(proxyAddress, targetUri)
+ if e != nil {
+ connectError.Add(1)
+ return
+ }
+ defer c.Close()
+
+ req := make([]byte, 64)
+ rand.Read(req)
+
+ for {
+ select {
+ default:
+ tot += 1
+ e = c.WriteDgramCapsule(req)
+ if e != nil {
+ if errors.Is(e, os.ErrDeadlineExceeded) {
+ timed += 1
+ } else {
+ we += 1
+ }
+ continue
+ }
+ resp, e := c.ReadDgramCapsule()
+ if e != nil {
+ if errors.Is(e, os.ErrDeadlineExceeded) {
+ timed += 1
+ } else if errors.Is(e, err.(*CapsuleParseError)) {
+ invalidData.Add(1)
+ } else {
+ re += 1
+ }
+ c.Close()
+ goto restart
+ }
+ if bytes.Compare(req, resp) != 0 {
+ invalidData.Add(1)
+ c.Close()
+ goto restart
+ }
+ case <-stop:
+ return
+ }
+ }
+ }()
+ }
+ for i := 0; i < 30; i++ {
+ GinkgoWriter.Print(".")
+ time.Sleep(time.Second)
+ }
+ GinkgoWriter.Print("\n")
+ close(stop) // tell clients to stop
+ wg.Wait() // wait until clients finish
+ successRatio := (float64(total.Load()-(timeout.Load()+readError.Load()+writeError.Load()+invalidData.Load())) / float64(total.Load())) * 100.0
+ summary := fmt.Sprintf("1000 connections %d requests in 30s", total.Load())
+ report := fmt.Sprintf("Requests/sec: %d\n", total.Load()/30)
+ report += fmt.Sprintf("Errors: timeout %d, read %d, write %d, invalid data received %d, connection %d\n", timeout.Load(), readError.Load(), writeError.Load(), invalidData.Load(), connectError.Load())
+ report += fmt.Sprintf("Successes ratio: %.2f%%\n", successRatio)
+ AddReportEntry(summary, report)
+ s.AssertGreaterThan(successRatio, 90.0)
+}
+
+func VppConnectUdpStressTest(s *VppUdpProxySuite) {
+ remoteServerConn := s.StartEchoServer()
+ defer remoteServerConn.Close()
+
+ vppProxy := s.Containers.VppProxy.VppInstance
+ cmd := fmt.Sprintf("test proxy server fifo-size 512k server-uri http://%s/%d", s.VppProxyAddr(), s.ProxyPort())
+ s.Log(vppProxy.Vppctl(cmd))
+
+ // no goVPP less noise
+ vppProxy.Disconnect()
+
+ vppConnectUdpStressLoad(s)
+}
+
+func VppConnectUdpStressMTTest(s *VppUdpProxySuite) {
+ remoteServerConn := s.StartEchoServer()
+ defer remoteServerConn.Close()
+
+ vppProxy := s.Containers.VppProxy.VppInstance
+ vppProxy.Disconnect()
+ cmd := fmt.Sprintf("test proxy server fifo-size 512k server-uri http://%s/%d", s.VppProxyAddr(), s.ProxyPort())
+ s.Log(vppProxy.Vppctl(cmd))
+
+ // no goVPP less noise
+ vppProxy.Disconnect()
+
+ vppConnectUdpStressLoad(s)
+}