@@ -4,13 +4,15 @@ package nl
44import (
55 "bytes"
66 "encoding/binary"
7+ "errors"
78 "fmt"
89 "net"
910 "os"
1011 "runtime"
1112 "sync"
1213 "sync/atomic"
1314 "syscall"
15+ "time"
1416 "unsafe"
1517
1618 "github.com/vishvananda/netns"
@@ -656,9 +658,11 @@ func NewNetlinkRequest(proto, flags int) *NetlinkRequest {
656658}
657659
658660type NetlinkSocket struct {
659- fd int32
660- file * os.File
661- lsa unix.SockaddrNetlink
661+ fd int32
662+ file * os.File
663+ lsa unix.SockaddrNetlink
664+ sendTimeout int64 // Access using atomic.Load/StoreInt64
665+ receiveTimeout int64 // Access using atomic.Load/StoreInt64
662666 sync.Mutex
663667}
664668
@@ -802,8 +806,44 @@ func (s *NetlinkSocket) GetFd() int {
802806 return int (s .fd )
803807}
804808
809+ func (s * NetlinkSocket ) GetTimeouts () (send , receive time.Duration ) {
810+ return time .Duration (atomic .LoadInt64 (& s .sendTimeout )),
811+ time .Duration (atomic .LoadInt64 (& s .receiveTimeout ))
812+ }
813+
805814func (s * NetlinkSocket ) Send (request * NetlinkRequest ) error {
806- return unix .Sendto (int (s .fd ), request .Serialize (), 0 , & s .lsa )
815+ rawConn , err := s .file .SyscallConn ()
816+ if err != nil {
817+ return err
818+ }
819+ var (
820+ deadline time.Time
821+ innerErr error
822+ )
823+ sendTimeout := atomic .LoadInt64 (& s .sendTimeout )
824+ if sendTimeout != 0 {
825+ deadline = time .Now ().Add (time .Duration (sendTimeout ))
826+ }
827+ if err := s .file .SetWriteDeadline (deadline ); err != nil {
828+ return err
829+ }
830+ serializedReq := request .Serialize ()
831+ err = rawConn .Write (func (fd uintptr ) (done bool ) {
832+ innerErr = unix .Sendto (int (s .fd ), serializedReq , 0 , & s .lsa )
833+ return innerErr != unix .EWOULDBLOCK
834+ })
835+ if innerErr != nil {
836+ return innerErr
837+ }
838+ if err != nil {
839+ // The timeout was previously implemented using SO_SNDTIMEO on a blocking
840+ // socket. So, continue to return EAGAIN when the timeout is reached.
841+ if errors .Is (err , os .ErrDeadlineExceeded ) {
842+ return unix .EAGAIN
843+ }
844+ return err
845+ }
846+ return nil
807847}
808848
809849func (s * NetlinkSocket ) Receive () ([]syscall.NetlinkMessage , * unix.SockaddrNetlink , error ) {
@@ -812,20 +852,33 @@ func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, *unix.SockaddrNetli
812852 return nil , nil , err
813853 }
814854 var (
855+ deadline time.Time
815856 fromAddr * unix.SockaddrNetlink
816857 rb [RECEIVE_BUFFER_SIZE ]byte
817858 nr int
818859 from unix.Sockaddr
819860 innerErr error
820861 )
862+ receiveTimeout := atomic .LoadInt64 (& s .receiveTimeout )
863+ if receiveTimeout != 0 {
864+ deadline = time .Now ().Add (time .Duration (receiveTimeout ))
865+ }
866+ if err := s .file .SetReadDeadline (deadline ); err != nil {
867+ return nil , nil , err
868+ }
821869 err = rawConn .Read (func (fd uintptr ) (done bool ) {
822870 nr , from , innerErr = unix .Recvfrom (int (fd ), rb [:], 0 )
823871 return innerErr != unix .EWOULDBLOCK
824872 })
825873 if innerErr != nil {
826- err = innerErr
874+ return nil , nil , innerErr
827875 }
828876 if err != nil {
877+ // The timeout was previously implemented using SO_RCVTIMEO on a blocking
878+ // socket. So, continue to return EAGAIN when the timeout is reached.
879+ if errors .Is (err , os .ErrDeadlineExceeded ) {
880+ return nil , nil , unix .EAGAIN
881+ }
829882 return nil , nil , err
830883 }
831884 fromAddr , ok := from .(* unix.SockaddrNetlink )
@@ -847,16 +900,14 @@ func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, *unix.SockaddrNetli
847900
848901// SetSendTimeout allows to set a send timeout on the socket
849902func (s * NetlinkSocket ) SetSendTimeout (timeout * unix.Timeval ) error {
850- // Set a send timeout of SOCKET_SEND_TIMEOUT, this will allow the Send to periodically unblock and avoid that a routine
851- // remains stuck on a send on a closed fd
852- return unix .SetsockoptTimeval (int (s .fd ), unix .SOL_SOCKET , unix .SO_SNDTIMEO , timeout )
903+ atomic .StoreInt64 (& s .sendTimeout , timeout .Nano ())
904+ return nil
853905}
854906
855907// SetReceiveTimeout allows to set a receive timeout on the socket
856908func (s * NetlinkSocket ) SetReceiveTimeout (timeout * unix.Timeval ) error {
857- // Set a read timeout of SOCKET_READ_TIMEOUT, this will allow the Read to periodically unblock and avoid that a routine
858- // remains stuck on a recvmsg on a closed fd
859- return unix .SetsockoptTimeval (int (s .fd ), unix .SOL_SOCKET , unix .SO_RCVTIMEO , timeout )
909+ atomic .StoreInt64 (& s .receiveTimeout , timeout .Nano ())
910+ return nil
860911}
861912
862913// SetReceiveBufferSize allows to set a receive buffer size on the socket
0 commit comments