Repository: cilium/cilium
Pull request #18502 (2022-01-17 15:44:10)

datapath: Remove !CONNTRACK
99% of Cilium functionality depends on CONNTRACK being enabled, so it
doesn't make sense to complicate our codebase with handling !CONNTRACK
case.

brb: /test

Patch:
diff --git a/Documentation/concepts/networking/masquerading.rst b/Documentation/concepts/networking/masquerading.rst
index 2c86347fbc..b6d6d5718b 100644
--- a/Documentation/concepts/networking/masquerading.rst
+++ b/Documentation/concepts/networking/masquerading.rst
@@ -74,10 +74,19 @@ The eBPF-based masquerading can masquerade packets of the following IPv4 L4 prot
 - UDP
 - ICMP (only Echo request and Echo reply)
 
-By default, any packet from a pod destined to an IP address outside of the
-``ipv4-native-routing-cidr`` range is masqueraded. The exclusion CIDR is shown
-in the above output of ``cilium status`` (``10.0.0.0/16``).  To allow more
-fine-grained control, Cilium implements `ip-masq-agent
+By default, all packets from a pod destined to an IP address outside of the
+``ipv4-native-routing-cidr`` range are masqueraded, except for packets destined
+to other cluster nodes. The exclusion CIDR is shown in the above output of
+``cilium status`` (``10.0.0.0/16``).
+
+.. note::
+
+    When eBPF-masquerading is enabled, traffic from pods to the External IP of
+    cluster nodes will also not be masqueraded. The eBPF implementation differs
+    from the iptables-based masquerading on that aspect. This limitation is
+    tracked at :gh-issue:`17177`.
+
+To allow more fine-grained control, Cilium implements `ip-masq-agent
 <https://github.com/kubernetes-sigs/ip-masq-agent>`_ in eBPF which can be
 enabled with the ``ipMasqAgent.enabled=true`` helm option.
 
diff --git a/Documentation/gettingstarted/k8s-install-default.rst b/Documentation/gettingstarted/k8s-install-default.rst
index a8ecfb6749..92bb1afdc9 100644
--- a/Documentation/gettingstarted/k8s-install-default.rst
+++ b/Documentation/gettingstarted/k8s-install-default.rst
@@ -149,7 +149,7 @@ to create a Kubernetes cluster locally or using a managed Kubernetes service:
        <https://github.com/weaveworks/eksctl>`_ for instructions on how to
        install ``eksctl`` and prepare your account.
 
-       .. code-block:: shell-session
+       .. code-block:: none
 
            export NAME="$(whoami)-$RANDOM"
            cat <<EOF >eks-config.yaml
@@ -164,8 +164,8 @@ to create a Kubernetes cluster locally or using a managed Kubernetes service:
            - name: ng-1
              desiredCapacity: 2
              privateNetworking: true
-             ## taint nodes so that application pods are
-             ## not scheduled until Cilium is deployed.
+             # taint nodes so that application pods are
+             # not scheduled until Cilium is deployed.
              taints:
               - key: "node.cilium.io/agent-not-ready"
                 value: "true"
@@ -229,7 +229,7 @@ You can install Cilium on any Kubernetes cluster. Pick one of the options below:
 
           cilium install
 
-    .. group-tab:: GCP/GKE
+    .. group-tab:: GKE
 
        .. include:: requirements-gke.rst
 
@@ -241,7 +241,7 @@ You can install Cilium on any Kubernetes cluster. Pick one of the options below:
 
            cilium install
 
-    .. group-tab:: Azure/AKS
+    .. group-tab:: AKS
 
        .. include:: requirements-aks.rst
 
@@ -253,7 +253,7 @@ You can install Cilium on any Kubernetes cluster. Pick one of the options below:
 
            cilium install --azure-resource-group "${AZURE_RESOURCE_GROUP}"
 
-    .. group-tab:: AWS/EKS
+    .. group-tab:: EKS
 
        .. include:: requirements-eks.rst
 
diff --git a/Documentation/gettingstarted/k8s-install-helm.rst b/Documentation/gettingstarted/k8s-install-helm.rst
index 4ac924cae7..e8fa58a598 100644
--- a/Documentation/gettingstarted/k8s-install-helm.rst
+++ b/Documentation/gettingstarted/k8s-install-helm.rst
@@ -48,7 +48,7 @@ Install Cilium
           helm install cilium |CHART_RELEASE| \\
             --namespace kube-system
 
-    .. group-tab:: GCP/GKE
+    .. group-tab:: GKE
 
        .. include:: requirements-gke.rst
 
@@ -81,7 +81,7 @@ Install Cilium
        * Reconfigure kubelet to run in CNI mode
        * Mount the eBPF filesystem
 
-    .. group-tab:: Azure/AKS
+    .. group-tab:: AKS
 
        .. include:: requirements-aks.rst
 
@@ -142,7 +142,7 @@ Install Cilium
             --set enableIPv4Masquerade=false \\
             --set nodeinit.enabled=true
 
-    .. group-tab:: AWS/EKS
+    .. group-tab:: EKS
 
        .. include:: requirements-eks.rst
 
diff --git a/Documentation/gettingstarted/requirements-eks.rst b/Documentation/gettingstarted/requirements-eks.rst
index e2c8af25e6..536c90d940 100644
--- a/Documentation/gettingstarted/requirements-eks.rst
+++ b/Documentation/gettingstarted/requirements-eks.rst
@@ -16,35 +16,32 @@ For more information on AWS ENI mode, see :ref:`ipam_eni`.
    If you want to chain Cilium on top of the AWS CNI, refer to the guide
    :ref:`chaining_aws_cni`.
 
-The following command creates a Kubernetes cluster with ``eksctl``
-using `Amazon Elastic Kubernetes Service
-<https://aws.amazon.com/eks/>`_.  See `eksctl Installation
-<https://github.com/weaveworks/eksctl>`_ for instructions on how to
-install ``eksctl`` and prepare your account.
-
-.. code-block:: shell-session
-
-   export NAME="$(whoami)-$RANDOM"
-   cat <<EOF >eks-config.yaml
-   apiVersion: eksctl.io/v1alpha5
-   kind: ClusterConfig
-
-   metadata:
-     name: ${NAME}
-     region: eu-west-1
-
-   managedNodeGroups:
-   - name: ng-1
-     desiredCapacity: 2
-     privateNetworking: true
-     ## taint nodes so that application pods are
-     ## not scheduled until Cilium is deployed.
-     taints:
-      - key: "node.cilium.io/agent-not-ready"
-        value: "true"
-        effect: "NoSchedule"
-   EOF
-   eksctl create cluster -f ./eks-config.yaml
+**Requirements:**
+
+* The `EKS Managed Nodegroups <https://eksctl.io/usage/eks-managed-nodes>`_ must
+  be properly tainted to ensure applications pods are properly managed by
+  Cilium:
+
+  * ``managedNodeGroups`` must be tainted with
+    ``node.cilium.io/agent-not-ready=true:NoSchedule`` to ensure application
+    pods will only be scheduled once Cilium is ready to manage them. For
+    example, when using a `ClusterConfig <https://eksctl.io/usage/creating-and-managing-clusters/#using-config-files>`_
+    file to create the cluster:
+
+    .. code-block:: yaml
+
+        apiVersion: eksctl.io/v1alpha5
+        kind: ClusterConfig
+        ...
+        managedNodeGroups:
+        - name: ng-1
+          ...
+          # taint nodes so that application pods are
+          # not scheduled until Cilium is deployed.
+          taints:
+           - key: "node.cilium.io/agent-not-ready"
+             value: "true"
+             effect: "NoSchedule"
 
 **Limitations:**
 
diff --git a/Documentation/helm-values.rst b/Documentation/helm-values.rst
index 711dd5ce35..82c324df1b 100644
--- a/Documentation/helm-values.rst
+++ b/Documentation/helm-values.rst
@@ -285,6 +285,10 @@
      - Install the CNI configuration and binary files into the filesystem.
      - bool
      - ``true``
+   * - cni.logFile
+     - Configure the log file for CNI logging with retention policy of 7 days. Disable CNI file logging by setting this field to empty explicitly.
+     - string
+     - ``"/var/run/cilium/cilium-cni.log"``
    * - containerRuntime
      - Configure container runtime specific integration.
      - object
@@ -350,7 +354,7 @@
      - bool
      - ``true``
    * - enableXTSocketFallback
-     - Enables the fallback compatibility solution for when the xt_socket kernel module is missing and it is needed for the datapath L7 redirection to work properly. See documentation for details on when this can be disabled: http://docs.cilium.io/en/stable/install/system_requirements/#admin-kernel-version.
+     - Enables the fallback compatibility solution for when the xt_socket kernel module is missing and it is needed for the datapath L7 redirection to work properly. See documentation for details on when this can be disabled: https://docs.cilium.io/en/stable/operations/system_requirements/#linux-kernel.
      - bool
      - ``true``
    * - encryption.enabled
@@ -610,7 +614,7 @@
      - string
      - ``":4244"``
    * - hubble.metrics
-     - Hubble metrics configuration. See https://docs.cilium.io/en/stable/configuration/metrics/#hubble-metrics for more comprehensive documentation about Hubble metrics.
+     - Hubble metrics configuration. See https://docs.cilium.io/en/stable/operations/metrics/#hubble-metrics for more comprehensive documentation about Hubble metrics.
      - object
      - ``{"enabled":null,"port":9091,"serviceAnnotations":{},"serviceMonitor":{"enabled":false,"labels":{}}}``
    * - hubble.metrics.enabled
diff --git a/Documentation/operations/performance/tuning.rst b/Documentation/operations/performance/tuning.rst
index 1b37ff5110..954609de68 100644
--- a/Documentation/operations/performance/tuning.rst
+++ b/Documentation/operations/performance/tuning.rst
@@ -51,7 +51,7 @@ bypassing the iptables connection tracker.
 * Kernel >= 4.19.57, >= 5.1.16, >= 5.2
 * Direct-routing configuration
 * eBPF-based kube-proxy replacement
-* eBPF masquerading
+* eBPF-based masquerading or no masquerading
 
 To enable the iptables connection-tracking bypass:
 
diff --git a/Documentation/operations/upgrade.rst b/Documentation/operations/upgrade.rst
index 46ecb78e9c..aec0fa633d 100644
--- a/Documentation/operations/upgrade.rst
+++ b/Documentation/operations/upgrade.rst
@@ -136,8 +136,8 @@ one stable release to a later stable release.
 
 .. include:: upgrade-warning.rst
 
-Step 1: Upgrade to latest patch version (Recommended)
------------------------------------------------------
+Step 1: Upgrade to latest patch version
+---------------------------------------
 
 When upgrading from one minor release to another minor release, for example
 1.x to 1.y, it is recommended to upgrade to the latest patch release for a
diff --git a/Documentation/spelling_wordlist.txt b/Documentation/spelling_wordlist.txt
index 1b1d5384e8..7e392b46ff 100644
--- a/Documentation/spelling_wordlist.txt
+++ b/Documentation/spelling_wordlist.txt
@@ -572,6 +572,7 @@ loadbalancing
 localRedirectPolicy
 localhost
 lockless
+logFile
 logSystemLoad
 logfile
 login
diff --git a/Makefile.docker b/Makefile.docker
index 320cb204ec..ef3361b1c7 100644
--- a/Makefile.docker
+++ b/Makefile.docker
@@ -78,11 +78,11 @@ endif
 define DOCKER_IMAGE_TEMPLATE
 .PHONY: $(1)
 $(1): GIT_VERSION $(2) $(2).dockerignore GIT_VERSION builder-info
-	$(eval IMAGE_NAME := $(subst %,$$$$*,$(3))$(UNSTRIPPED))
+	$(eval IMAGE_NAME := $(subst %,$$$$*,$(3)))
 	$(QUIET) $(CONTAINER_ENGINE) buildx build -f $(subst %,$$*,$(2)) \
 		$(DOCKER_FLAGS) \
 		--build-arg BASE_IMAGE=${BASE_IMAGE} \
-		--build-arg NOSTRIP=${NOSTRIP} \
+		--build-arg NOSTRIP=$${NOSTRIP} \
 		--build-arg NOOPT=${NOOPT} \
 		--build-arg LOCKDEBUG=${LOCKDEBUG} \
 		--build-arg RACE=${RACE}\
@@ -90,7 +90,7 @@ $(1): GIT_VERSION $(2) $(2).dockerignore GIT_VERSION builder-info
 		--build-arg LIBNETWORK_PLUGIN=${LIBNETWORK_PLUGIN} \
 		--build-arg CILIUM_SHA=$(firstword $(GIT_VERSION)) \
 		--build-arg OPERATOR_VARIANT=$(IMAGE_NAME) \
-		-t $(IMAGE_REPOSITORY)/$(IMAGE_NAME):$(4) .
+		-t $(IMAGE_REPOSITORY)/$(IMAGE_NAME)$${UNSTRIPPED}:$(4) .
 ifeq ($(findstring --push,$(DOCKER_FLAGS)),)
 	@echo 'Define "DOCKER_FLAGS=--push" to push the build results.'
 else
@@ -101,6 +101,7 @@ endif
 $(1)-unstripped: NOSTRIP=1
 $(1)-unstripped: UNSTRIPPED=-unstripped
 $(1)-unstripped: $(1)
+	@echo
 endef
 
 # docker-cilium-image
diff --git a/bpf/ep_config.h b/bpf/ep_config.h
index 3b0029b370..1084d2fd6c 100644
--- a/bpf/ep_config.h
+++ b/bpf/ep_config.h
@@ -42,6 +42,5 @@ DEFINE_U32(POLICY_VERDICT_LOG_FILTER, 0xffff);
 #define CALLS_MAP test_cilium_calls_65535
 #define CUSTOM_CALLS_MAP test_cilium_calls_custom_65535
 #define LOCAL_DELIVERY_METRICS
-#define CONNTRACK
 #define CONNTRACK_ACCOUNTING
 #define DIRECT_ROUTING_DEV_IFINDEX 0
diff --git a/bpf/lib/conntrack.h b/bpf/lib/conntrack.h
index 59418623b1..26e10792eb 100644
--- a/bpf/lib/conntrack.h
+++ b/bpf/lib/conntrack.h
@@ -18,7 +18,6 @@
 #include "nat46.h"
 #include "signal.h"
 
-#ifdef CONNTRACK
 enum {
 	ACTION_UNSPEC,
 	ACTION_CREATE,
@@ -1042,99 +1041,5 @@ static __always_inline int ct_create4(const void *map_main,
 	}
 	return 0;
 }
-#else /* !CONNTRACK */
-static __always_inline int
-ct_lookup6(const void *map __maybe_unused,
-	   struct ipv6_ct_tuple *tuple __maybe_unused,
-	   struct __ctx_buff *ctx __maybe_unused, int off __maybe_unused,
-	   int dir __maybe_unused, struct ct_state *ct_state __maybe_unused,
-	   __u32 *monitor __maybe_unused)
-{
-	return 0;
-}
-
-static __always_inline int
-ct_is_reply4(const void *map __maybe_unused,
-	     struct __ctx_buff *ctx __maybe_unused, int off __maybe_unused,
-	     struct ipv4_ct_tuple *tuple __maybe_unused,
-	     bool *is_reply __maybe_unused)
-{
-	return 0;
-}
-
-static __always_inline int
-ct_lookup4(const void *map __maybe_unused,
-	   struct ipv4_ct_tuple *tuple __maybe_unused,
-	   struct __ctx_buff *ctx __maybe_unused, int off __maybe_unused,
-	   int dir __maybe_unused, struct ct_state *ct_state __maybe_unused,
-	   __u32 *monitor __maybe_unused)
-{
-	return 0;
-}
-
-static __always_inline void
-ct_update6_backend_id(const void *map __maybe_unused,
-		      const struct ipv6_ct_tuple *tuple __maybe_unused,
-		      const struct ct_state *state __maybe_unused)
-{
-}
-
-static __always_inline void
-ct_update6_rev_nat_index(const void *map __maybe_unused,
-			 const struct ipv6_ct_tuple *tuple __maybe_unused,
-			 const struct ct_state *state __maybe_unused)
-{
-}
-
-static __always_inline void
-ct_update6_dsr(const void *map __maybe_unused,
-	       const struct ipv6_ct_tuple *tuple __maybe_unused,
-	       const bool dsr __maybe_unused)
-{
-}
-
-static __always_inline int
-ct_create6(const void *map_main __maybe_unused,
-	   const void *map_related __maybe_unused,
-	   struct ipv6_ct_tuple *tuple __maybe_unused,
-	   struct __ctx_buff *ctx __maybe_unused, const int dir __maybe_unused,
-	   struct ct_state *ct_state __maybe_unused,
-	   bool from_proxy __maybe_unused)
-{
-	return 0;
-}
-
-static __always_inline void
-ct_update4_backend_id(const void *map __maybe_unused,
-		      const struct ipv4_ct_tuple *tuple __maybe_unused,
-		      const struct ct_state *state __maybe_unused)
-{
-}
-
-static __always_inline void
-ct_update4_rev_nat_index(const void *map __maybe_unused,
-			 const struct ipv4_ct_tuple *tuple __maybe_unused,
-			 const struct ct_state *state __maybe_unused)
-{
-}
-
-static __always_inline void
-ct_update4_dsr(const void *map __maybe_unused,
-	       const struct ipv4_ct_tuple *tuple __maybe_unused,
-	       const bool dsr __maybe_unused)
-{
-}
-
-static __always_inline int
-ct_create4(const void *map_main __maybe_unused,
-	   const void *map_related __maybe_unused,
-	   struct ipv4_ct_tuple *tuple __maybe_unused,
-	   struct __ctx_buff *ctx __maybe_unused, const int dir __maybe_unused,
-	   const struct ct_state *ct_state __maybe_unused,
-	   bool proxy_redirect __maybe_unused)
-{
-	return 0;
-}
 
-#endif /* CONNTRACK */
 #endif /* __LIB_CONNTRACK_H_ */
diff --git a/bpf/lib/nat.h b/bpf/lib/nat.h
index ccbea36d91..72b154d12d 100644
--- a/bpf/lib/nat.h
+++ b/bpf/lib/nat.h
@@ -1062,7 +1062,6 @@ void snat_v6_delete_tuples(struct ipv6_ct_tuple *tuple __maybe_unused)
 }
 #endif
 
-#ifdef CONNTRACK
 static __always_inline __maybe_unused void
 ct_delete4(const void *map, struct ipv4_ct_tuple *tuple, struct __ctx_buff *ctx)
 {
@@ -1086,20 +1085,5 @@ ct_delete6(const void *map, struct ipv6_ct_tuple *tuple, struct __ctx_buff *ctx)
 	else
 		snat_v6_delete_tuples(tuple);
 }
-#else
-static __always_inline __maybe_unused void
-ct_delete4(const void *map __maybe_unused,
-	   struct ipv4_ct_tuple *tuple __maybe_unused,
-	   struct __ctx_buff *ctx __maybe_unused)
-{
-}
-
-static __always_inline __maybe_unused void
-ct_delete6(const void *map __maybe_unused,
-	   struct ipv6_ct_tuple *tuple __maybe_unused,
-	   struct __ctx_buff *ctx __maybe_unused)
-{
-}
-#endif
 
 #endif /* __LIB_NAT__ */
diff --git a/bpf/lib/nat46.h b/bpf/lib/nat46.h
index 7230d34654..2ac5bd0f74 100644
--- a/bpf/lib/nat46.h
+++ b/bpf/lib/nat46.h
@@ -15,7 +15,7 @@
 
 #if defined(ENABLE_NAT46) && \
     (!defined(ENABLE_IPV4) || !defined(ENABLE_IPV6) || \
-     !defined(CONNTRACK) || !defined(ENABLE_HOST_REDIRECT))
+     !defined(ENABLE_HOST_REDIRECT))
 #error "ENABLE_NAT46 requisite options are not configured, see lib/nat46.h."
 #endif
 
diff --git a/bpf/node_config.h b/bpf/node_config.h
index f9937224cd..da9c013901 100644
--- a/bpf/node_config.h
+++ b/bpf/node_config.h
@@ -133,7 +133,6 @@ DEFINE_IPV6(HOST_IP, 0xbe, 0xef, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0xa, 0x
 #define CT_MAP_ANY4 test_cilium_ct_any4_65535
 #define CT_MAP_SIZE_TCP 4096
 #define CT_MAP_SIZE_ANY 4096
-#define CONNTRACK
 #define CONNTRACK_ACCOUNTING
 #define LB4_HEALTH_MAP test_cilium_lb4_health
 #define LB6_HEALTH_MAP test_cilium_lb6_health
diff --git a/cilium/cmd/metrics_list.go b/cilium/cmd/metrics_list.go
index b459f8dc64..22ede7a51a 100644
--- a/cilium/cmd/metrics_list.go
+++ b/cilium/cmd/metrics_list.go
@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"os"
 	"regexp"
+	"sort"
 	"strings"
 	"text/tabwriter"
 
@@ -54,8 +55,13 @@ var MetricsListCmd = &cobra.Command{
 			label := ""
 			if len(metric.Labels) > 0 {
 				labelArray := []string{}
-				for key, value := range metric.Labels {
-					labelArray = append(labelArray, fmt.Sprintf(`%s="%s"`, key, value))
+				keys := make([]string, 0, len(metric.Labels))
+				for k := range metric.Labels {
+					keys = append(keys, k)
+				}
+				sort.Strings(keys)
+				for _, k := range keys {
+					labelArray = append(labelArray, fmt.Sprintf(`%s="%s"`, k, metric.Labels[k]))
 				}
 				label = strings.Join(labelArray, " ")
 			}
diff --git a/contrib/vagrant/start.sh b/contrib/vagrant/start.sh
index 377f7930ca..ed0f83e0ac 100755
--- a/contrib/vagrant/start.sh
+++ b/contrib/vagrant/start.sh
@@ -613,8 +613,12 @@ function createVm(){
     else
         vagrant up $PROVISION_ARGS $1
         if [ "$?" -eq "0" -a -n "${K8S}" ]; then
-            host_port=$(vagrant port --guest 6443 k8s1)
-            vagrant ssh k8s1 -- cat /home/vagrant/.kube/config | sed "s;server:.*:6443;server: https://k8s1:$host_port;g" > vagrant.kubeconfig
+            hostname=k8s1
+            if [ ! -z "$NETNEXT" -a "$NETNEXT" = "true" -o "$NETNEXT" = "1" ]; then
+                hostname=k8s1+
+            fi
+            host_port=$(vagrant port --guest 6443 $hostname)
+            vagrant ssh $hostname -- cat /home/vagrant/.kube/config | sed "s;server:.*:6443;server: https://k8s1:$host_port;g" > vagrant.kubeconfig
             echo "Add '127.0.0.1 k8s1' to your /etc/hosts to use vagrant.kubeconfig file for kubectl"
         fi
     fi
diff --git a/daemon/cmd/config.go b/daemon/cmd/config.go
index 7733904fd0..90de7372cc 100644
--- a/daemon/cmd/config.go
+++ b/daemon/cmd/config.go
@@ -194,7 +194,7 @@ func (h *getConfig) Handle(params GetConfigParams) middleware.Responder {
 			OperationMode:     option.Config.Ipvlan.OperationMode,
 		},
 		IpamMode:   option.Config.IPAM,
-		Masquerade: option.Config.EnableIPv4Masquerade || option.Config.EnableIPv6Masquerade,
+		Masquerade: option.Config.MasqueradingEnabled(),
 		MasqueradeProtocols: &models.DaemonConfigurationStatusMasqueradeProtocols{
 			IPV4: option.Config.EnableIPv4Masquerade,
 			IPV6: option.Config.EnableIPv6Masquerade,
diff --git a/daemon/cmd/kube_proxy_replacement.go b/daemon/cmd/kube_proxy_replacement.go
index f794748cce..4c38475e6c 100644
--- a/daemon/cmd/kube_proxy_replacement.go
+++ b/daemon/cmd/kube_proxy_replacement.go
@@ -401,7 +401,7 @@ func initKubeProxyReplacementOptions() (bool, error) {
 				option.InstallNoConntrackIptRules, option.KubeProxyReplacement, option.KubeProxyReplacementStrict)
 		}
 
-		if !option.Config.EnableBPFMasquerade {
+		if option.Config.MasqueradingEnabled() && !option.Config.EnableBPFMasquerade {
 			return false, fmt.Errorf("%s requires the agent to run with %s.",
 				option.InstallNoConntrackIptRules, option.EnableBPFMasquerade)
 		}
diff --git a/daemon/cmd/status.go b/daemon/cmd/status.go
index 547200f3ab..1cdefea4c4 100644
--- a/daemon/cmd/status.go
+++ b/daemon/cmd/status.go
@@ -115,14 +115,14 @@ func (d *Daemon) getK8sStatus() *models.K8sStatus {
 
 func (d *Daemon) getMasqueradingStatus() *models.Masquerading {
 	s := &models.Masquerading{
-		Enabled: option.Config.EnableIPv4Masquerade || option.Config.EnableIPv6Masquerade,
+		Enabled: option.Config.MasqueradingEnabled(),
 		EnabledProtocols: &models.MasqueradingEnabledProtocols{
 			IPV4: option.Config.EnableIPv4Masquerade,
 			IPV6: option.Config.EnableIPv6Masquerade,
 		},
 	}
 
-	if !option.Config.EnableIPv4Masquerade && !option.Config.EnableIPv6Masquerade {
+	if !option.Config.MasqueradingEnabled() {
 		return s
 	}
 
diff --git a/go.mod b/go.mod
index 033156ecb4..31505ddb51 100644
--- a/go.mod
+++ b/go.mod
@@ -68,7 +68,7 @@ require (
 	github.com/spf13/pflag v1.0.5
 	github.com/spf13/viper v1.10.1
 	github.com/stretchr/testify v1.7.0
-	github.com/vishvananda/netlink v1.1.1-0.20211101163509-b10eb8fe5cf6
+	github.com/vishvananda/netlink v1.1.1-0.20220117225832-796d4ea903f3
 	github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74
 	go.etcd.io/etcd/api/v3 v3.5.1
 	go.etcd.io/etcd/client/pkg/v3 v3.5.1
diff --git a/go.sum b/go.sum
index aae8461ef5..4fc8144f0d 100644
--- a/go.sum
+++ b/go.sum
@@ -1326,8 +1326,8 @@ github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJ
 github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
 github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
 github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
-github.com/vishvananda/netlink v1.1.1-0.20211101163509-b10eb8fe5cf6 h1:167a2omrzz+nN9Of6lN/0yOB9itzw+IOioRThNZ30jA=
-github.com/vishvananda/netlink v1.1.1-0.20211101163509-b10eb8fe5cf6/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
+github.com/vishvananda/netlink v1.1.1-0.20220117225832-796d4ea903f3 h1:N+oou1yPqtRAnQ03R2Q43160OszsM8/2JXgwkiHXkZA=
+github.com/vishvananda/netlink v1.1.1-0.20220117225832-796d4ea903f3/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
 github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
 github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
 github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
diff --git a/install/kubernetes/cilium/README.md b/install/kubernetes/cilium/README.md
index e2dd083a94..ac22006cd8 100644
--- a/install/kubernetes/cilium/README.md
+++ b/install/kubernetes/cilium/README.md
@@ -122,6 +122,7 @@ contributors across the globe, there is almost always someone available to help.
 | cni.exclusive | bool | `true` | Make Cilium take ownership over the `/etc/cni/net.d` directory on the node, renaming all non-Cilium CNI configurations to `*.cilium_bak`. This ensures no Pods can be scheduled using other CNI plugins during Cilium agent downtime. |
 | cni.hostConfDirMountPath | string | `"/host/etc/cni/net.d"` | Configure the path to where the CNI configuration directory is mounted inside the agent pod. |
 | cni.install | bool | `true` | Install the CNI configuration and binary files into the filesystem. |
+| cni.logFile | string | `"/var/run/cilium/cilium-cni.log"` | Configure the log file for CNI logging with retention policy of 7 days. Disable CNI file logging by setting this field to empty explicitly. |
 | containerRuntime | object | `{"integration":"none"}` | Configure container runtime specific integration. |
 | containerRuntime.integration | string | `"none"` | Enables specific integrations for container runtimes. Supported values: - containerd - crio - docker - none - auto (automatically detect the container runtime) |
 | customCalls | object | `{"enabled":false}` | Tail call hooks for custom eBPF programs. |
@@ -138,7 +139,7 @@ contributors across the globe, there is almost always someone available to help.
 | enableIPv6Masquerade | bool | `true` | Enables masquerading of IPv6 traffic leaving the node from endpoints. |
 | enableK8sEventHandover | bool | `false` | Configures the use of the KVStore to optimize Kubernetes event handling by mirroring it into the KVstore for reduced overhead in large clusters. |
 | enableK8sTerminatingEndpoint | bool | `true` | Configure whether to enable auto detect of terminating state for endpoints in order to support graceful termination. |
-| enableXTSocketFallback | bool | `true` | Enables the fallback compatibility solution for when the xt_socket kernel module is missing and it is needed for the datapath L7 redirection to work properly. See documentation for details on when this can be disabled: http://docs.cilium.io/en/stable/install/system_requirements/#admin-kernel-version. |
+| enableXTSocketFallback | bool | `true` | Enables the fallback compatibility solution for when the xt_socket kernel module is missing and it is needed for the datapath L7 redirection to work properly. See documentation for details on when this can be disabled: https://docs.cilium.io/en/stable/operations/system_requirements/#linux-kernel. |
 | encryption.enabled | bool | `false` | Enable transparent network encryption. |
 | encryption.interface | string | `""` | Deprecated in favor of encryption.ipsec.interface. The interface to use for encrypted traffic. This option is only effective when encryption.type is set to ipsec. |
 | encryption.ipsec.interface | string | `""` | The interface to use for encrypted traffic. |
@@ -203,7 +204,7 @@ contributors across the globe, there is almost always someone available to help.
 | hostServices.protocols | string | `"tcp,udp"` | Supported list of protocols to apply ClusterIP translation to. |
 | hubble.enabled | bool | `true` | Enable Hubble (true by default). |
 | hubble.listenAddress | string | `":4244"` | An additional address for Hubble to listen to. Set this field ":4244" if you are enabling Hubble Relay, as it assumes that Hubble is listening on port 4244. |
-| hubble.metrics | object | `{"enabled":null,"port":9091,"serviceAnnotations":{},"serviceMonitor":{"enabled":false,"labels":{}}}` | Hubble metrics configuration. See https://docs.cilium.io/en/stable/configuration/metrics/#hubble-metrics for more comprehensive documentation about Hubble metrics. |
+| hubble.metrics | object | `{"enabled":null,"port":9091,"serviceAnnotations":{},"serviceMonitor":{"enabled":false,"labels":{}}}` | Hubble metrics configuration. See https://docs.cilium.io/en/stable/operations/metrics/#hubble-metrics for more comprehensive documentation about Hubble metrics. |
 | hubble.metrics.enabled | string | `nil` | Configures the list of metrics to collect. If empty or null, metrics are disabled. Example:   enabled:   - dns:query;ignoreAAAA   - drop   - tcp   - flow   - icmp   - http You can specify the list of metrics from the helm CLI:   --set metrics.enabled="{dns:query;ignoreAAAA,drop,tcp,flow,icmp,http}" |
 | hubble.metrics.port | int | `9091` | Configure the port the hubble metric server listens on. |
 | hubble.metrics.serviceAnnotations | object | `{}` | Annotations to be added to hubble-metrics service. |
diff --git a/install/kubernetes/cilium/templates/cilium-agent/daemonset.yaml b/install/kubernetes/cilium/templates/cilium-agent/daemonset.yaml
index 1fccd9723b..6a127b7fd2 100644
--- a/install/kubernetes/cilium/templates/cilium-agent/daemonset.yaml
+++ b/install/kubernetes/cilium/templates/cilium-agent/daemonset.yaml
@@ -210,6 +210,7 @@ spec:
               - "/cni-install.sh"
               - "--enable-debug={{ .Values.debug.enabled }}"
               - "--cni-exclusive={{ .Values.cni.exclusive }}"
+              - "--log-file={{ .Values.cni.logFile }}"
           preStop:
             exec:
               command:
diff --git a/install/kubernetes/cilium/values.yaml b/install/kubernetes/cilium/values.yaml
index 44181c66cc..9c33af15c8 100644
--- a/install/kubernetes/cilium/values.yaml
+++ b/install/kubernetes/cilium/values.yaml
@@ -324,6 +324,10 @@ cni:
   # agent downtime.
   exclusive: true
 
+  # -- Configure the log file for CNI logging with retention policy of 7 days.
+  # Disable CNI file logging by setting this field to empty explicitly.
+  logFile: /var/run/cilium/cilium-cni.log
+
   # -- Skip writing of the CNI configuration. This can be used if
   # writing of the CNI configuration is performed by external automation.
   customConf: false
@@ -419,7 +423,7 @@ enableCiliumEndpointSlice: false
 # -- Enables the fallback compatibility solution for when the xt_socket kernel
 # module is missing and it is needed for the datapath L7 redirection to work
 # properly. See documentation for details on when this can be disabled:
-# http://docs.cilium.io/en/stable/install/system_requirements/#admin-kernel-version.
+# https://docs.cilium.io/en/stable/operations/system_requirements/#linux-kernel.
 enableXTSocketFallback: true
 
 encryption:
@@ -575,7 +579,7 @@ hubble:
   # eventBufferCapacity: "4095"
 
   # -- Hubble metrics configuration.
-  # See https://docs.cilium.io/en/stable/configuration/metrics/#hubble-metrics
+  # See https://docs.cilium.io/en/stable/operations/metrics/#hubble-metrics
   # for more comprehensive documentation about Hubble metrics.
   metrics:
     # -- Configures the list of metrics to collect. If empty or null, metrics
diff --git a/pkg/datapath/maps/map.go b/pkg/datapath/maps/map.go
index 3e2ce8735b..f820e43a7f 100644
--- a/pkg/datapath/maps/map.go
+++ b/pkg/datapath/maps/map.go
@@ -77,8 +77,7 @@ func (ms *MapSweeper) deleteMapIfStale(path string, filename string, endpointID
 
 func (ms *MapSweeper) checkStaleGlobalMap(path string, filename string) {
 	globalCTinUse := ms.HasGlobalCT() || option.Config.EnableNodePort ||
-		!option.Config.InstallIptRules && (option.Config.EnableIPv4Masquerade ||
-			option.Config.EnableIPv6Masquerade)
+		!option.Config.InstallIptRules && option.Config.MasqueradingEnabled()
 
 	if !globalCTinUse && ctmap.NameIsGlobal(filename) {
 		ms.RemoveMapPath(path)
diff --git a/pkg/endpoint/api.go b/pkg/endpoint/api.go
index 1faee77273..7933d48edf 100644
--- a/pkg/endpoint/api.go
+++ b/pkg/endpoint/api.go
@@ -355,12 +355,10 @@ func (e *Endpoint) GetPolicyModel() *models.EndpointPolicyStatus {
 	sortProxyStats(proxyStats)
 
 	var (
-		realizedCIDRPolicy *policy.CIDRPolicy
-		realizedL4Policy   *policy.L4Policy
+		realizedL4Policy *policy.L4Policy
 	)
 	if e.realizedPolicy != nil {
 		realizedL4Policy = e.realizedPolicy.L4Policy
-		realizedCIDRPolicy = e.realizedPolicy.CIDRPolicy
 	}
 
 	mdl := &models.EndpointPolicy{
@@ -372,17 +370,14 @@ func (e *Endpoint) GetPolicyModel() *models.EndpointPolicyStatus {
 		AllowedEgressIdentities:  realizedEgressIdentities,
 		DeniedIngressIdentities:  realizedDenyIngressIdentities,
 		DeniedEgressIdentities:   realizedDenyEgressIdentities,
-		CidrPolicy:               realizedCIDRPolicy.GetModel(),
 		L4:                       realizedL4Policy.GetModel(),
 		PolicyEnabled:            policyEnabled,
 	}
 
 	var (
-		desiredCIDRPolicy *policy.CIDRPolicy
-		desiredL4Policy   *policy.L4Policy
+		desiredL4Policy *policy.L4Policy
 	)
 	if e.desiredPolicy != nil {
-		desiredCIDRPolicy = e.desiredPolicy.CIDRPolicy
 		desiredL4Policy = e.desiredPolicy.L4Policy
 	}
 
@@ -395,7 +390,6 @@ func (e *Endpoint) GetPolicyModel() *models.EndpointPolicyStatus {
 		AllowedEgressIdentities:  desiredEgressIdentities,
 		DeniedIngressIdentities:  desiredDenyIngressIdentities,
 		DeniedEgressIdentities:   desiredDenyEgressIdentities,
-		CidrPolicy:               desiredCIDRPolicy.GetModel(),
 		L4:                       desiredL4Policy.GetModel(),
 		PolicyEnabled:            policyEnabled,
 	}
diff --git a/pkg/endpoint/policy.go b/pkg/endpoint/policy.go
index 587dfe478d..3689674b85 100644
--- a/pkg/endpoint/policy.go
+++ b/pkg/endpoint/policy.go
@@ -808,13 +808,7 @@ func (e *Endpoint) SetIdentity(identity *identityPkg.Identity, newEndpoint bool)
 // GetCIDRPrefixLengths returns the sorted list of unique prefix lengths used
 // for CIDR policy or IPcache lookup from this endpoint.
 func (e *Endpoint) GetCIDRPrefixLengths() (s6, s4 []int) {
-	if e.IsHost() {
-		return e.owner.GetCIDRPrefixLengths()
-	}
-	if e.desiredPolicy == nil || e.desiredPolicy.CIDRPolicy == nil {
-		return policy.GetDefaultPrefixLengths()
-	}
-	return e.desiredPolicy.CIDRPolicy.ToBPFData()
+	return e.owner.GetCIDRPrefixLengths()
 }
 
 // AnnotationsResolverCB provides an implementation for resolving the pod
diff --git a/pkg/logging/hooks/file_rotation.go b/pkg/logging/hooks/file_rotation.go
new file mode 100644
index 0000000000..a398106b34
--- /dev/null
+++ b/pkg/logging/hooks/file_rotation.go
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright 2022 Authors of Cilium
+
+package hooks
+
+import (
+	"github.com/sirupsen/logrus"
+	"gopkg.in/natefinch/lumberjack.v2"
+)
+
+// FileRotationOption provides all parameters for file rotation
+type FileRotationOption struct {
+	FileName   string
+	MaxSize    int
+	MaxAge     int
+	MaxBackups int
+	LocalTime  bool
+	Compress   bool
+}
+
+type Option func(*FileRotationOption)
+
+// WithMaxSize provides way to adjust maxSize (in MBs). Defaults to
+// 100 MBs.
+func WithMaxSize(maxSize int) Option {
+	return func(option *FileRotationOption) {
+		option.MaxSize = maxSize
+	}
+}
+
+// WithMaxAge provides way to adjust max age (in days). The default is
+// not to remove old log files based on age.
+func WithMaxAge(maxAge int) Option {
+	return func(option *FileRotationOption) {
+		option.MaxAge = maxAge
+	}
+}
+
+// WithMaxBackups provides way to adjust max number of backups. Defaults
+// to retain all old log files though MaxAge may still cause them to get
+// deleted.
+func WithMaxBackups(MaxBackups int) Option {
+	return func(option *FileRotationOption) {
+		option.MaxBackups = MaxBackups
+	}
+}
+
+// EnableLocalTime is to determine if the time used for formatting the
+// timestamps in backup files is the computer's local time.  The default
+// is to use UTC time.
+func EnableLocalTime() Option {
+	return func(option *FileRotationOption) {
+		option.LocalTime = true
+	}
+}
+
+// EnableCompression is to enable old log file gzip compression. Defaults
+// to false.
+func EnableCompression() Option {
+	return func(option *FileRotationOption) {
+		option.Compress = true
+	}
+}
+
+// FileRotationLogHook stores the configuration of the hook
+type FileRotationLogHook struct {
+	logger *lumberjack.Logger
+}
+
+// NewFileRotationLogHook creates a new FileRotationLogHook*/
+func NewFileRotationLogHook(fileName string, opts ...Option) *FileRotationLogHook {
+	options := &FileRotationOption{
+		FileName:  fileName,
+		MaxSize:   100,   // MBs
+		LocalTime: false, // UTC
+		Compress:  false, // no compression with gzip
+	}
+
+	for _, opt := range opts {
+		opt(options)
+	}
+
+	logger := &lumberjack.Logger{
+		Filename:   options.FileName,
+		MaxSize:    options.MaxSize,
+		MaxAge:     options.MaxAge,
+		MaxBackups: options.MaxBackups,
+		LocalTime:  options.LocalTime,
+		Compress:   options.Compress,
+	}
+
+	return &FileRotationLogHook{
+		logger: logger,
+	}
+}
+
+// Fire is called when a log event is fired.
+func (hook *FileRotationLogHook) Fire(entry *logrus.Entry) error {
+	line, err := entry.String()
+	if err != nil {
+		return err
+	}
+
+	_, err = hook.logger.Write([]byte(line))
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Levels returns the available logging levels
+func (hook *FileRotationLogHook) Levels() []logrus.Level {
+	return logrus.AllLevels
+}
diff --git a/pkg/logging/logging.go b/pkg/logging/logging.go
index 5695c7ac9a..9049efd693 100644
--- a/pkg/logging/logging.go
+++ b/pkg/logging/logging.go
@@ -136,11 +136,18 @@ func SetLogFormat(logFormat LogFormat) {
 	DefaultLogger.SetFormatter(GetFormatter(logFormat))
 }
 
-// SetLogLevel updates the DefaultLogger with the DefaultLogFormat
+// SetDefaultLogFormat updates the DefaultLogger with the DefaultLogFormat
 func SetDefaultLogFormat() {
 	DefaultLogger.SetFormatter(GetFormatter(DefaultLogFormat))
 }
 
+// AddHooks adds additional logrus hook to default logger
+func AddHooks(hooks ...logrus.Hook) {
+	for _, hook := range hooks {
+		DefaultLogger.AddHook(hook)
+	}
+}
+
 // SetupLogging sets up each logging service provided in loggers and configures
 // each logger with the provided logOpts.
 func SetupLogging(loggers []string, logOpts LogOptions, tag string, debug bool) error {
diff --git a/pkg/node/manager/manager.go b/pkg/node/manager/manager.go
index efee11dc71..43c9116b98 100644
--- a/pkg/node/manager/manager.go
+++ b/pkg/node/manager/manager.go
@@ -412,7 +412,7 @@ func (m *Manager) NodeUpdated(n nodeTypes.Node) {
 		}
 
 		if option.Config.IptablesMasqueradingEnabled() &&
-			address.Type != addressing.NodeCiliumInternalIP {
+			address.Type == addressing.NodeInternalIP {
 			iptables.AddToNodeIpset(address.IP)
 		}
 
@@ -596,7 +596,7 @@ func (m *Manager) NodeDeleted(n nodeTypes.Node) {
 
 	for _, address := range entry.node.IPAddresses {
 		if option.Config.IptablesMasqueradingEnabled() &&
-			address.Type != addressing.NodeCiliumInternalIP {
+			address.Type != addressing.NodeInternalIP {
 			iptables.RemoveFromNodeIpset(address.IP)
 		}
 
diff --git a/pkg/option/config.go b/pkg/option/config.go
index 82b46ba789..82917d337c 100644
--- a/pkg/option/config.go
+++ b/pkg/option/config.go
@@ -2180,6 +2180,11 @@ func (c *DaemonConfig) TunnelingEnabled() bool {
 	return c.Tunnel != TunnelDisabled
 }
 
+// MasqueradingEnabled returns true if either IPv4 or IPv6 masquerading is enabled.
+func (c *DaemonConfig) MasqueradingEnabled() bool {
+	return c.EnableIPv4Masquerade || c.EnableIPv6Masquerade
+}
+
 // IptablesMasqueradingIPv4Enabled returns true if iptables-based
 // masquerading is enabled for IPv4.
 func (c *DaemonConfig) IptablesMasqueradingIPv4Enabled() bool {
diff --git a/pkg/option/daemon.go b/pkg/option/daemon.go
index 3aa23089cb..130723f401 100644
--- a/pkg/option/daemon.go
+++ b/pkg/option/daemon.go
@@ -17,7 +17,6 @@ var (
 	DaemonMutableOptionLibrary = OptionLibrary{
 		ConntrackAccounting: &specConntrackAccounting,
 		ConntrackLocal:      &specConntrackLocal,
-		Conntrack:           &specConntrack,
 		Debug:               &specDebug,
 		DebugLB:             &specDebugLB,
 		DebugPolicy:         &specDebugPolicy,
diff --git a/pkg/option/endpoint.go b/pkg/option/endpoint.go
index 46a8582799..7e54f34b50 100644
--- a/pkg/option/endpoint.go
+++ b/pkg/option/endpoint.go
@@ -7,7 +7,6 @@ var (
 	endpointMutableOptionLibrary = OptionLibrary{
 		ConntrackAccounting: &specConntrackAccounting,
 		ConntrackLocal:      &specConntrackLocal,
-		Conntrack:           &specConntrack,
 		Debug:               &specDebug,
 		DebugLB:             &specDebugLB,
 		DebugPolicy:         &specDebugPolicy,
diff --git a/pkg/option/runtime_options.go b/pkg/option/runtime_options.go
index ddb80e4b6b..2336770b0d 100644
--- a/pkg/option/runtime_options.go
+++ b/pkg/option/runtime_options.go
@@ -47,11 +47,6 @@ var (
 		Requires:    []string{Conntrack},
 	}
 
-	specConntrack = Option{
-		Define:      "CONNTRACK",
-		Description: "Enable stateful connection tracking",
-	}
-
 	specDebug = Option{
 		Define:      "DEBUG",
 		Description: "Enable debugging trace statements",
diff --git a/pkg/policy/l3.go b/pkg/policy/l3.go
deleted file mode 100644
index 7a897935c7..0000000000
--- a/pkg/policy/l3.go
+++ /dev/null
@@ -1,189 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-// Copyright 2016-2019 Authors of Cilium
-
-package policy
-
-import (
-	"net"
-	"sort"
-	"strconv"
-
-	"github.com/cilium/cilium/api/v1/models"
-	"github.com/cilium/cilium/pkg/labels"
-)
-
-// CIDRPolicyMapRule holds a L3 (CIDR) prefix and the rule labels that allow it.
-type CIDRPolicyMapRule struct {
-	Prefix           net.IPNet
-	DerivedFromRules labels.LabelArrayList
-}
-
-// CIDRPolicyMap is a list of CIDR filters indexable by address/prefixlen
-// key format: "address/prefixlen", e.g., "10.1.1.0/24"
-// Each prefix struct also includes the rule labels that allowed it.
-//
-// CIDRPolicyMap does no locking internally, so the user is responsible for synchronizing
-// between multiple threads when applicable.
-type CIDRPolicyMap struct {
-	Map map[string]*CIDRPolicyMapRule // Allowed L3 (CIDR) prefixes
-
-	IPv6PrefixCount map[int]int // Count of IPv6 prefixes in 'Map' indexed by prefix length
-	IPv4PrefixCount map[int]int // Count of IPv4 prefixes in 'Map' indexed by prefix length
-}
-
-// GetDefaultPrefixLengths returns the set of prefix lengths for handling
-// CIDRs that are unconditionally mapped to identities, ie for the reserved
-// identities 'host', 'world'.
-func GetDefaultPrefixLengths() (s6 []int, s4 []int) {
-	// These *must* be in the order of longest prefix to shortest, as the
-	// LPM implementation on older kernels depends on this ordering.
-	s6 = []int{net.IPv6len * 8, 0}
-	s4 = []int{net.IPv4len * 8, 0}
-	return
-}
-
-// Insert places 'cidr' and its corresponding rule labels into map 'm'. Returns
-// `1` if `cidr` is added to the map, `0` otherwise.
-func (m *CIDRPolicyMap) Insert(cidr string, ruleLabels labels.LabelArray) int {
-	_, ipnet, err := net.ParseCIDR(cidr)
-	if err != nil {
-		var mask net.IPMask
-		ip := net.ParseIP(cidr)
-		// Use default CIDR mask for the address if the bits in the address
-		// after the mask are all zeroes.
-		ip4 := ip.To4()
-		if ip4 == nil {
-			mask = net.CIDRMask(128, 128)
-		} else { // IPv4
-			ip = ip4
-			mask = ip.DefaultMask() // IP address class based mask (/8, /16, or /24)
-			if !ip.Equal(ip.Mask(mask)) {
-				// IPv4 with non-zeroes after the subnetwork, use full mask.
-				mask = net.CIDRMask(32, 32)
-			}
-		}
-		ipnet = &net.IPNet{IP: ip, Mask: mask}
-	}
-
-	ones, _ := ipnet.Mask.Size()
-
-	key := ipnet.IP.String() + "/" + strconv.Itoa(ones)
-	if _, found := m.Map[key]; !found {
-		m.Map[key] = &CIDRPolicyMapRule{Prefix: *ipnet, DerivedFromRules: labels.LabelArrayList{ruleLabels}}
-		if ipnet.IP.To4() == nil {
-			m.IPv6PrefixCount[ones]++
-		} else {
-			m.IPv4PrefixCount[ones]++
-		}
-		return 1
-	} else {
-		m.Map[key].DerivedFromRules = append(m.Map[key].DerivedFromRules, ruleLabels)
-	}
-
-	return 0
-}
-
-// CIDRPolicy contains L3 (CIDR) policy maps for ingress.
-//
-// This is not used for map entry generation; It has two uses:
-// * On older kernels, generate the set of CIDR prefix lengths that
-//   are necessary to implement an LPM
-// * Reflect desired state of the CIDR policy in the API.
-type CIDRPolicy struct {
-	Ingress CIDRPolicyMap
-	Egress  CIDRPolicyMap
-}
-
-// NewCIDRPolicy creates a new CIDRPolicy.
-func NewCIDRPolicy() (policy *CIDRPolicy) {
-	policy = &CIDRPolicy{
-		Ingress: CIDRPolicyMap{
-			Map:             make(map[string]*CIDRPolicyMapRule),
-			IPv6PrefixCount: make(map[int]int),
-			IPv4PrefixCount: make(map[int]int),
-		},
-		Egress: CIDRPolicyMap{
-			Map:             make(map[string]*CIDRPolicyMapRule),
-			IPv6PrefixCount: make(map[int]int),
-			IPv4PrefixCount: make(map[int]int),
-		},
-	}
-	// Add a default reference to the default {host, cluster, world} prefix
-	// to ensure that ToBPFData() always serializes these lengths for LPM.
-	s6, s4 := GetDefaultPrefixLengths()
-	for _, i := range s6 {
-		policy.Egress.IPv6PrefixCount[i] = 0
-		policy.Ingress.IPv6PrefixCount[i] = 0
-	}
-	for _, i := range s4 {
-		policy.Egress.IPv4PrefixCount[i] = 0
-		policy.Ingress.IPv4PrefixCount[i] = 0
-	}
-	return
-}
-
-// ToBPFData converts the ingress and egress cidr map into int slices 's6'
-// (IPv6) and 's4' (IPv4), formatted for insertion into bpf program as prefix
-// lengths.
-//
-// Note that this will always include the CIDR prefix lengths for host (eg /32
-// for host), cluster (typically /8 or /64), and world (/0).
-//
-// FIXME: Move this function out of policy into a datapath specific package
-func (cp *CIDRPolicy) ToBPFData() (s6, s4 []int) {
-	s6duplicates, s4duplicates := map[int]bool{}, map[int]bool{}
-
-	for _, m := range []CIDRPolicyMap{cp.Ingress, cp.Egress} {
-		for p := range m.IPv6PrefixCount {
-			if _, ok := s6duplicates[p]; !ok {
-				s6 = append(s6, p)
-				s6duplicates[p] = true
-			}
-		}
-		for p := range m.IPv4PrefixCount {
-			if _, ok := s4duplicates[p]; !ok {
-				s4 = append(s4, p)
-				s4duplicates[p] = true
-			}
-		}
-	}
-
-	// The datapath expects longest-to-shortest prefixes so that it can
-	// clear progressively more bits with a single load of the address.
-	sort.Sort(sort.Reverse(sort.IntSlice(s6)))
-	sort.Sort(sort.Reverse(sort.IntSlice(s4)))
-	return
-}
-
-// GetModel returns the API model representation of the CIDRPolicy.
-func (cp *CIDRPolicy) GetModel() *models.CIDRPolicy {
-	if cp == nil {
-		return nil
-	}
-
-	ingress := []*models.PolicyRule{}
-	for _, v := range cp.Ingress.Map {
-		ingress = append(ingress, &models.PolicyRule{
-			Rule:             v.Prefix.String(),
-			DerivedFromRules: v.DerivedFromRules.GetModel(),
-		})
-	}
-
-	egress := []*models.PolicyRule{}
-	for _, v := range cp.Egress.Map {
-		egress = append(egress, &models.PolicyRule{
-			Rule:             v.Prefix.String(),
-			DerivedFromRules: v.DerivedFromRules.GetModel(),
-		})
-	}
-
-	return &models.CIDRPolicy{
-		Ingress: ingress,
-		Egress:  egress,
-	}
-}
-
-// Validate returns error if the CIDR policy might lead to code generation failure
-func (cp *CIDRPolicy) Validate() error {
-	return nil
-}
diff --git a/pkg/policy/repository.go b/pkg/policy/repository.go
index 0205e32386..a81cf1d6d6 100644
--- a/pkg/policy/repository.go
+++ b/pkg/policy/repository.go
@@ -660,7 +660,6 @@ func (p *Repository) resolvePolicyLocked(securityIdentity *identity.Identity) (*
 		Revision:             p.GetRevision(),
 		SelectorCache:        p.GetSelectorCache(),
 		L4Policy:             NewL4Policy(p.GetRevision()),
-		CIDRPolicy:           NewCIDRPolicy(),
 		IngressPolicyEnabled: ingressEnabled,
 		EgressPolicyEnabled:  egressEnabled,
 	}
@@ -691,13 +690,6 @@ func (p *Repository) resolvePolicyLocked(securityIdentity *identity.Identity) (*
 		if err != nil {
 			return nil, err
 		}
-
-		newCIDRIngressPolicy := matchingRules.resolveCIDRPolicy(&ingressCtx)
-		if err := newCIDRIngressPolicy.Validate(); err != nil {
-			return nil, err
-		}
-
-		calculatedPolicy.CIDRPolicy.Ingress = newCIDRIngressPolicy.Ingress
 		calculatedPolicy.L4Policy.Ingress = newL4IngressPolicy
 	}
 
@@ -706,13 +698,6 @@ func (p *Repository) resolvePolicyLocked(securityIdentity *identity.Identity) (*
 		if err != nil {
 			return nil, err
 		}
-
-		newCIDREgressPolicy := matchingRules.resolveCIDRPolicy(&egressCtx)
-		if err := newCIDREgressPolicy.Validate(); err != nil {
-			return nil, err
-		}
-
-		calculatedPolicy.CIDRPolicy.Egress = newCIDREgressPolicy.Egress
 		calculatedPolicy.L4Policy.Egress = newL4EgressPolicy
 	}
 
diff --git a/pkg/policy/resolve.go b/pkg/policy/resolve.go
index 9728f25e99..ffd88d9476 100644
--- a/pkg/policy/resolve.go
+++ b/pkg/policy/resolve.go
@@ -24,9 +24,6 @@ type selectorPolicy struct {
 	// L4Policy contains the computed L4 and L7 policy.
 	L4Policy *L4Policy
 
-	// CIDRPolicy contains the L3 (not L4) CIDR-based policy.
-	CIDRPolicy *CIDRPolicy
-
 	// IngressPolicyEnabled specifies whether this policy contains any policy
 	// at ingress.
 	IngressPolicyEnabled bool
diff --git a/pkg/policy/rule.go b/pkg/policy/rule.go
index da9cfc2c94..c952dd38a5 100644
--- a/pkg/policy/rule.go
+++ b/pkg/policy/rule.go
@@ -517,117 +517,6 @@ func (r *rule) resolveIngressPolicy(
 	return nil, nil
 }
 
-// ********************** CIDR POLICY **********************
-
-// mergeCIDR inserts all of the CIDRs in ipRules to resMap. Returns the number
-// of CIDRs added to resMap.
-func mergeCIDR(ctx *SearchContext, isDeny bool, dir string, ipRules []api.CIDR, ruleLabels labels.LabelArray, resMap *CIDRPolicyMap) int {
-	found := 0
-
-	for _, r := range ipRules {
-		strCIDR := string(r)
-		if !isDeny {
-			ctx.PolicyTrace("  Allows %s IP %s\n", dir, strCIDR)
-		} else {
-			ctx.PolicyTrace("  Denies %s IP %s\n", dir, strCIDR)
-		}
-
-		found += resMap.Insert(strCIDR, ruleLabels)
-	}
-
-	return found
-}
-
-// resolveCIDRPolicy inserts the CIDRs from the specified rule into result if
-// the rule corresponds to the current SearchContext. It returns the resultant
-// CIDRPolicy containing the added ingress and egress CIDRs. If no CIDRs are
-// added to result, a nil CIDRPolicy is returned.
-func (r *rule) resolveCIDRPolicy(ctx *SearchContext, state *traceState, result *CIDRPolicy) *CIDRPolicy {
-	// Don't select rule if it doesn't apply to the given context.
-	if !ctx.rulesSelect {
-		if !r.getSelector().Matches(ctx.To) {
-			state.unSelectRule(ctx, ctx.To, r)
-			return nil
-		}
-	}
-
-	state.selectRule(ctx, r)
-	found := 0
-
-	for _, ingressRule := range r.Ingress {
-		// TODO (ianvernon): GH-1658
-		allCIDRs := make([]api.CIDR, 0, len(ingressRule.FromCIDR))
-		allCIDRs = append(allCIDRs, ingressRule.FromCIDR...)
-		allCIDRs = append(allCIDRs, api.ComputeResultantCIDRSet(ingressRule.FromCIDRSet)...)
-
-		// CIDR + L4 rules are handled via mergeIngress(),
-		// skip them here.
-		if len(allCIDRs) > 0 && len(ingressRule.ToPorts) > 0 {
-			continue
-		}
-
-		if cnt := mergeCIDR(ctx, false, "Ingress", allCIDRs, r.Labels, &result.Ingress); cnt > 0 {
-			found += cnt
-		}
-	}
-
-	for _, ingressRule := range r.IngressDeny {
-		// TODO (ianvernon): GH-1658
-		allCIDRs := make([]api.CIDR, 0, len(ingressRule.FromCIDR))
-		allCIDRs = append(allCIDRs, ingressRule.FromCIDR...)
-		allCIDRs = append(allCIDRs, api.ComputeResultantCIDRSet(ingressRule.FromCIDRSet)...)
-
-		// CIDR + L4 rules are handled via mergeIngress(),
-		// skip them here.
-		if len(allCIDRs) > 0 && len(ingressRule.ToPorts) > 0 {
-			continue
-		}
-
-		if cnt := mergeCIDR(ctx, true, "Ingress", allCIDRs, r.Labels, &result.Ingress); cnt > 0 {
-			found += cnt
-		}
-	}
-
-	// CIDR egress policy is used for visibility of desired state in
-	// the API and for determining which prefix lengths are available,
-	// however it does not determine the actual CIDRs in the BPF maps
-	// for allowing traffic by CIDR!
-	for _, egressRule := range r.Egress {
-		allCIDRs := make([]api.CIDR, 0, len(egressRule.ToCIDR))
-		allCIDRs = append(allCIDRs, egressRule.ToCIDR...)
-		allCIDRs = append(allCIDRs, api.ComputeResultantCIDRSet(egressRule.ToCIDRSet)...)
-
-		// Unlike the Ingress policy which only counts L3 policy in
-		// this function, we count the CIDR+L4 policy in the
-		// desired egress CIDR policy here as well. This ensures
-		// proper computation of IPcache prefix lengths.
-		if cnt := mergeCIDR(ctx, false, "Egress", allCIDRs, r.Labels, &result.Egress); cnt > 0 {
-			found += cnt
-		}
-	}
-
-	for _, egressRule := range r.EgressDeny {
-		allCIDRs := make([]api.CIDR, 0, len(egressRule.ToCIDR))
-		allCIDRs = append(allCIDRs, egressRule.ToCIDR...)
-		allCIDRs = append(allCIDRs, api.ComputeResultantCIDRSet(egressRule.ToCIDRSet)...)
-
-		// Unlike the Ingress policy which only counts L3 policy in
-		// this function, we count the CIDR+L4 policy in the
-		// desired egress CIDR policy here as well. This ensures
-		// proper computation of IPcache prefix lengths.
-		if cnt := mergeCIDR(ctx, true, "Egress", allCIDRs, r.Labels, &result.Egress); cnt > 0 {
-			found += cnt
-		}
-	}
-
-	if found > 0 {
-		return result
-	}
-
-	ctx.PolicyTrace("    No L3 rules\n")
-	return nil
-}
-
 func (r *rule) matches(securityIdentity *identity.Identity) bool {
 	r.metadata.Mutex.Lock()
 	defer r.metadata.Mutex.Unlock()
diff --git a/pkg/policy/rules.go b/pkg/policy/rules.go
index 5610eec82c..307b0897e5 100644
--- a/pkg/policy/rules.go
+++ b/pkg/policy/rules.go
@@ -114,21 +114,6 @@ func (rules ruleSlice) resolveL4EgressPolicy(policyCtx PolicyContext, ctx *Searc
 	return result, nil
 }
 
-func (rules ruleSlice) resolveCIDRPolicy(ctx *SearchContext) *CIDRPolicy {
-	result := NewCIDRPolicy()
-
-	ctx.PolicyTrace("Resolving L3 (CIDR) policy for %+v\n", ctx.To)
-
-	state := traceState{}
-	for _, r := range rules {
-		r.resolveCIDRPolicy(ctx, &state, result)
-		state.ruleID++
-	}
-
-	state.trace(len(rules), ctx)
-	return result
-}
-
 // updateEndpointsCaches iterates over a given list of rules to update the cache
 // within the rule which determines whether or not the given identity is
 // selected by that rule. If a rule in the list does select said identity, it is
diff --git a/plugins/cilium-cni/05-cilium-cni.conf b/plugins/cilium-cni/05-cilium-cni.conf
index 4ad2f88f93..b1ec1528a8 100644
--- a/plugins/cilium-cni/05-cilium-cni.conf
+++ b/plugins/cilium-cni/05-cilium-cni.conf
@@ -2,5 +2,6 @@
     "cniVersion": "0.3.1",
     "name": "cilium",
     "type": "cilium-cni",
-    "enable-debug": true
+    "enable-debug": true,
+    "log-file": "/var/run/cilium/cilium-cni.log"
 }
diff --git a/plugins/cilium-cni/cilium-cni.go b/plugins/cilium-cni/cilium-cni.go
index 9b62869748..f70273151b 100644
--- a/plugins/cilium-cni/cilium-cni.go
+++ b/plugins/cilium-cni/cilium-cni.go
@@ -38,6 +38,7 @@ import (
 	endpointid "github.com/cilium/cilium/pkg/endpoint/id"
 	ipamOption "github.com/cilium/cilium/pkg/ipam/option"
 	"github.com/cilium/cilium/pkg/logging"
+	"github.com/cilium/cilium/pkg/logging/hooks"
 	"github.com/cilium/cilium/pkg/logging/logfields"
 	"github.com/cilium/cilium/pkg/netns"
 	"github.com/cilium/cilium/pkg/sysctl"
@@ -51,6 +52,12 @@ import (
 	"github.com/cilium/cilium/plugins/cilium-cni/types"
 )
 
+const (
+	// defaultLogMaxBackups is to make sure that we have an upper bound on disk space used by
+	// CNI file logging (e.g. < 7 * 100 MB).
+	defaultLogMaxBackups = 7
+)
+
 var (
 	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "cilium-cni")
 )
@@ -262,7 +269,19 @@ func setupLogging(n *types.NetConf) error {
 	logOptions := logging.LogOptions{
 		logging.FormatOpt: f,
 	}
-	return logging.SetupLogging([]string{}, logOptions, "cilium-cni", n.EnableDebug)
+	err := logging.SetupLogging([]string{}, logOptions, "cilium-cni", n.EnableDebug)
+	if err != nil {
+		return err
+	}
+
+	if len(n.LogFile) != 0 {
+		logging.AddHooks(hooks.NewFileRotationLogHook(n.LogFile,
+			hooks.EnableCompression(),
+			hooks.WithMaxBackups(defaultLogMaxBackups),
+		))
+	}
+
+	return nil
 }
 
 func cmdAdd(args *skel.CmdArgs) (err error) {
diff --git a/plugins/cilium-cni/cni-install.sh b/plugins/cilium-cni/cni-install.sh
index fc6f1e7642..5655b8a162 100755
--- a/plugins/cilium-cni/cni-install.sh
+++ b/plugins/cilium-cni/cni-install.sh
@@ -24,18 +24,28 @@ esac
 
 ENABLE_DEBUG=false
 CNI_EXCLUSIVE=true
+LOG_FILE="/var/run/cilium/cilium-cni.log"
+
 while test $# -gt 0; do
   case "$1" in
     --enable-debug*)
       # shellcheck disable=SC2001
+      # the below sed is to support both formats "--flag value" and "--flag=value"
       ENABLE_DEBUG=$(echo "$1" | sed -e 's/^[^=]*=//g')
       shift
       ;;
     --cni-exclusive*)
       # shellcheck disable=SC2001
+      # the below sed is to support both formats "--flag value" and "--flag=value"
       CNI_EXCLUSIVE=$(echo "$1" | sed -e 's/^[^=]*=//g')
       shift
       ;;
+    --log-file*)
+      # shellcheck disable=SC2001
+      # the below sed is to support both formats "--flag value" and "--flag=value"
+      LOG_FILE=$(echo "$1" | sed -e 's/^[^=]*=//g')
+      shift
+      ;;
     *)
       break
       ;;
@@ -134,7 +144,8 @@ case "$CILIUM_CNI_CHAINING_MODE" in
     {
        "name": "cilium",
        "type": "cilium-cni",
-       "enable-debug": ${ENABLE_DEBUG}
+       "enable-debug": ${ENABLE_DEBUG},
+       "log-file": "${LOG_FILE}"
     }
   ]
 }
@@ -150,7 +161,8 @@ EOF
     {
        "name": "cilium",
        "type": "cilium-cni",
-       "enable-debug": ${ENABLE_DEBUG}
+       "enable-debug": ${ENABLE_DEBUG},
+       "log-file": "${LOG_FILE}"
     },
     {
       "type": "portmap",
@@ -183,7 +195,8 @@ EOF
     {
        "name": "cilium",
        "type": "cilium-cni",
-       "enable-debug": ${ENABLE_DEBUG}
+       "enable-debug": ${ENABLE_DEBUG},
+       "log-file": "${LOG_FILE}"
     }
   ]
 }
@@ -196,7 +209,8 @@ EOF
   "cniVersion": "0.3.1",
   "name": "cilium",
   "type": "cilium-cni",
-  "enable-debug": ${ENABLE_DEBUG}
+  "enable-debug": ${ENABLE_DEBUG},
+  "log-file": "${LOG_FILE}"
 }
 EOF
 	;;
diff --git a/plugins/cilium-cni/types/types.go b/plugins/cilium-cni/types/types.go
index 90c9e61996..3d55597bd0 100644
--- a/plugins/cilium-cni/types/types.go
+++ b/plugins/cilium-cni/types/types.go
@@ -30,6 +30,7 @@ type NetConf struct {
 	AlibabaCloud alibabaCloudTypes.Spec `json:"alibaba-cloud,omitempty"`
 	EnableDebug  bool                   `json:"enable-debug"`
 	LogFormat    string                 `json:"log-format"`
+	LogFile      string                 `json:"log-file"`
 }
 
 // NetConfList is a CNI chaining configuration
diff --git a/vendor/github.com/vishvananda/netlink/.travis.yml b/vendor/github.com/vishvananda/netlink/.travis.yml
deleted file mode 100644
index 80219c69d6..0000000000
--- a/vendor/github.com/vishvananda/netlink/.travis.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-language: go
-go:
-  - "1.12.x"
-  - "1.13.x"
-  - "1.14.x"
-before_script:
-  # make sure we keep path in tact when we sudo
-  - sudo sed -i -e 's/^Defaults\tsecure_path.*$//' /etc/sudoers
-  # modprobe ip_gre or else the first gre device can't be deleted
-  - sudo modprobe ip_gre
-  # modprobe nf_conntrack for the conntrack testing
-  - sudo modprobe nf_conntrack
-  - sudo modprobe nf_conntrack_netlink
-  - sudo modprobe nf_conntrack_ipv4
-  - sudo modprobe nf_conntrack_ipv6
-  - sudo modprobe sch_hfsc
-  - sudo modprobe sch_sfq
-install:
-  - go get -v -t ./...
-go_import_path: github.com/vishvananda/netlink
diff --git a/vendor/github.com/vishvananda/netlink/README.md b/vendor/github.com/vishvananda/netlink/README.md
index 0e4ddb0164..0128bc67d5 100644
--- a/vendor/github.com/vishvananda/netlink/README.md
+++ b/vendor/github.com/vishvananda/netlink/README.md
@@ -1,6 +1,6 @@
 # netlink - netlink library for go #
 
-[![Build Status](https://app.travis-ci.com/vishvananda/netlink.svg?branch=master)](https://app.travis-ci.com/github/vishvananda/netlink) [![GoDoc](https://godoc.org/github.com/vishvananda/netlink?status.svg)](https://godoc.org/github.com/vishvananda/netlink)
+![Build Status](https://github.com/vishvananda/netlink/actions/workflows/main.yml/badge.svg) [![GoDoc](https://godoc.org/github.com/vishvananda/netlink?status.svg)](https://godoc.org/github.com/vishvananda/netlink)
 
 The netlink package provides a simple netlink library for go. Netlink
 is the interface a user-space program in linux uses to communicate with
diff --git a/vendor/github.com/vishvananda/netlink/filter.go b/vendor/github.com/vishvananda/netlink/filter.go
index 2dc34b9959..413abdb475 100644
--- a/vendor/github.com/vishvananda/netlink/filter.go
+++ b/vendor/github.com/vishvananda/netlink/filter.go
@@ -260,6 +260,40 @@ func NewSkbEditAction() *SkbEditAction {
 	}
 }
 
+type PoliceAction struct {
+	ActionAttrs
+	Rate            uint32 // in byte per second
+	Burst           uint32 // in byte
+	RCellLog        int
+	Mtu             uint32
+	Mpu             uint16 // in byte
+	PeakRate        uint32 // in byte per second
+	PCellLog        int
+	AvRate          uint32 // in byte per second
+	Overhead        uint16
+	LinkLayer       int
+	ExceedAction    TcPolAct
+	NotExceedAction TcPolAct
+}
+
+func (action *PoliceAction) Type() string {
+	return "police"
+}
+
+func (action *PoliceAction) Attrs() *ActionAttrs {
+	return &action.ActionAttrs
+}
+
+func NewPoliceAction() *PoliceAction {
+	return &PoliceAction{
+		RCellLog:        -1,
+		PCellLog:        -1,
+		LinkLayer:       1, // ETHERNET
+		ExceedAction:    TC_POLICE_RECLASSIFY,
+		NotExceedAction: TC_POLICE_OK,
+	}
+}
+
 // MatchAll filters match all packets
 type MatchAll struct {
 	FilterAttrs
@@ -275,20 +309,20 @@ func (filter *MatchAll) Type() string {
 	return "matchall"
 }
 
-type FilterFwAttrs struct {
-	ClassId   uint32
-	InDev     string
-	Mask      uint32
-	Index     uint32
-	Buffer    uint32
-	Mtu       uint32
-	Mpu       uint16
-	Rate      uint32
-	AvRate    uint32
-	PeakRate  uint32
-	Action    TcPolAct
-	Overhead  uint16
-	LinkLayer int
+type FwFilter struct {
+	FilterAttrs
+	ClassId uint32
+	InDev   string
+	Mask    uint32
+	Police  *PoliceAction
+}
+
+func (filter *FwFilter) Attrs() *FilterAttrs {
+	return &filter.FilterAttrs
+}
+
+func (filter *FwFilter) Type() string {
+	return "fw"
 }
 
 type BpfFilter struct {
diff --git a/vendor/github.com/vishvananda/netlink/filter_linux.go b/vendor/github.com/vishvananda/netlink/filter_linux.go
index d5d45c8725..3cfea45869 100644
--- a/vendor/github.com/vishvananda/netlink/filter_linux.go
+++ b/vendor/github.com/vishvananda/netlink/filter_linux.go
@@ -51,76 +51,6 @@ func (filter *U32) Type() string {
 	return "u32"
 }
 
-// Fw filter filters on firewall marks
-// NOTE: this is in filter_linux because it refers to nl.TcPolice which
-//       is defined in nl/tc_linux.go
-type Fw struct {
-	FilterAttrs
-	ClassId uint32
-	// TODO remove nl type from interface
-	Police nl.TcPolice
-	InDev  string
-	// TODO Action
-	Mask   uint32
-	AvRate uint32
-	Rtab   [256]uint32
-	Ptab   [256]uint32
-}
-
-func NewFw(attrs FilterAttrs, fattrs FilterFwAttrs) (*Fw, error) {
-	var rtab [256]uint32
-	var ptab [256]uint32
-	rcellLog := -1
-	pcellLog := -1
-	avrate := fattrs.AvRate / 8
-	police := nl.TcPolice{}
-	police.Rate.Rate = fattrs.Rate / 8
-	police.PeakRate.Rate = fattrs.PeakRate / 8
-	buffer := fattrs.Buffer
-	linklayer := nl.LINKLAYER_ETHERNET
-
-	if fattrs.LinkLayer != nl.LINKLAYER_UNSPEC {
-		linklayer = fattrs.LinkLayer
-	}
-
-	police.Action = int32(fattrs.Action)
-	if police.Rate.Rate != 0 {
-		police.Rate.Mpu = fattrs.Mpu
-		police.Rate.Overhead = fattrs.Overhead
-		if CalcRtable(&police.Rate, rtab[:], rcellLog, fattrs.Mtu, linklayer) < 0 {
-			return nil, errors.New("TBF: failed to calculate rate table")
-		}
-		police.Burst = Xmittime(uint64(police.Rate.Rate), uint32(buffer))
-	}
-	police.Mtu = fattrs.Mtu
-	if police.PeakRate.Rate != 0 {
-		police.PeakRate.Mpu = fattrs.Mpu
-		police.PeakRate.Overhead = fattrs.Overhead
-		if CalcRtable(&police.PeakRate, ptab[:], pcellLog, fattrs.Mtu, linklayer) < 0 {
-			return nil, errors.New("POLICE: failed to calculate peak rate table")
-		}
-	}
-
-	return &Fw{
-		FilterAttrs: attrs,
-		ClassId:     fattrs.ClassId,
-		InDev:       fattrs.InDev,
-		Mask:        fattrs.Mask,
-		Police:      police,
-		AvRate:      avrate,
-		Rtab:        rtab,
-		Ptab:        ptab,
-	}, nil
-}
-
-func (filter *Fw) Attrs() *FilterAttrs {
-	return &filter.FilterAttrs
-}
-
-func (filter *Fw) Type() string {
-	return "fw"
-}
-
 type Flower struct {
 	FilterAttrs
 	DestIP        net.IP
@@ -362,7 +292,7 @@ func (h *Handle) filterModify(filter Filter, flags int) error {
 		if err := EncodeActions(actionsAttr, filter.Actions); err != nil {
 			return err
 		}
-	case *Fw:
+	case *FwFilter:
 		if filter.Mask != 0 {
 			b := make([]byte, 4)
 			native.PutUint32(b, filter.Mask)
@@ -371,17 +301,10 @@ func (h *Handle) filterModify(filter Filter, flags int) error {
 		if filter.InDev != "" {
 			options.AddRtAttr(nl.TCA_FW_INDEV, nl.ZeroTerminated(filter.InDev))
 		}
-		if (filter.Police != nl.TcPolice{}) {
-
+		if filter.Police != nil {
 			police := options.AddRtAttr(nl.TCA_FW_POLICE, nil)
-			police.AddRtAttr(nl.TCA_POLICE_TBF, filter.Police.Serialize())
-			if (filter.Police.Rate != nl.TcRateSpec{}) {
-				payload := SerializeRtab(filter.Rtab)
-				police.AddRtAttr(nl.TCA_POLICE_RATE, payload)
-			}
-			if (filter.Police.PeakRate != nl.TcRateSpec{}) {
-				payload := SerializeRtab(filter.Ptab)
-				police.AddRtAttr(nl.TCA_POLICE_PEAKRATE, payload)
+			if err := encodePolice(police, filter.Police); err != nil {
+				return err
 			}
 		}
 		if filter.ClassId != 0 {
@@ -479,7 +402,7 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
 				case "u32":
 					filter = &U32{}
 				case "fw":
-					filter = &Fw{}
+					filter = &FwFilter{}
 				case "bpf":
 					filter = &BpfFilter{}
 				case "matchall":
@@ -551,6 +474,53 @@ func toAttrs(tcgen *nl.TcGen, attrs *ActionAttrs) {
 	attrs.Bindcnt = int(tcgen.Bindcnt)
 }
 
+func encodePolice(attr *nl.RtAttr, action *PoliceAction) error {
+	var rtab [256]uint32
+	var ptab [256]uint32
+	police := nl.TcPolice{}
+	police.Index = uint32(action.Attrs().Index)
+	police.Bindcnt = int32(action.Attrs().Bindcnt)
+	police.Capab = uint32(action.Attrs().Capab)
+	police.Refcnt = int32(action.Attrs().Refcnt)
+	police.Rate.Rate = action.Rate
+	police.PeakRate.Rate = action.PeakRate
+	police.Action = int32(action.ExceedAction)
+
+	if police.Rate.Rate != 0 {
+		police.Rate.Mpu = action.Mpu
+		police.Rate.Overhead = action.Overhead
+		if CalcRtable(&police.Rate, rtab[:], action.RCellLog, action.Mtu, action.LinkLayer) < 0 {
+			return errors.New("TBF: failed to calculate rate table")
+		}
+		police.Burst = Xmittime(uint64(police.Rate.Rate), action.Burst)
+	}
+
+	police.Mtu = action.Mtu
+	if police.PeakRate.Rate != 0 {
+		police.PeakRate.Mpu = action.Mpu
+		police.PeakRate.Overhead = action.Overhead
+		if CalcRtable(&police.PeakRate, ptab[:], action.PCellLog, action.Mtu, action.LinkLayer) < 0 {
+			return errors.New("POLICE: failed to calculate peak rate table")
+		}
+	}
+
+	attr.AddRtAttr(nl.TCA_POLICE_TBF, police.Serialize())
+	if police.Rate.Rate != 0 {
+		attr.AddRtAttr(nl.TCA_POLICE_RATE, SerializeRtab(rtab))
+	}
+	if police.PeakRate.Rate != 0 {
+		attr.AddRtAttr(nl.TCA_POLICE_PEAKRATE, SerializeRtab(ptab))
+	}
+	if action.AvRate != 0 {
+		attr.AddRtAttr(nl.TCA_POLICE_AVRATE, nl.Uint32Attr(action.AvRate))
+	}
+	if action.NotExceedAction != 0 {
+		attr.AddRtAttr(nl.TCA_POLICE_RESULT, nl.Uint32Attr(uint32(action.NotExceedAction)))
+	}
+
+	return nil
+}
+
 func EncodeActions(attr *nl.RtAttr, actions []Action) error {
 	tabIndex := int(nl.TCA_ACT_TAB)
 
@@ -558,6 +528,14 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error {
 		switch action := action.(type) {
 		default:
 			return fmt.Errorf("unknown action type %s", action.Type())
+		case *PoliceAction:
+			table := attr.AddRtAttr(tabIndex, nil)
+			tabIndex++
+			table.AddRtAttr(nl.TCA_ACT_KIND, nl.ZeroTerminated("police"))
+			aopts := table.AddRtAttr(nl.TCA_ACT_OPTIONS, nil)
+			if err := encodePolice(aopts, action); err != nil {
+				return err
+			}
 		case *MirredAction:
 			table := attr.AddRtAttr(tabIndex, nil)
 			tabIndex++
@@ -652,6 +630,29 @@ func EncodeActions(attr *nl.RtAttr, actions []Action) error {
 	return nil
 }
 
+func parsePolice(data syscall.NetlinkRouteAttr, police *PoliceAction) {
+	switch data.Attr.Type {
+	case nl.TCA_POLICE_RESULT:
+		police.NotExceedAction = TcPolAct(native.Uint32(data.Value[0:4]))
+	case nl.TCA_POLICE_AVRATE:
+		police.AvRate = native.Uint32(data.Value[0:4])
+	case nl.TCA_POLICE_TBF:
+		p := *nl.DeserializeTcPolice(data.Value)
+		police.ActionAttrs = ActionAttrs{}
+		police.Attrs().Index = int(p.Index)
+		police.Attrs().Bindcnt = int(p.Bindcnt)
+		police.Attrs().Capab = int(p.Capab)
+		police.Attrs().Refcnt = int(p.Refcnt)
+		police.ExceedAction = TcPolAct(p.Action)
+		police.Rate = p.Rate.Rate
+		police.PeakRate = p.PeakRate.Rate
+		police.Burst = Xmitsize(uint64(p.Rate.Rate), p.Burst)
+		police.Mtu = p.Mtu
+		police.LinkLayer = int(p.Rate.Linklayer) & nl.TC_LINKLAYER_MASK
+		police.Overhead = p.Rate.Overhead
+	}
+}
+
 func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
 	var actions []Action
 	for _, table := range tables {
@@ -680,6 +681,8 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
 					action = &TunnelKeyAction{}
 				case "skbedit":
 					action = &SkbEditAction{}
+				case "police":
+					action = &PoliceAction{}
 				default:
 					break nextattr
 				}
@@ -758,6 +761,8 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) {
 							gen := *nl.DeserializeTcGen(adatum.Value)
 							toAttrs(&gen, action.Attrs())
 						}
+					case "police":
+						parsePolice(adatum, action.(*PoliceAction))
 					}
 				}
 			}
@@ -813,7 +818,7 @@ func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
 }
 
 func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
-	fw := filter.(*Fw)
+	fw := filter.(*FwFilter)
 	detailed := true
 	for _, datum := range data {
 		switch datum.Attr.Type {
@@ -824,17 +829,12 @@ func parseFwData(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) {
 		case nl.TCA_FW_INDEV:
 			fw.InDev = string(datum.Value[:len(datum.Value)-1])
 		case nl.TCA_FW_POLICE:
+			var police PoliceAction
 			adata, _ := nl.ParseRouteAttr(datum.Value)
 			for _, aattr := range adata {
-				switch aattr.Attr.Type {
-				case nl.TCA_POLICE_TBF:
-					fw.Police = *nl.DeserializeTcPolice(aattr.Value)
-				case nl.TCA_POLICE_RATE:
-					fw.Rtab = DeserializeRtab(aattr.Value)
-				case nl.TCA_POLICE_PEAKRATE:
-					fw.Ptab = DeserializeRtab(aattr.Value)
-				}
+				parsePolice(aattr, &police)
 			}
+			fw.Police = &police
 		}
 	}
 	return detailed, nil
diff --git a/vendor/github.com/vishvananda/netlink/link.go b/vendor/github.com/vishvananda/netlink/link.go
index bdcb994ca0..82d0360eba 100644
--- a/vendor/github.com/vishvananda/netlink/link.go
+++ b/vendor/github.com/vishvananda/netlink/link.go
@@ -1073,6 +1073,37 @@ func (ip6tnl *Ip6tnl) Type() string {
 	return "ip6tnl"
 }
 
+// from https://elixir.bootlin.com/linux/v5.15.4/source/include/uapi/linux/if_tunnel.h#L84
+type TunnelEncapType uint16
+
+const (
+	None TunnelEncapType = iota
+	FOU
+	GUE
+)
+
+// from https://elixir.bootlin.com/linux/v5.15.4/source/include/uapi/linux/if_tunnel.h#L91
+type TunnelEncapFlag uint16
+
+const (
+	CSum    TunnelEncapFlag = 1 << 0
+	CSum6                   = 1 << 1
+	RemCSum                 = 1 << 2
+)
+
+// from https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/ip6_tunnel.h#L12
+type IP6TunnelFlag uint16
+
+const (
+	IP6_TNL_F_IGN_ENCAP_LIMIT    IP6TunnelFlag = 1  // don't add encapsulation limit if one isn't present in inner packet
+	IP6_TNL_F_USE_ORIG_TCLASS                  = 2  // copy the traffic class field from the inner packet
+	IP6_TNL_F_USE_ORIG_FLOWLABEL               = 4  // copy the flowlabel from the inner packet
+	IP6_TNL_F_MIP6_DEV                         = 8  // being used for Mobile IPv6
+	IP6_TNL_F_RCV_DSCP_COPY                    = 10 // copy DSCP from the outer packet
+	IP6_TNL_F_USE_ORIG_FWMARK                  = 20 // copy fwmark from inner packet
+	IP6_TNL_F_ALLOW_LOCAL_REMOTE               = 40 // allow remote endpoint on the local node
+)
+
 type Sittun struct {
 	LinkAttrs
 	Link       uint32
diff --git a/vendor/github.com/vishvananda/netlink/link_linux.go b/vendor/github.com/vishvananda/netlink/link_linux.go
index ad8447b2be..aa998e3113 100644
--- a/vendor/github.com/vishvananda/netlink/link_linux.go
+++ b/vendor/github.com/vishvananda/netlink/link_linux.go
@@ -1146,6 +1146,10 @@ func (h *Handle) LinkAdd(link Link) error {
 	return h.linkModify(link, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
 }
 
+func LinkModify(link Link) error {
+	return pkgHandle.LinkModify(link)
+}
+
 func (h *Handle) LinkModify(link Link) error {
 	return h.linkModify(link, unix.NLM_F_REQUEST|unix.NLM_F_ACK)
 }
diff --git a/vendor/github.com/vishvananda/netlink/nl/nl_linux.go b/vendor/github.com/vishvananda/netlink/nl/nl_linux.go
index dcd4b94695..a49f675701 100644
--- a/vendor/github.com/vishvananda/netlink/nl/nl_linux.go
+++ b/vendor/github.com/vishvananda/netlink/nl/nl_linux.go
@@ -27,7 +27,8 @@ const (
 	// tc rules or filters, or other more memory requiring data.
 	RECEIVE_BUFFER_SIZE = 65536
 	// Kernel netlink pid
-	PidKernel uint32 = 0
+	PidKernel     uint32 = 0
+	SizeofCnMsgOp        = 0x18
 )
 
 // SupportedNlFamilies contains the list of netlink families this netlink package supports
@@ -85,6 +86,56 @@ type NetlinkRequestData interface {
 	Serialize() []byte
 }
 
+const (
+	PROC_CN_MCAST_LISTEN = 1
+	PROC_CN_MCAST_IGNORE
+)
+
+type CbID struct {
+	Idx uint32
+	Val uint32
+}
+
+type CnMsg struct {
+	ID     CbID
+	Seq    uint32
+	Ack    uint32
+	Length uint16
+	Flags  uint16
+}
+
+type CnMsgOp struct {
+	CnMsg
+	// here we differ from the C header
+	Op uint32
+}
+
+func NewCnMsg(idx, val, op uint32) *CnMsgOp {
+	var cm CnMsgOp
+
+	cm.ID.Idx = idx
+	cm.ID.Val = val
+
+	cm.Ack = 0
+	cm.Seq = 1
+	cm.Length = uint16(binary.Size(op))
+	cm.Op = op
+
+	return &cm
+}
+
+func (msg *CnMsgOp) Serialize() []byte {
+	return (*(*[SizeofCnMsgOp]byte)(unsafe.Pointer(msg)))[:]
+}
+
+func DeserializeCnMsgOp(b []byte) *CnMsgOp {
+	return (*CnMsgOp)(unsafe.Pointer(&b[0:SizeofCnMsgOp][0]))
+}
+
+func (msg *CnMsgOp) Len() int {
+	return SizeofCnMsgOp
+}
+
 // IfInfomsg is related to links, but it is used for list requests as well
 type IfInfomsg struct {
 	unix.IfInfomsg
diff --git a/vendor/github.com/vishvananda/netlink/proc_event.go b/vendor/github.com/vishvananda/netlink/proc_event.go
new file mode 100644
index 0000000000..53bc59a6ec
--- /dev/null
+++ b/vendor/github.com/vishvananda/netlink/proc_event.go
@@ -0,0 +1,217 @@
+package netlink
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"os"
+	"syscall"
+
+	"github.com/vishvananda/netlink/nl"
+	"github.com/vishvananda/netns"
+	"golang.org/x/sys/unix"
+)
+
+const CN_IDX_PROC = 0x1
+
+const (
+	PROC_EVENT_NONE     = 0x00000000
+	PROC_EVENT_FORK     = 0x00000001
+	PROC_EVENT_EXEC     = 0x00000002
+	PROC_EVENT_UID      = 0x00000004
+	PROC_EVENT_GID      = 0x00000040
+	PROC_EVENT_SID      = 0x00000080
+	PROC_EVENT_PTRACE   = 0x00000100
+	PROC_EVENT_COMM     = 0x00000200
+	PROC_EVENT_COREDUMP = 0x40000000
+	PROC_EVENT_EXIT     = 0x80000000
+)
+
+const (
+	CN_VAL_PROC          = 0x1
+	PROC_CN_MCAST_LISTEN = 0x1
+)
+
+type ProcEventMsg interface {
+	Pid() uint32
+	Tgid() uint32
+}
+
+type ProcEventHeader struct {
+	What      uint32
+	CPU       uint32
+	Timestamp uint64
+}
+
+type ProcEvent struct {
+	ProcEventHeader
+	Msg ProcEventMsg
+}
+
+func (pe *ProcEvent) setHeader(h ProcEventHeader) {
+	pe.What = h.What
+	pe.CPU = h.CPU
+	pe.Timestamp = h.Timestamp
+}
+
+type ExitProcEvent struct {
+	ProcessPid  uint32
+	ProcessTgid uint32
+	ExitCode    uint32
+	ExitSignal  uint32
+	ParentPid   uint32
+	ParentTgid  uint32
+}
+
+type ExitProcEvent2 struct {
+	ProcessPid  uint32
+	ProcessTgid uint32
+	ExitCode    uint32
+	ExitSignal  uint32
+	ParentPid   uint32
+	ParentTgid  uint32
+}
+
+func (e *ExitProcEvent) Pid() uint32 {
+	return e.ProcessPid
+}
+
+func (e *ExitProcEvent) Tgid() uint32 {
+	return e.ProcessTgid
+}
+
+type ExecProcEvent struct {
+	ProcessPid  uint32
+	ProcessTgid uint32
+}
+
+func (e *ExecProcEvent) Pid() uint32 {
+	return e.ProcessPid
+}
+
+func (e *ExecProcEvent) Tgid() uint32 {
+	return e.ProcessTgid
+}
+
+type ForkProcEvent struct {
+	ParentPid  uint32
+	ParentTgid uint32
+	ChildPid   uint32
+	ChildTgid  uint32
+}
+
+func (e *ForkProcEvent) Pid() uint32 {
+	return e.ParentPid
+}
+
+func (e *ForkProcEvent) Tgid() uint32 {
+	return e.ParentTgid
+}
+
+type CommProcEvent struct {
+	ProcessPid  uint32
+	ProcessTgid uint32
+	Comm        [16]byte
+}
+
+func (e *CommProcEvent) Pid() uint32 {
+	return e.ProcessPid
+}
+
+func (e *CommProcEvent) Tgid() uint32 {
+	return e.ProcessTgid
+}
+
+func ProcEventMonitor(ch chan<- ProcEvent, done <-chan struct{}, errorChan chan<- error) error {
+	h, err := NewHandle()
+	if err != nil {
+		return err
+	}
+	defer h.Delete()
+
+	s, err := nl.SubscribeAt(netns.None(), netns.None(), unix.NETLINK_CONNECTOR, CN_IDX_PROC)
+	if err != nil {
+		return err
+	}
+
+	var nlmsg nl.NetlinkRequest
+
+	nlmsg.Pid = uint32(os.Getpid())
+	nlmsg.Type = unix.NLMSG_DONE
+	nlmsg.Len = uint32(unix.SizeofNlMsghdr)
+
+	cm := nl.NewCnMsg(CN_IDX_PROC, CN_VAL_PROC, PROC_CN_MCAST_LISTEN)
+	nlmsg.AddData(cm)
+
+	s.Send(&nlmsg)
+
+	if done != nil {
+		go func() {
+			<-done
+			s.Close()
+		}()
+	}
+
+	go func() {
+		defer close(ch)
+		for {
+			msgs, from, err := s.Receive()
+			if err != nil {
+				errorChan <- err
+				return
+			}
+			if from.Pid != nl.PidKernel {
+				errorChan <- fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
+				return
+			}
+
+			for _, m := range msgs {
+				e := parseNetlinkMessage(m)
+				if e != nil {
+					ch <- *e
+				}
+			}
+
+		}
+	}()
+
+	return nil
+}
+
+func parseNetlinkMessage(m syscall.NetlinkMessage) *ProcEvent {
+	if m.Header.Type == unix.NLMSG_DONE {
+		buf := bytes.NewBuffer(m.Data)
+		msg := &nl.CnMsg{}
+		hdr := &ProcEventHeader{}
+		binary.Read(buf, nl.NativeEndian(), msg)
+		binary.Read(buf, nl.NativeEndian(), hdr)
+
+		pe := &ProcEvent{}
+		pe.setHeader(*hdr)
+		switch hdr.What {
+		case PROC_EVENT_EXIT:
+			event := &ExitProcEvent{}
+			binary.Read(buf, nl.NativeEndian(), event)
+			pe.Msg = event
+			return pe
+		case PROC_EVENT_FORK:
+			event := &ForkProcEvent{}
+			binary.Read(buf, nl.NativeEndian(), event)
+			pe.Msg = event
+			return pe
+		case PROC_EVENT_EXEC:
+			event := &ExecProcEvent{}
+			binary.Read(buf, nl.NativeEndian(), event)
+			pe.Msg = event
+			return pe
+		case PROC_EVENT_COMM:
+			event := &CommProcEvent{}
+			binary.Read(buf, nl.NativeEndian(), event)
+			pe.Msg = event
+			return pe
+		}
+		return nil
+	}
+
+	return nil
+}
diff --git a/vendor/github.com/vishvananda/netlink/qdisc_linux.go b/vendor/github.com/vishvananda/netlink/qdisc_linux.go
index 9fc4b3d241..e182e1cfe6 100644
--- a/vendor/github.com/vishvananda/netlink/qdisc_linux.go
+++ b/vendor/github.com/vishvananda/netlink/qdisc_linux.go
@@ -706,3 +706,7 @@ func Xmittime(rate uint64, size uint32) uint32 {
 	// https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/tc/tc_core.c#n62
 	return time2Tick(uint32(TIME_UNITS_PER_SEC * (float64(size) / float64(rate))))
 }
+
+func Xmitsize(rate uint64, ticks uint32) uint32 {
+	return uint32((float64(rate) * float64(tick2Time(ticks))) / TIME_UNITS_PER_SEC)
+}
diff --git a/vendor/github.com/vishvananda/netlink/xfrm_policy_linux.go b/vendor/github.com/vishvananda/netlink/xfrm_policy_linux.go
index 694bd74e6d..3584968041 100644
--- a/vendor/github.com/vishvananda/netlink/xfrm_policy_linux.go
+++ b/vendor/github.com/vishvananda/netlink/xfrm_policy_linux.go
@@ -93,8 +93,10 @@ func (h *Handle) xfrmPolicyAddOrUpdate(policy *XfrmPolicy, nlProto int) error {
 		req.AddData(out)
 	}
 
-	ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(policy.Ifid)))
-	req.AddData(ifId)
+	if policy.Ifid != 0 {
+		ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(policy.Ifid)))
+		req.AddData(ifId)
+	}
 
 	_, err := req.Execute(unix.NETLINK_XFRM, 0)
 	return err
@@ -189,8 +191,10 @@ func (h *Handle) xfrmPolicyGetOrDelete(policy *XfrmPolicy, nlProto int) (*XfrmPo
 		req.AddData(out)
 	}
 
-	ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(policy.Ifid)))
-	req.AddData(ifId)
+	if policy.Ifid != 0 {
+		ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(policy.Ifid)))
+		req.AddData(ifId)
+	}
 
 	resType := nl.XFRM_MSG_NEWPOLICY
 	if nlProto == nl.XFRM_MSG_DELPOLICY {
diff --git a/vendor/github.com/vishvananda/netlink/xfrm_state_linux.go b/vendor/github.com/vishvananda/netlink/xfrm_state_linux.go
index 3b37b87d38..61a2d2dea2 100644
--- a/vendor/github.com/vishvananda/netlink/xfrm_state_linux.go
+++ b/vendor/github.com/vishvananda/netlink/xfrm_state_linux.go
@@ -167,8 +167,10 @@ func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error {
 		}
 	}
 
-	ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid)))
-	req.AddData(ifId)
+	if state.Ifid != 0 {
+		ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid)))
+		req.AddData(ifId)
+	}
 
 	_, err := req.Execute(unix.NETLINK_XFRM, 0)
 	return err
@@ -281,8 +283,10 @@ func (h *Handle) xfrmStateGetOrDelete(state *XfrmState, nlProto int) (*XfrmState
 		req.AddData(out)
 	}
 
-	ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid)))
-	req.AddData(ifId)
+	if state.Ifid != 0 {
+		ifId := nl.NewRtAttr(nl.XFRMA_IF_ID, nl.Uint32Attr(uint32(state.Ifid)))
+		req.AddData(ifId)
+	}
 
 	resType := nl.XFRM_MSG_NEWSA
 	if nlProto == nl.XFRM_MSG_DELSA {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 902982947a..3b05b819b8 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -749,7 +749,7 @@ github.com/tklauser/go-sysconf
 # github.com/tklauser/numcpus v0.3.0
 ## explicit; go 1.11
 github.com/tklauser/numcpus
-# github.com/vishvananda/netlink v1.1.1-0.20211101163509-b10eb8fe5cf6
+# github.com/vishvananda/netlink v1.1.1-0.20220117225832-796d4ea903f3
 ## explicit; go 1.12
 github.com/vishvananda/netlink
 github.com/vishvananda/netlink/nl


Test patch:
diff --git a/bpf/tests/bpf_ct_tests.c b/bpf/tests/bpf_ct_tests.c
index d197376971..031e6c6e2a 100644
--- a/bpf/tests/bpf_ct_tests.c
+++ b/bpf/tests/bpf_ct_tests.c
@@ -5,7 +5,6 @@
 #include <bpf/api.h>
 
 /* most values taken from node_config.h */
-#define CONNTRACK
 #define ENDPOINTS_MAP test_cilium_lxc
 #define POLICY_PROG_MAP_SIZE ENDPOINTS_MAP_SIZE
 #define METRICS_MAP test_cilium_metrics
diff --git a/cilium/cmd/encrypt_status_test.go b/cilium/cmd/encrypt_status_test.go
index eda33a79e7..24daad84c5 100644
--- a/cilium/cmd/encrypt_status_test.go
+++ b/cilium/cmd/encrypt_status_test.go
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: Apache-2.0
-// Copyright 2021 Authors of Cilium
+// Copyright 2021-2022 Authors of Cilium
 
 //go:build privileged_tests
 // +build privileged_tests
@@ -22,13 +22,17 @@ func Test(t *testing.T) {
 	TestingT(t)
 }
 
-type EncryptStatusSuite struct{}
+type EncryptStatusSuite struct {
+	currentNetNS netns.NsHandle
+}
 
 var _ = Suite(&EncryptStatusSuite{})
 
-const (
-	procTestFixtures = "fixtures/proc"
-)
+func (s *EncryptStatusSuite) SetUpSuite(c *C) {
+	var err error
+	s.currentNetNS, err = netns.Get()
+	c.Assert(err, IsNil)
+}
 
 func getXfrmState(src string, dst string, spi int, key string) *netlink.XfrmState {
 	k, _ := hex.DecodeString(key)
@@ -48,8 +52,12 @@ func getXfrmState(src string, dst string, spi int, key string) *netlink.XfrmStat
 
 func (s *EncryptStatusSuite) TestCountUniqueIPsecKeys(c *C) {
 	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ns, err := netns.New()
 	c.Assert(err, IsNil)
+	defer func() { c.Assert(ns.Close(), IsNil) }()
+	defer func() { c.Assert(netns.Set(s.currentNetNS), IsNil) }()
 
 	keys := countUniqueIPsecKeys()
 	c.Assert(keys, Equals, 0)
@@ -69,11 +77,10 @@ func (s *EncryptStatusSuite) TestCountUniqueIPsecKeys(c *C) {
 
 	keys = countUniqueIPsecKeys()
 	c.Assert(keys, Equals, 2)
-
-	ns.Close()
-	runtime.UnlockOSThread()
 }
 
+const procTestFixtures = "fixtures/proc"
+
 func (s *EncryptStatusSuite) TestGetXfrmStats(c *C) {
 	errCount, m := getXfrmStats(procTestFixtures)
 	currentCount := 0
diff --git a/pkg/azure/api/api_test.go b/pkg/azure/api/api_test.go
deleted file mode 100644
index 7e798ef924..0000000000
--- a/pkg/azure/api/api_test.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-// Copyright 2020 Authors of Cilium
-
-//go:build !privileged_tests
-// +build !privileged_tests
-
-package api
-
-import (
-	"context"
-	"os"
-	"testing"
-	"time"
-
-	"gopkg.in/check.v1"
-
-	"github.com/cilium/cilium/pkg/api/metrics/mock"
-)
-
-func Test(t *testing.T) {
-	check.TestingT(t)
-}
-
-type ApiSuite struct{}
-
-var _ = check.Suite(&ApiSuite{})
-
-func (a *ApiSuite) TestRateLimit(c *check.C) {
-	// Since github.com/Azure/go-autorest/autorest/azure/auth v0.5.6, the
-	// MSI_ENDPOINT environment variable must be set, otherwise this test will
-	// fail with the error "failed to get oauth token from MSI: MSI not
-	// available".
-	//
-	// Temporarily set the environment variable for the duration of the test,
-	// using the same workaround as used in
-	// github.com/Azure/go-autorest/autorest/azure/auth's tests.
-	os.Setenv("MSI_ENDPOINT", "http://localhost")
-	defer func() {
-		os.Unsetenv("MSI_ENDPOINT")
-	}()
-
-	metricsAPI := mock.NewMockMetrics()
-	client, err := NewClient("AZUREPUBLICCLOUD", "dummy-subscription", "dummy-resource-group", "", metricsAPI, 10.0, 4, true)
-	c.Assert(err, check.IsNil)
-	c.Assert(client, check.Not(check.IsNil))
-
-	for i := 0; i < 10; i++ {
-		client.limiter.Limit(context.TODO(), "test")
-	}
-
-	c.Assert(metricsAPI.RateLimit("test"), check.Not(check.DeepEquals), time.Duration(0))
-}
diff --git a/pkg/policy/l3_test.go b/pkg/policy/l3_test.go
deleted file mode 100644
index f0b377df52..0000000000
--- a/pkg/policy/l3_test.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-// Copyright 2018 Authors of Cilium
-
-//go:build !privileged_tests
-// +build !privileged_tests
-
-package policy
-
-import (
-	. "gopkg.in/check.v1"
-
-	"github.com/cilium/cilium/pkg/checker"
-	"github.com/cilium/cilium/pkg/cidr"
-	"github.com/cilium/cilium/pkg/labels"
-	"github.com/cilium/cilium/pkg/node"
-)
-
-func (ds *PolicyTestSuite) SetUpTest(c *C) {
-	v6node := cidr.MustParseCIDR("2001:DB8::/96")
-	v4node := cidr.MustParseCIDR("192.0.2.3/24")
-	node.SetIPv6NodeRange(v6node)
-	node.SetIPv4AllocRange(v4node)
-}
-
-func (ds *PolicyTestSuite) TearDownTest(c *C) {
-	node.Uninitialize()
-}
-
-func populateMap(m *CIDRPolicyMap) {
-	lbls := labels.LabelArray{}
-	m.Insert("10.1.1.0/24", lbls)
-	m.Insert("10.2.0.0/20", lbls)
-	m.Insert("10.3.3.3/32", lbls)
-	m.Insert("10.4.4.0/26", lbls)
-	m.Insert("10.5.0.0/16", lbls)
-}
-
-func (ds *PolicyTestSuite) TestToBPFData(c *C) {
-	cidrPolicy := NewCIDRPolicy()
-
-	populateMap(&cidrPolicy.Ingress)
-	_, s4 := cidrPolicy.ToBPFData()
-	exp := []int{32, 26, 24, 20, 16, 0}
-	c.Assert(s4, checker.DeepEquals, exp)
-
-	cidrPolicy = NewCIDRPolicy()
-	// 32 represent the host and 0 the world prefixes.
-	populateMap(&cidrPolicy.Egress)
-	_, s4 = cidrPolicy.ToBPFData()
-	exp = []int{32, 26, 24, 20, 16, 0}
-	c.Assert(s4, checker.DeepEquals, exp)
-}
-
-func (ds *PolicyTestSuite) TestGetDefaultPrefixLengths(c *C) {
-	expected6 := []int{128, 0}
-	expected4 := []int{32, 0}
-	s6, s4 := GetDefaultPrefixLengths()
-
-	c.Assert(s6, checker.DeepEquals, expected6)
-	c.Assert(s4, checker.DeepEquals, expected4)
-}
diff --git a/pkg/policy/resolve_deny_test.go b/pkg/policy/resolve_deny_test.go
index 6de753f067..297184b0aa 100644
--- a/pkg/policy/resolve_deny_test.go
+++ b/pkg/policy/resolve_deny_test.go
@@ -165,7 +165,6 @@ func (ds *PolicyTestSuite) TestL3WithIngressDenyWildcard(c *C) {
 				Egress: L4PolicyMap{},
 			},
 			IngressPolicyEnabled: true,
-			CIDRPolicy:           policy.CIDRPolicy,
 		},
 		PolicyOwner: DummyOwner{},
 		// inherit this from the result as it is outside of the scope
@@ -249,7 +248,6 @@ func (ds *PolicyTestSuite) TestL3WithLocalHostWildcardd(c *C) {
 				Egress: L4PolicyMap{},
 			},
 			IngressPolicyEnabled: true,
-			CIDRPolicy:           policy.CIDRPolicy,
 		},
 		PolicyOwner: DummyOwner{},
 		// inherit this from the result as it is outside of the scope
@@ -332,7 +330,6 @@ func (ds *PolicyTestSuite) TestMapStateWithIngressDenyWildcard(c *C) {
 				Egress: L4PolicyMap{},
 			},
 			IngressPolicyEnabled: true,
-			CIDRPolicy:           policy.CIDRPolicy,
 		},
 		PolicyOwner: DummyOwner{},
 		PolicyMapState: MapState{
@@ -471,7 +468,6 @@ func (ds *PolicyTestSuite) TestMapStateWithIngressDeny(c *C) {
 				Egress: L4PolicyMap{},
 			},
 			IngressPolicyEnabled: true,
-			CIDRPolicy:           policy.CIDRPolicy,
 		},
 		PolicyOwner: DummyOwner{},
 		PolicyMapState: MapState{
diff --git a/pkg/policy/resolve_test.go b/pkg/policy/resolve_test.go
index 8a0eecd8b7..96567af8a3 100644
--- a/pkg/policy/resolve_test.go
+++ b/pkg/policy/resolve_test.go
@@ -321,7 +321,6 @@ func (ds *PolicyTestSuite) TestL7WithIngressWildcard(c *C) {
 				},
 				Egress: L4PolicyMap{},
 			},
-			CIDRPolicy:           policy.CIDRPolicy,
 			IngressPolicyEnabled: true,
 			EgressPolicyEnabled:  false,
 		},
@@ -417,7 +416,6 @@ func (ds *PolicyTestSuite) TestL7WithLocalHostWildcardd(c *C) {
 				},
 				Egress: L4PolicyMap{},
 			},
-			CIDRPolicy:           policy.CIDRPolicy,
 			IngressPolicyEnabled: true,
 			EgressPolicyEnabled:  false,
 		},
@@ -502,7 +500,6 @@ func (ds *PolicyTestSuite) TestMapStateWithIngressWildcard(c *C) {
 				},
 				Egress: L4PolicyMap{},
 			},
-			CIDRPolicy:           policy.CIDRPolicy,
 			IngressPolicyEnabled: true,
 			EgressPolicyEnabled:  false,
 		},
@@ -642,7 +639,6 @@ func (ds *PolicyTestSuite) TestMapStateWithIngress(c *C) {
 				},
 				Egress: L4PolicyMap{},
 			},
-			CIDRPolicy:           policy.CIDRPolicy,
 			IngressPolicyEnabled: true,
 			EgressPolicyEnabled:  false,
 		},
diff --git a/pkg/policy/rule_test.go b/pkg/policy/rule_test.go
index 63f343c249..b428031dda 100644
--- a/pkg/policy/rule_test.go
+++ b/pkg/policy/rule_test.go
@@ -9,7 +9,6 @@ package policy
 import (
 	"bytes"
 	stdlog "log"
-	"net"
 
 	. "gopkg.in/check.v1"
 
@@ -937,37 +936,6 @@ func (ds *PolicyTestSuite) TestL3Policy(c *C) {
 	err = rule1.Sanitize()
 	c.Assert(err, IsNil)
 
-	expected := NewCIDRPolicy()
-	expected.Ingress.Map["10.0.1.0/24"] = &CIDRPolicyMapRule{Prefix: net.IPNet{IP: []byte{10, 0, 1, 0}, Mask: []byte{255, 255, 255, 0}}, DerivedFromRules: labels.LabelArrayList{nil}}
-	expected.Ingress.Map["192.168.2.0/24"] = &CIDRPolicyMapRule{Prefix: net.IPNet{IP: []byte{192, 168, 2, 0}, Mask: []byte{255, 255, 255, 0}}, DerivedFromRules: labels.LabelArrayList{nil}}
-	expected.Ingress.Map["10.0.3.1/32"] = &CIDRPolicyMapRule{Prefix: net.IPNet{IP: []byte{10, 0, 3, 1}, Mask: []byte{255, 255, 255, 255}}, DerivedFromRules: labels.LabelArrayList{nil}}
-	expected.Ingress.IPv4PrefixCount[32] = 1
-	expected.Ingress.IPv4PrefixCount[24] = 2
-	expected.Ingress.Map["2001:db8::/48"] = &CIDRPolicyMapRule{Prefix: net.IPNet{IP: []byte{0x20, 1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Mask: []byte{255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, DerivedFromRules: labels.LabelArrayList{nil}}
-	expected.Ingress.Map["2001:db9::/128"] = &CIDRPolicyMapRule{Prefix: net.IPNet{IP: []byte{0x20, 1, 0xd, 0xb9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}}, DerivedFromRules: labels.LabelArrayList{nil}}
-	expected.Ingress.IPv6PrefixCount[128] = 1
-	expected.Ingress.IPv6PrefixCount[48] = 1
-	expected.Egress.Map["10.1.0.0/16"] = &CIDRPolicyMapRule{Prefix: net.IPNet{IP: []byte{10, 1, 0, 0}, Mask: []byte{255, 255, 0, 0}}, DerivedFromRules: labels.LabelArrayList{nil}}
-	expected.Egress.Map["10.128.0.0/9"] = &CIDRPolicyMapRule{Prefix: net.IPNet{IP: []byte{10, 128, 0, 0}, Mask: []byte{255, 128, 0, 0}}, DerivedFromRules: labels.LabelArrayList{nil}}
-	expected.Egress.Map["10.0.0.0/10"] = &CIDRPolicyMapRule{Prefix: net.IPNet{IP: []byte{10, 0, 0, 0}, Mask: []byte{255, 192, 0, 0}}, DerivedFromRules: labels.LabelArrayList{nil}}
-	expected.Egress.Map["10.64.0.0/11"] = &CIDRPolicyMapRule{Prefix: net.IPNet{IP: []byte{10, 64, 0, 0}, Mask: []byte{255, 224, 0, 0}}, DerivedFromRules: labels.LabelArrayList{nil}}
-	expected.Egress.Map["10.112.0.0/12"] = &CIDRPolicyMapRule{Prefix: net.IPNet{IP: []byte{10, 112, 0, 0}, Mask: []byte{255, 240, 0, 0}}, DerivedFromRules: labels.LabelArrayList{nil}}
-	expected.Egress.IPv4PrefixCount[16] = 1
-	expected.Egress.IPv4PrefixCount[12] = 1
-	expected.Egress.IPv4PrefixCount[11] = 1
-	expected.Egress.IPv4PrefixCount[10] = 1
-	expected.Egress.IPv4PrefixCount[9] = 1
-	expected.Egress.Map["2001:dbf::/64"] = &CIDRPolicyMapRule{Prefix: net.IPNet{IP: []byte{0x20, 1, 0xd, 0xbf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}}, DerivedFromRules: labels.LabelArrayList{nil}}
-	expected.Egress.IPv6PrefixCount[64] = 1
-
-	toBar := &SearchContext{To: labels.ParseSelectLabelArray("bar")}
-	state := traceState{}
-	res := rule1.resolveCIDRPolicy(toBar, &state, NewCIDRPolicy())
-	c.Assert(res, Not(IsNil))
-	c.Assert(*res, checker.DeepEquals, *expected)
-	c.Assert(state.selectedRules, Equals, 1)
-	c.Assert(state.matchedRules, Equals, 0)
-
 	// Must be parsable, make sure Validate fails when not.
 	err = api.Rule{
 		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")),
@@ -1387,8 +1355,7 @@ func (ds *PolicyTestSuite) TestL3RuleLabels(c *C) {
 			rulesToApply:          []string{"rule0"},
 			expectedIngressLabels: nil,
 			expectedEgressLabels:  nil,
-		},
-		{
+		}, {
 			description:           "A rule that matches. Should apply labels",
 			rulesToApply:          []string{"rule1"},
 			expectedIngressLabels: map[string]labels.LabelArrayList{"10.0.1.0/32": {ruleLabels["rule1"]}},
@@ -1405,10 +1372,11 @@ func (ds *PolicyTestSuite) TestL3RuleLabels(c *C) {
 		}}
 
 	// endpoint selector for all tests
-	toBar := &SearchContext{To: labels.ParseSelectLabelArray("bar")}
+	toBar := &SearchContext{To: labels.ParseSelectLabelArray("bar"), Trace: TRACE_VERBOSE}
+	fromBar := &SearchContext{From: labels.ParseSelectLabelArray("bar"), Trace: TRACE_VERBOSE}
 
 	for _, test := range testCases {
-		finalPolicy := NewCIDRPolicy()
+		finalPolicy := NewL4Policy(0)
 		for _, r := range test.rulesToApply {
 			apiRule := rules[r]
 			err := apiRule.Sanitize()
@@ -1416,21 +1384,42 @@ func (ds *PolicyTestSuite) TestL3RuleLabels(c *C) {
 
 			rule := &rule{Rule: apiRule}
 
-			rule.resolveCIDRPolicy(toBar, &traceState{}, finalPolicy)
+			_, err = rule.resolveIngressPolicy(testPolicyContext, toBar, &traceState{}, finalPolicy.Ingress, nil, nil)
+			c.Assert(err, IsNil)
+			_, err = rule.resolveEgressPolicy(testPolicyContext, fromBar, &traceState{}, finalPolicy.Egress, nil, nil)
+			c.Assert(err, IsNil)
 		}
+		// For debugging the test:
+		//c.Assert(finalPolicy.Ingress, checker.DeepEquals, L4PolicyMap{})
 
-		c.Assert(len(finalPolicy.Ingress.Map), Equals, len(test.expectedIngressLabels), Commentf(test.description))
-		for cidrKey := range test.expectedIngressLabels {
-			out := finalPolicy.Ingress.Map[cidrKey]
-			c.Assert(out, Not(IsNil), Commentf(test.description))
-			c.Assert(out.DerivedFromRules, checker.DeepEquals, test.expectedIngressLabels[cidrKey], Commentf(test.description))
+		type expectedResult map[string]labels.LabelArrayList
+		mapDirectionalResultsToExpectedOutput := map[*L4Filter]expectedResult{
+			finalPolicy.Ingress[api.PortProtocolAny]: test.expectedIngressLabels,
+			finalPolicy.Egress[api.PortProtocolAny]:  test.expectedEgressLabels,
 		}
-
-		c.Assert(len(finalPolicy.Egress.Map), Equals, len(test.expectedEgressLabels), Commentf(test.description))
-		for cidrKey := range test.expectedEgressLabels {
-			out := finalPolicy.Egress.Map[cidrKey]
-			c.Assert(out, Not(IsNil), Commentf(test.description))
-			c.Assert(out.DerivedFromRules, checker.DeepEquals, test.expectedEgressLabels[cidrKey], Commentf(test.description))
+		for filter, exp := range mapDirectionalResultsToExpectedOutput {
+			if len(exp) > 0 {
+				for cidr, rule := range exp {
+					matches := false
+					for _, derived := range filter.DerivedFromRules {
+						if derived.Equals(rule[0]) {
+							matches = true
+							break
+						}
+					}
+					c.Assert(matches, Equals, true, Commentf("%s: expected filter %+v to be derived from rule %s", test.description, filter, rule))
+
+					matches = false
+					for sel := range filter.L7RulesPerSelector {
+						cidrLabels := labels.ParseLabelArray("cidr:" + cidr)
+						c.Logf("Testing %+v", cidrLabels)
+						if matches = sel.(*labelIdentitySelector).xxxMatches(cidrLabels); matches {
+							break
+						}
+					}
+					c.Assert(matches, Equals, true, Commentf("%s: expected cidr %s to match filter %+v", test.description, cidr, filter))
+				}
+			}
 		}
 	}
 }
diff --git a/test/bpf/unit-test.c b/test/bpf/unit-test.c
index 9ae716ef53..f98052a01d 100644
--- a/test/bpf/unit-test.c
+++ b/test/bpf/unit-test.c
@@ -22,8 +22,6 @@
 
 #define HAVE_LARGE_INSN_LIMIT
 
-#define CONNTRACK
-
 #define htonl bpf_htonl
 #define ntohl bpf_ntohl