Repository: cilium/cilium
Pull request #10559 (2020-03-12 15:23:25)

test: merge waitToDelete* functions
Follow up of https://github.com/cilium/cilium/pull/10520#discussion_r391665734

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/cilium/cilium/10559)
<!-- Reviewable:end -->


aanm: test-me-please
coveralls: 
[![Coverage Status](https://coveralls.io/builds/29305857/badge)](https://coveralls.io/builds/29305857)

Coverage increased (+0.02%) to 45.641% when pulling **5594149599cab9907e164d2ea619eef78f4d33c7 on pr/fix-operator-read-config-ci-follow-up** into **a734d81edc5aafd2d58532fa2c71f2984c9c080a on master**.

aanm: test-me-please
aanm: test-me-please
aanm: test-me-please

Patch:
diff --git a/.github/cilium-actions.yml b/.github/cilium-actions.yml
index 1d5990492d..7182a8207d 100644
--- a/.github/cilium-actions.yml
+++ b/.github/cilium-actions.yml
@@ -39,7 +39,7 @@ require-msgs-in-commit:
 block-pr-with:
   labels-unset:
     - regex-label: "release-note/.*"
-      helper: "Release note label not set, please set the appropriate release note."
+      helper: "Please set the appropriate release note label."
       set-labels:
         - "dont-merge/needs-release-note"
   labels-set:
diff --git a/Documentation/cmdref/cilium-agent.md b/Documentation/cmdref/cilium-agent.md
index 3821680ba2..e2f270f3fa 100644
--- a/Documentation/cmdref/cilium-agent.md
+++ b/Documentation/cmdref/cilium-agent.md
@@ -72,6 +72,7 @@ cilium-agent [flags]
       --encrypt-node                                  Enables encrypting traffic from non-Cilium pods and host networking
       --endpoint-interface-name-prefix string         Prefix of interface name shared by all endpoints (default "lxc+")
       --endpoint-queue-size int                       size of EventQueue per-endpoint (default 25)
+      --endpoint-status strings                       Enable additional CiliumEndpoint status features (controllers,health,log,policy,state)
       --envoy-log string                              Path to a separate Envoy log file, if any
       --exclude-local-address strings                 Exclude CIDR from being recognized as local address
       --fixed-identity-mapping map                    Key-value for the fixed identity mapping which allows to use reserved label for fixed identities (default map[])
diff --git a/bpf/bpf_sock.c b/bpf/bpf_sock.c
index b5f4c8fdf0..000a3f3a64 100644
--- a/bpf/bpf_sock.c
+++ b/bpf/bpf_sock.c
@@ -742,7 +742,8 @@ int sock6_xlate(struct bpf_sock_addr *ctx)
 }
 
 #ifdef ENABLE_HOST_SERVICES_UDP
-static __always_inline int sock6_xlate_snd_v4_in_v6(struct bpf_sock_addr *ctx)
+static __always_inline int
+sock6_xlate_snd_v4_in_v6(struct bpf_sock_addr *ctx __maybe_unused)
 {
 #ifdef ENABLE_IPV4
 	struct bpf_sock_addr fake_ctx;
@@ -837,7 +838,8 @@ static __always_inline int sock6_xlate_snd(struct bpf_sock_addr *ctx)
 	return SYS_PROCEED;
 }
 
-static __always_inline int sock6_xlate_rcv_v4_in_v6(struct bpf_sock_addr *ctx)
+static __always_inline int
+sock6_xlate_rcv_v4_in_v6(struct bpf_sock_addr *ctx __maybe_unused)
 {
 #ifdef ENABLE_IPV4
 	struct bpf_sock_addr fake_ctx;
diff --git a/daemon/cmd/daemon_main.go b/daemon/cmd/daemon_main.go
index a08c90a134..a6c69e4fc7 100644
--- a/daemon/cmd/daemon_main.go
+++ b/daemon/cmd/daemon_main.go
@@ -287,6 +287,10 @@ func init() {
 	flags.Bool(option.EnableHealthChecking, defaults.EnableHealthChecking, "Enable connectivity health checking")
 	option.BindEnv(option.EnableHealthChecking)
 
+	flags.StringSlice(option.EndpointStatus, []string{},
+		"Enable additional CiliumEndpoint status features ("+strings.Join(option.EndpointStatusValues(), ",")+")")
+	option.BindEnv(option.EndpointStatus)
+
 	flags.Bool(option.EnableEndpointHealthChecking, defaults.EnableEndpointHealthChecking, "Enable connectivity health checking between virtual endpoints")
 	option.BindEnv(option.EnableEndpointHealthChecking)
 
diff --git a/pkg/endpoint/bpf.go b/pkg/endpoint/bpf.go
index 461d750e17..3a6c397bc0 100644
--- a/pkg/endpoint/bpf.go
+++ b/pkg/endpoint/bpf.go
@@ -30,6 +30,7 @@ import (
 	"github.com/cilium/cilium/pkg/completion"
 	"github.com/cilium/cilium/pkg/controller"
 	"github.com/cilium/cilium/pkg/endpoint/regeneration"
+	"github.com/cilium/cilium/pkg/labels"
 	"github.com/cilium/cilium/pkg/loadinfo"
 	"github.com/cilium/cilium/pkg/logging/logfields"
 	"github.com/cilium/cilium/pkg/maps/ctmap"
@@ -224,7 +225,7 @@ func (e *Endpoint) addNewRedirectsFromDesiredPolicy(ingress bool, desiredRedirec
 				} else {
 					insertedDesiredMapState[keyFromFilter] = struct{}{}
 				}
-				if entry != policy.NoRedirectEntry {
+				if entry.IsRedirectEntry() {
 					entry.ProxyPort = redirectPort
 				}
 				e.desiredPolicy.PolicyMapState[keyFromFilter] = entry
@@ -334,10 +335,15 @@ func (e *Endpoint) addVisibilityRedirects(ingress bool, desiredRedirects map[str
 			TrafficDirection: direction.Uint8(),
 		}
 
-		e.desiredPolicy.PolicyMapState[newKey] = policy.MapStateEntry{
-			ProxyPort: redirectPort,
+		derivedFrom := labels.LabelArrayList{
+			labels.LabelArray{
+				labels.NewLabel(policy.LabelKeyPolicyDerivedFrom, policy.LabelVisibilityAnnotation, labels.LabelSourceReserved),
+			},
 		}
+		entry := policy.NewMapStateEntry(derivedFrom, true)
+		entry.ProxyPort = redirectPort
 
+		e.desiredPolicy.PolicyMapState[newKey] = entry
 		insertedDesiredMapState[newKey] = struct{}{}
 	}
 
@@ -1068,7 +1074,7 @@ func (e *Endpoint) applyPolicyMapChanges() (proxyChanges bool, err error) {
 
 	for keyToAdd, entry := range adds {
 		// Keep the existing proxy port, if any
-		if entry != policy.NoRedirectEntry {
+		if entry.IsRedirectEntry() {
 			entry.ProxyPort = e.realizedRedirects[policy.ProxyIDFromKey(e.ID, keyToAdd)]
 			if entry.ProxyPort != 0 {
 				proxyChanges = true
@@ -1143,7 +1149,7 @@ func (e *Endpoint) addPolicyMapDelta() error {
 	errors := 0
 
 	for keyToAdd, entry := range e.desiredPolicy.PolicyMapState {
-		if oldEntry, ok := e.realizedPolicy.PolicyMapState[keyToAdd]; !ok || oldEntry != entry {
+		if oldEntry, ok := e.realizedPolicy.PolicyMapState[keyToAdd]; !ok || !oldEntry.Equal(&entry) {
 			if !e.addPolicyKey(keyToAdd, entry, false) {
 				errors++
 			}
diff --git a/pkg/endpoint/endpoint_status.go b/pkg/endpoint/endpoint_status.go
index 97ab2e87d9..ae20d18db2 100644
--- a/pkg/endpoint/endpoint_status.go
+++ b/pkg/endpoint/endpoint_status.go
@@ -1,4 +1,4 @@
-// Copyright 2019 Authors of Cilium
+// Copyright 2019-2020 Authors of Cilium
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -292,31 +292,63 @@ func (e *Endpoint) getEndpointVisibilityPolicyStatus() *string {
 	return &str
 }
 
+// EndpointStatusConfiguration is the configuration interface that a caller of
+// to GetCiliumEndpointStatus() must implement
+type EndpointStatusConfiguration interface {
+	// EndpointStatusIsEnabled must return true if a particular
+	// option.EndpointStatus* feature is enabled
+	EndpointStatusIsEnabled(option string) bool
+}
+
+func compressEndpointState(state models.EndpointState) string {
+	switch state {
+	case models.EndpointStateRestoring, models.EndpointStateWaitingToRegenerate,
+		models.EndpointStateRegenerating, models.EndpointStateReady,
+		models.EndpointStateDisconnecting, models.EndpointStateDisconnected:
+		return string(models.EndpointStateReady)
+	}
+
+	return string(state)
+}
+
 // GetCiliumEndpointStatus creates a cilium_v2.EndpointStatus of an endpoint.
 // See cilium_v2.EndpointStatus for a detailed explanation of each field.
-func (e *Endpoint) GetCiliumEndpointStatus() *cilium_v2.EndpointStatus {
+func (e *Endpoint) GetCiliumEndpointStatus(conf EndpointStatusConfiguration) *cilium_v2.EndpointStatus {
 	e.mutex.RLock()
 	defer e.mutex.RUnlock()
 
 	model := e.GetModelRLocked()
 	modelStatus := model.Status
 
-	controllers := getEndpointStatusControllers(modelStatus)
-	identity := getEndpointIdentity(modelStatus)
-	log := e.getEndpointStatusLog()
-	networking := getEndpointNetworking(modelStatus)
-
-	return &cilium_v2.EndpointStatus{
-		ID:                     int64(e.ID),
-		ExternalIdentifiers:    modelStatus.ExternalIdentifiers,
-		Controllers:            controllers,
-		Identity:               identity,
-		Log:                    log,
-		Networking:             networking,
-		Health:                 modelStatus.Health,
-		State:                  string(modelStatus.State),
-		Policy:                 e.getEndpointPolicy(),
-		Encryption:             cilium_v2.EncryptionSpec{Key: int(node.GetIPsecKeyIdentity())},
-		VisibilityPolicyStatus: e.getEndpointVisibilityPolicyStatus(),
+	status := &cilium_v2.EndpointStatus{
+		ID:                  int64(e.ID),
+		ExternalIdentifiers: modelStatus.ExternalIdentifiers,
+		Identity:            getEndpointIdentity(modelStatus),
+		Networking:          getEndpointNetworking(modelStatus),
+		State:               compressEndpointState(modelStatus.State),
+		Encryption:          cilium_v2.EncryptionSpec{Key: int(node.GetIPsecKeyIdentity())},
+	}
+
+	if conf.EndpointStatusIsEnabled(option.EndpointStatusControllers) {
+		status.Controllers = getEndpointStatusControllers(modelStatus)
 	}
+
+	if conf.EndpointStatusIsEnabled(option.EndpointStatusPolicy) {
+		status.Policy = e.getEndpointPolicy()
+		status.VisibilityPolicyStatus = e.getEndpointVisibilityPolicyStatus()
+	}
+
+	if conf.EndpointStatusIsEnabled(option.EndpointStatusHealth) {
+		status.Health = modelStatus.Health
+	}
+
+	if conf.EndpointStatusIsEnabled(option.EndpointStatusLog) {
+		status.Log = e.getEndpointStatusLog()
+	}
+
+	if conf.EndpointStatusIsEnabled(option.EndpointStatusState) {
+		status.State = string(modelStatus.State)
+	}
+
+	return status
 }
diff --git a/pkg/endpoint/manager.go b/pkg/endpoint/manager.go
index 39f85f1dd1..eadeb1517c 100644
--- a/pkg/endpoint/manager.go
+++ b/pkg/endpoint/manager.go
@@ -25,7 +25,7 @@ import (
 
 type endpointManager interface {
 	AllocateID(id uint16) (uint16, error)
-	RunK8sCiliumEndpointSync(*Endpoint)
+	RunK8sCiliumEndpointSync(*Endpoint, EndpointStatusConfiguration)
 	UpdateReferences(map[id.PrefixType]string, *Endpoint)
 	UpdateIDReference(*Endpoint)
 	RemoveReferences(map[id.PrefixType]string)
@@ -64,7 +64,7 @@ func (e *Endpoint) Expose(mgr endpointManager) error {
 
 	e.getLogger().Info("New endpoint")
 
-	mgr.RunK8sCiliumEndpointSync(e)
+	mgr.RunK8sCiliumEndpointSync(e, option.Config)
 	return nil
 }
 
diff --git a/pkg/endpoint/policy.go b/pkg/endpoint/policy.go
index 56425e4c49..83f28b28c7 100644
--- a/pkg/endpoint/policy.go
+++ b/pkg/endpoint/policy.go
@@ -751,3 +751,23 @@ func (e *Endpoint) UpdateVisibilityPolicy(annoCB AnnotationsResolverCB) {
 	}
 	<-ch
 }
+
+// GetRealizedPolicyRuleLabelsForKey returns the list of policy rule labels
+// which match a given flow key (in host byte-order). The returned
+// LabelArrayList must not be modified. This function is exported to be
+// accessed by code outside of the Cilium code base (e.g. Hubble).
+func (e *Endpoint) GetRealizedPolicyRuleLabelsForKey(key policy.Key) (
+	derivedFrom labels.LabelArrayList,
+	revision uint64,
+	ok bool,
+) {
+	e.mutex.RLock()
+	defer e.mutex.RUnlock()
+
+	entry, ok := e.realizedPolicy.PolicyMapState[key]
+	if !ok {
+		return nil, 0, false
+	}
+
+	return entry.DerivedFromRules, e.policyRevision, true
+}
diff --git a/pkg/endpointmanager/manager.go b/pkg/endpointmanager/manager.go
index 4bbef9f307..05cd039599 100644
--- a/pkg/endpointmanager/manager.go
+++ b/pkg/endpointmanager/manager.go
@@ -61,7 +61,7 @@ type EndpointManager struct {
 // EndpointResourceSynchronizer is an interface which synchronizes CiliumEndpoint
 // resources with Kubernetes.
 type EndpointResourceSynchronizer interface {
-	RunK8sCiliumEndpointSync(ep *endpoint.Endpoint)
+	RunK8sCiliumEndpointSync(ep *endpoint.Endpoint, conf endpoint.EndpointStatusConfiguration)
 }
 
 // NewEndpointManager creates a new EndpointManager.
diff --git a/pkg/k8s/endpointsynchronizer/cep.go b/pkg/k8s/endpointsynchronizer/cep.go
index c5e62a11b0..97b1c240b5 100644
--- a/pkg/k8s/endpointsynchronizer/cep.go
+++ b/pkg/k8s/endpointsynchronizer/cep.go
@@ -49,7 +49,7 @@ type EndpointSynchronizer struct {
 // has 1 controller that updates it, and a local copy is retained and only
 // updates are pushed up.
 // CiliumEndpoint objects have the same name as the pod they represent.
-func (epSync *EndpointSynchronizer) RunK8sCiliumEndpointSync(e *endpoint.Endpoint) {
+func (epSync *EndpointSynchronizer) RunK8sCiliumEndpointSync(e *endpoint.Endpoint, conf endpoint.EndpointStatusConfiguration) {
 	var (
 		endpointID     = e.ID
 		controllerName = fmt.Sprintf("sync-to-k8s-ciliumendpoint (%v)", endpointID)
@@ -112,7 +112,7 @@ func (epSync *EndpointSynchronizer) RunK8sCiliumEndpointSync(e *endpoint.Endpoin
 
 				// Serialize the endpoint into a model. It is compared with the one
 				// from before, only updating on changes.
-				mdl := e.GetCiliumEndpointStatus()
+				mdl := e.GetCiliumEndpointStatus(conf)
 				if reflect.DeepEqual(mdl, lastMdl) {
 					scopedLog.Debug("Skipping CiliumEndpoint update because it has not changed")
 					return nil
@@ -144,7 +144,9 @@ func (epSync *EndpointSynchronizer) RunK8sCiliumEndpointSync(e *endpoint.Endpoin
 						ObjectMeta: meta_v1.ObjectMeta{
 							Name: podName,
 						},
-						Status: *mdl,
+					}
+					if mdl != nil {
+						cep.Status = *mdl
 					}
 					localCEP, err = ciliumClient.CiliumEndpoints(namespace).Create(cep)
 					if err != nil {
diff --git a/pkg/labels/array.go b/pkg/labels/array.go
index 88015ecc78..d7a1622b91 100644
--- a/pkg/labels/array.go
+++ b/pkg/labels/array.go
@@ -213,8 +213,8 @@ func (ls LabelArray) StringMap() map[string]string {
 	return o
 }
 
-// Same returns true if the label arrays are the same, i.e., have the same labels in the same order.
-func (ls LabelArray) Same(b LabelArray) bool {
+// Equals returns true if the label arrays are the same, i.e., have the same labels in the same order.
+func (ls LabelArray) Equals(b LabelArray) bool {
 	if len(ls) != len(b) {
 		return false
 	}
@@ -225,3 +225,36 @@ func (ls LabelArray) Same(b LabelArray) bool {
 	}
 	return true
 }
+
+// Less returns true if ls comes before b in the lexicographical order.
+// Assumes both ls and b are already sorted.
+func (ls LabelArray) Less(b LabelArray) bool {
+	lsLen, bLen := len(ls), len(b)
+
+	minLen := lsLen
+	if bLen < minLen {
+		minLen = bLen
+	}
+
+	for i := 0; i < minLen; i++ {
+		switch {
+		// Key
+		case ls[i].Key < b[i].Key:
+			return true
+		case ls[i].Key > b[i].Key:
+			return false
+		// Value
+		case ls[i].Value < b[i].Value:
+			return true
+		case ls[i].Value > b[i].Value:
+			return false
+		// Source
+		case ls[i].Source < b[i].Source:
+			return true
+		case ls[i].Source > b[i].Source:
+			return false
+		}
+	}
+
+	return lsLen < bLen
+}
diff --git a/pkg/labels/arraylist.go b/pkg/labels/arraylist.go
index e48224e41c..350bc1429e 100644
--- a/pkg/labels/arraylist.go
+++ b/pkg/labels/arraylist.go
@@ -14,6 +14,8 @@
 
 package labels
 
+import "sort"
+
 // LabelArrayList is an array of LabelArrays. It is primarily intended as a
 // simple collection
 type LabelArrayList []LabelArray
@@ -40,3 +42,27 @@ func (ls LabelArrayList) GetModel() [][]string {
 	}
 	return res
 }
+
+// Equals returns true if the label arrays lists have the same label arrays in the same order.
+func (ls LabelArrayList) Equals(b LabelArrayList) bool {
+	if len(ls) != len(b) {
+		return false
+	}
+	for l := range ls {
+		if !ls[l].Equals(b[l]) {
+			return false
+		}
+	}
+	return true
+}
+
+// Sort sorts the LabelArrayList in-place, but also returns the sorted list
+// for convenience. The LabelArrays themselves must already be sorted. This is
+// true for all constructors of LabelArray.
+func (ls LabelArrayList) Sort() LabelArrayList {
+	sort.Slice(ls, func(i, j int) bool {
+		return ls[i].Less(ls[j])
+	})
+
+	return ls
+}
diff --git a/pkg/option/config.go b/pkg/option/config.go
index 00e0bed8a2..11c8d69063 100644
--- a/pkg/option/config.go
+++ b/pkg/option/config.go
@@ -789,6 +789,25 @@ const (
 
 	// K8sHeartbeatTimeout configures the timeout for apiserver heartbeat
 	K8sHeartbeatTimeout = "k8s-heartbeat-timeout"
+
+	// EndpointStatus enables population of information in the
+	// CiliumEndpoint.Status resource
+	EndpointStatus = "endpoint-status"
+
+	// EndpointStatusPolicy enables CiliumEndpoint.Status.Policy
+	EndpointStatusPolicy = "policy"
+
+	// EndpointStatusHealth enables CilliumEndpoint.Status.Health
+	EndpointStatusHealth = "health"
+
+	// EndpointStatusControllers enables CiliumEndpoint.Status.Controllers
+	EndpointStatusControllers = "controllers"
+
+	// EndpointStatusLog enables CiliumEndpoint.Status.Log
+	EndpointStatusLog = "log"
+
+	// EndpointStatusState enables CiliumEndpoint.Status.State
+	EndpointStatusState = "state"
 )
 
 // Default string arguments
@@ -1579,6 +1598,10 @@ type DaemonConfig struct {
 
 	// K8sHeartbeatTimeout configures the timeout for apiserver heartbeat
 	K8sHeartbeatTimeout time.Duration
+
+	// EndpointStatus enables population of information in the
+	// CiliumEndpoint.Status resource
+	EndpointStatus map[string]struct{}
 }
 
 var (
@@ -1595,6 +1618,7 @@ var (
 		EnableIPv4:                   defaults.EnableIPv4,
 		EnableIPv6:                   defaults.EnableIPv6,
 		EnableL7Proxy:                defaults.EnableL7Proxy,
+		EndpointStatus:               make(map[string]struct{}),
 		ENITags:                      make(map[string]string),
 		ToFQDNsMaxIPsPerHost:         defaults.ToFQDNsMaxIPsPerHost,
 		KVstorePeriodicSync:          defaults.KVstorePeriodicSync,
@@ -1712,6 +1736,13 @@ func (c *DaemonConfig) IsFlannelMasterDeviceSet() bool {
 	return len(c.FlannelMasterDevice) != 0
 }
 
+// EndpointStatusIsEnabled returns true if a particular EndpointStatus* feature
+// is enabled
+func (c *DaemonConfig) EndpointStatusIsEnabled(option string) bool {
+	_, ok := c.EndpointStatus[option]
+	return ok
+}
+
 func (c *DaemonConfig) validateIPv6ClusterAllocCIDR() error {
 	ip, cidr, err := net.ParseCIDR(c.IPv6ClusterAllocCIDR)
 	if err != nil {
@@ -1821,6 +1852,13 @@ func (c *DaemonConfig) Validate() error {
 			HostReachableServicesProtos, HostServicesTCP, HostServicesUDP)
 	}
 
+	allowedEndpointStatusValues := EndpointStatusValuesMap()
+	for enabledEndpointStatus := range c.EndpointStatus {
+		if _, ok := allowedEndpointStatusValues[enabledEndpointStatus]; !ok {
+			return fmt.Errorf("unknown endpoint-status option '%s'", enabledEndpointStatus)
+		}
+	}
+
 	return nil
 }
 
@@ -2181,6 +2219,10 @@ func (c *DaemonConfig) Populate() {
 		c.IPAMAPIBurst = viper.GetInt(IPAMAPIBurst)
 	}
 
+	for _, option := range viper.GetStringSlice(EndpointStatus) {
+		c.EndpointStatus[option] = struct{}{}
+	}
+
 	if c.MonitorQueueSize == 0 {
 		c.MonitorQueueSize = getDefaultMonitorQueueSize(runtime.NumCPU())
 	}
@@ -2420,3 +2462,23 @@ func getDefaultMonitorQueueSize(numCPU int) int {
 	}
 	return monitorQueueSize
 }
+
+// EndpointStatusValues returns all available EndpointStatus option values
+func EndpointStatusValues() []string {
+	return []string{
+		EndpointStatusControllers,
+		EndpointStatusHealth,
+		EndpointStatusLog,
+		EndpointStatusPolicy,
+		EndpointStatusState,
+	}
+}
+
+// EndpointStatusValuesMap returns all EndpointStatus option values as a map
+func EndpointStatusValuesMap() (values map[string]struct{}) {
+	values = map[string]struct{}{}
+	for _, v := range EndpointStatusValues() {
+		values[v] = struct{}{}
+	}
+	return
+}
diff --git a/pkg/policy/l4.go b/pkg/policy/l4.go
index e3f223a42b..64a172a05d 100644
--- a/pkg/policy/l4.go
+++ b/pkg/policy/l4.go
@@ -241,7 +241,7 @@ func (l4 *L4Filter) GetPort() uint16 {
 }
 
 // ToMapState converts filter into a MapState with two possible values:
-// - NoRedirectEntry (ProxyPort = 0): No proxy redirection is needed for this key
+// - Entry with ProxyPort = 0: No proxy redirection is needed for this key
 // - Entry with any other port #: Proxy redirection is required for this key,
 //                                caller must replace the ProxyPort with the actual
 //                                listening port number.
@@ -291,11 +291,8 @@ func (l4 *L4Filter) ToMapState(direction trafficdirection.TrafficDirection) MapS
 			}).Debug("ToMapState: Skipping L3/L4 key due to existing L4-only key")
 			continue
 		}
-		entry := NoRedirectEntry
-		if l7 != nil {
-			entry = redirectEntry
-		}
 
+		entry := NewMapStateEntry(l4.DerivedFromRules, l7 != nil)
 		if cs.IsWildcard() {
 			keyToAdd.Identity = 0
 			keysToAdd.RedirectPreferredInsert(keyToAdd, entry)
@@ -364,7 +361,7 @@ func (l4 *L4Filter) IdentitySelectionUpdated(selector CachedSelector, selections
 			direction = trafficdirection.Ingress
 		}
 		l4Policy.AccumulateMapChanges(added, deleted, uint16(l4.Port), uint8(l4.U8Proto), direction,
-			l4.L7RulesPerSelector[selector] != nil)
+			l4.L7RulesPerSelector[selector] != nil, l4.DerivedFromRules)
 	}
 }
 
@@ -546,6 +543,10 @@ func (l4 *L4Filter) detach(selectorCache *SelectorCache) {
 }
 
 func (l4 *L4Filter) attach(ctx PolicyContext, l4Policy *L4Policy) {
+	// All rules have been added to the L4Filter at this point.
+	// Sort the rules label array list for more efficient equality comparison.
+	l4.DerivedFromRules.Sort()
+
 	// Compute Envoy policies when a policy is ready to be used
 	if ctx != nil {
 		for _, l7policy := range l4.L7RulesPerSelector {
@@ -802,10 +803,11 @@ func (l4 *L4Policy) insertUser(user *EndpointPolicy) {
 // The caller is responsible for making sure the same identity is not
 // present in both 'adds' and 'deletes'.
 func (l4 *L4Policy) AccumulateMapChanges(adds, deletes []identity.NumericIdentity,
-	port uint16, proto uint8, direction trafficdirection.TrafficDirection, redirect bool) {
+	port uint16, proto uint8, direction trafficdirection.TrafficDirection,
+	redirect bool, derivedFrom labels.LabelArrayList) {
 	l4.mutex.RLock()
 	for epPolicy := range l4.users {
-		epPolicy.policyMapChanges.AccumulateMapChanges(adds, deletes, port, proto, direction, redirect)
+		epPolicy.policyMapChanges.AccumulateMapChanges(adds, deletes, port, proto, direction, redirect, derivedFrom)
 	}
 	l4.mutex.RUnlock()
 }
diff --git a/pkg/policy/mapstate.go b/pkg/policy/mapstate.go
index 4c6478668e..fc416616e9 100644
--- a/pkg/policy/mapstate.go
+++ b/pkg/policy/mapstate.go
@@ -16,6 +16,7 @@ package policy
 
 import (
 	"github.com/cilium/cilium/pkg/identity"
+	"github.com/cilium/cilium/pkg/labels"
 	"github.com/cilium/cilium/pkg/lock"
 	"github.com/cilium/cilium/pkg/logging/logfields"
 	"github.com/cilium/cilium/pkg/option"
@@ -32,6 +33,14 @@ var (
 	}
 )
 
+const (
+	LabelKeyPolicyDerivedFrom  = "io.cilium.policy.derived-from"
+	LabelAllowLocalHostIngress = "allow-localhost-ingress"
+	LabelAllowAnyIngress       = "allow-any-ingress"
+	LabelAllowAnyEgress        = "allow-any-egress"
+	LabelVisibilityAnnotation  = "visibility-annotation"
+)
+
 // MapState is a state of a policy map.
 type MapState map[Key]MapStateEntry
 
@@ -68,22 +77,47 @@ type MapStateEntry struct {
 	// If 0 (default), there is no proxy redirection for the corresponding
 	// Key. Any other value signifies proxy redirection.
 	ProxyPort uint16
+
+	// DerivedFromRules tracks the policy rules this entry derives from
+	DerivedFromRules labels.LabelArrayList
 }
 
-// NoRedirectEntry is a special MapStateEntry used to signify that the
-// entry is not redirecting to a proxy.
-var NoRedirectEntry = MapStateEntry{ProxyPort: 0}
+// NewMapStateEntry creates a map state entry. If redirect is true, the
+// caller is expected to replace the ProxyPort field before it is added to
+// the actual BPF map.
+func NewMapStateEntry(derivedFrom labels.LabelArrayList, redirect bool) MapStateEntry {
+	var proxyPort uint16
+	if redirect {
+		// Any non-zero value will do, as the callers replace this with the
+		// actual proxy listening port number before the entry is added to the
+		// actual bpf map.
+		proxyPort = 1
+	}
+
+	return MapStateEntry{
+		ProxyPort:        proxyPort,
+		DerivedFromRules: derivedFrom,
+	}
+}
 
-// redirectEntry is a MapStateEntry used to signify that the entry
-// must redirect to a proxy. Any non-zero value would do, as the
-// callers replace this with the actual proxy listening port number
-// before the entry is added to the actual bpf map.
-var redirectEntry = MapStateEntry{ProxyPort: 1}
+// IsRedirectEntry returns true if e contains a redirect
+func (e *MapStateEntry) IsRedirectEntry() bool {
+	return e.ProxyPort != 0
+}
+
+// Equal returns true of two entries are equal
+func (e *MapStateEntry) Equal(o *MapStateEntry) bool {
+	if e == nil || o == nil {
+		return e == o
+	}
+
+	return e.ProxyPort == o.ProxyPort && e.DerivedFromRules.Equals(o.DerivedFromRules)
+}
 
 // RedirectPreferredInsert inserts a new entry giving priority to L7-redirects by
 // not overwriting a L7-redirect entry with a non-redirect entry
 func (keys MapState) RedirectPreferredInsert(key Key, entry MapStateEntry) {
-	if entry == NoRedirectEntry {
+	if !entry.IsRedirectEntry() {
 		if _, ok := keys[key]; ok {
 			// Key already exist, keep the existing entry so that
 			// a redirect entry is never overwritten by a non-redirect
@@ -100,7 +134,12 @@ func (keys MapState) RedirectPreferredInsert(key Key, entry MapStateEntry) {
 // endpoint.
 func (keys MapState) DetermineAllowLocalhostIngress(l4Policy *L4Policy) {
 	if option.Config.AlwaysAllowLocalhost() {
-		keys[localHostKey] = MapStateEntry{}
+		derivedFrom := labels.LabelArrayList{
+			labels.LabelArray{
+				labels.NewLabel(LabelKeyPolicyDerivedFrom, LabelAllowLocalHostIngress, labels.LabelSourceReserved),
+			},
+		}
+		keys[localHostKey] = NewMapStateEntry(derivedFrom, false)
 	}
 }
 
@@ -115,7 +154,12 @@ func (keys MapState) AllowAllIdentities(ingress, egress bool) {
 			Nexthdr:          0,
 			TrafficDirection: trafficdirection.Ingress.Uint8(),
 		}
-		keys[keyToAdd] = MapStateEntry{}
+		derivedFrom := labels.LabelArrayList{
+			labels.LabelArray{
+				labels.NewLabel(LabelKeyPolicyDerivedFrom, LabelAllowLocalHostIngress, labels.LabelSourceReserved),
+			},
+		}
+		keys[keyToAdd] = NewMapStateEntry(derivedFrom, false)
 	}
 	if egress {
 		keyToAdd := Key{
@@ -124,7 +168,12 @@ func (keys MapState) AllowAllIdentities(ingress, egress bool) {
 			Nexthdr:          0,
 			TrafficDirection: trafficdirection.Egress.Uint8(),
 		}
-		keys[keyToAdd] = MapStateEntry{}
+		derivedFrom := labels.LabelArrayList{
+			labels.LabelArray{
+				labels.NewLabel(LabelKeyPolicyDerivedFrom, LabelAllowAnyEgress, labels.LabelSourceReserved),
+			},
+		}
+		keys[keyToAdd] = NewMapStateEntry(derivedFrom, false)
 	}
 }
 
@@ -147,7 +196,8 @@ type MapChanges struct {
 // cases where an identity is first added and then deleted, or first
 // deleted and then added.
 func (mc *MapChanges) AccumulateMapChanges(adds, deletes []identity.NumericIdentity,
-	port uint16, proto uint8, direction trafficdirection.TrafficDirection, redirect bool) {
+	port uint16, proto uint8, direction trafficdirection.TrafficDirection,
+	redirect bool, derivedFrom labels.LabelArrayList) {
 	key := Key{
 		// The actual identity is set in the loops below
 		Identity: 0,
@@ -156,10 +206,8 @@ func (mc *MapChanges) AccumulateMapChanges(adds, deletes []identity.NumericIdent
 		Nexthdr:          proto,
 		TrafficDirection: direction.Uint8(),
 	}
-	var value MapStateEntry // defaults to ProxyPort 0 == no redirect
-	if redirect {
-		value = redirectEntry // Special entry to mark the need for redirection
-	}
+
+	value := NewMapStateEntry(derivedFrom, redirect)
 
 	if option.Config.Debug {
 		log.WithFields(logrus.Fields{
@@ -179,7 +227,7 @@ func (mc *MapChanges) AccumulateMapChanges(adds, deletes []identity.NumericIdent
 		}
 		for _, id := range adds {
 			key.Identity = id.Uint32()
-			// insert but do not allow NoRedirectEntry to overwrite a redirect entry
+			// insert but do not allow non-redirect entries to overwrite a redirect entry
 			mc.adds.RedirectPreferredInsert(key, value)
 
 			// Remove a potential previously deleted key
diff --git a/pkg/policy/resolve.go b/pkg/policy/resolve.go
index f6d33a59da..ce6e5b768e 100644
--- a/pkg/policy/resolve.go
+++ b/pkg/policy/resolve.go
@@ -157,13 +157,13 @@ func (p *EndpointPolicy) computeDirectionL4PolicyMapEntries(l4PolicyMap L4Policy
 		keysFromFilter := filter.ToMapState(direction)
 		for keyFromFilter, entry := range keysFromFilter {
 			// Fix up the proxy port for entries that need proxy redirection
-			if entry != NoRedirectEntry {
+			if entry.IsRedirectEntry() {
 				entry.ProxyPort = p.PolicyOwner.LookupRedirectPort(filter)
 				// If the currently allocated proxy port is 0, this is a new
 				// redirect, for which no port has been allocated yet. Ignore
 				// it for now. This will be configured by
 				// e.addNewRedirectsFromDesiredPolicy() once the port has been allocated.
-				if entry == NoRedirectEntry {
+				if !entry.IsRedirectEntry() {
 					continue
 				}
 			}
diff --git a/pkg/policy/selectorcache.go b/pkg/policy/selectorcache.go
index bc13256f7b..3a500046e2 100644
--- a/pkg/policy/selectorcache.go
+++ b/pkg/policy/selectorcache.go
@@ -791,7 +791,7 @@ func (sc *SelectorCache) UpdateIdentities(added, deleted cache.IdentityCache) {
 			// order is different, but identity labels are
 			// sorted for the kv-store, so there should
 			// not be too many false negatives.
-			if lbls.Same(old.lbls) {
+			if lbls.Equals(old.lbls) {
 				log.WithFields(logrus.Fields{logfields.Identity: numericID}).Debug("UpdateIdentities: Skipping add of an existing identical identity")
 				delete(added, numericID)
 				continue


Test patch:
diff --git a/daemon/cmd/daemon_test.go b/daemon/cmd/daemon_test.go
index 96bc836f88..7d0690e267 100644
--- a/daemon/cmd/daemon_test.go
+++ b/daemon/cmd/daemon_test.go
@@ -103,7 +103,8 @@ func TestMain(m *testing.M) {
 
 type dummyEpSyncher struct{}
 
-func (epSync *dummyEpSyncher) RunK8sCiliumEndpointSync(e *endpoint.Endpoint) {}
+func (epSync *dummyEpSyncher) RunK8sCiliumEndpointSync(e *endpoint.Endpoint, conf endpoint.EndpointStatusConfiguration) {
+}
 
 func (ds *DaemonSuite) SetUpTest(c *C) {
 
diff --git a/daemon/cmd/policy_test.go b/daemon/cmd/policy_test.go
index 74fe9e7d98..a8b43d97ba 100644
--- a/daemon/cmd/policy_test.go
+++ b/daemon/cmd/policy_test.go
@@ -807,7 +807,7 @@ func (d *dummyManager) AllocateID(id uint16) (uint16, error) {
 	return uint16(1), nil
 }
 
-func (d *dummyManager) RunK8sCiliumEndpointSync(*endpoint.Endpoint) {
+func (d *dummyManager) RunK8sCiliumEndpointSync(*endpoint.Endpoint, endpoint.EndpointStatusConfiguration) {
 }
 
 func (d *dummyManager) UpdateReferences(map[id.PrefixType]string, *endpoint.Endpoint) {
diff --git a/pkg/datapath/link/link_test.go b/pkg/datapath/link/link_test.go
new file mode 100644
index 0000000000..b4028db7da
--- /dev/null
+++ b/pkg/datapath/link/link_test.go
@@ -0,0 +1,113 @@
+// Copyright 2020 Authors of Cilium
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build privileged_tests
+
+package link
+
+import (
+	"testing"
+
+	"github.com/vishvananda/netlink"
+	. "gopkg.in/check.v1"
+)
+
+type LinkSuite struct{}
+
+var _ = Suite(&LinkSuite{})
+
+func Test(t *testing.T) {
+	TestingT(t)
+}
+
+func (s *LinkSuite) TestDeleteByName(c *C) {
+	testCases := []struct {
+		name        string
+		create      bool
+		expectError bool
+	}{
+		{
+			"foo",
+			true,
+			false,
+		},
+		{
+			"bar",
+			false,
+			true,
+		},
+	}
+	var err error
+
+	for _, tc := range testCases {
+		if tc.create {
+			err = netlink.LinkAdd(&netlink.Dummy{
+				LinkAttrs: netlink.LinkAttrs{
+					Name: tc.name,
+				},
+			})
+			c.Assert(err, IsNil)
+		}
+
+		err = DeleteByName(tc.name)
+		if tc.expectError {
+			c.Assert(err, NotNil)
+		} else {
+			c.Assert(err, IsNil)
+		}
+	}
+}
+
+func (s *LinkSuite) TestRename(c *C) {
+	testCases := []struct {
+		curName     string
+		newName     string
+		create      bool
+		expectError bool
+	}{
+		{
+			"abc",
+			"xyz",
+			true,
+			false,
+		},
+		{
+			"fizz",
+			"buzz",
+			false,
+			true,
+		},
+	}
+	var err error
+
+	for _, tc := range testCases {
+		if tc.create {
+			err = netlink.LinkAdd(&netlink.Dummy{
+				LinkAttrs: netlink.LinkAttrs{
+					Name: tc.curName,
+				},
+			})
+			c.Assert(err, IsNil)
+		}
+
+		err = Rename(tc.curName, tc.newName)
+		if tc.expectError {
+			c.Assert(err, NotNil)
+		} else {
+			c.Assert(err, IsNil)
+		}
+
+		DeleteByName(tc.newName)
+	}
+}
diff --git a/pkg/datapath/linux/probes/probes_test.go b/pkg/datapath/linux/probes/probes_test.go
index 76501b8b22..b1e1c46490 100644
--- a/pkg/datapath/linux/probes/probes_test.go
+++ b/pkg/datapath/linux/probes/probes_test.go
@@ -66,12 +66,28 @@ func (s *ProbesTestSuite) TestSystemConfigProbes(c *C) {
 			},
 			expectErr: false,
 		},
+		// Disable options which generate errors
 		{
 			systemConfig: SystemConfig{
-				ConfigBpf:           "y",
-				ConfigBpfSyscall:    "y",
+				ConfigBpf:           "n",
+				ConfigBpfSyscall:    "n",
 				ConfigNetSchIngress: "n",
 				ConfigNetClsBpf:     "n",
+				ConfigNetClsAct:     "n",
+				ConfigBpfJit:        "n",
+				ConfigHaveEbpfJit:   "n",
+				ConfigCgroupBpf:     "n",
+				ConfigLwtunnelBpf:   "n",
+				ConfigBpfEvents:     "n",
+			},
+			expectErr: true,
+		},
+		{
+			systemConfig: SystemConfig{
+				ConfigBpf:           "n",
+				ConfigBpfSyscall:    "y",
+				ConfigNetSchIngress: "y",
+				ConfigNetClsBpf:     "y",
 				ConfigNetClsAct:     "y",
 				ConfigBpfJit:        "y",
 				ConfigHaveEbpfJit:   "y",
@@ -83,18 +99,154 @@ func (s *ProbesTestSuite) TestSystemConfigProbes(c *C) {
 		},
 		{
 			systemConfig: SystemConfig{
-				ConfigBpf:           "n",
+				ConfigBpf:           "y",
 				ConfigBpfSyscall:    "n",
+				ConfigNetSchIngress: "y",
+				ConfigNetClsBpf:     "y",
+				ConfigNetClsAct:     "y",
+				ConfigBpfJit:        "y",
+				ConfigHaveEbpfJit:   "y",
+				ConfigCgroupBpf:     "y",
+				ConfigLwtunnelBpf:   "y",
+				ConfigBpfEvents:     "y",
+			},
+			expectErr: true,
+		},
+		{
+			systemConfig: SystemConfig{
+				ConfigBpf:           "y",
+				ConfigBpfSyscall:    "y",
 				ConfigNetSchIngress: "n",
+				ConfigNetClsBpf:     "y",
+				ConfigNetClsAct:     "y",
+				ConfigBpfJit:        "y",
+				ConfigHaveEbpfJit:   "y",
+				ConfigCgroupBpf:     "y",
+				ConfigLwtunnelBpf:   "y",
+				ConfigBpfEvents:     "y",
+			},
+			expectErr: true,
+		},
+		{
+			systemConfig: SystemConfig{
+				ConfigBpf:           "y",
+				ConfigBpfSyscall:    "y",
+				ConfigNetSchIngress: "y",
 				ConfigNetClsBpf:     "n",
+				ConfigNetClsAct:     "y",
+				ConfigBpfJit:        "y",
+				ConfigHaveEbpfJit:   "y",
+				ConfigCgroupBpf:     "y",
+				ConfigLwtunnelBpf:   "y",
+				ConfigBpfEvents:     "y",
+			},
+			expectErr: true,
+		},
+		{
+			systemConfig: SystemConfig{
+				ConfigBpf:           "y",
+				ConfigBpfSyscall:    "y",
+				ConfigNetSchIngress: "y",
+				ConfigNetClsBpf:     "y",
 				ConfigNetClsAct:     "n",
+				ConfigBpfJit:        "y",
+				ConfigHaveEbpfJit:   "y",
+				ConfigCgroupBpf:     "y",
+				ConfigLwtunnelBpf:   "y",
+				ConfigBpfEvents:     "y",
+			},
+			expectErr: true,
+		},
+		{
+			systemConfig: SystemConfig{
+				ConfigBpf:           "y",
+				ConfigBpfSyscall:    "y",
+				ConfigNetSchIngress: "y",
+				ConfigNetClsBpf:     "y",
+				ConfigNetClsAct:     "y",
 				ConfigBpfJit:        "n",
+				ConfigHaveEbpfJit:   "y",
+				ConfigCgroupBpf:     "y",
+				ConfigLwtunnelBpf:   "y",
+				ConfigBpfEvents:     "y",
+			},
+			expectErr: true,
+		},
+		{
+			systemConfig: SystemConfig{
+				ConfigBpf:           "y",
+				ConfigBpfSyscall:    "y",
+				ConfigNetSchIngress: "y",
+				ConfigNetClsBpf:     "y",
+				ConfigNetClsAct:     "y",
+				ConfigBpfJit:        "y",
 				ConfigHaveEbpfJit:   "n",
+				ConfigCgroupBpf:     "y",
+				ConfigLwtunnelBpf:   "y",
+				ConfigBpfEvents:     "y",
+			},
+			expectErr: true,
+		},
+		// Disable options which generate warnings
+		{
+			systemConfig: SystemConfig{
+				ConfigBpf:           "y",
+				ConfigBpfSyscall:    "y",
+				ConfigNetSchIngress: "y",
+				ConfigNetClsBpf:     "y",
+				ConfigNetClsAct:     "y",
+				ConfigBpfJit:        "y",
+				ConfigHaveEbpfJit:   "y",
 				ConfigCgroupBpf:     "n",
 				ConfigLwtunnelBpf:   "n",
 				ConfigBpfEvents:     "n",
 			},
-			expectErr: true,
+			expectErr: false,
+		},
+		{
+			systemConfig: SystemConfig{
+				ConfigBpf:           "y",
+				ConfigBpfSyscall:    "y",
+				ConfigNetSchIngress: "y",
+				ConfigNetClsBpf:     "y",
+				ConfigNetClsAct:     "y",
+				ConfigBpfJit:        "y",
+				ConfigHaveEbpfJit:   "y",
+				ConfigCgroupBpf:     "n",
+				ConfigLwtunnelBpf:   "y",
+				ConfigBpfEvents:     "y",
+			},
+			expectErr: false,
+		},
+		{
+			systemConfig: SystemConfig{
+				ConfigBpf:           "y",
+				ConfigBpfSyscall:    "y",
+				ConfigNetSchIngress: "y",
+				ConfigNetClsBpf:     "y",
+				ConfigNetClsAct:     "y",
+				ConfigBpfJit:        "y",
+				ConfigHaveEbpfJit:   "y",
+				ConfigCgroupBpf:     "y",
+				ConfigLwtunnelBpf:   "n",
+				ConfigBpfEvents:     "y",
+			},
+			expectErr: false,
+		},
+		{
+			systemConfig: SystemConfig{
+				ConfigBpf:           "y",
+				ConfigBpfSyscall:    "y",
+				ConfigNetSchIngress: "y",
+				ConfigNetClsBpf:     "y",
+				ConfigNetClsAct:     "y",
+				ConfigBpfJit:        "y",
+				ConfigHaveEbpfJit:   "y",
+				ConfigCgroupBpf:     "y",
+				ConfigLwtunnelBpf:   "y",
+				ConfigBpfEvents:     "n",
+			},
+			expectErr: false,
 		},
 	}
 	for _, tc := range testCases {
diff --git a/pkg/endpoint/endpoint_status_test.go b/pkg/endpoint/endpoint_status_test.go
index 3b3b497a1a..f238e50d15 100644
--- a/pkg/endpoint/endpoint_status_test.go
+++ b/pkg/endpoint/endpoint_status_test.go
@@ -1,4 +1,4 @@
-// Copyright 2019 Authors of Cilium
+// Copyright 2019-2020 Authors of Cilium
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@ import (
 	"github.com/cilium/cilium/pkg/identity"
 	cilium_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
 	"github.com/cilium/cilium/pkg/labels"
+	"github.com/cilium/cilium/pkg/option"
 	"github.com/cilium/cilium/pkg/policy"
 	"github.com/cilium/cilium/pkg/policy/trafficdirection"
 
@@ -48,6 +49,15 @@ type endpointGeneratorSpec struct {
 	fakeControllerManager    bool
 }
 
+type endpointStatusConfiguration map[string]bool
+
+func (e endpointStatusConfiguration) EndpointStatusIsEnabled(name string) bool {
+	if e != nil {
+		return e[string(name)]
+	}
+	return false
+}
+
 func (s *EndpointSuite) newEndpoint(c *check.C, spec endpointGeneratorSpec) *Endpoint {
 	e, err := NewEndpointFromChangeModel(context.TODO(), s, &FakeEndpointProxy{}, s.mgr, &models.EndpointChangeRequest{
 		Addressing: &models.AddressPair{},
@@ -115,7 +125,7 @@ func (s *EndpointSuite) newEndpoint(c *check.C, spec endpointGeneratorSpec) *End
 
 func (s *EndpointSuite) TestGetCiliumEndpointStatusSuccessfulControllers(c *check.C) {
 	e := s.newEndpoint(c, endpointGeneratorSpec{})
-	cepA := e.GetCiliumEndpointStatus()
+	cepA := e.GetCiliumEndpointStatus(&endpointStatusConfiguration{})
 
 	// Run successful controllers in the background
 	for i := 0; i < 50; i++ {
@@ -139,7 +149,7 @@ func (s *EndpointSuite) TestGetCiliumEndpointStatusSuccessfulControllers(c *chec
 		case <-timeout:
 			return
 		case <-tick:
-			cepB := e.GetCiliumEndpointStatus()
+			cepB := e.GetCiliumEndpointStatus(&endpointStatusConfiguration{})
 			c.Assert(cepA, checker.DeepEquals, cepB)
 		}
 	}
@@ -147,7 +157,7 @@ func (s *EndpointSuite) TestGetCiliumEndpointStatusSuccessfulControllers(c *chec
 
 func (s *EndpointSuite) TestGetCiliumEndpointStatusSuccessfulLog(c *check.C) {
 	e := s.newEndpoint(c, endpointGeneratorSpec{})
-	cepA := e.GetCiliumEndpointStatus()
+	cepA := e.GetCiliumEndpointStatus(&endpointStatusConfiguration{})
 
 	go func() {
 		for i := 0; i < 1000; i++ {
@@ -167,7 +177,7 @@ func (s *EndpointSuite) TestGetCiliumEndpointStatusSuccessfulLog(c *check.C) {
 		case <-timeout:
 			return
 		case <-tick:
-			cepB := e.GetCiliumEndpointStatus()
+			cepB := e.GetCiliumEndpointStatus(&endpointStatusConfiguration{})
 			c.Assert(cepA, checker.DeepEquals, cepB)
 		}
 	}
@@ -192,8 +202,8 @@ func (s *EndpointSuite) TestGetCiliumEndpointStatusDeepEqual(c *check.C) {
 		numPortsPerIdentity:      10,
 	})
 
-	cepA := a.GetCiliumEndpointStatus()
-	cepB := b.GetCiliumEndpointStatus()
+	cepA := a.GetCiliumEndpointStatus(&endpointStatusConfiguration{})
+	cepB := b.GetCiliumEndpointStatus(&endpointStatusConfiguration{})
 
 	c.Assert(cepA, checker.DeepEquals, cepB)
 }
@@ -208,7 +218,9 @@ func (s *EndpointSuite) TestGetCiliumEndpointStatusCorrectnes(c *check.C) {
 		numPortsPerIdentity:      10,
 	})
 
-	cep := e.GetCiliumEndpointStatus()
+	cep := e.GetCiliumEndpointStatus(&endpointStatusConfiguration{
+		option.EndpointStatusLog: true,
+	})
 
 	c.Assert(len(cep.Log), check.Equals, cilium_v2.EndpointStatusLogEntries)
 }
@@ -452,7 +464,7 @@ func (s *EndpointSuite) BenchmarkGetCiliumEndpointStatus(c *check.C) {
 
 	c.ResetTimer()
 	for i := 0; i < c.N; i++ {
-		status := e.GetCiliumEndpointStatus()
+		status := e.GetCiliumEndpointStatus(&endpointStatusConfiguration{})
 		c.Assert(status, check.Not(check.IsNil))
 	}
 	c.StopTimer()
diff --git a/pkg/endpoint/endpoint_test.go b/pkg/endpoint/endpoint_test.go
index a4f48ce46f..ffe32400f3 100644
--- a/pkg/endpoint/endpoint_test.go
+++ b/pkg/endpoint/endpoint_test.go
@@ -669,7 +669,7 @@ func (d *dummyManager) AllocateID(id uint16) (uint16, error) {
 	return uint16(1), nil
 }
 
-func (d *dummyManager) RunK8sCiliumEndpointSync(*Endpoint) {
+func (d *dummyManager) RunK8sCiliumEndpointSync(*Endpoint, EndpointStatusConfiguration) {
 }
 
 func (d *dummyManager) UpdateReferences(map[id.PrefixType]string, *Endpoint) {
diff --git a/pkg/endpointmanager/manager_test.go b/pkg/endpointmanager/manager_test.go
index 8aba9c2197..108b8c2889 100644
--- a/pkg/endpointmanager/manager_test.go
+++ b/pkg/endpointmanager/manager_test.go
@@ -95,7 +95,8 @@ func (d *DummyRuleCacheOwner) ClearPolicyConsumers(id uint16) *sync.WaitGroup {
 
 type dummyEpSyncher struct{}
 
-func (epSync *dummyEpSyncher) RunK8sCiliumEndpointSync(e *endpoint.Endpoint) {}
+func (epSync *dummyEpSyncher) RunK8sCiliumEndpointSync(e *endpoint.Endpoint, conf endpoint.EndpointStatusConfiguration) {
+}
 
 func (s *EndpointManagerSuite) TestLookup(c *C) {
 	ep := endpoint.NewEndpointWithState(s, &endpoint.FakeEndpointProxy{}, &allocator.FakeIdentityAllocator{}, 10, endpoint.StateReady)
diff --git a/pkg/labels/array_test.go b/pkg/labels/array_test.go
index 415d6d4af9..15418dc8b5 100644
--- a/pkg/labels/array_test.go
+++ b/pkg/labels/array_test.go
@@ -84,7 +84,7 @@ func (s *LabelsSuite) TestHas(c *C) {
 	}
 }
 
-func (s *LabelsSuite) TestSame(c *C) {
+func (s *LabelsSuite) TestEquals(c *C) {
 	lbls1 := LabelArray{
 		NewLabel("env", "devel", LabelSourceAny),
 		NewLabel("user", "bob", LabelSourceContainer),
@@ -105,47 +105,149 @@ func (s *LabelsSuite) TestSame(c *C) {
 	}
 	lbls6 := LabelArray{}
 
-	c.Assert(lbls1.Same(lbls1), Equals, true)
-	c.Assert(lbls1.Same(lbls2), Equals, true)
-	c.Assert(lbls1.Same(lbls3), Equals, false) // inverted order
-	c.Assert(lbls1.Same(lbls4), Equals, false) // different count
-	c.Assert(lbls1.Same(lbls5), Equals, false)
-	c.Assert(lbls1.Same(lbls6), Equals, false)
-
-	c.Assert(lbls2.Same(lbls1), Equals, true)
-	c.Assert(lbls2.Same(lbls2), Equals, true)
-	c.Assert(lbls2.Same(lbls3), Equals, false) // inverted order
-	c.Assert(lbls2.Same(lbls4), Equals, false) // different count
-	c.Assert(lbls2.Same(lbls5), Equals, false)
-	c.Assert(lbls2.Same(lbls6), Equals, false)
-
-	c.Assert(lbls3.Same(lbls1), Equals, false)
-	c.Assert(lbls3.Same(lbls2), Equals, false)
-	c.Assert(lbls3.Same(lbls3), Equals, true)
-	c.Assert(lbls3.Same(lbls4), Equals, false)
-	c.Assert(lbls3.Same(lbls5), Equals, false)
-	c.Assert(lbls3.Same(lbls6), Equals, false)
-
-	c.Assert(lbls4.Same(lbls1), Equals, false)
-	c.Assert(lbls4.Same(lbls2), Equals, false)
-	c.Assert(lbls4.Same(lbls3), Equals, false)
-	c.Assert(lbls4.Same(lbls4), Equals, true)
-	c.Assert(lbls4.Same(lbls5), Equals, false)
-	c.Assert(lbls4.Same(lbls6), Equals, false)
-
-	c.Assert(lbls5.Same(lbls1), Equals, false)
-	c.Assert(lbls5.Same(lbls2), Equals, false)
-	c.Assert(lbls5.Same(lbls3), Equals, false)
-	c.Assert(lbls5.Same(lbls4), Equals, false)
-	c.Assert(lbls5.Same(lbls5), Equals, true)
-	c.Assert(lbls5.Same(lbls6), Equals, false)
-
-	c.Assert(lbls6.Same(lbls1), Equals, false)
-	c.Assert(lbls6.Same(lbls2), Equals, false)
-	c.Assert(lbls6.Same(lbls3), Equals, false)
-	c.Assert(lbls6.Same(lbls4), Equals, false)
-	c.Assert(lbls6.Same(lbls5), Equals, false)
-	c.Assert(lbls6.Same(lbls6), Equals, true)
+	c.Assert(lbls1.Equals(lbls1), Equals, true)
+	c.Assert(lbls1.Equals(lbls2), Equals, true)
+	c.Assert(lbls1.Equals(lbls3), Equals, false) // inverted order
+	c.Assert(lbls1.Equals(lbls4), Equals, false) // different count
+	c.Assert(lbls1.Equals(lbls5), Equals, false)
+	c.Assert(lbls1.Equals(lbls6), Equals, false)
+
+	c.Assert(lbls2.Equals(lbls1), Equals, true)
+	c.Assert(lbls2.Equals(lbls2), Equals, true)
+	c.Assert(lbls2.Equals(lbls3), Equals, false) // inverted order
+	c.Assert(lbls2.Equals(lbls4), Equals, false) // different count
+	c.Assert(lbls2.Equals(lbls5), Equals, false)
+	c.Assert(lbls2.Equals(lbls6), Equals, false)
+
+	c.Assert(lbls3.Equals(lbls1), Equals, false)
+	c.Assert(lbls3.Equals(lbls2), Equals, false)
+	c.Assert(lbls3.Equals(lbls3), Equals, true)
+	c.Assert(lbls3.Equals(lbls4), Equals, false)
+	c.Assert(lbls3.Equals(lbls5), Equals, false)
+	c.Assert(lbls3.Equals(lbls6), Equals, false)
+
+	c.Assert(lbls4.Equals(lbls1), Equals, false)
+	c.Assert(lbls4.Equals(lbls2), Equals, false)
+	c.Assert(lbls4.Equals(lbls3), Equals, false)
+	c.Assert(lbls4.Equals(lbls4), Equals, true)
+	c.Assert(lbls4.Equals(lbls5), Equals, false)
+	c.Assert(lbls4.Equals(lbls6), Equals, false)
+
+	c.Assert(lbls5.Equals(lbls1), Equals, false)
+	c.Assert(lbls5.Equals(lbls2), Equals, false)
+	c.Assert(lbls5.Equals(lbls3), Equals, false)
+	c.Assert(lbls5.Equals(lbls4), Equals, false)
+	c.Assert(lbls5.Equals(lbls5), Equals, true)
+	c.Assert(lbls5.Equals(lbls6), Equals, false)
+
+	c.Assert(lbls6.Equals(lbls1), Equals, false)
+	c.Assert(lbls6.Equals(lbls2), Equals, false)
+	c.Assert(lbls6.Equals(lbls3), Equals, false)
+	c.Assert(lbls6.Equals(lbls4), Equals, false)
+	c.Assert(lbls6.Equals(lbls5), Equals, false)
+	c.Assert(lbls6.Equals(lbls6), Equals, true)
+}
+
+func (s *LabelsSuite) TestLess(c *C) {
+	// lbls1-lbls8 are defined in lexical order
+	lbls1 := LabelArray(nil)
+	lbls2 := LabelArray{
+		NewLabel("env", "devel", LabelSourceAny),
+	}
+	lbls3 := LabelArray{
+		NewLabel("env", "devel", LabelSourceReserved),
+	}
+	lbls4 := LabelArray{
+		NewLabel("env", "prod", LabelSourceAny),
+	}
+	lbls5 := LabelArray{
+		NewLabel("env", "prod", LabelSourceAny),
+		NewLabel("user", "bob", LabelSourceContainer),
+	}
+	lbls6 := LabelArray{
+		NewLabel("env", "prod", LabelSourceAny),
+		NewLabel("user", "bob", LabelSourceContainer),
+	}
+	lbls7 := LabelArray{
+		NewLabel("env", "prod", LabelSourceAny),
+		NewLabel("user", "bob", LabelSourceContainer),
+		NewLabel("xyz", "", LabelSourceAny),
+	}
+	lbls8 := LabelArray{
+		NewLabel("foo", "bar", LabelSourceAny),
+	}
+
+	c.Assert(lbls1.Less(lbls1), Equals, false)
+	c.Assert(lbls1.Less(lbls2), Equals, true)
+	c.Assert(lbls1.Less(lbls3), Equals, true)
+	c.Assert(lbls1.Less(lbls4), Equals, true)
+	c.Assert(lbls1.Less(lbls5), Equals, true)
+	c.Assert(lbls1.Less(lbls6), Equals, true)
+	c.Assert(lbls1.Less(lbls7), Equals, true)
+	c.Assert(lbls1.Less(lbls8), Equals, true)
+
+	c.Assert(lbls2.Less(lbls1), Equals, false)
+	c.Assert(lbls2.Less(lbls2), Equals, false)
+	c.Assert(lbls2.Less(lbls3), Equals, true)
+	c.Assert(lbls2.Less(lbls4), Equals, true)
+	c.Assert(lbls2.Less(lbls5), Equals, true)
+	c.Assert(lbls2.Less(lbls6), Equals, true)
+	c.Assert(lbls2.Less(lbls7), Equals, true)
+	c.Assert(lbls2.Less(lbls8), Equals, true)
+
+	c.Assert(lbls3.Less(lbls1), Equals, false)
+	c.Assert(lbls3.Less(lbls2), Equals, false)
+	c.Assert(lbls3.Less(lbls3), Equals, false)
+	c.Assert(lbls3.Less(lbls4), Equals, true)
+	c.Assert(lbls3.Less(lbls5), Equals, true)
+	c.Assert(lbls3.Less(lbls6), Equals, true)
+	c.Assert(lbls3.Less(lbls7), Equals, true)
+	c.Assert(lbls3.Less(lbls8), Equals, true)
+
+	c.Assert(lbls4.Less(lbls1), Equals, false)
+	c.Assert(lbls4.Less(lbls2), Equals, false)
+	c.Assert(lbls4.Less(lbls3), Equals, false)
+	c.Assert(lbls4.Less(lbls4), Equals, false)
+	c.Assert(lbls4.Less(lbls5), Equals, true)
+	c.Assert(lbls4.Less(lbls6), Equals, true)
+	c.Assert(lbls4.Less(lbls7), Equals, true)
+	c.Assert(lbls4.Less(lbls8), Equals, true)
+
+	c.Assert(lbls5.Less(lbls1), Equals, false)
+	c.Assert(lbls5.Less(lbls2), Equals, false)
+	c.Assert(lbls5.Less(lbls3), Equals, false)
+	c.Assert(lbls5.Less(lbls4), Equals, false)
+	c.Assert(lbls5.Less(lbls5), Equals, false)
+	c.Assert(lbls5.Less(lbls6), Equals, false)
+	c.Assert(lbls5.Less(lbls7), Equals, true)
+	c.Assert(lbls5.Less(lbls8), Equals, true)
+
+	c.Assert(lbls6.Less(lbls1), Equals, false)
+	c.Assert(lbls6.Less(lbls2), Equals, false)
+	c.Assert(lbls6.Less(lbls3), Equals, false)
+	c.Assert(lbls6.Less(lbls4), Equals, false)
+	c.Assert(lbls6.Less(lbls5), Equals, false)
+	c.Assert(lbls6.Less(lbls6), Equals, false)
+	c.Assert(lbls6.Less(lbls7), Equals, true)
+	c.Assert(lbls6.Less(lbls8), Equals, true)
+
+	c.Assert(lbls7.Less(lbls1), Equals, false)
+	c.Assert(lbls7.Less(lbls2), Equals, false)
+	c.Assert(lbls7.Less(lbls3), Equals, false)
+	c.Assert(lbls7.Less(lbls4), Equals, false)
+	c.Assert(lbls7.Less(lbls5), Equals, false)
+	c.Assert(lbls7.Less(lbls6), Equals, false)
+	c.Assert(lbls7.Less(lbls7), Equals, false)
+	c.Assert(lbls7.Less(lbls8), Equals, true)
+
+	c.Assert(lbls8.Less(lbls1), Equals, false)
+	c.Assert(lbls8.Less(lbls2), Equals, false)
+	c.Assert(lbls8.Less(lbls3), Equals, false)
+	c.Assert(lbls8.Less(lbls4), Equals, false)
+	c.Assert(lbls8.Less(lbls5), Equals, false)
+	c.Assert(lbls8.Less(lbls6), Equals, false)
+	c.Assert(lbls8.Less(lbls7), Equals, false)
+	c.Assert(lbls8.Less(lbls8), Equals, false)
 }
 
 // TestOutputConversions tests the various ways a LabelArray can be converted
diff --git a/pkg/labels/arraylist_test.go b/pkg/labels/arraylist_test.go
new file mode 100644
index 0000000000..34f9d579fc
--- /dev/null
+++ b/pkg/labels/arraylist_test.go
@@ -0,0 +1,176 @@
+// Copyright 2020 Authors of Cilium
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !privileged_tests
+
+package labels
+
+import (
+	"github.com/cilium/cilium/pkg/checker"
+	. "gopkg.in/check.v1"
+)
+
+func (s *LabelsSuite) TestLabelArrayListEquals(c *C) {
+	list1 := LabelArrayList{
+		{
+			NewLabel("env", "devel", LabelSourceAny),
+			NewLabel("user", "bob", LabelSourceContainer),
+		},
+		{
+			NewLabel("foo", "bar", LabelSourceAny),
+		},
+	}
+	list2 := LabelArrayList{
+		{
+			NewLabel("env", "devel", LabelSourceAny),
+			NewLabel("user", "bob", LabelSourceContainer),
+		},
+		{
+			NewLabel("foo", "bar", LabelSourceAny),
+		},
+	}
+	list3 := LabelArrayList{
+		{
+			NewLabel("foo", "bar", LabelSourceAny),
+		},
+		{
+			NewLabel("env", "devel", LabelSourceAny),
+			NewLabel("user", "bob", LabelSourceContainer),
+		},
+	}
+	list4 := LabelArrayList{
+		{
+			NewLabel("env", "devel", LabelSourceAny),
+			NewLabel("user", "bob", LabelSourceContainer),
+		},
+	}
+	list5 := LabelArrayList(nil)
+	list6 := LabelArrayList{}
+
+	c.Assert(list1.Equals(list1), Equals, true)
+	c.Assert(list1.Equals(list2), Equals, true)
+	c.Assert(list1.Equals(list3), Equals, false)
+	c.Assert(list1.Equals(list4), Equals, false)
+	c.Assert(list1.Equals(list5), Equals, false)
+	c.Assert(list1.Equals(list6), Equals, false)
+
+	c.Assert(list2.Equals(list1), Equals, true)
+	c.Assert(list2.Equals(list2), Equals, true)
+	c.Assert(list2.Equals(list3), Equals, false)
+	c.Assert(list2.Equals(list4), Equals, false)
+	c.Assert(list2.Equals(list5), Equals, false)
+	c.Assert(list2.Equals(list6), Equals, false)
+
+	c.Assert(list3.Equals(list1), Equals, false)
+	c.Assert(list3.Equals(list2), Equals, false)
+	c.Assert(list3.Equals(list3), Equals, true)
+	c.Assert(list3.Equals(list4), Equals, false)
+	c.Assert(list3.Equals(list5), Equals, false)
+	c.Assert(list3.Equals(list6), Equals, false)
+
+	c.Assert(list4.Equals(list1), Equals, false)
+	c.Assert(list4.Equals(list2), Equals, false)
+	c.Assert(list4.Equals(list3), Equals, false)
+	c.Assert(list4.Equals(list4), Equals, true)
+	c.Assert(list4.Equals(list5), Equals, false)
+	c.Assert(list4.Equals(list6), Equals, false)
+
+	c.Assert(list5.Equals(list1), Equals, false)
+	c.Assert(list5.Equals(list2), Equals, false)
+	c.Assert(list5.Equals(list3), Equals, false)
+	c.Assert(list5.Equals(list4), Equals, false)
+	c.Assert(list5.Equals(list5), Equals, true)
+	c.Assert(list5.Equals(list6), Equals, true)
+
+	c.Assert(list6.Equals(list1), Equals, false)
+	c.Assert(list6.Equals(list2), Equals, false)
+	c.Assert(list6.Equals(list3), Equals, false)
+	c.Assert(list6.Equals(list4), Equals, false)
+	c.Assert(list6.Equals(list5), Equals, true)
+	c.Assert(list6.Equals(list6), Equals, true)
+}
+
+func (s *LabelsSuite) TestLabelArrayListSort(c *C) {
+	c.Assert(LabelArrayList(nil).Sort(), checker.DeepEquals, LabelArrayList(nil))
+	c.Assert(LabelArrayList{}.Sort(), checker.DeepEquals, LabelArrayList{})
+
+	list1 := LabelArrayList{
+		{
+			NewLabel("env", "devel", LabelSourceAny),
+			NewLabel("user", "bob", LabelSourceContainer),
+		},
+		{
+			NewLabel("aaa", "", LabelSourceReserved),
+		},
+		{
+			NewLabel("env", "devel", LabelSourceAny),
+			NewLabel("user", "bob", LabelSourceContainer),
+			NewLabel("xyz", "", LabelSourceAny),
+		},
+		{
+			NewLabel("env", "devel", LabelSourceAny),
+		},
+		nil,
+		{
+			NewLabel("foo", "bar", LabelSourceAny),
+		},
+	}
+	expected1 := LabelArrayList{
+		nil,
+		{
+			NewLabel("aaa", "", LabelSourceReserved),
+		},
+		{
+			NewLabel("env", "devel", LabelSourceAny),
+		},
+		{
+			NewLabel("env", "devel", LabelSourceAny),
+			NewLabel("user", "bob", LabelSourceContainer),
+		},
+		{
+			NewLabel("env", "devel", LabelSourceAny),
+			NewLabel("user", "bob", LabelSourceContainer),
+			NewLabel("xyz", "", LabelSourceAny),
+		},
+		{
+			NewLabel("foo", "bar", LabelSourceAny),
+		},
+	}
+
+	c.Assert(list1.Sort(), checker.DeepEquals, expected1)
+
+	list2 := LabelArrayList{
+		{
+			NewLabel("aaa", "", LabelSourceReserved),
+		},
+		{
+			NewLabel("env", "devel", LabelSourceAny),
+		},
+		{
+			NewLabel("aaa", "", LabelSourceAny),
+		},
+	}
+	expected2 := LabelArrayList{
+		{
+			NewLabel("aaa", "", LabelSourceAny),
+		},
+		{
+			NewLabel("aaa", "", LabelSourceReserved),
+		},
+		{
+			NewLabel("env", "devel", LabelSourceAny),
+		},
+	}
+	c.Assert(list2.Sort(), checker.DeepEquals, expected2)
+}
diff --git a/pkg/option/config_test.go b/pkg/option/config_test.go
index 358f4871ba..9e2bd749c7 100644
--- a/pkg/option/config_test.go
+++ b/pkg/option/config_test.go
@@ -250,6 +250,15 @@ func (s *OptionSuite) TestLocalAddressExclusion(c *C) {
 	c.Assert(d.IsExcludedLocalAddress(net.ParseIP("f00d::2")), Equals, false)
 }
 
+func (s *OptionSuite) TestEndpointStatusIsEnabled(c *C) {
+
+	d := DaemonConfig{}
+	d.EndpointStatus = map[string]struct{}{EndpointStatusHealth: {}, EndpointStatusPolicy: {}}
+	c.Assert(d.EndpointStatusIsEnabled(EndpointStatusHealth), Equals, true)
+	c.Assert(d.EndpointStatusIsEnabled(EndpointStatusPolicy), Equals, true)
+	c.Assert(d.EndpointStatusIsEnabled(EndpointStatusLog), Equals, false)
+}
+
 func Test_populateNodePortRange(t *testing.T) {
 	type want struct {
 		wantMin int
@@ -421,3 +430,12 @@ func (s *OptionSuite) TestGetDefaultMonitorQueueSize(c *C) {
 	c.Assert(getDefaultMonitorQueueSize(4), Equals, 4*defaults.MonitorQueueSizePerCPU)
 	c.Assert(getDefaultMonitorQueueSize(1000), Equals, defaults.MonitorQueueSizePerCPUMaximum)
 }
+
+func (s *OptionSuite) TestEndpointStatusValues(c *C) {
+	c.Assert(len(EndpointStatusValues()), Not(Equals), 0)
+	c.Assert(len(EndpointStatusValuesMap()), Not(Equals), 0)
+	for _, v := range EndpointStatusValues() {
+		_, ok := EndpointStatusValuesMap()[v]
+		c.Assert(ok, Equals, true)
+	}
+}
diff --git a/pkg/policy/distillery_test.go b/pkg/policy/distillery_test.go
index d388a6dcc9..a9b8888b52 100644
--- a/pkg/policy/distillery_test.go
+++ b/pkg/policy/distillery_test.go
@@ -195,45 +195,64 @@ var (
 		L7Proto: ParserTypeHTTP.String(),
 	}
 	// API rule definitions for default-deny, L3, L3L4, L3L4L7, L4, L4L7
+	lbls____NoAllow = labels.ParseLabelArray("no-allow")
 	rule____NoAllow = api.NewRule().
+			WithLabels(lbls____NoAllow).
 			WithIngressRules([]api.IngressRule{{}})
+	lblsL3____Allow = labels.ParseLabelArray("l3-allow")
 	ruleL3____Allow = api.NewRule().
+			WithLabels(lblsL3____Allow).
 			WithIngressRules([]api.IngressRule{{
 			FromEndpoints: []api.EndpointSelector{allowFooL3_},
 			ToPorts:       allowAllL4_,
 		}})
+	lblsL3L4__Allow = labels.ParseLabelArray("l3l4-allow")
 	ruleL3L4__Allow = api.NewRule().
+			WithLabels(lblsL3L4__Allow).
 			WithIngressRules([]api.IngressRule{{
 			FromEndpoints: []api.EndpointSelector{allowFooL3_},
 			ToPorts:       allowPort80,
 		}})
+	lblsL3L4L7Allow = labels.ParseLabelArray("l3l4l7-allow")
 	ruleL3L4L7Allow = api.NewRule().
+			WithLabels(lblsL3L4L7Allow).
 			WithIngressRules([]api.IngressRule{{
 			FromEndpoints: []api.EndpointSelector{allowFooL3_},
 			ToPorts:       combineL4L7(allowPort80, allowHTTPRoot),
 		}})
+	lbls__L4__Allow = labels.ParseLabelArray("l4-allow")
 	rule__L4__Allow = api.NewRule().
+			WithLabels(lbls__L4__Allow).
 			WithIngressRules([]api.IngressRule{{
 			ToPorts: allowPort80,
 		}})
+	lbls__L4L7Allow = labels.ParseLabelArray("l4l7-allow")
 	rule__L4L7Allow = api.NewRule().
+			WithLabels(lbls__L4L7Allow).
 			WithIngressRules([]api.IngressRule{{
 			ToPorts: combineL4L7(allowPort80, allowHTTPRoot),
 		}})
-
+	lbls__L3AllowFoo = labels.ParseLabelArray("l3-allow-foo")
 	rule__L3AllowFoo = api.NewRule().
+				WithLabels(lbls__L3AllowFoo).
 				WithIngressRules([]api.IngressRule{{
 			FromEndpoints: []api.EndpointSelector{allowFooL3_},
 		}})
-
+	lbls__L3AllowBar = labels.ParseLabelArray("l3-allow-bar")
 	rule__L3AllowBar = api.NewRule().
+				WithLabels(lbls__L3AllowBar).
 				WithIngressRules([]api.IngressRule{{
 			FromEndpoints: []api.EndpointSelector{allowBarL3_},
 		}})
+	lbls____AllowAll = labels.ParseLabelArray("allow-all")
 	rule____AllowAll = api.NewRule().
+				WithLabels(lbls____AllowAll).
 				WithIngressRules([]api.IngressRule{{
 			FromEndpoints: []api.EndpointSelector{api.WildcardEndpointSelector},
 		}})
+	lblsAllowLocalHostIngress = labels.LabelArray{
+		labels.NewLabel(LabelKeyPolicyDerivedFrom, LabelAllowLocalHostIngress, labels.LabelSourceReserved),
+	}
 
 	// Misc other bpf key fields for convenience / readability.
 	l7RedirectNone_ = uint16(0)
@@ -246,8 +265,12 @@ var (
 	mapKeyAllow___L4 = Key{0, 80, 6, dirIngress}
 	mapKeyAllowAll__ = Key{0, 0, 0, dirIngress}
 	// Desired map entries for no L7 redirect / redirect to Proxy
-	mapEntryL7None_ = NoRedirectEntry
-	mapEntryL7Proxy = redirectEntry
+	mapEntryL7None_ = func(lbls ...labels.LabelArray) MapStateEntry {
+		return NewMapStateEntry(labels.LabelArrayList(lbls).Sort(), false)
+	}
+	mapEntryL7Proxy = func(lbls ...labels.LabelArray) MapStateEntry {
+		return NewMapStateEntry(labels.LabelArrayList(lbls).Sort(), true)
+	}
 )
 
 // combineL4L7 returns a new PortRule that refers to the specified l4 ports and
@@ -316,7 +339,7 @@ func (d *policyDistillery) distillPolicy(epLabels labels.LabelArray) (MapState,
 	for _, l4 := range l4IngressPolicy {
 		io.WriteString(d.log, fmt.Sprintf("[distill] Processing L4Filter (l4: %d/%s), (l3/7: %+v)\n", l4.Port, l4.Protocol, l4.L7RulesPerSelector))
 		for key, entry := range l4.ToMapState(0) {
-			io.WriteString(d.log, fmt.Sprintf("[distill] L4 ingress allow %+v (parser=%s, redirect=%t)\n", key, l4.L7Parser, entry != NoRedirectEntry))
+			io.WriteString(d.log, fmt.Sprintf("[distill] L4 ingress allow %+v (parser=%s, redirect=%t)\n", key, l4.L7Parser, entry.IsRedirectEntry()))
 			result[key] = entry
 		}
 	}
@@ -336,8 +359,8 @@ func Test_MergeL3(t *testing.T) {
 		rules  api.Rules
 		result MapState
 	}{
-		{0, api.Rules{rule__L3AllowFoo, rule__L3AllowBar}, MapState{mapKeyAllowFoo__: mapEntryL7None_, mapKeyAllowBar__: mapEntryL7None_}},
-		{1, api.Rules{rule__L3AllowFoo, ruleL3L4__Allow}, MapState{mapKeyAllowFoo__: mapEntryL7None_, mapKeyAllowFooL4: mapEntryL7None_}},
+		{0, api.Rules{rule__L3AllowFoo, rule__L3AllowBar}, MapState{mapKeyAllowFoo__: mapEntryL7None_(lbls__L3AllowFoo, lbls__L3AllowBar), mapKeyAllowBar__: mapEntryL7None_(lbls__L3AllowFoo, lbls__L3AllowBar)}},
+		{1, api.Rules{rule__L3AllowFoo, ruleL3L4__Allow}, MapState{mapKeyAllowFoo__: mapEntryL7None_(lbls__L3AllowFoo), mapKeyAllowFooL4: mapEntryL7None_(lblsL3L4__Allow)}},
 	}
 
 	for _, tt := range tests {
@@ -380,37 +403,37 @@ func Test_MergeRules(t *testing.T) {
 		//
 		//  Rule 0                   | Rule 1         | Rule 2         | Rule 3         | Rule 4         | Desired BPF map state
 		{0, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{}},
-		{1, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFoo__: mapEntryL7None_}},
-		{2, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7None_}},
-		{3, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}},
-		{4, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7None_}},
-		{5, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}},
-		{6, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7None_}},                                    // identical L3L4 entry suppressed
-		{7, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}}, // identical L3L4 entry suppressed
-		{8, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy}},
-		{9, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}},
-		{10, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy}},
-		{11, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}},
-		{12, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy}},                                    // L3L4 entry suppressed to allow L4-only entry to redirect
-		{13, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // L3L4 entry suppressed to allow L4-only entry to redirect
-		{14, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy}},                                    // L3L4 entry suppressed to allow L4-only entry to redirect
-		{15, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // L3L4 entry suppressed to allow L4-only entry to redirect
-		{16, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy}},
-		{17, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}},
-		{18, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7None_}},
-		{19, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}},
-		{20, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy}},
-		{21, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}},
-		{22, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7None_}},
-		{23, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}},
-		{24, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy}},                                    // identical L3L4 entry suppressed
-		{25, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // identical L3L4 entry suppressed
-		{26, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy}},                                    // identical L3L4 entry suppressed
-		{27, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // identical L3L4 entry suppressed
-		{28, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy}},                                    // identical L3L4 entry suppressed
-		{29, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // identical L3L4 entry suppressed
-		{30, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy}},                                    // identical L3L4 entry suppressed
-		{31, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // identical L3L4 entry suppressed
+		{1, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}},
+		{2, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7None_(lbls__L4__Allow)}},
+		{3, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7None_(lbls__L4__Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}},
+		{4, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7None_(lblsL3L4__Allow)}},
+		{5, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7None_(lblsL3L4__Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}},
+		{6, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7None_(lblsL3L4__Allow, lbls__L4__Allow)}},                                                     // identical L3L4 entry suppressed
+		{7, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7None_(lblsL3L4__Allow, lbls__L4__Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}}, // identical L3L4 entry suppressed
+		{8, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lbls__L4L7Allow)}},
+		{9, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lbls__L4L7Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}},
+		{10, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lbls__L4L7Allow, lbls__L4__Allow)}},
+		{11, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lbls__L4L7Allow, lbls__L4__Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}},
+		{12, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lbls__L4L7Allow, lblsL3L4__Allow)}},                                                                      // L3L4 entry suppressed to allow L4-only entry to redirect
+		{13, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lbls__L4L7Allow, lblsL3L4__Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}},                  // L3L4 entry suppressed to allow L4-only entry to redirect
+		{14, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lbls__L4L7Allow, lblsL3L4__Allow, lbls__L4__Allow)}},                                                     // L3L4 entry suppressed to allow L4-only entry to redirect
+		{15, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lbls__L4L7Allow, lblsL3L4__Allow, lbls__L4__Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}}, // L3L4 entry suppressed to allow L4-only entry to redirect
+		{16, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy(lblsL3L4L7Allow)}},
+		{17, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy(lblsL3L4L7Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}},
+		{18, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy(lblsL3L4L7Allow, lbls__L4__Allow), mapKeyAllow___L4: mapEntryL7None_(lblsL3L4L7Allow, lbls__L4__Allow)}},
+		{19, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy(lblsL3L4L7Allow, lbls__L4__Allow), mapKeyAllow___L4: mapEntryL7None_(lblsL3L4L7Allow, lbls__L4__Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}},
+		{20, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy(lblsL3L4L7Allow, lblsL3L4__Allow)}},
+		{21, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy(lblsL3L4L7Allow, lblsL3L4__Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}},
+		{22, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy(lblsL3L4L7Allow, lblsL3L4__Allow, lbls__L4__Allow), mapKeyAllow___L4: mapEntryL7None_(lblsL3L4L7Allow, lblsL3L4__Allow, lbls__L4__Allow)}},
+		{23, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy(lblsL3L4L7Allow, lblsL3L4__Allow, lbls__L4__Allow), mapKeyAllow___L4: mapEntryL7None_(lblsL3L4L7Allow, lblsL3L4__Allow, lbls__L4__Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}},
+		{24, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lblsL3L4L7Allow, lbls__L4L7Allow)}},                                                                                       // identical L3L4 entry suppressed
+		{25, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lblsL3L4L7Allow, lbls__L4L7Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}},                                   // identical L3L4 entry suppressed
+		{26, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lblsL3L4L7Allow, lbls__L4L7Allow, lbls__L4__Allow)}},                                                                      // identical L3L4 entry suppressed
+		{27, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lblsL3L4L7Allow, lbls__L4L7Allow, lbls__L4__Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}},                  // identical L3L4 entry suppressed
+		{28, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lblsL3L4L7Allow, lbls__L4L7Allow, lblsL3L4__Allow)}},                                                                      // identical L3L4 entry suppressed
+		{29, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lblsL3L4L7Allow, lbls__L4L7Allow, lblsL3L4__Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}},                  // identical L3L4 entry suppressed
+		{30, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lblsL3L4L7Allow, lbls__L4L7Allow, lblsL3L4__Allow, lbls__L4__Allow)}},                                                     // identical L3L4 entry suppressed
+		{31, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy(lblsL3L4L7Allow, lbls__L4L7Allow, lblsL3L4__Allow, lbls__L4__Allow), mapKeyAllowFoo__: mapEntryL7None_(lblsL3____Allow)}}, // identical L3L4 entry suppressed
 	}
 	for _, tt := range tests {
 		repo := newPolicyDistillery(selectorCache)
@@ -449,8 +472,8 @@ func Test_AllowAll(t *testing.T) {
 		rules    api.Rules
 		result   MapState
 	}{
-		{0, api.EndpointSelectorNone, api.Rules{rule____AllowAll}, MapState{mapKeyAllowAll__: mapEntryL7None_}},
-		{1, api.WildcardEndpointSelector, api.Rules{rule____AllowAll}, MapState{mapKeyAllowAll__: mapEntryL7None_}},
+		{0, api.EndpointSelectorNone, api.Rules{rule____AllowAll}, MapState{mapKeyAllowAll__: mapEntryL7None_(lblsAllowLocalHostIngress)}},
+		{1, api.WildcardEndpointSelector, api.Rules{rule____AllowAll}, MapState{mapKeyAllowAll__: mapEntryL7None_(lbls____AllowAll)}},
 	}
 
 	for _, tt := range tests {
diff --git a/pkg/policy/resolve_test.go b/pkg/policy/resolve_test.go
index ad6a4cf95b..c0327f681a 100644
--- a/pkg/policy/resolve_test.go
+++ b/pkg/policy/resolve_test.go
@@ -415,6 +415,11 @@ func (ds *PolicyTestSuite) TestL7WithLocalHostWildcardd(c *C) {
 func (ds *PolicyTestSuite) TestMapStateWithIngressWildcard(c *C) {
 	repo := bootstrapRepo(GenerateL3IngressRules, 1000, c)
 
+	ruleLabel := labels.ParseLabelArray("rule-foo-allow-port-80")
+	ruleLabelAllowAnyEgress := labels.LabelArray{
+		labels.NewLabel(LabelKeyPolicyDerivedFrom, LabelAllowAnyEgress, labels.LabelSourceReserved),
+	}
+
 	idFooSelectLabelArray := labels.ParseSelectLabelArray("id=foo")
 	idFooSelectLabels := labels.Labels{}
 	for _, lbl := range idFooSelectLabelArray {
@@ -425,6 +430,7 @@ func (ds *PolicyTestSuite) TestMapStateWithIngressWildcard(c *C) {
 	selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo"))
 	rule1 := api.Rule{
 		EndpointSelector: selFoo,
+		Labels:           ruleLabel,
 		Ingress: []api.IngressRule{
 			{
 				ToPorts: []api.PortRule{{
@@ -447,6 +453,9 @@ func (ds *PolicyTestSuite) TestMapStateWithIngressWildcard(c *C) {
 	c.Assert(err, IsNil)
 	policy := selPolicy.DistillPolicy(DummyOwner{})
 
+	rule1MapStateEntry := NewMapStateEntry(labels.LabelArrayList{ruleLabel}, false)
+	allowEgressMapStateEntry := NewMapStateEntry(labels.LabelArrayList{ruleLabelAllowAnyEgress}, false)
+
 	expectedEndpointPolicy := EndpointPolicy{
 		selectorPolicy: &selectorPolicy{
 			Revision:      repo.GetRevision(),
@@ -464,7 +473,7 @@ func (ds *PolicyTestSuite) TestMapStateWithIngressWildcard(c *C) {
 						L7RulesPerSelector: L7DataMap{
 							wildcardCachedSelector: nil,
 						},
-						DerivedFromRules: labels.LabelArrayList{nil},
+						DerivedFromRules: labels.LabelArrayList{ruleLabel},
 					},
 				},
 				Egress: L4PolicyMap{},
@@ -475,8 +484,8 @@ func (ds *PolicyTestSuite) TestMapStateWithIngressWildcard(c *C) {
 		},
 		PolicyOwner: DummyOwner{},
 		PolicyMapState: MapState{
-			{TrafficDirection: trafficdirection.Egress.Uint8()}: {},
-			{DestPort: 80, Nexthdr: 6}:                          {},
+			{TrafficDirection: trafficdirection.Egress.Uint8()}: allowEgressMapStateEntry,
+			{DestPort: 80, Nexthdr: 6}:                          rule1MapStateEntry,
 		},
 	}
 
@@ -497,6 +506,11 @@ func (ds *PolicyTestSuite) TestMapStateWithIngressWildcard(c *C) {
 func (ds *PolicyTestSuite) TestMapStateWithIngress(c *C) {
 	repo := bootstrapRepo(GenerateL3IngressRules, 1000, c)
 
+	ruleLabel := labels.ParseLabelArray("rule-world-allow-port-80")
+	ruleLabelAllowAnyEgress := labels.LabelArray{
+		labels.NewLabel(LabelKeyPolicyDerivedFrom, LabelAllowAnyEgress, labels.LabelSourceReserved),
+	}
+
 	idFooSelectLabelArray := labels.ParseSelectLabelArray("id=foo")
 	idFooSelectLabels := labels.Labels{}
 	for _, lbl := range idFooSelectLabelArray {
@@ -509,6 +523,7 @@ func (ds *PolicyTestSuite) TestMapStateWithIngress(c *C) {
 	selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo"))
 	rule1 := api.Rule{
 		EndpointSelector: selFoo,
+		Labels:           ruleLabel,
 		Ingress: []api.IngressRule{
 			{
 				FromEntities: []api.Entity{api.EntityWorld},
@@ -566,6 +581,9 @@ func (ds *PolicyTestSuite) TestMapStateWithIngress(c *C) {
 	cachedSelectorTest := testSelectorCache.FindCachedIdentitySelector(api.NewESFromLabels(lblTest))
 	c.Assert(cachedSelectorTest, Not(IsNil))
 
+	rule1MapStateEntry := NewMapStateEntry(labels.LabelArrayList{ruleLabel, ruleLabel}, false)
+	allowEgressMapStateEntry := NewMapStateEntry(labels.LabelArrayList{ruleLabelAllowAnyEgress}, false)
+
 	expectedEndpointPolicy := EndpointPolicy{
 		selectorPolicy: &selectorPolicy{
 			Revision:      repo.GetRevision(),
@@ -583,7 +601,7 @@ func (ds *PolicyTestSuite) TestMapStateWithIngress(c *C) {
 							cachedSelectorWorld: nil,
 							cachedSelectorTest:  nil,
 						},
-						DerivedFromRules: labels.LabelArrayList{nil, nil},
+						DerivedFromRules: labels.LabelArrayList{ruleLabel, ruleLabel},
 					},
 				},
 				Egress: L4PolicyMap{},
@@ -594,16 +612,16 @@ func (ds *PolicyTestSuite) TestMapStateWithIngress(c *C) {
 		},
 		PolicyOwner: DummyOwner{},
 		PolicyMapState: MapState{
-			{TrafficDirection: trafficdirection.Egress.Uint8()}:                          {},
-			{Identity: uint32(identity.ReservedIdentityWorld), DestPort: 80, Nexthdr: 6}: {},
+			{TrafficDirection: trafficdirection.Egress.Uint8()}:                          allowEgressMapStateEntry,
+			{Identity: uint32(identity.ReservedIdentityWorld), DestPort: 80, Nexthdr: 6}: rule1MapStateEntry,
 		},
 		policyMapChanges: MapChanges{
 			adds: MapState{
-				{Identity: 192, DestPort: 80, Nexthdr: 6}: {},
-				{Identity: 194, DestPort: 80, Nexthdr: 6}: {},
+				{Identity: 192, DestPort: 80, Nexthdr: 6}: rule1MapStateEntry,
+				{Identity: 194, DestPort: 80, Nexthdr: 6}: rule1MapStateEntry,
 			},
 			deletes: MapState{
-				{Identity: 193, DestPort: 80, Nexthdr: 6}: {},
+				{Identity: 193, DestPort: 80, Nexthdr: 6}: rule1MapStateEntry,
 			},
 		},
 	}
@@ -623,10 +641,10 @@ func (ds *PolicyTestSuite) TestMapStateWithIngress(c *C) {
 	c.Assert(policy.policyMapChanges.deletes, IsNil)
 
 	c.Assert(adds, checker.Equals, MapState{
-		{Identity: 192, DestPort: 80, Nexthdr: 6}: {},
-		{Identity: 194, DestPort: 80, Nexthdr: 6}: {},
+		{Identity: 192, DestPort: 80, Nexthdr: 6}: rule1MapStateEntry,
+		{Identity: 194, DestPort: 80, Nexthdr: 6}: rule1MapStateEntry,
 	})
 	c.Assert(deletes, checker.Equals, MapState{
-		{Identity: 193, DestPort: 80, Nexthdr: 6}: {},
+		{Identity: 193, DestPort: 80, Nexthdr: 6}: rule1MapStateEntry,
 	})
 }
diff --git a/pkg/proxy/kafka_test.go b/pkg/proxy/kafka_test.go
index 0056052b9d..f7d7eaeeea 100644
--- a/pkg/proxy/kafka_test.go
+++ b/pkg/proxy/kafka_test.go
@@ -234,7 +234,8 @@ func (m *metadataTester) Handler() RequestHandler {
 
 type dummyEpSyncher struct{}
 
-func (epSync *dummyEpSyncher) RunK8sCiliumEndpointSync(e *endpoint.Endpoint) {}
+func (epSync *dummyEpSyncher) RunK8sCiliumEndpointSync(e *endpoint.Endpoint, conf endpoint.EndpointStatusConfiguration) {
+}
 
 func (s *proxyTestSuite) TestKafkaRedirect(c *C) {
 	server := NewServer()
diff --git a/test/helpers/cons.go b/test/helpers/cons.go
index 7b5bd2f046..3ddba1dff3 100644
--- a/test/helpers/cons.go
+++ b/test/helpers/cons.go
@@ -68,6 +68,12 @@ const (
 	Private  = "private"
 	Name     = "Name"
 
+	// CiliumAgentLabel is the label used for Cilium
+	CiliumAgentLabel = "k8s-app=cilium"
+
+	// CiliumOperatorLabel is the label used in the Cilium Operator deployment
+	CiliumOperatorLabel = "io.cilium/app=operator"
+
 	// PolicyEnforcement represents the PolicyEnforcement configuration option
 	// for the Cilium agent.
 	PolicyEnforcement = "PolicyEnforcement"
diff --git a/test/helpers/kubectl.go b/test/helpers/kubectl.go
index df2184ba81..43baa40198 100644
--- a/test/helpers/kubectl.go
+++ b/test/helpers/kubectl.go
@@ -124,12 +124,10 @@ var (
 	}
 
 	microk8sHelmOverrides = map[string]string{
-		"global.cni.confPath":                 "/var/snap/microk8s/current/args/cni-network",
-		"global.cni.binPath":                  "/var/snap/microk8s/current/opt/cni/bin",
-		"global.cni.customConf":               "true",
-		"global.containerRuntime.integration": "containerd",
-		"global.containerRuntime.socketPath":  "/var/snap/microk8s/common/run/containerd.sock",
-		"global.daemon.runPath":               "/var/snap/microk8s/current/var/run/cilium",
+		"global.cni.confPath":   "/var/snap/microk8s/current/args/cni-network",
+		"global.cni.binPath":    "/var/snap/microk8s/current/opt/cni/bin",
+		"global.cni.customConf": "true",
+		"global.daemon.runPath": "/var/snap/microk8s/current/var/run/cilium",
 	}
 	minikubeHelmOverrides = map[string]string{
 		"global.ipv6.enabled":           "false",
@@ -1498,10 +1496,10 @@ func (kub *Kubectl) DeleteCiliumOperator() error {
 	// incomplete teardown.
 
 	_ = kub.DeleteResource("deployment", fmt.Sprintf("-n %s cilium-operator", GetCiliumNamespace(GetCurrentIntegration())))
-	return kub.waitToDeleteCiliumOperator()
+	return kub.waitToDelete("Cilium Operator", CiliumOperatorLabel)
 }
 
-func (kub *Kubectl) waitToDeleteCiliumOperator() error {
+func (kub *Kubectl) waitToDelete(name, label string) error {
 	var (
 		pods []string
 		err  error
@@ -1515,13 +1513,16 @@ func (kub *Kubectl) waitToDeleteCiliumOperator() error {
 
 		select {
 		case <-ctx.Done():
-			return fmt.Errorf("timed out waiting to delete Cilium Operator: pods still remaining: %s", pods)
+			return fmt.Errorf("timed out waiting to delete %s: pods still remaining: %s", name, pods)
 		default:
 		}
 
-		pods, err = kub.GetPodNamesContext(ctx, GetCiliumNamespace(GetCurrentIntegration()), "io.cilium/app=cilium")
+		pods, err = kub.GetPodNamesContext(ctx, GetCiliumNamespace(GetCurrentIntegration()), label)
+		if err != nil {
+			return err
+		}
 		status = len(pods)
-		kub.Logger().Infof("Cilium Operator pods terminating '%d' err='%v' pods='%v'", status, err, pods)
+		kub.Logger().Infof("%s pods terminating '%d' err='%v' pods='%v'", name, status, err, pods)
 		if status == 0 {
 			return nil
 		}
@@ -1535,36 +1536,7 @@ func (kub *Kubectl) DeleteCiliumDS() error {
 	// incomplete teardown.
 
 	_ = kub.DeleteResource("ds", fmt.Sprintf("-n %s cilium", GetCiliumNamespace(GetCurrentIntegration())))
-	return kub.waitToDeleteCilium()
-}
-
-func (kub *Kubectl) waitToDeleteCilium() error {
-	var (
-		pods []string
-		err  error
-	)
-
-	ctx, cancel := context.WithTimeout(context.Background(), HelperTimeout)
-	defer cancel()
-
-	status := 1
-	for status > 0 {
-
-		select {
-		case <-ctx.Done():
-			return fmt.Errorf("timed out waiting to delete Cilium: pods still remaining: %s", pods)
-		default:
-		}
-
-		pods, err = kub.GetCiliumPodsContext(ctx, GetCiliumNamespace(GetCurrentIntegration()))
-		status := len(pods)
-		kub.Logger().Infof("Cilium pods terminating '%d' err='%v' pods='%v'", status, err, pods)
-		if status == 0 {
-			return nil
-		}
-		time.Sleep(1 * time.Second)
-	}
-	return nil
+	return kub.waitToDelete("Cilium", CiliumAgentLabel)
 }
 
 // ciliumUninstallHelm uninstalls Cilium with the Helm options provided.
@@ -1963,6 +1935,25 @@ func (kub *Kubectl) CiliumNodesWait() (bool, error) {
 	return true, nil
 }
 
+// LoadedPolicyInFirstAgent returns the policy as loaded in the first cilium
+// agent that is found in the cluster
+func (kub *Kubectl) LoadedPolicyInFirstAgent() (string, error) {
+	pods, err := kub.GetCiliumPods(GetCiliumNamespace(GetCurrentIntegration()))
+	if err != nil {
+		return "", fmt.Errorf("cannot retrieve cilium pods: %s", err)
+	}
+	for _, pod := range pods {
+		ctx, cancel := context.WithTimeout(context.Background(), ShortCommandTimeout)
+		defer cancel()
+		res := kub.CiliumExecContext(ctx, pod, "cilium policy get")
+		if !res.WasSuccessful() {
+			return "", fmt.Errorf("cannot execute cilium policy get: %s", res.Output())
+		}
+		return res.CombineOutput().String(), nil
+	}
+	return "", fmt.Errorf("no running cilium pods")
+}
+
 // WaitPolicyDeleted waits for policy policyName to be deleted from the
 // cilium-agent running in pod. Returns an error if policyName was unable to
 // be deleted after some amount of time.
diff --git a/test/k8sT/KafkaPolicies.go b/test/k8sT/KafkaPolicies.go
index b1a3bbb22c..e6c89f5592 100644
--- a/test/k8sT/KafkaPolicies.go
+++ b/test/k8sT/KafkaPolicies.go
@@ -19,7 +19,6 @@ import (
 	"fmt"
 	"time"
 
-	"github.com/cilium/cilium/api/v1/models"
 	. "github.com/cilium/cilium/test/ginkgo-ext"
 	"github.com/cilium/cilium/test/helpers"
 
@@ -201,40 +200,6 @@ var _ = Describe("K8sKafkaPolicyTest", func() {
 				helpers.KubectlApply, helpers.HelperTimeout)
 			Expect(err).To(BeNil(), "L7 policy cannot be imported correctly")
 
-			By("validate that CEP is updated with correct enforcement mode")
-
-			desiredPolicyStatus := map[string]models.EndpointPolicyEnabled{
-				backupApp:   models.EndpointPolicyEnabledNone,
-				empireHqApp: models.EndpointPolicyEnabledNone,
-				kafkaApp:    models.EndpointPolicyEnabledBoth,
-				outpostApp:  models.EndpointPolicyEnabledNone,
-			}
-
-			body := func() bool {
-				for app, policy := range desiredPolicyStatus {
-					cep := kubectl.CepGet(helpers.DefaultNamespace, appPods[app])
-					if cep == nil {
-						return false
-					}
-					state := models.EndpointPolicyEnabledNone
-					switch {
-					case cep.Policy.Egress.Enforcing && cep.Policy.Ingress.Enforcing:
-						state = models.EndpointPolicyEnabledBoth
-					case cep.Policy.Egress.Enforcing:
-						state = models.EndpointPolicyEnabledIngress
-					case cep.Policy.Ingress.Enforcing:
-						state = models.EndpointPolicyEnabledEgress
-					}
-
-					if state != policy {
-						return false
-					}
-				}
-				return true
-			}
-			err = helpers.WithTimeout(body, "CEP policy enforcement", &helpers.TimeoutConfig{Timeout: helpers.HelperTimeout})
-			Expect(err).To(BeNil(), "CEP not updated with correct policy enforcement")
-
 			By("Testing Kafka L7 policy enforcement status")
 			err = kubectl.ExecKafkaPodCmd(
 				helpers.DefaultNamespace, appPods[empireHqApp], fmt.Sprintf(prodHqAnnounce))
diff --git a/test/k8sT/Services.go b/test/k8sT/Services.go
index dfdbd75e5d..35b99587da 100644
--- a/test/k8sT/Services.go
+++ b/test/k8sT/Services.go
@@ -797,7 +797,6 @@ var _ = Describe("K8sServicesTest", func() {
 
 		var (
 			expectedCIDR = "198.49.23.144/32"
-			podName      = "toservices"
 
 			endpointPath      string
 			podPath           string
@@ -837,32 +836,20 @@ var _ = Describe("K8sServicesTest", func() {
 		})
 
 		validateEgress := func() {
-			By("Checking that toServices CIDR is plumbed into CEP")
+			By("Checking that toServices CIDR is plumbed into the policy")
 			Eventually(func() string {
-				res := kubectl.Exec(fmt.Sprintf(
-					"%s -n %s get cep %s -o json",
-					helpers.KubectlCmd,
-					helpers.DefaultNamespace,
-					podName))
-				ExpectWithOffset(1, res).Should(helpers.CMDSuccess(), "cannot get Cilium endpoint")
-				data, err := res.Filter(`{.status.policy.egress}`)
-				ExpectWithOffset(1, err).To(BeNil(), "unable to get endpoint %s metadata", podName)
-				return data.String()
+				output, err := kubectl.LoadedPolicyInFirstAgent()
+				ExpectWithOffset(1, err).To(BeNil(), "unable to retrieve policy")
+				return output
 			}, 2*time.Minute, 2*time.Second).Should(ContainSubstring(expectedCIDR))
 		}
 
 		validateEgressAfterDeletion := func() {
-			By("Checking that toServices CIDR is no longer plumbed into CEP")
+			By("Checking that toServices CIDR is no longer plumbed into the policy")
 			Eventually(func() string {
-				res := kubectl.Exec(fmt.Sprintf(
-					"%s -n %s get cep %s -o json",
-					helpers.KubectlCmd,
-					helpers.DefaultNamespace,
-					podName))
-				ExpectWithOffset(1, res).Should(helpers.CMDSuccess(), "cannot get Cilium endpoint")
-				data, err := res.Filter(`{.status.policy.egress}`)
-				ExpectWithOffset(1, err).To(BeNil(), "unable to get endpoint %s metadata", podName)
-				return data.String()
+				output, err := kubectl.LoadedPolicyInFirstAgent()
+				ExpectWithOffset(1, err).To(BeNil(), "unable to retrieve policy")
+				return output
 			}, 2*time.Minute, 2*time.Second).ShouldNot(ContainSubstring(expectedCIDR))
 		}
 
diff --git a/test/k8sT/assertionHelpers.go b/test/k8sT/assertionHelpers.go
index cbd74433e2..317bbd0b89 100644
--- a/test/k8sT/assertionHelpers.go
+++ b/test/k8sT/assertionHelpers.go
@@ -101,7 +101,14 @@ func DeployCiliumOptionsAndDNS(vm *helpers.Kubectl, ciliumFilename string, optio
 	ExpectCiliumRunning(vm)
 
 	By("Installing DNS Deployment")
-	_ = vm.ApplyDefault(helpers.DNSDeployment(vm.BasePath()))
+	switch helpers.GetCurrentIntegration() {
+	case helpers.CIIntegrationMicrok8s:
+		By(fmt.Sprintf("%s (hint: %s)",
+			"Assuming that microk8s already has DNS deployed...",
+			"Use 'microk8s.enable dns' to create deployment"))
+	default:
+		_ = vm.ApplyDefault(helpers.DNSDeployment(vm.BasePath()))
+	}
 
 	switch helpers.GetCurrentIntegration() {
 	case helpers.CIIntegrationFlannel: