mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Use interface-specific NDP configurations instead of the stack-wide default.
This change makes it so that NDP work is done using the per-interface NDP configurations instead of the stack-wide default NDP configurations to correctly implement RFC 4861 section 6.3.2 (note here, a host is a single NIC operating as a host device), and RFC 4862 section 5.1. Test: Test that we can set NDP configurations on a per-interface basis without affecting the configurations of other interfaces or the stack-wide default. Also make sure that after the configurations are updated, the updated configurations are used for NDP processes (e.g. Duplicate Address Detection). PiperOrigin-RevId: 276525661
This commit is contained in:
committed by
gVisor bot
parent
d9fd536340
commit
f034790ad8
@@ -107,6 +107,9 @@ type ndpState struct {
|
||||
// The NIC this ndpState is for.
|
||||
nic *NIC
|
||||
|
||||
// configs is the per-interface NDP configurations.
|
||||
configs NDPConfigurations
|
||||
|
||||
// The DAD state to send the next NS message, or resolve the address.
|
||||
dad map[tcpip.Address]dadState
|
||||
}
|
||||
@@ -149,7 +152,7 @@ func (ndp *ndpState) startDuplicateAddressDetection(addr tcpip.Address, ref *ref
|
||||
panic(fmt.Sprintf("ndpdad: already performing DAD for addr %s on NIC(%d)", addr, ndp.nic.ID()))
|
||||
}
|
||||
|
||||
remaining := ndp.nic.stack.ndpConfigs.DupAddrDetectTransmits
|
||||
remaining := ndp.configs.DupAddrDetectTransmits
|
||||
|
||||
{
|
||||
done, err := ndp.doDuplicateAddressDetection(addr, remaining, ref)
|
||||
@@ -165,7 +168,7 @@ func (ndp *ndpState) startDuplicateAddressDetection(addr tcpip.Address, ref *ref
|
||||
|
||||
var done bool
|
||||
var timer *time.Timer
|
||||
timer = time.AfterFunc(ndp.nic.stack.ndpConfigs.RetransmitTimer, func() {
|
||||
timer = time.AfterFunc(ndp.configs.RetransmitTimer, func() {
|
||||
var d bool
|
||||
var err *tcpip.Error
|
||||
|
||||
@@ -218,7 +221,6 @@ func (ndp *ndpState) startDuplicateAddressDetection(addr tcpip.Address, ref *ref
|
||||
if doDadIteration() && ndp.nic.stack.ndpDisp != nil {
|
||||
ndp.nic.stack.ndpDisp.OnDuplicateAddressDetectionStatus(ndp.nic.ID(), addr, d, err)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
ndp.dad[addr] = dadState{
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
const (
|
||||
addr1 = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
|
||||
addr2 = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
|
||||
addr3 = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03"
|
||||
linkAddr1 = "\x02\x02\x03\x04\x05\x06"
|
||||
)
|
||||
|
||||
@@ -441,3 +442,168 @@ func TestDADStop(t *testing.T) {
|
||||
t.Fatalf("got NeighborSolicit = %d, want <= 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSetNDPConfigurationFailsForBadNICID tests to make sure we get an error if
|
||||
// we attempt to update NDP configurations using an invalid NICID.
|
||||
func TestSetNDPConfigurationFailsForBadNICID(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
|
||||
})
|
||||
|
||||
// No NIC with ID 1 yet.
|
||||
if got := s.SetNDPConfigurations(1, stack.NDPConfigurations{}); got != tcpip.ErrUnknownNICID {
|
||||
t.Fatalf("got s.SetNDPConfigurations = %v, want = %s", got, tcpip.ErrUnknownNICID)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSetNDPConfigurations tests that we can update and use per-interface NDP
|
||||
// configurations without affecting the default NDP configurations or other
|
||||
// interfaces' configurations.
|
||||
func TestSetNDPConfigurations(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dupAddrDetectTransmits uint8
|
||||
retransmitTimer time.Duration
|
||||
expectedRetransmitTimer time.Duration
|
||||
}{
|
||||
{
|
||||
"OK",
|
||||
1,
|
||||
time.Second,
|
||||
time.Second,
|
||||
},
|
||||
{
|
||||
"Invalid Retransmit Timer",
|
||||
1,
|
||||
0,
|
||||
time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ndpDisp := ndpDispatcher{
|
||||
dadC: make(chan ndpDADEvent),
|
||||
}
|
||||
e := channel.New(10, 1280, linkAddr1)
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
|
||||
NDPDisp: &ndpDisp,
|
||||
})
|
||||
|
||||
// This NIC(1)'s NDP configurations will be updated to
|
||||
// be different from the default.
|
||||
if err := s.CreateNIC(1, e); err != nil {
|
||||
t.Fatalf("CreateNIC(1) = %s", err)
|
||||
}
|
||||
|
||||
// Created before updating NIC(1)'s NDP configurations
|
||||
// but updating NIC(1)'s NDP configurations should not
|
||||
// affect other existing NICs.
|
||||
if err := s.CreateNIC(2, e); err != nil {
|
||||
t.Fatalf("CreateNIC(2) = %s", err)
|
||||
}
|
||||
|
||||
// Update the NDP configurations on NIC(1) to use DAD.
|
||||
configs := stack.NDPConfigurations{
|
||||
DupAddrDetectTransmits: test.dupAddrDetectTransmits,
|
||||
RetransmitTimer: test.retransmitTimer,
|
||||
}
|
||||
if err := s.SetNDPConfigurations(1, configs); err != nil {
|
||||
t.Fatalf("got SetNDPConfigurations(1, _) = %s", err)
|
||||
}
|
||||
|
||||
// Created after updating NIC(1)'s NDP configurations
|
||||
// but the stack's default NDP configurations should not
|
||||
// have been updated.
|
||||
if err := s.CreateNIC(3, e); err != nil {
|
||||
t.Fatalf("CreateNIC(3) = %s", err)
|
||||
}
|
||||
|
||||
// Add addresses for each NIC.
|
||||
if err := s.AddAddress(1, header.IPv6ProtocolNumber, addr1); err != nil {
|
||||
t.Fatalf("AddAddress(1, %d, %s) = %s", header.IPv6ProtocolNumber, addr1, err)
|
||||
}
|
||||
if err := s.AddAddress(2, header.IPv6ProtocolNumber, addr2); err != nil {
|
||||
t.Fatalf("AddAddress(2, %d, %s) = %s", header.IPv6ProtocolNumber, addr2, err)
|
||||
}
|
||||
if err := s.AddAddress(3, header.IPv6ProtocolNumber, addr3); err != nil {
|
||||
t.Fatalf("AddAddress(3, %d, %s) = %s", header.IPv6ProtocolNumber, addr3, err)
|
||||
}
|
||||
|
||||
// Address should not be considered bound to NIC(1) yet
|
||||
// (DAD ongoing).
|
||||
addr, err := s.GetMainNICAddress(1, header.IPv6ProtocolNumber)
|
||||
if err != nil {
|
||||
t.Fatalf("got stack.GetMainNICAddress(_, _) = (_, %v), want = (_, nil)", err)
|
||||
}
|
||||
if want := (tcpip.AddressWithPrefix{}); addr != want {
|
||||
t.Fatalf("got stack.GetMainNICAddress(_, _) = (%s, nil), want = (%s, nil)", addr, want)
|
||||
}
|
||||
|
||||
// Should get the address on NIC(2) and NIC(3)
|
||||
// immediately since we should not have performed DAD on
|
||||
// it as the stack was configured to not do DAD by
|
||||
// default and we only updated the NDP configurations on
|
||||
// NIC(1).
|
||||
addr, err = s.GetMainNICAddress(2, header.IPv6ProtocolNumber)
|
||||
if err != nil {
|
||||
t.Fatalf("stack.GetMainNICAddress(2, _) err = %s", err)
|
||||
}
|
||||
if addr.Address != addr2 {
|
||||
t.Fatalf("got stack.GetMainNICAddress(2, _) = %s, want = %s", addr, addr2)
|
||||
}
|
||||
addr, err = s.GetMainNICAddress(3, header.IPv6ProtocolNumber)
|
||||
if err != nil {
|
||||
t.Fatalf("stack.GetMainNICAddress(3, _) err = %s", err)
|
||||
}
|
||||
if addr.Address != addr3 {
|
||||
t.Fatalf("got stack.GetMainNICAddress(3, _) = %s, want = %s", addr, addr3)
|
||||
}
|
||||
|
||||
// Sleep until right (500ms before) before resolution to
|
||||
// make sure the address didn't resolve on NIC(1) yet.
|
||||
const delta = 500 * time.Millisecond
|
||||
time.Sleep(time.Duration(test.dupAddrDetectTransmits)*test.expectedRetransmitTimer - delta)
|
||||
addr, err = s.GetMainNICAddress(1, header.IPv6ProtocolNumber)
|
||||
if err != nil {
|
||||
t.Fatalf("got stack.GetMainNICAddress(_, _) = (_, %v), want = (_, nil)", err)
|
||||
}
|
||||
if want := (tcpip.AddressWithPrefix{}); addr != want {
|
||||
t.Fatalf("got stack.GetMainNICAddress(_, _) = (%s, nil), want = (%s, nil)", addr, want)
|
||||
}
|
||||
|
||||
// Wait for DAD to resolve.
|
||||
select {
|
||||
case <-time.After(2 * delta):
|
||||
// We should get a resolution event after 500ms
|
||||
// (delta) since we wait for 500ms less than the
|
||||
// expected resolution time above to make sure
|
||||
// that the address did not yet resolve. Waiting
|
||||
// for 1s (2x delta) without a resolution event
|
||||
// means something is wrong.
|
||||
t.Fatal("timed out waiting for DAD resolution")
|
||||
case e := <-ndpDisp.dadC:
|
||||
if e.err != nil {
|
||||
t.Fatal("got DAD error: ", e.err)
|
||||
}
|
||||
if e.nicid != 1 {
|
||||
t.Fatalf("got DAD event w/ nicid = %d, want = 1", e.nicid)
|
||||
}
|
||||
if e.addr != addr1 {
|
||||
t.Fatalf("got DAD event w/ addr = %s, want = %s", addr, addr1)
|
||||
}
|
||||
if !e.resolved {
|
||||
t.Fatal("got DAD event w/ resolved = false, want = true")
|
||||
}
|
||||
}
|
||||
addr, err = s.GetMainNICAddress(1, header.IPv6ProtocolNumber)
|
||||
if err != nil {
|
||||
t.Fatalf("stack.GetMainNICAddress(1, _) err = %s", err)
|
||||
}
|
||||
if addr.Address != addr1 {
|
||||
t.Fatalf("got stack.GetMainNICAddress(1, _) = %s, want = %s", addr, addr1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+19
-1
@@ -46,6 +46,10 @@ type NIC struct {
|
||||
|
||||
stats NICStats
|
||||
|
||||
// ndp is the NDP related state for NIC.
|
||||
//
|
||||
// Note, read and write operations on ndp require that the NIC is
|
||||
// appropriately locked.
|
||||
ndp ndpState
|
||||
}
|
||||
|
||||
@@ -80,6 +84,7 @@ const (
|
||||
NeverPrimaryEndpoint
|
||||
)
|
||||
|
||||
// newNIC returns a new NIC using the default NDP configurations from stack.
|
||||
func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, loopback bool) *NIC {
|
||||
// TODO(b/141011931): Validate a LinkEndpoint (ep) is valid. For
|
||||
// example, make sure that the link address it provides is a valid
|
||||
@@ -105,7 +110,8 @@ func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, loopback
|
||||
},
|
||||
},
|
||||
ndp: ndpState{
|
||||
dad: make(map[tcpip.Address]dadState),
|
||||
configs: stack.ndpConfigs,
|
||||
dad: make(map[tcpip.Address]dadState),
|
||||
},
|
||||
}
|
||||
nic.ndp.nic = nic
|
||||
@@ -937,6 +943,18 @@ func (n *NIC) dupTentativeAddrDetected(addr tcpip.Address) *tcpip.Error {
|
||||
return n.removePermanentAddressLocked(addr)
|
||||
}
|
||||
|
||||
// setNDPConfigs sets the NDP configurations for n.
|
||||
//
|
||||
// Note, if c contains invalid NDP configuration values, it will be fixed to
|
||||
// use default values for the erroneous values.
|
||||
func (n *NIC) setNDPConfigs(c NDPConfigurations) {
|
||||
c.validate()
|
||||
|
||||
n.mu.Lock()
|
||||
n.ndp.configs = c
|
||||
n.mu.Unlock()
|
||||
}
|
||||
|
||||
type networkEndpointKind int32
|
||||
|
||||
const (
|
||||
|
||||
@@ -399,7 +399,7 @@ type Stack struct {
|
||||
// TODO(gvisor.dev/issue/940): S/R this field.
|
||||
portSeed uint32
|
||||
|
||||
// ndpConfigs is the NDP configurations used by interfaces.
|
||||
// ndpConfigs is the default NDP configurations used by interfaces.
|
||||
ndpConfigs NDPConfigurations
|
||||
|
||||
// autoGenIPv6LinkLocal determines whether or not the stack will attempt
|
||||
@@ -433,7 +433,7 @@ type Options struct {
|
||||
// stack (false).
|
||||
HandleLocal bool
|
||||
|
||||
// NDPConfigs is the NDP configurations used by interfaces.
|
||||
// NDPConfigs is the default NDP configurations used by interfaces.
|
||||
//
|
||||
// By default, NDPConfigs will have a zero value for its
|
||||
// DupAddrDetectTransmits field, implying that DAD will not be performed
|
||||
@@ -1425,6 +1425,25 @@ func (s *Stack) DupTentativeAddrDetected(id tcpip.NICID, addr tcpip.Address) *tc
|
||||
return nic.dupTentativeAddrDetected(addr)
|
||||
}
|
||||
|
||||
// SetNDPConfigurations sets the per-interface NDP configurations on the NIC
|
||||
// with ID id to c.
|
||||
//
|
||||
// Note, if c contains invalid NDP configuration values, it will be fixed to
|
||||
// use default values for the erroneous values.
|
||||
func (s *Stack) SetNDPConfigurations(id tcpip.NICID, c NDPConfigurations) *tcpip.Error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
nic, ok := s.nics[id]
|
||||
if !ok {
|
||||
return tcpip.ErrUnknownNICID
|
||||
}
|
||||
|
||||
nic.setNDPConfigs(c)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PortSeed returns a 32 bit value that can be used as a seed value for port
|
||||
// picking.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user