From 304a10233001c2dfe401a152932e4dc6f3c88eff Mon Sep 17 00:00:00 2001 From: Periyasamy Palanisamy Date: Thu, 9 Nov 2023 12:28:09 +0530 Subject: [PATCH] Set user timeout for tcp connection This commit sets TCP_USER_TIMEOUT socket option for tcp connection so that channel write doesn't block indefinitely on network disconnect. Signed-off-by: Periyasamy Palanisamy --- client/client.go | 8 ++++++++ client/options.go | 1 + internal/syscall_linux.go | 31 +++++++++++++++++++++++++++++++ internal/syscall_nonlinux.go | 14 ++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 internal/syscall_linux.go create mode 100644 internal/syscall_nonlinux.go diff --git a/client/client.go b/client/client.go index 53f368fc..c0e71b4e 100644 --- a/client/client.go +++ b/client/client.go @@ -21,6 +21,7 @@ import ( "github.com/go-logr/logr" "github.com/go-logr/stdr" "github.com/ovn-org/libovsdb/cache" + syscall "github.com/ovn-org/libovsdb/internal" "github.com/ovn-org/libovsdb/mapper" "github.com/ovn-org/libovsdb/model" "github.com/ovn-org/libovsdb/ovsdb" @@ -429,6 +430,13 @@ func (o *ovsdbClient) createRPC2Client(conn net.Conn) { o.trafficSeen = make(chan struct{}) } o.conn = conn + // set TCP_USER_TIMEOUT socket option for connection so that + // channel write doesn't block indefinitely on network disconnect. + if o.options.timeout > 0 { + syscall.SetTCPUserTimeout(conn, o.options.timeout*3) + } else { + syscall.SetTCPUserTimeout(conn, defaultTimeOut) + } o.rpcClient = rpc2.NewClientWithCodec(jsonrpc.NewJSONCodec(conn)) o.rpcClient.SetBlocking(true) o.rpcClient.Handle("echo", func(_ *rpc2.Client, args []interface{}, reply *[]interface{}) error { diff --git a/client/options.go b/client/options.go index 6f9b7b17..4f17ecea 100644 --- a/client/options.go +++ b/client/options.go @@ -15,6 +15,7 @@ const ( defaultTCPEndpoint = "tcp:127.0.0.1:6640" defaultSSLEndpoint = "ssl:127.0.0.1:6640" defaultUnixEndpoint = "unix:/var/run/openvswitch/ovsdb.sock" + defaultTimeOut = 60 * time.Second ) type options struct { diff --git a/internal/syscall_linux.go b/internal/syscall_linux.go new file mode 100644 index 00000000..5138f22d --- /dev/null +++ b/internal/syscall_linux.go @@ -0,0 +1,31 @@ +package internal + +import ( + "fmt" + "net" + "syscall" + "time" + + "golang.org/x/sys/unix" +) + +// SetTCPUserTimeout sets the TCP user timeout on a connection's socket +func SetTCPUserTimeout(conn net.Conn, timeout time.Duration) error { + tcpconn, ok := conn.(*net.TCPConn) + if !ok { + // not a TCP connection. exit early + return nil + } + rawConn, err := tcpconn.SyscallConn() + if err != nil { + return fmt.Errorf("error getting raw connection: %v", err) + } + err = rawConn.Control(func(fd uintptr) { + err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_USER_TIMEOUT, int(timeout/time.Millisecond)) + }) + if err != nil { + return fmt.Errorf("error setting option on socket: %v", err) + } + + return nil +} diff --git a/internal/syscall_nonlinux.go b/internal/syscall_nonlinux.go new file mode 100644 index 00000000..6e6a26d6 --- /dev/null +++ b/internal/syscall_nonlinux.go @@ -0,0 +1,14 @@ +//go:build !linux +// +build !linux + +package internal + +import ( + "net" + "time" +) + +// SetTCPUserTimeout is a no-op function under non-linux environments. +func SetTCPUserTimeout(conn net.Conn, timeout time.Duration) error { + return nil +}