Repository: cilium/cilium
Pull request #12567 (2020-07-17 06:50:05)

Ignore collisions for named ports that are not actually used in an egress policy
Do not warn of port name conflicts when ports are collected, but keep
the conflicting ports. This allows (any) warnings to be postponed until
the named ports are used in a policy.

Behavior in presence of conflicting port name/number mapping, when
the port name is used in a policy is slightly changed:

 - Now these collisions will not affect ingress policies at all
 - For egress the policy rule using the conflicting port is now ignored.
   Cilium needs to be running with debug on to see this happening.
   Previously rule was instantiated using one of the many port numbers,
   essentially selected randomly.
 - Missing port name mappings, when used in a policy applicable to
   the local node, are logged as warnings, but rate limited.

Pass Endpoint (or a subset interface) to potential users of Endpoint's
NamedPortsMap so that instead of preparing the map in advance it can
be created of demand in case it is actually needed.

NamedPortsMap is an interface that is implemented by both NamedPortMap
or NamedPortMultiMap, first of which is built from the POD spec, and
the latter is a collection of named ports across all the PODs in the
cluster. When a named port is used in an ingress policy, it can be
translated by using the PODs own NamedPortMap. When a named port is
used in an egress policy the named port can be one of any remote PODs,
so the collection of all the named ports in the cluster is used as the
individual destination POD is not known at policy computation
time. This could be optimized to the scope of PODs sharing the same
set of labels as the destination security IDs are known when policy is
configured.

Currently same port name resolving to different port numbers is
flagged as a runtime error when that specific port name is used in a
policy. Traffic to this/these ports is not allowed. To get around this the
port names should be made unique so that each name maps to the
same port number and protocol, or preferably the port names in the
egress policies should be replaced with the desired port numbers.

The maintenance of the collection of named ports across the cluster
described above is started only after first needed for computing the
bpf map entries from an egress policy that uses a port name instead
the number. This way the related overhead is avoided if named ports
are never used in egress policies.

Fixes: #12514


jrajahalme: test-me-please
jrajahalme: test-focus K8sConformance
jrajahalme: test-upstream-k8s
jrajahalme: test-me-please
coveralls: 
[![Coverage Status](https://coveralls.io/builds/32137125/badge)](https://coveralls.io/builds/32137125)

Coverage decreased (-0.03%) to 37.004% when pulling **e748fce73010beef197d5b8223a4a86e56e1b211 on pr/jrajahalme/limit-named-port-warnings** into **f55ec9066e3a23461a0d14e20b3be8ab49a04c97 on master**.

jrajahalme: Unrelated test flake in Cilium-PR-Ginkgo-Tests-K8s
jrajahalme: @joestringer Currently we log on debug level about failures in name->port mappings.
joestringer: @jrajahalme SGTM, I presume these are different warnings so we can set the levels separately to an appropriate level? Main thing is just to ensure that potentially recurring messages wouldn't end up spamming the logs.
jrajahalme: @aanm Added a comment that also covers the locking requirements:
```
	// needNamedPorts is initially 'false', but will be changd to 'true' when the
	// clusterwide named port mappings are needed for network policy computation
	// for the first time. This avoids the overhead of maintaining 'namedPorts' map
	// when it is known not to be needed.
	// Protected by 'mutex'.
	needNamedPorts bool

	// namedPorts is a collection of all named ports in the cluster. This is needed
	// only if an egress policy refers to a port by name.
	// This map is returned to users so all updates must be made into a fresh map that
	// is then swapped in place while 'mutex' is being held.
```
maintainer-s-little-helper[bot]: Commit 3b63e41cbd71d1b4ec6aecfeb961ffc84c4c63a2 does not contain "Signed-off-by".

Please follow instructions provided in https://docs.cilium.io/en/stable/contributing/development/contributing_guide/#developer-s-certificate-of-origin
jrajahalme: Noticed a locking problem, fixed in the third commit, plan to squash into earlier commits before merging.
Added two more commits to first refactor and then add warning log rate limiting (max 1 log every 10 seconds with a burst of 3). This rate limit should likely be tuned before merging.

maintainer-s-little-helper[bot]: Commit 3b63e41cbd71d1b4ec6aecfeb961ffc84c4c63a2 does not contain "Signed-off-by".

Please follow instructions provided in https://docs.cilium.io/en/stable/contributing/development/contributing_guide/#developer-s-certificate-of-origin
jrajahalme: test-upstream-k8s
jrajahalme: test-upstream-k8s
jrajahalme: test-me-please
jrajahalme: Known flake #11847 in netnext
jrajahalme: > Thanks for adding that. Sounds like an optimization. Just out of curiosity, did we measure the impact of this?

Did not measure the impact, but learned since release that (to me) surprisingly many POD specs are naming ports while the names are (likely) not used for anything. Maintaining the collection of all the named ports in the cluster was run every time a POD with a named port was added or removed (cluster-wide), so I felt it prudent to avoid that computation for the benefit or large clusters where port names are not used in egress policies.
jrajahalme: test-upstream-k8s
jrajahalme: test-me-please
jrajahalme: Addressed Chris's comments and fixed a unit test broken by last refactoring.
jrajahalme: test-upstream-k8s
jrajahalme: test-me-please
jrajahalme: test-GKE
jrajahalme: @joestringer Thanks for a thorough review. I'll have to leave some of the heavier lifting to follow-on PR, but I fixed the nits as indicated in the responses above.
jrajahalme: test-me-please
jrajahalme: test-me-please

Patch:
diff --git a/.github/cilium-actions.yml b/.github/cilium-actions.yml
index 626c5d4461..88b37ad354 100644
--- a/.github/cilium-actions.yml
+++ b/.github/cilium-actions.yml
@@ -13,23 +13,23 @@ move-to-projects-for-labels-xored:
       column: "Backport done to v1.8"
   v1.7:
     needs-backport/1.7:
-      project: "https://github.com/cilium/cilium/projects/120"
+      project: "https://github.com/cilium/cilium/projects/123"
       column: "Needs backport from master"
     backport-pending/1.7:
-      project: "https://github.com/cilium/cilium/projects/120"
+      project: "https://github.com/cilium/cilium/projects/123"
       column: "Backport pending to v1.7"
     backport-done/1.7:
-      project: "https://github.com/cilium/cilium/projects/120"
+      project: "https://github.com/cilium/cilium/projects/123"
       column: "Backport done to v1.7"
   v1.6:
     needs-backport/1.6:
-      project: "https://github.com/cilium/cilium/projects/118"
+      project: "https://github.com/cilium/cilium/projects/122"
       column: "Needs backport from master"
     backport-pending/1.6:
-      project: "https://github.com/cilium/cilium/projects/118"
+      project: "https://github.com/cilium/cilium/projects/122"
       column: "Backport pending to v1.6"
     backport-done/1.6:
-      project: "https://github.com/cilium/cilium/projects/118"
+      project: "https://github.com/cilium/cilium/projects/122"
       column: "Backport done to v1.6"
 require-msgs-in-commit:
   - msg: "Signed-off-by"
diff --git a/.github/workflows/go-check.yaml b/.github/workflows/go-check.yaml
index 1ed57ed4f1..b58c4c25a8 100644
--- a/.github/workflows/go-check.yaml
+++ b/.github/workflows/go-check.yaml
@@ -80,7 +80,7 @@ jobs:
           path: src/github.com/cilium/cilium
       - name: Install protobuf dependencies
         env:
-          PROTOBUF_VERSION: 3.11.4
+          PROTOBUF_VERSION: 3.12.3
         run: |
           curl -Lo protoc-$PROTOBUF_VERSION-linux-x86_64.zip https://github.com/protocolbuffers/protobuf/releases/download/v$PROTOBUF_VERSION/protoc-$PROTOBUF_VERSION-linux-x86_64.zip
           unzip protoc-$PROTOBUF_VERSION-linux-x86_64.zip
diff --git a/Dockerfile b/Dockerfile
index 590716fddd..2868d62456 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,7 +2,7 @@
 #
 # cilium-envoy from github.com/cilium/proxy
 #
-FROM quay.io/cilium/cilium-envoy:ccad480c59aa8b946d98aaf79f8e9e38d6731fdc@sha256:215442a52fcd1022a97d7eae658c503ec693194a6f09f3488b8a360e509d5945 as cilium-envoy
+FROM quay.io/cilium/cilium-envoy:595bb722a4ab35576e89952f11cea3fd5aad63bc@sha256:b6eead55288b9ee61f763a99a31e5ff81661ea4d847c693eb8b6891b9b5ca3c3 as cilium-envoy
 ARG CILIUM_SHA=""
 LABEL cilium-sha=${CILIUM_SHA}
 
diff --git a/Documentation/community.rst b/Documentation/community.rst
index a0204f01a2..1f2c267fc2 100644
--- a/Documentation/community.rst
+++ b/Documentation/community.rst
@@ -17,7 +17,7 @@ and meeting links.
 ====================== ===================================== ============= ================================================================================
 SIG                    Meeting                               Slack         Description
 ====================== ===================================== ============= ================================================================================
-Datapath               Wednesdays, 08:00 PT                  #sig-datapath Owner of all BPF and Linux kernel related datapath code.
+Datapath               Wednesdays, 08:00 PT                  #sig-datapath Owner of all eBPF- and Linux-kernel-related datapath code.
 Documentation          None                                  #sig-docs     All documentation related discussions
 Envoy                  Biweekly on Thursdays, 09:00 PT       #sig-envoy    Envoy, Istio and maintenance of all L7 protocol parsers.
 Policy                 None                                  #sig-policy   All topics related to policy. The SIG is responsible for all security relevant APIs and the enforcement logic.
@@ -47,8 +47,8 @@ Slack channels
 ==================== ============================================================
 Name                 Purpose
 ==================== ============================================================
-#bpf                 BPF specific questions
 #development         Development discussions
+#ebpf                eBPF-specific questions
 #general             General user discussions & questions
 #git                 GitHub notifications
 #kubernetes          Kubernetes specific questions
diff --git a/Makefile b/Makefile
index 6ffbb5edaf..c76c2338ff 100644
--- a/Makefile
+++ b/Makefile
@@ -55,6 +55,8 @@ JOB_BASE_NAME ?= cilium_test
 GO_VERSION := $(shell cat GO_VERSION)
 GOARCH := $(shell $(GO) env GOARCH)
 
+DOCKER_FLAGS ?=
+
 TEST_LDFLAGS=-ldflags "-X github.com/cilium/cilium/pkg/kvstore.consulDummyAddress=https://consul:8443 \
 	-X github.com/cilium/cilium/pkg/kvstore.etcdDummyAddress=http://etcd:4002 \
 	-X github.com/cilium/cilium/pkg/testutils.CiliumRootDir=$(ROOT_DIR) \
diff --git a/Makefile.docker b/Makefile.docker
index e3f8820f22..73295510fe 100644
--- a/Makefile.docker
+++ b/Makefile.docker
@@ -7,10 +7,11 @@ docker-cilium-image-for-developers:
 	# DOCKER_BUILDKIT allows for faster build as well as the ability to use
 	# a dedicated dockerignore file per Dockerfile.
 	$(QUIET)DOCKER_BUILDKIT=1 $(CONTAINER_ENGINE) build \
-	     --build-arg LOCKDEBUG=\
-	     --build-arg V=\
-	     --build-arg LIBNETWORK_PLUGIN=\
-	     -t $(DOCKER_DEV_ACCOUNT)/cilium-dev:latest . -f ./cilium-dev.Dockerfile
+		$(DOCKER_FLAGS) \
+		--build-arg LOCKDEBUG=\
+		--build-arg V=\
+		--build-arg LIBNETWORK_PLUGIN=\
+		-t $(DOCKER_DEV_ACCOUNT)/cilium-dev:latest . -f ./cilium-dev.Dockerfile
 
 docker-image: clean docker-image-no-clean docker-plugin-image docker-hubble-relay-image
 	$(MAKE) docker-operator-image
@@ -26,6 +27,7 @@ docker-image-unstripped: clean docker-image-no-clean-unstripped docker-plugin-im
 
 docker-image-no-clean: GIT_VERSION $(BUILD_DIR)/Dockerfile build-context-update
 	$(QUIET)$(CONTAINER_ENGINE) build -f $(BUILD_DIR)/Dockerfile \
+		$(DOCKER_FLAGS) \
 		--build-arg NOSTRIP=${NOSTRIP} \
 		--build-arg LOCKDEBUG=${LOCKDEBUG} \
 		--build-arg V=${V} \
@@ -46,6 +48,7 @@ docker-cilium-manifest:
 
 dev-docker-image: GIT_VERSION $(BUILD_DIR)/Dockerfile build-context-update
 	$(QUIET)$(CONTAINER_ENGINE) build -f $(BUILD_DIR)/Dockerfile \
+		$(DOCKER_FLAGS) \
 		--build-arg NOSTRIP=${NOSTRIP} \
 		--build-arg LOCKDEBUG=${LOCKDEBUG} \
 		--build-arg V=${V} \
@@ -69,6 +72,7 @@ docker-cilium-dev-manifest:
 # to build also 'docker-operator-image', where the stem would be empty otherwise
 docker-opera%-image: GIT_VERSION $(BUILD_DIR)/cilium-opera%.Dockerfile build-context-update
 	$(QUIET)$(CONTAINER_ENGINE) build \
+		$(DOCKER_FLAGS) \
 		--build-arg NOSTRIP=${NOSTRIP} \
 		--build-arg LOCKDEBUG=${LOCKDEBUG} \
 		--build-arg CILIUM_SHA=$(firstword $(GIT_VERSION)) \
@@ -87,6 +91,7 @@ docker-operator-manifest:
 
 docker-plugin-image: GIT_VERSION $(BUILD_DIR)/cilium-docker-plugin.Dockerfile build-context-update
 	$(QUIET)$(CONTAINER_ENGINE) build \
+		$(DOCKER_FLAGS) \
 		--build-arg NOSTRIP=${NOSTRIP} \
 		--build-arg LOCKDEBUG=${LOCKDEBUG} \
 		--build-arg CILIUM_SHA=$(firstword $(GIT_VERSION)) \
@@ -105,7 +110,7 @@ docker-plugin-manifest:
 	$(QUIET) contrib/scripts/push_manifest.sh docker-plugin $(DOCKER_IMAGE_TAG)
 
 docker-image-runtime:
-	cd contrib/packaging/docker && $(CONTAINER_ENGINE) build --build-arg ARCH=$(GOARCH) -t cilium/cilium-runtime:$(UTC_DATE) -f Dockerfile.runtime .
+	cd contrib/packaging/docker && $(CONTAINER_ENGINE) build $(DOCKER_FLAGS) --build-arg ARCH=$(GOARCH) -t cilium/cilium-runtime:$(UTC_DATE) -f Dockerfile.runtime .
 	$(QUIET)$(CONTAINER_ENGINE) tag cilium/cilium-runtime:$(UTC_DATE) cilium/cilium-runtime:$(UTC_DATE)-${GOARCH}
 
 docker-cilium-runtime-manifest:
@@ -113,7 +118,7 @@ docker-cilium-runtime-manifest:
 	$(QUIET) contrib/scripts/push_manifest.sh cilium-runtime $(UTC_DATE)
 
 docker-image-builder:
-	$(QUIET)$(CONTAINER_ENGINE) build --build-arg ARCH=$(GOARCH) -t cilium/cilium-builder:$(UTC_DATE) -f Dockerfile.builder .
+	$(QUIET)$(CONTAINER_ENGINE) build $(DOCKER_FLAGS) --build-arg ARCH=$(GOARCH) -t cilium/cilium-builder:$(UTC_DATE) -f Dockerfile.builder .
 	$(QUIET)$(CONTAINER_ENGINE) tag cilium/cilium-builder:$(UTC_DATE) cilium/cilium-builder:$(UTC_DATE)-${GOARCH}
 
 docker-cilium-builder-manifest:
@@ -122,6 +127,7 @@ docker-cilium-builder-manifest:
 
 docker-hubble-relay-image: $(BUILD_DIR)/hubble-relay.Dockerfile build-context-update
 	$(QUIET)$(CONTAINER_ENGINE) build \
+		$(DOCKER_FLAGS) \
 		--build-arg NOSTRIP=${NOSTRIP} \
 		--build-arg LOCKDEBUG=${LOCKDEBUG} \
 		--build-arg CILIUM_SHA=$(firstword $(GIT_VERSION)) \
diff --git a/README.rst b/README.rst
index 7627f1fc90..be86a02907 100644
--- a/README.rst
+++ b/README.rst
@@ -35,9 +35,9 @@ minor release, corresponding image pull tags and their release notes:
 +-------------------------------------------------------+------------+--------------------------------------+---------------------------------------------------------------------------+------------------------------------------------------------------------+
 | `v1.8 <https://github.com/cilium/cilium/tree/v1.8>`__ | 2020-07-23 | ``docker.io/cilium/cilium:v1.8.2``   | `Release Notes <https://github.com/cilium/cilium/releases/tag/v1.8.2>`__  | `General Announcement <https://cilium.io/blog/2020/06/22/cilium-18>`__ |
 +-------------------------------------------------------+------------+--------------------------------------+---------------------------------------------------------------------------+------------------------------------------------------------------------+
-| `v1.7 <https://github.com/cilium/cilium/tree/v1.7>`__ | 2020-07-02 | ``docker.io/cilium/cilium:v1.7.6``   | `Release Notes <https://github.com/cilium/cilium/releases/tag/v1.7.6>`__  | `General Announcement <https://cilium.io/blog/2020/02/18/cilium-17>`__ |
+| `v1.7 <https://github.com/cilium/cilium/tree/v1.7>`__ | 2020-08-04 | ``docker.io/cilium/cilium:v1.7.7``   | `Release Notes <https://github.com/cilium/cilium/releases/tag/v1.7.7>`__  | `General Announcement <https://cilium.io/blog/2020/02/18/cilium-17>`__ |
 +-------------------------------------------------------+------------+--------------------------------------+---------------------------------------------------------------------------+------------------------------------------------------------------------+
-| `v1.6 <https://github.com/cilium/cilium/tree/v1.6>`__ | 2020-07-02 | ``docker.io/cilium/cilium:v1.6.10``  | `Release Notes <https://github.com/cilium/cilium/releases/tag/v1.6.10>`__ | `General Announcement <https://cilium.io/blog/2019/08/20/cilium-16>`__ |
+| `v1.6 <https://github.com/cilium/cilium/tree/v1.6>`__ | 2020-08-03 | ``docker.io/cilium/cilium:v1.6.11``  | `Release Notes <https://github.com/cilium/cilium/releases/tag/v1.6.11>`__ | `General Announcement <https://cilium.io/blog/2019/08/20/cilium-16>`__ |
 +-------------------------------------------------------+------------+--------------------------------------+---------------------------------------------------------------------------+------------------------------------------------------------------------+
 
 Functionality Overview
diff --git a/contrib/vagrant/scripts/helpers.bash b/contrib/vagrant/scripts/helpers.bash
index c1299fb2e4..66b10f86db 100644
--- a/contrib/vagrant/scripts/helpers.bash
+++ b/contrib/vagrant/scripts/helpers.bash
@@ -76,7 +76,7 @@ cluster_api_server_ip=${K8S_CLUSTER_API_SERVER_IP:-"172.20.0.1"}
 #cluster_dns_ip=${K8S_CLUSTER_DNS_IP:-"FD03::A"}
 #cluster_api_server_ip=${K8S_CLUSTER_API_SERVER_IP:-"FD03::1"}
 
-k8s_version="v1.19.0-rc.3"
+k8s_version="v1.19.0-rc.4"
 etcd_version="v3.4.9"
 
 function restore_flag {
diff --git a/examples/kubernetes/connectivity-check/Makefile b/examples/kubernetes/connectivity-check/Makefile
index 7949234bb1..7c0a3a585a 100644
--- a/examples/kubernetes/connectivity-check/Makefile
+++ b/examples/kubernetes/connectivity-check/Makefile
@@ -9,12 +9,13 @@ HOSTPORT_OUT := connectivity-check-hostport.yaml
 SINGLE_OUT := connectivity-check-single-node.yaml
 PROXY_OUT := connectivity-check-proxy.yaml
 QUARANTINE_OUT := connectivity-check-quarantine.yaml
+TOOLS_OUT := connectivity-debug-tools.yaml
 
 SERVERS_NAME := echo-a echo-b echo-b-host echo-b-host-headless echo-c echo-c-host echo-c-host-headless
 SERVERS_OUT := servers.yaml
 
 SRC := $(wildcard *.cue)
-ALL_TARGETS := $(DEFAULT_OUT) $(HOSTPORT_OUT) $(SINGLE_OUT) $(PROXY_OUT) $(QUARANTINE_OUT)
+ALL_TARGETS := $(DEFAULT_OUT) $(HOSTPORT_OUT) $(SINGLE_OUT) $(PROXY_OUT) $(QUARANTINE_OUT) $(TOOLS_OUT)
 
 DOCKER_RUN := $(CONTAINER_ENGINE) container run --rm \
 	--workdir /src/examples/kubernetes/connectivity-check \
@@ -48,6 +49,10 @@ $(QUARANTINE_OUT): $(SRC) $(SERVERS_OUT)
 	$(QUIET)$(CUE) -t component=all -t quarantine=true dump > $@
 	@cat $(SERVERS_OUT) >> $@
 
+$(TOOLS_OUT): $(SRC) $(SERVERS_OUT)
+	@echo '# Automatically generated by Makefile. DO NOT EDIT' > $(TOOLS_OUT)
+	$(QUIET)$(CUE) -t component=all -t type=tool dump >> $@
+
 clean:
 	@rm -f $(ALL_TARGETS)
 	@rm -f *.new
diff --git a/examples/kubernetes/connectivity-check/connectivity-check-hostport.yaml b/examples/kubernetes/connectivity-check/connectivity-check-hostport.yaml
index e48983051b..5c7cd2d5e0 100644
--- a/examples/kubernetes/connectivity-check/connectivity-check-hostport.yaml
+++ b/examples/kubernetes/connectivity-check/connectivity-check-hostport.yaml
@@ -6,6 +6,7 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -55,6 +56,7 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -105,6 +107,7 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -166,6 +169,7 @@ metadata:
     topology: any
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -216,6 +220,7 @@ metadata:
     topology: any
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -277,6 +282,7 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -329,6 +335,7 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -381,6 +388,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -425,6 +433,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -477,6 +486,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -529,6 +539,7 @@ metadata:
     topology: intra-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -606,6 +617,7 @@ metadata:
     topology: multi-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -683,6 +695,7 @@ metadata:
     topology: intra-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -760,6 +773,7 @@ metadata:
     topology: multi-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -837,6 +851,7 @@ metadata:
     topology: intra-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -914,6 +929,7 @@ metadata:
     topology: multi-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -991,6 +1007,7 @@ metadata:
     topology: multi-node
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -1053,6 +1070,7 @@ metadata:
     topology: multi-node
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -1115,6 +1133,7 @@ metadata:
     topology: multi-node
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -1178,6 +1197,7 @@ metadata:
     topology: multi-node
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -1241,6 +1261,7 @@ metadata:
     topology: multi-node
     component: hostport-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -1303,6 +1324,7 @@ metadata:
     topology: intra-node
     component: hostport-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -1365,12 +1387,13 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
+  type: ClusterIP
   selector:
     name: echo-a
-  type: ClusterIP
 apiVersion: v1
 kind: Service
 ---
@@ -1381,13 +1404,14 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
     nodePort: 31313
+  type: NodePort
   selector:
     name: echo-b
-  type: NodePort
 apiVersion: v1
 kind: Service
 ---
@@ -1398,12 +1422,13 @@ metadata:
     topology: any
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
+  type: ClusterIP
   selector:
     name: echo-c
-  type: ClusterIP
 apiVersion: v1
 kind: Service
 ---
@@ -1414,12 +1439,13 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
+  type: ClusterIP
   selector:
     name: echo-b
-  type: ClusterIP
   clusterIP: None
 apiVersion: v1
 kind: Service
@@ -1431,11 +1457,12 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports: []
+  type: ClusterIP
   selector:
     name: echo-b-host
-  type: ClusterIP
   clusterIP: None
 apiVersion: v1
 kind: Service
@@ -1447,12 +1474,13 @@ metadata:
     topology: any
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
+  type: ClusterIP
   selector:
     name: echo-c
-  type: ClusterIP
   clusterIP: None
 apiVersion: v1
 kind: Service
@@ -1464,11 +1492,12 @@ metadata:
     topology: any
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports: []
+  type: ClusterIP
   selector:
     name: echo-c-host
-  type: ClusterIP
   clusterIP: None
 apiVersion: v1
 kind: Service
@@ -1480,6 +1509,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -1511,6 +1541,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -1549,6 +1580,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -1588,6 +1620,7 @@ metadata:
     topology: intra-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -1630,6 +1663,7 @@ metadata:
     topology: multi-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -1672,6 +1706,7 @@ metadata:
     topology: intra-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -1714,6 +1749,7 @@ metadata:
     topology: multi-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -1756,6 +1792,7 @@ metadata:
     topology: any
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
diff --git a/examples/kubernetes/connectivity-check/connectivity-check-proxy.yaml b/examples/kubernetes/connectivity-check/connectivity-check-proxy.yaml
index 50318b2740..b572f0dcf2 100644
--- a/examples/kubernetes/connectivity-check/connectivity-check-proxy.yaml
+++ b/examples/kubernetes/connectivity-check/connectivity-check-proxy.yaml
@@ -6,6 +6,7 @@ metadata:
     topology: any
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -56,6 +57,7 @@ metadata:
     topology: any
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -117,6 +119,7 @@ metadata:
     topology: intra-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -194,6 +197,7 @@ metadata:
     topology: multi-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -271,6 +275,7 @@ metadata:
     topology: intra-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -348,6 +353,7 @@ metadata:
     topology: multi-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -425,6 +431,7 @@ metadata:
     topology: intra-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -502,6 +509,7 @@ metadata:
     topology: multi-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -579,12 +587,13 @@ metadata:
     topology: any
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
+  type: ClusterIP
   selector:
     name: echo-c
-  type: ClusterIP
 apiVersion: v1
 kind: Service
 ---
@@ -595,12 +604,13 @@ metadata:
     topology: any
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
+  type: ClusterIP
   selector:
     name: echo-c
-  type: ClusterIP
   clusterIP: None
 apiVersion: v1
 kind: Service
@@ -612,11 +622,12 @@ metadata:
     topology: any
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports: []
+  type: ClusterIP
   selector:
     name: echo-c-host
-  type: ClusterIP
   clusterIP: None
 apiVersion: v1
 kind: Service
@@ -628,6 +639,7 @@ metadata:
     topology: intra-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -670,6 +682,7 @@ metadata:
     topology: multi-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -712,6 +725,7 @@ metadata:
     topology: intra-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -754,6 +768,7 @@ metadata:
     topology: multi-node
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -796,6 +811,7 @@ metadata:
     topology: any
     component: proxy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -821,6 +837,7 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -870,12 +887,13 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
+  type: ClusterIP
   selector:
     name: echo-a
-  type: ClusterIP
 apiVersion: v1
 kind: Service
 
@@ -887,6 +905,7 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -937,13 +956,14 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
     nodePort: 31313
+  type: NodePort
   selector:
     name: echo-b
-  type: NodePort
 apiVersion: v1
 kind: Service
 
@@ -955,6 +975,7 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -1017,11 +1038,12 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports: []
+  type: ClusterIP
   selector:
     name: echo-b-host
-  type: ClusterIP
   clusterIP: None
 apiVersion: v1
 kind: Service
diff --git a/examples/kubernetes/connectivity-check/connectivity-check-quarantine.yaml b/examples/kubernetes/connectivity-check/connectivity-check-quarantine.yaml
index 53524370db..bab79da3b1 100644
--- a/examples/kubernetes/connectivity-check/connectivity-check-quarantine.yaml
+++ b/examples/kubernetes/connectivity-check/connectivity-check-quarantine.yaml
@@ -6,6 +6,7 @@ metadata:
     topology: multi-node
     component: hostport-check
     quarantine: "true"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -83,6 +84,7 @@ metadata:
     topology: intra-node
     component: hostport-check
     quarantine: "true"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -160,6 +162,7 @@ metadata:
     topology: multi-node
     component: hostport-check
     quarantine: "true"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -237,6 +240,7 @@ metadata:
     topology: intra-node
     component: hostport-check
     quarantine: "true"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -314,6 +318,7 @@ metadata:
     topology: multi-node
     component: hostport-check
     quarantine: "true"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -391,6 +396,7 @@ metadata:
     topology: intra-node
     component: hostport-check
     quarantine: "true"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -468,6 +474,7 @@ metadata:
     topology: multi-node
     component: hostport-check
     quarantine: "true"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -507,6 +514,7 @@ metadata:
     topology: intra-node
     component: hostport-check
     quarantine: "true"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -546,6 +554,7 @@ metadata:
     topology: multi-node
     component: hostport-check
     quarantine: "true"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -585,6 +594,7 @@ metadata:
     topology: intra-node
     component: hostport-check
     quarantine: "true"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -626,6 +636,7 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -675,12 +686,13 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
+  type: ClusterIP
   selector:
     name: echo-a
-  type: ClusterIP
 apiVersion: v1
 kind: Service
 
@@ -692,6 +704,7 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -742,13 +755,14 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
     nodePort: 31313
+  type: NodePort
   selector:
     name: echo-b
-  type: NodePort
 apiVersion: v1
 kind: Service
 
@@ -760,6 +774,7 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -822,11 +837,12 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports: []
+  type: ClusterIP
   selector:
     name: echo-b-host
-  type: ClusterIP
   clusterIP: None
 apiVersion: v1
 kind: Service
diff --git a/examples/kubernetes/connectivity-check/connectivity-check-single-node.yaml b/examples/kubernetes/connectivity-check/connectivity-check-single-node.yaml
index 8610b2fff5..e428331663 100644
--- a/examples/kubernetes/connectivity-check/connectivity-check-single-node.yaml
+++ b/examples/kubernetes/connectivity-check/connectivity-check-single-node.yaml
@@ -6,6 +6,7 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -55,6 +56,7 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -105,6 +107,7 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -166,6 +169,7 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -218,6 +222,7 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -270,6 +275,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -314,6 +320,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -366,6 +373,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -418,12 +426,13 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
+  type: ClusterIP
   selector:
     name: echo-a
-  type: ClusterIP
 apiVersion: v1
 kind: Service
 ---
@@ -434,13 +443,14 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
     nodePort: 31313
+  type: NodePort
   selector:
     name: echo-b
-  type: NodePort
 apiVersion: v1
 kind: Service
 ---
@@ -451,12 +461,13 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
+  type: ClusterIP
   selector:
     name: echo-b
-  type: ClusterIP
   clusterIP: None
 apiVersion: v1
 kind: Service
@@ -468,11 +479,12 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports: []
+  type: ClusterIP
   selector:
     name: echo-b-host
-  type: ClusterIP
   clusterIP: None
 apiVersion: v1
 kind: Service
@@ -484,6 +496,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -515,6 +528,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -553,6 +567,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
diff --git a/examples/kubernetes/connectivity-check/connectivity-check.yaml b/examples/kubernetes/connectivity-check/connectivity-check.yaml
index 33d5e1c940..9123eeecf9 100644
--- a/examples/kubernetes/connectivity-check/connectivity-check.yaml
+++ b/examples/kubernetes/connectivity-check/connectivity-check.yaml
@@ -6,6 +6,7 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -55,6 +56,7 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -105,6 +107,7 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -166,6 +169,7 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -218,6 +222,7 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -270,6 +275,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -314,6 +320,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -366,6 +373,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -418,6 +426,7 @@ metadata:
     topology: multi-node
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -480,6 +489,7 @@ metadata:
     topology: multi-node
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -542,6 +552,7 @@ metadata:
     topology: multi-node
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -605,6 +616,7 @@ metadata:
     topology: multi-node
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   template:
     metadata:
@@ -668,12 +680,13 @@ metadata:
     topology: any
     component: network-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
+  type: ClusterIP
   selector:
     name: echo-a
-  type: ClusterIP
 apiVersion: v1
 kind: Service
 ---
@@ -684,13 +697,14 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
     nodePort: 31313
+  type: NodePort
   selector:
     name: echo-b
-  type: NodePort
 apiVersion: v1
 kind: Service
 ---
@@ -701,12 +715,13 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports:
   - port: 80
+  type: ClusterIP
   selector:
     name: echo-b
-  type: ClusterIP
   clusterIP: None
 apiVersion: v1
 kind: Service
@@ -718,11 +733,12 @@ metadata:
     topology: any
     component: services-check
     quarantine: "false"
+    type: autocheck
 spec:
   ports: []
+  type: ClusterIP
   selector:
     name: echo-b-host
-  type: ClusterIP
   clusterIP: None
 apiVersion: v1
 kind: Service
@@ -734,6 +750,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -765,6 +782,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
@@ -803,6 +821,7 @@ metadata:
     topology: any
     component: policy-check
     quarantine: "false"
+    type: autocheck
 spec:
   endpointSelector:
     matchLabels:
diff --git a/examples/kubernetes/connectivity-check/connectivity-debug-tools.yaml b/examples/kubernetes/connectivity-check/connectivity-debug-tools.yaml
new file mode 100644
index 0000000000..2eb99f8026
--- /dev/null
+++ b/examples/kubernetes/connectivity-check/connectivity-debug-tools.yaml
@@ -0,0 +1,80 @@
+# Automatically generated by Makefile. DO NOT EDIT
+---
+metadata:
+  name: query-dns-policy
+  labels:
+    name: query-dns-policy
+    topology: any
+    component: proxy-check
+    quarantine: "false"
+    type: tool
+spec:
+  template:
+    metadata:
+      labels:
+        name: query-dns-policy
+    spec:
+      hostNetwork: false
+      containers:
+      - name: query-dns-policy-container
+        ports: []
+        image: cilium/demo-client
+        imagePullPolicy: IfNotPresent
+        command:
+        - /bin/sh
+        - -c
+        - while true; do dig +noall +question +answer +timeout=1 +tries=1 www.google.com
+          && sleep 1 ; done
+        readinessProbe:
+          exec:
+            command:
+            - "true"
+        livenessProbe:
+          exec:
+            command:
+            - "true"
+  selector:
+    matchLabels:
+      name: query-dns-policy
+  replicas: 1
+apiVersion: apps/v1
+kind: Deployment
+---
+metadata:
+  name: query-dns-policy
+  labels:
+    name: query-dns-policy
+    topology: any
+    component: proxy-check
+    quarantine: "false"
+    type: tool
+spec:
+  endpointSelector:
+    matchLabels:
+      name: query-dns-policy
+  egress:
+  - toPorts:
+    - ports:
+      - port: "53"
+        protocol: ANY
+      rules:
+        dns:
+        - matchPattern: '*'
+    toEndpoints:
+    - matchLabels:
+        k8s:io.kubernetes.pod.namespace: kube-system
+        k8s:k8s-app: kube-dns
+  - toPorts:
+    - ports:
+      - port: "5353"
+        protocol: UDP
+      rules:
+        dns:
+        - matchPattern: '*'
+    toEndpoints:
+    - matchLabels:
+        k8s:io.kubernetes.pod.namespace: openshift-dns
+        k8s:dns.operator.openshift.io/daemonset-dns: default
+apiVersion: cilium.io/v2
+kind: CiliumNetworkPolicy
+
diff --git a/examples/kubernetes/connectivity-check/main_tool.cue b/examples/kubernetes/connectivity-check/main_tool.cue
index c31ce12c8e..e86bdd335e 100644
--- a/examples/kubernetes/connectivity-check/main_tool.cue
+++ b/examples/kubernetes/connectivity-check/main_tool.cue
@@ -15,7 +15,7 @@ objectSets: [
 	ingressCNP,
 ]
 
-globalFlags: "[-t component=<component>] [-t kind=<kind>] [-t name=<name>] [-t topology=<topology>] [-t quarantine=true]"
+globalFlags: "[-t component=<component>] [-t kind=<kind>] [-t name=<name>] [-t topology=<topology>] [-t quarantine=true] [-t type=<tooltype>]"
 
 ccCommand: {
 	#flags: {
@@ -23,6 +23,7 @@ ccCommand: {
 		name:       *"" | string                                                                  @tag(name)
 		topology:   *"any" | "single-node"                                                        @tag(topology,short=any|single-node)
 		kind:       *"" | "Deployment" | "Service" | "CiliumNetworkPolicy"                        @tag(kind,short=Deployment|Service|CiliumNetworkPolicy)
+		type:       *"autocheck" | "tool"                                                         @tag(type,short=autocheck|tool)
 		quarantine: *"false" | "true"                                                             @tag(quarantine,short=false|true)
 	}
 
@@ -39,8 +40,12 @@ ccCommand: {
 		}
 	}
 
+	task: filterType: {
+		resources: [ for x in task.filterComponent.resources if x.metadata.labels.type == #flags.type {x}]
+	}
+
 	task: filterQuarantine: {
-		resources: [ for x in task.filterComponent.resources if x.metadata.labels.quarantine == #flags.quarantine {x}]
+		resources: [ for x in task.filterType.resources if x.metadata.labels.quarantine == #flags.quarantine {x}]
 	}
 
 	task: filterTopology: {
diff --git a/examples/kubernetes/connectivity-check/resources.cue b/examples/kubernetes/connectivity-check/resources.cue
index fc2f02865d..8d188d3359 100644
--- a/examples/kubernetes/connectivity-check/resources.cue
+++ b/examples/kubernetes/connectivity-check/resources.cue
@@ -10,9 +10,12 @@ _spec: {
 	_affinity:        *"" | string
 	_antiAffinity:    *"" | string
 	_serverPort:      *"" | string
-	_probeTarget:     string
-	_probePath:       *"/public" | string
-	_probeExpectFail: *false | true
+
+	_probeTarget:       string
+	_probePath:         *"/public" | string
+	_probeExpectFail:   *false | true
+	_allowProbe:        [] | [...string]
+	_rejectProbe:       [] | [...string]
 
 	_containers: [...{}]
 	_enableMultipleContainers: *false | true
@@ -34,8 +37,12 @@ _spec: {
 		}]
 	}
 
-	_allowProbe: [ "curl", "-sS", "--fail", "--connect-timeout", "\(_probeFailureTimeout)", "-o", "/dev/null", "\(_probeTarget)\(_probePath)"]
-	_rejectProbe: [ "ash", "-c", "! curl -s --fail --connect-timeout \(_probeFailureTimeout) -o /dev/null \(_probeTarget)/private"]
+	if len(_allowProbe) == 0 {
+		_allowProbe: [ "curl", "-sS", "--fail", "--connect-timeout", "\(_probeFailureTimeout)", "-o", "/dev/null", "\(_probeTarget)\(_probePath)"]
+	}
+	if len(_rejectProbe) == 0 {
+		_rejectProbe: [ "ash", "-c", "! curl -s --fail --connect-timeout \(_probeFailureTimeout) -o /dev/null \(_probeTarget)/private"]
+	}
 	if !_enableMultipleContainers {
 		_c1: _container & {
 			name: "\(_name)-container"
@@ -83,6 +90,7 @@ _spec: {
 		labels: {
 			name:       _name
 			topology:   *"any" | string
+			type:       *"autocheck" | string
 			component:  *"invalid" | string
 			quarantine: *"false" | "true"
 		}
@@ -154,6 +162,7 @@ service: [ID=_]: {
 		labels: {
 			name:       _name
 			topology:   *"any" | string
+			type:       *"autocheck" | string
 			component:  *"invalid" | string
 			quarantine: *"false" | "true"
 		}
@@ -174,6 +183,7 @@ _cnp: {
 		labels: {
 			name:       _name
 			topology:   *"any" | string
+			type:       *"autocheck" | string
 			component:  *"invalid" | string
 			quarantine: *"false" | "true"
 		}
@@ -272,6 +282,7 @@ for x in [deployment] for k, v in x {
 				name:       "\(v.metadata.name)-headless"
 				component:  v.metadata.labels.component
 				topology:   *"any" | string
+				type:       *"autocheck" | string
 				quarantine: *"false" | "true"
 			}
 			spec: selector:  v.spec.template.metadata.labels
diff --git a/examples/kubernetes/connectivity-check/tools.cue b/examples/kubernetes/connectivity-check/tools.cue
new file mode 100644
index 0000000000..2f2b761624
--- /dev/null
+++ b/examples/kubernetes/connectivity-check/tools.cue
@@ -0,0 +1,28 @@
+package connectivity_check
+
+// Definitions in this file are not intended for continuous integration
+// deployment, as their readiness/liveness probes are overridden to always
+// pass. Instead, they can be deployed and their logs followed for manual
+// testing via commands like the following:
+//
+// $ kubectl logs -l name=query-dns-policy --timestamps -f
+
+// query-dns-policy is written for debugging by watching the logs; the
+// liveness/readiness probes are bypassed so it will always report success!
+deployment: "query-dns-policy": {
+	_image: "cilium/demo-client"
+	_command: ["/bin/sh", "-c", "while true; do dig +noall +question +answer +timeout=1 +tries=1 www.google.com && sleep 1 ; done"]
+	_allowProbe: [ "true" ]
+	metadata: labels: {
+		component: "proxy-check"
+		type: "tool"
+	}
+}
+egressCNP: "query-dns-policy": {
+	metadata: labels: {
+		component: "proxy-check"
+		type: "tool"
+	}
+	_allowDNS: true
+	_enableDNSVisibility: true
+}
diff --git a/go.mod b/go.mod
index 005294f541..38fd090cf5 100644
--- a/go.mod
+++ b/go.mod
@@ -86,11 +86,11 @@ require (
 	gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f
 	gopkg.in/natefinch/lumberjack.v2 v2.0.0
 	gopkg.in/yaml.v2 v2.3.0
-	k8s.io/api v0.19.0-rc.3
-	k8s.io/apiextensions-apiserver v0.19.0-rc.3
-	k8s.io/apimachinery v0.19.0-rc.3
-	k8s.io/client-go v0.19.0-rc.3
-	k8s.io/code-generator v0.19.0-rc.3
+	k8s.io/api v0.19.0-rc.4
+	k8s.io/apiextensions-apiserver v0.19.0-rc.4
+	k8s.io/apimachinery v0.19.0-rc.4
+	k8s.io/client-go v0.19.0-rc.4
+	k8s.io/code-generator v0.19.0-rc.4
 	k8s.io/klog/v2 v2.2.0
 	sigs.k8s.io/yaml v1.2.0
 )
@@ -98,5 +98,5 @@ require (
 replace (
 	github.com/miekg/dns => github.com/cilium/dns v1.1.4-0.20190417235132-8e25ec9a0ff3
 	github.com/optiopay/kafka => github.com/cilium/kafka v0.0.0-20180809090225-01ce283b732b
-	k8s.io/client-go => github.com/cilium/client-go v0.0.0-20200729144658-19fb033f881a
+	k8s.io/client-go => github.com/cilium/client-go v0.0.0-20200804232341-96a4c8f3593c
 )
diff --git a/go.sum b/go.sum
index e6e65e075e..4eaadaf7d2 100644
--- a/go.sum
+++ b/go.sum
@@ -114,8 +114,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/cilium/arping v1.0.1-0.20190728065459-c5eaf8d7a710 h1:htVjkajqUYy6JmLMGlZYxfZ4urQq7rDvgUfmSJX7fSg=
 github.com/cilium/arping v1.0.1-0.20190728065459-c5eaf8d7a710/go.mod h1:ohfPr9hSb4vRsw5UEjCs3OoWD38U0z8EfSgZPbIe/+M=
-github.com/cilium/client-go v0.0.0-20200729144658-19fb033f881a h1:l16dIbsngozvhWeNXjmi9eNKh6YA98WbvfZuTUnRol0=
-github.com/cilium/client-go v0.0.0-20200729144658-19fb033f881a/go.mod h1:fSQURkVnvc4NTkbel1oV5NGth3QQVr0Lhpp4bzvrdAs=
+github.com/cilium/client-go v0.0.0-20200804232341-96a4c8f3593c h1:8ZEo7X8wroKk9q+gfphqPUob5D+8MJLHCxdoWCtbwdg=
+github.com/cilium/client-go v0.0.0-20200804232341-96a4c8f3593c/go.mod h1:6fKW3B9PgoMpZwAdRpMYyhRFCb4kHIfriPYpiiqhBWw=
 github.com/cilium/deepequal-gen v0.0.0-20200406125435-ad6a9003139e h1:VZolEtS7AlGDu3IH368iqkvfQQSGPgOnPjNaUx4dS7M=
 github.com/cilium/deepequal-gen v0.0.0-20200406125435-ad6a9003139e/go.mod h1:c4R5wxGyXhbM6zyKeRKNIc9aab5EZi4z4oOSZvUMvZA=
 github.com/cilium/dns v1.1.4-0.20190417235132-8e25ec9a0ff3 h1:wenYMyWJ08dgEUUj0Ija8qdK/V9vL3ThAD5sjOYlFlg=
@@ -1013,16 +1013,16 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-k8s.io/api v0.19.0-rc.3 h1:bq4HUeQmlDeIKZuvVNPAeaOAru+IVk/q/MsNQo634+k=
-k8s.io/api v0.19.0-rc.3/go.mod h1:hfDN4tL/yqfs4aVrlvx2vNMhKACfoiTyGSvVnygqSd4=
-k8s.io/apiextensions-apiserver v0.19.0-rc.3 h1:NkGMRRObuGC04gzCDBM/29/H45HQEmBZkQp3tYr8K3c=
-k8s.io/apiextensions-apiserver v0.19.0-rc.3/go.mod h1:R8Wu3tAk5fn77YqwhO5xXrRbOuJP1V0ariDEdj0fZFQ=
-k8s.io/apimachinery v0.19.0-rc.3 h1:awj/gA5ZC1m9H+iKT9EFxuiss1LNk6O2zVpDJXhr7uU=
-k8s.io/apimachinery v0.19.0-rc.3/go.mod h1:oE8UQU9DqIIc9PyIEYxTj/oJECzZLymCEU9dL0H4F+o=
-k8s.io/apiserver v0.19.0-rc.3/go.mod h1:EqUGouRrGmm6pi77sXXGjRqylr+X08Kz87v2aBJQmkk=
-k8s.io/code-generator v0.19.0-rc.3 h1:kFC+IQTerefBRV8XcOF4Vht0YP6AhMfZFF/lnv/FtaY=
-k8s.io/code-generator v0.19.0-rc.3/go.mod h1:l+Q5vASED8GWxNspCbHMV4q9mcQ6DhLzqwoZhvWz7wU=
-k8s.io/component-base v0.19.0-rc.3/go.mod h1:HLUAyKvLV2+BnaGb92sNqrnsyr0V0XfH/sCCWfKCyG0=
+k8s.io/api v0.19.0-rc.4 h1:l4LnbkiVjh8estJqbl/kZIfHDi1i8tpCegRzY0dw3XU=
+k8s.io/api v0.19.0-rc.4/go.mod h1:1xlMhKahfl3bVAn1T1PhMriUVYwRNJ7D8YMDnUz/yGw=
+k8s.io/apiextensions-apiserver v0.19.0-rc.4 h1:ZJOX/ngtfSEMH1+VtPX3he+z87Z82X0ra0gJuyPnid4=
+k8s.io/apiextensions-apiserver v0.19.0-rc.4/go.mod h1:uKN7TEAym7OS0plm0zNxEE/NeTIzLCFEahIC3lC1HzQ=
+k8s.io/apimachinery v0.19.0-rc.4 h1:LVJnI5LJCzGHtGiL1ztkYEceAzatGPnnmUwxiLUx77E=
+k8s.io/apimachinery v0.19.0-rc.4/go.mod h1:oE8UQU9DqIIc9PyIEYxTj/oJECzZLymCEU9dL0H4F+o=
+k8s.io/apiserver v0.19.0-rc.4/go.mod h1:0cfEqMQGjl0GpzK+o9yHgzdcCU8kW/wy+JuSGo3Zl68=
+k8s.io/code-generator v0.19.0-rc.4 h1:yGFQlO/A4EMc1ROV+ObmHVPXYrNhNaYQ66xXZMWpm/A=
+k8s.io/code-generator v0.19.0-rc.4/go.mod h1:l+Q5vASED8GWxNspCbHMV4q9mcQ6DhLzqwoZhvWz7wU=
+k8s.io/component-base v0.19.0-rc.4/go.mod h1:N91w2PScsJ53fqQDlu7zqBhGaAKz6YUjrybPtS053Vg=
 k8s.io/gengo v0.0.0-20200205140755-e0e292d8aa12 h1:pZzawYyz6VRNPVYpqGv61LWCimQv1BihyeqFrp50/G4=
 k8s.io/gengo v0.0.0-20200205140755-e0e292d8aa12/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
@@ -1035,8 +1035,8 @@ k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A=
 k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
 k8s.io/kube-openapi v0.0.0-20200427153329-656914f816f9 h1:5NC2ITmvg8RoxoH0wgmL4zn4VZqXGsKbxrikjaQx6s4=
 k8s.io/kube-openapi v0.0.0-20200427153329-656914f816f9/go.mod h1:bfCVj+qXcEaE5SCvzBaqpOySr6tuCcpPKqF6HD8nyCw=
-k8s.io/utils v0.0.0-20200720150651-0bdb4ca86cbc h1:GiXZzevctVRRBh56shqcqB9s9ReWMU6GTsFyE2RCFJQ=
-k8s.io/utils v0.0.0-20200720150651-0bdb4ca86cbc/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
+k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K8Hf8whTseBgJcg=
+k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/goversion v1.2.0 h1:SPn+NLTiAG7w30IRK/DKp1BjvpWabYgxlLp/+kx5J8w=
 rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=
diff --git a/install/kubernetes/cilium/charts/operator/templates/deployment.yaml b/install/kubernetes/cilium/charts/operator/templates/deployment.yaml
index 9be12f2615..b19399654b 100644
--- a/install/kubernetes/cilium/charts/operator/templates/deployment.yaml
+++ b/install/kubernetes/cilium/charts/operator/templates/deployment.yaml
@@ -36,6 +36,20 @@ spec:
         io.cilium/app: operator
         name: cilium-operator
     spec:
+{{- if or (ge .Capabilities.KubeVersion.Minor "14") (gt .Capabilities.KubeVersion.Major "1") }}
+      # In HA mode, cilium-operator pods must not be scheduled on the same
+      # node as they will clash with each other.
+      affinity:
+        podAntiAffinity:
+          requiredDuringSchedulingIgnoredDuringExecution:
+          - labelSelector:
+              matchExpressions:
+              - key: io.cilium/app
+                operator: In
+                values:
+                - operator
+            topologyKey: "kubernetes.io/hostname"
+{{- end }}
       containers:
       - args:
         - --config-dir=/tmp/cilium/config-map
diff --git a/install/kubernetes/experimental-install.yaml b/install/kubernetes/experimental-install.yaml
index b015a02f74..e5edbd84bb 100644
--- a/install/kubernetes/experimental-install.yaml
+++ b/install/kubernetes/experimental-install.yaml
@@ -829,6 +829,18 @@ spec:
         io.cilium/app: operator
         name: cilium-operator
     spec:
+      # In HA mode, cilium-operator pods must not be scheduled on the same
+      # node as they will clash with each other.
+      affinity:
+        podAntiAffinity:
+          requiredDuringSchedulingIgnoredDuringExecution:
+          - labelSelector:
+              matchExpressions:
+              - key: io.cilium/app
+                operator: In
+                values:
+                - operator
+            topologyKey: "kubernetes.io/hostname"
       containers:
       - args:
         - --config-dir=/tmp/cilium/config-map
diff --git a/install/kubernetes/quick-install.yaml b/install/kubernetes/quick-install.yaml
index a00778583f..ff4da10759 100644
--- a/install/kubernetes/quick-install.yaml
+++ b/install/kubernetes/quick-install.yaml
@@ -574,6 +574,18 @@ spec:
         io.cilium/app: operator
         name: cilium-operator
     spec:
+      # In HA mode, cilium-operator pods must not be scheduled on the same
+      # node as they will clash with each other.
+      affinity:
+        podAntiAffinity:
+          requiredDuringSchedulingIgnoredDuringExecution:
+          - labelSelector:
+              matchExpressions:
+              - key: io.cilium/app
+                operator: In
+                values:
+                - operator
+            topologyKey: "kubernetes.io/hostname"
       containers:
       - args:
         - --config-dir=/tmp/cilium/config-map
diff --git a/jenkinsfiles/ginkgo-gke.Jenkinsfile b/jenkinsfiles/ginkgo-gke.Jenkinsfile
index 0ba3ce0e1a..874bacc1f8 100644
--- a/jenkinsfiles/ginkgo-gke.Jenkinsfile
+++ b/jenkinsfiles/ginkgo-gke.Jenkinsfile
@@ -86,7 +86,7 @@ pipeline {
             parallel {
                 stage('Make Cilium images') {
                     steps {
-                        sh 'cd ${TESTDIR}; ./make-images-push-to-local-registry.sh $(./print-node-ip.sh) ${TAG}'
+                        sh 'cd ${TESTDIR}; ./make-images-push-to-local-registry.sh $(./print-node-ip.sh) ${TAG} "--no-cache"'
                     }
                     post {
                         unsuccessful {
diff --git a/pkg/checker/checker.go b/pkg/checker/checker.go
index e5f99a5484..46154bea11 100644
--- a/pkg/checker/checker.go
+++ b/pkg/checker/checker.go
@@ -15,6 +15,7 @@
 package checker
 
 import (
+	"fmt"
 	"reflect"
 	"regexp"
 
@@ -172,3 +173,42 @@ func (checker *matchesChecker) Check(params []interface{}, _ []string) (result b
 	}
 	return matches, ""
 }
+
+// -----------------------------------------------------------------------
+// HasKey checker.
+
+type hasKeyChecker struct {
+	*check.CheckerInfo
+}
+
+// The HasKey checker verifies that the obtained map contains the
+// provided key.
+//
+// For example:
+//
+//     c.Assert(myMap, HasKey, "five")
+//
+var HasKey check.Checker = &hasKeyChecker{
+	&check.CheckerInfo{Name: "HasKey", Params: []string{"map", "key"}},
+}
+
+func (checker *hasKeyChecker) Check(params []interface{}, names []string) (result bool, error string) {
+	m := reflect.ValueOf(params[0])
+	mType := m.Type()
+	key := reflect.ValueOf(params[1])
+	keyType := key.Type()
+
+	if mType.Kind() != reflect.Map {
+		return false, fmt.Sprintf("'%s' must be a map", names[0])
+	}
+	if mType.Key() != keyType {
+		return false, fmt.Sprintf("'%s' must be of '%s's key type (%s, not %s)",
+			names[1], names[0], mType.Key(), keyType)
+	}
+	for _, v := range m.MapKeys() {
+		if v.Interface() == key.Interface() {
+			return true, ""
+		}
+	}
+	return false, fmt.Sprintf("'%s' has no key %v", names[0], key.Interface())
+}
diff --git a/pkg/endpoint/bpf.go b/pkg/endpoint/bpf.go
index a94af35f3d..234c90716f 100644
--- a/pkg/endpoint/bpf.go
+++ b/pkg/endpoint/bpf.go
@@ -211,15 +211,18 @@ func (e *Endpoint) addNewRedirectsFromDesiredPolicy(ingress bool, desiredRedirec
 				var finalizeFunc revert.FinalizeFunc
 				var revertFunc revert.RevertFunc
 
-				proxyID, err := e.ProxyID(e.desiredPolicy.NamedPortsMap, l4)
-				if err != nil {
-					// skip redirects for which a proxyID cannot be created.
-					// Right now this happens only due to named port mapping not existing and
-					// we'll be called again when the mapping is available.
-					log.WithError(err).WithField(logfields.EndpointID, e.ID).Warning("Skipping adding redirect")
+				proxyID := e.proxyID(l4)
+				if proxyID == "" {
+					// Skip redirects for which a proxyID cannot be created.
+					// This may happen due to the named port mapping not
+					// existing or multiple PODs defining the same port name
+					// with different port values. The redirect will be created
+					// when the mapping is available or when the port name
+					// conflicts have been resolved in POD specs.
 					continue
 				}
 
+				var err error
 				redirectPort, err, finalizeFunc, revertFunc = e.proxy.CreateOrUpdateRedirect(l4, proxyID, e, proxyWaitGroup)
 				if err != nil {
 					revertStack.Revert() // Ignore errors while reverting. This is best-effort.
@@ -259,7 +262,7 @@ func (e *Endpoint) addNewRedirectsFromDesiredPolicy(ingress bool, desiredRedirec
 				direction = trafficdirection.Egress
 			}
 
-			keysFromFilter := l4.ToMapState(e.desiredPolicy.NamedPortsMap, direction)
+			keysFromFilter := l4.ToMapState(e, direction)
 
 			for keyFromFilter, entry := range keysFromFilter {
 				if oldEntry, ok := e.desiredPolicy.PolicyMapState[keyFromFilter]; ok {
diff --git a/pkg/endpoint/endpoint.go b/pkg/endpoint/endpoint.go
index 316884e186..6860b69318 100644
--- a/pkg/endpoint/endpoint.go
+++ b/pkg/endpoint/endpoint.go
@@ -46,8 +46,8 @@ import (
 	ciliumio "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
 	slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/core/v1"
 	"github.com/cilium/cilium/pkg/labels"
-	pkgLabels "github.com/cilium/cilium/pkg/labels"
 	"github.com/cilium/cilium/pkg/lock"
+	"github.com/cilium/cilium/pkg/logging"
 	"github.com/cilium/cilium/pkg/logging/logfields"
 	"github.com/cilium/cilium/pkg/mac"
 	"github.com/cilium/cilium/pkg/maps/ctmap"
@@ -154,7 +154,7 @@ type Endpoint struct {
 	// OpLabels is the endpoint's label configuration
 	//
 	// FIXME: Rename this field to Labels
-	OpLabels pkgLabels.OpLabels
+	OpLabels labels.OpLabels
 
 	// identityRevision is incremented each time the identity label
 	// information of the endpoint has changed
@@ -225,7 +225,10 @@ type Endpoint struct {
 
 	// k8sPorts contains container ports associated in the pod.
 	// It is used to enforce k8s network policies with port names.
-	k8sPorts policy.NamedPortsMap
+	k8sPorts policy.NamedPortMap
+
+	// logLimiter rate limits potentially repeating warning logs
+	logLimiter logging.Limiter
 
 	// k8sPortsSet keep track when k8sPorts was set at least one time.
 	hasK8sMetadata bool
@@ -420,7 +423,7 @@ func createEndpoint(owner regeneration.Owner, proxy EndpointProxy, allocator cac
 		ID:              ID,
 		proxy:           proxy,
 		ifName:          ifName,
-		OpLabels:        pkgLabels.NewOpLabels(),
+		OpLabels:        labels.NewOpLabels(),
 		DNSHistory:      fqdn.NewDNSCacheWithLimit(option.Config.ToFQDNsMinTTL, option.Config.ToFQDNsMaxIPsPerHost),
 		DNSZombies:      fqdn.NewDNSZombieMappings(option.Config.ToFQDNsMaxDeferredConnectionDeletes),
 		state:           "",
@@ -430,6 +433,7 @@ func createEndpoint(owner regeneration.Owner, proxy EndpointProxy, allocator cac
 		controllers:     controller.NewManager(),
 		regenFailedChan: make(chan struct{}, 1),
 		allocator:       allocator,
+		logLimiter:      logging.NewLimiter(10*time.Second, 3), // 1 log / 10 secs, burst of 3
 	}
 
 	ctx, cancel := context.WithCancel(context.Background())
@@ -498,7 +502,7 @@ func (e *Endpoint) GetID16() uint16 {
 
 // getK8sPodLabels returns all labels that exist in the endpoint and were
 // derived from k8s pod.
-func (e *Endpoint) getK8sPodLabels() pkgLabels.Labels {
+func (e *Endpoint) getK8sPodLabels() labels.Labels {
 	e.unconditionalRLock()
 	defer e.runlock()
 	allLabels := e.OpLabels.AllLabels()
@@ -506,9 +510,9 @@ func (e *Endpoint) getK8sPodLabels() pkgLabels.Labels {
 		return nil
 	}
 
-	allLabelsFromK8s := allLabels.GetFromSource(pkgLabels.LabelSourceK8s)
+	allLabelsFromK8s := allLabels.GetFromSource(labels.LabelSourceK8s)
 
-	k8sEPPodLabels := pkgLabels.Labels{}
+	k8sEPPodLabels := labels.Labels{}
 	for k, v := range allLabelsFromK8s {
 		if !strings.HasPrefix(v.Key, ciliumio.PodNamespaceMetaLabels) &&
 			!strings.HasPrefix(v.Key, ciliumio.PolicyLabelServiceAccount) &&
@@ -954,7 +958,7 @@ func (e *Endpoint) Update(cfg *models.EndpointConfigurationSpec) error {
 
 // HasLabels returns whether endpoint e contains all labels l. Will return 'false'
 // if any label in l is not in the endpoint's labels.
-func (e *Endpoint) HasLabels(l pkgLabels.Labels) bool {
+func (e *Endpoint) HasLabels(l labels.Labels) bool {
 	e.unconditionalRLock()
 	defer e.runlock()
 
@@ -964,7 +968,7 @@ func (e *Endpoint) HasLabels(l pkgLabels.Labels) bool {
 // hasLabelsRLocked returns whether endpoint e contains all labels l. Will
 // return 'false' if any label in l is not in the endpoint's labels.
 // e.Mutex must be RLocked
-func (e *Endpoint) hasLabelsRLocked(l pkgLabels.Labels) bool {
+func (e *Endpoint) hasLabelsRLocked(l labels.Labels) bool {
 	allEpLabels := e.OpLabels.AllLabels()
 
 	for _, v := range l {
@@ -986,7 +990,7 @@ func (e *Endpoint) hasLabelsRLocked(l pkgLabels.Labels) bool {
 // replaceInformationLabels replaces the information labels of the endpoint.
 // Passing a nil set of labels will not perform any action.
 // Must be called with e.Mutex.Lock().
-func (e *Endpoint) replaceInformationLabels(l pkgLabels.Labels) {
+func (e *Endpoint) replaceInformationLabels(l labels.Labels) {
 	if l == nil {
 		return
 	}
@@ -999,7 +1003,7 @@ func (e *Endpoint) replaceInformationLabels(l pkgLabels.Labels) {
 // Passing a nil set of labels will not perform any action and will return the
 // current endpoint's identityRevision.
 // Must be called with e.Mutex.Lock().
-func (e *Endpoint) replaceIdentityLabels(l pkgLabels.Labels) int {
+func (e *Endpoint) replaceIdentityLabels(l labels.Labels) int {
 	if l == nil {
 		return e.identityRevision
 	}
@@ -1138,7 +1142,7 @@ func (e *Endpoint) SetK8sNamespace(name string) {
 // Reading the 'e.k8sPorts' member (the "map pointer") *itself* requires the endpoint lock!
 // Can't really error out as that might break backwards compatibility.
 func (e *Endpoint) SetK8sMetadata(containerPorts []slim_corev1.ContainerPort) error {
-	k8sPorts := make(policy.NamedPortsMap, len(containerPorts))
+	k8sPorts := make(policy.NamedPortMap, len(containerPorts))
 	for _, cp := range containerPorts {
 		if cp.Name == "" {
 			continue // silently skip unnamed ports
@@ -1159,6 +1163,17 @@ func (e *Endpoint) SetK8sMetadata(containerPorts []slim_corev1.ContainerPort) er
 	return nil
 }
 
+// GetK8sPorts returns the k8sPorts, which must not be modified by the caller
+func (e *Endpoint) GetK8sPorts() (k8sPorts policy.NamedPortMap, err error) {
+	err = e.rlockAlive()
+	if err != nil {
+		return nil, err
+	}
+	k8sPorts = e.k8sPorts
+	e.mutex.RUnlock()
+	return k8sPorts, nil
+}
+
 // HaveK8sMetadata returns true once hasK8sMetadata was set
 func (e *Endpoint) HaveK8sMetadata() (metadataSet bool) {
 	e.mutex.RLock()
@@ -1645,7 +1660,7 @@ func (e *Endpoint) RunMetadataResolver(resolveMetadata MetadataResolverCB) {
 // Labels can be added or deleted. If a label change is performed, the
 // endpoint will receive a new identity and will be regenerated. Both of these
 // operations will happen in the background.
-func (e *Endpoint) ModifyIdentityLabels(addLabels, delLabels pkgLabels.Labels) error {
+func (e *Endpoint) ModifyIdentityLabels(addLabels, delLabels labels.Labels) error {
 	if err := e.lockAlive(); err != nil {
 		return err
 	}
@@ -1664,7 +1679,7 @@ func (e *Endpoint) ModifyIdentityLabels(addLabels, delLabels pkgLabels.Labels) e
 	// started with any label.
 	if len(addLabels) == 0 && len(delLabels) == 0 && e.IsInit() {
 		idLabls := e.OpLabels.IdentityLabels()
-		delete(idLabls, pkgLabels.IDNameInit)
+		delete(idLabls, labels.IDNameInit)
 		rev = e.replaceIdentityLabels(idLabls)
 		changed = true
 	}
@@ -1688,8 +1703,8 @@ func (e *Endpoint) ModifyIdentityLabels(addLabels, delLabels pkgLabels.Labels) e
 // IsInit returns true if the endpoint still hasn't received identity labels,
 // i.e. has the special identity with label reserved:init.
 func (e *Endpoint) IsInit() bool {
-	init, found := e.OpLabels.GetIdentityLabel(pkgLabels.IDNameInit)
-	return found && init.Source == pkgLabels.LabelSourceReserved
+	init, found := e.OpLabels.GetIdentityLabel(labels.IDNameInit)
+	return found && init.Source == labels.LabelSourceReserved
 }
 
 // UpdateLabels is called to update the labels of an endpoint. Calls to this
@@ -1701,7 +1716,7 @@ func (e *Endpoint) IsInit() bool {
 // run first synchronously if 'blocking' is true, and then in the background.
 //
 // Returns 'true' if endpoint regeneration was triggered.
-func (e *Endpoint) UpdateLabels(ctx context.Context, identityLabels, infoLabels pkgLabels.Labels, blocking bool) (regenTriggered bool) {
+func (e *Endpoint) UpdateLabels(ctx context.Context, identityLabels, infoLabels labels.Labels, blocking bool) (regenTriggered bool) {
 	log.WithFields(logrus.Fields{
 		logfields.ContainerID:    e.GetShortContainerID(),
 		logfields.EndpointID:     e.StringID(),
diff --git a/pkg/endpoint/policy.go b/pkg/endpoint/policy.go
index f51941adc3..a8e5944228 100644
--- a/pkg/endpoint/policy.go
+++ b/pkg/endpoint/policy.go
@@ -16,6 +16,7 @@ package endpoint
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"os"
 	"strconv"
@@ -40,29 +41,89 @@ import (
 	"github.com/cilium/cilium/pkg/option"
 	"github.com/cilium/cilium/pkg/policy"
 	"github.com/cilium/cilium/pkg/revert"
+	"github.com/cilium/cilium/pkg/u8proto"
 
 	"github.com/sirupsen/logrus"
 )
 
-// ProxyID returns a unique string to identify a proxy mapping.
-func (e *Endpoint) ProxyID(npMap policy.NamedPortsMap, l4 *policy.L4Filter) (string, error) {
-	return policy.ProxyIDFromFilter(e.ID, npMap, l4)
+// GetNamedPort returns the port for the given name.
+// Must be called with e.mutex NOT held
+func (e *Endpoint) GetNamedPort(ingress bool, name string, proto uint8) uint16 {
+	if ingress {
+		// Ingress only needs the ports of the POD itself
+		k8sPorts, err := e.GetK8sPorts()
+		if err != nil {
+			if e.logLimiter.Allow() {
+				e.getLogger().WithFields(logrus.Fields{
+					logfields.PortName:         name,
+					logfields.Protocol:         u8proto.U8proto(proto).String(),
+					logfields.TrafficDirection: "ingress",
+				}).WithError(err).Warning("Skipping named port")
+			}
+			return 0
+		}
+		return e.getNamedPortIngress(k8sPorts, name, proto)
+	}
+	// egress needs named ports of all the pods
+	return e.getNamedPortEgress(ipcache.IPIdentityCache.GetNamedPorts(), name, proto)
+}
+
+// GetNamedPortLocked returns port for the given name. May return an invalid (0) port
+// Must be called with e.mutex held.
+func (e *Endpoint) GetNamedPortLocked(ingress bool, name string, proto uint8) uint16 {
+	if ingress {
+		// Ingress only needs the ports of the POD itself
+		return e.getNamedPortIngress(e.k8sPorts, name, proto)
+	}
+	// egress needs named ports of all the pods
+	return e.getNamedPortEgress(ipcache.IPIdentityCache.GetNamedPorts(), name, proto)
+}
+
+func (e *Endpoint) getNamedPortIngress(npMap policy.NamedPortMap, name string, proto uint8) uint16 {
+	port, err := npMap.GetNamedPort(name, proto)
+	if err != nil && e.logLimiter.Allow() {
+		e.getLogger().WithFields(logrus.Fields{
+			logfields.PortName:         name,
+			logfields.Protocol:         u8proto.U8proto(proto).String(),
+			logfields.TrafficDirection: "ingress",
+		}).WithError(err).Warning("Skipping named port")
+	}
+	return port
+}
+
+func (e *Endpoint) getNamedPortEgress(npMap policy.NamedPortMultiMap, name string, proto uint8) uint16 {
+	port, err := npMap.GetNamedPort(name, proto)
+	// Skip logging for ErrUnknownNamedPort on egress, as the destination POD with the port name
+	// is likely not scheduled yet.
+	if err != nil && !errors.Is(err, policy.ErrUnknownNamedPort) && e.logLimiter.Allow() {
+		e.getLogger().WithFields(logrus.Fields{
+			logfields.PortName:         name,
+			logfields.Protocol:         u8proto.U8proto(proto).String(),
+			logfields.TrafficDirection: "egress",
+		}).WithError(err).Warning("Skipping named port")
+	}
+	return port
+}
+
+// proxyID returns a unique string to identify a proxy mapping.
+// Must be called with e.mutex held.
+func (e *Endpoint) proxyID(l4 *policy.L4Filter) string {
+	port := uint16(l4.Port)
+	if port == 0 && l4.PortName != "" {
+		port = e.GetNamedPortLocked(l4.Ingress, l4.PortName, uint8(l4.U8Proto))
+		if port == 0 {
+			return ""
+		}
+	}
+	return policy.ProxyID(e.ID, l4.Ingress, string(l4.Protocol), port)
 }
 
 // lookupRedirectPort returns the redirect L4 proxy port for the given L4
 // policy map key, in host byte order. Returns 0 if not found or the
 // filter doesn't require a redirect.
-// Must be called with Endpoint.Mutex held.
-func (e *Endpoint) LookupRedirectPortLocked(npMap policy.NamedPortsMap, l4Filter *policy.L4Filter) uint16 {
-	if !l4Filter.IsRedirect() {
-		return 0
-	}
-	proxyID, err := e.ProxyID(npMap, l4Filter)
-	if err != nil {
-		e.getLogger().WithError(err).Warn("ProxyID failed")
-		return 0
-	}
-	return e.realizedRedirects[proxyID]
+// Must be called with Endpoint.mutex held.
+func (e *Endpoint) LookupRedirectPortLocked(ingress bool, protocol string, port uint16) uint16 {
+	return e.realizedRedirects[policy.ProxyID(e.ID, ingress, protocol, port)]
 }
 
 // Note that this function assumes that endpoint policy has already been generated!
@@ -88,7 +149,7 @@ func (e *Endpoint) updateNetworkPolicy(proxyWaitGroup *completion.WaitGroup) (re
 	}
 
 	// Publish the updated policy to L7 proxies.
-	return e.proxy.UpdateNetworkPolicy(e, e.desiredPolicy.L4Policy, e.desiredPolicy.NamedPortsMap, e.desiredPolicy.IngressPolicyEnabled, e.desiredPolicy.EgressPolicyEnabled, proxyWaitGroup)
+	return e.proxy.UpdateNetworkPolicy(e, e.desiredPolicy.L4Policy, e.desiredPolicy.IngressPolicyEnabled, e.desiredPolicy.EgressPolicyEnabled, proxyWaitGroup)
 }
 
 func (e *Endpoint) useCurrentNetworkPolicy(proxyWaitGroup *completion.WaitGroup) {
@@ -181,7 +242,7 @@ func (e *Endpoint) regeneratePolicy() (retErr error) {
 		e.getLogger().WithError(err).Warning("Failed to update policy")
 		return err
 	}
-	calculatedPolicy := e.selectorPolicy.Consume(e, ipcache.IPIdentityCache.GetNamedPorts())
+	calculatedPolicy := e.selectorPolicy.Consume(e)
 
 	stats.policyCalculation.End(true)
 
diff --git a/pkg/endpoint/proxy.go b/pkg/endpoint/proxy.go
index 636f700b3c..f18d6fbbf8 100644
--- a/pkg/endpoint/proxy.go
+++ b/pkg/endpoint/proxy.go
@@ -27,7 +27,7 @@ import (
 type EndpointProxy interface {
 	CreateOrUpdateRedirect(l4 policy.ProxyPolicy, id string, localEndpoint logger.EndpointUpdater, wg *completion.WaitGroup) (proxyPort uint16, err error, finalizeFunc revert.FinalizeFunc, revertFunc revert.RevertFunc)
 	RemoveRedirect(id string, wg *completion.WaitGroup) (error, revert.FinalizeFunc, revert.RevertFunc)
-	UpdateNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy, npMap policy.NamedPortsMap, ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup) (error, func() error)
+	UpdateNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy, ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup) (error, func() error)
 	UseCurrentNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy, wg *completion.WaitGroup)
 	RemoveNetworkPolicy(ep logger.EndpointInfoSource)
 }
@@ -64,7 +64,7 @@ func (f *FakeEndpointProxy) RemoveRedirect(id string, wg *completion.WaitGroup)
 }
 
 // UpdateNetworkPolicy does nothing.
-func (f *FakeEndpointProxy) UpdateNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy, npMap policy.NamedPortsMap, ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup) (error, func() error) {
+func (f *FakeEndpointProxy) UpdateNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy, ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup) (error, func() error) {
 	return nil, nil
 }
 
diff --git a/pkg/endpoint/regeneration/owner.go b/pkg/endpoint/regeneration/owner.go
index 561461aad4..5d7dbcac41 100644
--- a/pkg/endpoint/regeneration/owner.go
+++ b/pkg/endpoint/regeneration/owner.go
@@ -60,7 +60,6 @@ type EndpointInfoSource interface {
 	HasSidecarProxy() bool
 	ConntrackName() string
 	ConntrackNameLocked() string
-	ProxyID(npMap policy.NamedPortsMap, l4 *policy.L4Filter) (string, error)
 	GetProxyInfoByFields() (uint64, string, string, []string, string, uint64, error)
 }
 
diff --git a/pkg/envoy/server.go b/pkg/envoy/server.go
index 96fce010c9..598d57e5f0 100644
--- a/pkg/envoy/server.go
+++ b/pkg/envoy/server.go
@@ -28,9 +28,7 @@ import (
 	"github.com/cilium/cilium/pkg/bpf"
 	"github.com/cilium/cilium/pkg/completion"
 	"github.com/cilium/cilium/pkg/envoy/xds"
-	"github.com/cilium/cilium/pkg/identity"
 	"github.com/cilium/cilium/pkg/lock"
-	"github.com/cilium/cilium/pkg/logging/logfields"
 	"github.com/cilium/cilium/pkg/option"
 	"github.com/cilium/cilium/pkg/policy"
 	"github.com/cilium/cilium/pkg/policy/api"
@@ -45,6 +43,8 @@ import (
 	envoy_config_listener "github.com/cilium/proxy/go/envoy/config/listener/v3"
 	envoy_config_route "github.com/cilium/proxy/go/envoy/config/route/v3"
 	envoy_config_http "github.com/cilium/proxy/go/envoy/extensions/filters/network/http_connection_manager/v3"
+	envoy_mongo_proxy "github.com/cilium/proxy/go/envoy/extensions/filters/network/mongo_proxy/v3"
+	envoy_mysql_proxy "github.com/cilium/proxy/go/envoy/extensions/filters/network/mysql_proxy/v3"
 	envoy_config_tcp "github.com/cilium/proxy/go/envoy/extensions/filters/network/tcp_proxy/v3"
 	envoy_type_matcher "github.com/cilium/proxy/go/envoy/type/matcher/v3"
 
@@ -286,31 +286,78 @@ func (s *XDSServer) getHttpFilterChainProto(clusterName string, tls bool) *envoy
 	return chain
 }
 
-func (s *XDSServer) getTcpFilterChainProto(clusterName string) *envoy_config_listener.FilterChain {
-	return &envoy_config_listener.FilterChain{
-		Filters: []*envoy_config_listener.Filter{{
-			Name: "cilium.network",
-			ConfigType: &envoy_config_listener.Filter_TypedConfig{
-				TypedConfig: toAny(&cilium.NetworkFilter{
-					Proxylib: "libcilium.so",
-					ProxylibParams: map[string]string{
-						"access-log-path": s.accessLogPath,
-						"xds-path":        s.socketPath,
-					},
-				}),
-			},
-		}, {
-			Name: "envoy.tcp_proxy",
-			ConfigType: &envoy_config_listener.Filter_TypedConfig{
-				TypedConfig: toAny(&envoy_config_tcp.TcpProxy{
-					StatPrefix: "tcp_proxy",
-					ClusterSpecifier: &envoy_config_tcp.TcpProxy_Cluster{
-						Cluster: clusterName,
-					},
-				}),
+// getTcpFilterChainProto creates a TCP filter chain with the Cilium network filter.
+// By default, the returned chain can be used with the Cilium Go extensions L7 parsers
+// in 'proxylib' directory in the Cilium repo.
+// When optional 'filterName' is given, it is configured as the first filter in the chain
+// and 'proxylib' is not configured. In this case the returned filter chain is only used
+// if the applicable network policy specifies 'filterName' as the L7 parser.
+func (s *XDSServer) getTcpFilterChainProto(clusterName string, filterName string, config *any.Any) *envoy_config_listener.FilterChain {
+	var filters []*envoy_config_listener.Filter
+
+	// 1. Add the filter 'filterName' to the beginning of the TCP chain with optional 'config', if needed.
+	if filterName != "" {
+		filter := &envoy_config_listener.Filter{Name: filterName}
+		if config != nil {
+			filter.ConfigType = &envoy_config_listener.Filter_TypedConfig{
+				TypedConfig: config,
+			}
+		}
+		filters = append(filters, filter)
+	}
+
+	// 2. Add Cilium Network filter.
+	var ciliumConfig *cilium.NetworkFilter
+	if filterName == "" {
+		// Use proxylib by default
+		ciliumConfig = &cilium.NetworkFilter{
+			Proxylib: "libcilium.so",
+			ProxylibParams: map[string]string{
+				"access-log-path": s.accessLogPath,
+				"xds-path":        s.socketPath,
 			},
-		}},
+		}
+	} else {
+		// Envoy metadata logging requires accesslog path
+		ciliumConfig = &cilium.NetworkFilter{
+			AccessLogPath: s.accessLogPath,
+		}
+	}
+	filters = append(filters, &envoy_config_listener.Filter{
+		Name: "cilium.network",
+		ConfigType: &envoy_config_listener.Filter_TypedConfig{
+			TypedConfig: toAny(ciliumConfig),
+		},
+	})
+
+	// 3. Add the TCP proxy filter.
+	filters = append(filters, &envoy_config_listener.Filter{
+		Name: "envoy.tcp_proxy",
+		ConfigType: &envoy_config_listener.Filter_TypedConfig{
+			TypedConfig: toAny(&envoy_config_tcp.TcpProxy{
+				StatPrefix: "tcp_proxy",
+				ClusterSpecifier: &envoy_config_tcp.TcpProxy_Cluster{
+					Cluster: clusterName,
+				},
+			}),
+		},
+	})
+
+	chain := &envoy_config_listener.FilterChain{
+		Filters: filters,
+		FilterChainMatch: &envoy_config_listener.FilterChainMatch{
+			// must have transport match, otherwise TLS inspector will be automatically inserted
+			TransportProtocol: "raw_buffer",
+		},
+	}
+
+	if filterName != "" {
+		// Add filter chain match for 'filterName' so that connections for which policy says to use this L7
+		// are handled by this filter chain.
+		chain.FilterChainMatch.ApplicationProtocols = []string{filterName}
 	}
+
+	return chain
 }
 
 // AddListener adds a listener to a running Envoy proxy.
@@ -380,13 +427,16 @@ func (s *XDSServer) AddListener(name string, kind policy.L7ParserType, port uint
 					BpfRoot:                     bpf.GetMapRoot(),
 				}),
 			},
-		}, {
-			Name: "envoy.listener.tls_inspector",
 		}},
 	}
 
 	// Add filter chains
 	if kind == policy.ParserTypeHTTP {
+		// Use tls_inspector only with HTTP, insert as the first filter
+		listenerConf.ListenerFilters = append([]*envoy_config_listener.ListenerFilter{{
+			Name: "envoy.listener.tls_inspector",
+		}}, listenerConf.ListenerFilters...)
+
 		listenerConf.FilterChains = append(listenerConf.FilterChains, s.getHttpFilterChainProto(clusterName, false))
 
 		// Add a TLS variant
@@ -396,7 +446,21 @@ func (s *XDSServer) AddListener(name string, kind policy.L7ParserType, port uint
 		}
 		listenerConf.FilterChains = append(listenerConf.FilterChains, s.getHttpFilterChainProto(tlsClusterName, true))
 	} else {
-		listenerConf.FilterChains = append(listenerConf.FilterChains, s.getTcpFilterChainProto(clusterName))
+		// Default TCP chain, takes care of all parsers in proxylib
+		listenerConf.FilterChains = append(listenerConf.FilterChains, s.getTcpFilterChainProto(clusterName, "", nil))
+
+		// Experimental TCP chain for MySQL 5.x
+		listenerConf.FilterChains = append(listenerConf.FilterChains, s.getTcpFilterChainProto(clusterName,
+			"envoy.filters.network.mysql_proxy", toAny(&envoy_mysql_proxy.MySQLProxy{
+				StatPrefix: "mysql",
+			})))
+
+		// Experimental TCP chain for MongoDB
+		listenerConf.FilterChains = append(listenerConf.FilterChains, s.getTcpFilterChainProto(clusterName,
+			"envoy.mongo_proxy", toAny(&envoy_mongo_proxy.MongoProxy{
+				StatPrefix:          "mongo",
+				EmitDynamicMetadata: true,
+			})))
 	}
 
 	s.listenerMutator.Upsert(ListenerTypeURL, name, listenerConf, []string{"127.0.0.1"}, wg,
@@ -458,14 +522,90 @@ func (s *XDSServer) stop() {
 	os.Remove(s.socketPath)
 }
 
-func getL7Rule(l7 *api.PortRuleL7) *cilium.L7NetworkPolicyRule {
-	rule := &cilium.L7NetworkPolicyRule{Rule: make(map[string]string, len(*l7))}
-
-	for k, v := range *l7 {
-		rule.Rule[k] = v
+func getL7Rules(l7Rules []api.PortRuleL7, l7Proto string) *cilium.L7NetworkPolicyRules {
+	allowRules := make([]*cilium.L7NetworkPolicyRule, 0, len(l7Rules))
+	denyRules := make([]*cilium.L7NetworkPolicyRule, 0, len(l7Rules))
+	useEnvoyMetadataMatcher := false
+	if strings.HasPrefix(l7Proto, "envoy.") {
+		useEnvoyMetadataMatcher = true
+	}
+	for _, l7 := range l7Rules {
+		if useEnvoyMetadataMatcher {
+			envoyFilterName := l7Proto
+			rule := &cilium.L7NetworkPolicyRule{MetadataRule: make([]*envoy_type_matcher.MetadataMatcher, 0, len(l7))}
+			denyRule := false
+			for k, v := range l7 {
+				switch k {
+				case "action":
+					switch v {
+					case "deny":
+						denyRule = true
+					}
+				default:
+					// map key to path segments and value to value matcher
+					// For now only one path segment is allowed
+					segments := strings.Split(k, "/")
+					var path []*envoy_type_matcher.MetadataMatcher_PathSegment
+					for _, key := range segments {
+						path = append(path, &envoy_type_matcher.MetadataMatcher_PathSegment{
+							Segment: &envoy_type_matcher.MetadataMatcher_PathSegment_Key{Key: key},
+						})
+					}
+					var value *envoy_type_matcher.ValueMatcher
+					if len(v) == 0 {
+						value = &envoy_type_matcher.ValueMatcher{
+							MatchPattern: &envoy_type_matcher.ValueMatcher_PresentMatch{
+								PresentMatch: true,
+							}}
+					} else {
+						value = &envoy_type_matcher.ValueMatcher{
+							MatchPattern: &envoy_type_matcher.ValueMatcher_ListMatch{
+								ListMatch: &envoy_type_matcher.ListMatcher{
+									MatchPattern: &envoy_type_matcher.ListMatcher_OneOf{
+										OneOf: &envoy_type_matcher.ValueMatcher{
+											MatchPattern: &envoy_type_matcher.ValueMatcher_StringMatch{
+												StringMatch: &envoy_type_matcher.StringMatcher{
+													MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+														Exact: v,
+													},
+													IgnoreCase: false,
+												},
+											},
+										},
+									},
+								},
+							}}
+					}
+					rule.MetadataRule = append(rule.MetadataRule, &envoy_type_matcher.MetadataMatcher{
+						Filter: envoyFilterName,
+						Path:   path,
+						Value:  value,
+					})
+				}
+			}
+			if denyRule {
+				denyRules = append(denyRules, rule)
+			} else {
+				allowRules = append(allowRules, rule)
+			}
+		} else {
+			// proxylib go extension key/value policy
+			rule := &cilium.L7NetworkPolicyRule{Rule: make(map[string]string, len(l7))}
+			for k, v := range l7 {
+				rule.Rule[k] = v
+			}
+			allowRules = append(allowRules, rule)
+		}
 	}
 
-	return rule
+	rules := &cilium.L7NetworkPolicyRules{}
+	if len(allowRules) > 0 {
+		rules.L7AllowRules = allowRules
+	}
+	if len(denyRules) > 0 {
+		rules.L7DenyRules = denyRules
+	}
+	return rules
 }
 
 func getKafkaL7Rules(l7Rules []kafka.PortRule) *cilium.KafkaNetworkPolicyRules {
@@ -847,16 +987,10 @@ func getPortNetworkPolicyRule(sel policy.CachedSelector, l7Parser policy.L7Parse
 	default:
 		// Assume unknown parser types use a Key-Value Pair policy
 		if len(l7Rules.L7) > 0 {
-			kvpRules := make([]*cilium.L7NetworkPolicyRule, 0, len(l7Rules.L7))
-			for _, l7 := range l7Rules.L7 {
-				kvpRules = append(kvpRules, getL7Rule(&l7))
-			}
 			// L7 rules are not sorted
 			r.L7Proto = l7Parser.String()
 			r.L7 = &cilium.PortNetworkPolicyRule_L7Rules{
-				L7Rules: &cilium.L7NetworkPolicyRules{
-					L7AllowRules: kvpRules,
-				},
+				L7Rules: getL7Rules(l7Rules.L7, r.L7Proto),
 			}
 		}
 	}
@@ -906,7 +1040,7 @@ func getWildcardNetworkPolicyRule(selectors policy.L7DataMap) *cilium.PortNetwor
 	}
 }
 
-func getDirectionNetworkPolicy(l4Policy policy.L4PolicyMap, npMap policy.NamedPortsMap, policyEnforced bool) []*cilium.PortNetworkPolicy {
+func getDirectionNetworkPolicy(ep logger.EndpointUpdater, l4Policy policy.L4PolicyMap, policyEnforced bool) []*cilium.PortNetworkPolicy {
 	if !policyEnforced {
 		// Return an allow-all policy.
 		return allowAllPortNetworkPolicy
@@ -930,10 +1064,8 @@ func getDirectionNetworkPolicy(l4Policy policy.L4PolicyMap, npMap policy.NamedPo
 
 		port := uint16(l4.Port)
 		if port == 0 && l4.PortName != "" {
-			var err error
-			port, err = npMap.GetNamedPort(l4.PortName, uint8(l4.U8Proto))
-			if err != nil {
-				log.WithError(err).WithField(logfields.PortName, l4.PortName).Debug("getDirectionNetworkPolicy: Skipping named port")
+			port = ep.GetNamedPortLocked(l4.Ingress, l4.PortName, uint8(l4.U8Proto))
+			if port == 0 {
 				continue
 			}
 		}
@@ -1016,18 +1148,18 @@ func getDirectionNetworkPolicy(l4Policy policy.L4PolicyMap, npMap policy.NamedPo
 }
 
 // getNetworkPolicy converts a network policy into a cilium.NetworkPolicy.
-func getNetworkPolicy(name string, id identity.NumericIdentity, conntrackName string, policy *policy.L4Policy,
-	npMap policy.NamedPortsMap, ingressPolicyEnforced, egressPolicyEnforced bool) *cilium.NetworkPolicy {
+func getNetworkPolicy(ep logger.EndpointUpdater, name string, policy *policy.L4Policy,
+	ingressPolicyEnforced, egressPolicyEnforced bool) *cilium.NetworkPolicy {
 	p := &cilium.NetworkPolicy{
 		Name:             name,
-		Policy:           uint64(id),
-		ConntrackMapName: conntrackName,
+		Policy:           uint64(ep.GetIdentityLocked()),
+		ConntrackMapName: ep.ConntrackNameLocked(),
 	}
 
 	// If no policy, deny all traffic. Otherwise, convert the policies for ingress and egress.
 	if policy != nil {
-		p.IngressPerPortPolicies = getDirectionNetworkPolicy(policy.Ingress, npMap, ingressPolicyEnforced)
-		p.EgressPerPortPolicies = getDirectionNetworkPolicy(policy.Egress, npMap, egressPolicyEnforced)
+		p.IngressPerPortPolicies = getDirectionNetworkPolicy(ep, policy.Ingress, ingressPolicyEnforced)
+		p.EgressPerPortPolicies = getDirectionNetworkPolicy(ep, policy.Egress, egressPolicyEnforced)
 	}
 
 	return p
@@ -1066,7 +1198,7 @@ func getNodeIDs(ep logger.EndpointUpdater, policy *policy.L4Policy) []string {
 // to L7 proxies.
 // When the proxy acknowledges the network policy update, it will result in
 // a subsequent call to the endpoint's OnProxyPolicyUpdate() function.
-func (s *XDSServer) UpdateNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy, npMap policy.NamedPortsMap,
+func (s *XDSServer) UpdateNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy,
 	ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup) (error, func() error) {
 	s.mutex.Lock()
 	defer s.mutex.Unlock()
@@ -1081,8 +1213,7 @@ func (s *XDSServer) UpdateNetworkPolicy(ep logger.EndpointUpdater, policy *polic
 		if ip == "" {
 			continue
 		}
-		networkPolicy := getNetworkPolicy(ip, ep.GetIdentityLocked(), ep.ConntrackNameLocked(), policy, npMap,
-			ingressPolicyEnforced, egressPolicyEnforced)
+		networkPolicy := getNetworkPolicy(ep, ip, policy, ingressPolicyEnforced, egressPolicyEnforced)
 		err := networkPolicy.Validate()
 		if err != nil {
 			return fmt.Errorf("error validating generated NetworkPolicy for %s: %s", ip, err), nil
diff --git a/pkg/ipcache/ipcache.go b/pkg/ipcache/ipcache.go
index 396124d470..73c68542cc 100644
--- a/pkg/ipcache/ipcache.go
+++ b/pkg/ipcache/ipcache.go
@@ -23,7 +23,6 @@ import (
 	"github.com/cilium/cilium/pkg/option"
 	"github.com/cilium/cilium/pkg/policy"
 	"github.com/cilium/cilium/pkg/source"
-	"github.com/cilium/cilium/pkg/u8proto"
 	"github.com/sirupsen/logrus"
 )
 
@@ -62,7 +61,7 @@ type K8sMetadata struct {
 	// PodName is the Kubernetes pod name behind the IP
 	PodName string
 	// NamedPorts is the set of named ports for the pod
-	NamedPorts policy.NamedPortsMap
+	NamedPorts policy.NamedPortMap
 }
 
 // IPCache is a collection of mappings:
@@ -83,7 +82,18 @@ type IPCache struct {
 
 	listeners []IPIdentityMappingListener
 
-	namedPorts policy.NamedPortsMap
+	// needNamedPorts is initially 'false', but will be changd to 'true' when the
+	// clusterwide named port mappings are needed for network policy computation
+	// for the first time. This avoids the overhead of maintaining 'namedPorts' map
+	// when it is known not to be needed.
+	// Protected by 'mutex'.
+	needNamedPorts bool
+
+	// namedPorts is a collection of all named ports in the cluster. This is needed
+	// only if an egress policy refers to a port by name.
+	// This map is returned to users so all updates must be made into a fresh map that
+	// is then swapped in place while 'mutex' is being held.
+	namedPorts policy.NamedPortMultiMap
 }
 
 // NewIPCache returns a new IPCache with the mappings of endpoint IP to security
@@ -177,49 +187,22 @@ func (ipc *IPCache) getK8sMetadata(ip string) *K8sMetadata {
 	return nil
 }
 
-func (ipc *IPCache) updateNamedPorts(newIP string, newNamedPorts policy.NamedPortsMap) (namedPortsChanged bool) {
+// updateNamedPorts accumulates named ports from all K8sMetadata entries to a single map
+func (ipc *IPCache) updateNamedPorts() (namedPortsChanged bool) {
+	if !ipc.needNamedPorts {
+		return false
+	}
 	// Collect new named Ports
-	npm := make(policy.NamedPortsMap, len(ipc.namedPorts))
-	for ip, km := range ipc.ipToK8sMetadata {
-		for name, value := range km.NamedPorts {
-			if ip != newIP {
-				// Check if there is a conflict between any old named port from other IPs
-				// with any of the newly added (or changed) values
-				if newValue, exists := newNamedPorts[name]; exists && newValue != value {
-					scopedLog := log.WithFields(logrus.Fields{
-						logfields.IPAddr:   newIP,
-						logfields.Identity: ipc.ipToIdentityCache[newIP],
-					})
-					newMeta := ipc.ipToK8sMetadata[newIP]
-					scopedLog = scopedLog.WithFields(logrus.Fields{
-						logfields.K8sPodName:   newMeta.PodName,
-						logfields.K8sNamespace: newMeta.Namespace,
-						logfields.NamedPorts:   newMeta.NamedPorts,
-					})
-					oldMeta := ipc.ipToK8sMetadata[ip]
-					scopedLog = scopedLog.WithFields(logrus.Fields{
-						"other-" + logfields.K8sPodName:   oldMeta.PodName,
-						"other-" + logfields.K8sNamespace: oldMeta.Namespace,
-						"other-" + logfields.NamedPorts:   oldMeta.NamedPorts,
-					})
-					scopedLog.Warningf("Conflicting named port definition: %s: %d/%s != %d/%s", name,
-						newValue.Port, u8proto.U8proto(newValue.Proto).String(),
-						value.Port, u8proto.U8proto(value.Proto).String())
-				}
-			}
-			if _, done := npm[name]; done {
-				// Do not process duplicate values
-				continue
+	npm := make(policy.NamedPortMultiMap, len(ipc.namedPorts))
+	for _, km := range ipc.ipToK8sMetadata {
+		for name, port := range km.NamedPorts {
+			if npm[name] == nil {
+				npm[name] = make(policy.PortProtoSet)
 			}
-			if oldValue, ok := ipc.namedPorts[name]; !ok || oldValue != value {
-				namedPortsChanged = true
-			}
-			npm[name] = value
+			npm[name][port] = struct{}{}
 		}
 	}
-	if len(npm) != len(ipc.namedPorts) {
-		namedPortsChanged = true
-	}
+	namedPortsChanged = !npm.Equal(ipc.namedPorts)
 	if namedPortsChanged {
 		// swap the new map in
 		if len(npm) == 0 {
@@ -241,7 +224,7 @@ func (ipc *IPCache) updateNamedPorts(newIP string, newNamedPorts policy.NamedPor
 // nil) and is propagated to the listeners. k8sMeta contains Kubernetes-specific
 // metadata such as pod namespace and pod name belonging to the IP (may be nil).
 func (ipc *IPCache) Upsert(ip string, hostIP net.IP, hostKey uint8, k8sMeta *K8sMetadata, newIdentity Identity) (updated bool, namedPortsChanged bool) {
-	var newNamedPorts policy.NamedPortsMap
+	var newNamedPorts policy.NamedPortMap
 	if k8sMeta != nil {
 		newNamedPorts = k8sMeta.NamedPorts
 	}
@@ -382,7 +365,7 @@ func (ipc *IPCache) Upsert(ip string, hostIP net.IP, hostKey uint8, k8sMeta *K8s
 		if namedPortsChanged {
 			// It is possible that some other POD defines same values, check if
 			// anything changes over all the PODs.
-			namedPortsChanged = ipc.updateNamedPorts(ip, newNamedPorts)
+			namedPortsChanged = ipc.updateNamedPorts()
 		}
 	}
 
@@ -490,7 +473,7 @@ func (ipc *IPCache) deleteLocked(ip string, source source.Source) (namedPortsCha
 	// Update named ports
 	namedPortsChanged = false
 	if oldK8sMeta != nil && len(oldK8sMeta.NamedPorts) > 0 {
-		namedPortsChanged = ipc.updateNamedPorts(ip, nil)
+		namedPortsChanged = ipc.updateNamedPorts()
 	}
 
 	if callbackListeners {
@@ -504,8 +487,12 @@ func (ipc *IPCache) deleteLocked(ip string, source source.Source) (namedPortsCha
 }
 
 // GetNamedPorts returns a copy of the named ports map. May return nil.
-func (ipc *IPCache) GetNamedPorts() (npm policy.NamedPortsMap) {
+func (ipc *IPCache) GetNamedPorts() (npm policy.NamedPortMultiMap) {
 	ipc.mutex.Lock()
+	if !ipc.needNamedPorts {
+		ipc.needNamedPorts = true
+		ipc.updateNamedPorts()
+	}
 	// Caller can keep using the map after the lock is released, as the map is never changed
 	// once published.
 	npm = ipc.namedPorts
diff --git a/pkg/ipcache/kvstore.go b/pkg/ipcache/kvstore.go
index d132426f3d..20670cc778 100644
--- a/pkg/ipcache/kvstore.go
+++ b/pkg/ipcache/kvstore.go
@@ -108,7 +108,7 @@ func newKVReferenceCounter(s store) *kvReferenceCounter {
 // UpsertIPToKVStore updates / inserts the provided IP->Identity mapping into the
 // kvstore, which will subsequently trigger an event in NewIPIdentityWatcher().
 func UpsertIPToKVStore(ctx context.Context, IP, hostIP net.IP, ID identity.NumericIdentity, key uint8,
-	metadata, k8sNamespace, k8sPodName string, npm policy.NamedPortsMap) error {
+	metadata, k8sNamespace, k8sPodName string, npm policy.NamedPortMap) error {
 	// Sort named ports into a slice
 	namedPorts := make([]identity.NamedPort, 0, len(npm))
 	for name, value := range npm {
@@ -291,7 +291,7 @@ restart:
 					k8sMeta = &K8sMetadata{
 						Namespace:  ipIDPair.K8sNamespace,
 						PodName:    ipIDPair.K8sPodName,
-						NamedPorts: make(policy.NamedPortsMap, len(ipIDPair.NamedPorts)),
+						NamedPorts: make(policy.NamedPortMap, len(ipIDPair.NamedPorts)),
 					}
 					for _, np := range ipIDPair.NamedPorts {
 						err = k8sMeta.NamedPorts.AddPort(np.Name, int(np.Port), np.Protocol)
diff --git a/pkg/k8s/watchers/cilium_endpoint.go b/pkg/k8s/watchers/cilium_endpoint.go
index 6877aea4fe..c66f1f54a8 100644
--- a/pkg/k8s/watchers/cilium_endpoint.go
+++ b/pkg/k8s/watchers/cilium_endpoint.go
@@ -141,16 +141,16 @@ func (k *K8sWatcher) endpointUpdated(endpoint *types.CiliumEndpoint) {
 		k8sMeta := &ipcache.K8sMetadata{
 			Namespace:  endpoint.Namespace,
 			PodName:    endpoint.Name,
-			NamedPorts: make(policy.NamedPortsMap, len(endpoint.NamedPorts)),
+			NamedPorts: make(policy.NamedPortMap, len(endpoint.NamedPorts)),
 		}
 		for _, port := range endpoint.NamedPorts {
 			p, err := u8proto.ParseProtocol(port.Protocol)
 			if err != nil {
 				continue
 			}
-			k8sMeta.NamedPorts[port.Name] = policy.NamedPort{
-				Proto: uint8(p),
+			k8sMeta.NamedPorts[port.Name] = policy.PortProto{
 				Port:  uint16(port.Port),
+				Proto: uint8(p),
 			}
 		}
 
diff --git a/pkg/k8s/watchers/pod.go b/pkg/k8s/watchers/pod.go
index 96bf369cc5..3a2865df95 100644
--- a/pkg/k8s/watchers/pod.go
+++ b/pkg/k8s/watchers/pod.go
@@ -608,11 +608,11 @@ func (k *K8sWatcher) updatePodHostData(pod *slim_corev1.Pod) (bool, error) {
 				return true, fmt.Errorf("ContainerPort: invalid port: %d", port.ContainerPort)
 			}
 			if k8sMeta.NamedPorts == nil {
-				k8sMeta.NamedPorts = make(policy.NamedPortsMap)
+				k8sMeta.NamedPorts = make(policy.NamedPortMap)
 			}
-			k8sMeta.NamedPorts[port.Name] = policy.NamedPort{
-				Proto: uint8(p),
+			k8sMeta.NamedPorts[port.Name] = policy.PortProto{
 				Port:  uint16(port.ContainerPort),
+				Proto: uint8(p),
 			}
 		}
 	}
diff --git a/pkg/kvstore/etcd.go b/pkg/kvstore/etcd.go
index d49853117a..50d5e2ff72 100644
--- a/pkg/kvstore/etcd.go
+++ b/pkg/kvstore/etcd.go
@@ -253,7 +253,7 @@ type etcdClient struct {
 	// is set up in the etcd client. If an error occurred and the initial
 	// session cannot be established, the error is provided via the
 	// channel.
-	firstSession chan error
+	firstSession chan struct{}
 
 	// stopStatusChecker is closed when the status checker can be terminated
 	stopStatusChecker chan struct{}
@@ -268,9 +268,10 @@ type etcdClient struct {
 	// statusCheckErrors receives all errors reported by statusChecker()
 	statusCheckErrors chan error
 
-	// protects sessions from concurrent access
+	// protects all sessions and sessionErr from concurrent access
 	lock.RWMutex
 
+	sessionErr    error
 	session       *concurrency.Session
 	sessionCancel context.CancelFunc
 
@@ -432,8 +433,8 @@ func (e *etcdClient) isConnectedAndHasQuorum(ctx context.Context) error {
 
 	select {
 	// Wait for the the initial connection to be established
-	case err := <-e.firstSession:
-		if err != nil {
+	case <-e.firstSession:
+		if err := e.sessionError(); err != nil {
 			return err
 		}
 	// Client is closing
@@ -685,7 +686,7 @@ func connectEtcdClient(ctx context.Context, config *client.Config, cfgPath strin
 		configPath:           cfgPath,
 		session:              &s,
 		lockSession:          &ls,
-		firstSession:         errChan,
+		firstSession:         make(chan struct{}),
 		controllers:          controller.NewManager(),
 		latestStatusSnapshot: "Waiting for initial connection to be established",
 		stopStatusChecker:    make(chan struct{}),
@@ -694,25 +695,33 @@ func connectEtcdClient(ctx context.Context, config *client.Config, cfgPath strin
 		statusCheckErrors:    make(chan error, 128),
 	}
 
+	handleSessionError := func(err error) {
+		ec.RWMutex.Lock()
+		ec.sessionErr = err
+		ec.RWMutex.Unlock()
+		errChan <- err
+	}
+
 	// wait for session to be created also in parallel
 	go func() {
 		defer close(errChan)
+		defer close(ec.firstSession)
 
 		select {
 		case err = <-errorChan:
 			if err != nil {
-				errChan <- err
+				handleSessionError(err)
 				return
 			}
 		case <-time.After(initialConnectionTimeout):
-			errChan <- fmt.Errorf("timed out while waiting for etcd session. Ensure that etcd is running on %s", config.Endpoints)
+			handleSessionError(fmt.Errorf("timed out while waiting for etcd session. Ensure that etcd is running on %s", config.Endpoints))
 			return
 		}
 
 		ec.getLogger().Info("Initial etcd session established")
 
 		if err := ec.checkMinVersion(ctx); err != nil {
-			errChan <- fmt.Errorf("unable to validate etcd version: %s", err)
+			handleSessionError(fmt.Errorf("unable to validate etcd version: %s", err))
 		}
 	}()
 
@@ -783,6 +792,13 @@ func getEPVersion(ctx context.Context, c client.Maintenance, etcdEP string, time
 	return v, nil
 }
 
+func (e *etcdClient) sessionError() (err error) {
+	e.RWMutex.RLock()
+	err = e.sessionErr
+	e.RWMutex.RUnlock()
+	return
+}
+
 // checkMinVersion checks the minimal version running on etcd cluster.  This
 // function should be run whenever the etcd client is connected for the first
 // time and whenever the session is renewed.
@@ -817,8 +833,8 @@ func (e *etcdClient) checkMinVersion(ctx context.Context) error {
 
 func (e *etcdClient) waitForInitialSession(ctx context.Context) error {
 	select {
-	case err := <-e.firstSession:
-		if err != nil {
+	case <-e.firstSession:
+		if err := e.sessionError(); err != nil {
 			return err
 		}
 	case <-ctx.Done():
diff --git a/pkg/logging/limiter.go b/pkg/logging/limiter.go
new file mode 100644
index 0000000000..c7578838b2
--- /dev/null
+++ b/pkg/logging/limiter.go
@@ -0,0 +1,47 @@
+// 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.
+
+package logging
+
+import (
+	"time"
+
+	"golang.org/x/time/rate"
+)
+
+// Limiter is a wrapper around rate.Limiter that does not panic when
+// the limiter is uninitialized. The wrapping also allows more logging
+// specific functionality to be added later without changing all the call
+// sites.
+type Limiter struct {
+	bucket *rate.Limiter
+}
+
+// NewLimiter returns a new Limiter allowing log messages to be
+// emitted on average once every 'interval' and upto 'burst' messages
+// during any 'interval'.
+func NewLimiter(interval time.Duration, burst int) Limiter {
+	return Limiter{
+		bucket: rate.NewLimiter(rate.Every(interval), burst),
+	}
+}
+
+// Allow returns true if the log message is allowed under the
+// configured rate limit.
+func (ll Limiter) Allow() bool {
+	if ll.bucket == nil {
+		return true // limiter not initialized => no limit
+	}
+	return ll.bucket.Allow()
+}
diff --git a/pkg/policy/distillery.go b/pkg/policy/distillery.go
index 558337c8e7..294afdd534 100644
--- a/pkg/policy/distillery.go
+++ b/pkg/policy/distillery.go
@@ -30,7 +30,7 @@ import (
 type SelectorPolicy interface {
 	// Consume returns the policy in terms of connectivity to peer
 	// Identities.
-	Consume(owner PolicyOwner, npMap NamedPortsMap) *EndpointPolicy
+	Consume(owner PolicyOwner) *EndpointPolicy
 }
 
 // PolicyCache represents a cache of resolved policies for identities.
@@ -205,10 +205,10 @@ func (cip *cachedSelectorPolicy) setPolicy(policy *selectorPolicy) {
 //
 // This denotes that a particular endpoint is 'consuming' the policy from the
 // selector policy cache.
-func (cip *cachedSelectorPolicy) Consume(owner PolicyOwner, npMap NamedPortsMap) *EndpointPolicy {
+func (cip *cachedSelectorPolicy) Consume(owner PolicyOwner) *EndpointPolicy {
 	// TODO: This currently computes the EndpointPolicy from SelectorPolicy
 	// on-demand, however in future the cip is intended to cache the
 	// EndpointPolicy for this Identity and emit datapath deltas instead.
 	isHost := cip.identity.ID == identityPkg.ReservedIdentityHost
-	return cip.getPolicy().DistillPolicy(owner, npMap, isHost)
+	return cip.getPolicy().DistillPolicy(owner, isHost)
 }
diff --git a/pkg/policy/l4.go b/pkg/policy/l4.go
index 008cae9332..ca8f8e8f78 100644
--- a/pkg/policy/l4.go
+++ b/pkg/policy/l4.go
@@ -1,4 +1,4 @@
-// Copyright 2016-2019 Authors of Cilium
+// Copyright 2016-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.
@@ -21,7 +21,6 @@ import (
 	"reflect"
 	"sort"
 	"strconv"
-	"strings"
 	"sync/atomic"
 	"unsafe"
 
@@ -244,71 +243,6 @@ func (l4 *L4Filter) GetPort() uint16 {
 	return uint16(l4.Port)
 }
 
-// NamedPort represents a mapping from a port name to the port number and protocol
-type NamedPort struct {
-	Port  uint16 // non-0
-	Proto uint8  // 0 for any
-}
-
-type NamedPortsMap map[string]NamedPort
-
-func ValidatePortName(name string) (string, error) {
-	if !iana.IsSvcName(name) { // Port names are formatted as IANA Service Names
-		return "", fmt.Errorf("Invalid port name \"%s\", not using as a named port", name)
-	}
-	return strings.ToLower(name), nil // Normalize for case-insensitive comparison
-}
-
-func ParsePortProtocol(port int, protocol string) (np NamedPort, err error) {
-	var u8p u8proto.U8proto
-	if protocol == "" {
-		u8p = u8proto.TCP // K8s ContainerPort protocol defaults to TCP
-	} else {
-		var err error
-		u8p, err = u8proto.ParseProtocol(protocol)
-		if err != nil {
-			return np, fmt.Errorf("Invalid protocol \"%s\": %s", protocol, err)
-		}
-	}
-	if port < 1 || port > 65535 {
-		return np, fmt.Errorf("Port number %d out of 16-bit range", port)
-	}
-	return NamedPort{
-		Proto: uint8(u8p),
-		Port:  uint16(port),
-	}, nil
-}
-
-func (npm NamedPortsMap) AddPort(name string, port int, protocol string) error {
-	name, err := ValidatePortName(name)
-	if err != nil {
-		return err
-	}
-	np, err := ParsePortProtocol(port, protocol)
-	if err != nil {
-		return err
-	}
-	npm[name] = np
-	return nil
-}
-
-func (npm NamedPortsMap) GetNamedPort(name string, proto uint8) (port uint16, err error) {
-	if npm == nil {
-		return 0, fmt.Errorf("nil map")
-	}
-	np, ok := npm[name]
-	if !ok {
-		return 0, fmt.Errorf("named port %s not found in %v", name, npm)
-	}
-	if np.Proto != 0 && proto != np.Proto {
-		return 0, fmt.Errorf("incompatible proto")
-	}
-	if np.Port == 0 {
-		return 0, fmt.Errorf("named port has zero value")
-	}
-	return np.Port, nil
-}
-
 // ToMapState converts filter into a MapState with two possible values:
 // - Entry with ProxyPort = 0: No proxy redirection is needed for this key
 // - Entry with any other port #: Proxy redirection is required for this key,
@@ -317,7 +251,8 @@ func (npm NamedPortsMap) GetNamedPort(name string, proto uint8) (port uint16, er
 // Note: It is possible for two selectors to select the same security ID.
 // To give priority for L7 redirection (e.g., for visibility purposes), we use
 // RedirectPreferredInsert() instead of directly inserting the value to the map.
-func (l4 *L4Filter) ToMapState(npMap NamedPortsMap, direction trafficdirection.TrafficDirection) MapState {
+// PolicyOwner (aka Endpoint) is locked during this call.
+func (l4 *L4Filter) ToMapState(policyOwner PolicyOwner, direction trafficdirection.TrafficDirection) MapState {
 	port := uint16(l4.Port)
 	proto := uint8(l4.U8Proto)
 
@@ -335,10 +270,8 @@ func (l4 *L4Filter) ToMapState(npMap NamedPortsMap, direction trafficdirection.T
 
 	// resolve named port
 	if port == 0 && l4.PortName != "" {
-		var err error
-		port, err = npMap.GetNamedPort(l4.PortName, proto)
-		if err != nil {
-			logger.Debugf("ToMapState: Skipping named port: %s", err)
+		port = policyOwner.GetNamedPortLocked(l4.Ingress, l4.PortName, proto)
+		if port == 0 {
 			return keysToAdd
 		}
 	}
@@ -838,6 +771,8 @@ type L4Policy struct {
 
 	// Endpoint policies using this L4Policy
 	// These are circular references, cleaned up in Detach()
+	// This mutex is taken while Endpoint mutex is held, so Endpoint lock
+	// MUST always be taken before this mutex.
 	mutex lock.RWMutex
 	users map[*EndpointPolicy]struct{}
 }
@@ -884,30 +819,26 @@ func (l4 *L4Policy) AccumulateMapChanges(adds, deletes []identity.NumericIdentit
 	proto := uint8(l4Filter.U8Proto)
 	derivedFrom := l4Filter.DerivedFromRules
 
+	// Must take a copy of 'users' as GetNamedPort() will lock the Endpoint below and
+	// the Endpoint lock may not be taken while 'l4.mutex' is held.
 	l4.mutex.RLock()
-	for epPolicy := range l4.users {
+	users := make(map[*EndpointPolicy]struct{}, len(l4.users))
+	for user := range l4.users {
+		users[user] = struct{}{}
+	}
+	l4.mutex.RUnlock()
+
+	for epPolicy := range users {
 		// resolve named port
 		if port == 0 && l4Filter.PortName != "" {
-			var err error
-			port, err = epPolicy.NamedPortsMap.GetNamedPort(l4Filter.PortName, proto)
-			if err != nil {
-				if option.Config.Debug {
-					logger := log.WithFields(logrus.Fields{
-						logfields.Port:             port,
-						logfields.PortName:         l4Filter.PortName,
-						logfields.Protocol:         proto,
-						logfields.TrafficDirection: direction,
-						logfields.EndpointID:       epPolicy.PolicyOwner.GetID(),
-					})
-					logger.WithError(err).Debug("AccumulateMapChanges: Skipping named port")
-				}
+			port = epPolicy.PolicyOwner.GetNamedPort(direction == trafficdirection.Ingress, l4Filter.PortName, proto)
+			if port == 0 {
 				continue
 			}
 		}
 
 		epPolicy.policyMapChanges.AccumulateMapChanges(adds, deletes, port, proto, direction, redirect, derivedFrom)
 	}
-	l4.mutex.RUnlock()
 }
 
 // Detach makes the L4Policy ready for garbage collection, removing
diff --git a/pkg/policy/portmap.go b/pkg/policy/portmap.go
new file mode 100644
index 0000000000..d196c575d7
--- /dev/null
+++ b/pkg/policy/portmap.go
@@ -0,0 +1,174 @@
+// 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.
+
+package policy
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+
+	"github.com/cilium/cilium/pkg/iana"
+	"github.com/cilium/cilium/pkg/u8proto"
+)
+
+var (
+	ErrNilMap               = errors.New("nil map")
+	ErrUnknownNamedPort     = errors.New("unknown named port")
+	ErrIncompatibleProtocol = errors.New("incompatible protocol")
+	ErrNamedPortIsZero      = errors.New("named port is zero")
+	ErrDuplicateNamedPorts  = errors.New("duplicate named ports")
+)
+
+// PortProto is a pair of port number and protocol and is used as the
+// value type in named port maps.
+type PortProto struct {
+	Port  uint16 // non-0
+	Proto uint8  // 0 for any
+}
+
+// NamedPortMap maps port names to port numbers and protocols.
+type NamedPortMap map[string]PortProto
+
+// PortProtoSet is a set of unique PortProto values.
+type PortProtoSet map[PortProto]struct{}
+
+// Equal returns true if the PortProtoSets are equal.
+func (pps PortProtoSet) Equal(other PortProtoSet) bool {
+	if len(pps) != len(other) {
+		return false
+	}
+
+	for port := range pps {
+		if _, exists := other[port]; !exists {
+			return false
+		}
+	}
+	return true
+}
+
+// NamedPortMultiMap may have multiple entries for a name if multiple PODs
+// define the same name with different values.
+type NamedPortMultiMap map[string]PortProtoSet
+
+// Equal returns true if the NamedPortMultiMaps are equal.
+func (npm NamedPortMultiMap) Equal(other NamedPortMultiMap) bool {
+	if len(npm) != len(other) {
+		return false
+	}
+	for name, ports := range npm {
+		if otherPorts, exists := other[name]; !exists || !ports.Equal(otherPorts) {
+			return false
+		}
+	}
+	return true
+}
+
+// ValidatePortName checks that the port name conforms to the IANA Service Names spec
+// and converts the port name to lower case for case-insensitive comparisons.
+func ValidatePortName(name string) (string, error) {
+	if !iana.IsSvcName(name) { // Port names are formatted as IANA Service Names
+		return "", fmt.Errorf("Invalid port name \"%s\", not using as a named port", name)
+	}
+	return strings.ToLower(name), nil // Normalize for case-insensitive comparison
+}
+
+func newPortProto(port int, protocol string) (pp PortProto, err error) {
+	var u8p u8proto.U8proto
+	if protocol == "" {
+		u8p = u8proto.TCP // K8s ContainerPort protocol defaults to TCP
+	} else {
+		var err error
+		u8p, err = u8proto.ParseProtocol(protocol)
+		if err != nil {
+			return pp, err
+		}
+	}
+	if port < 1 || port > 65535 {
+		if port == 0 {
+			return pp, ErrNamedPortIsZero
+		}
+		return pp, fmt.Errorf("Port number %d out of 16-bit range", port)
+	}
+	return PortProto{
+		Proto: uint8(u8p),
+		Port:  uint16(port),
+	}, nil
+}
+
+// AddPort adds a new PortProto to the NamedPortMap
+func (npm NamedPortMap) AddPort(name string, port int, protocol string) error {
+	name, err := ValidatePortName(name)
+	if err != nil {
+		return err
+	}
+	pp, err := newPortProto(port, protocol)
+	if err != nil {
+		return err
+	}
+	npm[name] = pp
+	return nil
+}
+
+// GetNamedPort returns the port number for the named port, if any.
+func (npm NamedPortMap) GetNamedPort(name string, proto uint8) (uint16, error) {
+	if npm == nil {
+		return 0, ErrNilMap
+	}
+	pp, ok := npm[name]
+	if !ok {
+		return 0, ErrUnknownNamedPort
+	}
+	if pp.Proto != 0 && proto != pp.Proto {
+		return 0, ErrIncompatibleProtocol
+	}
+	if pp.Port == 0 {
+		return 0, ErrNamedPortIsZero
+	}
+	return pp.Port, nil
+}
+
+// GetNamedPort returns the port number for the named port, if any.
+func (npm NamedPortMultiMap) GetNamedPort(name string, proto uint8) (uint16, error) {
+	if npm == nil {
+		return 0, ErrNilMap
+	}
+	pps, ok := npm[name]
+	if !ok {
+		// Return an error the caller can filter out as this happens only for egress policy
+		// and it is likely the destination POD with the port name is simply not scheduled yet.
+		return 0, ErrUnknownNamedPort
+	}
+	// Find if there is a single port that has no proto conflict and no zero port value
+	port := uint16(0)
+	err := ErrUnknownNamedPort
+	for pp := range pps {
+		if pp.Proto != 0 && proto != pp.Proto {
+			err = ErrIncompatibleProtocol
+			continue // conflicting proto
+		}
+		if pp.Port == 0 {
+			err = ErrNamedPortIsZero
+			continue // zero port
+		}
+		if port != 0 && pp.Port != port {
+			return 0, ErrDuplicateNamedPorts
+		}
+		port = pp.Port
+	}
+	if port == 0 {
+		return 0, err
+	}
+	return port, nil
+}
diff --git a/pkg/policy/proxyid.go b/pkg/policy/proxyid.go
index 8f4ec8ee99..b894f063bb 100644
--- a/pkg/policy/proxyid.go
+++ b/pkg/policy/proxyid.go
@@ -37,18 +37,6 @@ func ProxyIDFromKey(endpointID uint16, key Key) string {
 	return ProxyID(endpointID, key.TrafficDirection == trafficdirection.Ingress.Uint8(), u8proto.U8proto(key.Nexthdr).String(), key.DestPort)
 }
 
-// ProxyIDFromFilter returns a unique string to identify a proxy mapping.
-func ProxyIDFromFilter(endpointID uint16, npMap NamedPortsMap, l4 *L4Filter) (id string, err error) {
-	port := uint16(l4.Port)
-	if port == 0 && l4.PortName != "" {
-		port, err = npMap.GetNamedPort(l4.PortName, uint8(l4.U8Proto))
-		if err != nil {
-			return "", err
-		}
-	}
-	return ProxyID(endpointID, l4.Ingress, string(l4.Protocol), port), nil
-}
-
 // ParseProxyID parses a proxy ID returned by ProxyID and returns its components.
 func ParseProxyID(proxyID string) (endpointID uint16, ingress bool, protocol string, port uint16, err error) {
 	comps := strings.Split(proxyID, ":")
diff --git a/pkg/policy/resolve.go b/pkg/policy/resolve.go
index 93f1fd902f..f8196af297 100644
--- a/pkg/policy/resolve.go
+++ b/pkg/policy/resolve.go
@@ -57,10 +57,6 @@ type EndpointPolicy struct {
 	// referring to a shared selectorPolicy!
 	*selectorPolicy
 
-	// NamedPortsMap maps PortNames in L4Filters to port numbers. This mapping
-	// is endpoint specific. Can be nil!
-	NamedPortsMap NamedPortsMap
-
 	// PolicyMapState contains the state of this policy as it relates to the
 	// datapath. In the future, this will be factored out of this object to
 	// decouple the policy as it relates to the datapath vs. its userspace
@@ -80,7 +76,9 @@ type EndpointPolicy struct {
 // PolicyOwner is anything which consumes a EndpointPolicy.
 type PolicyOwner interface {
 	GetID() uint64
-	LookupRedirectPortLocked(npMap NamedPortsMap, l4 *L4Filter) uint16
+	LookupRedirectPortLocked(ingress bool, protocol string, port uint16) uint16
+	GetNamedPort(ingress bool, name string, proto uint8) uint16
+	GetNamedPortLocked(ingress bool, name string, proto uint8) uint16
 }
 
 // newSelectorPolicy returns an empty selectorPolicy stub.
@@ -114,10 +112,9 @@ func (p *selectorPolicy) Detach() {
 //
 // Must be performed while holding the Repository lock.
 // PolicyOwner (aka Endpoint) is also locked during this call.
-func (p *selectorPolicy) DistillPolicy(policyOwner PolicyOwner, npMap NamedPortsMap, isHost bool) *EndpointPolicy {
+func (p *selectorPolicy) DistillPolicy(policyOwner PolicyOwner, isHost bool) *EndpointPolicy {
 	calculatedPolicy := &EndpointPolicy{
 		selectorPolicy: p,
-		NamedPortsMap:  npMap,
 		PolicyMapState: make(MapState),
 		PolicyOwner:    policyOwner,
 	}
@@ -165,13 +162,15 @@ func (p *EndpointPolicy) computeDirectionL4PolicyMapEntries(l4PolicyMap L4Policy
 	for _, filter := range l4PolicyMap {
 		lookupDone := false
 		proxyport := uint16(0)
-		keysFromFilter := filter.ToMapState(p.NamedPortsMap, direction)
+		keysFromFilter := filter.ToMapState(p.PolicyOwner, direction)
 		for keyFromFilter, entry := range keysFromFilter {
 			// Fix up the proxy port for entries that need proxy redirection
 			if entry.IsRedirectEntry() {
 				if !lookupDone {
 					// only lookup once for each filter
-					proxyport = p.PolicyOwner.LookupRedirectPortLocked(p.NamedPortsMap, filter)
+					// Use 'destPort' from the key as it is already resolved
+					// from a named port if needed.
+					proxyport = p.PolicyOwner.LookupRedirectPortLocked(filter.Ingress, string(filter.Protocol), keyFromFilter.DestPort)
 					lookupDone = true
 				}
 				entry.ProxyPort = proxyport
diff --git a/pkg/proxy/logger/epinfo.go b/pkg/proxy/logger/epinfo.go
index a41d43aea5..41e1c94bd0 100644
--- a/pkg/proxy/logger/epinfo.go
+++ b/pkg/proxy/logger/epinfo.go
@@ -18,7 +18,6 @@ import (
 	"net"
 
 	"github.com/cilium/cilium/pkg/identity"
-	"github.com/cilium/cilium/pkg/policy"
 	"github.com/cilium/cilium/pkg/proxy/accesslog"
 )
 
@@ -39,7 +38,7 @@ type EndpointInfoSource interface {
 	// implementation.
 	ConntrackName() string
 	ConntrackNameLocked() string
-	ProxyID(npMap policy.NamedPortsMap, l4 *policy.L4Filter) (string, error)
+	GetNamedPortLocked(ingress bool, name string, proto uint8) uint16
 	GetProxyInfoByFields() (uint64, string, string, []string, string, uint64, error)
 }
 
diff --git a/pkg/proxy/proxy.go b/pkg/proxy/proxy.go
index 0205259854..0ad4bb7e5f 100644
--- a/pkg/proxy/proxy.go
+++ b/pkg/proxy/proxy.go
@@ -644,8 +644,8 @@ func (p *Proxy) updateRedirectMetrics() {
 }
 
 // UpdateNetworkPolicy must update the redirect configuration of an endpoint in the proxy
-func (p *Proxy) UpdateNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy, npMap policy.NamedPortsMap, ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup) (error, func() error) {
-	return p.XDSServer.UpdateNetworkPolicy(ep, policy, npMap, ingressPolicyEnforced, egressPolicyEnforced, wg)
+func (p *Proxy) UpdateNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy, ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup) (error, func() error) {
+	return p.XDSServer.UpdateNetworkPolicy(ep, policy, ingressPolicyEnforced, egressPolicyEnforced, wg)
 }
 
 // UseCurrentNetworkPolicy inserts a Completion to the WaitGroup if the current network policy has not yet been acked
diff --git a/proxylib/proxylib/policymap.go b/proxylib/proxylib/policymap.go
index 3c66bcd4a8..a2ac0de86a 100644
--- a/proxylib/proxylib/policymap.go
+++ b/proxylib/proxylib/policymap.go
@@ -17,6 +17,7 @@ package proxylib
 import (
 	"fmt"
 	"reflect"
+	"strings"
 
 	"github.com/cilium/proxy/go/cilium/api"
 	core "github.com/cilium/proxy/go/envoy/config/core/v3"
@@ -73,6 +74,9 @@ func newPortNetworkPolicyRule(config *cilium.PortNetworkPolicyRule) (PortNetwork
 			l7Name = typeOf.Elem().Name()
 		}
 	}
+	if strings.HasPrefix(l7Name, "envoy.") {
+		return rule, "", false // Silently drop Envoy filter traffic to this port if forwarded to proxylib
+	}
 	if l7Name != "" {
 		l7Parser, ok := l7RuleParsers[l7Name]
 		if ok {
@@ -81,7 +85,7 @@ func newPortNetworkPolicyRule(config *cilium.PortNetworkPolicyRule) (PortNetwork
 		} else {
 			log.Debugf("NPDS::PortNetworkPolicyRule: Unknown L7 (%s), should drop everything.", l7Name)
 		}
-		// Unknown parsers are expected, bur will result in drop-all policy
+		// Unknown parsers are expected, but will result in drop-all policy
 		return rule, l7Name, ok
 	}
 	return rule, "", true // No L7 is ok
diff --git a/vendor/github.com/cilium/proxy/go/envoy/extensions/filters/common/fault/v3/fault.pb.go b/vendor/github.com/cilium/proxy/go/envoy/extensions/filters/common/fault/v3/fault.pb.go
new file mode 100644
index 0000000000..98249e5f40
--- /dev/null
+++ b/vendor/github.com/cilium/proxy/go/envoy/extensions/filters/common/fault/v3/fault.pb.go
@@ -0,0 +1,394 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: envoy/extensions/filters/common/fault/v3/fault.proto
+
+package envoy_extensions_filters_common_fault_v3
+
+import (
+	fmt "fmt"
+	_ "github.com/cilium/proxy/go/envoy/annotations"
+	v3 "github.com/cilium/proxy/go/envoy/type/v3"
+	_ "github.com/cncf/udpa/go/udpa/annotations"
+	_ "github.com/envoyproxy/protoc-gen-validate/validate"
+	proto "github.com/golang/protobuf/proto"
+	duration "github.com/golang/protobuf/ptypes/duration"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type FaultDelay_FaultDelayType int32
+
+const (
+	// Unused and deprecated.
+	FaultDelay_FIXED FaultDelay_FaultDelayType = 0
+)
+
+var FaultDelay_FaultDelayType_name = map[int32]string{
+	0: "FIXED",
+}
+
+var FaultDelay_FaultDelayType_value = map[string]int32{
+	"FIXED": 0,
+}
+
+func (x FaultDelay_FaultDelayType) String() string {
+	return proto.EnumName(FaultDelay_FaultDelayType_name, int32(x))
+}
+
+func (FaultDelay_FaultDelayType) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_99c2ca93a0e6a448, []int{0, 0}
+}
+
+// Delay specification is used to inject latency into the
+// HTTP/gRPC/Mongo/Redis operation or delay proxying of TCP connections.
+// [#next-free-field: 6]
+type FaultDelay struct {
+	// Types that are valid to be assigned to FaultDelaySecifier:
+	//	*FaultDelay_FixedDelay
+	//	*FaultDelay_HeaderDelay_
+	FaultDelaySecifier isFaultDelay_FaultDelaySecifier `protobuf_oneof:"fault_delay_secifier"`
+	// The percentage of operations/connections/requests on which the delay will be injected.
+	Percentage           *v3.FractionalPercent `protobuf:"bytes,4,opt,name=percentage,proto3" json:"percentage,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}              `json:"-"`
+	XXX_unrecognized     []byte                `json:"-"`
+	XXX_sizecache        int32                 `json:"-"`
+}
+
+func (m *FaultDelay) Reset()         { *m = FaultDelay{} }
+func (m *FaultDelay) String() string { return proto.CompactTextString(m) }
+func (*FaultDelay) ProtoMessage()    {}
+func (*FaultDelay) Descriptor() ([]byte, []int) {
+	return fileDescriptor_99c2ca93a0e6a448, []int{0}
+}
+
+func (m *FaultDelay) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_FaultDelay.Unmarshal(m, b)
+}
+func (m *FaultDelay) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_FaultDelay.Marshal(b, m, deterministic)
+}
+func (m *FaultDelay) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_FaultDelay.Merge(m, src)
+}
+func (m *FaultDelay) XXX_Size() int {
+	return xxx_messageInfo_FaultDelay.Size(m)
+}
+func (m *FaultDelay) XXX_DiscardUnknown() {
+	xxx_messageInfo_FaultDelay.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_FaultDelay proto.InternalMessageInfo
+
+type isFaultDelay_FaultDelaySecifier interface {
+	isFaultDelay_FaultDelaySecifier()
+}
+
+type FaultDelay_FixedDelay struct {
+	FixedDelay *duration.Duration `protobuf:"bytes,3,opt,name=fixed_delay,json=fixedDelay,proto3,oneof"`
+}
+
+type FaultDelay_HeaderDelay_ struct {
+	HeaderDelay *FaultDelay_HeaderDelay `protobuf:"bytes,5,opt,name=header_delay,json=headerDelay,proto3,oneof"`
+}
+
+func (*FaultDelay_FixedDelay) isFaultDelay_FaultDelaySecifier() {}
+
+func (*FaultDelay_HeaderDelay_) isFaultDelay_FaultDelaySecifier() {}
+
+func (m *FaultDelay) GetFaultDelaySecifier() isFaultDelay_FaultDelaySecifier {
+	if m != nil {
+		return m.FaultDelaySecifier
+	}
+	return nil
+}
+
+func (m *FaultDelay) GetFixedDelay() *duration.Duration {
+	if x, ok := m.GetFaultDelaySecifier().(*FaultDelay_FixedDelay); ok {
+		return x.FixedDelay
+	}
+	return nil
+}
+
+func (m *FaultDelay) GetHeaderDelay() *FaultDelay_HeaderDelay {
+	if x, ok := m.GetFaultDelaySecifier().(*FaultDelay_HeaderDelay_); ok {
+		return x.HeaderDelay
+	}
+	return nil
+}
+
+func (m *FaultDelay) GetPercentage() *v3.FractionalPercent {
+	if m != nil {
+		return m.Percentage
+	}
+	return nil
+}
+
+// XXX_OneofWrappers is for the internal use of the proto package.
+func (*FaultDelay) XXX_OneofWrappers() []interface{} {
+	return []interface{}{
+		(*FaultDelay_FixedDelay)(nil),
+		(*FaultDelay_HeaderDelay_)(nil),
+	}
+}
+
+// Fault delays are controlled via an HTTP header (if applicable). See the
+// :ref:`HTTP fault filter <config_http_filters_fault_injection_http_header>` documentation for
+// more information.
+type FaultDelay_HeaderDelay struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *FaultDelay_HeaderDelay) Reset()         { *m = FaultDelay_HeaderDelay{} }
+func (m *FaultDelay_HeaderDelay) String() string { return proto.CompactTextString(m) }
+func (*FaultDelay_HeaderDelay) ProtoMessage()    {}
+func (*FaultDelay_HeaderDelay) Descriptor() ([]byte, []int) {
+	return fileDescriptor_99c2ca93a0e6a448, []int{0, 0}
+}
+
+func (m *FaultDelay_HeaderDelay) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_FaultDelay_HeaderDelay.Unmarshal(m, b)
+}
+func (m *FaultDelay_HeaderDelay) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_FaultDelay_HeaderDelay.Marshal(b, m, deterministic)
+}
+func (m *FaultDelay_HeaderDelay) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_FaultDelay_HeaderDelay.Merge(m, src)
+}
+func (m *FaultDelay_HeaderDelay) XXX_Size() int {
+	return xxx_messageInfo_FaultDelay_HeaderDelay.Size(m)
+}
+func (m *FaultDelay_HeaderDelay) XXX_DiscardUnknown() {
+	xxx_messageInfo_FaultDelay_HeaderDelay.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_FaultDelay_HeaderDelay proto.InternalMessageInfo
+
+// Describes a rate limit to be applied.
+type FaultRateLimit struct {
+	// Types that are valid to be assigned to LimitType:
+	//	*FaultRateLimit_FixedLimit_
+	//	*FaultRateLimit_HeaderLimit_
+	LimitType isFaultRateLimit_LimitType `protobuf_oneof:"limit_type"`
+	// The percentage of operations/connections/requests on which the rate limit will be injected.
+	Percentage           *v3.FractionalPercent `protobuf:"bytes,2,opt,name=percentage,proto3" json:"percentage,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}              `json:"-"`
+	XXX_unrecognized     []byte                `json:"-"`
+	XXX_sizecache        int32                 `json:"-"`
+}
+
+func (m *FaultRateLimit) Reset()         { *m = FaultRateLimit{} }
+func (m *FaultRateLimit) String() string { return proto.CompactTextString(m) }
+func (*FaultRateLimit) ProtoMessage()    {}
+func (*FaultRateLimit) Descriptor() ([]byte, []int) {
+	return fileDescriptor_99c2ca93a0e6a448, []int{1}
+}
+
+func (m *FaultRateLimit) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_FaultRateLimit.Unmarshal(m, b)
+}
+func (m *FaultRateLimit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_FaultRateLimit.Marshal(b, m, deterministic)
+}
+func (m *FaultRateLimit) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_FaultRateLimit.Merge(m, src)
+}
+func (m *FaultRateLimit) XXX_Size() int {
+	return xxx_messageInfo_FaultRateLimit.Size(m)
+}
+func (m *FaultRateLimit) XXX_DiscardUnknown() {
+	xxx_messageInfo_FaultRateLimit.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_FaultRateLimit proto.InternalMessageInfo
+
+type isFaultRateLimit_LimitType interface {
+	isFaultRateLimit_LimitType()
+}
+
+type FaultRateLimit_FixedLimit_ struct {
+	FixedLimit *FaultRateLimit_FixedLimit `protobuf:"bytes,1,opt,name=fixed_limit,json=fixedLimit,proto3,oneof"`
+}
+
+type FaultRateLimit_HeaderLimit_ struct {
+	HeaderLimit *FaultRateLimit_HeaderLimit `protobuf:"bytes,3,opt,name=header_limit,json=headerLimit,proto3,oneof"`
+}
+
+func (*FaultRateLimit_FixedLimit_) isFaultRateLimit_LimitType() {}
+
+func (*FaultRateLimit_HeaderLimit_) isFaultRateLimit_LimitType() {}
+
+func (m *FaultRateLimit) GetLimitType() isFaultRateLimit_LimitType {
+	if m != nil {
+		return m.LimitType
+	}
+	return nil
+}
+
+func (m *FaultRateLimit) GetFixedLimit() *FaultRateLimit_FixedLimit {
+	if x, ok := m.GetLimitType().(*FaultRateLimit_FixedLimit_); ok {
+		return x.FixedLimit
+	}
+	return nil
+}
+
+func (m *FaultRateLimit) GetHeaderLimit() *FaultRateLimit_HeaderLimit {
+	if x, ok := m.GetLimitType().(*FaultRateLimit_HeaderLimit_); ok {
+		return x.HeaderLimit
+	}
+	return nil
+}
+
+func (m *FaultRateLimit) GetPercentage() *v3.FractionalPercent {
+	if m != nil {
+		return m.Percentage
+	}
+	return nil
+}
+
+// XXX_OneofWrappers is for the internal use of the proto package.
+func (*FaultRateLimit) XXX_OneofWrappers() []interface{} {
+	return []interface{}{
+		(*FaultRateLimit_FixedLimit_)(nil),
+		(*FaultRateLimit_HeaderLimit_)(nil),
+	}
+}
+
+// Describes a fixed/constant rate limit.
+type FaultRateLimit_FixedLimit struct {
+	// The limit supplied in KiB/s.
+	LimitKbps            uint64   `protobuf:"varint,1,opt,name=limit_kbps,json=limitKbps,proto3" json:"limit_kbps,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *FaultRateLimit_FixedLimit) Reset()         { *m = FaultRateLimit_FixedLimit{} }
+func (m *FaultRateLimit_FixedLimit) String() string { return proto.CompactTextString(m) }
+func (*FaultRateLimit_FixedLimit) ProtoMessage()    {}
+func (*FaultRateLimit_FixedLimit) Descriptor() ([]byte, []int) {
+	return fileDescriptor_99c2ca93a0e6a448, []int{1, 0}
+}
+
+func (m *FaultRateLimit_FixedLimit) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_FaultRateLimit_FixedLimit.Unmarshal(m, b)
+}
+func (m *FaultRateLimit_FixedLimit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_FaultRateLimit_FixedLimit.Marshal(b, m, deterministic)
+}
+func (m *FaultRateLimit_FixedLimit) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_FaultRateLimit_FixedLimit.Merge(m, src)
+}
+func (m *FaultRateLimit_FixedLimit) XXX_Size() int {
+	return xxx_messageInfo_FaultRateLimit_FixedLimit.Size(m)
+}
+func (m *FaultRateLimit_FixedLimit) XXX_DiscardUnknown() {
+	xxx_messageInfo_FaultRateLimit_FixedLimit.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_FaultRateLimit_FixedLimit proto.InternalMessageInfo
+
+func (m *FaultRateLimit_FixedLimit) GetLimitKbps() uint64 {
+	if m != nil {
+		return m.LimitKbps
+	}
+	return 0
+}
+
+// Rate limits are controlled via an HTTP header (if applicable). See the
+// :ref:`HTTP fault filter <config_http_filters_fault_injection_http_header>` documentation for
+// more information.
+type FaultRateLimit_HeaderLimit struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *FaultRateLimit_HeaderLimit) Reset()         { *m = FaultRateLimit_HeaderLimit{} }
+func (m *FaultRateLimit_HeaderLimit) String() string { return proto.CompactTextString(m) }
+func (*FaultRateLimit_HeaderLimit) ProtoMessage()    {}
+func (*FaultRateLimit_HeaderLimit) Descriptor() ([]byte, []int) {
+	return fileDescriptor_99c2ca93a0e6a448, []int{1, 1}
+}
+
+func (m *FaultRateLimit_HeaderLimit) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_FaultRateLimit_HeaderLimit.Unmarshal(m, b)
+}
+func (m *FaultRateLimit_HeaderLimit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_FaultRateLimit_HeaderLimit.Marshal(b, m, deterministic)
+}
+func (m *FaultRateLimit_HeaderLimit) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_FaultRateLimit_HeaderLimit.Merge(m, src)
+}
+func (m *FaultRateLimit_HeaderLimit) XXX_Size() int {
+	return xxx_messageInfo_FaultRateLimit_HeaderLimit.Size(m)
+}
+func (m *FaultRateLimit_HeaderLimit) XXX_DiscardUnknown() {
+	xxx_messageInfo_FaultRateLimit_HeaderLimit.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_FaultRateLimit_HeaderLimit proto.InternalMessageInfo
+
+func init() {
+	proto.RegisterEnum("envoy.extensions.filters.common.fault.v3.FaultDelay_FaultDelayType", FaultDelay_FaultDelayType_name, FaultDelay_FaultDelayType_value)
+	proto.RegisterType((*FaultDelay)(nil), "envoy.extensions.filters.common.fault.v3.FaultDelay")
+	proto.RegisterType((*FaultDelay_HeaderDelay)(nil), "envoy.extensions.filters.common.fault.v3.FaultDelay.HeaderDelay")
+	proto.RegisterType((*FaultRateLimit)(nil), "envoy.extensions.filters.common.fault.v3.FaultRateLimit")
+	proto.RegisterType((*FaultRateLimit_FixedLimit)(nil), "envoy.extensions.filters.common.fault.v3.FaultRateLimit.FixedLimit")
+	proto.RegisterType((*FaultRateLimit_HeaderLimit)(nil), "envoy.extensions.filters.common.fault.v3.FaultRateLimit.HeaderLimit")
+}
+
+func init() {
+	proto.RegisterFile("envoy/extensions/filters/common/fault/v3/fault.proto", fileDescriptor_99c2ca93a0e6a448)
+}
+
+var fileDescriptor_99c2ca93a0e6a448 = []byte{
+	// 591 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0xcf, 0x8b, 0xd3, 0x40,
+	0x14, 0x6e, 0xd2, 0xec, 0xba, 0x3b, 0x15, 0xa9, 0x41, 0xb0, 0xb6, 0xb8, 0xac, 0x15, 0xb4, 0x2a,
+	0x4e, 0xa0, 0xd5, 0x55, 0x0b, 0xca, 0x12, 0x6b, 0xe9, 0xae, 0x3f, 0x28, 0xc1, 0x83, 0xb7, 0x32,
+	0x4d, 0x5e, 0xba, 0x83, 0x69, 0x26, 0x24, 0xd3, 0xd2, 0xde, 0x3c, 0xfa, 0x37, 0xf8, 0x27, 0x78,
+	0xf2, 0xec, 0x59, 0xc1, 0xab, 0xff, 0x8d, 0xec, 0x49, 0xe6, 0x47, 0xda, 0x2e, 0x2a, 0xb6, 0x7b,
+	0x9b, 0x37, 0xef, 0x7d, 0xdf, 0x7b, 0xef, 0xfb, 0x86, 0x41, 0x0f, 0x20, 0x9e, 0xb2, 0xb9, 0x03,
+	0x33, 0x0e, 0x71, 0x46, 0x59, 0x9c, 0x39, 0x21, 0x8d, 0x38, 0xa4, 0x99, 0xe3, 0xb3, 0xf1, 0x98,
+	0xc5, 0x4e, 0x48, 0x26, 0x11, 0x77, 0xa6, 0x2d, 0x75, 0xc0, 0x49, 0xca, 0x38, 0xb3, 0x1b, 0x12,
+	0x85, 0x97, 0x28, 0xac, 0x51, 0x58, 0xa1, 0xb0, 0x2a, 0x9e, 0xb6, 0xaa, 0x35, 0xc5, 0xcf, 0xe7,
+	0x09, 0x08, 0x92, 0x04, 0x52, 0x1f, 0x62, 0x4d, 0x53, 0xdd, 0x1b, 0x31, 0x36, 0x8a, 0xc0, 0x91,
+	0xd1, 0x70, 0x12, 0x3a, 0xc1, 0x24, 0x25, 0x9c, 0xb2, 0x58, 0xe7, 0x6f, 0x2a, 0x30, 0x89, 0x63,
+	0xc6, 0xe5, 0x7d, 0xe6, 0x04, 0x90, 0xa4, 0xe0, 0xaf, 0x16, 0x5d, 0x9f, 0x04, 0x09, 0x39, 0x53,
+	0x93, 0x71, 0xc2, 0x27, 0x99, 0x4e, 0xdf, 0xf8, 0x23, 0x3d, 0x85, 0x54, 0xcc, 0x4c, 0xe3, 0x91,
+	0x2e, 0xb9, 0x3a, 0x25, 0x11, 0x0d, 0x08, 0x07, 0x27, 0x3f, 0xa8, 0x44, 0xfd, 0x5b, 0x11, 0xa1,
+	0xae, 0xd8, 0xa4, 0x03, 0x11, 0x99, 0xdb, 0x5d, 0x54, 0x0a, 0xe9, 0x0c, 0x82, 0x41, 0x20, 0xc2,
+	0x4a, 0x71, 0xdf, 0x68, 0x94, 0x9a, 0xd7, 0xb0, 0x5a, 0x02, 0xe7, 0x4b, 0xe0, 0x8e, 0x5e, 0xc2,
+	0xdd, 0x39, 0x75, 0xb7, 0x3e, 0x1b, 0xe6, 0xdd, 0x42, 0xaf, 0xe0, 0x21, 0x89, 0x54, 0x3c, 0x80,
+	0x2e, 0x9e, 0x00, 0x09, 0x20, 0xd5, 0x44, 0x5b, 0x92, 0xe8, 0x10, 0xaf, 0x2b, 0x2a, 0x5e, 0xce,
+	0x84, 0x7b, 0x92, 0x48, 0x9e, 0x7b, 0x05, 0xaf, 0x74, 0xb2, 0x0c, 0xed, 0x43, 0x84, 0xb4, 0xdc,
+	0x64, 0x04, 0x15, 0x4b, 0x36, 0xd9, 0xd7, 0x4d, 0x84, 0x1f, 0x92, 0x29, 0x25, 0xbe, 0x98, 0x95,
+	0x44, 0x7d, 0x55, 0xea, 0xad, 0x60, 0xaa, 0x47, 0xa8, 0xb4, 0xc2, 0xdf, 0x6e, 0x7f, 0xfa, 0xfe,
+	0x71, 0xef, 0x21, 0x6a, 0x29, 0x0a, 0x9f, 0xc5, 0x21, 0x1d, 0xe9, 0x19, 0xf3, 0xd9, 0x9a, 0xff,
+	0x98, 0xad, 0x5e, 0x43, 0x97, 0x96, 0x99, 0xb7, 0xf3, 0x04, 0xec, 0x5d, 0xb4, 0xd5, 0x3d, 0x7a,
+	0xf7, 0xa2, 0x53, 0x2e, 0xb4, 0xb1, 0x20, 0xbe, 0x83, 0x6e, 0xaf, 0x49, 0xec, 0xd6, 0xd0, 0x15,
+	0x79, 0xad, 0xf4, 0x1b, 0x64, 0xe0, 0xd3, 0x90, 0x42, 0x6a, 0x17, 0x7f, 0xb9, 0xc6, 0xb1, 0xb5,
+	0x63, 0x96, 0x8b, 0xc7, 0xd6, 0x8e, 0x51, 0x36, 0x3d, 0x4b, 0xec, 0x59, 0xff, 0x62, 0xe9, 0xe6,
+	0x1e, 0xe1, 0xf0, 0x8a, 0x8e, 0x29, 0xb7, 0xc3, 0xdc, 0xca, 0x48, 0x84, 0x15, 0x43, 0x8a, 0xf3,
+	0x7c, 0x43, 0x07, 0x16, 0x74, 0xb8, 0x2b, 0xb8, 0xe4, 0x71, 0x61, 0xb5, 0xea, 0x43, 0x17, 0x56,
+	0xab, 0x46, 0xea, 0xcd, 0x74, 0xce, 0xdd, 0x48, 0x49, 0x9a, 0x77, 0xd2, 0x76, 0xab, 0x56, 0x67,
+	0xed, 0x36, 0xcf, 0x61, 0x77, 0x86, 0xd0, 0x72, 0x11, 0xfb, 0x16, 0x42, 0x72, 0xe6, 0xc1, 0xfb,
+	0x61, 0x92, 0x49, 0x85, 0x2c, 0xf7, 0xc2, 0xa9, 0x6b, 0x35, 0xcd, 0x86, 0xe1, 0xed, 0xca, 0xd4,
+	0xcb, 0x61, 0x92, 0xb5, 0x9f, 0x0a, 0xf3, 0x1e, 0xa3, 0x83, 0xff, 0x9b, 0xf7, 0x37, 0xbd, 0xaa,
+	0xaf, 0xf3, 0x37, 0x26, 0xc3, 0xf6, 0x33, 0xc1, 0xf6, 0x04, 0x3d, 0xda, 0x84, 0x6d, 0x15, 0xdf,
+	0x14, 0xf8, 0xfb, 0xe8, 0xde, 0x06, 0x78, 0xf7, 0x72, 0xbe, 0xa9, 0x90, 0x49, 0x3e, 0x22, 0xf7,
+	0xcd, 0xd7, 0x0f, 0x3f, 0x7e, 0x6e, 0x9b, 0x65, 0x13, 0x1d, 0x50, 0xa6, 0x44, 0x4c, 0x52, 0x36,
+	0x9b, 0xaf, 0x6d, 0x9c, 0xab, 0x3e, 0x8e, 0xbe, 0xf8, 0x14, 0xfa, 0xc6, 0x70, 0x5b, 0xfe, 0x0e,
+	0xad, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbf, 0x36, 0xd5, 0x6b, 0x6f, 0x05, 0x00, 0x00,
+}
diff --git a/vendor/github.com/cilium/proxy/go/envoy/extensions/filters/network/mongo_proxy/v3/mongo_proxy.pb.go b/vendor/github.com/cilium/proxy/go/envoy/extensions/filters/network/mongo_proxy/v3/mongo_proxy.pb.go
new file mode 100644
index 0000000000..4ddba91f23
--- /dev/null
+++ b/vendor/github.com/cilium/proxy/go/envoy/extensions/filters/network/mongo_proxy/v3/mongo_proxy.pb.go
@@ -0,0 +1,134 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: envoy/extensions/filters/network/mongo_proxy/v3/mongo_proxy.proto
+
+package envoy_extensions_filters_network_mongo_proxy_v3
+
+import (
+	fmt "fmt"
+	v3 "github.com/cilium/proxy/go/envoy/extensions/filters/common/fault/v3"
+	_ "github.com/cncf/udpa/go/udpa/annotations"
+	_ "github.com/envoyproxy/protoc-gen-validate/validate"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type MongoProxy struct {
+	// The human readable prefix to use when emitting :ref:`statistics
+	// <config_network_filters_mongo_proxy_stats>`.
+	StatPrefix string `protobuf:"bytes,1,opt,name=stat_prefix,json=statPrefix,proto3" json:"stat_prefix,omitempty"`
+	// The optional path to use for writing Mongo access logs. If not access log
+	// path is specified no access logs will be written. Note that access log is
+	// also gated :ref:`runtime <config_network_filters_mongo_proxy_runtime>`.
+	AccessLog string `protobuf:"bytes,2,opt,name=access_log,json=accessLog,proto3" json:"access_log,omitempty"`
+	// Inject a fixed delay before proxying a Mongo operation. Delays are
+	// applied to the following MongoDB operations: Query, Insert, GetMore,
+	// and KillCursors. Once an active delay is in progress, all incoming
+	// data up until the timer event fires will be a part of the delay.
+	Delay *v3.FaultDelay `protobuf:"bytes,3,opt,name=delay,proto3" json:"delay,omitempty"`
+	// Flag to specify whether :ref:`dynamic metadata
+	// <config_network_filters_mongo_proxy_dynamic_metadata>` should be emitted. Defaults to false.
+	EmitDynamicMetadata  bool     `protobuf:"varint,4,opt,name=emit_dynamic_metadata,json=emitDynamicMetadata,proto3" json:"emit_dynamic_metadata,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *MongoProxy) Reset()         { *m = MongoProxy{} }
+func (m *MongoProxy) String() string { return proto.CompactTextString(m) }
+func (*MongoProxy) ProtoMessage()    {}
+func (*MongoProxy) Descriptor() ([]byte, []int) {
+	return fileDescriptor_60ec9b7c3b00e562, []int{0}
+}
+
+func (m *MongoProxy) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_MongoProxy.Unmarshal(m, b)
+}
+func (m *MongoProxy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_MongoProxy.Marshal(b, m, deterministic)
+}
+func (m *MongoProxy) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MongoProxy.Merge(m, src)
+}
+func (m *MongoProxy) XXX_Size() int {
+	return xxx_messageInfo_MongoProxy.Size(m)
+}
+func (m *MongoProxy) XXX_DiscardUnknown() {
+	xxx_messageInfo_MongoProxy.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MongoProxy proto.InternalMessageInfo
+
+func (m *MongoProxy) GetStatPrefix() string {
+	if m != nil {
+		return m.StatPrefix
+	}
+	return ""
+}
+
+func (m *MongoProxy) GetAccessLog() string {
+	if m != nil {
+		return m.AccessLog
+	}
+	return ""
+}
+
+func (m *MongoProxy) GetDelay() *v3.FaultDelay {
+	if m != nil {
+		return m.Delay
+	}
+	return nil
+}
+
+func (m *MongoProxy) GetEmitDynamicMetadata() bool {
+	if m != nil {
+		return m.EmitDynamicMetadata
+	}
+	return false
+}
+
+func init() {
+	proto.RegisterType((*MongoProxy)(nil), "envoy.extensions.filters.network.mongo_proxy.v3.MongoProxy")
+}
+
+func init() {
+	proto.RegisterFile("envoy/extensions/filters/network/mongo_proxy/v3/mongo_proxy.proto", fileDescriptor_60ec9b7c3b00e562)
+}
+
+var fileDescriptor_60ec9b7c3b00e562 = []byte{
+	// 373 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x91, 0xb1, 0x4b, 0xe4, 0x40,
+	0x14, 0xc6, 0x49, 0x6e, 0x6f, 0xef, 0x76, 0xb6, 0xb8, 0x23, 0xc7, 0x71, 0x61, 0x61, 0x8f, 0x68,
+	0x95, 0x6a, 0x06, 0x36, 0x2b, 0x82, 0x68, 0x61, 0x58, 0x2c, 0xc4, 0x85, 0x90, 0xd2, 0x26, 0x8c,
+	0xc9, 0x24, 0x0c, 0x26, 0xf3, 0x42, 0x32, 0x1b, 0x37, 0x9d, 0xa5, 0xbd, 0x9d, 0x7f, 0x8a, 0xbd,
+	0x60, 0xeb, 0xbf, 0x63, 0x25, 0x93, 0x89, 0xec, 0x82, 0x6e, 0x61, 0xf7, 0x92, 0xef, 0xbd, 0xdf,
+	0xf7, 0xe6, 0x7b, 0xe8, 0x94, 0x89, 0x06, 0x5a, 0xc2, 0xd6, 0x92, 0x89, 0x9a, 0x83, 0xa8, 0x49,
+	0xca, 0x73, 0xc9, 0xaa, 0x9a, 0x08, 0x26, 0x6f, 0xa0, 0xba, 0x26, 0x05, 0x88, 0x0c, 0xa2, 0xb2,
+	0x82, 0x75, 0x4b, 0x1a, 0x6f, 0xfb, 0x13, 0x97, 0x15, 0x48, 0xb0, 0x48, 0x87, 0xc0, 0x1b, 0x04,
+	0xee, 0x11, 0xb8, 0x47, 0xe0, 0xed, 0x99, 0xc6, 0x9b, 0xcc, 0x77, 0x7a, 0xc6, 0x50, 0x14, 0x20,
+	0x48, 0x4a, 0x57, 0xb9, 0x54, 0x66, 0x5d, 0xa1, 0x6d, 0x26, 0xd3, 0x55, 0x52, 0x52, 0x42, 0x85,
+	0x00, 0x49, 0x65, 0x37, 0x55, 0x4b, 0x2a, 0x57, 0x75, 0x2f, 0xef, 0x7d, 0x90, 0x1b, 0x56, 0x29,
+	0x3a, 0x17, 0x59, 0xdf, 0xf2, 0xaf, 0xa1, 0x39, 0x4f, 0xa8, 0x64, 0xe4, 0xbd, 0xd0, 0xc2, 0xfe,
+	0xbd, 0x89, 0xd0, 0x52, 0xed, 0x18, 0xa8, 0x15, 0x2d, 0x17, 0x8d, 0x15, 0x3a, 0x2a, 0x2b, 0x96,
+	0xf2, 0xb5, 0x6d, 0x38, 0x86, 0x3b, 0xf2, 0x7f, 0xbc, 0xfa, 0x83, 0xca, 0x74, 0x8c, 0x10, 0x29,
+	0x2d, 0xe8, 0x24, 0x6b, 0x8a, 0x10, 0x8d, 0x63, 0x56, 0xd7, 0x51, 0x0e, 0x99, 0x6d, 0xaa, 0xc6,
+	0x70, 0xa4, 0xff, 0x5c, 0x40, 0x66, 0x9d, 0xa3, 0xef, 0x09, 0xcb, 0x69, 0x6b, 0x7f, 0x73, 0x0c,
+	0x77, 0x3c, 0x9b, 0xe3, 0x9d, 0x49, 0xe9, 0x87, 0x63, 0xfd, 0xde, 0xc6, 0xc3, 0x67, 0xaa, 0x58,
+	0xa8, 0xd9, 0x50, 0x23, 0xac, 0x19, 0xfa, 0xcb, 0x0a, 0x2e, 0xa3, 0xa4, 0x15, 0xb4, 0xe0, 0x71,
+	0x54, 0x30, 0x49, 0x13, 0x2a, 0xa9, 0x3d, 0x70, 0x0c, 0xf7, 0x67, 0xf8, 0x47, 0x89, 0x0b, 0xad,
+	0x2d, 0x7b, 0xe9, 0xe8, 0xf8, 0xe1, 0xe9, 0xee, 0xff, 0x21, 0x3a, 0xd0, 0xb6, 0x31, 0x88, 0x94,
+	0x67, 0xbd, 0xe5, 0xe7, 0xb7, 0x99, 0xe1, 0x4d, 0x0c, 0xfe, 0xe5, 0xe3, 0xed, 0xf3, 0xcb, 0xd0,
+	0xfc, 0x6d, 0xa2, 0x13, 0x0e, 0x7a, 0x75, 0xdd, 0xf6, 0xc5, 0x7b, 0xfb, 0xbf, 0x36, 0xd0, 0x40,
+	0xe5, 0x1d, 0x18, 0x57, 0xc3, 0x2e, 0x78, 0xef, 0x2d, 0x00, 0x00, 0xff, 0xff, 0xb9, 0x39, 0x0c,
+	0xaa, 0x7f, 0x02, 0x00, 0x00,
+}
diff --git a/vendor/github.com/cilium/proxy/go/envoy/extensions/filters/network/mysql_proxy/v3/mysql_proxy.pb.go b/vendor/github.com/cilium/proxy/go/envoy/extensions/filters/network/mysql_proxy/v3/mysql_proxy.pb.go
new file mode 100644
index 0000000000..4d522948e2
--- /dev/null
+++ b/vendor/github.com/cilium/proxy/go/envoy/extensions/filters/network/mysql_proxy/v3/mysql_proxy.pb.go
@@ -0,0 +1,105 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: envoy/extensions/filters/network/mysql_proxy/v3/mysql_proxy.proto
+
+package envoy_extensions_filters_network_mysql_proxy_v3
+
+import (
+	fmt "fmt"
+	_ "github.com/cncf/udpa/go/udpa/annotations"
+	_ "github.com/envoyproxy/protoc-gen-validate/validate"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type MySQLProxy struct {
+	// The human readable prefix to use when emitting :ref:`statistics
+	// <config_network_filters_mysql_proxy_stats>`.
+	StatPrefix string `protobuf:"bytes,1,opt,name=stat_prefix,json=statPrefix,proto3" json:"stat_prefix,omitempty"`
+	// [#not-implemented-hide:] The optional path to use for writing MySQL access logs.
+	// If the access log field is empty, access logs will not be written.
+	AccessLog            string   `protobuf:"bytes,2,opt,name=access_log,json=accessLog,proto3" json:"access_log,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *MySQLProxy) Reset()         { *m = MySQLProxy{} }
+func (m *MySQLProxy) String() string { return proto.CompactTextString(m) }
+func (*MySQLProxy) ProtoMessage()    {}
+func (*MySQLProxy) Descriptor() ([]byte, []int) {
+	return fileDescriptor_8896af45b7c675fb, []int{0}
+}
+
+func (m *MySQLProxy) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_MySQLProxy.Unmarshal(m, b)
+}
+func (m *MySQLProxy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_MySQLProxy.Marshal(b, m, deterministic)
+}
+func (m *MySQLProxy) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MySQLProxy.Merge(m, src)
+}
+func (m *MySQLProxy) XXX_Size() int {
+	return xxx_messageInfo_MySQLProxy.Size(m)
+}
+func (m *MySQLProxy) XXX_DiscardUnknown() {
+	xxx_messageInfo_MySQLProxy.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MySQLProxy proto.InternalMessageInfo
+
+func (m *MySQLProxy) GetStatPrefix() string {
+	if m != nil {
+		return m.StatPrefix
+	}
+	return ""
+}
+
+func (m *MySQLProxy) GetAccessLog() string {
+	if m != nil {
+		return m.AccessLog
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterType((*MySQLProxy)(nil), "envoy.extensions.filters.network.mysql_proxy.v3.MySQLProxy")
+}
+
+func init() {
+	proto.RegisterFile("envoy/extensions/filters/network/mysql_proxy/v3/mysql_proxy.proto", fileDescriptor_8896af45b7c675fb)
+}
+
+var fileDescriptor_8896af45b7c675fb = []byte{
+	// 294 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x90, 0xb1, 0x4e, 0xc3, 0x30,
+	0x14, 0x45, 0x95, 0x08, 0x15, 0xd5, 0x0c, 0xa0, 0x2c, 0x54, 0x95, 0x8a, 0x0a, 0x53, 0x27, 0x5b,
+	0x55, 0x36, 0x50, 0x07, 0x32, 0xb7, 0x52, 0x28, 0x1b, 0x4b, 0x64, 0x52, 0x27, 0x58, 0x04, 0xbf,
+	0x60, 0xbb, 0x26, 0xd9, 0x18, 0xf9, 0x05, 0xf8, 0x14, 0x76, 0x24, 0x56, 0x7e, 0x87, 0x09, 0xd9,
+	0x0e, 0x6a, 0x25, 0x58, 0xba, 0xe5, 0xe5, 0x5e, 0x9f, 0xe7, 0x63, 0x74, 0xc9, 0x84, 0x81, 0x96,
+	0xb0, 0x46, 0x33, 0xa1, 0x38, 0x08, 0x45, 0x0a, 0x5e, 0x69, 0x26, 0x15, 0x11, 0x4c, 0x3f, 0x81,
+	0xbc, 0x27, 0x0f, 0xad, 0x7a, 0xac, 0xb2, 0x5a, 0x42, 0xd3, 0x12, 0x13, 0x6f, 0x8f, 0xb8, 0x96,
+	0xa0, 0x21, 0x22, 0x0e, 0x81, 0x37, 0x08, 0xdc, 0x21, 0x70, 0x87, 0xc0, 0xdb, 0x67, 0x4c, 0x3c,
+	0x1c, 0xad, 0x57, 0x35, 0x25, 0x54, 0x08, 0xd0, 0x54, 0xbb, 0x9d, 0x4a, 0x53, 0xbd, 0x56, 0x9e,
+	0x37, 0x3c, 0xfd, 0x13, 0x1b, 0x26, 0x2d, 0x98, 0x8b, 0xb2, 0xab, 0x1c, 0x1b, 0x5a, 0xf1, 0x15,
+	0xd5, 0x8c, 0xfc, 0x7e, 0xf8, 0xe0, 0xec, 0x35, 0x40, 0x68, 0xd1, 0x5e, 0x5f, 0xcd, 0x53, 0xbb,
+	0x2c, 0x9a, 0xa0, 0x03, 0x8b, 0xce, 0x6a, 0xc9, 0x0a, 0xde, 0x0c, 0x82, 0x71, 0x30, 0xe9, 0x27,
+	0xfb, 0xdf, 0xc9, 0x9e, 0x0c, 0xc7, 0xc1, 0x12, 0xd9, 0x2c, 0x75, 0x51, 0x34, 0x42, 0x88, 0xe6,
+	0x39, 0x53, 0x2a, 0xab, 0xa0, 0x1c, 0x84, 0xb6, 0xb8, 0xec, 0xfb, 0x3f, 0x73, 0x28, 0xcf, 0x93,
+	0xb7, 0x8f, 0x97, 0x93, 0x19, 0xba, 0xf0, 0xaa, 0x39, 0x88, 0x82, 0x97, 0x9d, 0xe6, 0xff, 0x96,
+	0x53, 0x5a, 0xd5, 0x77, 0x74, 0x8a, 0x37, 0x97, 0x49, 0x6e, 0xde, 0x9f, 0x3f, 0xbf, 0x7a, 0xe1,
+	0x51, 0x88, 0x66, 0x1c, 0xb0, 0x23, 0xf9, 0xf2, 0x8e, 0xef, 0x97, 0x1c, 0x2e, 0xec, 0xec, 0xa0,
+	0xa9, 0xb5, 0x4e, 0x83, 0xdb, 0x9e, 0xd3, 0x8f, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0xfa, 0xe7,
+	0x24, 0xba, 0xcf, 0x01, 0x00, 0x00,
+}
diff --git a/vendor/k8s.io/api/core/v1/generated.proto b/vendor/k8s.io/api/core/v1/generated.proto
index 3ae96250c2..916e2601e6 100644
--- a/vendor/k8s.io/api/core/v1/generated.proto
+++ b/vendor/k8s.io/api/core/v1/generated.proto
@@ -424,6 +424,7 @@ message ComponentCondition {
 }
 
 // ComponentStatus (and ComponentStatusList) holds the cluster validation info.
+// Deprecated: This API is deprecated in v1.19+
 message ComponentStatus {
   // Standard object's metadata.
   // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
@@ -438,6 +439,7 @@ message ComponentStatus {
 }
 
 // Status of all the conditions for the component as a list of ComponentStatus objects.
+// Deprecated: This API is deprecated in v1.19+
 message ComponentStatusList {
   // Standard list metadata.
   // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
diff --git a/vendor/k8s.io/api/core/v1/lifecycle.go b/vendor/k8s.io/api/core/v1/lifecycle.go
new file mode 100644
index 0000000000..21ca90e815
--- /dev/null
+++ b/vendor/k8s.io/api/core/v1/lifecycle.go
@@ -0,0 +1,37 @@
+/*
+Copyright 2020 The Kubernetes Authors.
+
+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.
+*/
+
+package v1
+
+// APILifecycleIntroduced returns the release in which the API struct was introduced as int versions of major and minor for comparison.
+func (in *ComponentStatus) APILifecycleIntroduced() (major, minor int) {
+	return 1, 0
+}
+
+// APILifecycleDeprecated returns the release in which the API struct was or will be deprecated as int versions of major and minor for comparison.
+func (in *ComponentStatus) APILifecycleDeprecated() (major, minor int) {
+	return 1, 19
+}
+
+// APILifecycleIntroduced returns the release in which the API struct was introduced as int versions of major and minor for comparison.
+func (in *ComponentStatusList) APILifecycleIntroduced() (major, minor int) {
+	return 1, 0
+}
+
+// APILifecycleDeprecated returns the release in which the API struct was or will be deprecated as int versions of major and minor for comparison.
+func (in *ComponentStatusList) APILifecycleDeprecated() (major, minor int) {
+	return 1, 19
+}
diff --git a/vendor/k8s.io/api/core/v1/types.go b/vendor/k8s.io/api/core/v1/types.go
index 08b6c70067..f3ec52e71c 100644
--- a/vendor/k8s.io/api/core/v1/types.go
+++ b/vendor/k8s.io/api/core/v1/types.go
@@ -5828,6 +5828,7 @@ type ComponentCondition struct {
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // ComponentStatus (and ComponentStatusList) holds the cluster validation info.
+// Deprecated: This API is deprecated in v1.19+
 type ComponentStatus struct {
 	metav1.TypeMeta `json:",inline"`
 	// Standard object's metadata.
@@ -5845,6 +5846,7 @@ type ComponentStatus struct {
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // Status of all the conditions for the component as a list of ComponentStatus objects.
+// Deprecated: This API is deprecated in v1.19+
 type ComponentStatusList struct {
 	metav1.TypeMeta `json:",inline"`
 	// Standard list metadata.
diff --git a/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go b/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go
index 2969a9b0e5..61832b8159 100644
--- a/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go
+++ b/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go
@@ -230,7 +230,7 @@ func (ComponentCondition) SwaggerDoc() map[string]string {
 }
 
 var map_ComponentStatus = map[string]string{
-	"":           "ComponentStatus (and ComponentStatusList) holds the cluster validation info.",
+	"":           "ComponentStatus (and ComponentStatusList) holds the cluster validation info. Deprecated: This API is deprecated in v1.19+",
 	"metadata":   "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata",
 	"conditions": "List of component conditions observed",
 }
@@ -240,7 +240,7 @@ func (ComponentStatus) SwaggerDoc() map[string]string {
 }
 
 var map_ComponentStatusList = map[string]string{
-	"":         "Status of all the conditions for the component as a list of ComponentStatus objects.",
+	"":         "Status of all the conditions for the component as a list of ComponentStatus objects. Deprecated: This API is deprecated in v1.19+",
 	"metadata": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
 	"items":    "List of ComponentStatus objects.",
 }
diff --git a/vendor/k8s.io/client-go/util/jsonpath/parser.go b/vendor/k8s.io/client-go/util/jsonpath/parser.go
index e1aab6804f..b84016a9f9 100644
--- a/vendor/k8s.io/client-go/util/jsonpath/parser.go
+++ b/vendor/k8s.io/client-go/util/jsonpath/parser.go
@@ -214,8 +214,11 @@ func (p *Parser) parseIdentifier(cur *ListNode) error {
 	return p.parseInsideAction(cur)
 }
 
-// parseRecursive scans the recursive desent operator ..
+// parseRecursive scans the recursive descent operator ..
 func (p *Parser) parseRecursive(cur *ListNode) error {
+	if lastIndex := len(cur.Nodes) - 1; lastIndex >= 0 && cur.Nodes[lastIndex].Type() == NodeRecursive {
+		return fmt.Errorf("invalid multiple recursive descent")
+	}
 	p.pos += len("..")
 	p.consumeText()
 	cur.append(newRecursive())
diff --git a/vendor/k8s.io/utils/trace/trace.go b/vendor/k8s.io/utils/trace/trace.go
index 7ae41672e1..2af4967ca0 100644
--- a/vendor/k8s.io/utils/trace/trace.go
+++ b/vendor/k8s.io/utils/trace/trace.go
@@ -197,7 +197,7 @@ func (t *Trace) logTrace() {
 		}
 
 		// if any step took more than it's share of the total allowed time, it deserves a higher log level
-		buffer.WriteString(fmt.Sprintf("(%v) (total time: %vms):", t.startTime.Format("02-Jan-2006 15:04:00.000"), totalTime.Milliseconds()))
+		buffer.WriteString(fmt.Sprintf("(%v) (total time: %vms):", t.startTime.Format("02-Jan-2006 15:04:05.000"), totalTime.Milliseconds()))
 		stepThreshold := t.calculateStepThreshold()
 		t.writeTraceSteps(&buffer, fmt.Sprintf("\nTrace[%d]: ", traceNum), stepThreshold)
 		buffer.WriteString(fmt.Sprintf("\nTrace[%d]: [%v] [%v] END\n", traceNum, t.endTime.Sub(t.startTime), totalTime))
diff --git a/vendor/modules.txt b/vendor/modules.txt
index a4bee5d240..528ca25d3a 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -117,7 +117,10 @@ github.com/cilium/proxy/go/envoy/config/metrics/v3
 github.com/cilium/proxy/go/envoy/config/overload/v3
 github.com/cilium/proxy/go/envoy/config/route/v3
 github.com/cilium/proxy/go/envoy/config/trace/v3
+github.com/cilium/proxy/go/envoy/extensions/filters/common/fault/v3
 github.com/cilium/proxy/go/envoy/extensions/filters/network/http_connection_manager/v3
+github.com/cilium/proxy/go/envoy/extensions/filters/network/mongo_proxy/v3
+github.com/cilium/proxy/go/envoy/extensions/filters/network/mysql_proxy/v3
 github.com/cilium/proxy/go/envoy/extensions/filters/network/tcp_proxy/v3
 github.com/cilium/proxy/go/envoy/extensions/transport_sockets/tls/v3
 github.com/cilium/proxy/go/envoy/service/discovery/v3
@@ -831,7 +834,7 @@ gopkg.in/tomb.v1
 gopkg.in/yaml.v2
 # gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
 gopkg.in/yaml.v3
-# k8s.io/api v0.19.0-rc.3
+# k8s.io/api v0.19.0-rc.4
 ## explicit
 k8s.io/api/admissionregistration/v1
 k8s.io/api/admissionregistration/v1beta1
@@ -874,7 +877,7 @@ k8s.io/api/settings/v1alpha1
 k8s.io/api/storage/v1
 k8s.io/api/storage/v1alpha1
 k8s.io/api/storage/v1beta1
-# k8s.io/apiextensions-apiserver v0.19.0-rc.3
+# k8s.io/apiextensions-apiserver v0.19.0-rc.4
 ## explicit
 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions
 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1
@@ -887,7 +890,7 @@ k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextension
 k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/fake
 k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1
 k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/fake
-# k8s.io/apimachinery v0.19.0-rc.3
+# k8s.io/apimachinery v0.19.0-rc.4
 ## explicit
 k8s.io/apimachinery/pkg/api/equality
 k8s.io/apimachinery/pkg/api/errors
@@ -932,7 +935,7 @@ k8s.io/apimachinery/pkg/version
 k8s.io/apimachinery/pkg/watch
 k8s.io/apimachinery/third_party/forked/golang/json
 k8s.io/apimachinery/third_party/forked/golang/reflect
-# k8s.io/client-go v0.19.0-rc.3 => github.com/cilium/client-go v0.0.0-20200729144658-19fb033f881a
+# k8s.io/client-go v0.19.0-rc.4 => github.com/cilium/client-go v0.0.0-20200804232341-96a4c8f3593c
 ## explicit
 k8s.io/client-go/discovery
 k8s.io/client-go/discovery/fake
@@ -1051,7 +1054,7 @@ k8s.io/client-go/util/homedir
 k8s.io/client-go/util/jsonpath
 k8s.io/client-go/util/keyutil
 k8s.io/client-go/util/workqueue
-# k8s.io/code-generator v0.19.0-rc.3
+# k8s.io/code-generator v0.19.0-rc.4
 ## explicit
 k8s.io/code-generator
 k8s.io/code-generator/cmd/client-gen
@@ -1110,7 +1113,7 @@ k8s.io/kube-openapi/pkg/generators
 k8s.io/kube-openapi/pkg/generators/rules
 k8s.io/kube-openapi/pkg/util/proto
 k8s.io/kube-openapi/pkg/util/sets
-# k8s.io/utils v0.0.0-20200720150651-0bdb4ca86cbc
+# k8s.io/utils v0.0.0-20200729134348-d5654de09c73
 k8s.io/utils/buffer
 k8s.io/utils/integer
 k8s.io/utils/pointer
@@ -1122,4 +1125,4 @@ sigs.k8s.io/structured-merge-diff/v3/value
 sigs.k8s.io/yaml
 # github.com/miekg/dns => github.com/cilium/dns v1.1.4-0.20190417235132-8e25ec9a0ff3
 # github.com/optiopay/kafka => github.com/cilium/kafka v0.0.0-20180809090225-01ce283b732b
-# k8s.io/client-go => github.com/cilium/client-go v0.0.0-20200729144658-19fb033f881a
+# k8s.io/client-go => github.com/cilium/client-go v0.0.0-20200804232341-96a4c8f3593c


Test patch:
diff --git a/Documentation/contributing/testing/e2e.rst b/Documentation/contributing/testing/e2e.rst
index 06266794a0..587ca53f22 100644
--- a/Documentation/contributing/testing/e2e.rst
+++ b/Documentation/contributing/testing/e2e.rst
@@ -122,10 +122,6 @@ contain any characters after it.
 
 The Kubernetes tests support the following Kubernetes versions:
 
-* 1.8
-* 1.9
-* 1.10
-* 1.11
 * 1.12
 * 1.13
 * 1.14
@@ -135,7 +131,7 @@ The Kubernetes tests support the following Kubernetes versions:
 * 1.18
 * 1.19
 
-By default, the Vagrant VMs are provisioned with Kubernetes 1.18. To run with any other
+By default, the Vagrant VMs are provisioned with Kubernetes 1.19. To run with any other
 supported version of Kubernetes, run the test suite with the following format:
 
 ::
diff --git a/pkg/endpoint/endpoint_test.go b/pkg/endpoint/endpoint_test.go
index 8591dafbc2..49507c9586 100644
--- a/pkg/endpoint/endpoint_test.go
+++ b/pkg/endpoint/endpoint_test.go
@@ -506,8 +506,8 @@ func (s *EndpointSuite) TestWaitForPolicyRevision(c *C) {
 func (s *EndpointSuite) TestProxyID(c *C) {
 	e := &Endpoint{ID: 123, policyRevision: 0}
 
-	id, err := e.ProxyID(nil, &policy.L4Filter{Port: 8080, Protocol: api.ProtoTCP, Ingress: true})
-	c.Assert(err, IsNil)
+	id := e.proxyID(&policy.L4Filter{Port: 8080, Protocol: api.ProtoTCP, Ingress: true})
+	c.Assert(id, Not(Equals), "")
 	endpointID, ingress, protocol, port, err := policy.ParseProxyID(id)
 	c.Assert(endpointID, Equals, uint16(123))
 	c.Assert(ingress, Equals, true)
diff --git a/pkg/endpoint/redirect_test.go b/pkg/endpoint/redirect_test.go
index efb4c3e920..1a7669a8af 100644
--- a/pkg/endpoint/redirect_test.go
+++ b/pkg/endpoint/redirect_test.go
@@ -63,7 +63,7 @@ func (r *RedirectSuiteProxy) RemoveRedirect(id string, wg *completion.WaitGroup)
 }
 
 // UpdateNetworkPolicy does nothing.
-func (r *RedirectSuiteProxy) UpdateNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy, npMap policy.NamedPortsMap, ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup) (error, func() error) {
+func (r *RedirectSuiteProxy) UpdateNetworkPolicy(ep logger.EndpointUpdater, policy *policy.L4Policy, ingressPolicyEnforced, egressPolicyEnforced bool, wg *completion.WaitGroup) (error, func() error) {
 	return nil, nil
 }
 
diff --git a/pkg/envoy/server_test.go b/pkg/envoy/server_test.go
index 801bb62946..68173342ca 100644
--- a/pkg/envoy/server_test.go
+++ b/pkg/envoy/server_test.go
@@ -24,6 +24,8 @@ import (
 	"github.com/cilium/cilium/pkg/policy"
 	"github.com/cilium/cilium/pkg/policy/api"
 	"github.com/cilium/cilium/pkg/policy/api/kafka"
+	"github.com/cilium/cilium/pkg/proxy/logger"
+	"github.com/cilium/cilium/pkg/proxy/logger/test"
 
 	"github.com/cilium/proxy/go/cilium/api"
 	envoy_config_core "github.com/cilium/proxy/go/envoy/config/core/v3"
@@ -44,6 +46,14 @@ var (
 	_        = Suite(&ServerSuite{})
 	IPv4Addr = "10.1.1.1"
 	Identity = identity.NumericIdentity(123)
+
+	ep logger.EndpointUpdater = &test.ProxyUpdaterMock{
+		Id:       1000,
+		Ipv4:     "10.0.0.1",
+		Ipv6:     "f00d::1",
+		Labels:   []string{"id.foo", "id.bar"},
+		Identity: Identity,
+	}
 )
 
 var PortRuleHTTP1 = &api.PortRuleHTTP{
@@ -486,99 +496,106 @@ func (s *ServerSuite) TestGetPortNetworkPolicyRule(c *C) {
 
 func (s *ServerSuite) TestGetDirectionNetworkPolicy(c *C) {
 	// L4+L7
-	obtained := getDirectionNetworkPolicy(L4PolicyMap1, nil, true)
+	obtained := getDirectionNetworkPolicy(ep, L4PolicyMap1, true)
 	c.Assert(obtained, checker.Equals, ExpectedPerPortPolicies1)
 
 	// L4+L7 with header mods
-	obtained = getDirectionNetworkPolicy(L4PolicyMap1HeaderMatch, nil, true)
+	obtained = getDirectionNetworkPolicy(ep, L4PolicyMap1HeaderMatch, true)
 	c.Assert(obtained, checker.Equals, ExpectedPerPortPolicies1HeaderMatch)
 
 	// L4+L7
-	obtained = getDirectionNetworkPolicy(L4PolicyMap2, nil, true)
+	obtained = getDirectionNetworkPolicy(ep, L4PolicyMap2, true)
 	c.Assert(obtained, checker.Equals, ExpectedPerPortPolicies2)
 
 	// L4-only
-	obtained = getDirectionNetworkPolicy(L4PolicyMap4, nil, true)
+	obtained = getDirectionNetworkPolicy(ep, L4PolicyMap4, true)
 	c.Assert(obtained, checker.Equals, ExpectedPerPortPolicies6)
 
 	// L4-only
-	obtained = getDirectionNetworkPolicy(L4PolicyMap5, nil, true)
+	obtained = getDirectionNetworkPolicy(ep, L4PolicyMap5, true)
 	c.Assert(obtained, checker.Equals, ExpectedPerPortPolicies7)
 }
 
 func (s *ServerSuite) TestGetNetworkPolicy(c *C) {
-	obtained := getNetworkPolicy(IPv4Addr, Identity, "", L4Policy1, nil, true, true)
+	obtained := getNetworkPolicy(ep, IPv4Addr, L4Policy1, true, true)
 	expected := &cilium.NetworkPolicy{
 		Name:                   IPv4Addr,
 		Policy:                 uint64(Identity),
 		IngressPerPortPolicies: ExpectedPerPortPolicies1,
 		EgressPerPortPolicies:  ExpectedPerPortPolicies2,
+		ConntrackMapName:       "global",
 	}
 	c.Assert(obtained, checker.Equals, expected)
 }
 
 func (s *ServerSuite) TestGetNetworkPolicyWildcard(c *C) {
-	obtained := getNetworkPolicy(IPv4Addr, Identity, "", L4Policy2, nil, true, true)
+	obtained := getNetworkPolicy(ep, IPv4Addr, L4Policy2, true, true)
 	expected := &cilium.NetworkPolicy{
 		Name:                   IPv4Addr,
 		Policy:                 uint64(Identity),
 		IngressPerPortPolicies: ExpectedPerPortPolicies3,
 		EgressPerPortPolicies:  ExpectedPerPortPolicies2,
+		ConntrackMapName:       "global",
 	}
 	c.Assert(obtained, checker.Equals, expected)
 }
 
 func (s *ServerSuite) TestGetNetworkPolicyDeny(c *C) {
-	obtained := getNetworkPolicy(IPv4Addr, Identity, "", L4Policy1RequiresV2, nil, true, true)
+	obtained := getNetworkPolicy(ep, IPv4Addr, L4Policy1RequiresV2, true, true)
 	expected := &cilium.NetworkPolicy{
 		Name:                   IPv4Addr,
 		Policy:                 uint64(Identity),
 		IngressPerPortPolicies: ExpectedPerPortPolicies4RequiresV2,
 		EgressPerPortPolicies:  ExpectedPerPortPolicies2,
+		ConntrackMapName:       "global",
 	}
 	c.Assert(obtained, checker.Equals, expected)
 }
 
 func (s *ServerSuite) TestGetNetworkPolicyWildcardDeny(c *C) {
-	obtained := getNetworkPolicy(IPv4Addr, Identity, "", L4Policy2RequiresV2, nil, true, true)
+	obtained := getNetworkPolicy(ep, IPv4Addr, L4Policy2RequiresV2, true, true)
 	expected := &cilium.NetworkPolicy{
 		Name:                   IPv4Addr,
 		Policy:                 uint64(Identity),
 		IngressPerPortPolicies: ExpectedPerPortPolicies5RequiresV2,
 		EgressPerPortPolicies:  ExpectedPerPortPolicies2,
+		ConntrackMapName:       "global",
 	}
 	c.Assert(obtained, checker.Equals, expected)
 }
 
 func (s *ServerSuite) TestGetNetworkPolicyNil(c *C) {
-	obtained := getNetworkPolicy(IPv4Addr, Identity, "", nil, nil, true, true)
+	obtained := getNetworkPolicy(ep, IPv4Addr, nil, true, true)
 	expected := &cilium.NetworkPolicy{
 		Name:                   IPv4Addr,
 		Policy:                 uint64(Identity),
 		IngressPerPortPolicies: nil,
 		EgressPerPortPolicies:  nil,
+		ConntrackMapName:       "global",
 	}
 	c.Assert(obtained, checker.Equals, expected)
 }
 
 func (s *ServerSuite) TestGetNetworkPolicyIngressNotEnforced(c *C) {
-	obtained := getNetworkPolicy(IPv4Addr, Identity, "", L4Policy2, nil, false, true)
+	obtained := getNetworkPolicy(ep, IPv4Addr, L4Policy2, false, true)
 	expected := &cilium.NetworkPolicy{
 		Name:                   IPv4Addr,
 		Policy:                 uint64(Identity),
 		IngressPerPortPolicies: allowAllPortNetworkPolicy,
 		EgressPerPortPolicies:  ExpectedPerPortPolicies2,
+		ConntrackMapName:       "global",
 	}
 	c.Assert(obtained, checker.Equals, expected)
 }
 
 func (s *ServerSuite) TestGetNetworkPolicyEgressNotEnforced(c *C) {
-	obtained := getNetworkPolicy(IPv4Addr, Identity, "", L4Policy2RequiresV2, nil, true, false)
+	obtained := getNetworkPolicy(ep, IPv4Addr, L4Policy2RequiresV2, true, false)
 	expected := &cilium.NetworkPolicy{
 		Name:                   IPv4Addr,
 		Policy:                 uint64(Identity),
 		IngressPerPortPolicies: ExpectedPerPortPolicies5RequiresV2,
 		EgressPerPortPolicies:  allowAllPortNetworkPolicy,
+		ConntrackMapName:       "global",
 	}
 	c.Assert(obtained, checker.Equals, expected)
 }
@@ -630,11 +647,12 @@ var ExpectedPerPortPoliciesL7 = []*cilium.PortNetworkPolicy{
 }
 
 func (s *ServerSuite) TestGetNetworkPolicyL7(c *C) {
-	obtained := getNetworkPolicy(IPv4Addr, Identity, "", L4PolicyL7, nil, true, true)
+	obtained := getNetworkPolicy(ep, IPv4Addr, L4PolicyL7, true, true)
 	expected := &cilium.NetworkPolicy{
 		Name:                   IPv4Addr,
 		Policy:                 uint64(Identity),
 		IngressPerPortPolicies: ExpectedPerPortPoliciesL7,
+		ConntrackMapName:       "global",
 	}
 	c.Assert(obtained, checker.Equals, expected)
 }
@@ -684,11 +702,82 @@ var ExpectedPerPortPoliciesKafka = []*cilium.PortNetworkPolicy{
 }
 
 func (s *ServerSuite) TestGetNetworkPolicyKafka(c *C) {
-	obtained := getNetworkPolicy(IPv4Addr, Identity, "", L4PolicyKafka, nil, true, true)
+	obtained := getNetworkPolicy(ep, IPv4Addr, L4PolicyKafka, true, true)
 	expected := &cilium.NetworkPolicy{
 		Name:                   IPv4Addr,
 		Policy:                 uint64(Identity),
 		IngressPerPortPolicies: ExpectedPerPortPoliciesKafka,
+		ConntrackMapName:       "global",
+	}
+	c.Assert(obtained, checker.Equals, expected)
+}
+
+var L4PolicyMySQL = &policy.L4Policy{
+	Egress: map[string]*policy.L4Filter{
+		"3306/TCP": {
+			Port: 3306, Protocol: api.ProtoTCP,
+			L7Parser: "envoy.filters.network.mysql_proxy",
+			L7RulesPerSelector: policy.L7DataMap{
+				cachedSelector1: &policy.PerSelectorPolicy{L7Rules: api.L7Rules{
+					L7Proto: "envoy.filters.network.mysql_proxy",
+					L7: []api.PortRuleL7{
+						map[string]string{
+							"action":     "deny",
+							"user.mysql": "select"},
+					},
+				}},
+			},
+			Ingress: false,
+		},
+	},
+}
+
+var ExpectedPerPortPoliciesMySQL = []*cilium.PortNetworkPolicy{
+	{
+		Port:     3306,
+		Protocol: envoy_config_core.SocketAddress_TCP,
+		Rules: []*cilium.PortNetworkPolicyRule{{
+			RemotePolicies: []uint64{1001, 1002},
+			L7Proto:        "envoy.filters.network.mysql_proxy",
+			L7: &cilium.PortNetworkPolicyRule_L7Rules{
+				L7Rules: &cilium.L7NetworkPolicyRules{
+					L7DenyRules: []*cilium.L7NetworkPolicyRule{{
+						MetadataRule: []*envoy_type_matcher.MetadataMatcher{{
+							Filter: "envoy.filters.network.mysql_proxy",
+							Path: []*envoy_type_matcher.MetadataMatcher_PathSegment{{
+								Segment: &envoy_type_matcher.MetadataMatcher_PathSegment_Key{Key: "user.mysql"},
+							}},
+							Value: &envoy_type_matcher.ValueMatcher{
+								MatchPattern: &envoy_type_matcher.ValueMatcher_ListMatch{
+									ListMatch: &envoy_type_matcher.ListMatcher{
+										MatchPattern: &envoy_type_matcher.ListMatcher_OneOf{
+											OneOf: &envoy_type_matcher.ValueMatcher{
+												MatchPattern: &envoy_type_matcher.ValueMatcher_StringMatch{
+													StringMatch: &envoy_type_matcher.StringMatcher{
+														MatchPattern: &envoy_type_matcher.StringMatcher_Exact{
+															Exact: "select",
+														},
+													},
+												},
+											},
+										},
+									},
+								},
+							},
+						}},
+					}},
+				},
+			}},
+		},
+	},
+}
+
+func (s *ServerSuite) TestGetNetworkPolicyMySQL(c *C) {
+	obtained := getNetworkPolicy(IPv4Addr, Identity, "", L4PolicyMySQL, nil, true, true)
+	expected := &cilium.NetworkPolicy{
+		Name:                  IPv4Addr,
+		Policy:                uint64(Identity),
+		EgressPerPortPolicies: ExpectedPerPortPoliciesMySQL,
 	}
 	c.Assert(obtained, checker.Equals, expected)
 }
diff --git a/pkg/ipcache/ipcache_test.go b/pkg/ipcache/ipcache_test.go
index 3c280c4146..f9e6ab34cd 100644
--- a/pkg/ipcache/ipcache_test.go
+++ b/pkg/ipcache/ipcache_test.go
@@ -277,9 +277,9 @@ func (s *IPCacheTestSuite) TestIPCacheNamedPorts(c *C) {
 	meta := K8sMetadata{
 		Namespace: "default",
 		PodName:   "app1",
-		NamedPorts: policy.NamedPortsMap{
-			"http": policy.NamedPort{Port: 80, Proto: uint8(u8proto.TCP)},
-			"dns":  policy.NamedPort{Port: 53},
+		NamedPorts: policy.NamedPortMap{
+			"http": policy.PortProto{Port: 80, Proto: uint8(u8proto.TCP)},
+			"dns":  policy.PortProto{Port: 53},
 		},
 	}
 
@@ -301,14 +301,12 @@ func (s *IPCacheTestSuite) TestIPCacheNamedPorts(c *C) {
 	c.Assert(cachedIdentity.Source, Equals, source.Kubernetes)
 
 	// Named ports have been updated
-	c.Assert(namedPortsChanged, Equals, true)
+	c.Assert(namedPortsChanged, Equals, false) // not before GetNamedPorts() has been called once
 	npm := IPIdentityCache.GetNamedPorts()
 	c.Assert(npm, NotNil)
 	c.Assert(len(npm), Equals, 2)
-	c.Assert(npm["http"].Port, Equals, uint16(80))
-	c.Assert(npm["http"].Proto, Equals, uint8(6)) // TCP
-	c.Assert(npm["dns"].Port, Equals, uint16(53))
-	c.Assert(npm["dns"].Proto, Equals, uint8(0)) // ANY
+	c.Assert(npm["http"], checker.HasKey, policy.PortProto{Port: uint16(80), Proto: uint8(6)})
+	c.Assert(npm["dns"], checker.HasKey, policy.PortProto{Port: uint16(53), Proto: uint8(0)})
 
 	// No duplicates.
 	c.Assert(len(IPIdentityCache.ipToIdentityCache), Equals, 1)
@@ -328,9 +326,9 @@ func (s *IPCacheTestSuite) TestIPCacheNamedPorts(c *C) {
 	meta2 := K8sMetadata{
 		Namespace: "testing",
 		PodName:   "app2",
-		NamedPorts: policy.NamedPortsMap{
-			"https": policy.NamedPort{Port: 443, Proto: uint8(u8proto.TCP)},
-			"dns":   policy.NamedPort{Port: 53},
+		NamedPorts: policy.NamedPortMap{
+			"https": policy.PortProto{Port: 443, Proto: uint8(u8proto.TCP)},
+			"dns":   policy.PortProto{Port: 53},
 		},
 	}
 
@@ -356,22 +354,17 @@ func (s *IPCacheTestSuite) TestIPCacheNamedPorts(c *C) {
 	npm = IPIdentityCache.GetNamedPorts()
 	c.Assert(npm, NotNil)
 	c.Assert(len(npm), Equals, 3)
-	c.Assert(npm["http"].Port, Equals, uint16(80))
-	c.Assert(npm["http"].Proto, Equals, uint8(6)) // TCP
-	c.Assert(npm["dns"].Port, Equals, uint16(53))
-	c.Assert(npm["dns"].Proto, Equals, uint8(0)) // ANY
-	c.Assert(npm["https"].Port, Equals, uint16(443))
-	c.Assert(npm["https"].Proto, Equals, uint8(6)) // TCP
+	c.Assert(npm["http"], checker.HasKey, policy.PortProto{Port: uint16(80), Proto: uint8(6)})
+	c.Assert(npm["dns"], checker.HasKey, policy.PortProto{Port: uint16(53), Proto: uint8(0)})
+	c.Assert(npm["https"], checker.HasKey, policy.PortProto{Port: uint16(443), Proto: uint8(6)})
 
 	namedPortsChanged = IPIdentityCache.Delete(endpointIP, source.Kubernetes)
 	c.Assert(namedPortsChanged, Equals, true)
 	npm = IPIdentityCache.GetNamedPorts()
 	c.Assert(npm, NotNil)
 	c.Assert(len(npm), Equals, 2)
-	c.Assert(npm["dns"].Port, Equals, uint16(53))
-	c.Assert(npm["dns"].Proto, Equals, uint8(0)) // ANY
-	c.Assert(npm["https"].Port, Equals, uint16(443))
-	c.Assert(npm["https"].Proto, Equals, uint8(6)) // TCP
+	c.Assert(npm["dns"], checker.HasKey, policy.PortProto{Port: uint16(53), Proto: uint8(0)})
+	c.Assert(npm["https"], checker.HasKey, policy.PortProto{Port: uint16(443), Proto: uint8(6)})
 
 	// Assure deletion occurs across all mappings.
 	c.Assert(len(IPIdentityCache.ipToIdentityCache), Equals, 1)
@@ -444,8 +437,8 @@ func (s *IPCacheTestSuite) TestIPCacheNamedPorts(c *C) {
 	// Test mapping of multiple IPs to same identity.
 	endpointIPs := []string{"192.168.0.1", "20.3.75.3", "27.2.2.2", "127.0.0.1", "127.0.0.1"}
 	identities := []identityPkg.NumericIdentity{5, 67, 29, 29, 29}
-	k8sMeta.NamedPorts = policy.NamedPortsMap{
-		"http2": policy.NamedPort{Port: 8080, Proto: uint8(u8proto.TCP)},
+	k8sMeta.NamedPorts = policy.NamedPortMap{
+		"http2": policy.PortProto{Port: 8080, Proto: uint8(u8proto.TCP)},
 	}
 
 	for index := range endpointIPs {
@@ -457,7 +450,7 @@ func (s *IPCacheTestSuite) TestIPCacheNamedPorts(c *C) {
 		c.Assert(updated, Equals, true)
 		npm = IPIdentityCache.GetNamedPorts()
 		c.Assert(npm, NotNil)
-		c.Assert(npm["http2"].Port, Equals, uint16(8080))
+		c.Assert(npm["http2"], checker.HasKey, policy.PortProto{Port: uint16(8080), Proto: uint8(6)})
 		// only the first changes named ports, as they are all the same
 		c.Assert(namedPortsChanged, Equals, index == 0)
 		cachedIdentity, _ := IPIdentityCache.LookupByIP(endpointIPs[index])
diff --git a/pkg/k8s/factory_functions_test.go b/pkg/k8s/factory_functions_test.go
index 9f756164a4..57a08b0288 100644
--- a/pkg/k8s/factory_functions_test.go
+++ b/pkg/k8s/factory_functions_test.go
@@ -31,7 +31,6 @@ import (
 	"github.com/cilium/cilium/pkg/policy/api"
 
 	. "gopkg.in/check.v1"
-	"k8s.io/api/core/v1"
 	core_v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/tools/cache"
@@ -1000,7 +999,7 @@ func (s *K8sSuite) Test_ConvertToK8sService(c *C) {
 		{
 			name: "normal conversion",
 			args: args{
-				obj: &v1.Service{},
+				obj: &core_v1.Service{},
 			},
 			want: &slim_corev1.Service{},
 		},
@@ -1009,7 +1008,7 @@ func (s *K8sSuite) Test_ConvertToK8sService(c *C) {
 			args: args{
 				obj: cache.DeletedFinalStateUnknown{
 					Key: "foo",
-					Obj: &v1.Service{},
+					Obj: &core_v1.Service{},
 				},
 			},
 			want: cache.DeletedFinalStateUnknown{
@@ -1176,7 +1175,7 @@ func (s *K8sSuite) Test_ConvertToNode(c *C) {
 		{
 			name: "normal conversion",
 			args: args{
-				obj: &v1.Node{},
+				obj: &core_v1.Node{},
 			},
 			want: &slim_corev1.Node{},
 		},
@@ -1185,7 +1184,7 @@ func (s *K8sSuite) Test_ConvertToNode(c *C) {
 			args: args{
 				obj: cache.DeletedFinalStateUnknown{
 					Key: "foo",
-					Obj: &v1.Node{},
+					Obj: &core_v1.Node{},
 				},
 			},
 			want: cache.DeletedFinalStateUnknown{
diff --git a/pkg/k8s/service_cache_test.go b/pkg/k8s/service_cache_test.go
index 6b87b232b0..332248dbdd 100644
--- a/pkg/k8s/service_cache_test.go
+++ b/pkg/k8s/service_cache_test.go
@@ -32,7 +32,6 @@ import (
 	"github.com/cilium/cilium/pkg/testutils"
 
 	"gopkg.in/check.v1"
-	. "gopkg.in/check.v1"
 )
 
 func (s *K8sSuite) TestGetUniqueServiceFrontends(c *check.C) {
@@ -322,13 +321,13 @@ func testServiceCache(c *check.C,
 	c.Assert(testutils.WaitUntil(func() bool {
 		swgSvcs.Wait()
 		return true
-	}, 2*time.Second), IsNil)
+	}, 2*time.Second), check.IsNil)
 
 	swgEps.Stop()
 	c.Assert(testutils.WaitUntil(func() bool {
 		swgEps.Wait()
 		return true
-	}, 2*time.Second), IsNil)
+	}, 2*time.Second), check.IsNil)
 }
 
 func (s *K8sSuite) TestCacheActionString(c *check.C) {
@@ -587,13 +586,13 @@ func (s *K8sSuite) TestServiceMerging(c *check.C) {
 	c.Assert(testutils.WaitUntil(func() bool {
 		swgSvcs.Wait()
 		return true
-	}, 2*time.Second), IsNil)
+	}, 2*time.Second), check.IsNil)
 
 	swgEps.Stop()
 	c.Assert(testutils.WaitUntil(func() bool {
 		swgEps.Wait()
 		return true
-	}, 2*time.Second), IsNil)
+	}, 2*time.Second), check.IsNil)
 }
 
 func (s *K8sSuite) TestNonSharedServie(c *check.C) {
@@ -642,7 +641,7 @@ func (s *K8sSuite) TestNonSharedServie(c *check.C) {
 	c.Assert(testutils.WaitUntil(func() bool {
 		swgSvcs.Wait()
 		return true
-	}, 2*time.Second), IsNil)
+	}, 2*time.Second), check.IsNil)
 }
 
 func (s *K8sSuite) TestServiceCacheWith2EndpointSlice(c *check.C) {
@@ -879,11 +878,11 @@ func (s *K8sSuite) TestServiceCacheWith2EndpointSlice(c *check.C) {
 	c.Assert(testutils.WaitUntil(func() bool {
 		swgSvcs.Wait()
 		return true
-	}, 2*time.Second), IsNil)
+	}, 2*time.Second), check.IsNil)
 
 	swgEps.Stop()
 	c.Assert(testutils.WaitUntil(func() bool {
 		swgEps.Wait()
 		return true
-	}, 2*time.Second), IsNil)
+	}, 2*time.Second), check.IsNil)
 }
diff --git a/pkg/logging/limiter_test.go b/pkg/logging/limiter_test.go
new file mode 100644
index 0000000000..af9b2239ff
--- /dev/null
+++ b/pkg/logging/limiter_test.go
@@ -0,0 +1,59 @@
+// 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 logging
+
+import (
+	"time"
+
+	. "gopkg.in/check.v1"
+)
+
+func (s *LoggingSuite) TestLimiter(c *C) {
+	// Set up a limiter that allows one event every half second with the burts of 3.
+	// The underlying token bucket has the capacity of three and fill rate of
+	// 2 per second.
+	limiter := NewLimiter(500*time.Millisecond, 3)
+
+	// Initially tree events should be allowed and the rest denied.
+	c.Assert(limiter.Allow(), Equals, true)
+	c.Assert(limiter.Allow(), Equals, true)
+	c.Assert(limiter.Allow(), Equals, true)
+	c.Assert(limiter.Allow(), Equals, false)
+	c.Assert(limiter.Allow(), Equals, false)
+	c.Assert(limiter.Allow(), Equals, false)
+
+	// After half second one more event should be allowed, the rest denied
+	time.Sleep(500 * time.Millisecond)
+	c.Assert(limiter.Allow(), Equals, true)
+	c.Assert(limiter.Allow(), Equals, false)
+	c.Assert(limiter.Allow(), Equals, false)
+
+	// After one more second two events should be allowed, the rest denied
+	time.Sleep(1 * time.Second)
+	c.Assert(limiter.Allow(), Equals, true)
+	c.Assert(limiter.Allow(), Equals, true)
+	c.Assert(limiter.Allow(), Equals, false)
+	c.Assert(limiter.Allow(), Equals, false)
+
+	// After two more seconds three events should be allowed, the rest denied
+	time.Sleep(2 * time.Second)
+	c.Assert(limiter.Allow(), Equals, true)
+	c.Assert(limiter.Allow(), Equals, true)
+	c.Assert(limiter.Allow(), Equals, true)
+	c.Assert(limiter.Allow(), Equals, false)
+	c.Assert(limiter.Allow(), Equals, false)
+}
diff --git a/pkg/policy/distillery_test.go b/pkg/policy/distillery_test.go
index 72c4596268..2d977eadd9 100644
--- a/pkg/policy/distillery_test.go
+++ b/pkg/policy/distillery_test.go
@@ -311,7 +311,7 @@ func (d *policyDistillery) WithLogBuffer(w io.Writer) *policyDistillery {
 
 // distillPolicy distills the policy repository into a set of bpf map state
 // entries for an endpoint with the specified labels.
-func (d *policyDistillery) distillPolicy(npMap NamedPortsMap, epLabels labels.LabelArray) (MapState, error) {
+func (d *policyDistillery) distillPolicy(owner PolicyOwner, epLabels labels.LabelArray) (MapState, error) {
 	result := make(MapState)
 
 	endpointSelected, _ := d.Repository.GetRulesMatching(epLabels)
@@ -339,7 +339,7 @@ func (d *policyDistillery) distillPolicy(npMap NamedPortsMap, epLabels labels.La
 	io.WriteString(d.log, "[distill] Producing L4 filter keys\n")
 	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(npMap, 0) {
+		for key, entry := range l4.ToMapState(owner, 0) {
 			io.WriteString(d.log, fmt.Sprintf("[distill] L4 ingress allow %+v (parser=%s, redirect=%t)\n", key, l4.L7Parser, entry.IsRedirectEntry()))
 			result[key] = entry
 		}
@@ -375,7 +375,7 @@ func Test_MergeL3(t *testing.T) {
 		t.Run(fmt.Sprintf("permutation_%d", tt.test), func(t *testing.T) {
 			logBuffer := new(bytes.Buffer)
 			repo = repo.WithLogBuffer(logBuffer)
-			mapstate, err := repo.distillPolicy(nil, labelsFoo)
+			mapstate, err := repo.distillPolicy(DummyOwner{}, labelsFoo)
 			if err != nil {
 				t.Errorf("Policy resolution failure: %s", err)
 			}
@@ -447,7 +447,7 @@ func Test_MergeRules(t *testing.T) {
 		t.Run(fmt.Sprintf("permutation_%d", tt.test), func(t *testing.T) {
 			logBuffer := new(bytes.Buffer)
 			repo = repo.WithLogBuffer(logBuffer)
-			mapstate, err := repo.distillPolicy(nil, labelsFoo)
+			mapstate, err := repo.distillPolicy(DummyOwner{}, labelsFoo)
 			if err != nil {
 				t.Errorf("Policy resolution failure: %s", err)
 			}
@@ -466,10 +466,6 @@ func Test_MergeRulesWithNamedPorts(t *testing.T) {
 	}
 	selectorCache := testNewSelectorCache(identityCache)
 
-	npMap := NamedPortsMap{
-		"port-80": NamedPort{Proto: uint8(0), Port: uint16(80)},
-	}
-
 	tests := []struct {
 		test   int
 		rules  api.Rules
@@ -523,7 +519,7 @@ func Test_MergeRulesWithNamedPorts(t *testing.T) {
 		t.Run(fmt.Sprintf("permutation_%d", tt.test), func(t *testing.T) {
 			logBuffer := new(bytes.Buffer)
 			repo = repo.WithLogBuffer(logBuffer)
-			mapstate, err := repo.distillPolicy(npMap, labelsFoo)
+			mapstate, err := repo.distillPolicy(DummyOwner{}, labelsFoo)
 			if err != nil {
 				t.Errorf("Policy resolution failure: %s", err)
 			}
@@ -564,7 +560,7 @@ func Test_AllowAll(t *testing.T) {
 		t.Run(fmt.Sprintf("permutation_%d", tt.test), func(t *testing.T) {
 			logBuffer := new(bytes.Buffer)
 			repo = repo.WithLogBuffer(logBuffer)
-			mapstate, err := repo.distillPolicy(nil, labelsFoo)
+			mapstate, err := repo.distillPolicy(DummyOwner{}, labelsFoo)
 			if err != nil {
 				t.Errorf("Policy resolution failure: %s", err)
 			}
diff --git a/pkg/policy/portmap_test.go b/pkg/policy/portmap_test.go
new file mode 100644
index 0000000000..d5091772a0
--- /dev/null
+++ b/pkg/policy/portmap_test.go
@@ -0,0 +1,196 @@
+// Copyright 2019 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 policy
+
+import (
+	"github.com/cilium/cilium/pkg/u8proto"
+
+	. "gopkg.in/check.v1"
+)
+
+func (ds *PolicyTestSuite) TestPolicyValidateName(c *C) {
+	name, err := ValidatePortName("Http")
+	c.Assert(err, IsNil)
+	c.Assert(name, Equals, "http")
+
+	name, err = ValidatePortName("dns-tcp")
+	c.Assert(err, IsNil)
+	c.Assert(name, Equals, "dns-tcp")
+
+	_, err = ValidatePortName("-http")
+	c.Assert(err, Not(IsNil))
+
+	_, err = ValidatePortName("http-")
+	c.Assert(err, Not(IsNil))
+
+	name, err = ValidatePortName("http-80")
+	c.Assert(err, IsNil)
+	c.Assert(name, Equals, "http-80")
+
+	_, err = ValidatePortName("http--s")
+	c.Assert(err, Not(IsNil))
+}
+
+func (ds *PolicyTestSuite) TestPolicyNewPortProto(c *C) {
+	np, err := newPortProto(80, "tcp")
+	c.Assert(err, IsNil)
+	c.Assert(np, Equals, PortProto{Port: uint16(80), Proto: uint8(6)})
+
+	_, err = newPortProto(88888, "tcp")
+	c.Assert(err, Not(IsNil))
+	c.Assert(err.Error(), Equals, "Port number 88888 out of 16-bit range")
+
+	_, err = newPortProto(80, "cccp")
+	c.Assert(err, Not(IsNil))
+	c.Assert(err.Error(), Equals, "unknown protocol 'cccp'")
+
+	np, err = newPortProto(88, "")
+	c.Assert(err, IsNil)
+	c.Assert(np, Equals, PortProto{Port: uint16(88), Proto: uint8(6)})
+}
+
+func (ds *PolicyTestSuite) TestPolicyNamedPortMap(c *C) {
+	npm := make(NamedPortMap)
+
+	err := npm.AddPort("http", 80, "tcp")
+	c.Assert(err, IsNil)
+	c.Assert(npm, HasLen, 1)
+
+	err = npm.AddPort("dns", 53, "UDP")
+	c.Assert(err, IsNil)
+	c.Assert(npm, HasLen, 2)
+
+	err = npm.AddPort("zero", 0, "TCP")
+	c.Assert(err, Equals, ErrNamedPortIsZero)
+	c.Assert(npm, HasLen, 2)
+
+	proto, err := u8proto.ParseProtocol("UDP")
+	c.Assert(err, IsNil)
+	c.Assert(uint8(proto), Equals, uint8(17))
+
+	port, err := npm.GetNamedPort("dns", uint8(proto))
+	c.Assert(err, IsNil)
+	c.Assert(port, Equals, uint16(53))
+
+	port, err = npm.GetNamedPort("dns", uint8(6))
+	c.Assert(err, Equals, ErrIncompatibleProtocol)
+	c.Assert(port, Equals, uint16(0))
+
+	port, err = npm.GetNamedPort("unknown", uint8(proto))
+	c.Assert(err, Equals, ErrUnknownNamedPort)
+	c.Assert(port, Equals, uint16(0))
+}
+
+func (ds *PolicyTestSuite) TestPolicyPortProtoSet(c *C) {
+	a := PortProtoSet{
+		PortProto{Port: 80, Proto: 6}:  struct{}{},
+		PortProto{Port: 443, Proto: 6}: struct{}{},
+		PortProto{Port: 53, Proto: 17}: struct{}{},
+	}
+	b := PortProtoSet{
+		PortProto{Port: 80, Proto: 6}:  struct{}{},
+		PortProto{Port: 443, Proto: 6}: struct{}{},
+		PortProto{Port: 53, Proto: 6}:  struct{}{},
+	}
+	c.Assert(a.Equal(a), Equals, true)
+	c.Assert(a.Equal(b), Equals, false)
+	c.Assert(b.Equal(b), Equals, true)
+}
+
+func (ds *PolicyTestSuite) TestPolicyNamedPortMultiMap(c *C) {
+	a := NamedPortMultiMap{
+		"http": PortProtoSet{
+			PortProto{Port: 80, Proto: 6}:   struct{}{},
+			PortProto{Port: 8080, Proto: 6}: struct{}{},
+		},
+		"https": PortProtoSet{
+			PortProto{Port: 443, Proto: 6}: struct{}{},
+		},
+		"zero": PortProtoSet{
+			PortProto{Port: 0, Proto: 6}: struct{}{},
+		},
+		"none": PortProtoSet{},
+		"dns": PortProtoSet{
+			PortProto{Port: 53, Proto: 17}: struct{}{},
+			PortProto{Port: 53, Proto: 6}:  struct{}{},
+		},
+	}
+	b := NamedPortMultiMap{
+		"http": PortProtoSet{
+			PortProto{Port: 80, Proto: 6}:   struct{}{},
+			PortProto{Port: 8080, Proto: 6}: struct{}{},
+		},
+		"https": PortProtoSet{
+			PortProto{Port: 443, Proto: 6}: struct{}{},
+		},
+		"zero": PortProtoSet{
+			PortProto{Port: 0, Proto: 6}: struct{}{},
+		},
+		"none": PortProtoSet{},
+		"dns": PortProtoSet{
+			PortProto{Port: 53, Proto: 0}: struct{}{},
+		},
+	}
+
+	c.Assert(a.Equal(a), Equals, true)
+	c.Assert(a.Equal(b), Equals, false)
+	c.Assert(b.Equal(b), Equals, true)
+
+	port, err := a.GetNamedPort("http", 6)
+	c.Assert(err, Equals, ErrDuplicateNamedPorts)
+	c.Assert(port, Equals, uint16(0))
+
+	port, err = a.GetNamedPort("http", 17)
+	c.Assert(err, Equals, ErrIncompatibleProtocol)
+	c.Assert(port, Equals, uint16(0))
+
+	port, err = a.GetNamedPort("zero", 6)
+	c.Assert(err, Equals, ErrNamedPortIsZero)
+	c.Assert(port, Equals, uint16(0))
+
+	port, err = a.GetNamedPort("none", 6)
+	c.Assert(err, Equals, ErrUnknownNamedPort)
+	c.Assert(port, Equals, uint16(0))
+
+	port, err = a.GetNamedPort("unknown", 6)
+	c.Assert(err, Equals, ErrUnknownNamedPort)
+	c.Assert(port, Equals, uint16(0))
+
+	port, err = NamedPortMultiMap(nil).GetNamedPort("unknown", 6)
+	c.Assert(err, Equals, ErrNilMap)
+	c.Assert(port, Equals, uint16(0))
+
+	port, err = a.GetNamedPort("https", 6)
+	c.Assert(err, IsNil)
+	c.Assert(port, Equals, uint16(443))
+
+	port, err = a.GetNamedPort("dns", 6)
+	c.Assert(err, IsNil)
+	c.Assert(port, Equals, uint16(53))
+
+	port, err = b.GetNamedPort("dns", 6)
+	c.Assert(err, IsNil)
+	c.Assert(port, Equals, uint16(53))
+
+	port, err = a.GetNamedPort("dns", 17)
+	c.Assert(err, IsNil)
+	c.Assert(port, Equals, uint16(53))
+
+	port, err = b.GetNamedPort("dns", 17)
+	c.Assert(err, IsNil)
+	c.Assert(port, Equals, uint16(53))
+}
diff --git a/pkg/policy/resolve_test.go b/pkg/policy/resolve_test.go
index b5f7dfdf9c..8554c96152 100644
--- a/pkg/policy/resolve_test.go
+++ b/pkg/policy/resolve_test.go
@@ -174,12 +174,16 @@ func GenerateCIDRRules(numRules int) api.Rules {
 
 type DummyOwner struct{}
 
-func (d DummyOwner) LookupRedirectPortLocked(npMap NamedPortsMap, l4 *L4Filter) uint16 {
-	// Return a fake non-0 listening port number for redirect filters.
-	if l4.IsRedirect() {
-		return 4242
-	}
-	return 0
+func (d DummyOwner) LookupRedirectPortLocked(bool, string, uint16) uint16 {
+	return 4242
+}
+
+func (d DummyOwner) GetNamedPort(ingress bool, name string, proto uint8) uint16 {
+	return 80
+}
+
+func (d DummyOwner) GetNamedPortLocked(ingress bool, name string, proto uint8) uint16 {
+	return 80
 }
 
 func (d DummyOwner) GetID() uint64 {
@@ -220,7 +224,7 @@ func (ds *PolicyTestSuite) BenchmarkRegenerateCIDRPolicyRules(c *C) {
 	c.ResetTimer()
 	for i := 0; i < c.N; i++ {
 		ip, _ := testRepo.resolvePolicyLocked(fooIdentity)
-		_ = ip.DistillPolicy(DummyOwner{}, nil, false)
+		_ = ip.DistillPolicy(DummyOwner{}, false)
 		ip.Detach()
 	}
 }
@@ -230,7 +234,7 @@ func (ds *PolicyTestSuite) BenchmarkRegenerateL3IngressPolicyRules(c *C) {
 	c.ResetTimer()
 	for i := 0; i < c.N; i++ {
 		ip, _ := testRepo.resolvePolicyLocked(fooIdentity)
-		_ = ip.DistillPolicy(DummyOwner{}, nil, false)
+		_ = ip.DistillPolicy(DummyOwner{}, false)
 		ip.Detach()
 	}
 }
@@ -240,7 +244,7 @@ func (ds *PolicyTestSuite) BenchmarkRegenerateL3EgressPolicyRules(c *C) {
 	c.ResetTimer()
 	for i := 0; i < c.N; i++ {
 		ip, _ := testRepo.resolvePolicyLocked(fooIdentity)
-		_ = ip.DistillPolicy(DummyOwner{}, nil, false)
+		_ = ip.DistillPolicy(DummyOwner{}, false)
 		ip.Detach()
 	}
 }
@@ -282,7 +286,7 @@ func (ds *PolicyTestSuite) TestL7WithIngressWildcard(c *C) {
 	defer repo.Mutex.RUnlock()
 	selPolicy, err := repo.resolvePolicyLocked(fooIdentity)
 	c.Assert(err, IsNil)
-	policy := selPolicy.DistillPolicy(DummyOwner{}, nil, false)
+	policy := selPolicy.DistillPolicy(DummyOwner{}, false)
 
 	expectedEndpointPolicy := EndpointPolicy{
 		selectorPolicy: &selectorPolicy{
@@ -371,7 +375,7 @@ func (ds *PolicyTestSuite) TestL7WithLocalHostWildcardd(c *C) {
 
 	selPolicy, err := repo.resolvePolicyLocked(fooIdentity)
 	c.Assert(err, IsNil)
-	policy := selPolicy.DistillPolicy(DummyOwner{}, nil, false)
+	policy := selPolicy.DistillPolicy(DummyOwner{}, false)
 
 	cachedSelectorHost := testSelectorCache.FindCachedIdentitySelector(api.ReservedEndpointSelectors[labels.IDNameHost])
 	c.Assert(cachedSelectorHost, Not(IsNil))
@@ -459,7 +463,7 @@ func (ds *PolicyTestSuite) TestMapStateWithIngressWildcard(c *C) {
 	defer repo.Mutex.RUnlock()
 	selPolicy, err := repo.resolvePolicyLocked(fooIdentity)
 	c.Assert(err, IsNil)
-	policy := selPolicy.DistillPolicy(DummyOwner{}, nil, false)
+	policy := selPolicy.DistillPolicy(DummyOwner{}, false)
 
 	rule1MapStateEntry := NewMapStateEntry(labels.LabelArrayList{ruleLabel}, false)
 	allowEgressMapStateEntry := NewMapStateEntry(labels.LabelArrayList{ruleLabelAllowAnyEgress}, false)
@@ -564,7 +568,7 @@ func (ds *PolicyTestSuite) TestMapStateWithIngress(c *C) {
 	defer repo.Mutex.RUnlock()
 	selPolicy, err := repo.resolvePolicyLocked(fooIdentity)
 	c.Assert(err, IsNil)
-	policy := selPolicy.DistillPolicy(DummyOwner{}, nil, false)
+	policy := selPolicy.DistillPolicy(DummyOwner{}, false)
 
 	// Add new identity to test accumulation of PolicyMapChanges
 	added1 := cache.IdentityCache{
diff --git a/pkg/policy/rule_test.go b/pkg/policy/rule_test.go
index ef5ab481bc..4153a995b9 100644
--- a/pkg/policy/rule_test.go
+++ b/pkg/policy/rule_test.go
@@ -24,7 +24,6 @@ import (
 	"github.com/cilium/cilium/api/v1/models"
 	"github.com/cilium/cilium/pkg/checker"
 	"github.com/cilium/cilium/pkg/identity"
-	identity2 "github.com/cilium/cilium/pkg/identity"
 	"github.com/cilium/cilium/pkg/labels"
 	"github.com/cilium/cilium/pkg/policy/api"
 	"github.com/cilium/cilium/pkg/policy/api/kafka"
@@ -2265,14 +2264,14 @@ func (ds *PolicyTestSuite) TestMatches(c *C) {
 	hostRule := repo.rules[1]
 
 	selectedEpLabels := labels.ParseSelectLabel("id=a")
-	selectedIdentity := identity2.NewIdentity(54321, labels.Labels{selectedEpLabels.Key: selectedEpLabels})
+	selectedIdentity := identity.NewIdentity(54321, labels.Labels{selectedEpLabels.Key: selectedEpLabels})
 
 	notSelectedEpLabels := labels.ParseSelectLabel("id=b")
-	notSelectedIdentity := identity2.NewIdentity(9876, labels.Labels{notSelectedEpLabels.Key: notSelectedEpLabels})
+	notSelectedIdentity := identity.NewIdentity(9876, labels.Labels{notSelectedEpLabels.Key: notSelectedEpLabels})
 
 	hostLabels := labels.Labels{selectedEpLabels.Key: selectedEpLabels}
 	hostLabels.MergeLabels(labels.LabelHost)
-	hostIdentity := identity2.NewIdentity(identity.ReservedIdentityHost, hostLabels)
+	hostIdentity := identity.NewIdentity(identity.ReservedIdentityHost, hostLabels)
 
 	// notSelectedEndpoint is not selected by rule, so we it shouldn't be added
 	// to EndpointsSelected.
diff --git a/pkg/proxy/logger/test/epinfo.go b/pkg/proxy/logger/test/epinfo.go
index 682d902078..e94a5feb6b 100644
--- a/pkg/proxy/logger/test/epinfo.go
+++ b/pkg/proxy/logger/test/epinfo.go
@@ -20,7 +20,6 @@ import (
 	"github.com/cilium/cilium/pkg/identity"
 	"github.com/cilium/cilium/pkg/labels"
 	"github.com/cilium/cilium/pkg/lock"
-	"github.com/cilium/cilium/pkg/policy"
 	"github.com/cilium/cilium/pkg/proxy/accesslog"
 )
 
@@ -41,14 +40,14 @@ func (m *ProxyUpdaterMock) GetProxyInfoByFields() (uint64, string, string, []str
 func (m *ProxyUpdaterMock) UnconditionalRLock() { m.RWMutex.RLock() }
 func (m *ProxyUpdaterMock) RUnlock()            { m.RWMutex.RUnlock() }
 
-func (m *ProxyUpdaterMock) GetID() uint64                               { return m.Id }
-func (m *ProxyUpdaterMock) GetIPv4Address() string                      { return m.Ipv4 }
-func (m *ProxyUpdaterMock) GetIPv6Address() string                      { return m.Ipv6 }
-func (m *ProxyUpdaterMock) GetLabels() []string                         { return m.Labels }
-func (m *ProxyUpdaterMock) GetIdentityLocked() identity.NumericIdentity { return m.Identity }
-func (m *ProxyUpdaterMock) ProxyID(npMap policy.NamedPortsMap, l4 *policy.L4Filter) (string, error) {
-	return "", nil
-}
+func (m *ProxyUpdaterMock) GetID() uint64                                 { return m.Id }
+func (m *ProxyUpdaterMock) GetIPv4Address() string                        { return m.Ipv4 }
+func (m *ProxyUpdaterMock) GetIPv6Address() string                        { return m.Ipv6 }
+func (m *ProxyUpdaterMock) GetLabels() []string                           { return m.Labels }
+func (m *ProxyUpdaterMock) GetEgressPolicyEnabledLocked() bool            { return true }
+func (m *ProxyUpdaterMock) GetIngressPolicyEnabledLocked() bool           { return true }
+func (m *ProxyUpdaterMock) GetIdentityLocked() identity.NumericIdentity   { return m.Identity }
+func (m *ProxyUpdaterMock) GetNamedPortLocked(bool, string, uint8) uint16 { return 0 }
 func (m *ProxyUpdaterMock) GetLabelsSHA() string {
 	return labels.NewLabelsFromModel(m.Labels).SHA256Sum()
 }
diff --git a/proxylib/proxylib_test.go b/proxylib/proxylib_test.go
index d7c636a67c..0b243411ac 100644
--- a/proxylib/proxylib_test.go
+++ b/proxylib/proxylib_test.go
@@ -355,6 +355,59 @@ func TestUnsupportedL7DropsGeneric(t *testing.T) {
 	CheckClose(t, 1, buf, 1)
 }
 
+func TestEnvoyL7DropsGeneric(t *testing.T) {
+	logServer := test.StartAccessLogServer("access_log.sock", 10)
+	defer logServer.Close()
+
+	mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug)
+	if mod == 0 {
+		t.Errorf("OpenModule() with access log path %s failed", logServer.Path)
+	} else {
+		defer CloseModule(mod)
+	}
+
+	insertPolicyText(t, mod, "1", []string{`
+		name: "FooBar"
+		policy: 2
+		ingress_per_port_policies: <
+		  port: 80
+		  rules: <
+		    remote_policies: 1
+		    remote_policies: 3
+		    remote_policies: 4
+		    l7_proto: "envoy.filter.network.test"
+		    l7_rules: <
+		      l7_allow_rules: <
+		        rule: <
+		          key: "action"
+		          value: "drop"
+		        >
+		      >
+		    >
+		  >
+		>
+		`})
+
+	// Using headertester parser
+	buf := CheckOnNewConnection(t, mod, "test.headerparser", 1, true, 1, 2, "1.1.1.1:34567", "2.2.2.2:80", "FooBar",
+		256, proxylib.OK, 1)
+
+	// Original direction data, drops with remaining data
+	line1, line2, line3, line4 := "Beginning----\n", "foo\n", "----End\n", "\n"
+	data := line1 + line2 + line3 + line4
+	CheckOnData(t, 1, false, false, &[][]byte{[]byte(data)}, []ExpFilterOp{
+		{proxylib.DROP, len(line1)},
+		{proxylib.DROP, len(line2)},
+		{proxylib.DROP, len(line3)},
+		{proxylib.DROP, len(line4)},
+	}, proxylib.OK, "Line dropped: "+line1+"Line dropped: "+line2+"Line dropped: "+line3+"Line dropped: "+line4)
+
+	expPasses, expDrops := 0, 4
+	checkAccessLogs(t, logServer, expPasses, expDrops)
+
+	CheckClose(t, 1, buf, 1)
+}
+
 func TestTwoRulesOnSamePortFirstNoL7(t *testing.T) {
 	logServer := test.StartAccessLogServer("access_log.sock", 10)
 	defer logServer.Close()
diff --git a/test/helpers/kubectl.go b/test/helpers/kubectl.go
index 42dc1e1bd7..3359192364 100644
--- a/test/helpers/kubectl.go
+++ b/test/helpers/kubectl.go
@@ -3282,9 +3282,11 @@ func (kub *Kubectl) GatherLogs(ctx context.Context) {
 		"kubectl get deployment --all-namespaces -o json":            "deployment.txt",
 		"kubectl get crd ciliumnetworkpolicies.cilium.io -o json":    "cilium-network-policies-crd.json",
 
-		fmt.Sprintf("kubectl get cm cilium-config -n %s -o json", GetCiliumNamespace(GetCurrentIntegration())):                                              "cilium-config.json",
-		fmt.Sprintf("kubectl logs -l k8s-app=cilium -n %s --timestamps --all-containers --tail -1", GetCiliumNamespace(GetCurrentIntegration())):            "cilium-combined-logs.txt",
-		fmt.Sprintf("kubectl logs -l k8s-app=cilium -n %s --timestamps --all-containers --previous --tail -1", GetCiliumNamespace(GetCurrentIntegration())): "cilium-combined-logs-previous.txt",
+		fmt.Sprintf("kubectl get cm cilium-config -n %s -o json", GetCiliumNamespace(GetCurrentIntegration())):                                                   "cilium-config.json",
+		fmt.Sprintf("kubectl logs -l k8s-app=cilium -n %s --timestamps -c clean-cilium-state --tail -1", GetCiliumNamespace(GetCurrentIntegration())):            "cilium-init-container-logs.txt",
+		fmt.Sprintf("kubectl logs -l k8s-app=cilium -n %s --timestamps -c clean-cilium-state --previous --tail -1", GetCiliumNamespace(GetCurrentIntegration())): "cilium-init-container-logs-previous.txt",
+		fmt.Sprintf("kubectl logs -l k8s-app=cilium -n %s --timestamps --all-containers --tail -1", GetCiliumNamespace(GetCurrentIntegration())):                 "cilium-combined-logs.txt",
+		fmt.Sprintf("kubectl logs -l k8s-app=cilium -n %s --timestamps --all-containers --previous --tail -1", GetCiliumNamespace(GetCurrentIntegration())):      "cilium-combined-logs-previous.txt",
 	}
 
 	kub.GeneratePodLogGatheringCommands(ctx, reportCmds)
diff --git a/test/kubernetes-test.sh b/test/kubernetes-test.sh
index f5a82665da..d191f20c98 100755
--- a/test/kubernetes-test.sh
+++ b/test/kubernetes-test.sh
@@ -73,4 +73,12 @@ export KUBE_MASTER=192.168.36.11
 export KUBE_MASTER_IP=192.168.36.11
 export KUBE_MASTER_URL="https://192.168.36.11:6443"
 
-${HOME}/go/bin/kubetest --provider=local --test --test_args="--ginkgo.focus=NetworkPolicy.* --e2e-verify-service-account=false --host ${KUBE_MASTER_URL} --ginkgo.skip=(should.allow.egress.access.to.server.in.CIDR.block)|(should.allow.ingress.access.from.updated.pod)|(should.not.allow.access.by.TCP.when.a.policy.specifies.only.SCTP)"
+# We currently skip the following tests:
+# should not allow access by TCP when a policy specifies only SCTP
+#  - Cilium does not support SCTP yet
+# should allow egress access to server in CIDR block and
+# should ensure an IP overlapping both IPBlock.CIDR and IPBlock.Except is allowed
+#  - TL;DR Cilium does not allow to specify pod CIDRs as part of the policy
+#    because it conflicts with the pod's security identity.
+#  - More info at https://github.com/cilium/cilium/issues/9209
+${HOME}/go/bin/kubetest --provider=local --test --test_args="--ginkgo.focus=NetworkPolicy.* --e2e-verify-service-account=false --host ${KUBE_MASTER_URL} --ginkgo.skip=(should.ensure.an.IP.overlapping.both.IPBlock.CIDR.and.IPBlock.Except.is.allowed)|(should.allow.egress.access.to.server.in.CIDR.block)|(should.not.allow.access.by.TCP.when.a.policy.specifies.only.SCTP)"
diff --git a/test/make-images-push-to-local-registry.sh b/test/make-images-push-to-local-registry.sh
index 09cb089ac1..624a04e9de 100755
--- a/test/make-images-push-to-local-registry.sh
+++ b/test/make-images-push-to-local-registry.sh
@@ -3,7 +3,7 @@
 set -e
 
 cd ..
-DOCKER_BUILDKIT=1 make docker-image DOCKER_IMAGE_TAG="$2"
+DOCKER_BUILDKIT=1 make docker-image DOCKER_IMAGE_TAG="$2" DOCKER_FLAGS="$3"
 
 docker tag "cilium/cilium:$2" "$1/cilium/cilium:$2"
 docker tag "cilium/cilium:$2" "$1/cilium/cilium-dev:$2"
diff --git a/test/provision/k8s_install.sh b/test/provision/k8s_install.sh
index 35f83c43c4..8e5dffb745 100755
--- a/test/provision/k8s_install.sh
+++ b/test/provision/k8s_install.sh
@@ -309,7 +309,7 @@ case $K8S_VERSION in
         sudo apt-get install -y conntrack
         KUBERNETES_CNI_VERSION="0.8.6"
         KUBERNETES_CNI_OS="-linux"
-        K8S_FULL_VERSION="1.19.0-rc.3"
+        K8S_FULL_VERSION="1.19.0-rc.4"
         KUBEADM_OPTIONS="--ignore-preflight-errors=cri"
         KUBEADM_SLAVE_OPTIONS="--discovery-token-unsafe-skip-ca-verification --ignore-preflight-errors=cri,SystemVerification"
         sudo ln -sf $COREDNS_DEPLOYMENT $DNS_DEPLOYMENT
diff --git a/test/test_suite_test.go b/test/test_suite_test.go
index 1dc05fa6fc..43c3997ec1 100644
--- a/test/test_suite_test.go
+++ b/test/test_suite_test.go
@@ -26,7 +26,6 @@ import (
 	"github.com/cilium/cilium/pkg/logging"
 	"github.com/cilium/cilium/test/config"
 	. "github.com/cilium/cilium/test/ginkgo-ext"
-	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"
 	"github.com/cilium/cilium/test/helpers"
 	"github.com/cilium/cilium/test/logger"
 	gops "github.com/google/gops/agent"
@@ -69,7 +68,7 @@ func configLogsOutput() {
 	log.Formatter = &logger.Formatter
 	log.Hooks.Add(&logger.LogHook{})
 
-	ginkgoext.GinkgoWriter = NewWriter(log.Out)
+	GinkgoWriter = NewWriter(log.Out)
 }
 
 func ShowCommands() {
@@ -77,7 +76,7 @@ func ShowCommands() {
 		return
 	}
 
-	helpers.SSHMetaLogs = ginkgoext.NewWriter(os.Stdout)
+	helpers.SSHMetaLogs = NewWriter(os.Stdout)
 }
 
 func TestTest(t *testing.T) {
@@ -106,7 +105,7 @@ func TestTest(t *testing.T) {
 	} else {
 		RegisterFailHandler(Fail)
 	}
-	junitReporter := ginkgoext.NewJUnitReporter(fmt.Sprintf(
+	junitReporter := NewJUnitReporter(fmt.Sprintf(
 		"%s.xml", helpers.GetScopeWithVersion()))
 	RunSpecsWithDefaultAndCustomReporters(
 		t, fmt.Sprintf("Suite-%s", helpers.GetScopeWithVersion()),
@@ -156,7 +155,7 @@ func reportCreateVMFailure(vm string, err error) {
 
         =======================================================================
         `, vm, err)
-	ginkgoext.GinkgoPrint(failmsg)
+	GinkgoPrint(failmsg)
 	Fail(failmsg)
 }
 
@@ -326,7 +325,7 @@ var _ = AfterEach(func() {
 	if ginkgo.CurrentGinkgoTestDescription().Failed && helpers.IsRunningOnJenkins() {
 		// ReportDirectory is already created. No check the error
 		path, _ := helpers.CreateReportDirectory()
-		zipFileName := fmt.Sprintf("%s_%s.zip", helpers.MakeUID(), ginkgoext.GetTestName())
+		zipFileName := fmt.Sprintf("%s_%s.zip", helpers.MakeUID(), GetTestName())
 		zipFilePath := filepath.Join(helpers.TestResultsPath, zipFileName)
 
 		_, err := exec.Command(
@@ -336,7 +335,7 @@ var _ = AfterEach(func() {
 			log.WithError(err).Errorf("cannot create zip file '%s'", zipFilePath)
 		}
 
-		ginkgoext.GinkgoPrint("[[ATTACHMENT|%s]]", zipFileName)
+		GinkgoPrint("[[ATTACHMENT|%s]]", zipFileName)
 	}
 
 	if !ginkgo.CurrentGinkgoTestDescription().Failed && helpers.IsRunningOnJenkins() {
diff --git a/tests/sql-proxy.sh b/tests/sql-proxy.sh
new file mode 100755
index 0000000000..9bd72b5a3d
--- /dev/null
+++ b/tests/sql-proxy.sh
@@ -0,0 +1,132 @@
+#!/bin/bash
+
+dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+source "${dir}/helpers.bash"
+# dir might have been overwritten by helpers.bash
+dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+
+TEST_NAME=$(get_filename_without_extension $0)
+LOGS_DIR="${dir}/cilium-files/${TEST_NAME}/logs"
+redirect_debug_logs ${LOGS_DIR}
+
+set -ex
+
+function cleanup {
+  monitor_stop
+  cilium policy delete --all 2> /dev/null || true
+  docker rm -f mysql-server mysql-client 2> /dev/null || true
+}
+
+function finish_test {
+  echo cleanup
+}
+
+trap finish_test EXIT
+
+SERVER_LABEL="mysql-server"
+CLIENT_LABEL="mysql-client"
+TAG="5.5"
+
+CLIENT_RUN="docker run --rm -t --net=cilium --name mysql-client -l mysql-client mysql:$TAG mysql -ucilium -pcilium --disable-ssl"
+cleanup
+logs_clear
+
+function proxy_init {
+  log "beginning proxy_init"
+  create_cilium_docker_network
+
+  docker run -dt --net=cilium --name mysql-server -l $SERVER_LABEL -e MYSQL_ROOT_PASSWORD=cilium --publish 6603:3306 mysql:$TAG --disable-ssl
+  wait_for_docker_ipv6_addr mysql-server
+
+  log "waiting for all 3 endpoints to get an identity"
+  while ! cilium endpoint list -o jsonpath='{range [*]}{.status.identity.id}{" "}{.status.identity.labels}{"\n"}' | grep '^[0-9].*mysql-server' ; do
+    log "waiting..."
+    sleep 1
+  done
+
+  echo "probing until mysql-server is responsive"
+  until docker exec -i mysql-server mysql -uroot -pcilium -e "SHOW DATABASES" 2>/dev/null >/dev/null; do
+      echo "."
+      sleep 1
+  done
+  
+  echo "Creating user"
+  docker exec -i mysql-server mysql -uroot -pcilium -e "CREATE USER 'cilium'@'%' IDENTIFIED BY 'cilium';"
+  echo "Granting privileges"
+  docker exec -i mysql-server mysql -uroot -pcilium -e "GRANT ALL ON *.* TO 'cilium'@'%'; FLUSH PRIVILEGES;"
+
+  SERVER_IP4=$(docker inspect --format '{{ .NetworkSettings.Networks.cilium.IPAddress }}' mysql-server)
+
+  echo "Testing client without policy"
+  $CLIENT_RUN -h$SERVER_IP4 -e "SELECT host FROM mysql.user WHERE User = 'cilium';"
+
+  monitor_start
+  log "finished proxy_init"
+}
+
+function policy_single_egress {
+  cilium policy delete --all
+  cat <<EOF | policy_import_and_wait -
+[{
+    "endpointSelector": {"matchLabels":{"id.server":""}},
+    "ingress": [{
+        "fromEndpoints": [
+	    {"matchLabels":{"reserved:host":""}},
+	    {"matchLabels":{"mysql-server":""}}
+	]
+    }]
+},{
+    "endpointSelector": {"matchLabels":{"mysql-client":""}},
+    "egress": [{
+	"toPorts": [{
+	    "ports": [{"port": "3306", "protocol": "TCP"}],
+	    "rules": {
+	        "l7proto": "envoy.filters.network.mysql_proxy",
+		"l7": [{
+		    "action": "deny",
+		    "user.mysql": "select"
+		}]
+	    }
+	}]
+    }]
+}]
+EOF
+}
+
+function proxy_test {
+  log "beginning MySQL proxy test"
+  monitor_clear
+
+  log "trying to reach MySQL server at $SERVER_IP4 from client"
+  if $CLIENT_RUN -h$SERVER_IP4 -e "SHOW DATABASES;"; then
+      echo "Success"
+  else
+      abort "MySQL query failed"  
+  fi
+
+  log "trying to select denied table at $SERVER_IP4 from client"
+  if $CLIENT_RUN -h$SERVER_IP4 -e "SELECT host FROM mysql.user;"; then
+      abort "MySQL query should have failed, but it succeeded"
+  else
+      echo "MySQL query failed as expected"  
+  fi
+
+  monitor_dump
+
+  log "finished MySQL proxy test"
+}
+
+proxy_init
+
+policy_single_egress
+
+proxy_test
+
+# Leave test setup behind for manual testing
+#
+# log "deleting all policies from Cilium"
+# cilium policy delete --all 2> /dev/null || true
+# log "removing containers"
+# docker rm -f mysql-server mysql-client 2> /dev/null || true
+
+test_succeeded "${TEST_NAME}"