diff options
author | Audrius Butkevicius <github@audrius.rocks> | 2021-05-11 06:59:56 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-11 07:59:56 +0200 |
commit | aca1b45e938eb59e350f082a0a34aadeb01bd65b (patch) | |
tree | 799ef6b1c842e998cb0566f50b544f55a44ce848 | |
parent | 5cb2a1013875f495d5d37080ac8c815b301e4c3a (diff) |
lib/connections: Update pfilter to pick up bugfix/oob stuff, support OOB connections (fixes #7636) (#7654)
-rw-r--r-- | go.mod | 2 | ||||
-rw-r--r-- | go.sum | 15 | ||||
-rw-r--r-- | lib/connections/quic_listen.go | 57 | ||||
-rw-r--r-- | lib/stun/stun.go | 42 |
4 files changed, 85 insertions, 31 deletions
@@ -1,7 +1,7 @@ module github.com/syncthing/syncthing require ( - github.com/AudriusButkevicius/pfilter v0.0.0-20210218141631-7468b85d810a + github.com/AudriusButkevicius/pfilter v0.0.0-20210510194644-fad42c10c5ac github.com/AudriusButkevicius/recli v0.0.5 github.com/alecthomas/kong v0.2.16 github.com/bkaradzic/go-lz4 v0.0.0-20160924222819-7224d8d8f27e @@ -7,8 +7,8 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -github.com/AudriusButkevicius/pfilter v0.0.0-20210218141631-7468b85d810a h1:dUzIAgRmHLIUU0PT+OJ0X1WI5j1hlLbf+G420gUjIQg= -github.com/AudriusButkevicius/pfilter v0.0.0-20210218141631-7468b85d810a/go.mod h1:1N0EEx/irz4B1qV17wW82TFbjQrE7oX316Cki6eDY0Q= +github.com/AudriusButkevicius/pfilter v0.0.0-20210510194644-fad42c10c5ac h1:ua8XsAiW9JrUa97ioh+ZaZu2JeSMrhcQ2peBxSMrqSs= +github.com/AudriusButkevicius/pfilter v0.0.0-20210510194644-fad42c10c5ac/go.mod h1:EEEtt5r8y0gGHlRFF2+cLx0WUy/rKHnjALmom5E0+74= github.com/AudriusButkevicius/recli v0.0.5 h1:xUa55PvWTHBm17T6RvjElRO3y5tALpdceH86vhzQ5wg= github.com/AudriusButkevicius/recli v0.0.5/go.mod h1:Q2E26yc6RvWWEz/TJ/goUp6yXvipYdJI096hpoaqsNs= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= @@ -129,6 +129,7 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -141,6 +142,8 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -187,6 +190,7 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -261,6 +265,8 @@ github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl5 github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= github.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07VxbSabxn5A5gZQ= github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= +github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9Mvs5yNoZZ28A= +github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= github.com/maruel/panicparse v1.6.1 h1:803MjBzGcUgE1vYgg3UMNq3G1oyYeKkMu3t6hBS97x0= github.com/maruel/panicparse v1.6.1/go.mod h1:uoxI4w9gJL6XahaYPMq/z9uadrdr1SyHuQwV2q80Mm0= github.com/maruel/panicparse/v2 v2.1.1/go.mod h1:AeTWdCE4lcq8OKsLb6cHSj1RWHVSnV9HBCk7sKLF4Jg= @@ -451,8 +457,6 @@ github.com/syncthing/notify v0.0.0-20210308121556-f45149b04939/go.mod h1:J0q59IW github.com/syndtr/goleveldb v1.0.1-0.20200815071216-d9e9293bd0f7 h1:udtnv1cokhJYqnUfCMCppJ71bFN9VKfG1BQ6UsYZnx8= github.com/syndtr/goleveldb v1.0.1-0.20200815071216-d9e9293bd0f7/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/thejerf/suture/v4 v4.0.0 h1:GX3X+1Qaewtj9flL2wgoTBfLA5NcmrCY39TJRpPbUrI= -github.com/thejerf/suture/v4 v4.0.0/go.mod h1:g0e8vwskm9tI0jRjxrnA6lSr0q6OfPdWJVX7G5bVWRs= github.com/thejerf/suture/v4 v4.0.1 h1:CLnC1wxLAiHA5zTbbvhSWMupVuGe5ZJ7YddWE3lvb4M= github.com/thejerf/suture/v4 v4.0.1/go.mod h1:g0e8vwskm9tI0jRjxrnA6lSr0q6OfPdWJVX7G5bVWRs= github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek= @@ -585,6 +589,7 @@ golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -647,6 +652,7 @@ google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= @@ -658,6 +664,7 @@ google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/lib/connections/quic_listen.go b/lib/connections/quic_listen.go index 75bfb5bb01..00a34544ab 100644 --- a/lib/connections/quic_listen.go +++ b/lib/connections/quic_listen.go @@ -83,18 +83,33 @@ func (t *quicListener) OnExternalAddressChanged(address *stun.Host, via string) func (t *quicListener) serve(ctx context.Context) error { network := strings.ReplaceAll(t.uri.Scheme, "quic", "udp") - packetConn, err := net.ListenPacket(network, t.uri.Host) + udpAddr, err := net.ResolveUDPAddr(network, t.uri.Host) if err != nil { l.Infoln("Listen (BEP/quic):", err) return err } - defer packetConn.Close() - svc, conn := stun.New(t.cfg, t, packetConn) + udpConn, err := net.ListenUDP(network, udpAddr) + if err != nil { + l.Infoln("Listen (BEP/quic):", err) + return err + } + defer func() { _ = udpConn.Close() }() + + svc, conn := stun.New(t.cfg, t, udpConn) defer conn.Close() - wrapped := &stunConnQUICWrapper{ + + quicWrapper := quicWrapper{ PacketConn: conn, - underlying: packetConn.(*net.UDPConn), + underlying: udpConn, + } + var wrapped net.PacketConn = &quicWrapper + + if oobConn, ok := conn.(oobConn); ok { + l.Debugf("wrapping in oob conn") + wrapped = &oobConnWrapper{ + quicWrapper, oobConn, + } } go svc.Serve(ctx) @@ -112,11 +127,11 @@ func (t *quicListener) serve(ctx context.Context) error { t.notifyAddressesChanged(t) defer t.clearAddresses(t) - l.Infof("QUIC listener (%v) starting", packetConn.LocalAddr()) - defer l.Infof("QUIC listener (%v) shutting down", packetConn.LocalAddr()) + l.Infof("QUIC listener (%v) starting", udpConn.LocalAddr()) + defer l.Infof("QUIC listener (%v) shutting down", udpConn.LocalAddr()) t.mut.Lock() - t.laddr = packetConn.LocalAddr() + t.laddr = udpConn.LocalAddr() t.mut.Unlock() defer func() { t.mut.Lock() @@ -233,18 +248,32 @@ func (quicListenerFactory) Enabled(cfg config.Configuration) bool { return true } -// stunConnQUICWrapper provides methods used by quic. -type stunConnQUICWrapper struct { +// quicWrapper provides methods used by quic +// https://github.com/lucas-clemente/quic-go/blob/master/packet_handler_map.go#L85 +type quicWrapper struct { net.PacketConn underlying *net.UDPConn } -func (s *stunConnQUICWrapper) SetReadBuffer(size int) error { - // https://github.com/lucas-clemente/quic-go/blob/master/packet_handler_map.go#L85 +// SetReadBuffer is required by QUIC +func (s *quicWrapper) SetReadBuffer(size int) error { return s.underlying.SetReadBuffer(size) } -func (s *stunConnQUICWrapper) SyscallConn() (syscall.RawConn, error) { - // https://github.com/lucas-clemente/quic-go/blob/84e03e59760ceee37359688871bb0688fcc4e98f/conn_windows.go#L18 +// SyscallConn is required by QUIC +func (s *quicWrapper) SyscallConn() (syscall.RawConn, error) { return s.underlying.SyscallConn() } + +// oobConn is used to assert that stun package returned a net.PacketConn that implements this interface. +// If it does, we then wrap quicWrapper in oobConnWrapper, to expose those methods to QUIC package. +type oobConn interface { + ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) + WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) +} + +// See: https://pkg.go.dev/github.com/lucas-clemente/quic-go#OOBCapablePacketConn +type oobConnWrapper struct { + quicWrapper + oobConn +} diff --git a/lib/stun/stun.go b/lib/stun/stun.go index 2a24f2f859..ee7b88783e 100644 --- a/lib/stun/stun.go +++ b/lib/stun/stun.go @@ -38,17 +38,35 @@ const ( NATSymmetricUDPFirewall = stun.NATSymmetricUDPFirewall ) -type writeTrackingPacketConn struct { +type writeTrackingUdpConn struct { lastWrite int64 // atomic, must remain 64-bit aligned - net.PacketConn + // Needs to be UDPConn not PacketConn, as pfilter checks for WriteMsgUDP/ReadMsgUDP + // and even if we embed UDPConn here, in place of a PacketConn, seems the interface + // check fails. + *net.UDPConn } -func (c *writeTrackingPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { +func (c *writeTrackingUdpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { atomic.StoreInt64(&c.lastWrite, time.Now().Unix()) - return c.PacketConn.WriteTo(p, addr) + return c.UDPConn.WriteTo(p, addr) } -func (c *writeTrackingPacketConn) getLastWrite() time.Time { +func (c *writeTrackingUdpConn) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) { + atomic.StoreInt64(&c.lastWrite, time.Now().Unix()) + return c.UDPConn.WriteMsgUDP(b, oob, addr) +} + +func (c *writeTrackingUdpConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) { + atomic.StoreInt64(&c.lastWrite, time.Now().Unix()) + return c.UDPConn.WriteToUDP(b, addr) +} + +func (c *writeTrackingUdpConn) Write(b []byte) (int, error) { + atomic.StoreInt64(&c.lastWrite, time.Now().Unix()) + return c.UDPConn.Write(b) +} + +func (c *writeTrackingUdpConn) getLastWrite() time.Time { unix := atomic.LoadInt64(&c.lastWrite) return time.Unix(unix, 0) } @@ -65,18 +83,18 @@ type Service struct { stunConn net.PacketConn client *stun.Client - writeTrackingPacketConn *writeTrackingPacketConn + writeTrackingUdpConn *writeTrackingUdpConn natType NATType addr *Host } -func New(cfg config.Wrapper, subscriber Subscriber, conn net.PacketConn) (*Service, net.PacketConn) { +func New(cfg config.Wrapper, subscriber Subscriber, conn *net.UDPConn) (*Service, net.PacketConn) { // Wrap the original connection to track writes on it - writeTrackingPacketConn := &writeTrackingPacketConn{lastWrite: 0, PacketConn: conn} + writeTrackingUdpConn := &writeTrackingUdpConn{lastWrite: 0, UDPConn: conn} // Wrap it in a filter and split it up, so that stun packets arrive on stun conn, others arrive on the data conn - filterConn := pfilter.NewPacketFilter(writeTrackingPacketConn) + filterConn := pfilter.NewPacketFilter(writeTrackingUdpConn) otherDataConn := filterConn.NewConn(otherDataPriority, nil) stunConn := filterConn.NewConn(stunFilterPriority, &stunFilter{ ids: make(map[string]time.Time), @@ -97,7 +115,7 @@ func New(cfg config.Wrapper, subscriber Subscriber, conn net.PacketConn) (*Servi stunConn: stunConn, client: client, - writeTrackingPacketConn: writeTrackingPacketConn, + writeTrackingUdpConn: writeTrackingUdpConn, natType: NATUnknown, addr: nil, @@ -241,7 +259,7 @@ func (s *Service) stunKeepAlive(ctx context.Context, addr string, extAddr *Host) } // Adjust the keepalives to fire only nextSleep after last write. - lastWrite := s.writeTrackingPacketConn.getLastWrite() + lastWrite := s.writeTrackingUdpConn.getLastWrite() minSleep := time.Duration(s.cfg.Options().StunKeepaliveMinS) * time.Second if nextSleep < minSleep { nextSleep = minSleep @@ -270,7 +288,7 @@ func (s *Service) stunKeepAlive(ctx context.Context, addr string, extAddr *Host) } // Check if any writes happened while we were sleeping, if they did, sleep again - lastWrite = s.writeTrackingPacketConn.getLastWrite() + lastWrite = s.writeTrackingUdpConn.getLastWrite() if gap := time.Since(lastWrite); gap < nextSleep { l.Debugf("%s stun last write gap less than next sleep: %s < %s. Will try later", s, gap, nextSleep) goto tryLater |