github.com/vishvananda/netlink@v1.3.0/neigh_test.go (about)

     1  // +build linux
     2  
     3  package netlink
     4  
     5  import (
     6  	"net"
     7  	"syscall"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/vishvananda/netns"
    12  	"golang.org/x/sys/unix"
    13  )
    14  
    15  type arpEntry struct {
    16  	ip  net.IP
    17  	mac net.HardwareAddr
    18  }
    19  
    20  type proxyEntry struct {
    21  	ip  net.IP
    22  	dev int
    23  }
    24  
    25  func parseMAC(s string) net.HardwareAddr {
    26  	m, err := net.ParseMAC(s)
    27  	if err != nil {
    28  		panic(err)
    29  	}
    30  	return m
    31  }
    32  
    33  func dumpContains(dump []Neigh, e arpEntry) bool {
    34  	for _, n := range dump {
    35  		if n.IP.Equal(e.ip) && (n.State&NUD_INCOMPLETE) == 0 {
    36  			return true
    37  		}
    38  	}
    39  	return false
    40  }
    41  
    42  func dumpContainsNeigh(dump []Neigh, ne Neigh) bool {
    43  	for _, n := range dump {
    44  		if n.IP.Equal(ne.IP) && n.LLIPAddr.Equal(ne.LLIPAddr) {
    45  			return true
    46  		}
    47  	}
    48  	return false
    49  }
    50  
    51  func dumpContainsState(dump []Neigh, e arpEntry, s uint16) bool {
    52  	for _, n := range dump {
    53  		if n.IP.Equal(e.ip) && uint16(n.State) == s {
    54  			return true
    55  		}
    56  	}
    57  	return false
    58  }
    59  
    60  func dumpContainsProxy(dump []Neigh, p proxyEntry) bool {
    61  	for _, n := range dump {
    62  		if n.IP.Equal(p.ip) && (n.LinkIndex == p.dev) && (n.Flags&NTF_PROXY) == NTF_PROXY {
    63  			return true
    64  		}
    65  	}
    66  	return false
    67  }
    68  
    69  func TestNeighAddDelLLIPAddr(t *testing.T) {
    70  	setUpNetlinkTestWithKModule(t, "ip_gre")
    71  
    72  	tearDown := setUpNetlinkTest(t)
    73  	defer tearDown()
    74  
    75  	dummy := Gretun{
    76  		LinkAttrs: LinkAttrs{Name: "neigh0"},
    77  		Local:     net.IPv4(127, 0, 0, 1),
    78  		IKey:      1234,
    79  		OKey:      1234}
    80  	if err := LinkAdd(&dummy); err != nil {
    81  		t.Errorf("Failed to create link: %v", err)
    82  	}
    83  	ensureIndex(dummy.Attrs())
    84  
    85  	entry := Neigh{
    86  		LinkIndex: dummy.Index,
    87  		State:     NUD_PERMANENT,
    88  		IP:        net.IPv4(198, 51, 100, 2),
    89  		LLIPAddr:  net.IPv4(198, 51, 100, 1),
    90  	}
    91  
    92  	err := NeighAdd(&entry)
    93  	if err != nil {
    94  		t.Errorf("Failed to NeighAdd: %v", err)
    95  	}
    96  
    97  	// Dump and see that all added entries are there
    98  	dump, err := NeighList(dummy.Index, 0)
    99  	if err != nil {
   100  		t.Errorf("Failed to NeighList: %v", err)
   101  	}
   102  
   103  	if !dumpContainsNeigh(dump, entry) {
   104  		t.Errorf("Dump does not contain: %v: %v", entry, dump)
   105  	}
   106  
   107  	// Delete the entry
   108  	err = NeighDel(&entry)
   109  	if err != nil {
   110  		t.Errorf("Failed to NeighDel: %v", err)
   111  	}
   112  
   113  	if err := LinkDel(&dummy); err != nil {
   114  		t.Fatal(err)
   115  	}
   116  }
   117  
   118  func TestNeighAddDel(t *testing.T) {
   119  	tearDown := setUpNetlinkTest(t)
   120  	defer tearDown()
   121  
   122  	dummy := Dummy{LinkAttrs{Name: "neigh0"}}
   123  	if err := LinkAdd(&dummy); err != nil {
   124  		t.Fatal(err)
   125  	}
   126  
   127  	ensureIndex(dummy.Attrs())
   128  
   129  	arpTable := []arpEntry{
   130  		{net.ParseIP("10.99.0.1"), parseMAC("aa:bb:cc:dd:00:01")},
   131  		{net.ParseIP("10.99.0.2"), parseMAC("aa:bb:cc:dd:00:02")},
   132  		{net.ParseIP("10.99.0.3"), parseMAC("aa:bb:cc:dd:00:03")},
   133  		{net.ParseIP("10.99.0.4"), parseMAC("aa:bb:cc:dd:00:04")},
   134  		{net.ParseIP("10.99.0.5"), parseMAC("aa:bb:cc:dd:00:05")},
   135  	}
   136  
   137  	// Add the arpTable
   138  	for _, entry := range arpTable {
   139  		err := NeighAdd(&Neigh{
   140  			LinkIndex:    dummy.Index,
   141  			State:        NUD_REACHABLE,
   142  			IP:           entry.ip,
   143  			HardwareAddr: entry.mac,
   144  		})
   145  
   146  		if err != nil {
   147  			t.Errorf("Failed to NeighAdd: %v", err)
   148  		}
   149  	}
   150  
   151  	// Dump and see that all added entries are there
   152  	dump, err := NeighList(dummy.Index, 0)
   153  	if err != nil {
   154  		t.Errorf("Failed to NeighList: %v", err)
   155  	}
   156  
   157  	for _, entry := range arpTable {
   158  		if !dumpContains(dump, entry) {
   159  			t.Errorf("Dump does not contain: %v", entry)
   160  		}
   161  	}
   162  
   163  	// Delete the arpTable
   164  	for _, entry := range arpTable {
   165  		err := NeighDel(&Neigh{
   166  			LinkIndex:    dummy.Index,
   167  			IP:           entry.ip,
   168  			HardwareAddr: entry.mac,
   169  		})
   170  
   171  		if err != nil {
   172  			t.Errorf("Failed to NeighDel: %v", err)
   173  		}
   174  	}
   175  
   176  	// TODO: seems not working because of cache
   177  	//// Dump and see that none of deleted entries are there
   178  	//dump, err = NeighList(dummy.Index, 0)
   179  	//if err != nil {
   180  	//t.Errorf("Failed to NeighList: %v", err)
   181  	//}
   182  
   183  	//for _, entry := range arpTable {
   184  	//if dumpContains(dump, entry) {
   185  	//t.Errorf("Dump contains: %v", entry)
   186  	//}
   187  	//}
   188  
   189  	if err := LinkDel(&dummy); err != nil {
   190  		t.Fatal(err)
   191  	}
   192  }
   193  
   194  func TestNeighAddDelProxy(t *testing.T) {
   195  	tearDown := setUpNetlinkTest(t)
   196  	defer tearDown()
   197  
   198  	dummy := Dummy{LinkAttrs{Name: "neigh0"}}
   199  	if err := LinkAdd(&dummy); err != nil {
   200  		t.Fatal(err)
   201  	}
   202  
   203  	ensureIndex(dummy.Attrs())
   204  
   205  	proxyTable := []proxyEntry{
   206  		{net.ParseIP("10.99.0.1"), dummy.Index},
   207  		{net.ParseIP("10.99.0.2"), dummy.Index},
   208  		{net.ParseIP("10.99.0.3"), dummy.Index},
   209  		{net.ParseIP("10.99.0.4"), dummy.Index},
   210  		{net.ParseIP("10.99.0.5"), dummy.Index},
   211  	}
   212  
   213  	// Add the proxyTable
   214  	for _, entry := range proxyTable {
   215  		err := NeighAdd(&Neigh{
   216  			LinkIndex: dummy.Index,
   217  			Flags:     NTF_PROXY,
   218  			IP:        entry.ip,
   219  		})
   220  
   221  		if err != nil {
   222  			t.Errorf("Failed to NeighAdd: %v", err)
   223  		}
   224  	}
   225  
   226  	// Dump and see that all added entries are there
   227  	dump, err := NeighProxyList(dummy.Index, 0)
   228  	if err != nil {
   229  		t.Errorf("Failed to NeighList: %v", err)
   230  	}
   231  
   232  	for _, entry := range proxyTable {
   233  		if !dumpContainsProxy(dump, entry) {
   234  			t.Errorf("Dump does not contain: %v", entry)
   235  		}
   236  	}
   237  
   238  	// Delete the proxyTable
   239  	for _, entry := range proxyTable {
   240  		err := NeighDel(&Neigh{
   241  			LinkIndex: dummy.Index,
   242  			Flags:     NTF_PROXY,
   243  			IP:        entry.ip,
   244  		})
   245  
   246  		if err != nil {
   247  			t.Errorf("Failed to NeighDel: %v", err)
   248  		}
   249  	}
   250  
   251  	// Dump and see that none of deleted entries are there
   252  	dump, err = NeighProxyList(dummy.Index, 0)
   253  	if err != nil {
   254  		t.Errorf("Failed to NeighList: %v", err)
   255  	}
   256  
   257  	for _, entry := range proxyTable {
   258  		if dumpContainsProxy(dump, entry) {
   259  			t.Errorf("Dump contains: %v", entry)
   260  		}
   261  	}
   262  
   263  	if err := LinkDel(&dummy); err != nil {
   264  		t.Fatal(err)
   265  	}
   266  }
   267  
   268  // expectNeighUpdate returns whether the expected updates are received within one second.
   269  func expectNeighUpdate(ch <-chan NeighUpdate, expected []NeighUpdate) bool {
   270  	for {
   271  		timeout := time.After(time.Second)
   272  		select {
   273  		case update := <-ch:
   274  			var toDelete []int
   275  			for index, elem := range expected {
   276  				if update.Type == elem.Type &&
   277  					update.Neigh.State == elem.Neigh.State &&
   278  					update.Neigh.IP != nil &&
   279  					update.Neigh.IP.Equal(elem.Neigh.IP) {
   280  					toDelete = append(toDelete, index)
   281  				}
   282  			}
   283  			for done, index := range toDelete {
   284  				expected = append(expected[:index-done], expected[index-done+1:]...)
   285  			}
   286  			if len(expected) == 0 {
   287  				return true
   288  			}
   289  		case <-timeout:
   290  			return false
   291  		}
   292  	}
   293  }
   294  
   295  func TestNeighSubscribe(t *testing.T) {
   296  	tearDown := setUpNetlinkTest(t)
   297  	defer tearDown()
   298  
   299  	dummy := &Dummy{LinkAttrs{Name: "neigh0"}}
   300  	if err := LinkAdd(dummy); err != nil {
   301  		t.Errorf("Failed to create link: %v", err)
   302  	}
   303  	ensureIndex(dummy.Attrs())
   304  	defer func() {
   305  		if err := LinkDel(dummy); err != nil {
   306  			t.Fatal(err)
   307  		}
   308  	}()
   309  
   310  	ch := make(chan NeighUpdate)
   311  	done := make(chan struct{})
   312  	defer close(done)
   313  	if err := NeighSubscribe(ch, done); err != nil {
   314  		t.Fatal(err)
   315  	}
   316  
   317  	entry := &Neigh{
   318  		LinkIndex:    dummy.Index,
   319  		State:        NUD_REACHABLE,
   320  		IP:           net.IPv4(10, 99, 0, 1),
   321  		HardwareAddr: parseMAC("aa:bb:cc:dd:00:01"),
   322  	}
   323  
   324  	if err := NeighAdd(entry); err != nil {
   325  		t.Errorf("Failed to NeighAdd: %v", err)
   326  	}
   327  	if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{
   328  		Type:  unix.RTM_NEWNEIGH,
   329  		Neigh: *entry,
   330  	}}) {
   331  		t.Fatalf("Add update not received as expected")
   332  	}
   333  	if err := NeighDel(entry); err != nil {
   334  		t.Fatal(err)
   335  	}
   336  	if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{
   337  		Type: unix.RTM_NEWNEIGH,
   338  		Neigh: Neigh{
   339  			State: NUD_FAILED,
   340  			IP:    entry.IP},
   341  	}}) {
   342  		t.Fatalf("Del update not received as expected")
   343  	}
   344  }
   345  
   346  func TestNeighSubscribeWithOptions(t *testing.T) {
   347  	tearDown := setUpNetlinkTest(t)
   348  	defer tearDown()
   349  
   350  	ch := make(chan NeighUpdate)
   351  	done := make(chan struct{})
   352  	defer close(done)
   353  	var lastError error
   354  	defer func() {
   355  		if lastError != nil {
   356  			t.Fatalf("Fatal error received during subscription: %v", lastError)
   357  		}
   358  	}()
   359  	if err := NeighSubscribeWithOptions(ch, done, NeighSubscribeOptions{
   360  		ErrorCallback: func(err error) {
   361  			lastError = err
   362  		},
   363  	}); err != nil {
   364  		t.Fatal(err)
   365  	}
   366  
   367  	dummy := &Dummy{LinkAttrs{Name: "neigh0"}}
   368  	if err := LinkAdd(dummy); err != nil {
   369  		t.Errorf("Failed to create link: %v", err)
   370  	}
   371  	ensureIndex(dummy.Attrs())
   372  	defer func() {
   373  		if err := LinkDel(dummy); err != nil {
   374  			t.Fatal(err)
   375  		}
   376  	}()
   377  
   378  	entry := &Neigh{
   379  		LinkIndex:    dummy.Index,
   380  		State:        NUD_REACHABLE,
   381  		IP:           net.IPv4(10, 99, 0, 1),
   382  		HardwareAddr: parseMAC("aa:bb:cc:dd:00:01"),
   383  	}
   384  
   385  	err := NeighAdd(entry)
   386  	if err != nil {
   387  		t.Errorf("Failed to NeighAdd: %v", err)
   388  	}
   389  	if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{
   390  		Type:  unix.RTM_NEWNEIGH,
   391  		Neigh: *entry,
   392  	}}) {
   393  		t.Fatalf("Add update not received as expected")
   394  	}
   395  }
   396  
   397  func TestNeighSubscribeAt(t *testing.T) {
   398  	skipUnlessRoot(t)
   399  
   400  	// Create an handle on a custom netns
   401  	newNs, err := netns.New()
   402  	if err != nil {
   403  		t.Fatal(err)
   404  	}
   405  	defer newNs.Close()
   406  
   407  	nh, err := NewHandleAt(newNs)
   408  	if err != nil {
   409  		t.Fatal(err)
   410  	}
   411  	defer nh.Close()
   412  
   413  	// Subscribe for Neigh events on the custom netns
   414  	ch := make(chan NeighUpdate)
   415  	done := make(chan struct{})
   416  	defer close(done)
   417  	if err := NeighSubscribeAt(newNs, ch, done); err != nil {
   418  		t.Fatal(err)
   419  	}
   420  
   421  	dummy := &Dummy{LinkAttrs{Name: "neigh0"}}
   422  	if err := nh.LinkAdd(dummy); err != nil {
   423  		t.Errorf("Failed to create link: %v", err)
   424  	}
   425  	ensureIndex(dummy.Attrs())
   426  	defer func() {
   427  		if err := nh.LinkDel(dummy); err != nil {
   428  			t.Fatal(err)
   429  		}
   430  	}()
   431  
   432  	entry := &Neigh{
   433  		LinkIndex:    dummy.Index,
   434  		State:        NUD_REACHABLE,
   435  		IP:           net.IPv4(198, 51, 100, 1),
   436  		HardwareAddr: parseMAC("aa:bb:cc:dd:00:01"),
   437  	}
   438  
   439  	err = nh.NeighAdd(entry)
   440  	if err != nil {
   441  		t.Errorf("Failed to NeighAdd: %v", err)
   442  	}
   443  	if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{
   444  		Type:  unix.RTM_NEWNEIGH,
   445  		Neigh: *entry,
   446  	}}) {
   447  		t.Fatalf("Add update not received as expected")
   448  	}
   449  }
   450  
   451  func TestNeighSubscribeListExisting(t *testing.T) {
   452  	skipUnlessRoot(t)
   453  
   454  	// Create an handle on a custom netns
   455  	newNs, err := netns.New()
   456  	if err != nil {
   457  		t.Fatal(err)
   458  	}
   459  	defer newNs.Close()
   460  
   461  	nh, err := NewHandleAt(newNs)
   462  	if err != nil {
   463  		t.Fatal(err)
   464  	}
   465  	defer nh.Close()
   466  
   467  	dummy := &Dummy{LinkAttrs{Name: "neigh0"}}
   468  	if err := nh.LinkAdd(dummy); err != nil {
   469  		t.Errorf("Failed to create link: %v", err)
   470  	}
   471  	ensureIndex(dummy.Attrs())
   472  	defer func() {
   473  		if err := nh.LinkDel(dummy); err != nil {
   474  			t.Fatal(err)
   475  		}
   476  	}()
   477  
   478  	vxlani := &Vxlan{LinkAttrs: LinkAttrs{Name: "neigh1"}, VxlanId: 1}
   479  	if err := nh.LinkAdd(vxlani); err != nil {
   480  		t.Errorf("Failed to create link: %v", err)
   481  	}
   482  	ensureIndex(vxlani.Attrs())
   483  	defer func() {
   484  		if err := nh.LinkDel(vxlani); err != nil {
   485  			t.Fatal(err)
   486  		}
   487  	}()
   488  
   489  	entry1 := &Neigh{
   490  		LinkIndex:    dummy.Index,
   491  		State:        NUD_REACHABLE,
   492  		IP:           net.IPv4(198, 51, 100, 1),
   493  		HardwareAddr: parseMAC("aa:bb:cc:dd:00:01"),
   494  	}
   495  
   496  	entryBr := &Neigh{
   497  		Family:       syscall.AF_BRIDGE,
   498  		LinkIndex:    vxlani.Index,
   499  		State:        NUD_PERMANENT,
   500  		Flags:        NTF_SELF,
   501  		IP:           net.IPv4(198, 51, 100, 3),
   502  		HardwareAddr: parseMAC("aa:bb:cc:dd:00:03"),
   503  	}
   504  
   505  	err = nh.NeighAdd(entry1)
   506  	if err != nil {
   507  		t.Errorf("Failed to NeighAdd: %v", err)
   508  	}
   509  	err = nh.NeighAppend(entryBr)
   510  	if err != nil {
   511  		t.Errorf("Failed to NeighAdd: %v", err)
   512  	}
   513  
   514  	// Subscribe for Neigh events including existing neighbors
   515  	ch := make(chan NeighUpdate)
   516  	done := make(chan struct{})
   517  	defer close(done)
   518  	if err := NeighSubscribeWithOptions(ch, done, NeighSubscribeOptions{
   519  		Namespace:    &newNs,
   520  		ListExisting: true},
   521  	); err != nil {
   522  		t.Fatal(err)
   523  	}
   524  
   525  	if !expectNeighUpdate(ch, []NeighUpdate{
   526  		NeighUpdate{
   527  			Type:  unix.RTM_NEWNEIGH,
   528  			Neigh: *entry1,
   529  		},
   530  		NeighUpdate{
   531  			Type:  unix.RTM_NEWNEIGH,
   532  			Neigh: *entryBr,
   533  		},
   534  	}) {
   535  		t.Fatalf("Existing add update not received as expected")
   536  	}
   537  
   538  	entry2 := &Neigh{
   539  		LinkIndex:    dummy.Index,
   540  		State:        NUD_PERMANENT,
   541  		IP:           net.IPv4(198, 51, 100, 2),
   542  		HardwareAddr: parseMAC("aa:bb:cc:dd:00:02"),
   543  	}
   544  
   545  	err = nh.NeighAdd(entry2)
   546  	if err != nil {
   547  		t.Errorf("Failed to NeighAdd: %v", err)
   548  	}
   549  
   550  	if !expectNeighUpdate(ch, []NeighUpdate{NeighUpdate{
   551  		Type:  unix.RTM_NEWNEIGH,
   552  		Neigh: *entry2,
   553  	}}) {
   554  		t.Fatalf("Existing add update not received as expected")
   555  	}
   556  }
   557  
   558  func TestNeighListExecuteStateFilter(t *testing.T) {
   559  	tearDown := setUpNetlinkTest(t)
   560  	defer tearDown()
   561  
   562  	// Create dummy iface
   563  	dummy := Dummy{LinkAttrs{Name: "neigh0"}}
   564  	if err := LinkAdd(&dummy); err != nil {
   565  		t.Fatal(err)
   566  	}
   567  
   568  	ensureIndex(dummy.Attrs())
   569  
   570  	// Define some entries
   571  	reachArpTable := []arpEntry{
   572  		{net.ParseIP("198.51.100.1"), parseMAC("44:bb:cc:dd:00:01")},
   573  		{net.ParseIP("2001:db8::1"), parseMAC("66:bb:cc:dd:00:02")},
   574  	}
   575  
   576  	staleArpTable := []arpEntry{
   577  		{net.ParseIP("198.51.100.10"), parseMAC("44:bb:cc:dd:00:10")},
   578  		{net.ParseIP("2001:db8::10"), parseMAC("66:bb:cc:dd:00:10")},
   579  	}
   580  
   581  	entries := append(reachArpTable, staleArpTable...)
   582  
   583  	// Add reachable neigh entries
   584  	for _, entry := range reachArpTable {
   585  		err := NeighAdd(&Neigh{
   586  			LinkIndex:    dummy.Index,
   587  			State:        NUD_REACHABLE,
   588  			IP:           entry.ip,
   589  			HardwareAddr: entry.mac,
   590  		})
   591  
   592  		if err != nil {
   593  			t.Errorf("Failed to NeighAdd: %v", err)
   594  		}
   595  	}
   596  	// Add stale neigh entries
   597  	for _, entry := range staleArpTable {
   598  		err := NeighAdd(&Neigh{
   599  			LinkIndex:    dummy.Index,
   600  			State:        NUD_STALE,
   601  			IP:           entry.ip,
   602  			HardwareAddr: entry.mac,
   603  		})
   604  
   605  		if err != nil {
   606  			t.Errorf("Failed to NeighAdd: %v", err)
   607  		}
   608  	}
   609  
   610  	// Dump reachable and see that all added reachable entries are present and there are no stale entries
   611  	dump, err := NeighListExecute(Ndmsg{
   612  		Index: uint32(dummy.Index),
   613  		State: NUD_REACHABLE,
   614  	})
   615  	if err != nil {
   616  		t.Errorf("Failed to NeighListExecute: %v", err)
   617  	}
   618  
   619  	for _, entry := range reachArpTable {
   620  		if !dumpContainsState(dump, entry, NUD_REACHABLE) {
   621  			t.Errorf("Dump does not contains: %v", entry)
   622  		}
   623  	}
   624  	for _, entry := range staleArpTable {
   625  		if dumpContainsState(dump, entry, NUD_STALE) {
   626  			t.Errorf("Dump contains: %v", entry)
   627  		}
   628  	}
   629  
   630  	// Delete all neigh entries
   631  	for _, entry := range entries {
   632  		err := NeighDel(&Neigh{
   633  			LinkIndex:    dummy.Index,
   634  			IP:           entry.ip,
   635  			HardwareAddr: entry.mac,
   636  		})
   637  
   638  		if err != nil {
   639  			t.Errorf("Failed to NeighDel: %v", err)
   640  		}
   641  	}
   642  }