From 36def4b34a7cc7d1ca7db29a340a26f8009b47d4 Mon Sep 17 00:00:00 2001 From: Petr Zhizhin Date: Sat, 9 Nov 2024 23:41:40 +0100 Subject: [PATCH] Fix PR comments --- x/configurl/disorder.go | 6 ++-- x/disorder/stream_dialer.go | 54 ++++++++---------------------- x/disorder/writer.go | 66 +++++++++++++++++++++---------------- x/examples/fetch/.gitignore | 1 - x/go.mod | 10 +++--- x/go.sum | 20 +++++------ 6 files changed, 70 insertions(+), 87 deletions(-) delete mode 100644 x/examples/fetch/.gitignore diff --git a/x/configurl/disorder.go b/x/configurl/disorder.go index da0695c5..5ad5b40a 100644 --- a/x/configurl/disorder.go +++ b/x/configurl/disorder.go @@ -29,11 +29,11 @@ func registerDisorderDialer(r TypeRegistry[transport.StreamDialer], typeID strin if err != nil { return nil, err } - prefixBytesStr := config.URL.Opaque - prefixBytes, err := strconv.Atoi(prefixBytesStr) + disorderPacketNStr := config.URL.Opaque + disorderPacketN, err := strconv.Atoi(disorderPacketNStr) if err != nil { return nil, fmt.Errorf("disoder: could not parse splice position: %v", err) } - return disorder.NewStreamDialer(sd, int64(prefixBytes)) + return disorder.NewStreamDialer(sd, disorderPacketN) }) } diff --git a/x/disorder/stream_dialer.go b/x/disorder/stream_dialer.go index 30989f49..eccdadf7 100644 --- a/x/disorder/stream_dialer.go +++ b/x/disorder/stream_dialer.go @@ -19,18 +19,14 @@ import ( "errors" "fmt" "net" - "net/netip" "github.com/Jigsaw-Code/outline-sdk/transport" - "golang.org/x/net/ipv4" - "golang.org/x/net/ipv6" + "github.com/Jigsaw-Code/outline-sdk/x/sockopt" ) -var defaultTTL = 64 - type disorderDialer struct { - dialer transport.StreamDialer - splitPoint int64 + dialer transport.StreamDialer + disorderPacketN int } var _ transport.StreamDialer = (*disorderDialer)(nil) @@ -43,11 +39,11 @@ var _ transport.StreamDialer = (*disorderDialer)(nil) // * The next part of data is sent normally // * Server notices the lost fragment and requests re-transmission // Currently this only works with Linux kernel (for Windows/Mac a different implementation is required) -func NewStreamDialer(dialer transport.StreamDialer, prefixBytes int64) (transport.StreamDialer, error) { +func NewStreamDialer(dialer transport.StreamDialer, disorderPacketN int) (transport.StreamDialer, error) { if dialer == nil { return nil, errors.New("argument dialer must not be nil") } - return &disorderDialer{dialer: dialer, splitPoint: prefixBytes}, nil + return &disorderDialer{dialer: dialer, disorderPacketN: disorderPacketN}, nil } // DialStream implements [transport.StreamDialer].DialStream. @@ -57,43 +53,21 @@ func (d *disorderDialer) DialStream(ctx context.Context, remoteAddr string) (tra return nil, err } - oldTTL, err := setHopLimit(innerConn, 1) - if err != nil { - return nil, fmt.Errorf("disorder strategy: failed to change ttl: %w", err) + tcpInnerConn, ok := innerConn.(*net.TCPConn) + if !ok { + return nil, fmt.Errorf("disorder strategy: expected base dialer to return TCPConn") } - - dw := NewWriter(innerConn, d.splitPoint, oldTTL) - - return transport.WrapConn(innerConn, innerConn, dw), nil -} - -// setHopLimit changes the socket TTL for IPv4 (or HopLimit for IPv6) and returns the old value -// socket must be `*net.TCPConn` -func setHopLimit(conn net.Conn, ttl int) (oldTTL int, err error) { - addr, err := netip.ParseAddrPort(conn.RemoteAddr().String()) + tcpOptions, err := sockopt.NewTCPOptions(tcpInnerConn) if err != nil { - return 0, fmt.Errorf("could not parse remote addr: %w", err) + return nil, err } - switch { - case addr.Addr().Is4(): - conn := ipv4.NewConn(conn) - oldTTL, _ = conn.TTL() - err = conn.SetTTL(ttl) - case addr.Addr().Is6(): - conn := ipv6.NewConn(conn) - oldTTL, _ = conn.HopLimit() - err = conn.SetHopLimit(ttl) - default: - return 0, fmt.Errorf("unknown remote addr type (%v)", addr.Addr().String()) - } + defaultHopLimit, err := tcpOptions.HopLimit() if err != nil { - return 0, fmt.Errorf("failed to change TTL: %w", err) + return nil, fmt.Errorf("disorder strategy: failed to get base connection HopLimit: %w", err) } - if oldTTL == 0 { - oldTTL = defaultTTL - } + dw := NewWriter(innerConn, tcpOptions, d.disorderPacketN, defaultHopLimit) - return + return transport.WrapConn(innerConn, innerConn, dw), nil } diff --git a/x/disorder/writer.go b/x/disorder/writer.go index d7a87cf8..8804ebc3 100644 --- a/x/disorder/writer.go +++ b/x/disorder/writer.go @@ -17,50 +17,60 @@ package disorder import ( "fmt" "io" - "net" - "sync" + + "github.com/Jigsaw-Code/outline-sdk/x/sockopt" ) type disorderWriter struct { - conn net.Conn - resetTTL sync.Once - prefixBytes int64 - oldTTL int + conn io.Writer + tcpOptions sockopt.TCPOptions + runAtPacketN int + defaultHopLimit int + writeCalls int } var _ io.Writer = (*disorderWriter)(nil) -// TODO -// var _ io.ReaderFrom = (*splitWriterReaderFrom)(nil) +// Setting number of hops to 1 will lead to data to get lost on host +var disorderHopN = 1 -// TODO -func NewWriter(conn net.Conn, prefixBytes int64, oldTTL int) io.Writer { - // TODO support ReaderFrom +func NewWriter(conn io.Writer, tcpOptions sockopt.TCPOptions, runAtPacketN int, defaultHopLimit int) io.Writer { return &disorderWriter{ - conn: conn, - prefixBytes: prefixBytes, - oldTTL: oldTTL, + conn: conn, + tcpOptions: tcpOptions, + runAtPacketN: runAtPacketN, + defaultHopLimit: defaultHopLimit, + writeCalls: 0, } } func (w *disorderWriter) Write(data []byte) (written int, err error) { - if 0 < w.prefixBytes && w.prefixBytes < int64(len(data)) { - written, err = w.conn.Write(data[:w.prefixBytes]) - w.prefixBytes -= int64(written) + shouldDoDisorder := w.writeCalls == w.runAtPacketN + if shouldDoDisorder { + err = w.tcpOptions.SetHopLimit(disorderHopN) if err != nil { - return written, err + return 0, fmt.Errorf("failed to set the hop limit to %d: %w", disorderHopN, err) } - data = data[written:] - } - w.resetTTL.Do(func() { - _, err = setHopLimit(w.conn, w.oldTTL) - }) - if err != nil { - return written, fmt.Errorf("setsockopt IPPROTO_IP/IP_TTL error: %w", err) + + // The packet will get lost at the first send, since the hop limit is too low } n, err := w.conn.Write(data) - written += n - w.prefixBytes -= int64(n) - return written, err + + // TODO: Wait for queued data to be sent by the kernel to the socket + + if shouldDoDisorder { + // The packet with low hop limit was sent + // Make next calls send data normally + // + // The packet with the low hop limit will get resent by the kernel later + // The network filters will receive data out of order + err = w.tcpOptions.SetHopLimit(w.defaultHopLimit) + if err != nil { + return n, fmt.Errorf("failed to set the hop limit error %d: %w", w.defaultHopLimit, err) + } + } + + w.writeCalls += 1 + return n, err } diff --git a/x/examples/fetch/.gitignore b/x/examples/fetch/.gitignore deleted file mode 100644 index bad6ace5..00000000 --- a/x/examples/fetch/.gitignore +++ /dev/null @@ -1 +0,0 @@ -fetch diff --git a/x/go.mod b/x/go.mod index 71db16e8..b432aec4 100644 --- a/x/go.mod +++ b/x/go.mod @@ -13,9 +13,9 @@ require ( github.com/stretchr/testify v1.9.0 github.com/vishvananda/netlink v1.1.0 golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b - golang.org/x/net v0.30.0 - golang.org/x/sys v0.26.0 - golang.org/x/term v0.25.0 + golang.org/x/net v0.28.0 + golang.org/x/sys v0.23.0 + golang.org/x/term v0.23.0 ) require ( @@ -72,11 +72,11 @@ require ( github.com/wader/filtertransport v0.0.0-20200316221534-bdd9e61eee78 // indirect gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/goptlib v1.5.0 // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/crypto v0.28.0 // indirect + golang.org/x/crypto v0.26.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/text v0.19.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/x/go.sum b/x/go.sum index 87b4075b..a7238e9a 100644 --- a/x/go.sum +++ b/x/go.sum @@ -205,8 +205,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +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-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b h1:WX7nnnLfCEXg+FmdYZPai2XuP3VqCP1HZVMST0n9DF0= @@ -224,8 +224,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -247,8 +247,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.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/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -256,16 +256,16 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= 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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +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=