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:
Ghanan Gowripalan
2019-10-22 13:54:33 -07:00
committed by gVisor bot
parent 8720bd643e
commit c356fe2ebb
3 changed files with 178 additions and 9 deletions
+1
View File
@@ -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
View File
@@ -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
}
}
+137
View File
@@ -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)
}
}
})
}
}
}