github.com/cilium/cilium@v1.16.2/pkg/fqdn/dnsproxy/proxy_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package dnsproxy 5 6 import ( 7 "bytes" 8 "context" 9 "encoding/json" 10 "fmt" 11 "net" 12 "net/netip" 13 "os" 14 "runtime" 15 "strconv" 16 "strings" 17 "sync" 18 "testing" 19 "time" 20 21 "github.com/cilium/dns" 22 "github.com/stretchr/testify/require" 23 "golang.org/x/exp/maps" 24 "sigs.k8s.io/yaml" 25 26 datapath "github.com/cilium/cilium/pkg/datapath/types" 27 "github.com/cilium/cilium/pkg/endpoint" 28 "github.com/cilium/cilium/pkg/fqdn/restore" 29 "github.com/cilium/cilium/pkg/identity" 30 "github.com/cilium/cilium/pkg/identity/cache" 31 "github.com/cilium/cilium/pkg/ipcache" 32 v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 33 "github.com/cilium/cilium/pkg/labels" 34 monitorAPI "github.com/cilium/cilium/pkg/monitor/api" 35 "github.com/cilium/cilium/pkg/option" 36 "github.com/cilium/cilium/pkg/policy" 37 "github.com/cilium/cilium/pkg/policy/api" 38 "github.com/cilium/cilium/pkg/source" 39 "github.com/cilium/cilium/pkg/testutils" 40 testidentity "github.com/cilium/cilium/pkg/testutils/identity" 41 testipcache "github.com/cilium/cilium/pkg/testutils/ipcache" 42 "github.com/cilium/cilium/pkg/u8proto" 43 ) 44 45 type DNSProxyTestSuite struct { 46 repo *policy.Repository 47 dnsTCPClient *dns.Client 48 dnsServer *dns.Server 49 proxy *DNSProxy 50 restoring bool 51 } 52 53 func setupDNSProxyTestSuite(tb testing.TB) *DNSProxyTestSuite { 54 testutils.PrivilegedTest(tb) 55 56 s := &DNSProxyTestSuite{} 57 58 // Add these identities 59 wg := &sync.WaitGroup{} 60 testSelectorCache.UpdateIdentities(identity.IdentityMap{ 61 dstID1: labels.Labels{"Dst1": labels.NewLabel("Dst1", "test", labels.LabelSourceK8s)}.LabelArray(), 62 dstID2: labels.Labels{"Dst2": labels.NewLabel("Dst2", "test", labels.LabelSourceK8s)}.LabelArray(), 63 dstID3: labels.Labels{"Dst3": labels.NewLabel("Dst3", "test", labels.LabelSourceK8s)}.LabelArray(), 64 dstID4: labels.Labels{"Dst4": labels.NewLabel("Dst4", "test", labels.LabelSourceK8s)}.LabelArray(), 65 }, nil, wg) 66 wg.Wait() 67 68 s.repo = policy.NewPolicyRepository(nil, nil, nil) 69 s.dnsTCPClient = &dns.Client{Net: "tcp", Timeout: time.Second, SingleInflight: true} 70 s.dnsServer = setupServer(tb) 71 require.NotNil(tb, s.dnsServer, "unable to setup DNS server") 72 dnsProxyConfig := DNSProxyConfig{ 73 Address: "", 74 Port: 0, 75 IPv4: true, 76 IPv6: true, 77 EnableDNSCompression: true, 78 MaxRestoreDNSIPs: 1000, 79 ConcurrencyLimit: 0, 80 ConcurrencyGracePeriod: 0, 81 } 82 proxy, err := StartDNSProxy(dnsProxyConfig, // any address, any port, enable ipv4, enable ipv6, enable compression, max 1000 restore IPs 83 // LookupEPByIP 84 func(ip netip.Addr) (*endpoint.Endpoint, error) { 85 if s.restoring { 86 return nil, fmt.Errorf("No EPs available when restoring") 87 } 88 return endpoint.NewTestEndpointWithState(tb, s, s, testipcache.NewMockIPCache(), &endpoint.FakeEndpointProxy{}, testidentity.NewMockIdentityAllocator(nil), uint16(epID1), endpoint.StateReady), nil 89 }, 90 // LookupSecIDByIP 91 func(ip netip.Addr) (ipcache.Identity, bool) { 92 DNSServerListenerAddr := (s.dnsServer.Listener.Addr()).(*net.TCPAddr) 93 switch { 94 case ip.String() == DNSServerListenerAddr.IP.String(): 95 ident := ipcache.Identity{ 96 ID: dstID1, 97 Source: source.Unspec, 98 } 99 return ident, true 100 default: 101 ident := ipcache.Identity{ 102 ID: dstID2, 103 Source: source.Unspec, 104 } 105 return ident, true 106 } 107 }, 108 // LookupIPsBySecID 109 func(nid identity.NumericIdentity) []string { 110 DNSServerListenerAddr := (s.dnsServer.Listener.Addr()).(*net.TCPAddr) 111 switch nid { 112 case dstID1: 113 return []string{DNSServerListenerAddr.IP.String()} 114 case dstID2: 115 return []string{"127.0.0.1", "127.0.0.2"} 116 default: 117 return nil 118 } 119 }, 120 // NotifyOnDNSMsg 121 func(lookupTime time.Time, ep *endpoint.Endpoint, epIPPort string, serverID identity.NumericIdentity, dstAddr string, msg *dns.Msg, protocol string, allowed bool, stat *ProxyRequestContext) error { 122 return nil 123 }, 124 ) 125 require.Nil(tb, err, "error starting DNS Proxy") 126 s.proxy = proxy 127 128 // This is here because Listener or Listeer.Addr() was nil. The 129 // lookupTargetDNSServer function doesn't need to change the target. 130 require.NotNil(tb, s.dnsServer.Listener, "DNS server missing a Listener") 131 DNSServerListenerAddr := (s.dnsServer.Listener.Addr()).(*net.TCPAddr) 132 require.NotNil(tb, DNSServerListenerAddr, "DNS server missing a Listener address") 133 s.proxy.lookupTargetDNSServer = func(w dns.ResponseWriter) (serverIP net.IP, serverPortProto restore.PortProto, addrStr string, err error) { 134 return DNSServerListenerAddr.IP, restore.MakeV2PortProto(uint16(DNSServerListenerAddr.Port), uint8(u8proto.UDP)), DNSServerListenerAddr.String(), nil 135 } 136 dstPortProto = restore.MakeV2PortProto(uint16(DNSServerListenerAddr.Port), udpProto) 137 138 tb.Cleanup(func() { 139 for epID := range s.proxy.allowed { 140 for pp := range s.proxy.allowed[epID] { 141 s.proxy.UpdateAllowed(epID, pp, nil) 142 } 143 } 144 for epID := range s.proxy.restored { 145 s.proxy.RemoveRestoredRules(uint16(epID)) 146 } 147 if len(s.proxy.cache) > 0 { 148 tb.Error("cache not fully empty after removing all rules. Possible memory leak found.") 149 } 150 s.proxy.SetRejectReply(option.FQDNProxyDenyWithRefused) 151 s.dnsServer.Listener.Close() 152 for _, s := range s.proxy.DNSServers { 153 s.Shutdown() 154 } 155 }) 156 157 return s 158 } 159 160 func (s *DNSProxyTestSuite) GetPolicyRepository() *policy.Repository { 161 return s.repo 162 } 163 164 func (s *DNSProxyTestSuite) GetProxyPort(string) (uint16, error) { 165 return 0, nil 166 } 167 168 func (s *DNSProxyTestSuite) QueueEndpointBuild(ctx context.Context, epID uint64) (func(), error) { 169 return nil, nil 170 } 171 172 func (s *DNSProxyTestSuite) GetCompilationLock() datapath.CompilationLock { 173 return nil 174 } 175 176 func (s *DNSProxyTestSuite) GetCIDRPrefixLengths() (s6, s4 []int) { 177 return nil, nil 178 } 179 180 func (s *DNSProxyTestSuite) SendNotification(msg monitorAPI.AgentNotifyMessage) error { 181 return nil 182 } 183 184 func (s *DNSProxyTestSuite) Datapath() datapath.Datapath { 185 return nil 186 } 187 188 func (s *DNSProxyTestSuite) GetDNSRules(epID uint16) restore.DNSRules { 189 return nil 190 } 191 192 func (s *DNSProxyTestSuite) RemoveRestoredDNSRules(epID uint16) { 193 } 194 195 func setupServer(tb testing.TB) (dnsServer *dns.Server) { 196 waitOnListen := make(chan struct{}) 197 dnsServer = &dns.Server{Addr: ":0", Net: "tcp", NotifyStartedFunc: func() { close(waitOnListen) }} 198 go dnsServer.ListenAndServe() 199 dns.HandleFunc(".", serveDNS) 200 201 select { 202 case <-waitOnListen: 203 return dnsServer 204 205 case <-time.After(10 * time.Second): 206 tb.Error("DNS server did not start listening") 207 } 208 209 return nil 210 } 211 212 func serveDNS(w dns.ResponseWriter, r *dns.Msg) { 213 m := new(dns.Msg) 214 m.SetReply(r) 215 216 retARR, err := dns.NewRR(m.Question[0].Name + " 60 IN A 1.1.1.1") 217 if err != nil { 218 panic(err) 219 } 220 m.Answer = append(m.Answer, retARR) 221 222 w.WriteMsg(m) 223 } 224 225 type DummySelectorCacheUser struct{} 226 227 func (d *DummySelectorCacheUser) IdentitySelectionUpdated(selector policy.CachedSelector, added, deleted []identity.NumericIdentity) { 228 } 229 230 // Setup identities, ports and endpoint IDs we will need 231 var ( 232 cacheAllocator = cache.NewCachingIdentityAllocator(&testidentity.IdentityAllocatorOwnerMock{}) 233 testSelectorCache = policy.NewSelectorCache(cacheAllocator.GetIdentityCache()) 234 dummySelectorCacheUser = &DummySelectorCacheUser{} 235 DstID1Selector = api.NewESFromLabels(labels.ParseSelectLabel("k8s:Dst1=test")) 236 cachedDstID1Selector, _ = testSelectorCache.AddIdentitySelector(dummySelectorCacheUser, nil, DstID1Selector) 237 DstID2Selector = api.NewESFromLabels(labels.ParseSelectLabel("k8s:Dst2=test")) 238 cachedDstID2Selector, _ = testSelectorCache.AddIdentitySelector(dummySelectorCacheUser, nil, DstID2Selector) 239 DstID3Selector = api.NewESFromLabels(labels.ParseSelectLabel("k8s:Dst3=test")) 240 cachedDstID3Selector, _ = testSelectorCache.AddIdentitySelector(dummySelectorCacheUser, nil, DstID3Selector) 241 DstID4Selector = api.NewESFromLabels(labels.ParseSelectLabel("k8s:Dst4=test")) 242 cachedDstID4Selector, _ = testSelectorCache.AddIdentitySelector(dummySelectorCacheUser, nil, DstID4Selector) 243 244 cachedWildcardSelector, _ = testSelectorCache.AddIdentitySelector(dummySelectorCacheUser, nil, api.WildcardEndpointSelector) 245 246 epID1 = uint64(111) 247 epID2 = uint64(222) 248 epID3 = uint64(333) 249 dstID1 = identity.NumericIdentity(1001) 250 dstID2 = identity.NumericIdentity(2002) 251 dstID3 = identity.NumericIdentity(3003) 252 dstID4 = identity.NumericIdentity(4004) 253 dstPortProto = restore.MakeV2PortProto(53, udpProto) // Set below when we setup the server! 254 udpProtoPort53 = dstPortProto 255 udpProtoPort54 = restore.MakeV2PortProto(54, udpProto) 256 udpProtoPort8053 = restore.MakeV2PortProto(8053, udpProto) 257 tcpProtoPort53 = restore.MakeV2PortProto(53, tcpProto) 258 ) 259 260 func TestRejectFromDifferentEndpoint(t *testing.T) { 261 s := setupDNSProxyTestSuite(t) 262 263 name := "cilium.io." 264 l7map := policy.L7DataMap{ 265 cachedDstID1Selector: &policy.PerSelectorPolicy{ 266 L7Rules: api.L7Rules{ 267 DNS: []api.PortRuleDNS{{MatchName: name}}, 268 }, 269 }, 270 } 271 query := name 272 273 // Reject a query from not endpoint 1 274 err := s.proxy.UpdateAllowed(epID1, dstPortProto, l7map) 275 require.NoError(t, err, "Could not update with rules") 276 allowed, err := s.proxy.CheckAllowed(epID2, dstPortProto, dstID1, nil, query) 277 require.NoError(t, err, "Error when checking allowed") 278 require.False(t, allowed, "request was not rejected when it should be blocked") 279 } 280 281 func TestAcceptFromMatchingEndpoint(t *testing.T) { 282 s := setupDNSProxyTestSuite(t) 283 284 name := "cilium.io." 285 l7map := policy.L7DataMap{ 286 cachedDstID1Selector: &policy.PerSelectorPolicy{ 287 L7Rules: api.L7Rules{ 288 DNS: []api.PortRuleDNS{{MatchName: name}}, 289 }, 290 }, 291 } 292 query := name 293 294 // accept a query that matches from endpoint1 295 err := s.proxy.UpdateAllowed(epID1, dstPortProto, l7map) 296 require.NoError(t, err, "Could not update with rules") 297 allowed, err := s.proxy.CheckAllowed(epID1, dstPortProto, dstID1, nil, query) 298 require.NoError(t, err, "Error when checking allowed") 299 require.True(t, allowed, "request was rejected when it should be allowed") 300 } 301 302 func TestAcceptNonRegex(t *testing.T) { 303 s := setupDNSProxyTestSuite(t) 304 305 name := "simple.io." 306 l7map := policy.L7DataMap{ 307 cachedDstID1Selector: &policy.PerSelectorPolicy{ 308 L7Rules: api.L7Rules{ 309 DNS: []api.PortRuleDNS{{MatchName: name}}, 310 }, 311 }, 312 } 313 query := name 314 315 // accept a query that matches from endpoint1 316 err := s.proxy.UpdateAllowed(epID1, dstPortProto, l7map) 317 require.Equal(t, nil, err, "Could not update with rules") 318 allowed, err := s.proxy.CheckAllowed(epID1, dstPortProto, dstID1, nil, query) 319 require.Equal(t, nil, err, "Error when checking allowed") 320 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 321 } 322 323 func TestRejectNonRegex(t *testing.T) { 324 s := setupDNSProxyTestSuite(t) 325 326 name := "cilium.io." 327 l7map := policy.L7DataMap{ 328 cachedDstID1Selector: &policy.PerSelectorPolicy{ 329 L7Rules: api.L7Rules{ 330 DNS: []api.PortRuleDNS{{MatchName: name}}, 331 }, 332 }, 333 } 334 query := "ciliumXio." 335 336 // reject a query for a non-regex where a . is different (i.e. ensure simple FQDNs treat . as .) 337 err := s.proxy.UpdateAllowed(epID1, dstPortProto, l7map) 338 require.Equal(t, nil, err, "Could not update with rules") 339 allowed, err := s.proxy.CheckAllowed(epID1, dstPortProto, dstID1, nil, query) 340 require.Equal(t, nil, err, "Error when checking allowed") 341 require.Equal(t, false, allowed, "request was not rejected when it should be blocked") 342 } 343 344 func (s *DNSProxyTestSuite) requestRejectNonMatchingRefusedResponse(t *testing.T) *dns.Msg { 345 name := "cilium.io." 346 l7map := policy.L7DataMap{ 347 cachedDstID1Selector: &policy.PerSelectorPolicy{ 348 L7Rules: api.L7Rules{ 349 DNS: []api.PortRuleDNS{{MatchName: name}}, 350 }, 351 }, 352 } 353 query := "notcilium.io." 354 355 err := s.proxy.UpdateAllowed(epID1, dstPortProto, l7map) 356 require.Equal(t, nil, err, "Could not update with rules") 357 allowed, err := s.proxy.CheckAllowed(epID1, dstPortProto, dstID1, nil, query) 358 require.Equal(t, nil, err, "Error when checking allowed") 359 require.Equal(t, false, allowed, "request was not rejected when it should be blocked") 360 361 request := new(dns.Msg) 362 request.SetQuestion(query, dns.TypeA) 363 return request 364 } 365 366 func TestRejectNonMatchingRefusedResponseWithNameError(t *testing.T) { 367 s := setupDNSProxyTestSuite(t) 368 369 request := s.requestRejectNonMatchingRefusedResponse(t) 370 371 // reject a query with NXDomain 372 s.proxy.SetRejectReply(option.FQDNProxyDenyWithNameError) 373 response, _, err := s.dnsTCPClient.Exchange(request, s.proxy.DNSServers[0].Listener.Addr().String()) 374 require.NoError(t, err, "DNS request from test client failed when it should succeed") 375 require.Equal(t, dns.RcodeNameError, response.Rcode, "DNS request from test client was not rejected when it should be blocked") 376 } 377 378 func TestRejectNonMatchingRefusedResponseWithRefused(t *testing.T) { 379 s := setupDNSProxyTestSuite(t) 380 381 request := s.requestRejectNonMatchingRefusedResponse(t) 382 383 // reject a query with Refused 384 s.proxy.SetRejectReply(option.FQDNProxyDenyWithRefused) 385 response, _, err := s.dnsTCPClient.Exchange(request, s.proxy.DNSServers[0].Listener.Addr().String()) 386 require.NoError(t, err, "DNS request from test client failed when it should succeed") 387 require.Equal(t, dns.RcodeRefused, response.Rcode, "DNS request from test client was not rejected when it should be blocked") 388 } 389 390 func TestRespondViaCorrectProtocol(t *testing.T) { 391 s := setupDNSProxyTestSuite(t) 392 393 // Respond with an actual answer for the query. This also tests that the 394 // connection was forwarded via the correct protocol (tcp/udp) because we 395 // connet with TCP, and the server only listens on TCP. 396 397 name := "cilium.io." 398 l7map := policy.L7DataMap{ 399 cachedDstID1Selector: &policy.PerSelectorPolicy{ 400 L7Rules: api.L7Rules{ 401 DNS: []api.PortRuleDNS{{MatchName: name}}, 402 }, 403 }, 404 } 405 query := name 406 407 err := s.proxy.UpdateAllowed(epID1, dstPortProto, l7map) 408 require.Equal(t, nil, err, "Could not update with rules") 409 allowed, err := s.proxy.CheckAllowed(epID1, dstPortProto, dstID1, nil, query) 410 require.Equal(t, nil, err, "Error when checking allowed") 411 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 412 413 request := new(dns.Msg) 414 request.SetQuestion(query, dns.TypeA) 415 response, rtt, err := s.dnsTCPClient.Exchange(request, s.proxy.DNSServers[0].Listener.Addr().String()) 416 require.NoErrorf(t, err, "DNS request from test client failed when it should succeed (RTT: %v)", rtt) 417 require.Equal(t, 1, len(response.Answer), "Proxy returned incorrect number of answer RRs %s", response) 418 require.Equal(t, "cilium.io.\t60\tIN\tA\t1.1.1.1", response.Answer[0].String(), "Proxy returned incorrect RRs") 419 } 420 421 func TestRespondMixedCaseInRequestResponse(t *testing.T) { 422 s := setupDNSProxyTestSuite(t) 423 424 // Test that mixed case query is allowed out and then back in to support 425 // high-order-bit query uniqueing schemes (and a data exfiltration 426 // vector :( ) 427 name := "cilium.io." 428 l7map := policy.L7DataMap{ 429 cachedDstID1Selector: &policy.PerSelectorPolicy{ 430 L7Rules: api.L7Rules{ 431 DNS: []api.PortRuleDNS{{MatchName: name}}, 432 }, 433 }, 434 } 435 query := "CILIUM.io." 436 437 err := s.proxy.UpdateAllowed(epID1, dstPortProto, l7map) 438 require.Equal(t, nil, err, "Could not update with rules") 439 allowed, err := s.proxy.CheckAllowed(epID1, dstPortProto, dstID1, nil, query) 440 require.Equal(t, nil, err, "Error when checking allowed") 441 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 442 443 request := new(dns.Msg) 444 request.SetQuestion(query, dns.TypeA) 445 response, _, err := s.dnsTCPClient.Exchange(request, s.proxy.DNSServers[0].Listener.Addr().String()) 446 require.NoError(t, err, "DNS request from test client failed when it should succeed") 447 require.Equal(t, 1, len(response.Answer), "Proxy returned incorrect number of answer RRs %s", response) 448 require.Equal(t, "CILIUM.io.\t60\tIN\tA\t1.1.1.1", response.Answer[0].String(), "Proxy returned incorrect RRs") 449 450 request.SetQuestion("ciliuM.io.", dns.TypeA) 451 response, _, err = s.dnsTCPClient.Exchange(request, s.proxy.DNSServers[0].Listener.Addr().String()) 452 require.NoError(t, err, "DNS request from test client failed when it should succeed") 453 require.Equal(t, 1, len(response.Answer), "Proxy returned incorrect number of answer RRs %+v", response.Answer) 454 require.Equal(t, "ciliuM.io.\t60\tIN\tA\t1.1.1.1", response.Answer[0].String(), "Proxy returned incorrect RRs") 455 } 456 457 func TestCheckNoRules(t *testing.T) { 458 s := setupDNSProxyTestSuite(t) 459 460 name := "cilium.io." 461 l7map := policy.L7DataMap{ 462 cachedDstID1Selector: &policy.PerSelectorPolicy{ 463 L7Rules: api.L7Rules{}, 464 }, 465 } 466 query := name 467 468 err := s.proxy.UpdateAllowed(epID1, dstPortProto, l7map) 469 require.Equal(t, nil, err, "Error when inserting rules") 470 471 allowed, err := s.proxy.CheckAllowed(epID1, dstPortProto, dstID1, nil, query) 472 require.Equal(t, nil, err, "Error when checking allowed") 473 474 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 475 476 l7map = policy.L7DataMap{ 477 cachedDstID1Selector: &policy.PerSelectorPolicy{ 478 L7Rules: api.L7Rules{ 479 DNS: []api.PortRuleDNS{}, 480 }, 481 }, 482 } 483 err = s.proxy.UpdateAllowed(epID1, dstPortProto, l7map) 484 require.Equal(t, nil, err, "Error when inserting rules") 485 486 allowed, err = s.proxy.CheckAllowed(epID1, dstPortProto, dstID1, nil, query) 487 require.Equal(t, nil, err, "Error when checking allowed") 488 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 489 } 490 491 func TestCheckAllowedTwiceRemovedOnce(t *testing.T) { 492 s := setupDNSProxyTestSuite(t) 493 494 name := "cilium.io." 495 l7map := policy.L7DataMap{ 496 cachedDstID1Selector: &policy.PerSelectorPolicy{ 497 L7Rules: api.L7Rules{ 498 DNS: []api.PortRuleDNS{{MatchName: name}}, 499 }, 500 }, 501 } 502 query := name 503 504 // Add the rule twice 505 err := s.proxy.UpdateAllowed(epID1, dstPortProto, l7map) 506 require.Equal(t, nil, err, "Could not update with rules") 507 err = s.proxy.UpdateAllowed(epID1, dstPortProto, l7map) 508 require.Equal(t, nil, err, "Could not update with rules") 509 allowed, err := s.proxy.CheckAllowed(epID1, dstPortProto, dstID1, nil, query) 510 require.Equal(t, nil, err, "Error when checking allowed") 511 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 512 513 // Delete once, it should reject 514 err = s.proxy.UpdateAllowed(epID1, dstPortProto, nil) 515 require.Equal(t, nil, err, "Could not update with rules") 516 allowed, err = s.proxy.CheckAllowed(epID1, dstPortProto, dstID1, nil, query) 517 require.Equal(t, nil, err, "Error when checking allowed") 518 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 519 520 // Delete once, it should reject and not crash 521 err = s.proxy.UpdateAllowed(epID1, dstPortProto, nil) 522 require.Equal(t, nil, err, "Could not update with rules") 523 allowed, err = s.proxy.CheckAllowed(epID1, dstPortProto, dstID1, nil, query) 524 require.Equal(t, nil, err, "Error when checking allowed") 525 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 526 } 527 528 func TestFullPathDependence(t *testing.T) { 529 s := setupDNSProxyTestSuite(t) 530 531 // Test that we consider each of endpoint ID, destination SecID (via the 532 // selector in L7DataMap), destination port (set in the redirect itself) and 533 // the DNS name. 534 // The rules approximate: 535 // +------+--------+---------+----------+----------------+ 536 // | From | To | DstPort | Protocol | DNSNames | 537 // +======+========+=========+===========================+ 538 // | EP1 | DstID1 | 53 | UDP | *.ubuntu.com | 539 // | EP1 | DstID1 | 53 | TCP | sub.ubuntu.com | 540 // | EP1 | DstID1 | 53 | UDP | aws.amazon.com | 541 // | EP1 | DstID2 | 53 | UDP | cilium.io | 542 // | EP1 | * | 54 | UDP | example.com | 543 // | EP3 | DstID1 | 53 | UDP | example.com | 544 // | EP3 | DstID3 | 53 | UDP | * | 545 // | EP3 | DstID3 | 53 | TCP | example.com | 546 // | EP3 | DstID4 | 53 | UDP | nil | 547 // +------+--------+---------+---------------------------+ 548 // 549 // Cases: 550 // +------+-------+--------+------+---------------------------+----------+----------------------------------------------------------------+ 551 // | Case | From | To | Port | Protocol | Query | Outcome | Reason | 552 // +------+-------+--------+------+----------+----------------+----------+----------------------------------------------------------------+ 553 // | 1 | EPID1 | DstID1 | 53 | UDP | www.ubuntu.com | Allowed | | 554 // | 2 | EPID1 | DstID1 | 53 | TCP | www.ubuntu.com | Rejected | Protocol TCP only allows "sub.ubuntu.com" | 555 // | 3 | EPID1 | DstID1 | 53 | TCP | sub.ubuntu.com | Allowed | | 556 // | 4 | EPID1 | DstID1 | 53 | UDP | sub.ubuntu.com | Allowed | | 557 // | 5 | EPID1 | DstID1 | 54 | UDP | cilium.io | Rejected | Port 54 only allows example.com | 558 // | 6 | EPID1 | DstID2 | 53 | UDP | cilium.io | Allowed | | 559 // | 7 | EPID1 | DstID2 | 53 | UDP | aws.amazon.com | Rejected | Only cilium.io is allowed with DstID2 | 560 // | 8 | EPID1 | DstID1 | 54 | UDP | example.com | Allowed | | 561 // | 9 | EPID2 | DstID1 | 53 | UDP | cilium.io | Rejected | EPID2 is not allowed as a source by any policy | 562 // | 10 | EPID3 | DstID1 | 53 | UDP | example.com | Allowed | | 563 // | 11 | EPID3 | DstID1 | 53 | UDP | aws.amazon.com | Rejected | EPID3 is only allowed to ask DstID1 on Port 53 for example.com | 564 // | 12 | EPID3 | DstID1 | 54 | UDP | example.com | Rejected | EPID3 is only allowed to ask DstID1 on Port 53 for example.com | 565 // | 13 | EPID3 | DstID2 | 53 | UDP | example.com | Rejected | EPID3 is only allowed to ask DstID1 on Port 53 for example.com | 566 // | 14 | EPID3 | DstID3 | 53 | UDP | example.com | Allowed | Allowed due to wildcard match pattern | 567 // | 15 | EPID3 | DstID3 | 53 | TCP | example.com | Allowed | | 568 // | 16 | EPID3 | DstID3 | 53 | TCP | amazon.com | Rejected | TCP protocol only allows "example.com" | 569 // | 17 | EPID3 | DstID4 | 53 | TCP | example.com | Rejected | "example.com" only allowed for DstID3 | 570 // | 18 | EPID3 | DstID4 | 53 | UDP | example.com | Allowed | Allowed due to a nil rule | 571 // +------+-------+--------+------+----------------+----------+----------+----------------------------------------------------------------+ 572 573 // Setup rules 574 // | EP1 | DstID1 | 53 | UDP | *.ubuntu.com | 575 // | EP1 | DstID1 | 53 | UDP | aws.amazon.com | 576 // | EP1 | DstID2 | 53 | UDP | cilium.io | 577 err := s.proxy.UpdateAllowed(epID1, udpProtoPort53, policy.L7DataMap{ 578 cachedDstID1Selector: &policy.PerSelectorPolicy{ 579 L7Rules: api.L7Rules{ 580 DNS: []api.PortRuleDNS{ 581 {MatchPattern: "*.ubuntu.com."}, 582 {MatchPattern: "aws.amazon.com."}, 583 }, 584 }, 585 }, 586 cachedDstID2Selector: &policy.PerSelectorPolicy{ 587 L7Rules: api.L7Rules{ 588 DNS: []api.PortRuleDNS{ 589 {MatchPattern: "cilium.io."}, 590 }, 591 }, 592 }, 593 }) 594 require.Equal(t, nil, err, "Could not update with port 53 rules") 595 596 // | EP1 | DstID1 | 53 | TCP | sub.ubuntu.com | 597 err = s.proxy.UpdateAllowed(epID1, tcpProtoPort53, policy.L7DataMap{ 598 cachedDstID1Selector: &policy.PerSelectorPolicy{ 599 L7Rules: api.L7Rules{ 600 DNS: []api.PortRuleDNS{ 601 {MatchPattern: "sub.ubuntu.com."}, 602 }, 603 }, 604 }, 605 }) 606 require.Equal(t, nil, err, "Could not update with rules") 607 608 // | EP1 | DstID1 | 54 | UDP | example.com | 609 err = s.proxy.UpdateAllowed(epID1, udpProtoPort54, policy.L7DataMap{ 610 cachedWildcardSelector: &policy.PerSelectorPolicy{ 611 L7Rules: api.L7Rules{ 612 DNS: []api.PortRuleDNS{ 613 {MatchPattern: "example.com."}, 614 }, 615 }, 616 }, 617 }) 618 require.Equal(t, nil, err, "Could not update with rules") 619 620 // | EP3 | DstID1 | 53 | UDP | example.com | 621 // | EP3 | DstID3 | 53 | UDP | * | 622 // | EP3 | DstID4 | 53 | UDP | nil | 623 err = s.proxy.UpdateAllowed(epID3, udpProtoPort53, policy.L7DataMap{ 624 cachedDstID1Selector: &policy.PerSelectorPolicy{ 625 L7Rules: api.L7Rules{ 626 DNS: []api.PortRuleDNS{ 627 {MatchPattern: "example.com."}, 628 }, 629 }, 630 }, 631 cachedDstID3Selector: &policy.PerSelectorPolicy{ 632 L7Rules: api.L7Rules{ 633 DNS: []api.PortRuleDNS{ 634 {MatchPattern: "*"}, 635 }, 636 }, 637 }, 638 cachedDstID4Selector: nil, 639 }) 640 require.Equal(t, nil, err, "Could not update with rules") 641 642 // | EP3 | DstID3 | 53 | TCP | example.com | 643 err = s.proxy.UpdateAllowed(epID3, tcpProtoPort53, policy.L7DataMap{ 644 cachedDstID3Selector: &policy.PerSelectorPolicy{ 645 L7Rules: api.L7Rules{ 646 DNS: []api.PortRuleDNS{ 647 {MatchPattern: "example.com"}, 648 }, 649 }, 650 }, 651 }) 652 require.Equal(t, nil, err, "Could not update with rules") 653 654 // Test cases 655 // Case 1 | EPID1 | DstID1 | 53 | UDP | www.ubuntu.com | Allowed 656 allowed, err := s.proxy.CheckAllowed(epID1, udpProtoPort53, dstID1, nil, "www.ubuntu.com") 657 require.Equal(t, nil, err, "Error when checking allowed") 658 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 659 660 // Case 2 | EPID1 | DstID1 | 53 | TCP | www.ubuntu.com | Rejected | Protocol TCP only allows "sub.ubuntu.com" 661 allowed, err = s.proxy.CheckAllowed(epID1, tcpProtoPort53, dstID1, nil, "www.ubuntu.com") 662 require.Equal(t, nil, err, "Error when checking allowed") 663 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 664 665 // Case 3 | EPID1 | DstID1 | 53 | TCP | sub.ubuntu.com | Allowed 666 allowed, err = s.proxy.CheckAllowed(epID1, tcpProtoPort53, dstID1, nil, "sub.ubuntu.com") 667 require.Equal(t, nil, err, "Error when checking allowed") 668 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 669 670 // Case 4 | EPID1 | DstID1 | 53 | UDP | sub.ubuntu.com | Allowed 671 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, dstID1, nil, "sub.ubuntu.com") 672 require.Equal(t, nil, err, "Error when checking allowed") 673 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 674 675 // Case 5 | EPID1 | DstID1 | 54 | UDP | cilium.io | Rejected | Port 54 only allows example.com 676 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort54, dstID1, nil, "cilium.io") 677 require.Equal(t, nil, err, "Error when checking allowed") 678 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 679 680 // Case 6 | EPID1 | DstID2 | 53 | UDP | cilium.io | Allowed 681 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, dstID2, nil, "cilium.io") 682 require.Equal(t, nil, err, "Error when checking allowed") 683 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 684 685 // Case 7 | EPID1 | DstID2 | 53 | UDP | aws.amazon.com | Rejected | Only cilium.io is allowed with DstID2 686 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, dstID2, nil, "aws.amazon.com") 687 require.Equal(t, nil, err, "Error when checking allowed") 688 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 689 690 // Case 8 | EPID1 | DstID1 | 54 | UDP | example.com | Allowed 691 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort54, dstID1, nil, "example.com") 692 require.Equal(t, nil, err, "Error when checking allowed") 693 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 694 695 // Case 9 | EPID2 | DstID1 | 53 | UDP | cilium.io | Rejected | EPID2 is not allowed as a source by any policy 696 allowed, err = s.proxy.CheckAllowed(epID2, udpProtoPort53, dstID1, nil, "cilium.io") 697 require.Equal(t, nil, err, "Error when checking allowed") 698 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 699 700 // Case 10 | EPID3 | DstID1 | 53 | UDP | example.com | Allowed 701 allowed, err = s.proxy.CheckAllowed(epID3, udpProtoPort53, dstID1, nil, "example.com") 702 require.Equal(t, nil, err, "Error when checking allowed") 703 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 704 705 // Case 11 | EPID3 | DstID1 | 53 | UDP | aws.amazon.com | Rejected | EPID3 is only allowed to ask DstID1 on Port 53 for example.com 706 allowed, err = s.proxy.CheckAllowed(epID3, udpProtoPort53, dstID1, nil, "aws.amazon.io") 707 require.Equal(t, nil, err, "Error when checking allowed") 708 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 709 710 // Case 12 | EPID3 | DstID1 | 54 | UDP | example.com | Rejected | EPID3 is only allowed to ask DstID1 on Port 53 for example.com 711 allowed, err = s.proxy.CheckAllowed(epID3, udpProtoPort54, dstID1, nil, "example.com") 712 require.Equal(t, nil, err, "Error when checking allowed") 713 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 714 715 // Case 13 | EPID3 | DstID2 | 53 | UDP | example.com | Rejected | EPID3 is only allowed to ask DstID1 on Port 53 for example.com 716 allowed, err = s.proxy.CheckAllowed(epID3, udpProtoPort53, dstID2, nil, "example.com") 717 require.Equal(t, nil, err, "Error when checking allowed") 718 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 719 720 // Case 14 | EPID3 | DstID3 | 53 | UDP | example.com | Allowed due to wildcard match pattern 721 allowed, err = s.proxy.CheckAllowed(epID3, udpProtoPort53, dstID3, nil, "example.com") 722 require.Equal(t, nil, err, "Error when checking allowed") 723 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 724 725 // Case 15 | EPID3 | DstID3 | 53 | TCP | example.com | Allowed 726 allowed, err = s.proxy.CheckAllowed(epID3, tcpProtoPort53, dstID3, nil, "example.com") 727 require.Equal(t, nil, err, "Error when checking allowed") 728 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 729 730 // Case 16 | EPID3 | DstID3 | 53 | TCP | amazon.com | Rejected | TCP protocol only allows "example.com" 731 allowed, err = s.proxy.CheckAllowed(epID3, tcpProtoPort53, dstID3, nil, "amazon.com") 732 require.Equal(t, nil, err, "Error when checking allowed") 733 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 734 735 // Case 17 | EPID3 | DstID4 | 53 | TCP | example.com | Rejected | "example.com" only allowed for DstID3 736 allowed, err = s.proxy.CheckAllowed(epID3, tcpProtoPort53, dstID4, nil, "example.com") 737 require.Equal(t, nil, err, "Error when checking allowed") 738 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 739 740 // Case 18 | EPID3 | DstID4 | 53 | UDP | example.com | Allowed due to a nil rule 741 allowed, err = s.proxy.CheckAllowed(epID3, udpProtoPort53, dstID4, nil, "example.com") 742 require.Equal(t, nil, err, "Error when checking allowed") 743 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 744 745 // Get rules for restoration 746 expected1 := restore.DNSRules{ 747 udpProtoPort53: restore.IPRules{ 748 asIPRule(s.proxy.allowed[epID1][udpProtoPort53][cachedDstID1Selector], map[string]struct{}{"::": {}}), 749 asIPRule(s.proxy.allowed[epID1][udpProtoPort53][cachedDstID2Selector], map[string]struct{}{"127.0.0.1": {}, "127.0.0.2": {}}), 750 }.Sort(nil), 751 udpProtoPort54: restore.IPRules{ 752 asIPRule(s.proxy.allowed[epID1][udpProtoPort54][cachedWildcardSelector], nil), 753 }, 754 tcpProtoPort53: restore.IPRules{ 755 asIPRule(s.proxy.allowed[epID1][tcpProtoPort53][cachedDstID1Selector], map[string]struct{}{"::": {}}), 756 }, 757 } 758 restored1, _ := s.proxy.GetRules(uint16(epID1)) 759 restored1.Sort(nil) 760 require.EqualValues(t, expected1, restored1) 761 762 expected2 := restore.DNSRules{} 763 restored2, _ := s.proxy.GetRules(uint16(epID2)) 764 restored2.Sort(nil) 765 require.EqualValues(t, expected2, restored2) 766 767 expected3 := restore.DNSRules{ 768 udpProtoPort53: restore.IPRules{ 769 asIPRule(s.proxy.allowed[epID3][udpProtoPort53][cachedDstID1Selector], map[string]struct{}{"::": {}}), 770 asIPRule(s.proxy.allowed[epID3][udpProtoPort53][cachedDstID3Selector], map[string]struct{}{}), 771 asIPRule(s.proxy.allowed[epID3][udpProtoPort53][cachedDstID4Selector], map[string]struct{}{}), 772 }.Sort(nil), 773 tcpProtoPort53: restore.IPRules{ 774 asIPRule(s.proxy.allowed[epID3][tcpProtoPort53][cachedDstID3Selector], map[string]struct{}{}), 775 }, 776 } 777 restored3, _ := s.proxy.GetRules(uint16(epID3)) 778 restored3.Sort(nil) 779 require.EqualValues(t, expected3, restored3) 780 781 // Test with limited set of allowed IPs 782 oldUsed := s.proxy.usedServers 783 s.proxy.usedServers = map[string]struct{}{"127.0.0.2": {}} 784 785 expected1b := restore.DNSRules{ 786 udpProtoPort53: restore.IPRules{ 787 asIPRule(s.proxy.allowed[epID1][udpProtoPort53][cachedDstID1Selector], map[string]struct{}{}), 788 asIPRule(s.proxy.allowed[epID1][udpProtoPort53][cachedDstID2Selector], map[string]struct{}{"127.0.0.2": {}}), 789 }.Sort(nil), 790 udpProtoPort54: restore.IPRules{ 791 asIPRule(s.proxy.allowed[epID1][udpProtoPort54][cachedWildcardSelector], nil), 792 }, 793 tcpProtoPort53: restore.IPRules{ 794 asIPRule(s.proxy.allowed[epID1][tcpProtoPort53][cachedDstID1Selector], map[string]struct{}{}), 795 }, 796 } 797 restored1b, _ := s.proxy.GetRules(uint16(epID1)) 798 restored1b.Sort(nil) 799 require.EqualValues(t, expected1b, restored1b) 800 801 // unlimited again 802 s.proxy.usedServers = oldUsed 803 804 s.proxy.UpdateAllowed(epID1, udpProtoPort53, nil) 805 s.proxy.UpdateAllowed(epID1, udpProtoPort54, nil) 806 s.proxy.UpdateAllowed(epID1, tcpProtoPort53, nil) 807 _, exists := s.proxy.allowed[epID1] 808 require.Equal(t, false, exists) 809 810 _, exists = s.proxy.allowed[epID2] 811 require.Equal(t, false, exists) 812 813 s.proxy.UpdateAllowed(epID3, udpProtoPort53, nil) 814 s.proxy.UpdateAllowed(epID3, tcpProtoPort53, nil) 815 _, exists = s.proxy.allowed[epID3] 816 require.Equal(t, false, exists) 817 818 dstIP1 := (s.dnsServer.Listener.Addr()).(*net.TCPAddr).IP 819 dstIP2a := net.ParseIP("127.0.0.1") 820 dstIP2b := net.ParseIP("127.0.0.2") 821 dstIPrandom := net.ParseIP("127.0.0.42") 822 823 // Before restore: all rules removed above, everything is dropped 824 // Case 1 | EPID1 | DstID1 | 53 | UDP | www.ubuntu.com | Rejected | No rules 825 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, dstID1, dstIP1, "www.ubuntu.com") 826 require.Equal(t, nil, err, "Error when checking allowed") 827 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 828 829 // Case 2 | EPID1 | DstID1 | 54 | UDP | cilium.io | Rejected | No rules 830 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort54, dstID1, dstIP1, "cilium.io") 831 require.Equal(t, nil, err, "Error when checking allowed") 832 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 833 834 // Case 3 | EPID1 | DstID2 | 53 | UDP | cilium.io | Rejected | No rules 835 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, dstID2, dstIP2a, "cilium.io") 836 require.Equal(t, nil, err, "Error when checking allowed") 837 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 838 839 // Case 4 | EPID1 | DstID2 | 53 | UDP | aws.amazon.com | Rejected | No rules 840 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, dstID2, dstIP2b, "aws.amazon.com") 841 require.Equal(t, nil, err, "Error when checking allowed") 842 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 843 844 // Case 5 | EPID1 | DstID1 | 54 | UDP | example.com | Rejected | No rules 845 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort54, dstID1, dstIP1, "example.com") 846 require.Equal(t, nil, err, "Error when checking allowed") 847 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 848 849 // Restore rules 850 ep1 := endpoint.NewTestEndpointWithState(t, s, s, testipcache.NewMockIPCache(), &endpoint.FakeEndpointProxy{}, testidentity.NewMockIdentityAllocator(nil), uint16(epID1), endpoint.StateReady) 851 ep1.DNSRulesV2 = restored1 852 s.proxy.RestoreRules(ep1) 853 _, exists = s.proxy.restored[epID1] 854 require.Equal(t, true, exists) 855 856 // Same tests with 2 (WORLD) dstID to make sure it is not used, but with correct destination IP 857 858 // Case 1 | EPID1 | dstIP1 | 53 | UDP | www.ubuntu.com | Allowed due to restored rules 859 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, 2, dstIP1, "www.ubuntu.com") 860 require.Equal(t, nil, err, "Error when checking allowed") 861 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 862 863 // Case 2 | EPID1 | dstIP1 | 54 | UDP | cilium.io | Rejected due to restored rules | Port 54 only allows example.com 864 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort54, 2, dstIP1, "cilium.io") 865 require.Equal(t, nil, err, "Error when checking allowed") 866 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 867 868 // Case 3 | EPID1 | dstIP2a | 53 | UDP | cilium.io | Allowed due to restored rules 869 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, 2, dstIP2a, "cilium.io") 870 require.Equal(t, nil, err, "Error when checking allowed") 871 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 872 873 // Case 4 | EPID1 | dstIP2b | 53 | UDP | aws.amazon.com | Rejected due to restored rules | Only cilium.io is allowed with DstID2 874 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, 2, dstIP2b, "aws.amazon.com") 875 require.Equal(t, nil, err, "Error when checking allowed") 876 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 877 878 // Case 5 | EPID1 | dstIP1 | 54 | UDP | example.com | Allowed due to restored rules 879 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort54, 2, dstIP1, "example.com") 880 require.Equal(t, nil, err, "Error when checking allowed") 881 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 882 883 // make sure random IP is not allowed 884 // Case 5 | EPID1 | random IP | 53 | UDP | example.com | Rejected due to restored rules 885 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, 2, dstIPrandom, "example.com") 886 require.Equal(t, nil, err, "Error when checking allowed") 887 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 888 889 // make sure random destination IP is allowed in a wildcard selector 890 // Case 5 | EPID1 | random IP | 54 | UDP | example.com | Allowed due to restored rules 891 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort54, 2, dstIPrandom, "example.com") 892 require.Equal(t, nil, err, "Error when checking allowed") 893 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 894 895 // Restore rules for epID3 896 ep3 := endpoint.NewTestEndpointWithState(t, s, s, testipcache.NewMockIPCache(), &endpoint.FakeEndpointProxy{}, testidentity.NewMockIdentityAllocator(nil), uint16(epID3), endpoint.StateReady) 897 ep3.DNSRulesV2 = restored3 898 s.proxy.RestoreRules(ep3) 899 _, exists = s.proxy.restored[epID3] 900 require.Equal(t, true, exists) 901 902 // Set empty ruleset, check that restored rules were deleted in epID3 903 err = s.proxy.UpdateAllowed(epID3, udpProtoPort53, nil) 904 require.Equal(t, nil, err, "Could not update with rules") 905 906 _, exists = s.proxy.restored[epID3] 907 require.Equal(t, false, exists) 908 909 // epID1 still has restored rules 910 _, exists = s.proxy.restored[epID1] 911 require.Equal(t, true, exists) 912 913 // Marshal restored rules to JSON 914 jsn, err := json.Marshal(s.proxy.restored[epID1]) 915 require.Equal(t, nil, err, "Could not marshal restored rules to json") 916 917 expected := ` 918 { 919 "` + restore.MakeV2PortProto(53, tcpProto).String() + `":[{ 920 "Re":"^(?:sub[.]ubuntu[.]com[.])$", 921 "IPs":{"::":{}} 922 }], 923 "` + restore.MakeV2PortProto(53, udpProto).String() + `":[{ 924 "Re":"^(?:[-a-zA-Z0-9_]*[.]ubuntu[.]com[.]|aws[.]amazon[.]com[.])$", 925 "IPs":{"::":{}} 926 },{ 927 "Re":"^(?:cilium[.]io[.])$", 928 "IPs":{"127.0.0.1":{},"127.0.0.2":{}} 929 }], 930 "` + restore.MakeV2PortProto(54, udpProto).String() + `":[{ 931 "Re":"^(?:example[.]com[.])$", 932 "IPs":null 933 }] 934 }` 935 pretty := new(bytes.Buffer) 936 err = json.Compact(pretty, []byte(expected)) 937 require.Equal(t, nil, err, "Could not compact expected json") 938 require.Equal(t, pretty.String(), string(jsn)) 939 940 s.proxy.RemoveRestoredRules(uint16(epID1)) 941 _, exists = s.proxy.restored[epID1] 942 require.Equal(t, false, exists) 943 944 // Before restore after marshal: previous restored rules are removed, everything is dropped 945 // Case 1 | EPID1 | DstID1 | 53 | UDP | www.ubuntu.com | Rejected | No rules 946 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, dstID1, dstIP1, "www.ubuntu.com") 947 require.Equal(t, nil, err, "Error when checking allowed") 948 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 949 950 // Case 2 | EPID1 | DstID1 | 54 | UDP | cilium.io | Rejected | No rules 951 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort54, dstID1, dstIP1, "cilium.io") 952 require.Equal(t, nil, err, "Error when checking allowed") 953 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 954 955 // Case 3 | EPID1 | DstID2 | 53 | UDP | cilium.io | Rejected | No rules 956 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, dstID2, dstIP2a, "cilium.io") 957 require.Equal(t, nil, err, "Error when checking allowed") 958 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 959 960 // Case 4 | EPID1 | DstID2 | 53 | UDP | aws.amazon.com | Rejected | No rules 961 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, dstID2, dstIP2b, "aws.amazon.com") 962 require.Equal(t, nil, err, "Error when checking allowed") 963 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 964 965 // Case 5 | EPID1 | DstID1 | 54 | UDP | example.com | Rejected | No rules 966 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort54, dstID1, dstIP1, "example.com") 967 require.Equal(t, nil, err, "Error when checking allowed") 968 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 969 970 // Case 5 | EPID1 | random IP | 54 | UDP | example.com | Rejected 971 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort54, 2, dstIPrandom, "example.com") 972 require.Equal(t, nil, err, "Error when checking allowed") 973 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 974 975 // Restore Unmarshaled rules 976 var rules restore.DNSRules 977 err = json.Unmarshal(jsn, &rules) 978 rules = rules.Sort(nil) 979 require.Equal(t, nil, err, "Could not unmarshal restored rules from json") 980 require.EqualValues(t, expected1, rules) 981 982 // Marshal again & compare 983 // Marshal restored rules to JSON 984 jsn2, err := json.Marshal(rules) 985 require.Equal(t, nil, err, "Could not marshal restored rules to json") 986 require.Equal(t, pretty.String(), string(jsn2)) 987 988 ep1.DNSRulesV2 = rules 989 s.proxy.RestoreRules(ep1) 990 _, exists = s.proxy.restored[epID1] 991 require.Equal(t, true, exists) 992 993 // After restoration of JSON marshaled/unmarshaled rules 994 995 // Case 1 | EPID1 | dstIP1 | 53 | UDP | www.ubuntu.com | Allowed due to restored rules 996 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, 2, dstIP1, "www.ubuntu.com") 997 require.Equal(t, nil, err, "Error when checking allowed") 998 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 999 1000 // Case 2 | EPID1 | dstIP1 | 54 | UDP | cilium.io | Rejected due to restored rules | Port 54 only allows example.com 1001 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort54, 2, dstIP1, "cilium.io") 1002 require.Equal(t, nil, err, "Error when checking allowed") 1003 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 1004 1005 // Case 3 | EPID1 | dstIP2a | 53 | UDP | cilium.io | Allowed due to restored rules 1006 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, 2, dstIP2a, "cilium.io") 1007 require.Equal(t, nil, err, "Error when checking allowed") 1008 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 1009 1010 // Case 4 | EPID1 | dstIP2b | 53 | UDP | aws.amazon.com | Rejected due to restored rules | Only cilium.io is allowed with DstID2 1011 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, 2, dstIP2b, "aws.amazon.com") 1012 require.Equal(t, nil, err, "Error when checking allowed") 1013 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 1014 1015 // Case 5 | EPID1 | dstIP1 | 54 | UDP | example.com | Allowed due to restored rules 1016 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort54, 2, dstIP1, "example.com") 1017 require.Equal(t, nil, err, "Error when checking allowed") 1018 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 1019 1020 // make sure random IP is not allowed 1021 // Case 5 | EPID1 | random IP | 53 | UDP | example.com | Rejected due to restored rules 1022 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort53, 2, dstIPrandom, "example.com") 1023 require.Equal(t, nil, err, "Error when checking allowed") 1024 require.Equal(t, false, allowed, "request was allowed when it should be rejected") 1025 1026 // make sure random IP is allowed on a wildcard 1027 // Case 5 | EPID1 | random IP | 54 | UDP | example.com | Allowed due to restored rules 1028 allowed, err = s.proxy.CheckAllowed(epID1, udpProtoPort54, 2, dstIPrandom, "example.com") 1029 require.Equal(t, nil, err, "Error when checking allowed") 1030 require.Equal(t, true, allowed, "request was rejected when it should be allowed") 1031 1032 s.proxy.RemoveRestoredRules(uint16(epID1)) 1033 _, exists = s.proxy.restored[epID1] 1034 require.Equal(t, false, exists) 1035 } 1036 1037 func TestRestoredEndpoint(t *testing.T) { 1038 s := setupDNSProxyTestSuite(t) 1039 1040 // Respond with an actual answer for the query. This also tests that the 1041 // connection was forwarded via the correct protocol (tcp/udp) because we 1042 // connet with TCP, and the server only listens on TCP. 1043 1044 name := "cilium.io." 1045 pattern := "*.cilium.com." 1046 l7map := policy.L7DataMap{ 1047 cachedDstID1Selector: &policy.PerSelectorPolicy{ 1048 L7Rules: api.L7Rules{ 1049 DNS: []api.PortRuleDNS{{MatchName: name}, {MatchPattern: pattern}}, 1050 }, 1051 }, 1052 } 1053 queries := []string{name, strings.ReplaceAll(pattern, "*", "sub")} 1054 1055 err := s.proxy.UpdateAllowed(epID1, dstPortProto, l7map) 1056 require.Equal(t, nil, err, "Could not update with rules") 1057 for _, query := range queries { 1058 allowed, err := s.proxy.CheckAllowed(epID1, dstPortProto, dstID1, nil, query) 1059 require.Equal(t, nil, err, "Error when checking allowed query: %q", query) 1060 require.Equal(t, true, allowed, "request was rejected when it should be allowed for query: %q", query) 1061 } 1062 1063 // 1st request 1064 for _, query := range queries { 1065 request := new(dns.Msg) 1066 request.SetQuestion(query, dns.TypeA) 1067 response, rtt, err := s.dnsTCPClient.Exchange(request, s.proxy.DNSServers[0].Listener.Addr().String()) 1068 require.NoErrorf(t, err, "DNS request from test client failed when it should succeed (RTT: %v) (query: %q)", rtt, query) 1069 require.Equal(t, 1, len(response.Answer), "Proxy returned incorrect number of answer RRs %s (query: %q)", response, query) 1070 require.Equal(t, query+"\t60\tIN\tA\t1.1.1.1", response.Answer[0].String(), "Proxy returned incorrect RRs") 1071 } 1072 1073 for _, query := range queries { 1074 request := new(dns.Msg) 1075 request.SetQuestion(query, dns.TypeA) 1076 response, rtt, err := s.dnsTCPClient.Exchange(request, s.proxy.DNSServers[0].Listener.Addr().String()) 1077 require.NoErrorf(t, err, "DNS request from test client failed when it should succeed (RTT: %v) (query: %q)", rtt, query) 1078 require.Equal(t, 1, len(response.Answer), "Proxy returned incorrect number of answer RRs %s (query: %q)", response, query) 1079 require.Equal(t, query+"\t60\tIN\tA\t1.1.1.1", response.Answer[0].String(), "Proxy returned incorrect RRs") 1080 } 1081 1082 // Get restored rules 1083 restored, _ := s.proxy.GetRules(uint16(epID1)) 1084 restored.Sort(nil) 1085 1086 // remove rules 1087 err = s.proxy.UpdateAllowed(epID1, dstPortProto, nil) 1088 require.Equal(t, nil, err, "Could not remove rules") 1089 1090 // 2nd request, refused due to no rules 1091 for _, query := range queries { 1092 request := new(dns.Msg) 1093 request.SetQuestion(query, dns.TypeA) 1094 response, rtt, err := s.dnsTCPClient.Exchange(request, s.proxy.DNSServers[0].Listener.Addr().String()) 1095 require.NoErrorf(t, err, "DNS request from test client failed when it should succeed (RTT: %v) (query: %q)", rtt, query) 1096 require.Equal(t, 0, len(response.Answer), "Proxy returned incorrect number of answer RRs %s (query: %q)", response, query) 1097 require.Equal(t, dns.RcodeRefused, response.Rcode, "DNS request from test client was not rejected when it should be blocked (query: %q)", query) 1098 } 1099 1100 // restore rules, set the mock to restoring state 1101 s.restoring = true 1102 ep1 := endpoint.NewTestEndpointWithState(t, s, s, testipcache.NewMockIPCache(), &endpoint.FakeEndpointProxy{}, testidentity.NewMockIdentityAllocator(nil), uint16(epID1), endpoint.StateReady) 1103 ep1.IPv4 = netip.MustParseAddr("127.0.0.1") 1104 ep1.IPv6 = netip.MustParseAddr("::1") 1105 ep1.DNSRulesV2 = restored 1106 s.proxy.RestoreRules(ep1) 1107 _, exists := s.proxy.restored[epID1] 1108 require.Equal(t, true, exists) 1109 1110 // 3nd request, answered due to restored Endpoint and rules being found 1111 for _, query := range queries { 1112 request := new(dns.Msg) 1113 request.SetQuestion(query, dns.TypeA) 1114 response, rtt, err := s.dnsTCPClient.Exchange(request, s.proxy.DNSServers[0].Listener.Addr().String()) 1115 require.NoErrorf(t, err, "DNS request from test client failed when it should succeed (RTT: %v) (query: %q)", rtt, query) 1116 require.Equal(t, 1, len(response.Answer), "Proxy returned incorrect number of answer RRs %s (query: %q)", response, query) 1117 require.Equal(t, query+"\t60\tIN\tA\t1.1.1.1", response.Answer[0].String(), "Proxy returned incorrect RRs") 1118 } 1119 // cleanup 1120 s.proxy.RemoveRestoredRules(uint16(epID1)) 1121 _, exists = s.proxy.restored[epID1] 1122 require.Equal(t, false, exists) 1123 1124 invalidRePattern := "invalid-re-pattern((*" 1125 validRePattern := "^this[.]domain[.]com[.]$" 1126 1127 // extract the port the DNS-server is listening on by looking at the restored rules. The port is non-deterministic 1128 // since it's listening on :0 1129 require.Equal(t, 1, len(restored), "GetRules is expected to return rules for one port but returned for %d", len(restored)) 1130 portProto := maps.Keys(restored)[0] 1131 1132 // Insert one valid and one invalid pattern and ensure that the valid one works 1133 // and that the invalid one doesn't interfere with the other rules. 1134 restored[portProto] = append(restored[portProto], 1135 restore.IPRule{Re: restore.RuleRegex{Pattern: &invalidRePattern}}, 1136 restore.IPRule{Re: restore.RuleRegex{Pattern: &validRePattern}}, 1137 ) 1138 ep1.DNSRulesV2 = restored 1139 s.proxy.RestoreRules(ep1) 1140 _, exists = s.proxy.restored[epID1] 1141 require.Equal(t, true, exists) 1142 1143 // 4nd request, answered due to restored Endpoint and rules being found, including domain matched by new regex 1144 for _, query := range append(queries, "this.domain.com.") { 1145 request := new(dns.Msg) 1146 request.SetQuestion(query, dns.TypeA) 1147 response, rtt, err := s.dnsTCPClient.Exchange(request, s.proxy.DNSServers[0].Listener.Addr().String()) 1148 require.NoErrorf(t, err, "DNS request from test client failed when it should succeed (RTT: %v) (query: %q)", rtt, query) 1149 require.Equal(t, 1, len(response.Answer), "Proxy returned incorrect number of answer RRs %s (query: %q)", response, query) 1150 require.Equal(t, query+"\t60\tIN\tA\t1.1.1.1", response.Answer[0].String(), "Proxy returned incorrect RRs") 1151 } 1152 1153 // cleanup 1154 s.proxy.RemoveRestoredRules(uint16(epID1)) 1155 _, exists = s.proxy.restored[epID1] 1156 require.Equal(t, false, exists) 1157 1158 s.restoring = false 1159 } 1160 1161 func TestProxyRequestContext_IsTimeout(t *testing.T) { 1162 p := new(ProxyRequestContext) 1163 p.Err = fmt.Errorf("sample err: %w", context.DeadlineExceeded) 1164 require.Equal(t, true, p.IsTimeout()) 1165 1166 // Assert that failing to wrap the error properly (by using '%w') causes 1167 // IsTimeout() to return the wrong value. 1168 //nolint:errorlint 1169 p.Err = fmt.Errorf("sample err: %s", context.DeadlineExceeded) 1170 require.Equal(t, false, p.IsTimeout()) 1171 1172 p.Err = ErrFailedAcquireSemaphore{} 1173 require.Equal(t, true, p.IsTimeout()) 1174 p.Err = ErrTimedOutAcquireSemaphore{ 1175 gracePeriod: 1 * time.Second, 1176 } 1177 require.Equal(t, true, p.IsTimeout()) 1178 } 1179 1180 type selectorMock struct { 1181 key string 1182 } 1183 1184 func (t selectorMock) GetSelections() identity.NumericIdentitySlice { 1185 panic("implement me") 1186 } 1187 1188 func (t selectorMock) GetMetadataLabels() labels.LabelArray { 1189 panic("implement me") 1190 } 1191 1192 func (t selectorMock) Selects(nid identity.NumericIdentity) bool { 1193 panic("implement me") 1194 } 1195 1196 func (t selectorMock) IsWildcard() bool { 1197 panic("implement me") 1198 } 1199 1200 func (t selectorMock) IsNone() bool { 1201 panic("implement me") 1202 } 1203 1204 func (t selectorMock) String() string { 1205 return t.key 1206 } 1207 1208 func Benchmark_perEPAllow_setPortRulesForID(b *testing.B) { 1209 const ( 1210 nEPs = 10000 1211 nEPsAtOnce = 60 1212 nMatchPatterns = 30 1213 nMatchNames = 600 1214 everyNIsEqual = 10 1215 everyNHasWildcard = 20 1216 ) 1217 runtime.GC() 1218 initialHeap := getMemStats().HeapInuse 1219 rulesPerEP := make([]policy.L7DataMap, 0, nEPs) 1220 1221 var defaultRules []api.PortRuleDNS 1222 for i := 0; i < nMatchPatterns; i++ { 1223 defaultRules = append(defaultRules, api.PortRuleDNS{MatchPattern: "*.bar" + strconv.Itoa(i) + "another.very.long.domain.here"}) 1224 } 1225 for i := 0; i < nMatchNames; i++ { 1226 defaultRules = append(defaultRules, api.PortRuleDNS{MatchName: strconv.Itoa(i) + "very.long.domain.containing.a.lot.of.chars"}) 1227 } 1228 1229 for i := 0; i < nEPs; i++ { 1230 commonRules := append([]api.PortRuleDNS{}, defaultRules...) 1231 if i%everyNIsEqual != 0 { 1232 commonRules = append( 1233 commonRules, 1234 api.PortRuleDNS{MatchName: "custom-for-this-one" + strconv.Itoa(i) + ".domain.tld"}, 1235 api.PortRuleDNS{MatchPattern: "custom2-for-this-one*" + strconv.Itoa(i) + ".domain.tld"}, 1236 ) 1237 } 1238 if (i+1)%everyNHasWildcard == 0 { 1239 commonRules = append(commonRules, api.PortRuleDNS{MatchPattern: "*"}) 1240 } 1241 psp := &policy.PerSelectorPolicy{L7Rules: api.L7Rules{DNS: commonRules}} 1242 rulesPerEP = append(rulesPerEP, policy.L7DataMap{new(selectorMock): psp, new(selectorMock): psp}) 1243 } 1244 1245 pea := perEPAllow{} 1246 c := regexCache{} 1247 b.ReportAllocs() 1248 b.StopTimer() 1249 b.ResetTimer() 1250 for i := 0; i < b.N; i++ { 1251 for epID := uint64(0); epID < nEPs; epID++ { 1252 pea.setPortRulesForID(c, epID, udpProtoPort8053, nil) 1253 } 1254 b.StartTimer() 1255 for epID, rules := range rulesPerEP { 1256 if epID >= nEPsAtOnce { 1257 pea.setPortRulesForID(c, uint64(epID)-nEPsAtOnce, udpProtoPort8053, nil) 1258 } 1259 pea.setPortRulesForID(c, uint64(epID), udpProtoPort8053, rules) 1260 } 1261 b.StopTimer() 1262 } 1263 runtime.GC() 1264 // This is a ~proxy metric for the growth of heap per b.N. We call it here instead of the loop to 1265 // ensure we also count things like the strings "borrowed" from rulesPerEP 1266 b.ReportMetric(float64(getMemStats().HeapInuse-initialHeap), "B(HeapInUse)/op") 1267 1268 for epID := uint64(0); epID < nEPs; epID++ { 1269 pea.setPortRulesForID(c, epID, udpProtoPort8053, nil) 1270 } 1271 if len(pea) > 0 { 1272 b.Fail() 1273 } 1274 b.StopTimer() 1275 // Remove all the inserted rules to ensure the cache goes down to zero entries 1276 for epID := uint64(0); epID < 20; epID++ { 1277 pea.setPortRulesForID(c, epID, udpProtoPort8053, nil) 1278 } 1279 if len(pea) > 0 || len(c) > 0 { 1280 b.Fail() 1281 } 1282 } 1283 1284 func Benchmark_perEPAllow_setPortRulesForID_large(b *testing.B) { 1285 b.Skip() 1286 numEPs := uint64(20) 1287 cnpFile := "testdata/cnps-large.yaml" 1288 1289 runtime.GC() 1290 m := getMemStats() 1291 fmt.Printf("Before Setup (N=%v,EPs=%d)\n", b.N, numEPs) 1292 1293 fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc)) 1294 fmt.Printf("\tHeapInuse = %v MiB", bToMb(m.HeapInuse)) 1295 fmt.Printf("\tSys = %v MiB", bToMb(m.Sys)) 1296 fmt.Printf("\tNumGC = %v\n", m.NumGC) 1297 1298 bb, err := os.ReadFile(cnpFile) 1299 if err != nil { 1300 b.Fatal(err) 1301 } 1302 var cnpList v2.CiliumNetworkPolicyList 1303 if err := yaml.Unmarshal(bb, &cnpList); err != nil { 1304 b.Fatal(err) 1305 } 1306 1307 rules := policy.L7DataMap{} 1308 1309 addEgress := func(e []api.EgressRule) { 1310 var portRuleDNS []api.PortRuleDNS 1311 for _, egress := range e { 1312 if egress.ToPorts != nil { 1313 for _, ports := range egress.ToPorts { 1314 if ports.Rules != nil { 1315 for _, dns := range ports.Rules.DNS { 1316 if len(dns.MatchPattern) > 0 { 1317 portRuleDNS = append(portRuleDNS, api.PortRuleDNS{ 1318 MatchPattern: dns.MatchPattern, 1319 }) 1320 } 1321 if len(dns.MatchName) > 0 { 1322 portRuleDNS = append(portRuleDNS, api.PortRuleDNS{ 1323 MatchName: dns.MatchName, 1324 }) 1325 } 1326 } 1327 } 1328 } 1329 } 1330 } 1331 rules[new(selectorMock)] = &policy.PerSelectorPolicy{ 1332 L7Rules: api.L7Rules{ 1333 DNS: portRuleDNS, 1334 }, 1335 } 1336 } 1337 1338 for _, cnp := range cnpList.Items { 1339 if cnp.Specs != nil { 1340 for _, spec := range cnp.Specs { 1341 if spec.Egress != nil { 1342 addEgress(spec.Egress) 1343 } 1344 } 1345 } 1346 if cnp.Spec != nil { 1347 if cnp.Spec.Egress != nil { 1348 addEgress(cnp.Spec.Egress) 1349 } 1350 } 1351 } 1352 1353 runtime.GC() 1354 m = getMemStats() 1355 fmt.Printf("Before Test (N=%v,EPs=%d)\n", b.N, numEPs) 1356 1357 fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc)) 1358 fmt.Printf("\tHeapInuse = %v MiB", bToMb(m.HeapInuse)) 1359 fmt.Printf("\tSys = %v MiB", bToMb(m.Sys)) 1360 fmt.Printf("\tNumGC = %v\n", m.NumGC) 1361 1362 pea := perEPAllow{} 1363 c := regexCache{} 1364 b.ReportAllocs() 1365 b.ResetTimer() 1366 for i := 0; i < b.N; i++ { 1367 for epID := uint64(0); epID < numEPs; epID++ { 1368 pea.setPortRulesForID(c, epID, udpProtoPort8053, rules) 1369 } 1370 } 1371 b.StopTimer() 1372 1373 // Uncomment to see the HeapInUse from only the regexp cache 1374 // for epID := uint64(0); epID < numEPs; epID++ { 1375 // pea.setPortRulesForID(epID, udpProtoPort8053, nil) 1376 // } 1377 1378 // Explicitly run gc to ensure we measure what we want 1379 runtime.GC() 1380 m = getMemStats() 1381 // Explicitly keep a reference to "pea" to keep it on the heap 1382 // so that we can measure it before it is garbage collected. 1383 fmt.Printf("After Test (N=%v,EPs=%d)\n", b.N, len(pea)) 1384 fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc)) 1385 fmt.Printf("\tHeapInuse = %v MiB", bToMb(m.HeapInuse)) 1386 fmt.Printf("\tSys = %v MiB", bToMb(m.Sys)) 1387 fmt.Printf("\tNumGC = %v\n", m.NumGC) 1388 // Remove all the inserted rules to ensure both indexes go to zero entries 1389 for epID := uint64(0); epID < numEPs; epID++ { 1390 pea.setPortRulesForID(c, epID, udpProtoPort8053, nil) 1391 } 1392 if len(pea) > 0 || len(c) > 0 { 1393 b.Fail() 1394 } 1395 } 1396 1397 func getMemStats() runtime.MemStats { 1398 var m runtime.MemStats 1399 runtime.ReadMemStats(&m) 1400 return m 1401 } 1402 1403 func bToMb(b uint64) uint64 { 1404 return b / 1024 / 1024 1405 }