Skip to content

Commit e4ef8fe

Browse files
author
Flavio Crisciani
authored
Merge pull request #2349 from DataDog/lbernail/ipvs-timeouts
IPVS: Add support for GetConfig/SetConfig
2 parents 5f7a3f6 + 2aa8b21 commit e4ef8fe

3 files changed

Lines changed: 103 additions & 0 deletions

File tree

ipvs/ipvs.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ type Destination struct {
6868
// DstStats defines IPVS destination (real server) statistics
6969
type DstStats SvcStats
7070

71+
// Config defines IPVS timeout configuration
72+
type Config struct {
73+
TimeoutTCP time.Duration
74+
TimeoutTCPFin time.Duration
75+
TimeoutUDP time.Duration
76+
}
77+
7178
// Handle provides a namespace specific ipvs handle to program ipvs
7279
// rules.
7380
type Handle struct {
@@ -188,3 +195,13 @@ func (i *Handle) GetService(s *Service) (*Service, error) {
188195

189196
return res[0], nil
190197
}
198+
199+
// GetConfig returns the current timeout configuration
200+
func (i *Handle) GetConfig() (*Config, error) {
201+
return i.doGetConfigCmd()
202+
}
203+
204+
// SetConfig set the current timeout configuration. 0: no change
205+
func (i *Handle) SetConfig(c *Config) error {
206+
return i.doSetConfigCmd(c)
207+
}

ipvs/ipvs_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net"
77
"syscall"
88
"testing"
9+
"time"
910

1011
"github.com/docker/libnetwork/testutils"
1112
"github.com/vishvananda/netlink"
@@ -342,3 +343,33 @@ func TestDestination(t *testing.T) {
342343
}
343344
}
344345
}
346+
347+
func TestTimeouts(t *testing.T) {
348+
if testutils.RunningOnCircleCI() {
349+
t.Skip("Skipping as not supported on CIRCLE CI kernel")
350+
}
351+
defer testutils.SetupTestOSContext(t)()
352+
353+
i, err := New("")
354+
assert.NilError(t, err)
355+
356+
_, err = i.GetConfig()
357+
assert.NilError(t, err)
358+
359+
cfg := Config{66 * time.Second, 66 * time.Second, 66 * time.Second}
360+
err = i.SetConfig(&cfg)
361+
assert.NilError(t, err)
362+
363+
c2, err := i.GetConfig()
364+
assert.NilError(t, err)
365+
assert.DeepEqual(t, cfg, *c2)
366+
367+
// A timeout value 0 means that the current timeout value of the corresponding entry is preserved
368+
cfg = Config{77 * time.Second, 0 * time.Second, 77 * time.Second}
369+
err = i.SetConfig(&cfg)
370+
assert.NilError(t, err)
371+
372+
c3, err := i.GetConfig()
373+
assert.NilError(t, err)
374+
assert.DeepEqual(t, *c3, Config{77 * time.Second, 66 * time.Second, 77 * time.Second})
375+
}

ipvs/netlink.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"sync"
1313
"sync/atomic"
1414
"syscall"
15+
"time"
1516
"unsafe"
1617

1718
"github.com/sirupsen/logrus"
@@ -503,6 +504,60 @@ func (i *Handle) doGetDestinationsCmd(s *Service, d *Destination) ([]*Destinatio
503504
return res, nil
504505
}
505506

507+
// parseConfig given a ipvs netlink response this function will respond with a valid config entry, an error otherwise
508+
func (i *Handle) parseConfig(msg []byte) (*Config, error) {
509+
var c Config
510+
511+
//Remove General header for this message
512+
hdr := deserializeGenlMsg(msg)
513+
attrs, err := nl.ParseRouteAttr(msg[hdr.Len():])
514+
if err != nil {
515+
return nil, err
516+
}
517+
518+
for _, attr := range attrs {
519+
attrType := int(attr.Attr.Type)
520+
switch attrType {
521+
case ipvsCmdAttrTimeoutTCP:
522+
c.TimeoutTCP = time.Duration(native.Uint32(attr.Value)) * time.Second
523+
case ipvsCmdAttrTimeoutTCPFin:
524+
c.TimeoutTCPFin = time.Duration(native.Uint32(attr.Value)) * time.Second
525+
case ipvsCmdAttrTimeoutUDP:
526+
c.TimeoutUDP = time.Duration(native.Uint32(attr.Value)) * time.Second
527+
}
528+
}
529+
530+
return &c, nil
531+
}
532+
533+
// doGetConfigCmd a wrapper function to be used by GetConfig
534+
func (i *Handle) doGetConfigCmd() (*Config, error) {
535+
msg, err := i.doCmdWithoutAttr(ipvsCmdGetConfig)
536+
if err != nil {
537+
return nil, err
538+
}
539+
540+
res, err := i.parseConfig(msg[0])
541+
if err != nil {
542+
return res, err
543+
}
544+
return res, nil
545+
}
546+
547+
// doSetConfigCmd a wrapper function to be used by SetConfig
548+
func (i *Handle) doSetConfigCmd(c *Config) error {
549+
req := newIPVSRequest(ipvsCmdSetConfig)
550+
req.Seq = atomic.AddUint32(&i.seq, 1)
551+
552+
req.AddData(nl.NewRtAttr(ipvsCmdAttrTimeoutTCP, nl.Uint32Attr(uint32(c.TimeoutTCP.Seconds()))))
553+
req.AddData(nl.NewRtAttr(ipvsCmdAttrTimeoutTCPFin, nl.Uint32Attr(uint32(c.TimeoutTCPFin.Seconds()))))
554+
req.AddData(nl.NewRtAttr(ipvsCmdAttrTimeoutUDP, nl.Uint32Attr(uint32(c.TimeoutUDP.Seconds()))))
555+
556+
_, err := execute(i.sock, req, 0)
557+
558+
return err
559+
}
560+
506561
// IPVS related netlink message format explained
507562

508563
/* EACH NETLINK MSG is of the below format, this is what we will receive from execute() api.

0 commit comments

Comments
 (0)