Repository: cilium/cilium
Pull request #18479 (2022-01-14 07:42:21)

monitor: Output non-trace messages to stderr
When we use JSON output of cilium monitor, it would be useful if we
could filter the output by the tools like JQ. However, since some of the
non-JSON messages are generated through stdout, it ends up to the JSON
parse error. Fix `cilium monitor` command to generate non-JSON messages
to stderr.

Signed-off-by: Yutaro Hayakawa <yutaro.hayakawa@isovalent.com>

Please ensure your pull request adheres to the following guidelines:

- [x] For first time contributors, read [Submitting a pull request](https://docs.cilium.io/en/stable/contributing/development/contributing_guide/#submitting-a-pull-request)
- [x] All code is covered by unit and/or runtime tests where feasible.
- [x] All commits contain a well written commit description including a title,
      description and a `Fixes: #XXX` line if the commit addresses a particular
      GitHub issue.
- [x] All commits are signed off. See the section [Developer’s Certificate of Origin](https://docs.cilium.io/en/stable/contributing/development/contributing_guide/#dev-coo)
- [x] Provide a title or release-note blurb suitable for the release notes.
- [x] Thanks for contributing!


Patch:
diff --git a/.github/maintainers-little-helper.yaml b/.github/maintainers-little-helper.yaml
index c8e1473a5c..6ab69d032e 100644
--- a/.github/maintainers-little-helper.yaml
+++ b/.github/maintainers-little-helper.yaml
@@ -3,33 +3,33 @@ column: "In progress"
 move-to-projects-for-labels-xored:
   v1.11:
     needs-backport/1.11:
-      project: "https://github.com/cilium/cilium/projects/174"
+      project: "https://github.com/cilium/cilium/projects/179"
       column: "Needs backport from master"
     backport-pending/1.11:
-      project: "https://github.com/cilium/cilium/projects/174"
+      project: "https://github.com/cilium/cilium/projects/179"
       column: "Backport pending to v1.10"
     backport-done/1.11:
-      project: "https://github.com/cilium/cilium/projects/174"
+      project: "https://github.com/cilium/cilium/projects/179"
       column: "Backport done to v1.11"
   v1.10:
     needs-backport/1.10:
-      project: "https://github.com/cilium/cilium/projects/175"
+      project: "https://github.com/cilium/cilium/projects/178"
       column: "Needs backport from master"
     backport-pending/1.10:
-      project: "https://github.com/cilium/cilium/projects/175"
+      project: "https://github.com/cilium/cilium/projects/178"
       column: "Backport pending to v1.10"
     backport-done/1.10:
-      project: "https://github.com/cilium/cilium/projects/175"
+      project: "https://github.com/cilium/cilium/projects/178"
       column: "Backport done to v1.10"
   v1.9:
     needs-backport/1.9:
-      project: "https://github.com/cilium/cilium/projects/171"
+      project: "https://github.com/cilium/cilium/projects/177"
       column: "Needs backport from master"
     backport-pending/1.9:
-      project: "https://github.com/cilium/cilium/projects/171"
+      project: "https://github.com/cilium/cilium/projects/177"
       column: "Backport pending to v1.9"
     backport-done/1.9:
-      project: "https://github.com/cilium/cilium/projects/171"
+      project: "https://github.com/cilium/cilium/projects/177"
       column: "Backport done to v1.9"
 require-msgs-in-commit:
   - msg: "Signed-off-by"
diff --git a/.github/workflows/build-images-base.yaml b/.github/workflows/build-images-base.yaml
index 18da192afc..25f417cbe9 100644
--- a/.github/workflows/build-images-base.yaml
+++ b/.github/workflows/build-images-base.yaml
@@ -74,7 +74,7 @@ jobs:
 
       - name: Release build ${{ matrix.name }}
         if: ${{ steps.tag-in-repositories.outputs.exists == 'false' }}
-        uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
+        uses: docker/build-push-action@1814d3dfb36d6f84174e61f4a4b05bd84089a4b9
         id: docker_build_release
         with:
           context: ${{ matrix.context }}
diff --git a/.github/workflows/build-images-beta.yaml b/.github/workflows/build-images-beta.yaml
index 82032d08ef..85f8c16607 100644
--- a/.github/workflows/build-images-beta.yaml
+++ b/.github/workflows/build-images-beta.yaml
@@ -79,7 +79,7 @@ jobs:
           persist-credentials: false
 
       - name: Release Build ${{ matrix.name }}
-        uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
+        uses: docker/build-push-action@1814d3dfb36d6f84174e61f4a4b05bd84089a4b9
         id: docker_build_release
         with:
           context: .
diff --git a/.github/workflows/build-images-ci.yaml b/.github/workflows/build-images-ci.yaml
index 6c85b51ee4..28268175d9 100644
--- a/.github/workflows/build-images-ci.yaml
+++ b/.github/workflows/build-images-ci.yaml
@@ -103,7 +103,7 @@ jobs:
 
       # Import GitHub's cache build to docker cache
       - name: Copy ${{ matrix.name }} Golang cache to docker cache
-        uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
+        uses: docker/build-push-action@1814d3dfb36d6f84174e61f4a4b05bd84089a4b9
         with:
           context: /tmp/.cache/${{ matrix.name }}
           file: ./images/cache/Dockerfile
@@ -114,7 +114,7 @@ jobs:
       # master branch pushes
       - name: CI Build ${{ matrix.name }}
         if: ${{ github.event_name != 'pull_request_target' }}
-        uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
+        uses: docker/build-push-action@1814d3dfb36d6f84174e61f4a4b05bd84089a4b9
         id: docker_build_ci_master
         with:
           context: .
@@ -132,7 +132,7 @@ jobs:
 
       - name: CI race detection Build ${{ matrix.name }}
         if: ${{ github.event_name != 'pull_request_target' }}
-        uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
+        uses: docker/build-push-action@1814d3dfb36d6f84174e61f4a4b05bd84089a4b9
         id: docker_build_ci_master_detect_race_condition
         with:
           context: .
@@ -164,7 +164,7 @@ jobs:
       # PR updates
       - name: CI Build ${{ matrix.name }}
         if: ${{ github.event_name == 'pull_request_target' }}
-        uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
+        uses: docker/build-push-action@1814d3dfb36d6f84174e61f4a4b05bd84089a4b9
         id: docker_build_ci_pr
         with:
           context: .
@@ -185,7 +185,7 @@ jobs:
 
       - name: CI race detection Build ${{ matrix.name }}
         if: ${{ github.event_name == 'pull_request_target' }}
-        uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
+        uses: docker/build-push-action@1814d3dfb36d6f84174e61f4a4b05bd84089a4b9
         id: docker_build_ci_pr_detect_race_condition
         with:
           context: .
@@ -219,7 +219,7 @@ jobs:
       # Store docker's golang's cache build locally only on the main branch
       - name: Store ${{ matrix.name }} Golang cache build locally
         if: ${{ github.event_name != 'pull_request_target' && steps.cache.outputs.cache-hit != 'true' }}
-        uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
+        uses: docker/build-push-action@1814d3dfb36d6f84174e61f4a4b05bd84089a4b9
         with:
           context: .
           file: ./images/cache/Dockerfile
@@ -294,7 +294,7 @@ jobs:
 
       # Import GitHub's cache build to docker cache
       - name: Copy ${{ matrix.name }} Golang cache to docker cache
-        uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
+        uses: docker/build-push-action@1814d3dfb36d6f84174e61f4a4b05bd84089a4b9
         with:
           context: /tmp/.cache/${{ matrix.name }}
           file: ./images/cache/Dockerfile
@@ -303,7 +303,7 @@ jobs:
           target: import-cache
 
       - name: CI Build ${{ matrix.name }}
-        uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
+        uses: docker/build-push-action@1814d3dfb36d6f84174e61f4a4b05bd84089a4b9
         id: docker_build_ci_master
         with:
           context: .
@@ -335,7 +335,7 @@ jobs:
       # Store docker's golang's cache build locally only on the main branch
       - name: Store ${{ matrix.name }} Golang cache build locally
         if: ${{ github.event_name != 'pull_request_target' && steps.cache.outputs.cache-hit != 'true' }}
-        uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
+        uses: docker/build-push-action@1814d3dfb36d6f84174e61f4a4b05bd84089a4b9
         with:
           context: .
           file: ./images/cache/Dockerfile
diff --git a/.github/workflows/build-images-hotfixes.yaml b/.github/workflows/build-images-hotfixes.yaml
index b2890a5c80..5c2b992197 100644
--- a/.github/workflows/build-images-hotfixes.yaml
+++ b/.github/workflows/build-images-hotfixes.yaml
@@ -73,7 +73,7 @@ jobs:
           persist-credentials: false
 
       - name: Release Build ${{ matrix.name }}
-        uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
+        uses: docker/build-push-action@1814d3dfb36d6f84174e61f4a4b05bd84089a4b9
         id: docker_build_release
         with:
           context: .
diff --git a/.github/workflows/build-images-releases.yaml b/.github/workflows/build-images-releases.yaml
index c211a4e524..a5b2dc0169 100644
--- a/.github/workflows/build-images-releases.yaml
+++ b/.github/workflows/build-images-releases.yaml
@@ -71,7 +71,7 @@ jobs:
           persist-credentials: false
 
       - name: Release Build ${{ matrix.name }}
-        uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
+        uses: docker/build-push-action@1814d3dfb36d6f84174e61f4a4b05bd84089a4b9
         id: docker_build_release
         with:
           context: .
diff --git a/.github/workflows/conformance-aws-cni-v1.10.yaml b/.github/workflows/conformance-aws-cni-v1.10.yaml
index 97270112c3..85180ca4ec 100644
--- a/.github/workflows/conformance-aws-cni-v1.10.yaml
+++ b/.github/workflows/conformance-aws-cni-v1.10.yaml
@@ -162,7 +162,7 @@ jobs:
           rm eksctl_$(uname -s)_amd64.tar.gz
 
       - name: Set up AWS CLI credentials
-        uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
+        uses: aws-actions/configure-aws-credentials@05b148adc31e091bafbaf404f745055d4d3bc9d2
         with:
           aws-access-key-id: ${{ secrets.AWS_PR_SA_ID }}
           aws-secret-access-key: ${{ secrets.AWS_PR_SA_KEY }}
diff --git a/.github/workflows/conformance-aws-cni-v1.11.yaml b/.github/workflows/conformance-aws-cni-v1.11.yaml
index e4ecdd50e5..cf0dd61e78 100644
--- a/.github/workflows/conformance-aws-cni-v1.11.yaml
+++ b/.github/workflows/conformance-aws-cni-v1.11.yaml
@@ -162,7 +162,7 @@ jobs:
           rm eksctl_$(uname -s)_amd64.tar.gz
 
       - name: Set up AWS CLI credentials
-        uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
+        uses: aws-actions/configure-aws-credentials@05b148adc31e091bafbaf404f745055d4d3bc9d2
         with:
           aws-access-key-id: ${{ secrets.AWS_PR_SA_ID }}
           aws-secret-access-key: ${{ secrets.AWS_PR_SA_KEY }}
diff --git a/.github/workflows/conformance-aws-cni.yaml b/.github/workflows/conformance-aws-cni.yaml
index 1789e9d60e..a85b9a6863 100644
--- a/.github/workflows/conformance-aws-cni.yaml
+++ b/.github/workflows/conformance-aws-cni.yaml
@@ -165,7 +165,7 @@ jobs:
           rm eksctl_$(uname -s)_amd64.tar.gz
 
       - name: Set up AWS CLI credentials
-        uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
+        uses: aws-actions/configure-aws-credentials@05b148adc31e091bafbaf404f745055d4d3bc9d2
         with:
           aws-access-key-id: ${{ secrets.AWS_PR_SA_ID }}
           aws-secret-access-key: ${{ secrets.AWS_PR_SA_KEY }}
diff --git a/.github/workflows/conformance-eks-v1.10.yaml b/.github/workflows/conformance-eks-v1.10.yaml
index 867022aad3..0514ac997a 100644
--- a/.github/workflows/conformance-eks-v1.10.yaml
+++ b/.github/workflows/conformance-eks-v1.10.yaml
@@ -174,7 +174,7 @@ jobs:
           rm eksctl_$(uname -s)_amd64.tar.gz
 
       - name: Set up AWS CLI credentials
-        uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
+        uses: aws-actions/configure-aws-credentials@05b148adc31e091bafbaf404f745055d4d3bc9d2
         with:
           aws-access-key-id: ${{ secrets.AWS_PR_SA_ID }}
           aws-secret-access-key: ${{ secrets.AWS_PR_SA_KEY }}
diff --git a/.github/workflows/conformance-eks-v1.11.yaml b/.github/workflows/conformance-eks-v1.11.yaml
index 972d5411eb..3091a185a2 100644
--- a/.github/workflows/conformance-eks-v1.11.yaml
+++ b/.github/workflows/conformance-eks-v1.11.yaml
@@ -174,7 +174,7 @@ jobs:
           rm eksctl_$(uname -s)_amd64.tar.gz
 
       - name: Set up AWS CLI credentials
-        uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
+        uses: aws-actions/configure-aws-credentials@05b148adc31e091bafbaf404f745055d4d3bc9d2
         with:
           aws-access-key-id: ${{ secrets.AWS_PR_SA_ID }}
           aws-secret-access-key: ${{ secrets.AWS_PR_SA_KEY }}
diff --git a/.github/workflows/conformance-eks.yaml b/.github/workflows/conformance-eks.yaml
index 43ba596be3..fc4ed4820d 100644
--- a/.github/workflows/conformance-eks.yaml
+++ b/.github/workflows/conformance-eks.yaml
@@ -176,7 +176,7 @@ jobs:
           rm eksctl_$(uname -s)_amd64.tar.gz
 
       - name: Set up AWS CLI credentials
-        uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
+        uses: aws-actions/configure-aws-credentials@05b148adc31e091bafbaf404f745055d4d3bc9d2
         with:
           aws-access-key-id: ${{ secrets.AWS_PR_SA_ID }}
           aws-secret-access-key: ${{ secrets.AWS_PR_SA_KEY }}
diff --git a/.github/workflows/lint-codeql.yaml b/.github/workflows/lint-codeql.yaml
index 9c027f19f9..cec961f51d 100644
--- a/.github/workflows/lint-codeql.yaml
+++ b/.github/workflows/lint-codeql.yaml
@@ -51,8 +51,8 @@ jobs:
         persist-credentials: false
         fetch-depth: 1
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@cd783c8a29bdcf5a5c79c5137889e24651fa626c
+      uses: github/codeql-action/init@8a4b243fbf9a03a93e93a71c1ec257347041f9c4
       with:
         languages: go
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@cd783c8a29bdcf5a5c79c5137889e24651fa626c
+      uses: github/codeql-action/analyze@8a4b243fbf9a03a93e93a71c1ec257347041f9c4
diff --git a/Documentation/cmdref/cilium-agent.md b/Documentation/cmdref/cilium-agent.md
index bcc2ea9632..e57b64222e 100644
--- a/Documentation/cmdref/cilium-agent.md
+++ b/Documentation/cmdref/cilium-agent.md
@@ -126,7 +126,6 @@ cilium-agent [flags]
       --enable-xt-socket-fallback                            Enable fallback for missing xt_socket module (default true)
       --encrypt-interface string                             Transparent encryption interface
       --encrypt-node                                         Enables encrypting traffic from non-Cilium pods and host networking
-      --endpoint-interface-name-prefix string                Prefix of interface name shared by all endpoints (default "lxc+")
       --endpoint-queue-size int                              size of EventQueue per-endpoint (default 25)
       --endpoint-status strings                              Enable additional CiliumEndpoint status features (controllers,health,log,policy,state)
       --envoy-log string                                     Path to a separate Envoy log file, if any
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/system_requirements.rst b/Documentation/operations/system_requirements.rst
index b1ddd4294a..984e2382eb 100644
--- a/Documentation/operations/system_requirements.rst
+++ b/Documentation/operations/system_requirements.rst
@@ -103,6 +103,9 @@ RancherOS_                 >= 1.5.5
 Linux Kernel
 ============
 
+Base Requirements
+~~~~~~~~~~~~~~~~~
+
 Cilium leverages and builds on the kernel eBPF functionality as well as various
 subsystems which integrate with eBPF. Therefore, host systems are required to
 run Linux kernel version 4.9.17 or later to run a Cilium agent. More recent
@@ -132,6 +135,21 @@ linked, either choice is valid.
    Users running Linux 4.10 or earlier with Cilium CIDR policies may face
    :ref:`cidr_limitations`.
 
+Requirements for Iptables-based Masquerading
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you are not using BPF for masquerading (``enable-bpf-masquerade=false``, the
+default value), then you will need the following kernel configuration options.
+
+::
+
+        CONFIG_NETFILTER_XT_SET=m
+        CONFIG_IP_SET=m
+        CONFIG_IP_SET_HASH_IP=m
+
+Requirements for L7 and FQDN Policies
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 L7 proxy redirection currently uses ``TPROXY`` iptables actions as well
 as ``socket`` matches. For L7 redirection to work as intended kernel
 configuration must include the following modules:
@@ -163,6 +181,46 @@ by adding the following to the helm configuration command line:
 
 .. _features_kernel_matrix:
 
+Requirements for IPsec
+~~~~~~~~~~~~~~~~~~~~~~
+
+The :ref:`encryption_ipsec` feature requires a lot of kernel configuration
+options, most of which to enable the actual encryption. Note that the
+specific options required depend on the algorithm. The list below
+corresponds to requirements for GMC-128-AES.
+
+::
+
+        CONFIG_XFRM=y
+        CONFIG_XFRM_OFFLOAD=y
+        CONFIG_XFRM_STATISTICS=y
+        CONFIG_XFRM_ALGO=m
+        CONFIG_XFRM_USER=m
+        CONFIG_INET{,6}_ESP=m
+        CONFIG_INET{,6}_IPCOMP=m
+        CONFIG_INET{,6}_XFRM_TUNNEL=m
+        CONFIG_INET{,6}_TUNNEL=m
+        CONFIG_INET_XFRM_MODE_TUNNEL=m
+        CONFIG_CRYPTO_AEAD=m
+        CONFIG_CRYPTO_AEAD2=m
+        CONFIG_CRYPTO_GCM=m
+        CONFIG_CRYPTO_SEQIV=m
+        CONFIG_CRYPTO_CBC=m
+        CONFIG_CRYPTO_HMAC=m
+        CONFIG_CRYPTO_SHA256=m
+        CONFIG_CRYPTO_AES=m
+
+Requirements for the Bandwidth Manager
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :ref:`bandwidth-manager` requires the following kernel configuration option
+to change the packet scheduling algorithm.
+
+::
+
+        CONFIG_NET_SCH_FQ=m
+
+
 Required Kernel Versions for Advanced Features
 ==============================================
 
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/README.rst b/README.rst
index aae62b2690..7a9e391106 100644
--- a/README.rst
+++ b/README.rst
@@ -32,11 +32,11 @@ Listed below are the actively maintained release branches along with their lates
 minor release, corresponding image pull tags and their release notes:
 
 +---------------------------------------------------------+------------+-----------------------------------+---------------------------------------------------------------------------+--------------------------------------------------------------------------------+
-| `v1.11 <https://github.com/cilium/cilium/tree/v1.11>`__ | 2021-12-09 | ``quay.io/cilium/cilium:v1.11.0`` | `Release Notes <https://github.com/cilium/cilium/releases/tag/v1.11.0>`__ | `General Announcement <https://isovalent.com/blog/post/2021-12-release-111>`__ |
+| `v1.11 <https://github.com/cilium/cilium/tree/v1.11>`__ | 2022-01-18 | ``quay.io/cilium/cilium:v1.11.1`` | `Release Notes <https://github.com/cilium/cilium/releases/tag/v1.11.1>`__ | `General Announcement <https://isovalent.com/blog/post/2021-12-release-111>`__ |
 +---------------------------------------------------------+------------+-----------------------------------+---------------------------------------------------------------------------+--------------------------------------------------------------------------------+
-| `v1.10 <https://github.com/cilium/cilium/tree/v1.10>`__ | 2021-12-09 | ``quay.io/cilium/cilium:v1.10.6`` | `Release Notes <https://github.com/cilium/cilium/releases/tag/v1.10.6>`__ | `General Announcement <https://cilium.io/blog/2021/05/20/cilium-110>`__        |
+| `v1.10 <https://github.com/cilium/cilium/tree/v1.10>`__ | 2022-01-18 | ``quay.io/cilium/cilium:v1.10.7`` | `Release Notes <https://github.com/cilium/cilium/releases/tag/v1.10.7>`__ | `General Announcement <https://cilium.io/blog/2021/05/20/cilium-110>`__        |
 +---------------------------------------------------------+------------+-----------------------------------+---------------------------------------------------------------------------+--------------------------------------------------------------------------------+
-| `v1.9 <https://github.com/cilium/cilium/tree/v1.9>`__   | 2021-11-05 | ``quay.io/cilium/cilium:v1.9.11`` | `Release Notes <https://github.com/cilium/cilium/releases/tag/v1.9.11>`__ | `General Announcement <https://cilium.io/blog/2020/11/10/cilium-19>`__         |
+| `v1.9 <https://github.com/cilium/cilium/tree/v1.9>`__   | 2022-01-18 | ``quay.io/cilium/cilium:v1.9.12`` | `Release Notes <https://github.com/cilium/cilium/releases/tag/v1.9.12>`__ | `General Announcement <https://cilium.io/blog/2020/11/10/cilium-19>`__         |
 +---------------------------------------------------------+------------+-----------------------------------+---------------------------------------------------------------------------+--------------------------------------------------------------------------------+
 
 Functionality Overview
diff --git a/bpf/bpf_host.c b/bpf/bpf_host.c
index b637121807..bf21f92a8c 100644
--- a/bpf/bpf_host.c
+++ b/bpf/bpf_host.c
@@ -1141,11 +1141,6 @@ int to_host(struct __ctx_buff *ctx)
 	 */
 	ctx_change_type(ctx, PACKET_HOST);
 #endif
-
-	if (!traced)
-		send_trace_notify(ctx, TRACE_TO_STACK, src_id, 0, 0,
-				  CILIUM_IFINDEX, 0, 0);
-
 #ifdef ENABLE_HOST_FIREWALL
 	if (!validate_ethertype(ctx, &proto)) {
 		ret = DROP_UNSUPPORTED_L2;
@@ -1183,6 +1178,10 @@ out:
 		return send_drop_notify_error(ctx, src_id, ret, CTX_ACT_DROP,
 					      METRIC_INGRESS);
 
+	if (!traced)
+		send_trace_notify(ctx, TRACE_TO_STACK, src_id, 0, 0,
+				  CILIUM_IFINDEX, 0, 0);
+
 	return ret;
 }
 
diff --git a/bpf/bpf_lxc.c b/bpf/bpf_lxc.c
index 2480b1ff44..e78e0e0a81 100644
--- a/bpf/bpf_lxc.c
+++ b/bpf/bpf_lxc.c
@@ -272,19 +272,21 @@ ct_recreate6:
 		policy_mark_skip(ctx);
 
 #ifdef ENABLE_NODEPORT
-		/* See comment in handle_ipv4_from_lxc(). */
-		if (ct_state.node_port) {
-			ctx->tc_index |= TC_INDEX_F_SKIP_RECIRCULATION;
-			ep_tail_call(ctx, CILIUM_CALL_IPV6_NODEPORT_REVNAT);
-			return DROP_MISSED_TAIL_CALL;
-		}
 # ifdef ENABLE_DSR
 		if (ct_state.dsr) {
 			ret = xlate_dsr_v6(ctx, tuple, l4_off);
 			if (ret != 0)
 				return ret;
-		}
+		} else
 # endif /* ENABLE_DSR */
+		/* See comment in handle_ipv4_from_lxc(). */
+		if (ct_state.node_port) {
+			send_trace_notify(ctx, TRACE_TO_NETWORK, SECLABEL,
+					  *dst_id, 0, 0, ret, monitor);
+			ctx->tc_index |= TC_INDEX_F_SKIP_RECIRCULATION;
+			ep_tail_call(ctx, CILIUM_CALL_IPV6_NODEPORT_REVNAT);
+			return DROP_MISSED_TAIL_CALL;
+		}
 #endif /* ENABLE_NODEPORT */
 		if (ct_state.rev_nat_index) {
 			ret = lb6_rev_nat(ctx, l4_off, &csum_off,
@@ -702,22 +704,25 @@ ct_recreate4:
 		policy_mark_skip(ctx);
 
 #ifdef ENABLE_NODEPORT
+# ifdef ENABLE_DSR
+		if (ct_state.dsr) {
+			ret = xlate_dsr_v4(ctx, &tuple, l4_off, has_l4_header);
+			if (ret != 0)
+				return ret;
+		} else
+# endif /* ENABLE_DSR */
 		/* This handles reply traffic for the case where the nodeport EP
-		 * is local to the node. We'll redirect to bpf_host egress to
-		 * perform the reverse DNAT.
+		 * is local to the node. We'll do the tail call to perform
+		 * the reverse DNAT.
 		 */
 		if (ct_state.node_port) {
+			send_trace_notify(ctx, TRACE_TO_NETWORK, SECLABEL,
+					  *dst_id, 0, 0, ct_ret, monitor);
 			ctx->tc_index |= TC_INDEX_F_SKIP_RECIRCULATION;
 			ep_tail_call(ctx, CILIUM_CALL_IPV4_NODEPORT_REVNAT);
 			return DROP_MISSED_TAIL_CALL;
 		}
-# ifdef ENABLE_DSR
-		if (ct_state.dsr) {
-			ret = xlate_dsr_v4(ctx, &tuple, l4_off, has_l4_header);
-			if (ret != 0)
-				return ret;
-		}
-# endif /* ENABLE_DSR */
+
 #endif /* ENABLE_NODEPORT */
 
 		if (ct_state.rev_nat_index) {
@@ -1126,9 +1131,10 @@ ipv6_policy(struct __ctx_buff *ctx, int ifindex, __u32 src_label, __u8 *reason,
 					   verdict, policy_match_type, audited);
 	}
 
-#ifdef ENABLE_DSR
+#ifdef ENABLE_NODEPORT
 	if (ret == CT_NEW || ret == CT_REOPENED) {
 		bool dsr = false;
+# ifdef ENABLE_DSR
 		int ret2;
 
 		ret2 = handle_dsr_v6(ctx, &dsr);
@@ -1136,15 +1142,25 @@ ipv6_policy(struct __ctx_buff *ctx, int ifindex, __u32 src_label, __u8 *reason,
 			return ret2;
 
 		ct_state_new.dsr = dsr;
-		if (ret == CT_REOPENED)
+		if (ret == CT_REOPENED && ct_state.dsr != dsr)
 			ct_update6_dsr(get_ct_map6(&tuple), &tuple, dsr);
+# endif /* ENABLE_DSR */
+		if (!dsr) {
+			bool node_port =
+				ct_has_nodeport_egress_entry6(get_ct_map6(&tuple),
+							      &tuple);
+
+			ct_state_new.node_port = node_port;
+			if (ret == CT_REOPENED &&
+			    ct_state.node_port != node_port)
+				ct_update_nodeport(get_ct_map6(&tuple), &tuple,
+						   node_port);
+		}
 	}
-#endif /* ENABLE_DSR */
+#endif /* ENABLE_NODEPORT */
 
 	if (ret == CT_NEW) {
 		ct_state_new.src_sec_id = src_label;
-		ct_state_new.node_port = ct_state.node_port;
-		ct_state_new.ifindex = ct_state.ifindex;
 		ret = ct_create6(get_ct_map6(&tuple), &CT_MAP_ANY6, &tuple, ctx, CT_INGRESS,
 				 &ct_state_new, verdict > 0);
 		if (IS_ERR(ret))
@@ -1431,9 +1447,10 @@ ipv4_policy(struct __ctx_buff *ctx, int ifindex, __u32 src_label, __u8 *reason,
 skip_policy_enforcement:
 #endif /* !ENABLE_HOST_SERVICES_FULL && !DISABLE_LOOPBACK_LB */
 
-#ifdef ENABLE_DSR
+#ifdef ENABLE_NODEPORT
 	if (ret == CT_NEW || ret == CT_REOPENED) {
 		bool dsr = false;
+# ifdef ENABLE_DSR
 		int ret2;
 
 		ret2 = handle_dsr_v4(ctx, &dsr);
@@ -1441,15 +1458,25 @@ skip_policy_enforcement:
 			return ret2;
 
 		ct_state_new.dsr = dsr;
-		if (ret == CT_REOPENED)
+		if (ret == CT_REOPENED && ct_state.dsr != dsr)
 			ct_update4_dsr(get_ct_map4(&tuple), &tuple, dsr);
+# endif /* ENABLE_DSR */
+		if (!dsr) {
+			bool node_port =
+				ct_has_nodeport_egress_entry4(get_ct_map4(&tuple),
+							      &tuple);
+
+			ct_state_new.node_port = node_port;
+			if (ret == CT_REOPENED &&
+			    ct_state.node_port != node_port)
+				ct_update_nodeport(get_ct_map4(&tuple), &tuple,
+						   node_port);
+		}
 	}
-#endif /* ENABLE_DSR */
+#endif /* ENABLE_NODEPORT */
 
 	if (ret == CT_NEW) {
 		ct_state_new.src_sec_id = src_label;
-		ct_state_new.node_port = ct_state.node_port;
-		ct_state_new.ifindex = ct_state.ifindex;
 		ret = ct_create4(get_ct_map4(&tuple), &CT_MAP_ANY4, &tuple, ctx, CT_INGRESS,
 				 &ct_state_new, verdict > 0);
 		if (IS_ERR(ret))
diff --git a/bpf/lib/conntrack.h b/bpf/lib/conntrack.h
index 59418623b1..79d6a063db 100644
--- a/bpf/lib/conntrack.h
+++ b/bpf/lib/conntrack.h
@@ -183,31 +183,6 @@ static __always_inline bool ct_entry_alive(const struct ct_entry *entry)
 	return !entry->rx_closing || !entry->tx_closing;
 }
 
-/* Helper for holding 2nd service entry alive in nodeport case. */
-static __always_inline bool __ct_entry_keep_alive(const void *map,
-						  const void *tuple)
-{
-	struct ct_entry *entry;
-
-	/* Lookup indicates to LRU that key/value is in use. */
-	entry = map_lookup_elem(map, tuple);
-	if (entry) {
-		if (entry->node_port) {
-#ifdef NEEDS_TIMEOUT
-			__u32 lifetime = (entry->seen_non_syn ?
-					  bpf_sec_to_mono(CT_SERVICE_LIFETIME_TCP) :
-					  bpf_sec_to_mono(CT_SERVICE_LIFETIME_NONTCP)) +
-					 bpf_mono_now();
-			WRITE_ONCE(entry->lifetime, lifetime);
-#endif
-			if (!ct_entry_alive(entry))
-				ct_reset_closing(entry);
-		}
-		return true;
-	}
-	return false;
-}
-
 static __always_inline __u8 __ct_lookup(const void *map, struct __ctx_buff *ctx,
 					const void *tuple, int action, int dir,
 					struct ct_state *ct_state,
@@ -1042,6 +1017,61 @@ static __always_inline int ct_create4(const void *map_main,
 	}
 	return 0;
 }
+
+/* The function tries to determine whether the flow identified by the given
+ * CT_INGRESS tuple belongs to a NodePort traffic (i.e., outside client => N/S
+ * LB => local backend).
+ *
+ * When the client send the NodePort request, the NodePort BPF
+ * (nodeport_lb{4,6}()) creates the CT_EGRESS entry for the
+ * (saddr=client,daddr=backend) tuple. So, to derive whether the reply packet
+ * backend => client belongs to the LB flow we can query the CT_EGRESS entry.
+ */
+static __always_inline bool
+ct_has_nodeport_egress_entry4(const void *map,
+			      struct ipv4_ct_tuple *ingress_tuple)
+{
+	int prev_flags = ingress_tuple->flags;
+	struct ct_entry *entry;
+
+	ingress_tuple->flags = TUPLE_F_OUT;
+	entry = map_lookup_elem(map, ingress_tuple);
+	ingress_tuple->flags = prev_flags;
+
+	if (entry)
+		return entry->node_port;
+
+	return 0;
+}
+
+static __always_inline bool
+ct_has_nodeport_egress_entry6(const void *map,
+			      struct ipv6_ct_tuple *ingress_tuple)
+{
+	int prev_flags = ingress_tuple->flags;
+	struct ct_entry *entry;
+
+	ingress_tuple->flags = TUPLE_F_OUT;
+	entry = map_lookup_elem(map, ingress_tuple);
+	ingress_tuple->flags = prev_flags;
+
+	if (entry)
+		return entry->node_port;
+
+	return 0;
+}
+
+static __always_inline void
+ct_update_nodeport(const void *map, const void *tuple, const bool node_port)
+{
+	struct ct_entry *entry;
+
+	entry = map_lookup_elem(map, tuple);
+	if (!entry)
+		return;
+
+	entry->node_port = node_port;
+}
 #else /* !CONNTRACK */
 static __always_inline int
 ct_lookup6(const void *map __maybe_unused,
diff --git a/bpf/lib/nodeport.h b/bpf/lib/nodeport.h
index c0c6621922..bee74e340b 100644
--- a/bpf/lib/nodeport.h
+++ b/bpf/lib/nodeport.h
@@ -786,7 +786,7 @@ skip_service_lookup:
 				 CT_EGRESS, &ct_state, &monitor);
 		switch (ret) {
 		case CT_NEW:
-redo_all:
+redo:
 			ct_state_new.src_sec_id = SECLABEL;
 			ct_state_new.node_port = 1;
 			ct_state_new.ifindex = NATIVE_DEV_IFINDEX;
@@ -794,33 +794,13 @@ redo_all:
 					 CT_EGRESS, &ct_state_new, false);
 			if (IS_ERR(ret))
 				return ret;
-			if (backend_local) {
-				ct_flip_tuple_dir6(&tuple);
-redo_local:
-				ct_state_new.rev_nat_index = 0;
-				ret = ct_create6(get_ct_map6(&tuple), NULL,
-						 &tuple, ctx, CT_INGRESS,
-						 &ct_state_new, false);
-				if (IS_ERR(ret))
-					return ret;
-			}
 			break;
 		case CT_REOPENED:
 		case CT_ESTABLISHED:
 		case CT_REPLY:
 			if (unlikely(ct_state.rev_nat_index !=
 				     svc->rev_nat_index))
-				goto redo_all;
-			if (backend_local) {
-				ct_flip_tuple_dir6(&tuple);
-				if (!__ct_entry_keep_alive(get_ct_map6(&tuple),
-							   &tuple)) {
-					ct_state_new.src_sec_id = SECLABEL;
-					ct_state_new.node_port = 1;
-					ct_state_new.ifindex = NATIVE_DEV_IFINDEX;
-					goto redo_local;
-				}
-			}
+				goto redo;
 			break;
 		default:
 			return DROP_UNKNOWN_CT;
@@ -1792,7 +1772,7 @@ skip_service_lookup:
 				 CT_EGRESS, &ct_state, &monitor);
 		switch (ret) {
 		case CT_NEW:
-redo_all:
+redo:
 			ct_state_new.src_sec_id = SECLABEL;
 			ct_state_new.node_port = 1;
 			ct_state_new.ifindex = NATIVE_DEV_IFINDEX;
@@ -1800,19 +1780,6 @@ redo_all:
 					 CT_EGRESS, &ct_state_new, false);
 			if (IS_ERR(ret))
 				return ret;
-			if (backend_local) {
-				ct_flip_tuple_dir4(&tuple);
-redo_local:
-				/* Reset rev_nat_index, otherwise ipv4_policy()
-				 * in bpf_lxc will do invalid xlation.
-				 */
-				ct_state_new.rev_nat_index = 0;
-				ret = ct_create4(get_ct_map4(&tuple), NULL,
-						 &tuple, ctx, CT_INGRESS,
-						 &ct_state_new, false);
-				if (IS_ERR(ret))
-					return ret;
-			}
 			break;
 		case CT_REOPENED:
 		case CT_ESTABLISHED:
@@ -1822,17 +1789,7 @@ redo_local:
 			 */
 			if (unlikely(ct_state.rev_nat_index !=
 				     svc->rev_nat_index))
-				goto redo_all;
-			if (backend_local) {
-				ct_flip_tuple_dir4(&tuple);
-				if (!__ct_entry_keep_alive(get_ct_map4(&tuple),
-							   &tuple)) {
-					ct_state_new.src_sec_id = SECLABEL;
-					ct_state_new.node_port = 1;
-					ct_state_new.ifindex = NATIVE_DEV_IFINDEX;
-					goto redo_local;
-				}
-			}
+				goto redo;
 			break;
 		default:
 			return DROP_UNKNOWN_CT;
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/cilium/cmd/monitor.go b/cilium/cmd/monitor.go
index 0908abb6e2..97fb915bbd 100644
--- a/cilium/cmd/monitor.go
+++ b/cilium/cmd/monitor.go
@@ -85,7 +85,7 @@ func setupSigHandler() {
 	signal.Notify(signalChan, os.Interrupt)
 	go func() {
 		for range signalChan {
-			fmt.Printf("\nReceived an interrupt, disconnecting from monitor...\n\n")
+			fmt.Fprintf(os.Stderr, "\nReceived an interrupt, disconnecting from monitor...\n\n")
 			os.Exit(0)
 		}
 	}()
@@ -226,7 +226,7 @@ func validateEndpointsFilters() {
 
 func runMonitor(args []string) {
 	if len(args) > 0 {
-		fmt.Println("Error: arguments not recognized")
+		fmt.Fprintln(os.Stderr, "Error: arguments not recognized")
 		os.Exit(1)
 	}
 
@@ -235,11 +235,11 @@ func runMonitor(args []string) {
 	setupSigHandler()
 	if resp, err := client.Daemon.GetHealthz(nil); err == nil {
 		if nm := resp.Payload.NodeMonitor; nm != nil {
-			fmt.Printf("Listening for events on %d CPUs with %dx%d of shared memory\n",
+			fmt.Fprintf(os.Stderr, "Listening for events on %d CPUs with %dx%d of shared memory\n",
 				nm.Cpus, nm.Npages, nm.Pagesize)
 		}
 	}
-	fmt.Printf("Press Ctrl-C to quit\n")
+	fmt.Fprintf(os.Stderr, "Press Ctrl-C to quit\n")
 
 	// On EOF, retry
 	// On other errors, exit
diff --git a/contrib/release/post-release.sh b/contrib/release/post-release.sh
index daf347b662..cfb9625a78 100755
--- a/contrib/release/post-release.sh
+++ b/contrib/release/post-release.sh
@@ -65,7 +65,9 @@ main() {
 
     git checkout -b pr/$version-digests $version
     ${DIR}/pull-docker-manifests.sh "$@"
-    logrun make -C Documentation update-helm-values
+    if grep -q update-helm-values Documentation/Makefile; then
+        logrun make -C Documentation update-helm-values
+    fi
     logecho
     logecho "Check that the following changes look correct:"
     # TODO: Make this less interactive when we have used it enough
diff --git a/contrib/release/start-release.sh b/contrib/release/start-release.sh
index 64415fc47b..204a3226fd 100755
--- a/contrib/release/start-release.sh
+++ b/contrib/release/start-release.sh
@@ -88,7 +88,9 @@ main() {
     echo $ersion > VERSION
     sed -i 's/"[^"]*"/""/g' install/kubernetes/Makefile.digests
     logrun make -C install/kubernetes all USE_DIGESTS=false
-    logrun make -C Documentation update-helm-values
+    if grep -q update-helm-values Documentation/Makefile; then
+        logrun make -C Documentation update-helm-values
+    fi
     logrun make update-authors
     if ! version_is_prerelease "$version"; then
         old_proj=$(grep "projects" $ACTS_YAML | sed "$PROJECTS_REGEX")
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/daemon_main.go b/daemon/cmd/daemon_main.go
index e0b3a38559..f72cb134db 100644
--- a/daemon/cmd/daemon_main.go
+++ b/daemon/cmd/daemon_main.go
@@ -343,8 +343,10 @@ func initializeFlags() {
 	flags.StringSlice(option.IPv6PodSubnets, []string{}, "List of IPv6 pod subnets to preconfigure for encryption")
 	option.BindEnv(option.IPv6PodSubnets)
 
-	flags.String(option.EndpointInterfaceNamePrefix, defaults.EndpointInterfaceNamePrefix, "Prefix of interface name shared by all endpoints")
+	flags.String(option.EndpointInterfaceNamePrefix, "", "Prefix of interface name shared by all endpoints")
 	option.BindEnv(option.EndpointInterfaceNamePrefix)
+	flags.MarkHidden(option.EndpointInterfaceNamePrefix)
+	flags.MarkDeprecated(option.EndpointInterfaceNamePrefix, "This option no longer has any effect and will be removed in v1.13.")
 
 	flags.StringSlice(option.ExcludeLocalAddress, []string{}, "Exclude CIDR from being recognized as local address")
 	option.BindEnv(option.ExcludeLocalAddress)
diff --git a/daemon/cmd/kube_proxy_replacement.go b/daemon/cmd/kube_proxy_replacement.go
index f794748cce..863e26e8d1 100644
--- a/daemon/cmd/kube_proxy_replacement.go
+++ b/daemon/cmd/kube_proxy_replacement.go
@@ -199,7 +199,7 @@ func initKubeProxyReplacementOptions() (bool, error) {
 				option.Config.MaglevHashSeed,
 				uint64(option.Config.MaglevTableSize),
 			); err != nil {
-				return false, fmt.Errorf("Failed to initialize maglev hash seeds")
+				return false, fmt.Errorf("Failed to initialize maglev hash seeds: %w", err)
 			}
 		}
 	}
@@ -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)
 		}
@@ -581,14 +581,14 @@ func finishKubeProxyReplacementInit(isKubeProxyReplacementStrict bool) error {
 
 	if option.Config.NodePortAcceleration != option.NodePortAccelerationDisabled {
 		if err := loader.SetXDPMode(option.Config.NodePortAcceleration); err != nil {
-			return fmt.Errorf("Cannot set NodePort acceleration")
+			return fmt.Errorf("Cannot set NodePort acceleration: %w", err)
 		}
 	}
 
 	for _, iface := range option.Config.Devices {
 		link, err := netlink.LinkByName(iface)
 		if err != nil {
-			return fmt.Errorf("Cannot retrieve %s link", iface)
+			return fmt.Errorf("Cannot retrieve %s link: %w", iface, err)
 		}
 		if strings.ContainsAny(iface, "=;") {
 			// Because we pass IPV{4,6}_NODEPORT addresses to bpf/init.sh
@@ -714,7 +714,7 @@ func markHostExtension() {
 func checkNodePortAndEphemeralPortRanges() error {
 	ephemeralPortRangeStr, err := sysctl.Read("net.ipv4.ip_local_port_range")
 	if err != nil {
-		return fmt.Errorf("Unable to read net.ipv4.ip_local_port_range")
+		return fmt.Errorf("Unable to read net.ipv4.ip_local_port_range: %w", err)
 	}
 	ephemeralPortRange := strings.Split(ephemeralPortRangeStr, "\t")
 	if len(ephemeralPortRange) != 2 {
@@ -722,11 +722,13 @@ func checkNodePortAndEphemeralPortRanges() error {
 	}
 	ephemeralPortMin, err := strconv.Atoi(ephemeralPortRange[0])
 	if err != nil {
-		return fmt.Errorf("Unable to parse min port value %s for ephemeral range", ephemeralPortRange[0])
+		return fmt.Errorf("Unable to parse min port value %s for ephemeral range: %w",
+			ephemeralPortRange[0], err)
 	}
 	ephemeralPortMax, err := strconv.Atoi(ephemeralPortRange[1])
 	if err != nil {
-		return fmt.Errorf("Unable to parse max port value %s for ephemeral range", ephemeralPortRange[1])
+		return fmt.Errorf("Unable to parse max port value %s for ephemeral range: %w",
+			ephemeralPortRange[1], err)
 	}
 
 	if option.Config.NodePortMax < ephemeralPortMin {
@@ -744,7 +746,7 @@ func checkNodePortAndEphemeralPortRanges() error {
 
 	reservedPortsStr, err := sysctl.Read("net.ipv4.ip_local_reserved_ports")
 	if err != nil {
-		return fmt.Errorf("Unable to read net.ipv4.ip_local_reserved_ports")
+		return fmt.Errorf("Unable to read net.ipv4.ip_local_reserved_ports: %w", err)
 	}
 	for _, portRange := range strings.Split(reservedPortsStr, ",") {
 		if portRange == "" {
@@ -756,7 +758,7 @@ func checkNodePortAndEphemeralPortRanges() error {
 		}
 		from, err := strconv.Atoi(ports[0])
 		if err != nil {
-			return fmt.Errorf("Unable to parse reserved port %q", ports[0])
+			return fmt.Errorf("Unable to parse reserved port %q: %w", ports[0], err)
 		}
 		to := from
 		if len(ports) == 2 {
@@ -789,7 +791,7 @@ func checkNodePortAndEphemeralPortRanges() error {
 	}
 	reservedPortsStr += fmt.Sprintf("%d-%d", option.Config.NodePortMin, option.Config.NodePortMax)
 	if err := sysctl.Write("net.ipv4.ip_local_reserved_ports", reservedPortsStr); err != nil {
-		return fmt.Errorf("Unable to addend nodeport range (%s) to net.ipv4.ip_local_reserved_ports: %s",
+		return fmt.Errorf("Unable to addend nodeport range (%s) to net.ipv4.ip_local_reserved_ports: %w",
 			nodePortRangeStr, err)
 	}
 
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/defaults/defaults.go b/pkg/defaults/defaults.go
index 5c013b158c..9c705f0dd3 100644
--- a/pkg/defaults/defaults.go
+++ b/pkg/defaults/defaults.go
@@ -286,10 +286,6 @@ const (
 	// LoopbackIPv4 is the default address for service loopback
 	LoopbackIPv4 = "169.254.42.1"
 
-	// EndpointInterfaceNamePrefix is the default prefix name of the
-	// interface names shared by all endpoints
-	EndpointInterfaceNamePrefix = "lxc+"
-
 	// ForceLocalPolicyEvalAtSource is the default value for
 	// option.ForceLocalPolicyEvalAtSource. It is enabled by default to
 	// provide backwards compatibility, it can be disabled via an option
diff --git a/pkg/elf/elf.go b/pkg/elf/elf.go
index 553ec1bfa8..a44d17b480 100644
--- a/pkg/elf/elf.go
+++ b/pkg/elf/elf.go
@@ -139,10 +139,11 @@ processSymbols:
 		var value []byte
 		switch symbol.kind {
 		case symbolUint32:
-			v, exists := intOptions[symbol.name]
-			if exists {
-				value = make([]byte, unsafe.Sizeof(v))
-				elf.metadata.ByteOrder.PutUint32(value, v)
+			if symbol.size == uint64(unsafe.Sizeof(uint32(0))) {
+				if v, exists := intOptions[symbol.name]; exists {
+					value = make([]byte, symbol.size)
+					elf.metadata.ByteOrder.PutUint32(value, v)
+				}
 			}
 
 		case symbolString:
diff --git a/pkg/elf/symbols.go b/pkg/elf/symbols.go
index e8d26db2c6..5bde3b9ff7 100644
--- a/pkg/elf/symbols.go
+++ b/pkg/elf/symbols.go
@@ -12,7 +12,6 @@ import (
 	"io"
 	"sort"
 	"strings"
-	"unsafe"
 )
 
 const (
@@ -67,8 +66,7 @@ func newSymbol(name string, kind symbolKind, offset, size uint64) symbol {
 	}
 }
 
-func newVariable(name string, offset uint64) symbol {
-	size := uint64(unsafe.Sizeof(symbolUint32))
+func newVariable(name string, offset, size uint64) symbol {
 	return newSymbol(name, symbolUint32, offset, size)
 }
 
@@ -179,7 +177,7 @@ func (s *symbols) extractFrom(e *elf.File) error {
 		case section.Name == dataSection:
 			// Offset from start of binary to variable inside .data
 			offset := section.Offset + sym.Value
-			dataOffsets[sym.Name] = newVariable(sym.Name, offset)
+			dataOffsets[sym.Name] = newVariable(sym.Name, offset, sym.Size)
 			log.WithField(fieldSymbol, sym.Name).Debugf("Found variable with offset %d", offset)
 
 		// Find the absolute offset to the map's entry in .strtab.
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/hubble/parser/threefour/parser.go b/pkg/hubble/parser/threefour/parser.go
index 03d735410d..f95549ea9c 100644
--- a/pkg/hubble/parser/threefour/parser.go
+++ b/pkg/hubble/parser/threefour/parser.go
@@ -488,6 +488,10 @@ func decodeICMPv6(icmp *layers.ICMPv6) *pb.Layer4 {
 	}
 }
 
+func isReply(reason uint8) bool {
+	return reason & ^monitor.TraceReasonEncryptMask == monitor.TraceReasonCtReply
+}
+
 func decodeIsReply(tn *monitor.TraceNotify, pvn *monitor.PolicyVerdictNotify) *wrapperspb.BoolValue {
 	switch {
 	case tn != nil && monitorAPI.TraceObservationPointHasConnState(tn.ObsPoint):
@@ -495,7 +499,19 @@ func decodeIsReply(tn *monitor.TraceNotify, pvn *monitor.PolicyVerdictNotify) *w
 		// tracking state available. For certain trace point
 		// events, we do not know if it actually was a reply or not.
 		return &wrapperspb.BoolValue{
-			Value: tn.Reason & ^monitor.TraceReasonEncryptMask == monitor.TraceReasonCtReply,
+			Value: isReply(tn.Reason),
+		}
+	case tn != nil && tn.ObsPoint == monitorAPI.TraceToNetwork && tn.Reason > 0:
+		// FIXME(GH-18460): Even though the BPF programs emitting TraceToNetwork
+		// do have access to connection tracking state, that state is currently
+		// not exposed to userspace by all trace points. Therefore TraceToNetwork
+		// is currently excluded in TraceObservationPointHasConnState.
+		// However, the NodePort return path in handle_ipv4_from_lxc does
+		// populate tn.Reason, and always has with a non-zero value due it
+		// only being used for replies. Therefore, if tn.Reason is non-zero,
+		// we can safely determine if the traced packet was a reply or not.
+		return &wrapperspb.BoolValue{
+			Value: isReply(tn.Reason),
 		}
 	case pvn != nil && pvn.Verdict >= 0:
 		// Forwarded PolicyVerdictEvents are emitted for the first packet of
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..92a2db116e 100644
--- a/pkg/option/config.go
+++ b/pkg/option/config.go
@@ -1666,10 +1666,6 @@ type DaemonConfig struct {
 	// LocalRouterIPv6 is the link-local IPv6 address used for Cilium's router device
 	LocalRouterIPv6 string
 
-	// EndpointInterfaceNamePrefix is the prefix name of the interface
-	// names shared by all endpoints
-	EndpointInterfaceNamePrefix string
-
 	// ForceLocalPolicyEvalAtSource forces a policy decision at the source
 	// endpoint for all local communication
 	ForceLocalPolicyEvalAtSource bool
@@ -2080,7 +2076,6 @@ var (
 		LogOpt:                       make(map[string]string),
 		SelectiveRegeneration:        defaults.SelectiveRegeneration,
 		LoopbackIPv4:                 defaults.LoopbackIPv4,
-		EndpointInterfaceNamePrefix:  defaults.EndpointInterfaceNamePrefix,
 		ForceLocalPolicyEvalAtSource: defaults.ForceLocalPolicyEvalAtSource,
 		EnableEndpointRoutes:         defaults.EnableEndpointRoutes,
 		AnnotateK8sNode:              defaults.AnnotateK8sNode,
@@ -2180,6 +2175,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 {
@@ -2518,7 +2518,6 @@ func (c *DaemonConfig) Populate() {
 	c.EnableWireguard = viper.GetBool(EnableWireguard)
 	c.EnableWireguardUserspaceFallback = viper.GetBool(EnableWireguardUserspaceFallback)
 	c.EnableWellKnownIdentities = viper.GetBool(EnableWellKnownIdentities)
-	c.EndpointInterfaceNamePrefix = viper.GetString(EndpointInterfaceNamePrefix)
 	c.EnableXDPPrefilter = viper.GetBool(EnableXDPPrefilter)
 	c.DevicePreFilter = viper.GetString(PrefilterDevice)
 	c.DisableCiliumEndpointCRD = viper.GetBool(DisableCiliumEndpointCRDName)
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/stable.txt b/stable.txt
index cd74ac3b53..65b4811dfe 100644
--- a/stable.txt
+++ b/stable.txt
@@ -1,1 +1,1 @@
-v1.11.0
+v1.11.1
diff --git a/vagrant_box_defaults.rb b/vagrant_box_defaults.rb
index bad167436e..e603425b20 100644
--- a/vagrant_box_defaults.rb
+++ b/vagrant_box_defaults.rb
@@ -3,12 +3,12 @@
 Vagrant.require_version ">= 2.2.0"
 
 $SERVER_BOX = "cilium/ubuntu-dev"
-$SERVER_VERSION= "213"
+$SERVER_VERSION= "214"
 $NETNEXT_SERVER_BOX= "cilium/ubuntu-next"
-$NETNEXT_SERVER_VERSION= "117"
+$NETNEXT_SERVER_VERSION= "120"
 @v54_SERVER_BOX= "cilium/ubuntu-5-4"
-@v54_SERVER_VERSION= "19"
+@v54_SERVER_VERSION= "20"
 @v419_SERVER_BOX= "cilium/ubuntu-4-19"
-@v419_SERVER_VERSION= "54"
+@v419_SERVER_VERSION= "55"
 @v49_SERVER_BOX= "cilium/ubuntu"
-@v49_SERVER_VERSION= "213"
+@v49_SERVER_VERSION= "214"
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/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/k8sT/Policies.go b/test/k8sT/Policies.go
index 465f332cec..22a95d4d86 100644
--- a/test/k8sT/Policies.go
+++ b/test/k8sT/Policies.go
@@ -1732,7 +1732,10 @@ var _ = SkipDescribeIf(func() bool {
 				validateConnectivity(HostConnectivityAllow, RemoteNodeConnectivityAllow, PodConnectivityAllow, WorldConnectivityDeny)
 			})
 
-			It("Validates fromEntities all policy", func() {
+			// Quarantined on net-next until #18520 is fixed
+			SkipItIf(func() bool {
+				return helpers.RunsOnNetNextKernel() && helpers.SkipQuarantined()
+			}, "Validates fromEntities all policy", func() {
 				installDefaultDenyIngressPolicy(kubectl, testNamespace, validateConnectivity)
 
 				By("Installing fromEntities all policy")