mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Respect new PrimaryEndpointBehavior when addresses gets promoted to permanent
This change makes sure that when an address which is already known by a NIC and has kind = permanentExpired gets promoted to permanent, the new PrimaryEndpointBehavior is respected. PiperOrigin-RevId: 276136317
This commit is contained in:
committed by
gVisor bot
parent
8720bd643e
commit
c356fe2ebb
@@ -73,6 +73,7 @@ go_test(
|
||||
"//pkg/tcpip/transport/icmp",
|
||||
"//pkg/tcpip/transport/udp",
|
||||
"//pkg/waiter",
|
||||
"@com_github_google_go-cmp//cmp:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
+40
-9
@@ -335,9 +335,31 @@ func (n *NIC) addPermanentAddressLocked(protocolAddress tcpip.ProtocolAddress, p
|
||||
// The NIC already have a permanent endpoint with that address.
|
||||
return nil, tcpip.ErrDuplicateAddress
|
||||
case permanentExpired, temporary:
|
||||
// Promote the endpoint to become permanent.
|
||||
// Promote the endpoint to become permanent and respect
|
||||
// the new peb.
|
||||
if ref.tryIncRef() {
|
||||
ref.setKind(permanent)
|
||||
|
||||
refs := n.primary[ref.protocol]
|
||||
for i, r := range refs {
|
||||
if r == ref {
|
||||
switch peb {
|
||||
case CanBePrimaryEndpoint:
|
||||
return ref, nil
|
||||
case FirstPrimaryEndpoint:
|
||||
if i == 0 {
|
||||
return ref, nil
|
||||
}
|
||||
n.primary[r.protocol] = append(refs[:i], refs[i+1:]...)
|
||||
case NeverPrimaryEndpoint:
|
||||
n.primary[r.protocol] = append(refs[:i], refs[i+1:]...)
|
||||
return ref, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n.insertPrimaryEndpointLocked(ref, peb)
|
||||
|
||||
return ref, nil
|
||||
}
|
||||
// tryIncRef failing means the endpoint is scheduled to be removed once
|
||||
@@ -406,12 +428,7 @@ func (n *NIC) addAddressLocked(protocolAddress tcpip.ProtocolAddress, peb Primar
|
||||
|
||||
n.endpoints[id] = ref
|
||||
|
||||
switch peb {
|
||||
case CanBePrimaryEndpoint:
|
||||
n.primary[protocolAddress.Protocol] = append(n.primary[protocolAddress.Protocol], ref)
|
||||
case FirstPrimaryEndpoint:
|
||||
n.primary[protocolAddress.Protocol] = append([]*referencedNetworkEndpoint{ref}, n.primary[protocolAddress.Protocol]...)
|
||||
}
|
||||
n.insertPrimaryEndpointLocked(ref, peb)
|
||||
|
||||
// If we are adding a tentative IPv6 address, start DAD.
|
||||
if isIPv6Unicast && kind == permanentTentative {
|
||||
@@ -533,6 +550,19 @@ func (n *NIC) AddressRanges() []tcpip.Subnet {
|
||||
return append(sns, n.addressRanges...)
|
||||
}
|
||||
|
||||
// insertPrimaryEndpointLocked adds r to n's primary endpoint list as required
|
||||
// by peb.
|
||||
//
|
||||
// n MUST be locked.
|
||||
func (n *NIC) insertPrimaryEndpointLocked(r *referencedNetworkEndpoint, peb PrimaryEndpointBehavior) {
|
||||
switch peb {
|
||||
case CanBePrimaryEndpoint:
|
||||
n.primary[r.protocol] = append(n.primary[r.protocol], r)
|
||||
case FirstPrimaryEndpoint:
|
||||
n.primary[r.protocol] = append([]*referencedNetworkEndpoint{r}, n.primary[r.protocol]...)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NIC) removeEndpointLocked(r *referencedNetworkEndpoint) {
|
||||
id := *r.ep.ID()
|
||||
|
||||
@@ -550,9 +580,10 @@ func (n *NIC) removeEndpointLocked(r *referencedNetworkEndpoint) {
|
||||
}
|
||||
|
||||
delete(n.endpoints, id)
|
||||
for i, ref := range n.primary[r.protocol] {
|
||||
refs := n.primary[r.protocol]
|
||||
for i, ref := range refs {
|
||||
if ref == r {
|
||||
n.primary[r.protocol] = append(n.primary[r.protocol][:i], n.primary[r.protocol][i+1:]...)
|
||||
n.primary[r.protocol] = append(refs[:i], refs[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
@@ -2013,3 +2014,139 @@ func TestNICAutoGenAddrDoesDAD(t *testing.T) {
|
||||
t.Fatalf("got stack.GetMainNICAddress(_, _) = %s, want = %s", addr, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewPEB tests that a new PrimaryEndpointBehavior value (peb) is respected
|
||||
// when an address's kind gets "promoted" to permanent from permanentExpired.
|
||||
func TestNewPEBOnPromotionToPermanent(t *testing.T) {
|
||||
pebs := []stack.PrimaryEndpointBehavior{
|
||||
stack.NeverPrimaryEndpoint,
|
||||
stack.CanBePrimaryEndpoint,
|
||||
stack.FirstPrimaryEndpoint,
|
||||
}
|
||||
|
||||
for _, pi := range pebs {
|
||||
for _, ps := range pebs {
|
||||
t.Run(fmt.Sprintf("%d-to-%d", pi, ps), func(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{fakeNetFactory()},
|
||||
})
|
||||
ep1 := channel.New(10, defaultMTU, "")
|
||||
if err := s.CreateNIC(1, ep1); err != nil {
|
||||
t.Fatal("CreateNIC failed:", err)
|
||||
}
|
||||
|
||||
// Add a permanent address with initial
|
||||
// PrimaryEndpointBehavior (peb), pi. If pi is
|
||||
// NeverPrimaryEndpoint, the address should not
|
||||
// be returned by a call to GetMainNICAddress;
|
||||
// else, it should.
|
||||
if err := s.AddAddressWithOptions(1, fakeNetNumber, "\x01", pi); err != nil {
|
||||
t.Fatal("AddAddressWithOptions failed:", err)
|
||||
}
|
||||
addr, err := s.GetMainNICAddress(1, fakeNetNumber)
|
||||
if err != nil {
|
||||
t.Fatal("s.GetMainNICAddress failed:", err)
|
||||
}
|
||||
if pi == stack.NeverPrimaryEndpoint {
|
||||
if want := (tcpip.AddressWithPrefix{}); addr != want {
|
||||
t.Fatalf("got GetMainNICAddress = %s, want = %s", addr, want)
|
||||
|
||||
}
|
||||
} else if addr.Address != "\x01" {
|
||||
t.Fatalf("got GetMainNICAddress = %s, want = 1", addr.Address)
|
||||
}
|
||||
|
||||
{
|
||||
subnet, err := tcpip.NewSubnet("\x00", "\x00")
|
||||
if err != nil {
|
||||
t.Fatalf("NewSubnet failed:", err)
|
||||
}
|
||||
s.SetRouteTable([]tcpip.Route{{Destination: subnet, Gateway: "\x00", NIC: 1}})
|
||||
}
|
||||
|
||||
// Take a route through the address so its ref
|
||||
// count gets incremented and does not actually
|
||||
// get deleted when RemoveAddress is called
|
||||
// below. This is because we want to test that a
|
||||
// new peb is respected when an address gets
|
||||
// "promoted" to permanent from a
|
||||
// permanentExpired kind.
|
||||
r, err := s.FindRoute(1, "\x01", "\x02", fakeNetNumber, false)
|
||||
if err != nil {
|
||||
t.Fatal("FindRoute failed:", err)
|
||||
}
|
||||
defer r.Release()
|
||||
if err := s.RemoveAddress(1, "\x01"); err != nil {
|
||||
t.Fatalf("RemoveAddress failed:", err)
|
||||
}
|
||||
|
||||
//
|
||||
// At this point, the address should still be
|
||||
// known by the NIC, but have its
|
||||
// kind = permanentExpired.
|
||||
//
|
||||
|
||||
// Add some other address with peb set to
|
||||
// FirstPrimaryEndpoint.
|
||||
if err := s.AddAddressWithOptions(1, fakeNetNumber, "\x03", stack.FirstPrimaryEndpoint); err != nil {
|
||||
t.Fatal("AddAddressWithOptions failed:", err)
|
||||
|
||||
}
|
||||
|
||||
// Add back the address we removed earlier and
|
||||
// make sure the new peb was respected.
|
||||
// (The address should just be promoted now).
|
||||
if err := s.AddAddressWithOptions(1, fakeNetNumber, "\x01", ps); err != nil {
|
||||
t.Fatal("AddAddressWithOptions failed:", err)
|
||||
}
|
||||
var primaryAddrs []tcpip.Address
|
||||
for _, pa := range s.NICInfo()[1].ProtocolAddresses {
|
||||
primaryAddrs = append(primaryAddrs, pa.AddressWithPrefix.Address)
|
||||
}
|
||||
var expectedList []tcpip.Address
|
||||
switch ps {
|
||||
case stack.FirstPrimaryEndpoint:
|
||||
expectedList = []tcpip.Address{
|
||||
"\x01",
|
||||
"\x03",
|
||||
}
|
||||
case stack.CanBePrimaryEndpoint:
|
||||
expectedList = []tcpip.Address{
|
||||
"\x03",
|
||||
"\x01",
|
||||
}
|
||||
case stack.NeverPrimaryEndpoint:
|
||||
expectedList = []tcpip.Address{
|
||||
"\x03",
|
||||
}
|
||||
}
|
||||
if !cmp.Equal(primaryAddrs, expectedList) {
|
||||
t.Fatalf("got NIC's primary addresses = %v, want = %v", primaryAddrs, expectedList)
|
||||
}
|
||||
|
||||
// Once we remove the other address, if the new
|
||||
// peb, ps, was NeverPrimaryEndpoint, no address
|
||||
// should be returned by a call to
|
||||
// GetMainNICAddress; else, our original address
|
||||
// should be returned.
|
||||
if err := s.RemoveAddress(1, "\x03"); err != nil {
|
||||
t.Fatalf("RemoveAddress failed:", err)
|
||||
}
|
||||
addr, err = s.GetMainNICAddress(1, fakeNetNumber)
|
||||
if err != nil {
|
||||
t.Fatal("s.GetMainNICAddress failed:", err)
|
||||
}
|
||||
if ps == stack.NeverPrimaryEndpoint {
|
||||
if want := (tcpip.AddressWithPrefix{}); addr != want {
|
||||
t.Fatalf("got GetMainNICAddress = %s, want = %s", addr, want)
|
||||
|
||||
}
|
||||
} else {
|
||||
if addr.Address != "\x01" {
|
||||
t.Fatalf("got GetMainNICAddress = %s, want = 1", addr.Address)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user