github.com/imran-kn/cilium-fork@v1.6.9/pkg/ipcache/ipcache_test.go (about)

     1  // Copyright 2018 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build !privileged_tests
    16  
    17  package ipcache
    18  
    19  import (
    20  	"net"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"github.com/cilium/cilium/pkg/checker"
    25  	identityPkg "github.com/cilium/cilium/pkg/identity"
    26  	"github.com/cilium/cilium/pkg/source"
    27  
    28  	. "gopkg.in/check.v1"
    29  )
    30  
    31  // Hook up gocheck into the "go test" runner.
    32  type IPCacheTestSuite struct{}
    33  
    34  var _ = Suite(&IPCacheTestSuite{})
    35  
    36  func Test(t *testing.T) {
    37  	TestingT(t)
    38  }
    39  
    40  func (s *IPCacheTestSuite) TestIPCache(c *C) {
    41  	endpointIP := "10.0.0.15"
    42  	identity := (identityPkg.NumericIdentity(68))
    43  
    44  	// Assure sane state at start.
    45  	c.Assert(len(IPIdentityCache.ipToIdentityCache), Equals, 0)
    46  	c.Assert(len(IPIdentityCache.identityToIPCache), Equals, 0)
    47  
    48  	// Deletion of key that doesn't exist doesn't cause panic.
    49  	IPIdentityCache.Delete(endpointIP, source.KVStore)
    50  
    51  	IPIdentityCache.Upsert(endpointIP, nil, 0, Identity{
    52  		ID:     identity,
    53  		Source: source.KVStore,
    54  	})
    55  
    56  	// Assure both caches are updated..
    57  	c.Assert(len(IPIdentityCache.ipToIdentityCache), Equals, 1)
    58  	c.Assert(len(IPIdentityCache.identityToIPCache), Equals, 1)
    59  
    60  	cachedIdentity, exists := IPIdentityCache.LookupByIP(endpointIP)
    61  	c.Assert(exists, Equals, true)
    62  	c.Assert(cachedIdentity.ID, Equals, identity)
    63  	c.Assert(cachedIdentity.Source, Equals, source.KVStore)
    64  
    65  	// kubernetes source cannot update kvstore source
    66  	updated := IPIdentityCache.Upsert(endpointIP, nil, 0, Identity{
    67  		ID:     identity,
    68  		Source: source.Kubernetes,
    69  	})
    70  	c.Assert(updated, Equals, false)
    71  
    72  	IPIdentityCache.Upsert(endpointIP, nil, 0, Identity{
    73  		ID:     identity,
    74  		Source: source.KVStore,
    75  	})
    76  
    77  	// No duplicates.
    78  	c.Assert(len(IPIdentityCache.ipToIdentityCache), Equals, 1)
    79  	c.Assert(len(IPIdentityCache.identityToIPCache), Equals, 1)
    80  
    81  	IPIdentityCache.Delete(endpointIP, source.KVStore)
    82  
    83  	// Assure deletion occurs across both mappings.
    84  	c.Assert(len(IPIdentityCache.ipToIdentityCache), Equals, 0)
    85  	c.Assert(len(IPIdentityCache.identityToIPCache), Equals, 0)
    86  
    87  	_, exists = IPIdentityCache.LookupByIP(endpointIP)
    88  
    89  	c.Assert(exists, Equals, false)
    90  
    91  	IPIdentityCache.Upsert(endpointIP, nil, 0, Identity{
    92  		ID:     identity,
    93  		Source: source.KVStore,
    94  	})
    95  
    96  	newIdentity := identityPkg.NumericIdentity(69)
    97  	IPIdentityCache.Upsert(endpointIP, nil, 0, Identity{
    98  		ID:     newIdentity,
    99  		Source: source.KVStore,
   100  	})
   101  
   102  	// Ensure that update of cache with new identity doesn't keep old identity-to-ip
   103  	// mapping around.
   104  	_, exists = IPIdentityCache.LookupByIdentity(identity)
   105  	c.Assert(exists, Equals, false)
   106  
   107  	cachedIPSet, exists := IPIdentityCache.LookupByIdentity(newIdentity)
   108  	c.Assert(exists, Equals, true)
   109  	for cachedIP := range cachedIPSet {
   110  		c.Assert(cachedIP, Equals, endpointIP)
   111  	}
   112  
   113  	IPIdentityCache.Delete(endpointIP, source.KVStore)
   114  
   115  	// Assure deletion occurs across both mappings.
   116  	c.Assert(len(IPIdentityCache.ipToIdentityCache), Equals, 0)
   117  	c.Assert(len(IPIdentityCache.identityToIPCache), Equals, 0)
   118  
   119  	// Test mapping of multiple IPs to same identity.
   120  	endpointIPs := []string{"192.168.0.1", "20.3.75.3", "27.2.2.2", "127.0.0.1", "127.0.0.1"}
   121  	identities := []identityPkg.NumericIdentity{5, 67, 29, 29, 29}
   122  
   123  	for index := range endpointIPs {
   124  		IPIdentityCache.Upsert(endpointIPs[index], nil, 0, Identity{
   125  			ID:     identities[index],
   126  			Source: source.KVStore,
   127  		})
   128  		cachedIdentity, _ := IPIdentityCache.LookupByIP(endpointIPs[index])
   129  		c.Assert(cachedIdentity.ID, Equals, identities[index])
   130  	}
   131  
   132  	expectedIPList := map[string]struct{}{
   133  		"27.2.2.2":  {},
   134  		"127.0.0.1": {},
   135  	}
   136  
   137  	cachedEndpointIPs, _ := IPIdentityCache.LookupByIdentity(29)
   138  	c.Assert(reflect.DeepEqual(cachedEndpointIPs, expectedIPList), Equals, true)
   139  
   140  	IPIdentityCache.Delete("27.2.2.2", source.KVStore)
   141  
   142  	expectedIPList = map[string]struct{}{
   143  		"127.0.0.1": {},
   144  	}
   145  
   146  	cachedEndpointIPs, exists = IPIdentityCache.LookupByIdentity(29)
   147  	c.Assert(exists, Equals, true)
   148  	c.Assert(reflect.DeepEqual(cachedEndpointIPs, expectedIPList), Equals, true)
   149  
   150  	cachedIdentity, exists = IPIdentityCache.LookupByIP("127.0.0.1")
   151  	c.Assert(exists, Equals, true)
   152  	c.Assert(cachedIdentity.ID, Equals, identityPkg.NumericIdentity(29))
   153  
   154  	cachedIdentity, exists = IPIdentityCache.LookupByPrefix("127.0.0.1/32")
   155  	c.Assert(exists, Equals, true)
   156  	c.Assert(cachedIdentity.ID, Equals, identityPkg.NumericIdentity(29))
   157  
   158  	IPIdentityCache.Delete("127.0.0.1", source.KVStore)
   159  
   160  	_, exists = IPIdentityCache.LookupByIdentity(29)
   161  	c.Assert(exists, Equals, false)
   162  
   163  	_, exists = IPIdentityCache.LookupByPrefix("127.0.0.1/32")
   164  	c.Assert(exists, Equals, false)
   165  
   166  	// Clean up.
   167  	for index := range endpointIPs {
   168  		IPIdentityCache.Delete(endpointIPs[index], source.KVStore)
   169  		_, exists = IPIdentityCache.LookupByIP(endpointIPs[index])
   170  		c.Assert(exists, Equals, false)
   171  
   172  		_, exists = IPIdentityCache.LookupByIdentity(identities[index])
   173  		c.Assert(exists, Equals, false)
   174  	}
   175  
   176  	c.Assert(len(IPIdentityCache.ipToIdentityCache), Equals, 0)
   177  	c.Assert(len(IPIdentityCache.identityToIPCache), Equals, 0)
   178  
   179  }
   180  
   181  func (s *IPCacheTestSuite) TestKeyToIPNet(c *C) {
   182  	// Valid IPv6.
   183  	validIPv6Key := "cilium/state/ip/v1/default/f00d::a00:0:0:c164"
   184  
   185  	_, expectedIPv6, err := net.ParseCIDR("f00d::a00:0:0:c164/128")
   186  	c.Assert(err, IsNil)
   187  
   188  	ipv6, isHost, err := keyToIPNet(validIPv6Key)
   189  	c.Assert(ipv6, Not(IsNil))
   190  	c.Assert(err, IsNil)
   191  	c.Assert(isHost, Equals, true)
   192  	c.Assert(ipv6, checker.DeepEquals, expectedIPv6)
   193  
   194  	// Valid IPv6 prefix.
   195  	validIPv6Key = "cilium/state/ip/v1/default/f00d::a00:0:0:0/64"
   196  
   197  	_, expectedIPv6, err = net.ParseCIDR("f00d::a00:0:0:0/64")
   198  	c.Assert(err, IsNil)
   199  
   200  	ipv6, isHost, err = keyToIPNet(validIPv6Key)
   201  	c.Assert(ipv6, Not(IsNil))
   202  	c.Assert(err, IsNil)
   203  	c.Assert(isHost, Equals, false)
   204  	c.Assert(ipv6, checker.DeepEquals, expectedIPv6)
   205  
   206  	// Valid IPv4.
   207  	validIPv4Key := "cilium/state/ip/v1/default/10.0.114.197"
   208  	_, expectedIPv4, err := net.ParseCIDR("10.0.114.197/32")
   209  	c.Assert(err, IsNil)
   210  	ipv4, isHost, err := keyToIPNet(validIPv4Key)
   211  	c.Assert(ipv4, Not(IsNil))
   212  	c.Assert(err, IsNil)
   213  	c.Assert(isHost, Equals, true)
   214  	c.Assert(ipv4, checker.DeepEquals, expectedIPv4)
   215  
   216  	// Valid IPv4 prefix.
   217  	validIPv4Key = "cilium/state/ip/v1/default/10.0.114.0/24"
   218  	_, expectedIPv4, err = net.ParseCIDR("10.0.114.0/24")
   219  	c.Assert(err, IsNil)
   220  	ipv4, isHost, err = keyToIPNet(validIPv4Key)
   221  	c.Assert(ipv4, Not(IsNil))
   222  	c.Assert(err, IsNil)
   223  	c.Assert(isHost, Equals, false)
   224  	c.Assert(ipv4, checker.DeepEquals, expectedIPv4)
   225  
   226  	// Invalid prefix.
   227  	invalidPrefixKey := "cilium/state/foobar/v1/default/f00d::a00:0:0:c164"
   228  	nilIP, isHost, err := keyToIPNet(invalidPrefixKey)
   229  	c.Assert(nilIP, IsNil)
   230  	c.Assert(err, Not(IsNil))
   231  	c.Assert(isHost, Equals, false)
   232  
   233  	// Invalid IP in key.
   234  	invalidIPKey := "cilium/state/ip/v1/default/10.abfd.114.197"
   235  	nilIP, isHost, err = keyToIPNet(invalidIPKey)
   236  	c.Assert(nilIP, IsNil)
   237  	c.Assert(err, Not(IsNil))
   238  	c.Assert(isHost, Equals, false)
   239  
   240  	// Invalid CIDR.
   241  	invalidIPKey = "cilium/state/ip/v1/default/192.0.2.3/54"
   242  	nilIP, isHost, err = keyToIPNet(invalidIPKey)
   243  	c.Assert(nilIP, IsNil)
   244  	c.Assert(err, Not(IsNil))
   245  	c.Assert(isHost, Equals, false)
   246  }
   247  
   248  type dummyListener struct {
   249  	entries map[string]identityPkg.NumericIdentity
   250  	ipc     *IPCache
   251  }
   252  
   253  func newDummyListener(ipc *IPCache) *dummyListener {
   254  	return &dummyListener{
   255  		ipc: ipc,
   256  	}
   257  }
   258  
   259  func (dl *dummyListener) OnIPIdentityCacheChange(modType CacheModification,
   260  	cidr net.IPNet, oldHostIP, newHostIP net.IP, oldID *identityPkg.NumericIdentity,
   261  	newID identityPkg.NumericIdentity, encryptKey uint8) {
   262  
   263  	switch modType {
   264  	case Upsert:
   265  		dl.entries[cidr.String()] = newID
   266  	default:
   267  		// Ignore, for simplicity we just clear the cache every time
   268  	}
   269  }
   270  
   271  func (dl *dummyListener) OnIPIdentityCacheGC() {}
   272  
   273  func (dl *dummyListener) ExpectMapping(c *C, targetIP string, targetIdentity identityPkg.NumericIdentity) {
   274  	// Identity lookup directly shows the expected mapping
   275  	identity, exists := dl.ipc.LookupByPrefix(targetIP)
   276  	c.Assert(exists, Equals, true)
   277  	c.Assert(identity.ID, Equals, targetIdentity)
   278  
   279  	// Dump reliably supplies the IP once and only the pod identity.
   280  	dl.entries = make(map[string]identityPkg.NumericIdentity)
   281  	dl.ipc.DumpToListenerLocked(dl)
   282  	c.Assert(dl.entries, checker.DeepEquals,
   283  		map[string]identityPkg.NumericIdentity{
   284  			targetIP: targetIdentity,
   285  		})
   286  }
   287  
   288  func (s *IPCacheTestSuite) TestIPCacheShadowing(c *C) {
   289  	endpointIP := "10.0.0.15"
   290  	cidrOverlap := "10.0.0.15/32"
   291  	epIdentity := (identityPkg.NumericIdentity(68))
   292  	cidrIdentity := (identityPkg.NumericIdentity(202))
   293  	ipc := NewIPCache()
   294  
   295  	// Assure sane state at start.
   296  	c.Assert(ipc.ipToIdentityCache, checker.DeepEquals, map[string]Identity{})
   297  	c.Assert(ipc.identityToIPCache, checker.DeepEquals, map[identityPkg.NumericIdentity]map[string]struct{}{})
   298  
   299  	// Upsert overlapping identities for the IP. Pod identity takes precedence.
   300  	ipc.Upsert(endpointIP, nil, 0, Identity{
   301  		ID:     epIdentity,
   302  		Source: source.KVStore,
   303  	})
   304  	ipc.Upsert(cidrOverlap, nil, 0, Identity{
   305  		ID:     cidrIdentity,
   306  		Source: source.Generated,
   307  	})
   308  	ipcache := newDummyListener(ipc)
   309  	ipcache.ExpectMapping(c, cidrOverlap, epIdentity)
   310  
   311  	// Deleting pod identity shows that cidr identity is now used.
   312  	ipc.Delete(endpointIP, source.KVStore)
   313  	ipcache.ExpectMapping(c, cidrOverlap, cidrIdentity)
   314  
   315  	// Reinsert of pod IP should shadow the CIDR identity again.
   316  	ipc.Upsert(endpointIP, nil, 0, Identity{
   317  		ID:     epIdentity,
   318  		Source: source.KVStore,
   319  	})
   320  	ipcache.ExpectMapping(c, cidrOverlap, epIdentity)
   321  
   322  	// Deletion of the shadowed identity should not change the output.
   323  	ipc.Delete(cidrOverlap, source.Generated)
   324  	ipcache.ExpectMapping(c, cidrOverlap, epIdentity)
   325  
   326  	// Clean up
   327  	ipc.Delete(endpointIP, source.KVStore)
   328  	_, exists := ipc.LookupByPrefix(cidrOverlap)
   329  	c.Assert(exists, Equals, false)
   330  }