Skip to content

Commit

Permalink
Add a 'ListExisting' option to get the existing entries in the
Browse files Browse the repository at this point in the history
route/addr/link tables as part of RouteSubscribeWithOptions,
AddrSubscribeWithOptions, and LinkSubscribeWithOptions.
  • Loading branch information
eriknordmark authored and aboch committed Jan 23, 2018
1 parent 5a988e8 commit 5f5d5cd
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 13 deletions.
33 changes: 29 additions & 4 deletions addr_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"net"
"strings"
"syscall"

"github.com/vishvananda/netlink/nl"
"github.com/vishvananda/netns"
Expand Down Expand Up @@ -249,20 +250,21 @@ type AddrUpdate struct {
// AddrSubscribe takes a chan down which notifications will be sent
// when addresses change. Close the 'done' chan to stop subscription.
func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error {
return addrSubscribeAt(netns.None(), netns.None(), ch, done, nil)
return addrSubscribeAt(netns.None(), netns.None(), ch, done, nil, false)
}

// AddrSubscribeAt works like AddrSubscribe plus it allows the caller
// to choose the network namespace in which to subscribe (ns).
func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error {
return addrSubscribeAt(ns, netns.None(), ch, done, nil)
return addrSubscribeAt(ns, netns.None(), ch, done, nil, false)
}

// AddrSubscribeOptions contains a set of options to use with
// AddrSubscribeWithOptions.
type AddrSubscribeOptions struct {
Namespace *netns.NsHandle
ErrorCallback func(error)
ListExisting bool
}

// AddrSubscribeWithOptions work like AddrSubscribe but enable to
Expand All @@ -273,10 +275,10 @@ func AddrSubscribeWithOptions(ch chan<- AddrUpdate, done <-chan struct{}, option
none := netns.None()
options.Namespace = &none
}
return addrSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback)
return addrSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting)
}

func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}, cberr func(error)) error {
func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_IPV4_IFADDR, unix.RTNLGRP_IPV6_IFADDR)
if err != nil {
return err
Expand All @@ -287,6 +289,15 @@ func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-c
s.Close()
}()
}
if listExisting {
req := pkgHandle.newNetlinkRequest(unix.RTM_GETADDR,
unix.NLM_F_DUMP)
infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC)
req.AddData(infmsg)
if err := s.Send(req); err != nil {
return err
}
}
go func() {
defer close(ch)
for {
Expand All @@ -298,6 +309,20 @@ func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-c
return
}
for _, m := range msgs {
if m.Header.Type == unix.NLMSG_DONE {
continue
}
if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
continue
}
if cberr != nil {
cberr(syscall.Errno(-error))
}
return
}
msgType := m.Header.Type
if msgType != unix.RTM_NEWADDR && msgType != unix.RTM_DELADDR {
if cberr != nil {
Expand Down
40 changes: 40 additions & 0 deletions addr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,3 +245,43 @@ func TestAddrSubscribeWithOptions(t *testing.T) {
t.Fatal("Add update not received as expected")
}
}

func TestAddrSubscribeListExisting(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()

ch := make(chan AddrUpdate)
done := make(chan struct{})
defer close(done)

// get loopback interface
link, err := LinkByName("lo")
if err != nil {
t.Fatal(err)
}

// bring the interface up
if err = LinkSetUp(link); err != nil {
t.Fatal(err)
}

var lastError error
defer func() {
if lastError != nil {
t.Fatalf("Fatal error received during subscription: %v", lastError)
}
}()
if err := AddrSubscribeWithOptions(ch, done, AddrSubscribeOptions{
ErrorCallback: func(err error) {
lastError = err
},
ListExisting: true,
}); err != nil {
t.Fatal(err)
}

ip := net.IPv4(127, 0, 0, 1)
if !expectAddrUpdate(ch, true, ip) {
t.Fatal("Add update not received as expected")
}
}
32 changes: 28 additions & 4 deletions link_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1476,20 +1476,21 @@ type LinkUpdate struct {
// LinkSubscribe takes a chan down which notifications will be sent
// when links change. Close the 'done' chan to stop subscription.
func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error {
return linkSubscribeAt(netns.None(), netns.None(), ch, done, nil)
return linkSubscribeAt(netns.None(), netns.None(), ch, done, nil, false)
}

// LinkSubscribeAt works like LinkSubscribe plus it allows the caller
// to choose the network namespace in which to subscribe (ns).
func LinkSubscribeAt(ns netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}) error {
return linkSubscribeAt(ns, netns.None(), ch, done, nil)
return linkSubscribeAt(ns, netns.None(), ch, done, nil, false)
}

// LinkSubscribeOptions contains a set of options to use with
// LinkSubscribeWithOptions.
type LinkSubscribeOptions struct {
Namespace *netns.NsHandle
ErrorCallback func(error)
ListExisting bool
}

// LinkSubscribeWithOptions work like LinkSubscribe but enable to
Expand All @@ -1500,10 +1501,10 @@ func LinkSubscribeWithOptions(ch chan<- LinkUpdate, done <-chan struct{}, option
none := netns.None()
options.Namespace = &none
}
return linkSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback)
return linkSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting)
}

func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}, cberr func(error)) error {
func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_LINK)
if err != nil {
return err
Expand All @@ -1514,6 +1515,15 @@ func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-c
s.Close()
}()
}
if listExisting {
req := pkgHandle.newNetlinkRequest(unix.RTM_GETLINK,
unix.NLM_F_DUMP)
msg := nl.NewIfInfomsg(unix.AF_UNSPEC)
req.AddData(msg)
if err := s.Send(req); err != nil {
return err
}
}
go func() {
defer close(ch)
for {
Expand All @@ -1525,6 +1535,20 @@ func linkSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-c
return
}
for _, m := range msgs {
if m.Header.Type == unix.NLMSG_DONE {
continue
}
if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
continue
}
if cberr != nil {
cberr(syscall.Errno(-error))
}
return
}
ifmsg := nl.DeserializeIfInfomsg(m.Data)
header := unix.NlMsghdr(m.Header)
link, err := LinkDeserialize(&header, m.Data)
Expand Down
53 changes: 53 additions & 0 deletions link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,59 @@ func TestLinkSubscribeAt(t *testing.T) {
}
}

func TestLinkSubscribeListExisting(t *testing.T) {
skipUnlessRoot(t)

// Create an handle on a custom netns
newNs, err := netns.New()
if err != nil {
t.Fatal(err)
}
defer newNs.Close()

nh, err := NewHandleAt(newNs)
if err != nil {
t.Fatal(err)
}
defer nh.Delete()

link := &Veth{LinkAttrs{Name: "test", TxQLen: testTxQLen, MTU: 1400}, "bar"}
if err := nh.LinkAdd(link); err != nil {
t.Fatal(err)
}

// Subscribe for Link events on the custom netns
ch := make(chan LinkUpdate)
done := make(chan struct{})
defer close(done)
if err := LinkSubscribeWithOptions(ch, done, LinkSubscribeOptions{
Namespace: &newNs,
ListExisting: true},
); err != nil {
t.Fatal(err)
}

if !expectLinkUpdate(ch, "test", false) {
t.Fatal("Add update not received as expected")
}

if err := nh.LinkSetUp(link); err != nil {
t.Fatal(err)
}

if !expectLinkUpdate(ch, "test", true) {
t.Fatal("Link Up update not received as expected")
}

if err := nh.LinkDel(link); err != nil {
t.Fatal(err)
}

if !expectLinkUpdate(ch, "test", false) {
t.Fatal("Del update not received as expected")
}
}

func TestLinkStats(t *testing.T) {
defer setUpNetlinkTest(t)()

Expand Down
32 changes: 28 additions & 4 deletions route_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -789,20 +789,21 @@ func (h *Handle) RouteGet(destination net.IP) ([]Route, error) {
// RouteSubscribe takes a chan down which notifications will be sent
// when routes are added or deleted. Close the 'done' chan to stop subscription.
func RouteSubscribe(ch chan<- RouteUpdate, done <-chan struct{}) error {
return routeSubscribeAt(netns.None(), netns.None(), ch, done, nil)
return routeSubscribeAt(netns.None(), netns.None(), ch, done, nil, false)
}

// RouteSubscribeAt works like RouteSubscribe plus it allows the caller
// to choose the network namespace in which to subscribe (ns).
func RouteSubscribeAt(ns netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error {
return routeSubscribeAt(ns, netns.None(), ch, done, nil)
return routeSubscribeAt(ns, netns.None(), ch, done, nil, false)
}

// RouteSubscribeOptions contains a set of options to use with
// RouteSubscribeWithOptions.
type RouteSubscribeOptions struct {
Namespace *netns.NsHandle
ErrorCallback func(error)
ListExisting bool
}

// RouteSubscribeWithOptions work like RouteSubscribe but enable to
Expand All @@ -813,10 +814,10 @@ func RouteSubscribeWithOptions(ch chan<- RouteUpdate, done <-chan struct{}, opti
none := netns.None()
options.Namespace = &none
}
return routeSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback)
return routeSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting)
}

func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}, cberr func(error)) error {
func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_IPV4_ROUTE, unix.RTNLGRP_IPV6_ROUTE)
if err != nil {
return err
Expand All @@ -827,6 +828,15 @@ func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <
s.Close()
}()
}
if listExisting {
req := pkgHandle.newNetlinkRequest(unix.RTM_GETROUTE,
unix.NLM_F_DUMP)
infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC)
req.AddData(infmsg)
if err := s.Send(req); err != nil {
return err
}
}
go func() {
defer close(ch)
for {
Expand All @@ -838,6 +848,20 @@ func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <
return
}
for _, m := range msgs {
if m.Header.Type == unix.NLMSG_DONE {
continue
}
if m.Header.Type == unix.NLMSG_ERROR {
native := nl.NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
continue
}
if cberr != nil {
cberr(syscall.Errno(-error))
}
return
}
route, err := deserializeRoute(m.Data)
if err != nil {
if cberr != nil {
Expand Down
Loading

0 comments on commit 5f5d5cd

Please sign in to comment.