diff --git a/ipvs/ipvs.go b/ipvs/ipvs.go index ed3c2870d6..d3ccb4135a 100644 --- a/ipvs/ipvs.go +++ b/ipvs/ipvs.go @@ -68,6 +68,13 @@ type Destination struct { // DstStats defines IPVS destination (real server) statistics type DstStats SvcStats +// Config defines IPVS timeout configuration +type Config struct { + TimeoutTCP time.Duration + TimeoutTCPFin time.Duration + TimeoutUDP time.Duration +} + // Handle provides a namespace specific ipvs handle to program ipvs // rules. type Handle struct { @@ -188,3 +195,13 @@ func (i *Handle) GetService(s *Service) (*Service, error) { return res[0], nil } + +// GetConfig returns the current timeout configuration +func (i *Handle) GetConfig() (*Config, error) { + return i.doGetConfigCmd() +} + +// SetConfig set the current timeout configuration. 0: no change +func (i *Handle) SetConfig(c *Config) error { + return i.doSetConfigCmd(c) +} diff --git a/ipvs/ipvs_test.go b/ipvs/ipvs_test.go index 4bcc67f2ff..7ba95ba361 100644 --- a/ipvs/ipvs_test.go +++ b/ipvs/ipvs_test.go @@ -6,6 +6,7 @@ import ( "net" "syscall" "testing" + "time" "github.com/docker/libnetwork/testutils" "github.com/vishvananda/netlink" @@ -342,3 +343,33 @@ func TestDestination(t *testing.T) { } } } + +func TestTimeouts(t *testing.T) { + if testutils.RunningOnCircleCI() { + t.Skip("Skipping as not supported on CIRCLE CI kernel") + } + defer testutils.SetupTestOSContext(t)() + + i, err := New("") + assert.NilError(t, err) + + _, err = i.GetConfig() + assert.NilError(t, err) + + cfg := Config{66 * time.Second, 66 * time.Second, 66 * time.Second} + err = i.SetConfig(&cfg) + assert.NilError(t, err) + + c2, err := i.GetConfig() + assert.NilError(t, err) + assert.DeepEqual(t, cfg, *c2) + + // A timeout value 0 means that the current timeout value of the corresponding entry is preserved + cfg = Config{77 * time.Second, 0 * time.Second, 77 * time.Second} + err = i.SetConfig(&cfg) + assert.NilError(t, err) + + c3, err := i.GetConfig() + assert.NilError(t, err) + assert.DeepEqual(t, *c3, Config{77 * time.Second, 66 * time.Second, 77 * time.Second}) +} diff --git a/ipvs/netlink.go b/ipvs/netlink.go index d158d11153..083909ae05 100644 --- a/ipvs/netlink.go +++ b/ipvs/netlink.go @@ -12,6 +12,7 @@ import ( "sync" "sync/atomic" "syscall" + "time" "unsafe" "github.com/sirupsen/logrus" @@ -503,6 +504,60 @@ func (i *Handle) doGetDestinationsCmd(s *Service, d *Destination) ([]*Destinatio return res, nil } +// parseConfig given a ipvs netlink response this function will respond with a valid config entry, an error otherwise +func (i *Handle) parseConfig(msg []byte) (*Config, error) { + var c Config + + //Remove General header for this message + hdr := deserializeGenlMsg(msg) + attrs, err := nl.ParseRouteAttr(msg[hdr.Len():]) + if err != nil { + return nil, err + } + + for _, attr := range attrs { + attrType := int(attr.Attr.Type) + switch attrType { + case ipvsCmdAttrTimeoutTCP: + c.TimeoutTCP = time.Duration(native.Uint32(attr.Value)) * time.Second + case ipvsCmdAttrTimeoutTCPFin: + c.TimeoutTCPFin = time.Duration(native.Uint32(attr.Value)) * time.Second + case ipvsCmdAttrTimeoutUDP: + c.TimeoutUDP = time.Duration(native.Uint32(attr.Value)) * time.Second + } + } + + return &c, nil +} + +// doGetConfigCmd a wrapper function to be used by GetConfig +func (i *Handle) doGetConfigCmd() (*Config, error) { + msg, err := i.doCmdWithoutAttr(ipvsCmdGetConfig) + if err != nil { + return nil, err + } + + res, err := i.parseConfig(msg[0]) + if err != nil { + return res, err + } + return res, nil +} + +// doSetConfigCmd a wrapper function to be used by SetConfig +func (i *Handle) doSetConfigCmd(c *Config) error { + req := newIPVSRequest(ipvsCmdSetConfig) + req.Seq = atomic.AddUint32(&i.seq, 1) + + req.AddData(nl.NewRtAttr(ipvsCmdAttrTimeoutTCP, nl.Uint32Attr(uint32(c.TimeoutTCP.Seconds())))) + req.AddData(nl.NewRtAttr(ipvsCmdAttrTimeoutTCPFin, nl.Uint32Attr(uint32(c.TimeoutTCPFin.Seconds())))) + req.AddData(nl.NewRtAttr(ipvsCmdAttrTimeoutUDP, nl.Uint32Attr(uint32(c.TimeoutUDP.Seconds())))) + + _, err := execute(i.sock, req, 0) + + return err +} + // IPVS related netlink message format explained /* EACH NETLINK MSG is of the below format, this is what we will receive from execute() api.