mirror of https://github.com/vishvananda/netlink
neighSubscribeAt: AF_BRIDGE entries not listed when listExisting is true
When subscribing to neigh updates, the updates for all neigh protocol families are received. However when listExisting is set, the request is made with AF_UNSPEC family, this request does not include AF_BRIDGE entries. This patch add a second request for AF_BRIDGE entries. Add test for existing AF_BRIDGE entry and make expectNeighUpdate take a slice of expected updates Creates a VXLAN interface for this test as its AF_BRIDGE entries looks a lot like usual ones Also add support for latest (2014+) neighbour attributes NDA_MASTER was added back in 2014, it indicates whether a neigh entry is linked to a master interface and index of this interface. The other entries, namely NDA_LINK_NETNSID and NDA_SRC_VNI were added later and will need extra handling. Signed-off-by: Nicolas Belouin <nicolas.belouin@gandi.net>
This commit is contained in:
parent
941b4de9e1
commit
a1c9a648f7
1
neigh.go
1
neigh.go
|
@ -17,6 +17,7 @@ type Neigh struct {
|
|||
LLIPAddr net.IP //Used in the case of NHRP
|
||||
Vlan int
|
||||
VNI int
|
||||
MasterIndex int
|
||||
}
|
||||
|
||||
// String returns $ip/$hwaddr $label
|
||||
|
|
|
@ -21,7 +21,10 @@ const (
|
|||
NDA_PORT
|
||||
NDA_VNI
|
||||
NDA_IFINDEX
|
||||
NDA_MAX = NDA_IFINDEX
|
||||
NDA_MASTER
|
||||
NDA_LINK_NETNSID
|
||||
NDA_SRC_VNI
|
||||
NDA_MAX = NDA_SRC_VNI
|
||||
)
|
||||
|
||||
// Neighbor Cache Entry States.
|
||||
|
@ -174,6 +177,11 @@ func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
|
|||
req.AddData(vniData)
|
||||
}
|
||||
|
||||
if neigh.MasterIndex != 0 {
|
||||
masterData := nl.NewRtAttr(NDA_MASTER, nl.Uint32Attr(uint32(neigh.MasterIndex)))
|
||||
req.AddData(masterData)
|
||||
}
|
||||
|
||||
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
|
||||
return err
|
||||
}
|
||||
|
@ -289,6 +297,8 @@ func NeighDeserialize(m []byte) (*Neigh, error) {
|
|||
neigh.Vlan = int(native.Uint16(attr.Value[0:2]))
|
||||
case NDA_VNI:
|
||||
neigh.VNI = int(native.Uint32(attr.Value[0:4]))
|
||||
case NDA_MASTER:
|
||||
neigh.MasterIndex = int(native.Uint32(attr.Value[0:4]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -328,6 +338,16 @@ func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, opti
|
|||
|
||||
func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
|
||||
s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH)
|
||||
makeRequest := func(family int) error {
|
||||
req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH,
|
||||
unix.NLM_F_DUMP)
|
||||
infmsg := nl.NewIfInfomsg(family)
|
||||
req.AddData(infmsg)
|
||||
if err := s.Send(req); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -338,13 +358,10 @@ func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <
|
|||
}()
|
||||
}
|
||||
if listExisting {
|
||||
req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH,
|
||||
unix.NLM_F_DUMP)
|
||||
infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC)
|
||||
req.AddData(infmsg)
|
||||
if err := s.Send(req); err != nil {
|
||||
if err := makeRequest(unix.AF_UNSPEC); err != nil {
|
||||
return err
|
||||
}
|
||||
// We have to wait for NLMSG_DONE before making AF_BRIDGE request
|
||||
}
|
||||
go func() {
|
||||
defer close(ch)
|
||||
|
@ -364,6 +381,18 @@ func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <
|
|||
}
|
||||
for _, m := range msgs {
|
||||
if m.Header.Type == unix.NLMSG_DONE {
|
||||
if listExisting {
|
||||
// This will be called after handling AF_UNSPEC
|
||||
// list request, we have to wait for NLMSG_DONE
|
||||
// before making another request
|
||||
if err := makeRequest(unix.AF_BRIDGE); err != nil {
|
||||
if cberr != nil {
|
||||
cberr(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
listExisting = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
if m.Header.Type == unix.NLMSG_ERROR {
|
||||
|
|
|
@ -4,6 +4,7 @@ package netlink
|
|||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -255,13 +256,25 @@ func TestNeighAddDelProxy(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// expectNeighUpdate returns whether the expected updated is received within one second.
|
||||
func expectNeighUpdate(ch <-chan NeighUpdate, t uint16, state int, ip net.IP) bool {
|
||||
// expectNeighUpdate returns whether the expected updates are received within one second.
|
||||
func expectNeighUpdate(ch <-chan NeighUpdate, expected []NeighUpdate) bool {
|
||||
for {
|
||||
timeout := time.After(time.Second)
|
||||
select {
|
||||
case update := <-ch:
|
||||
if update.Type == t && update.Neigh.State == state && update.Neigh.IP != nil && update.Neigh.IP.Equal(ip) {
|
||||
var toDelete []int
|
||||
for index, elem := range expected {
|
||||
if update.Type == elem.Type &&
|
||||
update.Neigh.State == elem.Neigh.State &&
|
||||
update.Neigh.IP != nil &&
|
||||
update.Neigh.IP.Equal(elem.Neigh.IP) {
|
||||
toDelete = append(toDelete, index)
|
||||
}
|
||||
}
|
||||
for done, index := range toDelete {
|
||||
expected = append(expected[:index-done], expected[index-done+1:]...)
|
||||
}
|
||||
if len(expected) == 0 {
|
||||
return true
|
||||
}
|
||||
case <-timeout:
|
||||
|
@ -302,13 +315,21 @@ func TestNeighSubscribe(t *testing.T) {
|
|||
if err := NeighAdd(entry); err != nil {
|
||||
t.Errorf("Failed to NeighAdd: %v", err)
|
||||
}
|
||||
if !expectNeighUpdate(ch, unix.RTM_NEWNEIGH, NUD_REACHABLE, entry.IP) {
|
||||
if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{
|
||||
Type: unix.RTM_NEWNEIGH,
|
||||
Neigh: *entry,
|
||||
}}) {
|
||||
t.Fatalf("Add update not received as expected")
|
||||
}
|
||||
if err := NeighDel(entry); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !expectNeighUpdate(ch, unix.RTM_NEWNEIGH, NUD_FAILED, entry.IP) {
|
||||
if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{
|
||||
Type: unix.RTM_NEWNEIGH,
|
||||
Neigh: Neigh{
|
||||
State: NUD_FAILED,
|
||||
IP: entry.IP},
|
||||
}}) {
|
||||
t.Fatalf("Del update not received as expected")
|
||||
}
|
||||
}
|
||||
|
@ -356,7 +377,10 @@ func TestNeighSubscribeWithOptions(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("Failed to NeighAdd: %v", err)
|
||||
}
|
||||
if !expectNeighUpdate(ch, unix.RTM_NEWNEIGH, NUD_REACHABLE, entry.IP) {
|
||||
if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{
|
||||
Type: unix.RTM_NEWNEIGH,
|
||||
Neigh: *entry,
|
||||
}}) {
|
||||
t.Fatalf("Add update not received as expected")
|
||||
}
|
||||
}
|
||||
|
@ -407,7 +431,10 @@ func TestNeighSubscribeAt(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("Failed to NeighAdd: %v", err)
|
||||
}
|
||||
if !expectNeighUpdate(ch, unix.RTM_NEWNEIGH, NUD_REACHABLE, entry.IP) {
|
||||
if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{
|
||||
Type: unix.RTM_NEWNEIGH,
|
||||
Neigh: *entry,
|
||||
}}) {
|
||||
t.Fatalf("Add update not received as expected")
|
||||
}
|
||||
}
|
||||
|
@ -439,6 +466,17 @@ func TestNeighSubscribeListExisting(t *testing.T) {
|
|||
}
|
||||
}()
|
||||
|
||||
vxlani := &Vxlan{LinkAttrs: LinkAttrs{Name: "neigh1"}, VxlanId: 1}
|
||||
if err := nh.LinkAdd(vxlani); err != nil {
|
||||
t.Errorf("Failed to create link: %v", err)
|
||||
}
|
||||
ensureIndex(vxlani.Attrs())
|
||||
defer func() {
|
||||
if err := nh.LinkDel(vxlani); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
entry1 := &Neigh{
|
||||
LinkIndex: dummy.Index,
|
||||
State: NUD_REACHABLE,
|
||||
|
@ -446,10 +484,23 @@ func TestNeighSubscribeListExisting(t *testing.T) {
|
|||
HardwareAddr: parseMAC("aa:bb:cc:dd:00:01"),
|
||||
}
|
||||
|
||||
entryBr := &Neigh{
|
||||
Family: syscall.AF_BRIDGE,
|
||||
LinkIndex: vxlani.Index,
|
||||
State: NUD_PERMANENT,
|
||||
Flags: NTF_SELF,
|
||||
IP: net.IPv4(198, 51, 100, 3),
|
||||
HardwareAddr: parseMAC("aa:bb:cc:dd:00:03"),
|
||||
}
|
||||
|
||||
err = nh.NeighAdd(entry1)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to NeighAdd: %v", err)
|
||||
}
|
||||
err = nh.NeighAppend(entryBr)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to NeighAdd: %v", err)
|
||||
}
|
||||
|
||||
// Subscribe for Neigh events including existing neighbors
|
||||
ch := make(chan NeighUpdate)
|
||||
|
@ -462,7 +513,16 @@ func TestNeighSubscribeListExisting(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !expectNeighUpdate(ch, unix.RTM_NEWNEIGH, NUD_REACHABLE, entry1.IP) {
|
||||
if !expectNeighUpdate(ch, []NeighUpdate{
|
||||
NeighUpdate{
|
||||
Type: unix.RTM_NEWNEIGH,
|
||||
Neigh: *entry1,
|
||||
},
|
||||
NeighUpdate{
|
||||
Type: unix.RTM_NEWNEIGH,
|
||||
Neigh: *entryBr,
|
||||
},
|
||||
}) {
|
||||
t.Fatalf("Existing add update not received as expected")
|
||||
}
|
||||
|
||||
|
@ -478,7 +538,10 @@ func TestNeighSubscribeListExisting(t *testing.T) {
|
|||
t.Errorf("Failed to NeighAdd: %v", err)
|
||||
}
|
||||
|
||||
if !expectNeighUpdate(ch, unix.RTM_NEWNEIGH, NUD_PERMANENT, entry2.IP) {
|
||||
if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{
|
||||
Type: unix.RTM_NEWNEIGH,
|
||||
Neigh: *entry2,
|
||||
}}) {
|
||||
t.Fatalf("Existing add update not received as expected")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue