Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: support get ipvs info #14

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ const (
ipvsCmdAttrTimeoutUDP
)

// Attributes used to describe an info
const (
ipvsCmdAttrInfoUnspec int = iota
ipvsCmdAttrInfoVersion
ipvsCmdAttrInfoConnTableSize
)

// Attributes used to describe a service. Used inside nested attribute
// ipvsCmdAttrService
const (
Expand Down
59 changes: 59 additions & 0 deletions ipvs.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,24 @@ type Config struct {
TimeoutUDP time.Duration
}

// Info defines IPVS info
type Info struct {
Version *Version
ConnTableSize uint32
}

// Version defines IPVS version
type Version struct {
Major uint
Minor uint
Patch uint
}

// String returns a string of IPVS version
func (v *Version) String() string {
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
}

// Handle provides a namespace specific ipvs handle to program ipvs
// rules.
type Handle struct {
Expand Down Expand Up @@ -204,3 +222,44 @@ func (i *Handle) GetConfig() (*Config, error) {
func (i *Handle) SetConfig(c *Config) error {
return i.doSetConfigCmd(c)
}

func toVersion(v uint) *Version {
return &Version{
Major: (v >> 16) & 0xff,
Minor: (v >> 8) & 0xff,
Patch: v & 0xff,
}
}

// GetInfo returns info details from IPVS
func (i *Handle) GetInfo() (*Info, error) {
res, err := i.doGetInfoCmd()
if err != nil {
return nil, err
}

return &Info{
Version: toVersion(uint(res.version)),
ConnTableSize: res.connTableSize,
}, nil
}

// GetVersion returns version from IPVS
func (i *Handle) GetVersion() (*Version, error) {
res, err := i.doGetInfoCmd()
if err != nil {
return nil, err
}

return toVersion(uint(res.version)), nil
}

// GetConnectionTableSize returns connection table size from IPVS
func (i *Handle) GetConnectionTableSize() (uint32, error) {
res, err := i.doGetInfoCmd()
if err != nil {
return 0, err
}

return res.connTableSize, nil
}
38 changes: 38 additions & 0 deletions ipvs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package ipvs

import (
"net"
"regexp"
"runtime"
"syscall"
"testing"
Expand Down Expand Up @@ -44,6 +45,8 @@ var (
"Tunnel",
"Route",
}

verRegexp = regexp.MustCompile(`^\d\.\d\.\d$`)
)

func lookupFwMethod(fwMethod uint32) string {
Expand Down Expand Up @@ -371,6 +374,41 @@ func TestTimeouts(t *testing.T) {
assert.DeepEqual(t, *c3, Config{77 * time.Second, 66 * time.Second, 77 * time.Second})
}

func TestInfo(t *testing.T) {
defer setupTestOSContext(t)

i, err := New("")
assert.NilError(t, err)

info, err := i.GetInfo()
assert.NilError(t, err)
assert.Check(t, info.Version != nil)
assert.Assert(t, info.Version.String() != "")
assert.Assert(t, info.ConnTableSize > 0)
}

func TestVersion(t *testing.T) {
defer setupTestOSContext(t)

i, err := New("")
assert.NilError(t, err)

ver, err := i.GetVersion()
assert.NilError(t, err)
assert.Assert(t, verRegexp.MatchString(ver.String()))
}

func TestConnTableSize(t *testing.T) {
defer setupTestOSContext(t)

i, err := New("")
assert.NilError(t, err)

size, err := i.GetConnectionTableSize()
assert.NilError(t, err)
assert.Assert(t, size > 0)
}

// setupTestOSContext joins a new network namespace, and returns its associated
// teardown function.
//
Expand Down
43 changes: 43 additions & 0 deletions netlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ type ipvsFlags struct {
mask uint32
}

type ipvsInfo struct {
version uint32
connTableSize uint32
}

func deserializeGenlMsg(b []byte) (hdr *genlMsgHdr) {
return (*genlMsgHdr)(unsafe.Pointer(&b[0:unsafe.Sizeof(*hdr)][0]))
}
Expand Down Expand Up @@ -573,6 +578,44 @@ func (i *Handle) doSetConfigCmd(c *Config) error {
return err
}

// parseInfo given a ipvs netlink response this function will respond with a valid info entry, an error otherwise
func (i *Handle) parseInfo(msg []byte) (*ipvsInfo, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this needs to be bound to *Handle?

var info ipvsInfo

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 ipvsCmdAttrInfoVersion:
info.version = native.Uint32(attr.Value)
case ipvsCmdAttrInfoConnTableSize:
info.connTableSize = native.Uint32(attr.Value)
}
}

return &info, nil
}

// doGetInfoCmd a wrapper function to be used by GetInfo
func (i *Handle) doGetInfoCmd() (*ipvsInfo, error) {
msg, err := i.doCmdWithoutAttr(ipvsCmdGetInfo)
if err != nil {
return nil, err
}

res, err := i.parseInfo(msg[0])
if err != nil {
return nil, err
}

return res, nil
}

// IPVS related netlink message format explained

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