Repository: cilium/cilium
Pull request #18498 (2022-01-17 12:38:05)

vagrant: Generate kubeconfig correctly for netnext
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!

When we start VMs with K8S=1 NETNEXT=1, vagrant.kubeconfig file is not
generated correctly because start.sh tries to `vagrant port` and
`vagrant ssh` with the host name k8s1 instead of k8s1+. Fix it to use
correct host name.

Bit tricky thing in here is we don't modify the host name inside the
kubeconfig from k8s1 to k8s1+. This is because the k8s1+ is the host
name on the Vagrant. Linux inside the VM still uses the host name k8s1.
Therefore we don't modify the "Add '127.0.0.1 k8s1' ~" instruction
sentence as well.


YutaroHayakawa: /test
pchaigno: This is not covered by end-to-end tests, so good to merge.

Patch:
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/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/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/cilium/cmd/metrics_list.go b/cilium/cmd/metrics_list.go
index b459f8dc64..22ede7a51a 100644
--- a/cilium/cmd/metrics_list.go
+++ b/cilium/cmd/metrics_list.go
@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"os"
 	"regexp"
+	"sort"
 	"strings"
 	"text/tabwriter"
 
@@ -54,8 +55,13 @@ var MetricsListCmd = &cobra.Command{
 			label := ""
 			if len(metric.Labels) > 0 {
 				labelArray := []string{}
-				for key, value := range metric.Labels {
-					labelArray = append(labelArray, fmt.Sprintf(`%s="%s"`, key, value))
+				keys := make([]string, 0, len(metric.Labels))
+				for k := range metric.Labels {
+					keys = append(keys, k)
+				}
+				sort.Strings(keys)
+				for _, k := range keys {
+					labelArray = append(labelArray, fmt.Sprintf(`%s="%s"`, k, metric.Labels[k]))
 				}
 				label = strings.Join(labelArray, " ")
 			}
diff --git a/contrib/vagrant/start.sh b/contrib/vagrant/start.sh
index 377f7930ca..ed0f83e0ac 100755
--- a/contrib/vagrant/start.sh
+++ b/contrib/vagrant/start.sh
@@ -613,8 +613,12 @@ function createVm(){
     else
         vagrant up $PROVISION_ARGS $1
         if [ "$?" -eq "0" -a -n "${K8S}" ]; then
-            host_port=$(vagrant port --guest 6443 k8s1)
-            vagrant ssh k8s1 -- cat /home/vagrant/.kube/config | sed "s;server:.*:6443;server: https://k8s1:$host_port;g" > vagrant.kubeconfig
+            hostname=k8s1
+            if [ ! -z "$NETNEXT" -a "$NETNEXT" = "true" -o "$NETNEXT" = "1" ]; then
+                hostname=k8s1+
+            fi
+            host_port=$(vagrant port --guest 6443 $hostname)
+            vagrant ssh $hostname -- cat /home/vagrant/.kube/config | sed "s;server:.*:6443;server: https://k8s1:$host_port;g" > vagrant.kubeconfig
             echo "Add '127.0.0.1 k8s1' to your /etc/hosts to use vagrant.kubeconfig file for kubectl"
         fi
     fi
diff --git a/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/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


Test patch:
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))
+				}
+			}
 		}
 	}
 }