Skip to content

Commit

Permalink
Add support for disabling netns goroutine (#1)
Browse files Browse the repository at this point in the history
This change introduces a config parameter that allows to disable the goroutine used for running
netlink socket APIs in a different network namespace. When the goroutine is disabled, no network
namespace switching is performed, and all netlink socket APIs are run in the caller thread's netns.
  • Loading branch information
Michael Velbaum authored Apr 12, 2020
1 parent c558cf2 commit 6260ab5
Show file tree
Hide file tree
Showing 19 changed files with 81 additions and 37 deletions.
2 changes: 1 addition & 1 deletion attribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"errors"
"fmt"

"github.com/mdlayher/netlink/nlenc"
"github.com/twistlock/netlink/nlenc"
)

// errInvalidAttribute specifies if an Attribute's length is incorrect.
Expand Down
2 changes: 1 addition & 1 deletion attribute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"unsafe"

"github.com/google/go-cmp/cmp"
"github.com/mdlayher/netlink/nlenc"
"github.com/twistlock/netlink/nlenc"
)

func TestMarshalAttributes(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package netlink_test
import (
"testing"

"github.com/mdlayher/netlink"
"github.com/twistlock/netlink"
)

var attrBench = []struct {
Expand Down
8 changes: 8 additions & 0 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,14 @@ type Config struct {
// no multicast group subscriptions will be made.
Groups uint32

// DisableNSGoroutine disables the goroutine that allows switching to a different
// network namespace. This forces running netlink socket syscalls in the caller thread's netns.
//
// Users who manage namespace transitions manually, or do not require them at all should
// set this to true, otherwise overhead will be incurred due to context switches and channel communication
// for the executed system calls.
DisableNSGoroutine bool

// NetNS specifies the network namespace the Conn will operate in.
//
// If set (non-zero), Conn will enter the specified network namespace and
Expand Down
59 changes: 47 additions & 12 deletions conn_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,24 +328,59 @@ func newError(errno int) error {

var _ socket = &sysSocket{}

// netNSRunner runs a function in a network namespace
type netNSRunner interface {
// stop stops the runner
stop()
// run runs the given function in a network namespace of the runner
run(f func())
}

// A sysSocket is a socket which uses system calls for socket operations.
type sysSocket struct {
mu sync.RWMutex
fd *os.File
closed bool
g *lockedNetNSGoroutine
runner netNSRunner
}

// selfNetNSRunner executes functions in the current network namespace
type selfNetNSRunner struct{}

func (l *selfNetNSRunner) stop() {
// Empty impl to appease the netNSRunner interface
return
}

func (l *selfNetNSRunner) run(f func()) {
// Run the given function directly in the caller thread
f()
}

func newSelfNetNSExecutor() *selfNetNSRunner {
return &selfNetNSRunner{}
}

// newSysSocket creates a sysSocket that optionally locks its internal goroutine
// to a single thread.
func newSysSocket(config *Config) (*sysSocket, error) {
// Determine network namespaces using the threadNetNS function.
g, err := newLockedNetNSGoroutine(config.NetNS, threadNetNS, !config.DisableNSLockThread)
if err != nil {
return nil, err
var runner netNSRunner
var err error

if config.DisableNSGoroutine {
if config.NetNS != 0 {
return nil, errors.New("netlink Conn attempted to set a namespace with NS goroutine disabled")
}
runner = newSelfNetNSExecutor()
} else {
// Determine network namespaces using the threadNetNS function.
if runner, err = newLockedNetNSGoroutine(config.NetNS, threadNetNS, !config.DisableNSLockThread); err != nil {
return nil, err
}
}

return &sysSocket{
g: g,
runner: runner,
}, nil
}

Expand All @@ -360,7 +395,7 @@ func (s *sysSocket) do(f func()) error {
return syscall.EBADF
}

s.g.run(f)
s.runner.run(f)
return nil
}

Expand All @@ -374,7 +409,7 @@ func (s *sysSocket) read(f func(fd int) bool) error {
}

var err error
s.g.run(func() {
s.runner.run(func() {
err = fdread(s.fd, f)
})
return err
Expand All @@ -390,7 +425,7 @@ func (s *sysSocket) write(f func(fd int) bool) error {
}

var err error
s.g.run(func() {
s.runner.run(func() {
err = fdwrite(s.fd, f)
})
return err
Expand All @@ -406,7 +441,7 @@ func (s *sysSocket) control(f func(fd int)) error {
}

var err error
s.g.run(func() {
s.runner.run(func() {
err = fdcontrol(s.fd, f)
})
return err
Expand Down Expand Up @@ -508,7 +543,7 @@ func (s *sysSocket) Close() error {
s.closed = true

// Stop the associated goroutine and wait for it to return.
s.g.stop()
s.runner.stop()

return err
}
Expand Down Expand Up @@ -643,7 +678,7 @@ type lockedNetNSGoroutine struct {
// specified network namespace netNS (by file descriptor), and will use the
// getNS function to produce netNS handles.
func newLockedNetNSGoroutine(netNS int, getNS func() (*netNS, error), lockThread bool) (*lockedNetNSGoroutine, error) {
// Any bare syscall errors (e.g. setns) should be wrapped with
// Any bare syscall errors (e.runner. setns) should be wrapped with
// os.NewSyscallError for the remainder of this function.

// If the caller has instructed us to not lock OS thread but also attempts
Expand Down
6 changes: 3 additions & 3 deletions conn_linux_error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/mdlayher/netlink"
"github.com/mdlayher/netlink/nlenc"
"github.com/mdlayher/netlink/nltest"
"github.com/twistlock/netlink"
"github.com/twistlock/netlink/nlenc"
"github.com/twistlock/netlink/nltest"
"golang.org/x/sys/unix"
)

Expand Down
2 changes: 1 addition & 1 deletion conn_linux_gteq_1.12_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

"github.com/google/go-cmp/cmp"
"github.com/jsimonetti/rtnetlink/rtnl"
"github.com/mdlayher/netlink"
"github.com/twistlock/netlink"
"golang.org/x/sys/unix"
)

Expand Down
4 changes: 2 additions & 2 deletions conn_linux_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (
"time"

"github.com/google/go-cmp/cmp"
"github.com/mdlayher/netlink"
"github.com/mdlayher/netlink/nlenc"
"github.com/twistlock/netlink"
"github.com/twistlock/netlink/nlenc"
"golang.org/x/net/bpf"
"golang.org/x/sys/unix"
)
Expand Down
4 changes: 2 additions & 2 deletions conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"testing"
"time"

"github.com/mdlayher/netlink"
"github.com/mdlayher/netlink/nltest"
"github.com/twistlock/netlink"
"github.com/twistlock/netlink/nltest"
)

func TestConnExecute(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion example_attributedecoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"log"

"github.com/mdlayher/netlink"
"github.com/twistlock/netlink"
)

// decodeNested is a nested structure within decodeOut.
Expand Down
2 changes: 1 addition & 1 deletion example_attributeencoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"fmt"
"log"

"github.com/mdlayher/netlink"
"github.com/twistlock/netlink"
)

// encodeNested is a nested structure within out.
Expand Down
6 changes: 3 additions & 3 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package netlink_test
import (
"log"

"github.com/mdlayher/netlink"
"github.com/mdlayher/netlink/nlenc"
"github.com/mdlayher/netlink/nltest"
"github.com/twistlock/netlink"
"github.com/twistlock/netlink/nlenc"
"github.com/twistlock/netlink/nltest"
)

// This example demonstrates using a netlink.Conn to execute requests against
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/mdlayher/netlink
module github.com/twistlock/netlink

go 1.12

Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4 h1:nwOc1YaOrYJ37sEBrtWZrdqzK22hiJs3GpDmP3sR2Yw=
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v1.0.0 h1:vySPY5Oxnn/8lxAPn2cK6kAzcZzYJl3KriSLO46OT18=
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
Expand Down
2 changes: 1 addition & 1 deletion message.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"errors"
"fmt"

"github.com/mdlayher/netlink/nlenc"
"github.com/twistlock/netlink/nlenc"
)

// Flags which may apply to netlink attribute types when communicating with
Expand Down
2 changes: 1 addition & 1 deletion message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"reflect"
"testing"

"github.com/mdlayher/netlink/nlenc"
"github.com/twistlock/netlink/nlenc"
)

func TestHeaderFlagsString(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions nltest/nltest.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"io"
"os"

"github.com/mdlayher/netlink"
"github.com/mdlayher/netlink/nlenc"
"github.com/twistlock/netlink"
"github.com/twistlock/netlink/nlenc"
)

// PID is the netlink header PID value assigned by nltest.
Expand Down
4 changes: 2 additions & 2 deletions nltest/nltest_linux_gteq_1.13_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"os"
"testing"

"github.com/mdlayher/netlink"
"github.com/mdlayher/netlink/nltest"
"github.com/twistlock/netlink"
"github.com/twistlock/netlink/nltest"
"golang.org/x/sys/unix"
)

Expand Down
4 changes: 2 additions & 2 deletions nltest/nltest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/mdlayher/netlink"
"github.com/mdlayher/netlink/nltest"
"github.com/twistlock/netlink"
"github.com/twistlock/netlink/nltest"
)

func TestConnSend(t *testing.T) {
Expand Down

0 comments on commit 6260ab5

Please sign in to comment.