Repository: cilium/cilium
Pull request #13287 (2020-09-25 07:08:14)

Avoid loops with local-redirect service translation
Service translation logic for a `local-redirect` service can cause packets to be looped back to a service node-local backend after translation. This can happen when the node-local pod itself tries to connect to the service frontend for which it acts as a backend. There are cases (e.g., kiam agent connecting the AWS metadata server on EKS) where this can break traffic flow if the backend needs to forward the redirected traffic to the actual service frontend.

Check commit description for more details about the issue and testing.

AWS metadata server doesn't seem to support IPv6 [1], hence the fix is applicable only to IPv4. IPv6 changes can be added in future.

Follow-ups:  end-to-end tests, update gsg with kernel requirement for the socket lookup

Fixes: #11646

[1] https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html

aditighag: test-me-please
aditighag: Marking the PR as draft to look into the CI failures.
aditighag: test-me-please
aanm: @aditighag I've changed the release note label since this is not a user facing change and we aren't fixing a bug of a released feature.
aditighag: Trying to figure out how to test ipv6 functionality. 
aditighag: test-me-please
aditighag: Fixed formatting errors flagged by checkpatch.
aditighag: test-me-please
joestringer: There's still a couple of minor checkpatch warnings, I think kernel style expects tabs to be 8 chars wide:

```
CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
#99: FILE: bpf/bpf_sock.c:289:
+sock4_skip_xlate_for_same_netns_backend(struct bpf_sock_addr *ctx,
+					     const struct lb4_backend *backend)

CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
#141: FILE: bpf/bpf_sock.c:407:
+	if (lb4_svc_is_localredirect(svc) &&
+		sock4_skip_xlate_for_same_netns_backend(ctx_full, backend)) {


NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.
```

Also need to triage the unsuccessful checks.
aditighag: > There's still a couple of minor checkpatch warnings, I think kernel style expects tabs to be 8 chars wide:
> 
> ```
> CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
> #99: FILE: bpf/bpf_sock.c:289:
> +sock4_skip_xlate_for_same_netns_backend(struct bpf_sock_addr *ctx,
> +					     const struct lb4_backend *backend)
> 
> CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
> #141: FILE: bpf/bpf_sock.c:407:
> +	if (lb4_svc_is_localredirect(svc) &&
> +		sock4_skip_xlate_for_same_netns_backend(ctx_full, backend)) {
> 
> 
> NOTE: For some of the reported defects, checkpatch may be able to
>       mechanically convert to the typical style using --fix or --fix-inplace.
> ```
> 
> Also need to triage the unsuccessful checks.

I'm not able to see this in the IDE, and the --fix-inplace isn't working. 
aditighag: > The datapath changes LGTM. A few things:
> 
> * We should log an info msg about consequences in the daemon if sk_lookup_{tcp,udp} is missing and users have enabled LRP.

Do we have an equivalent of `HAVE_PROG_TYPE_HELPER ` from datapath in the cilium agent code? Can you point me to it? 

> * Considering that we have a testing infrastructure for LRP, could we add a e2e test for it? 

Absolutely! It's added in the follow-ups list. Let me see if I can it to this same PR.

> * Do you know whether there might be a valid case for LRP in which a request from LRP backend is expected to be sent back to the same backend?

It seems unlikely. Currently we don't have such a use case.


brb: > Do we have an equivalent of HAVE_PROG_TYPE_HELPER from datapath in the cilium agent code? Can you point me to it?
@aditighag In the daemon we check for the helpers, e.g. https://github.com/cilium/cilium/blob/master/daemon/cmd/kube_proxy_replacement.go#L150. Perhaps you could do the same for `sk_lookup_{tcp,udp}`.
aditighag: test-net-next
aditighag: I wrapped the code path under ifdef of enable-ipv6 so that it's disabled for IPv4 mapped IPv6 mapping. Per the description, IPv6 isn't required at the moment. With this, the kernel net-next is now passing. PTAL.
aditighag: test-4.19
aditighag: test-4.9
aditighag: test-runtime
aditighag: test-ginkgo
aanm: retest-gke
aditighag: > LGTM, minus the checkpatch failure.

checkpatch is giving a warning about one of the lines in the commit description being longer than the certain characters limit.
joestringer: > checkpatch is giving a warning about one of the lines in the commit description being longer than the certain characters limit.

:+1: This doesn't make sense to restrict when including wider sample output of commands in the commit message, it's more useful to have it exactly how it is right now rather than to make checkpatch happy in this specific case. Maybe we should consider relaxing checkpatch in this area.
aditighag: test-me-please

Patch:
diff --git a/Documentation/cmdref/cilium-operator-azure.md b/Documentation/cmdref/cilium-operator-azure.md
index 1f57a78ee2..45f81ca4d2 100644
--- a/Documentation/cmdref/cilium-operator-azure.md
+++ b/Documentation/cmdref/cilium-operator-azure.md
@@ -18,6 +18,7 @@ cilium-operator-azure [flags]
       --azure-cloud-name string                   Name of the Azure cloud being used (default "AzurePublicCloud")
       --azure-resource-group string               Resource group to use for Azure IPAM
       --azure-subscription-id string              Subscription ID to access Azure API
+      --azure-use-primary-address                 Use Azure IP address from interface's primary IPConfigurations (default true)
       --azure-user-assigned-identity-id string    ID of the user assigned identity used to auth with the Azure API
       --cilium-endpoint-gc-interval duration      GC interval for cilium endpoints (default 5m0s)
       --cluster-id int                            Unique identifier of the cluster
diff --git a/Documentation/cmdref/cilium-operator.md b/Documentation/cmdref/cilium-operator.md
index b1f9ded74b..fdf73f1c6b 100644
--- a/Documentation/cmdref/cilium-operator.md
+++ b/Documentation/cmdref/cilium-operator.md
@@ -20,6 +20,7 @@ cilium-operator [flags]
       --azure-cloud-name string                   Name of the Azure cloud being used (default "AzurePublicCloud")
       --azure-resource-group string               Resource group to use for Azure IPAM
       --azure-subscription-id string              Subscription ID to access Azure API
+      --azure-use-primary-address                 Use Azure IP address from interface's primary IPConfigurations (default true)
       --azure-user-assigned-identity-id string    ID of the user assigned identity used to auth with the Azure API
       --cilium-endpoint-gc-interval duration      GC interval for cilium endpoints (default 5m0s)
       --cluster-id int                            Unique identifier of the cluster
diff --git a/Documentation/cmdref/cilium_service_update.md b/Documentation/cmdref/cilium_service_update.md
index 849df74b6f..e76c3832c6 100644
--- a/Documentation/cmdref/cilium_service_update.md
+++ b/Documentation/cmdref/cilium_service_update.md
@@ -26,7 +26,6 @@ cilium service update [flags]
       --k8s-node-port               Set service as a k8s NodePort
       --k8s-traffic-policy string   Set service with k8s externalTrafficPolicy as {Local,Cluster} (default "Cluster")
       --local-redirect              Set service as Local Redirect
-      --protocol string             Protocol for service (e.g. TCP, UDP) (default "tcp")
 ```
 
 ### Options inherited from parent commands
diff --git a/Documentation/gettingstarted/kubeproxy-free.rst b/Documentation/gettingstarted/kubeproxy-free.rst
index 0b64849f7d..17bcea5f83 100644
--- a/Documentation/gettingstarted/kubeproxy-free.rst
+++ b/Documentation/gettingstarted/kubeproxy-free.rst
@@ -224,16 +224,16 @@ port ``31940`` (one for each of devices ``eth0`` and ``eth1``):
 .. parsed-literal::
 
     kubectl exec -it -n kube-system cilium-fmh8d -- cilium service list
-    ID   Frontend                   Service Type   Backend
+    ID   Frontend               Service Type   Backend                    
     [...]
-    4    10.104.239.135:80/TCP      ClusterIP      1 => 10.217.0.107:80/TCP
-                                                   2 => 10.217.0.149:80/TCP
-    5    0.0.0.0:31940/TCP          NodePort       1 => 10.217.0.107:80/TCP
-                                                   2 => 10.217.0.149:80/TCP
-    6    192.168.178.29:31940/TCP   NodePort       1 => 10.217.0.107:80/TCP
-                                                   2 => 10.217.0.149:80/TCP
-    7    172.16.0.29:31940/TCP      NodePort       1 => 10.217.0.107:80/TCP
-                                                   2 => 10.217.0.149:80/TCP
+    4    10.104.239.135:80      ClusterIP      1 => 10.217.0.107:80       
+                                               2 => 10.217.0.149:80       
+    5    0.0.0.0:31940          NodePort       1 => 10.217.0.107:80       
+                                               2 => 10.217.0.149:80       
+    6    192.168.178.29:31940   NodePort       1 => 10.217.0.107:80       
+                                               2 => 10.217.0.149:80       
+    7    172.16.0.29:31940      NodePort       1 => 10.217.0.107:80       
+                                               2 => 10.217.0.149:80       
 
 At the same time we can inspect through ``iptables`` in the host namespace
 that no ``iptables`` rule for the service is present:
diff --git a/api/v1/models/backend_address.go b/api/v1/models/backend_address.go
index d305c02d8b..4675a9767f 100644
--- a/api/v1/models/backend_address.go
+++ b/api/v1/models/backend_address.go
@@ -29,9 +29,6 @@ type BackendAddress struct {
 
 	// Layer 4 port number
 	Port uint16 `json:"port,omitempty"`
-
-	// Layer 4 protocol (TCP, UDP, etc)
-	Protocol string `json:"protocol,omitempty"`
 }
 
 // Validate validates this backend address
diff --git a/api/v1/openapi.yaml b/api/v1/openapi.yaml
index c045df68b8..f9c552725d 100644
--- a/api/v1/openapi.yaml
+++ b/api/v1/openapi.yaml
@@ -2250,9 +2250,6 @@ definitions:
         description: Layer 4 port number
         type: integer
         format: uint16
-      protocol:
-        description: Layer 4 protocol (TCP, UDP, etc)
-        type: string
       nodeName:
         description: Optional name of the node on which this backend runs
         type: string
diff --git a/api/v1/server/embedded_spec.go b/api/v1/server/embedded_spec.go
index 1d8b94e1a1..375d4011fb 100644
--- a/api/v1/server/embedded_spec.go
+++ b/api/v1/server/embedded_spec.go
@@ -1429,10 +1429,6 @@ func init() {
           "description": "Layer 4 port number",
           "type": "integer",
           "format": "uint16"
-        },
-        "protocol": {
-          "description": "Layer 4 protocol (TCP, UDP, etc)",
-          "type": "string"
         }
       }
     },
@@ -5151,10 +5147,6 @@ func init() {
           "description": "Layer 4 port number",
           "type": "integer",
           "format": "uint16"
-        },
-        "protocol": {
-          "description": "Layer 4 protocol (TCP, UDP, etc)",
-          "type": "string"
         }
       }
     },
diff --git a/bpf/bpf_sock.c b/bpf/bpf_sock.c
index 2a746c53f3..9be45f4656 100644
--- a/bpf/bpf_sock.c
+++ b/bpf/bpf_sock.c
@@ -264,25 +264,79 @@ sock4_wildcard_lookup_full(struct lb4_key *key __maybe_unused,
 	return svc;
 }
 
+/* Service translation logic for a local-redirect service can cause
+ * packets to be looped back to a service node-local backend after translation.
+ * This can happen when the node-local backend itself tries to connect to the
+ * service frontend for which it acts as a backend.
+ * There are cases where this can break traffic flow if the backend needs to
+ * forward the redirected traffic to the actual service frontend.
+ * Hence, allow service translation for pod traffic getting redirected to
+ * backend (across network namespaces), but skip service translation for backend
+ * to itself or another service backend within the same namespace.
+ *
+ * For example, in EKS cluster, a local-redirect service exists with the AWS
+ * metadata IP, port as the frontend <169.254.169.254, 80> and kiam proxy as a
+ * backend pod. When traffic destined to the frontend originates from the kiam pod
+ * in namespace ns1 (host ns when the kiam proxy pod is deployed in hostNetwork
+ * mode or regular pod ns) and the pod is selected as a backend, the traffic
+ * would get looped back to the proxy pod.
+ * Identify such cases by doing a socket lookup for the backend <ip, port> in its
+ * namespace, ns1, and skip service translation.
+ */
+#ifdef BPF_HAVE_SOCKET_LOOKUP
+static __always_inline __maybe_unused bool
+sock4_skip_xlate_for_same_netns_backend(struct bpf_sock_addr *ctx,
+					const struct lb4_backend *backend)
+{
+	struct bpf_sock_tuple tuple = {
+		.ipv4.saddr = 0,
+		.ipv4.sport = 0,
+		.ipv4.daddr = backend->address,
+		.ipv4.dport = backend->port,
+	};
+	struct bpf_sock *sk = NULL;
+
+	switch (ctx->protocol) {
+	case IPPROTO_TCP:
+		sk = sk_lookup_tcp(ctx, &tuple, sizeof(tuple.ipv4), BPF_F_CURRENT_NETNS, 0);
+		break;
+	case IPPROTO_UDP:
+		sk = sk_lookup_udp(ctx, &tuple, sizeof(tuple.ipv4), BPF_F_CURRENT_NETNS, 0);
+		break;
+	default:
+		break;
+	}
+
+	/* There exists a socket listening on the <ip, port> of the backend
+	 * in the same namespace where socket connect call originates from.
+	 */
+	if (sk) {
+		sk_release(sk);
+		return true;
+	}
+
+	return false;
+}
+#endif
+
 static __always_inline int __sock4_xlate_fwd(struct bpf_sock_addr *ctx,
 					     struct bpf_sock_addr *ctx_full,
-					     const bool udp_only)
+					     const bool udp_only,
+					     const bool ipv6_context  __maybe_unused)
 {
 	union lb4_affinity_client_id id;
 	const bool in_hostns = ctx_in_hostns(ctx_full, &id.client_cookie);
 	struct lb4_backend *backend;
 	struct lb4_service *svc;
-	__u32 proto = ctx->protocol;
 	struct lb4_key key = {
 		.address	= ctx->user_ip4,
 		.dport		= ctx_dst_port(ctx),
-		.proto		= proto,
 	}, orig_key = key;
 	struct lb4_service *backend_slot;
 	bool backend_from_affinity = false;
 	__u32 backend_id = 0;
 
-	if (!udp_only && !sock_proto_enabled(proto))
+	if (!udp_only && !sock_proto_enabled(ctx->protocol))
 		return -ENOTSUP;
 
 	/* In case a direct match fails, we try to look-up surrogate
@@ -347,6 +401,13 @@ static __always_inline int __sock4_xlate_fwd(struct bpf_sock_addr *ctx,
 		return -ENOENT;
 	}
 
+#ifdef BPF_HAVE_SOCKET_LOOKUP
+	if (lb4_svc_is_localredirect(svc) && !ipv6_context &&
+	    sock4_skip_xlate_for_same_netns_backend(ctx_full, backend)) {
+		return 0;
+	}
+#endif
+
 	if (lb4_svc_is_affinity(svc) && !backend_from_affinity)
 		lb4_update_affinity_by_netns(svc, &id, backend_id);
 
@@ -365,7 +426,7 @@ static __always_inline int __sock4_xlate_fwd(struct bpf_sock_addr *ctx,
 __section("connect4")
 int sock4_connect(struct bpf_sock_addr *ctx)
 {
-	__sock4_xlate_fwd(ctx, ctx, false);
+	__sock4_xlate_fwd(ctx, ctx, false, false);
 	return SYS_PROCEED;
 }
 
@@ -374,14 +435,12 @@ static __always_inline int __sock4_bind(struct bpf_sock *ctx,
 					struct bpf_sock *ctx_full)
 {
 	struct lb4_service *svc;
-	__u32 proto = ctx->protocol;
 	struct lb4_key key = {
 		.address	= ctx->src_ip4,
 		.dport		= ctx_src_port(ctx),
-		.proto		= proto,
 	};
 
-	if (!sock_proto_enabled(proto) ||
+	if (!sock_proto_enabled(ctx->protocol) ||
 	    !ctx_in_hostns(ctx_full, NULL))
 		return 0;
 
@@ -432,7 +491,6 @@ static __always_inline int __sock4_xlate_rev(struct bpf_sock_addr *ctx,
 		struct lb4_key svc_key = {
 			.address	= val->address,
 			.dport		= val->port,
-			.proto		= ctx->protocol,
 		};
 
 		svc = lb4_lookup_service(&svc_key, true);
@@ -456,7 +514,7 @@ static __always_inline int __sock4_xlate_rev(struct bpf_sock_addr *ctx,
 __section("sendmsg4")
 int sock4_sendmsg(struct bpf_sock_addr *ctx)
 {
-	__sock4_xlate_fwd(ctx, ctx, true);
+	__sock4_xlate_fwd(ctx, ctx, true, false);
 	return SYS_PROCEED;
 }
 
@@ -639,7 +697,7 @@ int sock6_xlate_v4_in_v6(struct bpf_sock_addr *ctx __maybe_unused,
 	fake_ctx.user_ip4  = addr6.p4;
 	fake_ctx.user_port = ctx_dst_port(ctx);
 
-	ret = __sock4_xlate_fwd(&fake_ctx, ctx, udp_only);
+	ret = __sock4_xlate_fwd(&fake_ctx, ctx, udp_only, true);
 	if (ret < 0)
 		return ret;
 
@@ -677,13 +735,11 @@ sock6_bind_v4_in_v6(struct bpf_sock *ctx __maybe_unused)
 static __always_inline int __sock6_bind(struct bpf_sock *ctx)
 {
 	struct lb6_service *svc;
-	__u32 proto = ctx->protocol;
 	struct lb6_key key = {
 		.dport		= ctx_src_port(ctx),
-		.proto		= proto,
 	};
 
-	if (!sock_proto_enabled(proto) ||
+	if (!sock_proto_enabled(ctx->protocol) ||
 	    !ctx_in_hostns(ctx, NULL))
 		return 0;
 
@@ -722,16 +778,14 @@ static __always_inline int __sock6_xlate_fwd(struct bpf_sock_addr *ctx,
 	const bool in_hostns = ctx_in_hostns(ctx, &id.client_cookie);
 	struct lb6_backend *backend;
 	struct lb6_service *svc;
-	__u32 proto = ctx->protocol;
 	struct lb6_key key = {
 		.dport		= ctx_dst_port(ctx),
-		.proto		= proto,
 	}, orig_key;
 	struct lb6_service *backend_slot;
 	bool backend_from_affinity = false;
 	__u32 backend_id = 0;
 
-	if (!udp_only && !sock_proto_enabled(proto))
+	if (!udp_only && !sock_proto_enabled(ctx->protocol))
 		return -ENOTSUP;
 
 	ctx_get_v6_address(ctx, &key.address);
@@ -848,7 +902,6 @@ static __always_inline int __sock6_xlate_rev(struct bpf_sock_addr *ctx)
 		struct lb6_key svc_key = {
 			.address	= val->address,
 			.dport		= val->port,
-			.proto		= ctx->protocol,
 		};
 
 		svc = lb6_lookup_service(&svc_key, true);
diff --git a/bpf/include/bpf/features.h b/bpf/include/bpf/features.h
index 9b3bdf1fd7..136ff8f555 100644
--- a/bpf/include/bpf/features.h
+++ b/bpf/include/bpf/features.h
@@ -31,4 +31,9 @@
 # define BPF_HAVE_CSUM_LEVEL 1
 #endif
 
+#if HAVE_PROG_TYPE_HELPER(cgroup_sock_addr, bpf_sk_lookup_tcp) && \
+    HAVE_PROG_TYPE_HELPER(cgroup_sock_addr, bpf_sk_lookup_udp)
+# define BPF_HAVE_SOCKET_LOOKUP 1
+#endif
+
 #endif /* ____BPF_FEATURES____ */
diff --git a/bpf/include/bpf/helpers.h b/bpf/include/bpf/helpers.h
index 7502842bf4..04fa59a838 100644
--- a/bpf/include/bpf/helpers.h
+++ b/bpf/include/bpf/helpers.h
@@ -84,4 +84,12 @@ static int BPF_FUNC(sock_hash_update, struct bpf_sock_ops *skops, void *map,
 static int BPF_FUNC(msg_redirect_hash, struct sk_msg_md *md, void *map,
 		    void *key, __u64 flags);
 
+/* Socket lookup helpers */
+static struct bpf_sock *BPF_FUNC(sk_lookup_tcp, void *ctx,
+				 struct bpf_sock_tuple *tuple, __u32 tuple_size,
+				 __u64 netns, __u64 flags);
+static struct bpf_sock *BPF_FUNC(sk_lookup_udp, void *ctx,
+				 struct bpf_sock_tuple *tuple, __u32 tuple_size,
+				 __u64 netns, __u64 flags);
+
 #endif /* __BPF_HELPERS__ */
diff --git a/bpf/include/bpf/helpers_skb.h b/bpf/include/bpf/helpers_skb.h
index 947555f1fc..d443ae8924 100644
--- a/bpf/include/bpf/helpers_skb.h
+++ b/bpf/include/bpf/helpers_skb.h
@@ -54,9 +54,6 @@ static int BPF_FUNC_REMAP(skb_event_output, struct __sk_buff *skb, void *map,
 static struct bpf_sock *BPF_FUNC(skc_lookup_tcp, struct __sk_buff *skb,
 				 struct bpf_sock_tuple *tuple, __u32 tuple_size,
 				 __u64 netns, __u64 flags);
-static struct bpf_sock *BPF_FUNC(sk_lookup_udp, struct __sk_buff *skb,
-				 struct bpf_sock_tuple *tuple, __u32 tuple_size,
-				 __u64 netns, __u64 flags);
 static int BPF_FUNC(sk_release, struct bpf_sock *sk);
 static int BPF_FUNC(sk_assign, struct __sk_buff *skb, struct bpf_sock *sk,
 		    __u64 flags);
diff --git a/bpf/lib/common.h b/bpf/lib/common.h
index 60a430ed4b..e0612d946f 100644
--- a/bpf/lib/common.h
+++ b/bpf/lib/common.h
@@ -559,7 +559,11 @@ enum {
 	SVC_FLAG_LOADBALANCER = (1 << 5),  /* LoadBalancer service */
 	SVC_FLAG_ROUTABLE     = (1 << 6),  /* Not a surrogate/ClusterIP entry */
 	SVC_FLAG_SOURCE_RANGE = (1 << 7),  /* Check LoadBalancer source range */
-	SVC_FLAG_LOCALREDIRECT = (1 << 8),  /* local redirect */
+};
+
+/* Service flags (lb{4,6}_service->flags2) */
+enum {
+	SVC_FLAG_LOCALREDIRECT = (1 << 0),  /* local redirect */
 };
 
 struct ipv6_ct_tuple {
@@ -632,7 +636,7 @@ struct lb6_key {
 	union v6addr address;	/* Service virtual IPv6 address */
 	__be16 dport;		/* L4 port filter, if unset, all ports apply */
 	__u16 backend_slot;	/* Backend iterator, 0 indicates the svc frontend */
-	__u8 proto;		/* L4 protocol */
+	__u8 proto;		/* L4 protocol, currently not used (set to 0) */
 	__u8 scope;		/* LB_LOOKUP_SCOPE_* for externalTrafficPolicy=Local */
 	__u8 pad[2];
 };
@@ -680,7 +684,7 @@ struct lb4_key {
 	__be32 address;		/* Service virtual IPv4 address */
 	__be16 dport;		/* L4 port filter, if unset, all ports apply */
 	__u16 backend_slot;	/* Backend iterator, 0 indicates the svc frontend */
-	__u8 proto;		/* L4 protocol */
+	__u8 proto;		/* L4 protocol, currently not used (set to 0) */
 	__u8 scope;		/* LB_LOOKUP_SCOPE_* for externalTrafficPolicy=Local */
 	__u8 pad[2];
 };
diff --git a/bpf/lib/lb.h b/bpf/lib/lb.h
index 6615518025..42ac11a266 100644
--- a/bpf/lib/lb.h
+++ b/bpf/lib/lb.h
@@ -333,6 +333,12 @@ bool lb6_svc_is_routable(const struct lb6_service *svc)
 	return __lb_svc_is_routable(svc->flags);
 }
 
+static __always_inline
+bool lb4_svc_is_localredirect(const struct lb4_service *svc __maybe_unused)
+{
+	return svc->flags2 & SVC_FLAG_LOCALREDIRECT;
+}
+
 static __always_inline int extract_l4_port(struct __ctx_buff *ctx, __u8 nexthdr,
 					   int l4_off, __be16 *port,
 					   __maybe_unused struct iphdr *ip4)
@@ -500,7 +506,8 @@ static __always_inline int lb6_extract_key(struct __ctx_buff *ctx __maybe_unused
 					   int dir)
 {
 	union v6addr *addr;
-	key->proto = tuple->nexthdr;
+	/* FIXME(brb): set after adding support for different L4 protocols in LB */
+	key->proto = 0;
 	addr = (dir == CT_INGRESS) ? &tuple->saddr : &tuple->daddr;
 	ipv6_addr_copy(&key->address, addr);
 	csum_l4_offset_and_flags(tuple->nexthdr, csum_off);
@@ -1019,7 +1026,8 @@ static __always_inline int lb4_extract_key(struct __ctx_buff *ctx __maybe_unused
 					   struct csum_offset *csum_off,
 					   int dir)
 {
-	key->proto = ip4->protocol;
+	/* FIXME: set after adding support for different L4 protocols in LB */
+	key->proto = 0;
 	key->address = (dir == CT_INGRESS) ? ip4->saddr : ip4->daddr;
 	if (ipv4_has_l4_header(ip4))
 		csum_l4_offset_and_flags(ip4->protocol, csum_off);
diff --git a/bpf/sockops/bpf_redir.c b/bpf/sockops/bpf_redir.c
index f55fdab14a..77855a71ae 100644
--- a/bpf/sockops/bpf_redir.c
+++ b/bpf/sockops/bpf_redir.c
@@ -28,10 +28,6 @@ static __always_inline void sk_msg_extract4_key(const struct sk_msg_md *msg,
 	key->dip4 = msg->remote_ip4;
 	key->sip4 = msg->local_ip4;
 	key->family = ENDPOINT_KEY_IPV4;
-	/* Only TCP packets flow through sk_msg progs.
-	 * cf GH issue #13490 to fix
-	 */
-	key->protocol = IPPROTO_TCP;
 
 	key->sport = (bpf_ntohl(msg->local_port) >> 16);
 	/* clang-7.1 or higher seems to think it can do a 16-bit read here
diff --git a/bpf/sockops/bpf_sockops.c b/bpf/sockops/bpf_sockops.c
index 13545c4e80..a932a3194b 100644
--- a/bpf/sockops/bpf_sockops.c
+++ b/bpf/sockops/bpf_sockops.c
@@ -29,11 +29,6 @@ static __always_inline void sk_extract4_key(const struct bpf_sock_ops *ops,
 	key->dip4 = ops->remote_ip4;
 	key->sip4 = ops->local_ip4;
 	key->family = ENDPOINT_KEY_IPV4;
-	/* We will get misses on UDP, but the verifier
-	 * does not allow access to the socket field until >= 5.3
-	 * cf GH issue #13490 to fix
-	 */
-	key->protocol = IPPROTO_TCP;
 
 	key->sport = (bpf_ntohl(ops->local_port) >> 16);
 	/* clang-7.1 or higher seems to think it can do a 16-bit read here
@@ -50,7 +45,6 @@ static __always_inline void sk_lb4_key(struct lb4_key *lb4,
 	/* SK MSG is always egress, so use daddr */
 	lb4->address = key->dip4;
 	lb4->dport = key->dport;
-	lb4->proto = key->protocol;
 }
 
 static __always_inline bool redirect_to_proxy(int verdict)
diff --git a/bpf/sockops/bpf_sockops.h b/bpf/sockops/bpf_sockops.h
index f45ad0ffe0..13c4a44025 100644
--- a/bpf/sockops/bpf_sockops.h
+++ b/bpf/sockops/bpf_sockops.h
@@ -24,7 +24,7 @@ struct sock_key {
 		union v6addr	dip6;
 	};
 	__u8 family;
-	__u8 protocol;
+	__u8 pad7;
 	__u16 pad8;
 	__u32 sport;
 	__u32 dport;
diff --git a/cilium/cmd/service_update.go b/cilium/cmd/service_update.go
index 76fe88285c..bf9eb225bd 100644
--- a/cilium/cmd/service_update.go
+++ b/cilium/cmd/service_update.go
@@ -37,7 +37,6 @@ var (
 	localRedirect      bool
 	idU                uint64
 	frontend           string
-	protocol           string
 	backends           []string
 )
 
@@ -61,36 +60,11 @@ func init() {
 	serviceUpdateCmd.Flags().StringVarP(&k8sTrafficPolicy, "k8s-traffic-policy", "", "Cluster", "Set service with k8s externalTrafficPolicy as {Local,Cluster}")
 	serviceUpdateCmd.Flags().BoolVarP(&k8sClusterInternal, "k8s-cluster-internal", "", false, "Set service as cluster-internal for externalTrafficPolicy=Local")
 	serviceUpdateCmd.Flags().StringVarP(&frontend, "frontend", "", "", "Frontend address")
-	serviceUpdateCmd.Flags().StringVarP(&protocol, "protocol", "", "tcp", "Protocol for service (e.g. TCP, UDP)")
 	serviceUpdateCmd.Flags().StringSliceVarP(&backends, "backends", "", []string{}, "Backend address or addresses (<IP:Port>)")
 }
 
-func parseAddress(l4Protocol, address string) (ip net.IP, port int, proto string, err error) {
-	switch proto = strings.ToLower(l4Protocol); proto {
-	case "tcp":
-		var tcpAddr *net.TCPAddr
-		tcpAddr, err = net.ResolveTCPAddr(proto, address)
-		if err != nil {
-			return
-		}
-		ip = tcpAddr.IP
-		port = tcpAddr.Port
-	case "udp":
-		var udpAddr *net.UDPAddr
-		udpAddr, err = net.ResolveUDPAddr(proto, address)
-		if err != nil {
-			return
-		}
-		ip = udpAddr.IP
-		port = udpAddr.Port
-	default:
-		err = fmt.Errorf("unrecognized protocol %q", l4Protocol)
-	}
-	return
-}
-
-func parseFrontendAddress(l4Protocol, address string) (*models.FrontendAddress, net.IP) {
-	ip, port, proto, err := parseAddress(l4Protocol, address)
+func parseFrontendAddress(address string) (*models.FrontendAddress, net.IP) {
+	frontend, err := net.ResolveTCPAddr("tcp", address)
 	if err != nil {
 		Fatalf("Unable to parse frontend address: %s\n", err)
 	}
@@ -100,12 +74,13 @@ func parseFrontendAddress(l4Protocol, address string) (*models.FrontendAddress,
 		scope = models.FrontendAddressScopeInternal
 	}
 
+	// FIXME support more than TCP
 	return &models.FrontendAddress{
-		IP:       ip.String(),
-		Port:     uint16(port),
-		Protocol: proto,
+		IP:       frontend.IP.String(),
+		Port:     uint16(frontend.Port),
+		Protocol: models.FrontendAddressProtocolTCP,
 		Scope:    scope,
-	}, ip
+	}, frontend.IP
 }
 
 func boolToInt(set bool) int {
@@ -117,7 +92,7 @@ func boolToInt(set bool) int {
 
 func updateService(cmd *cobra.Command, args []string) {
 	id := int64(idU)
-	fa, faIP := parseFrontendAddress(protocol, frontend)
+	fa, faIP := parseFrontendAddress(frontend)
 
 	var spec *models.ServiceSpec
 	svc, err := client.GetServiceID(id)
@@ -175,19 +150,20 @@ func updateService(cmd *cobra.Command, args []string) {
 
 	spec.BackendAddresses = nil
 	for _, backend := range backends {
-		ip, port, proto, err := parseAddress(protocol, backend)
+		beAddr, err := net.ResolveTCPAddr("tcp", backend)
 		if err != nil {
-			Fatalf("Cannot parse backend address %q: %s", backend, err)
+			Fatalf("Cannot parse backend address \"%s\": %s", backend, err)
 		}
+
 		// Backend ID will be set by the daemon
-		be := loadbalancer.NewBackend(0, loadbalancer.L4Type(strings.ToUpper(proto)), ip, uint16(port))
+		be := loadbalancer.NewBackend(0, loadbalancer.TCP, beAddr.IP, uint16(beAddr.Port))
 
 		if be.IsIPv6() && faIP.To4() != nil {
 			Fatalf("Address mismatch between frontend and backend %s", backend)
 		}
 
-		if fa.Port == 0 && port != 0 {
-			Fatalf("L4 backend found (%s:%d) with L3 frontend", ip, port)
+		if fa.Port == 0 && beAddr.Port != 0 {
+			Fatalf("L4 backend found (%v) with L3 frontend", beAddr)
 		}
 
 		ba := be.GetBackendModel()
diff --git a/operator/option/config.go b/operator/option/config.go
index ede2ef522f..cbc0e12b8a 100644
--- a/operator/option/config.go
+++ b/operator/option/config.go
@@ -155,6 +155,10 @@ const (
 	// for retrieving Azure API credentials
 	AzureUserAssignedIdentityID = "azure-user-assigned-identity-id"
 
+	// AzureUsePrimaryAddress specify wether we should use or ignore the interface's
+	// primary IPConfiguration
+	AzureUsePrimaryAddress = "azure-use-primary-address"
+
 	// CRDWaitTimeout it the time after which Cilium CRDs have to be available.
 	CRDWaitTimeout = "crd-wait-timeout"
 
@@ -294,6 +298,10 @@ type OperatorConfig struct {
 	// for retrieving Azure API credentials
 	AzureUserAssignedIdentityID string
 
+	// AzureUsePrimaryAddress specify wether we should use or ignore the interface's
+	// primary IPConfiguration
+	AzureUsePrimaryAddress bool
+
 	// CRDWaitTimeout it the time after which Cilium CRDs have to be available.
 	CRDWaitTimeout time.Duration
 
@@ -348,6 +356,7 @@ func (c *OperatorConfig) Populate() {
 	c.AzureCloudName = viper.GetString(AzureCloudName)
 	c.AzureSubscriptionID = viper.GetString(AzureSubscriptionID)
 	c.AzureResourceGroup = viper.GetString(AzureResourceGroup)
+	c.AzureUsePrimaryAddress = viper.GetBool(AzureUsePrimaryAddress)
 	c.AzureUserAssignedIdentityID = viper.GetString(AzureUserAssignedIdentityID)
 
 	// Option maps and slices
diff --git a/operator/provider_azure_flags.go b/operator/provider_azure_flags.go
index c9dc555d5a..4925d2bf76 100644
--- a/operator/provider_azure_flags.go
+++ b/operator/provider_azure_flags.go
@@ -38,5 +38,8 @@ func init() {
 	flags.String(operatorOption.AzureUserAssignedIdentityID, "", "ID of the user assigned identity used to auth with the Azure API")
 	option.BindEnvWithLegacyEnvFallback(operatorOption.AzureUserAssignedIdentityID, "AZURE_USER_ASSIGNED_IDENTITY_ID")
 
+	flags.Bool(operatorOption.AzureUsePrimaryAddress, true, "Use Azure IP address from interface's primary IPConfigurations")
+	option.BindEnvWithLegacyEnvFallback(operatorOption.AzureUsePrimaryAddress, "AZURE_USE_PRIMARY_ADDRESS")
+
 	viper.BindPFlags(flags)
 }
diff --git a/pkg/azure/api/api.go b/pkg/azure/api/api.go
index b65f98717a..2dd72cf233 100644
--- a/pkg/azure/api/api.go
+++ b/pkg/azure/api/api.go
@@ -54,6 +54,7 @@ type Client struct {
 	vmscalesets     compute.VirtualMachineScaleSetsClient
 	limiter         *helpers.ApiLimiter
 	metricsAPI      MetricsAPI
+	usePrimary      bool
 }
 
 // MetricsAPI represents the metrics maintained by the Azure API client
@@ -91,7 +92,7 @@ func constructAuthorizer(cloudName, userAssignedIdentityID string) (autorest.Aut
 }
 
 // NewClient returns a new Azure client
-func NewClient(cloudName, subscriptionID, resourceGroup, userAssignedIdentityID string, metrics MetricsAPI, rateLimit float64, burst int) (*Client, error) {
+func NewClient(cloudName, subscriptionID, resourceGroup, userAssignedIdentityID string, metrics MetricsAPI, rateLimit float64, burst int, usePrimary bool) (*Client, error) {
 	c := &Client{
 		resourceGroup:   resourceGroup,
 		interfaces:      network.NewInterfacesClient(subscriptionID),
@@ -100,6 +101,7 @@ func NewClient(cloudName, subscriptionID, resourceGroup, userAssignedIdentityID
 		vmscalesets:     compute.NewVirtualMachineScaleSetsClient(subscriptionID),
 		metricsAPI:      metrics,
 		limiter:         helpers.NewApiLimiter(metrics, rateLimit, burst),
+		usePrimary:      usePrimary,
 	}
 
 	authorizer, err := constructAuthorizer(cloudName, userAssignedIdentityID)
@@ -219,7 +221,7 @@ func (c *Client) vmssNetworkInterfaces(ctx context.Context) ([]network.Interface
 
 // parseInterfaces parses a network.Interface as returned by the Azure API
 // converts it into a types.AzureInterface
-func parseInterface(iface *network.Interface, subnets ipamTypes.SubnetMap) (instanceID string, i *types.AzureInterface) {
+func parseInterface(iface *network.Interface, subnets ipamTypes.SubnetMap, usePrimary bool) (instanceID string, i *types.AzureInterface) {
 	i = &types.AzureInterface{}
 
 	if iface.VirtualMachine != nil && iface.VirtualMachine.ID != nil {
@@ -247,6 +249,9 @@ func parseInterface(iface *network.Interface, subnets ipamTypes.SubnetMap) (inst
 
 	if iface.IPConfigurations != nil {
 		for _, ip := range *iface.IPConfigurations {
+			if !usePrimary && ip.Primary != nil && *ip.Primary {
+				continue
+			}
 			if ip.PrivateIPAddress != nil {
 				addr := types.AzureAddress{
 					IP:    *ip.PrivateIPAddress,
@@ -289,7 +294,7 @@ func (c *Client) GetInstances(ctx context.Context, subnets ipamTypes.SubnetMap)
 	}
 
 	for _, iface := range networkInterfaces {
-		if id, azureInterface := parseInterface(&iface, subnets); id != "" {
+		if id, azureInterface := parseInterface(&iface, subnets, c.usePrimary); id != "" {
 			instances.Update(id, ipamTypes.InterfaceRevision{Resource: azureInterface})
 		}
 	}
diff --git a/pkg/ipam/allocator/azure/azure.go b/pkg/ipam/allocator/azure/azure.go
index 6ca696aed7..6213b1dd10 100644
--- a/pkg/ipam/allocator/azure/azure.go
+++ b/pkg/ipam/allocator/azure/azure.go
@@ -78,7 +78,7 @@ func (*AllocatorAzure) Start(getterUpdater ipam.CiliumNodeGetterUpdater) (alloca
 		iMetrics = &ipamMetrics.NoOpMetrics{}
 	}
 
-	azureClient, err := azureAPI.NewClient(operatorOption.Config.AzureCloudName, subscriptionID, resourceGroupName, operatorOption.Config.AzureUserAssignedIdentityID, azMetrics, operatorOption.Config.IPAMAPIQPSLimit, operatorOption.Config.IPAMAPIBurst)
+	azureClient, err := azureAPI.NewClient(operatorOption.Config.AzureCloudName, subscriptionID, resourceGroupName, operatorOption.Config.AzureUserAssignedIdentityID, azMetrics, operatorOption.Config.IPAMAPIQPSLimit, operatorOption.Config.IPAMAPIBurst, operatorOption.Config.AzureUsePrimaryAddress)
 	if err != nil {
 		return nil, fmt.Errorf("unable to create Azure client: %w", err)
 	}
diff --git a/pkg/k8s/apis/cilium.io/v2/clrp_types.go b/pkg/k8s/apis/cilium.io/v2/clrp_types.go
index 7a3aff6d70..fc49c240c1 100644
--- a/pkg/k8s/apis/cilium.io/v2/clrp_types.go
+++ b/pkg/k8s/apis/cilium.io/v2/clrp_types.go
@@ -234,6 +234,11 @@ func (pInfo *PortInfo) SanitizePortInfo(checkNamedPort bool) (uint16, string, lb
 	}
 	pName = strings.ToLower(pInfo.Name) // Normalize for case insensitive comparison
 
-	protocol = lb.NewL4Type(string(pInfo.Protocol))
+	// Sanitize protocol
+	var err error
+	protocol, err = lb.NewL4Type(string(pInfo.Protocol))
+	if err != nil {
+		return pInt, pName, protocol, err
+	}
 	return pInt, pName, protocol, nil
 }
diff --git a/pkg/k8s/service.go b/pkg/k8s/service.go
index 35e7030abf..09a6b3db4a 100644
--- a/pkg/k8s/service.go
+++ b/pkg/k8s/service.go
@@ -442,6 +442,18 @@ func NewService(ip net.IP, externalIPs, loadBalancerIPs, loadBalancerSourceRange
 	}
 }
 
+// UniquePorts returns a map of all unique ports configured in the service
+func (s *Service) UniquePorts() map[uint16]bool {
+	// We are not discriminating the different L4 protocols on the same L4
+	// port so we create the number of unique sets of service IP + service
+	// port.
+	uniqPorts := map[uint16]bool{}
+	for _, p := range s.Ports {
+		uniqPorts[p.Port] = true
+	}
+	return uniqPorts
+}
+
 // NewClusterService returns the serviceStore.ClusterService representing a
 // Kubernetes Service
 func NewClusterService(id ServiceID, k8sService *Service, k8sEndpoints *Endpoints) serviceStore.ClusterService {
diff --git a/pkg/k8s/service_cache.go b/pkg/k8s/service_cache.go
index e47c725896..7731451ef0 100644
--- a/pkg/k8s/service_cache.go
+++ b/pkg/k8s/service_cache.go
@@ -341,6 +341,28 @@ func (s *ServiceCache) DeleteEndpointSlices(epSlice *slim_discovery_v1beta1.Endp
 // FrontendList is the list of all k8s service frontends
 type FrontendList map[string]struct{}
 
+// LooseMatch returns true if the provided frontend is found in the
+// FrontendList. If the frontend has a protocol value set, it only matches a
+// k8s service with a matching protocol. If no protocol is set, any k8s service
+// matching frontend IP and port is considered a match, regardless of protocol.
+func (l FrontendList) LooseMatch(frontend loadbalancer.L3n4Addr) (exists bool) {
+	switch frontend.Protocol {
+	case loadbalancer.NONE:
+		for _, protocol := range loadbalancer.AllProtocols {
+			frontend.Protocol = protocol
+			_, exists = l[frontend.StringWithProtocol()]
+			if exists {
+				return
+			}
+		}
+
+	// If the protocol is set, perform an exact match
+	default:
+		_, exists = l[frontend.StringWithProtocol()]
+	}
+	return
+}
+
 // UniqueServiceFrontends returns all externally scoped services known to
 // the service cache as a map, indexed by the string representation of a
 // loadbalancer.L3n4Addr. This helper is only used in unit tests.
@@ -357,12 +379,12 @@ func (s *ServiceCache) UniqueServiceFrontends() FrontendList {
 				L4Addr: *p,
 				Scope:  loadbalancer.ScopeExternal,
 			}
-			uniqueFrontends[address.String()] = struct{}{}
+			uniqueFrontends[address.StringWithProtocol()] = struct{}{}
 		}
 		for _, nodePortFEs := range svc.NodePorts {
 			for _, fe := range nodePortFEs {
 				if fe.Scope == loadbalancer.ScopeExternal {
-					uniqueFrontends[fe.String()] = struct{}{}
+					uniqueFrontends[fe.StringWithProtocol()] = struct{}{}
 				}
 			}
 		}
diff --git a/pkg/k8s/watchers/pod.go b/pkg/k8s/watchers/pod.go
index da46a87b1e..bed5e05a0b 100644
--- a/pkg/k8s/watchers/pod.go
+++ b/pkg/k8s/watchers/pod.go
@@ -491,9 +491,13 @@ func (k *K8sWatcher) genServiceMappings(pod *slim_corev1.Pod, podIPs []string, l
 				continue
 			}
 
+			proto, err := loadbalancer.NewL4Type(string(p.Protocol))
+			if err != nil {
+				continue
+			}
+
 			var bes4 []loadbalancer.Backend
 			var bes6 []loadbalancer.Backend
-			proto := loadbalancer.NewL4Type(string(p.Protocol))
 
 			for _, podIP := range podIPs {
 				be := loadbalancer.Backend{
diff --git a/pkg/k8s/watchers/watcher.go b/pkg/k8s/watchers/watcher.go
index c43d79825a..b95807b78b 100644
--- a/pkg/k8s/watchers/watcher.go
+++ b/pkg/k8s/watchers/watcher.go
@@ -626,8 +626,16 @@ func (k *K8sWatcher) delK8sSVCs(svc k8s.ServiceID, svcInfo *k8s.Service, se *k8s
 		logfields.K8sNamespace: svc.Namespace,
 	})
 
+	repPorts := svcInfo.UniquePorts()
+
 	frontends := []*loadbalancer.L3n4Addr{}
+
 	for portName, svcPort := range svcInfo.Ports {
+		if !repPorts[svcPort.Port] {
+			continue
+		}
+		repPorts[svcPort.Port] = false
+
 		fe := loadbalancer.NewL3n4Addr(svcPort.Protocol, svcInfo.FrontendIP, svcPort.Port, loadbalancer.ScopeExternal)
 		frontends = append(frontends, fe)
 
@@ -741,8 +749,14 @@ func genCartesianProduct(
 
 // datapathSVCs returns all services that should be set in the datapath.
 func datapathSVCs(svc *k8s.Service, endpoints *k8s.Endpoints) (svcs []loadbalancer.SVC) {
+	uniqPorts := svc.UniquePorts()
+
 	clusterIPPorts := map[loadbalancer.FEPortName]*loadbalancer.L4Addr{}
 	for fePortName, fePort := range svc.Ports {
+		if !uniqPorts[fePort.Port] {
+			continue
+		}
+		uniqPorts[fePort.Port] = false
 		clusterIPPorts[fePortName] = fePort
 	}
 	if svc.FrontendIP != nil {
@@ -852,7 +866,6 @@ func (k *K8sWatcher) addK8sSVCs(svcID k8s.ServiceID, oldSvc, svc *k8s.Service, e
 			Name:                      svcID.Name,
 			Namespace:                 svcID.Namespace,
 		}
-		log.WithField(logfields.Object, logfields.Repr(*p)).Debug("upserting loadbalancer repr")
 		if _, _, err := k.svcManager.UpsertService(p); err != nil {
 			scopedLog.WithError(err).Error("Error while inserting service in LB map")
 		}
diff --git a/pkg/loadbalancer/loadbalancer.go b/pkg/loadbalancer/loadbalancer.go
index 4dd2b623ff..508c392aff 100644
--- a/pkg/loadbalancer/loadbalancer.go
+++ b/pkg/loadbalancer/loadbalancer.go
@@ -160,7 +160,6 @@ func (s ServiceFlags) UInt16() uint16 {
 }
 
 const (
-	// NONE type.
 	NONE = L4Type("NONE")
 	// TCP type.
 	TCP = L4Type("TCP")
@@ -266,25 +265,14 @@ func (s *SVC) GetModel() *models.Service {
 	}
 }
 
-func NewL4Type(name string) L4Type {
+func NewL4Type(name string) (L4Type, error) {
 	switch strings.ToLower(name) {
 	case "tcp":
-		return TCP
+		return TCP, nil
 	case "udp":
-		return UDP
+		return UDP, nil
 	default:
-		return NONE
-	}
-}
-
-func NewL4TypeFromNumber(proto uint8) L4Type {
-	switch proto {
-	case 6:
-		return TCP
-	case 17:
-		return UDP
-	default:
-		return NONE
+		return "", fmt.Errorf("unknown L4 protocol")
 	}
 }
 
@@ -319,11 +307,6 @@ func (l *L4Addr) DeepCopy() *L4Addr {
 	}
 }
 
-// String returns a string representation of an L4Addr
-func (l *L4Addr) String() string {
-	return fmt.Sprintf("%d/%s", l.Port, l.Protocol)
-}
-
 // L3n4Addr is used to store, as an unique L3+L4 address in the KVStore. It also
 // includes the lookup scope for frontend addresses which is used in service
 // handling for externalTrafficPolicy=Local, that is, Scope{External,Internal}.
@@ -353,9 +336,16 @@ func NewL3n4AddrFromModel(base *models.FrontendAddress) (*L3n4Addr, error) {
 		return nil, fmt.Errorf("missing IP address")
 	}
 
-	p := NewL4Type(base.Protocol)
+	proto := NONE
+	if base.Protocol != "" {
+		p, err := NewL4Type(base.Protocol)
+		if err != nil {
+			return nil, err
+		}
+		proto = p
+	}
 
-	l4addr := NewL4Addr(p, base.Port)
+	l4addr := NewL4Addr(proto, base.Port)
 	ip := net.ParseIP(base.IP)
 	if ip == nil {
 		return nil, fmt.Errorf("invalid IP address \"%s\"", base.IP)
@@ -388,8 +378,8 @@ func NewBackendFromBackendModel(base *models.BackendAddress) (*Backend, error) {
 		return nil, fmt.Errorf("missing IP address")
 	}
 
-	p := NewL4Type(base.Protocol)
-	l4addr := NewL4Addr(p, base.Port)
+	// FIXME: Should this be NONE ?
+	l4addr := NewL4Addr(NONE, base.Port)
 	ip := net.ParseIP(*base.IP)
 	if ip == nil {
 		return nil, fmt.Errorf("invalid IP address \"%s\"", *base.IP)
@@ -403,8 +393,8 @@ func NewL3n4AddrFromBackendModel(base *models.BackendAddress) (*L3n4Addr, error)
 		return nil, fmt.Errorf("missing IP address")
 	}
 
-	p := NewL4Type(base.Protocol)
-	l4addr := NewL4Addr(p, base.Port)
+	// FIXME: Should this be NONE ?
+	l4addr := NewL4Addr(NONE, base.Port)
 	ip := net.ParseIP(*base.IP)
 	if ip == nil {
 		return nil, fmt.Errorf("invalid IP address \"%s\"", *base.IP)
@@ -422,10 +412,9 @@ func (a *L3n4Addr) GetModel() *models.FrontendAddress {
 		scope = models.FrontendAddressScopeInternal
 	}
 	return &models.FrontendAddress{
-		IP:       a.IP.String(),
-		Port:     a.Port,
-		Scope:    scope,
-		Protocol: a.Protocol,
+		IP:    a.IP.String(),
+		Port:  a.Port,
+		Scope: scope,
 	}
 }
 
@@ -439,13 +428,25 @@ func (b *Backend) GetBackendModel() *models.BackendAddress {
 		IP:       &ip,
 		Port:     b.Port,
 		NodeName: b.NodeName,
-		Protocol: b.Protocol,
 	}
 }
 
-// String returns the L3n4Addr in the "IPv4:Port/Protocol[/Scope]" format for IPv4 and
-// "[IPv6]:Port/Protocol[/Scope]" format for IPv6.
+// String returns the L3n4Addr in the "IPv4:Port[/Scope]" format for IPv4 and
+// "[IPv6]:Port[/Scope]" format for IPv6.
 func (a *L3n4Addr) String() string {
+	var scope string
+	if a.Scope == ScopeInternal {
+		scope = "/i"
+	}
+	if a.IsIPv6() {
+		return fmt.Sprintf("[%s]:%d%s", a.IP.String(), a.Port, scope)
+	}
+	return fmt.Sprintf("%s:%d%s", a.IP.String(), a.Port, scope)
+}
+
+// StringWithProtocol returns the L3n4Addr in the "IPv4:Port/Protocol[/Scope]"
+// format for IPv4 and "[IPv6]:Port/Protocol[/Scope]" format for IPv6.
+func (a *L3n4Addr) StringWithProtocol() string {
 	var scope string
 	if a.Scope == ScopeInternal {
 		scope = "/i"
@@ -476,7 +477,14 @@ func (a *L3n4Addr) DeepCopy() *L3n4Addr {
 
 // Hash calculates L3n4Addr's internal SHA256Sum.
 func (a L3n4Addr) Hash() string {
-	str := []byte(fmt.Sprintf("%s", a.String()))
+	// FIXME: Remove Protocol's omission once we care about protocols.
+	protoBak := a.Protocol
+	a.Protocol = ""
+	defer func() {
+		a.Protocol = protoBak
+	}()
+
+	str := []byte(fmt.Sprintf("%+v", a))
 	return fmt.Sprintf("%x", sha512.Sum512_256(str))
 }
 
diff --git a/pkg/maps/lbmap/ipv4.go b/pkg/maps/lbmap/ipv4.go
index 523f20f908..bc2751e1bd 100644
--- a/pkg/maps/lbmap/ipv4.go
+++ b/pkg/maps/lbmap/ipv4.go
@@ -171,28 +171,11 @@ func NewService4Key(ip net.IP, port uint16, proto u8proto.U8proto, scope uint8,
 }
 
 func (k *Service4Key) String() string {
-	return serviceKey(
-		k.Address.String(),
-		k.Port,
-		k.GetProtocol(),
-		k.Scope == loadbalancer.ScopeInternal,
-	)
-}
-
-func serviceKey(address string, port uint16, protocol uint8, internal bool) string {
-	a := net.JoinHostPort(address, fmt.Sprintf("%d", port))
-
-	p, err := u8proto.FromNumber(protocol)
-	if err != nil {
-		p = u8proto.ANY
-	}
-
-	var i string
-	if internal {
-		i = "/i"
+	addr := net.JoinHostPort(k.Address.String(), fmt.Sprintf("%d", k.Port))
+	if k.Scope == loadbalancer.ScopeInternal {
+		addr += "/i"
 	}
-
-	return fmt.Sprintf("%s/%s%s", a, p, i)
+	return addr
 }
 
 func (k *Service4Key) GetKeyPtr() unsafe.Pointer { return unsafe.Pointer(k) }
@@ -206,7 +189,6 @@ func (k *Service4Key) SetScope(scope uint8)      { k.Scope = scope }
 func (k *Service4Key) GetScope() uint8           { return k.Scope }
 func (k *Service4Key) GetAddress() net.IP        { return k.Address.IP() }
 func (k *Service4Key) GetPort() uint16           { return k.Port }
-func (k *Service4Key) GetProtocol() uint8        { return k.Proto }
 func (k *Service4Key) MapDelete() error          { return k.Map().Delete(k.ToNetwork()) }
 
 func (k *Service4Key) RevNatValue() RevNatValue {
@@ -331,7 +313,6 @@ func (v *Backend4Value) GetValuePtr() unsafe.Pointer { return unsafe.Pointer(v)
 
 func (b *Backend4Value) GetAddress() net.IP { return b.Address.IP() }
 func (b *Backend4Value) GetPort() uint16    { return b.Port }
-func (b *Backend4Value) GetProtocol() uint8 { return uint8(b.Proto) }
 
 func (v *Backend4Value) ToNetwork() BackendValue {
 	n := *v
diff --git a/pkg/maps/lbmap/ipv6.go b/pkg/maps/lbmap/ipv6.go
index 1d20e2dba8..25b3a96cbb 100644
--- a/pkg/maps/lbmap/ipv6.go
+++ b/pkg/maps/lbmap/ipv6.go
@@ -164,12 +164,11 @@ func NewService6Key(ip net.IP, port uint16, proto u8proto.U8proto, scope uint8,
 }
 
 func (k *Service6Key) String() string {
-	return serviceKey(
-		k.Address.String(),
-		k.Port,
-		k.GetProtocol(),
-		k.Scope == loadbalancer.ScopeInternal,
-	)
+	if k.Scope == loadbalancer.ScopeInternal {
+		return fmt.Sprintf("[%s]:%d/i", k.Address, k.Port)
+	} else {
+		return fmt.Sprintf("[%s]:%d", k.Address, k.Port)
+	}
 }
 
 func (k *Service6Key) GetKeyPtr() unsafe.Pointer { return unsafe.Pointer(k) }
@@ -183,7 +182,6 @@ func (k *Service6Key) SetScope(scope uint8)      { k.Scope = scope }
 func (k *Service6Key) GetScope() uint8           { return k.Scope }
 func (k *Service6Key) GetAddress() net.IP        { return k.Address.IP() }
 func (k *Service6Key) GetPort() uint16           { return k.Port }
-func (k *Service6Key) GetProtocol() uint8        { return k.Proto }
 func (k *Service6Key) MapDelete() error          { return k.Map().Delete(k.ToNetwork()) }
 
 func (k *Service6Key) RevNatValue() RevNatValue {
@@ -299,7 +297,6 @@ func (v *Backend6Value) GetValuePtr() unsafe.Pointer { return unsafe.Pointer(v)
 
 func (b *Backend6Value) GetAddress() net.IP { return b.Address.IP() }
 func (b *Backend6Value) GetPort() uint16    { return b.Port }
-func (b *Backend6Value) GetProtocol() uint8 { return uint8(b.Proto) }
 
 func (v *Backend6Value) ToNetwork() BackendValue {
 	n := *v
diff --git a/pkg/maps/lbmap/lbmap.go b/pkg/maps/lbmap/lbmap.go
index b04c77d6a7..04059f3e51 100644
--- a/pkg/maps/lbmap/lbmap.go
+++ b/pkg/maps/lbmap/lbmap.go
@@ -64,7 +64,6 @@ type UpsertServiceParams struct {
 	ID                        uint16
 	IP                        net.IP
 	Port                      uint16
-	Protocol                  string
 	Backends                  map[string]uint16
 	PrevBackendCount          int
 	IPv6                      bool
@@ -91,15 +90,10 @@ func (lbmap *LBBPFMap) UpsertService(p *UpsertServiceParams) error {
 		return fmt.Errorf("Invalid svc ID 0")
 	}
 
-	proto, err := u8proto.ParseProtocol(p.Protocol)
-	if err != nil {
-		return err
-	}
-
 	if p.IPv6 {
-		svcKey = NewService6Key(p.IP, p.Port, proto, p.Scope, 0)
+		svcKey = NewService6Key(p.IP, p.Port, u8proto.ANY, p.Scope, 0)
 	} else {
-		svcKey = NewService4Key(p.IP, p.Port, proto, p.Scope, 0)
+		svcKey = NewService4Key(p.IP, p.Port, u8proto.ANY, p.Scope, 0)
 	}
 
 	slot := 1
@@ -182,16 +176,12 @@ func (*LBBPFMap) DeleteService(svc loadbalancer.L3n4AddrID, backendCount int, us
 		return fmt.Errorf("Invalid svc ID 0")
 	}
 
-	proto, err := u8proto.ParseProtocol(string(svc.Protocol))
-	if err != nil {
-		return err
-	}
 	ipv6 := svc.IsIPv6()
 	if ipv6 {
-		svcKey = NewService6Key(svc.IP, svc.Port, proto, svc.Scope, 0)
+		svcKey = NewService6Key(svc.IP, svc.Port, u8proto.ANY, svc.Scope, 0)
 		revNATKey = NewRevNat6Key(uint16(svc.ID))
 	} else {
-		svcKey = NewService4Key(svc.IP, svc.Port, proto, svc.Scope, 0)
+		svcKey = NewService4Key(svc.IP, svc.Port, u8proto.ANY, svc.Scope, 0)
 		revNATKey = NewRevNat4Key(uint16(svc.ID))
 	}
 
@@ -216,7 +206,7 @@ func (*LBBPFMap) DeleteService(svc loadbalancer.L3n4AddrID, backendCount int, us
 }
 
 // AddBackend adds a backend into a BPF map.
-func (*LBBPFMap) AddBackend(id uint16, ip net.IP, protocol loadbalancer.L4Type, port uint16, ipv6 bool) error {
+func (*LBBPFMap) AddBackend(id uint16, ip net.IP, port uint16, ipv6 bool) error {
 	var (
 		backend Backend
 		err     error
@@ -226,14 +216,10 @@ func (*LBBPFMap) AddBackend(id uint16, ip net.IP, protocol loadbalancer.L4Type,
 		return fmt.Errorf("Invalid backend ID 0")
 	}
 
-	p, err := u8proto.ParseProtocol(string(protocol))
-	if err != nil {
-		return err
-	}
 	if ipv6 {
-		backend, err = NewBackend6(loadbalancer.BackendID(id), ip, port, p)
+		backend, err = NewBackend6(loadbalancer.BackendID(id), ip, port, u8proto.ANY)
 	} else {
-		backend, err = NewBackend4(loadbalancer.BackendID(id), ip, port, p)
+		backend, err = NewBackend4(loadbalancer.BackendID(id), ip, port, u8proto.ANY)
 	}
 	if err != nil {
 		return fmt.Errorf("Unable to create backend (%d, %s, %d, %t): %s",
@@ -487,7 +473,7 @@ func (*LBBPFMap) DumpBackendMaps() ([]*loadbalancer.Backend, error) {
 	for backendID, backendVal := range backendValueMap {
 		ip := backendVal.GetAddress()
 		port := backendVal.GetPort()
-		proto := loadbalancer.NewL4TypeFromNumber(backendVal.GetProtocol())
+		proto := loadbalancer.NONE
 		lbBackend := loadbalancer.NewBackend(backendID, proto, ip, port)
 		lbBackends = append(lbBackends, lbBackend)
 	}
diff --git a/pkg/maps/lbmap/types.go b/pkg/maps/lbmap/types.go
index 3260ec1d33..f8eefee7ac 100644
--- a/pkg/maps/lbmap/types.go
+++ b/pkg/maps/lbmap/types.go
@@ -53,9 +53,6 @@ type ServiceKey interface {
 	// Get frontend port
 	GetPort() uint16
 
-	// Get protocol
-	GetProtocol() uint8
-
 	// Returns a RevNatValue matching a ServiceKey
 	RevNatValue() RevNatValue
 
@@ -128,9 +125,6 @@ type BackendValue interface {
 	// Get backend port
 	GetPort() uint16
 
-	// Get backend protocol
-	GetProtocol() uint8
-
 	// Convert fields to network byte order.
 	ToNetwork() BackendValue
 }
@@ -174,8 +168,7 @@ type BackendIDByServiceIDSet map[uint16]map[uint16]struct{} // svc ID => backend
 type SourceRangeSetByServiceID map[uint16][]*cidr.CIDR // svc ID => src range CIDRs
 
 func svcFrontend(svcKey ServiceKey, svcValue ServiceValue) *loadbalancer.L3n4AddrID {
-	p := loadbalancer.NewL4TypeFromNumber(svcKey.GetProtocol())
-	feL3n4Addr := loadbalancer.NewL3n4Addr(p, svcKey.GetAddress(), svcKey.GetPort(), svcKey.GetScope())
+	feL3n4Addr := loadbalancer.NewL3n4Addr(loadbalancer.NONE, svcKey.GetAddress(), svcKey.GetPort(), svcKey.GetScope())
 	feL3n4AddrID := &loadbalancer.L3n4AddrID{
 		L3n4Addr: *feL3n4Addr,
 		ID:       loadbalancer.ID(svcValue.GetRevNat()),
@@ -186,7 +179,7 @@ func svcFrontend(svcKey ServiceKey, svcValue ServiceValue) *loadbalancer.L3n4Add
 func svcBackend(backendID loadbalancer.BackendID, backend BackendValue) *loadbalancer.Backend {
 	beIP := backend.GetAddress()
 	bePort := backend.GetPort()
-	p := loadbalancer.NewL4TypeFromNumber(backend.GetProtocol())
-	beBackend := loadbalancer.NewBackend(backendID, p, beIP, bePort)
+	beProto := loadbalancer.NONE
+	beBackend := loadbalancer.NewBackend(backendID, beProto, beIP, bePort)
 	return beBackend
 }
diff --git a/pkg/maps/sockmap/sockmap.go b/pkg/maps/sockmap/sockmap.go
index 9fa63e017b..f6731d7fff 100644
--- a/pkg/maps/sockmap/sockmap.go
+++ b/pkg/maps/sockmap/sockmap.go
@@ -23,20 +23,19 @@ import (
 	"github.com/cilium/cilium/pkg/logging"
 	"github.com/cilium/cilium/pkg/logging/logfields"
 	"github.com/cilium/cilium/pkg/types"
-	"github.com/cilium/cilium/pkg/u8proto"
 )
 
 // SockmapKey is the 5-tuple used to lookup a socket
 // +k8s:deepcopy-gen=true
 // +k8s:deepcopy-gen:interfaces=github.com/cilium/cilium/pkg/bpf.MapKey
 type SockmapKey struct {
-	DIP      types.IPv6 `align:"$union0"`
-	SIP      types.IPv6 `align:"$union1"`
-	Family   uint8      `align:"family"`
-	Protocol uint8      `align:"protocol"`
-	Pad8     uint16     `align:"pad8"`
-	SPort    uint32     `align:"sport"`
-	DPort    uint32     `align:"dport"`
+	DIP    types.IPv6 `align:"$union0"`
+	SIP    types.IPv6 `align:"$union1"`
+	Family uint8      `align:"family"`
+	Pad7   uint8      `align:"pad7"`
+	Pad8   uint16     `align:"pad8"`
+	SPort  uint32     `align:"sport"`
+	DPort  uint32     `align:"dport"`
 }
 
 // SockmapValue is the fd of a socket
@@ -48,11 +47,7 @@ type SockmapValue struct {
 
 // String pretty print the 5-tuple as sip:sport->dip:dport
 func (v SockmapKey) String() string {
-	p, err := u8proto.FromNumber(v.Protocol)
-	if err != nil {
-		p = u8proto.ANY
-	}
-	return fmt.Sprintf("%s:%d/%s->%s:%d/%s", v.SIP.String(), v.SPort, p, v.DIP.String(), v.DPort, p)
+	return fmt.Sprintf("%s:%d->%s:%d", v.SIP.String(), v.SPort, v.DIP.String(), v.DPort)
 }
 
 // String pretty print the file descriptor value, note this is local to agent.
diff --git a/pkg/option/config.go b/pkg/option/config.go
index 56a7756b13..4af044c928 100644
--- a/pkg/option/config.go
+++ b/pkg/option/config.go
@@ -1510,7 +1510,7 @@ type DaemonConfig struct {
 	// runs in the same container as Cilium.
 	EnvoyLogPath string
 
-	// SockopsEnable specifies whether to enable sockops (socket lookup).
+	// EnableSockOps specifies whether to enable sockops (socket lookup).
 	SockopsEnable bool
 
 	// PrependIptablesChains is the name of the option to enable prepending
diff --git a/pkg/service/service.go b/pkg/service/service.go
index 42101ce6d4..8f0a948c3d 100644
--- a/pkg/service/service.go
+++ b/pkg/service/service.go
@@ -46,7 +46,7 @@ var (
 type LBMap interface {
 	UpsertService(*lbmap.UpsertServiceParams) error
 	DeleteService(lb.L3n4AddrID, int, bool) error
-	AddBackend(uint16, net.IP, lb.L4Type, uint16, bool) error
+	AddBackend(uint16, net.IP, uint16, bool) error
 	DeleteBackendByID(uint16, bool) error
 	AddAffinityMatch(uint16, uint16) error
 	DeleteAffinityMatch(uint16, uint16) error
@@ -702,7 +702,6 @@ func (s *Service) upsertServiceIntoLBMaps(svc *svcInfo, onlyLocalBackends bool,
 		}).Debug("Adding new backend")
 
 		if err := s.lbmap.AddBackend(uint16(b.ID), b.L3n4Addr.IP,
-			b.L3n4Addr.L4Addr.Protocol,
 			b.L3n4Addr.L4Addr.Port, ipv6); err != nil {
 			return err
 		}
@@ -718,7 +717,6 @@ func (s *Service) upsertServiceIntoLBMaps(svc *svcInfo, onlyLocalBackends bool,
 		ID:                        uint16(svc.frontend.ID),
 		IP:                        svc.frontend.L3n4Addr.IP,
 		Port:                      svc.frontend.L3n4Addr.L4Addr.Port,
-		Protocol:                  string(svc.frontend.L3n4Addr.L4Addr.Protocol),
 		Backends:                  backends,
 		PrevBackendCount:          prevBackendCount,
 		IPv6:                      ipv6,
diff --git a/pkg/u8proto/u8proto.go b/pkg/u8proto/u8proto.go
index 91f26a07eb..b396d2c043 100644
--- a/pkg/u8proto/u8proto.go
+++ b/pkg/u8proto/u8proto.go
@@ -43,7 +43,6 @@ var protoNames = map[U8proto]string{
 var ProtoIDs = map[string]U8proto{
 	"all":    0,
 	"any":    0,
-	"none":   0,
 	"icmp":   1,
 	"tcp":    6,
 	"udp":    17,
@@ -63,13 +62,5 @@ func ParseProtocol(proto string) (U8proto, error) {
 	if u, ok := ProtoIDs[strings.ToLower(proto)]; ok {
 		return u, nil
 	}
-	return 0, fmt.Errorf("unknown protocol %q", proto)
-}
-
-func FromNumber(proto uint8) (U8proto, error) {
-	_, ok := protoNames[U8proto(proto)]
-	if !ok {
-		return 0, fmt.Errorf("unknown protocol %d", proto)
-	}
-	return U8proto(proto), nil
+	return 0, fmt.Errorf("unknown protocol '%s'", proto)
 }


Test patch:
diff --git a/pkg/azure/api/api_test.go b/pkg/azure/api/api_test.go
index e0914fa4a7..eb3505a19b 100644
--- a/pkg/azure/api/api_test.go
+++ b/pkg/azure/api/api_test.go
@@ -36,7 +36,7 @@ var _ = check.Suite(&ApiSuite{})
 
 func (a *ApiSuite) TestRateLimit(c *check.C) {
 	metricsAPI := mock.NewMockMetrics()
-	client, err := NewClient("", "dummy-subscription", "dummy-resource-group", "", metricsAPI, 10.0, 4)
+	client, err := NewClient("", "dummy-subscription", "dummy-resource-group", "", metricsAPI, 10.0, 4, true)
 	c.Assert(err, check.IsNil)
 	c.Assert(client, check.Not(check.IsNil))
 
diff --git a/pkg/k8s/endpoints_test.go b/pkg/k8s/endpoints_test.go
index 0e3ebdca18..7bc606ef5a 100644
--- a/pkg/k8s/endpoints_test.go
+++ b/pkg/k8s/endpoints_test.go
@@ -52,7 +52,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.TCP,
+									Protocol: loadbalancer.NONE,
 									Port:     1,
 								},
 							},
@@ -67,7 +67,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.TCP,
+									Protocol: loadbalancer.NONE,
 									Port:     1,
 								},
 							},
@@ -86,7 +86,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.TCP,
+									Protocol: loadbalancer.NONE,
 									Port:     1,
 								},
 							},
@@ -100,7 +100,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.2": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.TCP,
+									Protocol: loadbalancer.NONE,
 									Port:     1,
 								},
 							},
@@ -118,7 +118,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.TCP,
+									Protocol: loadbalancer.NONE,
 									Port:     1,
 								},
 							},
@@ -132,7 +132,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foz": {
-									Protocol: loadbalancer.TCP,
+									Protocol: loadbalancer.NONE,
 									Port:     1,
 								},
 							},
@@ -150,7 +150,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.TCP,
+									Protocol: loadbalancer.NONE,
 									Port:     1,
 								},
 							},
@@ -158,70 +158,6 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 					},
 				},
 			},
-			args: args{
-				o: &Endpoints{
-					Backends: map[string]*Backend{
-						"172.20.0.1": {
-							Ports: map[string]*loadbalancer.L4Addr{
-								"foo": {
-									Protocol: loadbalancer.TCP,
-									Port:     2,
-								},
-							},
-						},
-					},
-				},
-			},
-			want: false,
-		},
-		{
-			name: "protocols different content",
-			fields: fields{
-				svcEP: &Endpoints{
-					Backends: map[string]*Backend{
-						"172.20.0.1": {
-							Ports: map[string]*loadbalancer.L4Addr{
-								"foo": {
-									Protocol: loadbalancer.TCP,
-									Port:     2,
-								},
-							},
-						},
-					},
-				},
-			},
-			args: args{
-				o: &Endpoints{
-					Backends: map[string]*Backend{
-						"172.20.0.1": {
-							Ports: map[string]*loadbalancer.L4Addr{
-								"foo": {
-									Protocol: loadbalancer.UDP,
-									Port:     2,
-								},
-							},
-						},
-					},
-				},
-			},
-			want: false,
-		},
-		{
-			name: "protocols different one none",
-			fields: fields{
-				svcEP: &Endpoints{
-					Backends: map[string]*Backend{
-						"172.20.0.1": {
-							Ports: map[string]*loadbalancer.L4Addr{
-								"foo": {
-									Protocol: loadbalancer.TCP,
-									Port:     2,
-								},
-							},
-						},
-					},
-				},
-			},
 			args: args{
 				o: &Endpoints{
 					Backends: map[string]*Backend{
@@ -246,7 +182,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.TCP,
+									Protocol: loadbalancer.NONE,
 									Port:     1,
 								},
 							},
@@ -260,11 +196,11 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.TCP,
+									Protocol: loadbalancer.NONE,
 									Port:     1,
 								},
 								"baz": {
-									Protocol: loadbalancer.TCP,
+									Protocol: loadbalancer.NONE,
 									Port:     2,
 								},
 							},
@@ -283,7 +219,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.TCP,
+									Protocol: loadbalancer.NONE,
 									Port:     1,
 								},
 							},
diff --git a/pkg/k8s/service_cache_test.go b/pkg/k8s/service_cache_test.go
index 531fc96ce9..332248dbdd 100644
--- a/pkg/k8s/service_cache_test.go
+++ b/pkg/k8s/service_cache_test.go
@@ -95,6 +95,33 @@ func (s *K8sSuite) TestGetUniqueServiceFrontends(c *check.C) {
 		"1.1.1.1:20/TCP": {},
 		"2.2.2.2:20/UDP": {},
 	})
+
+	scopes := []uint8{loadbalancer.ScopeExternal, loadbalancer.ScopeInternal}
+	for _, scope := range scopes {
+		// Validate all frontends as exact matches
+		// These should match only for external scope
+		exact_match_ok := scope == loadbalancer.ScopeExternal
+		frontend := loadbalancer.NewL3n4Addr(loadbalancer.TCP, net.ParseIP("1.1.1.1"), 10, scope)
+		c.Assert(frontends.LooseMatch(*frontend), check.Equals, exact_match_ok)
+		frontend = loadbalancer.NewL3n4Addr(loadbalancer.TCP, net.ParseIP("1.1.1.1"), 20, scope)
+		c.Assert(frontends.LooseMatch(*frontend), check.Equals, exact_match_ok)
+		frontend = loadbalancer.NewL3n4Addr(loadbalancer.UDP, net.ParseIP("2.2.2.2"), 20, scope)
+		c.Assert(frontends.LooseMatch(*frontend), check.Equals, exact_match_ok)
+
+		// Validate protocol mismatch on exact match
+		frontend = loadbalancer.NewL3n4Addr(loadbalancer.TCP, net.ParseIP("2.2.2.2"), 20, scope)
+		c.Assert(frontends.LooseMatch(*frontend), check.Equals, false)
+
+		// Validate protocol wildcard matching
+		// These should match only for external scope
+		wild_match_ok := scope == loadbalancer.ScopeExternal
+		frontend = loadbalancer.NewL3n4Addr(loadbalancer.NONE, net.ParseIP("2.2.2.2"), 20, scope)
+		c.Assert(frontends.LooseMatch(*frontend), check.Equals, wild_match_ok)
+		frontend = loadbalancer.NewL3n4Addr(loadbalancer.NONE, net.ParseIP("1.1.1.1"), 10, scope)
+		c.Assert(frontends.LooseMatch(*frontend), check.Equals, wild_match_ok)
+		frontend = loadbalancer.NewL3n4Addr(loadbalancer.NONE, net.ParseIP("1.1.1.1"), 20, scope)
+		c.Assert(frontends.LooseMatch(*frontend), check.Equals, wild_match_ok)
+	}
 }
 
 func (s *K8sSuite) TestServiceCacheEndpoints(c *check.C) {
diff --git a/pkg/k8s/service_test.go b/pkg/k8s/service_test.go
index 0c59d9078c..6484c5aed9 100644
--- a/pkg/k8s/service_test.go
+++ b/pkg/k8s/service_test.go
@@ -180,6 +180,41 @@ func (s *K8sSuite) TestIsK8ServiceExternal(c *check.C) {
 	c.Assert(si.IsExternal(), check.Equals, false)
 }
 
+func (s *K8sSuite) TestServiceUniquePorts(c *check.C) {
+	type testMatrix struct {
+		input    Service
+		expected map[uint16]bool
+	}
+
+	matrix := []testMatrix{
+		{
+			input:    Service{},
+			expected: map[uint16]bool{},
+		},
+		{
+			input: Service{
+				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
+					loadbalancer.FEPortName("foo"): {
+						Protocol: loadbalancer.NONE,
+						Port:     1,
+					},
+					loadbalancer.FEPortName("bar"): {
+						Protocol: loadbalancer.NONE,
+						Port:     2,
+					},
+				},
+			},
+			expected: map[uint16]bool{
+				1: true,
+				2: true,
+			}},
+	}
+
+	for _, m := range matrix {
+		c.Assert(m.input.UniquePorts(), checker.DeepEquals, m.expected)
+	}
+}
+
 func TestService_Equals(t *testing.T) {
 	type args struct {
 		o *Service
@@ -197,7 +232,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.TCP,
+						Protocol: loadbalancer.NONE,
 						Port:     1,
 					},
 				},
@@ -206,7 +241,7 @@ func TestService_Equals(t *testing.T) {
 						"0.0.0.0:31000": {
 							L3n4Addr: loadbalancer.L3n4Addr{
 								L4Addr: loadbalancer.L4Addr{
-									Protocol: loadbalancer.TCP,
+									Protocol: loadbalancer.NONE,
 									Port:     31000,
 								},
 								IP: net.IPv4(0, 0, 0, 0),
@@ -229,7 +264,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.TCP,
+							Protocol: loadbalancer.NONE,
 							Port:     1,
 						},
 					},
@@ -238,7 +273,7 @@ func TestService_Equals(t *testing.T) {
 							"0.0.0.0:31000": {
 								L3n4Addr: loadbalancer.L3n4Addr{
 									L4Addr: loadbalancer.L4Addr{
-										Protocol: loadbalancer.TCP,
+										Protocol: loadbalancer.NONE,
 										Port:     31000,
 									},
 									IP: net.IPv4(0, 0, 0, 0),
@@ -265,7 +300,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.TCP,
+						Protocol: loadbalancer.NONE,
 						Port:     1,
 					},
 				},
@@ -280,7 +315,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.TCP,
+							Protocol: loadbalancer.NONE,
 							Port:     1,
 						},
 					},
@@ -301,7 +336,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.TCP,
+						Protocol: loadbalancer.NONE,
 						Port:     1,
 					},
 				},
@@ -314,7 +349,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.TCP,
+							Protocol: loadbalancer.NONE,
 							Port:     1,
 						},
 					},
@@ -333,7 +368,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foz"): {
-						Protocol: loadbalancer.TCP,
+						Protocol: loadbalancer.NONE,
 						Port:     1,
 					},
 				},
@@ -346,7 +381,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.TCP,
+							Protocol: loadbalancer.NONE,
 							Port:     1,
 						},
 					},
@@ -363,73 +398,13 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.TCP,
+						Protocol: loadbalancer.NONE,
 						Port:     1,
 					},
 				},
 				Labels:   map[string]string{},
 				Selector: map[string]string{},
 			},
-			args: args{
-				o: &Service{
-					FrontendIP: net.ParseIP("1.1.1.1"),
-					IsHeadless: true,
-					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
-						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.TCP,
-							Port:     2,
-						},
-					},
-					Labels:   map[string]string{},
-					Selector: map[string]string{},
-				},
-			},
-			want: false,
-		},
-		{
-			name: "protocols different content",
-			fields: &Service{
-				FrontendIP: net.ParseIP("1.1.1.1"),
-				IsHeadless: true,
-				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
-					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.TCP,
-						Port:     2,
-					},
-				},
-				Labels:   map[string]string{},
-				Selector: map[string]string{},
-			},
-			args: args{
-				o: &Service{
-					FrontendIP: net.ParseIP("1.1.1.1"),
-					IsHeadless: true,
-					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
-						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.UDP,
-							Port:     2,
-						},
-					},
-					Labels:   map[string]string{},
-					Selector: map[string]string{},
-				},
-			},
-			want: false,
-		},
-		{
-			name: "protocols different content one is none",
-			fields: &Service{
-				FrontendIP: net.ParseIP("1.1.1.1"),
-				IsHeadless: true,
-				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
-					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.TCP,
-						Port:     2,
-					},
-				},
-				Labels:   map[string]string{},
-				Selector: map[string]string{},
-			},
 			args: args{
 				o: &Service{
 					FrontendIP: net.ParseIP("1.1.1.1"),
@@ -453,7 +428,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.TCP,
+						Protocol: loadbalancer.NONE,
 						Port:     1,
 					},
 				},
@@ -466,11 +441,11 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.TCP,
+							Protocol: loadbalancer.NONE,
 							Port:     1,
 						},
 						loadbalancer.FEPortName("baz"): {
-							Protocol: loadbalancer.TCP,
+							Protocol: loadbalancer.NONE,
 							Port:     2,
 						},
 					},
@@ -494,7 +469,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.TCP,
+							Protocol: loadbalancer.NONE,
 							Port:     1,
 						},
 					},
@@ -511,7 +486,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.TCP,
+						Protocol: loadbalancer.NONE,
 						Port:     1,
 					},
 				},
@@ -520,7 +495,7 @@ func TestService_Equals(t *testing.T) {
 						"1.1.1.1:31000": {
 							L3n4Addr: loadbalancer.L3n4Addr{
 								L4Addr: loadbalancer.L4Addr{
-									Protocol: loadbalancer.TCP,
+									Protocol: loadbalancer.NONE,
 									Port:     31000,
 								},
 								IP: net.IPv4(1, 1, 1, 1),
@@ -543,7 +518,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.TCP,
+							Protocol: loadbalancer.NONE,
 							Port:     1,
 						},
 					},
@@ -552,7 +527,7 @@ func TestService_Equals(t *testing.T) {
 							"0.0.0.0:31000": {
 								L3n4Addr: loadbalancer.L3n4Addr{
 									L4Addr: loadbalancer.L4Addr{
-										Protocol: loadbalancer.TCP,
+										Protocol: loadbalancer.NONE,
 										Port:     31000,
 									},
 									IP: net.IPv4(0, 0, 0, 0),
@@ -579,7 +554,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: false,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.TCP,
+						Protocol: loadbalancer.NONE,
 						Port:     1,
 					},
 				},
@@ -597,7 +572,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: false,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.TCP,
+							Protocol: loadbalancer.NONE,
 							Port:     1,
 						},
 					},
diff --git a/pkg/loadbalancer/loadbalancer_test.go b/pkg/loadbalancer/loadbalancer_test.go
index 336e316bbf..64a5685e79 100644
--- a/pkg/loadbalancer/loadbalancer_test.go
+++ b/pkg/loadbalancer/loadbalancer_test.go
@@ -45,12 +45,12 @@ func TestL4Addr_Equals(t *testing.T) {
 		{
 			name: "both equal",
 			fields: &L4Addr{
-				Protocol: TCP,
+				Protocol: NONE,
 				Port:     1,
 			},
 			args: args{
 				o: &L4Addr{
-					Protocol: TCP,
+					Protocol: NONE,
 					Port:     1,
 				},
 			},
@@ -59,12 +59,12 @@ func TestL4Addr_Equals(t *testing.T) {
 		{
 			name: "both different",
 			fields: &L4Addr{
-				Protocol: TCP,
+				Protocol: NONE,
 				Port:     0,
 			},
 			args: args{
 				o: &L4Addr{
-					Protocol: TCP,
+					Protocol: NONE,
 					Port:     1,
 				},
 			},
@@ -78,40 +78,12 @@ func TestL4Addr_Equals(t *testing.T) {
 		{
 			name: "other nil",
 			fields: &L4Addr{
-				Protocol: TCP,
+				Protocol: NONE,
 				Port:     1,
 			},
 			args: args{},
 			want: false,
 		},
-		{
-			name: "protocol different",
-			fields: &L4Addr{
-				Protocol: TCP,
-				Port:     1,
-			},
-			args: args{
-				o: &L4Addr{
-					Protocol: UDP,
-					Port:     1,
-				},
-			},
-			want: false,
-		},
-		{
-			name: "protocol different one is none",
-			fields: &L4Addr{
-				Protocol: TCP,
-				Port:     1,
-			},
-			args: args{
-				o: &L4Addr{
-					Protocol: NONE,
-					Port:     1,
-				},
-			},
-			want: false,
-		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -138,7 +110,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 			fields: &L3n4AddrID{
 				L3n4Addr: L3n4Addr{
 					L4Addr: L4Addr{
-						Protocol: TCP,
+						Protocol: NONE,
 						Port:     1,
 					},
 					IP: net.IPv4(1, 1, 1, 1),
@@ -149,7 +121,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 				o: &L3n4AddrID{
 					L3n4Addr: L3n4Addr{
 						L4Addr: L4Addr{
-							Protocol: TCP,
+							Protocol: NONE,
 							Port:     1,
 						},
 						IP: net.IPv4(1, 1, 1, 1),
@@ -164,7 +136,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 			fields: &L3n4AddrID{
 				L3n4Addr: L3n4Addr{
 					L4Addr: L4Addr{
-						Protocol: TCP,
+						Protocol: NONE,
 						Port:     1,
 					},
 					IP: net.IPv4(1, 1, 1, 1),
@@ -175,7 +147,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 				o: &L3n4AddrID{
 					L3n4Addr: L3n4Addr{
 						L4Addr: L4Addr{
-							Protocol: TCP,
+							Protocol: NONE,
 							Port:     1,
 						},
 						IP: net.IPv4(1, 1, 1, 1),
@@ -190,7 +162,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 			fields: &L3n4AddrID{
 				L3n4Addr: L3n4Addr{
 					L4Addr: L4Addr{
-						Protocol: TCP,
+						Protocol: NONE,
 						Port:     1,
 					},
 					IP: net.IPv4(2, 2, 2, 2),
@@ -201,7 +173,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 				o: &L3n4AddrID{
 					L3n4Addr: L3n4Addr{
 						L4Addr: L4Addr{
-							Protocol: TCP,
+							Protocol: NONE,
 							Port:     1,
 						},
 						IP: net.IPv4(1, 1, 1, 1),
@@ -216,7 +188,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 			fields: &L3n4AddrID{
 				L3n4Addr: L3n4Addr{
 					L4Addr: L4Addr{
-						Protocol: TCP,
+						Protocol: NONE,
 						Port:     2,
 					},
 					IP: net.IPv4(1, 1, 1, 1),
@@ -227,7 +199,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 				o: &L3n4AddrID{
 					L3n4Addr: L3n4Addr{
 						L4Addr: L4Addr{
-							Protocol: TCP,
+							Protocol: NONE,
 							Port:     1,
 						},
 						IP: net.IPv4(1, 1, 1, 1),
@@ -237,32 +209,6 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 			},
 			want: false,
 		},
-		{
-			name: "protocols different",
-			fields: &L3n4AddrID{
-				L3n4Addr: L3n4Addr{
-					L4Addr: L4Addr{
-						Protocol: TCP,
-						Port:     2,
-					},
-					IP: net.IPv4(1, 1, 1, 1),
-				},
-				ID: 1,
-			},
-			args: args{
-				o: &L3n4AddrID{
-					L3n4Addr: L3n4Addr{
-						L4Addr: L4Addr{
-							Protocol: UDP,
-							Port:     2,
-						},
-						IP: net.IPv4(1, 1, 1, 1),
-					},
-					ID: 1,
-				},
-			},
-			want: false,
-		},
 		{
 			name: "both nil",
 			args: args{},
diff --git a/pkg/policy/portmap_test.go b/pkg/policy/portmap_test.go
index 662caa345a..d5091772a0 100644
--- a/pkg/policy/portmap_test.go
+++ b/pkg/policy/portmap_test.go
@@ -56,7 +56,7 @@ func (ds *PolicyTestSuite) TestPolicyNewPortProto(c *C) {
 
 	_, err = newPortProto(80, "cccp")
 	c.Assert(err, Not(IsNil))
-	c.Assert(err.Error(), Equals, "unknown protocol \"cccp\"")
+	c.Assert(err.Error(), Equals, "unknown protocol 'cccp'")
 
 	np, err = newPortProto(88, "")
 	c.Assert(err, IsNil)
diff --git a/pkg/redirectpolicy/manager_test.go b/pkg/redirectpolicy/manager_test.go
index 0b686e1876..684e45e826 100644
--- a/pkg/redirectpolicy/manager_test.go
+++ b/pkg/redirectpolicy/manager_test.go
@@ -99,11 +99,11 @@ func (ps *fakePodStore) Resync() error {
 }
 
 var (
-	tcpStr = "TCP"
-	udpStr = "UDP"
-	proto1 = lb.NewL4Type(tcpStr)
-	proto2 = lb.NewL4Type(udpStr)
-	fe1    = lb.NewL3n4Addr(
+	tcpStr    = "TCP"
+	udpStr    = "UDP"
+	proto1, _ = lb.NewL4Type(tcpStr)
+	proto2, _ = lb.NewL4Type(udpStr)
+	fe1       = lb.NewL3n4Addr(
 		proto1,
 		net.ParseIP("1.1.1.1"),
 		80,
diff --git a/pkg/service/id_test.go b/pkg/service/id_test.go
index 65cda1fca1..f6bc26d2d2 100644
--- a/pkg/service/id_test.go
+++ b/pkg/service/id_test.go
@@ -87,10 +87,11 @@ func (s *IDAllocTestSuite) TestServices(c *C) {
 	c.Assert(err, Equals, nil)
 	c.Assert(l3n4AddrID.ID, Equals, loadbalancer.ID(ffsIDu16+1))
 
-	// different protocols should change the id
+	// l3n4Addr3 should have the same ID as l3n4Addr2 since we are omitting the
+	// protocol type.
 	l3n4AddrID, err = AcquireID(l3n4Addr3, 0)
 	c.Assert(err, Equals, nil)
-	c.Assert(l3n4AddrID.ID, Equals, loadbalancer.ID(ffsIDu16+2))
+	c.Assert(l3n4AddrID.ID, Equals, loadbalancer.ID(ffsIDu16+1))
 
 	gotL3n4AddrID, err := GetID(FirstFreeServiceID)
 	c.Assert(err, Equals, nil)
diff --git a/pkg/service/service_test.go b/pkg/service/service_test.go
index e004d0770d..bce716536f 100644
--- a/pkg/service/service_test.go
+++ b/pkg/service/service_test.go
@@ -21,7 +21,6 @@ import (
 
 	"github.com/cilium/cilium/pkg/checker"
 	"github.com/cilium/cilium/pkg/cidr"
-	"github.com/cilium/cilium/pkg/loadbalancer"
 	lb "github.com/cilium/cilium/pkg/loadbalancer"
 	nodeTypes "github.com/cilium/cilium/pkg/node/types"
 	"github.com/cilium/cilium/pkg/option"
@@ -69,7 +68,6 @@ func (m *ManagerTestSuite) TearDownTest(c *C) {
 var (
 	frontend1 = *lb.NewL3n4AddrID(lb.TCP, net.ParseIP("1.1.1.1"), 80, lb.ScopeExternal, 0)
 	frontend2 = *lb.NewL3n4AddrID(lb.TCP, net.ParseIP("1.1.1.2"), 80, lb.ScopeExternal, 0)
-	frontend3 = *lb.NewL3n4AddrID(lb.UDP, net.ParseIP("1.1.1.2"), 80, lb.ScopeExternal, 0)
 	backends1 = []lb.Backend{
 		*lb.NewBackend(0, lb.TCP, net.ParseIP("10.0.0.1"), 8080),
 		*lb.NewBackend(0, lb.TCP, net.ParseIP("10.0.0.2"), 8080),
@@ -78,10 +76,6 @@ var (
 		*lb.NewBackend(0, lb.TCP, net.ParseIP("10.0.0.2"), 8080),
 		*lb.NewBackend(0, lb.TCP, net.ParseIP("10.0.0.3"), 8080),
 	}
-	backends3 = []lb.Backend{
-		*lb.NewBackend(0, lb.UDP, net.ParseIP("10.0.0.2"), 8080),
-		*lb.NewBackend(0, lb.UDP, net.ParseIP("10.0.0.3"), 8080),
-	}
 )
 
 func (m *ManagerTestSuite) TestUpsertAndDeleteService(c *C) {
@@ -176,50 +170,9 @@ func (m *ManagerTestSuite) TestUpsertAndDeleteService(c *C) {
 	c.Assert(len(m.lbmap.AffinityMatch[uint16(id2)]), Equals, 2)
 	c.Assert(len(m.lbmap.SourceRanges[uint16(id2)]), Equals, 2)
 
-	// Should add a similar service ot the others, different only by protocol;
-	// the other service(s) should not be corrupted/affected.
-	p3 := &lb.SVC{
-		Frontend:                  frontend3,
-		Backends:                  backends3,
-		Type:                      lb.SVCTypeLoadBalancer,
-		TrafficPolicy:             lb.SVCTrafficPolicyCluster,
-		SessionAffinity:           true,
-		SessionAffinityTimeoutSec: 300,
-		Name:                      "svc3",
-		Namespace:                 "ns2",
-		LoadBalancerSourceRanges:  []*cidr.CIDR{cidr1, cidr2},
-	}
-	created, id3, err := m.svc.UpsertService(p3)
-	c.Assert(err, IsNil)
-	c.Assert(created, Equals, true)
-	c.Assert(id3, Equals, lb.ID(3))
-	c.Assert(m.lbmap.ServiceByID[uint16(id3)].Frontend.Protocol, Equals, loadbalancer.UDP)
-	c.Assert(len(m.lbmap.ServiceByID[uint16(id3)].Backends), Equals, 2)
-	c.Assert(m.lbmap.ServiceByID[uint16(id3)].Backends[0].Protocol, Equals, loadbalancer.UDP)
-	c.Assert(m.lbmap.ServiceByID[uint16(id3)].Backends, Not(DeepEquals), m.lbmap.ServiceByID[uint16(id2)].Backends)
-	c.Assert(len(m.lbmap.BackendByID), Equals, 4)
-	c.Assert(m.svc.svcByID[id3].svcName, Equals, "svc3")
-	c.Assert(m.svc.svcByID[id3].svcNamespace, Equals, "ns2")
-	c.Assert(len(m.lbmap.AffinityMatch[uint16(id3)]), Equals, 2)
-	c.Assert(len(m.lbmap.SourceRanges[uint16(id3)]), Equals, 2)
-	// check that the similar service has not been overwritten
-	c.Assert(m.lbmap.ServiceByID[uint16(id2)].Frontend.Protocol, Equals, loadbalancer.TCP)
-	c.Assert(len(m.lbmap.ServiceByID[uint16(id2)].Backends), Equals, 2)
-	c.Assert(m.lbmap.ServiceByID[uint16(id2)].Backends[0].Protocol, Equals, loadbalancer.TCP)
-	c.Assert(m.svc.svcByID[id2].svcName, Equals, "svc2")
-	c.Assert(m.svc.svcByID[id2].svcNamespace, Equals, "ns2")
-	c.Assert(len(m.lbmap.AffinityMatch[uint16(id2)]), Equals, 2)
-	c.Assert(len(m.lbmap.SourceRanges[uint16(id2)]), Equals, 2)
-	// delete newest added service
-	found, err := m.svc.DeleteServiceByID(lb.ServiceID(id3))
-	c.Assert(err, IsNil)
-	c.Assert(found, Equals, true)
-	c.Assert(len(m.lbmap.ServiceByID), Equals, 2)
-	c.Assert(len(m.lbmap.BackendByID), Equals, 2)
-	c.Assert(len(m.lbmap.AffinityMatch[uint16(id3)]), Equals, 0)
 	// Should remove the service and the backend, but keep another service and
 	// its backends. Also, should remove the affinity match.
-	found, err = m.svc.DeleteServiceByID(lb.ServiceID(id1))
+	found, err := m.svc.DeleteServiceByID(lb.ServiceID(id1))
 	c.Assert(err, IsNil)
 	c.Assert(found, Equals, true)
 	c.Assert(len(m.lbmap.ServiceByID), Equals, 1)
diff --git a/pkg/service/store/store_test.go b/pkg/service/store/store_test.go
index bff6bb8db0..fdfb0ee470 100644
--- a/pkg/service/store/store_test.go
+++ b/pkg/service/store/store_test.go
@@ -61,45 +61,45 @@ func (s *ServiceGenericSuite) TestPortConfigurationDeepEqual(c *check.C) {
 
 		{
 			a: PortConfiguration{
-				"foo": {Protocol: loadbalancer.TCP, Port: 1},
+				"foo": {Protocol: loadbalancer.NONE, Port: 1},
 			},
 			b: PortConfiguration{
-				"foo": {Protocol: loadbalancer.TCP, Port: 1},
+				"foo": {Protocol: loadbalancer.NONE, Port: 1},
 			},
 			want: true,
 		},
 		{
 			a: PortConfiguration{
-				"foo": {Protocol: loadbalancer.TCP, Port: 1},
+				"foo": {Protocol: loadbalancer.NONE, Port: 1},
 			},
 			b: PortConfiguration{
-				"foz": {Protocol: loadbalancer.TCP, Port: 1},
+				"foz": {Protocol: loadbalancer.NONE, Port: 1},
 			},
 			want: false,
 		},
 		{
 			a: PortConfiguration{
-				"foo": {Protocol: loadbalancer.TCP, Port: 1},
+				"foo": {Protocol: loadbalancer.NONE, Port: 1},
 			},
 			b: PortConfiguration{
-				"foo": {Protocol: loadbalancer.TCP, Port: 2},
+				"foo": {Protocol: loadbalancer.NONE, Port: 2},
 			},
 			want: false,
 		},
 		{
 			a: PortConfiguration{
-				"foo": {Protocol: loadbalancer.TCP, Port: 1},
+				"foo": {Protocol: loadbalancer.NONE, Port: 1},
 			},
 			b: PortConfiguration{
-				"foo": {Protocol: loadbalancer.TCP, Port: 1},
-				"baz": {Protocol: loadbalancer.TCP, Port: 2},
+				"foo": {Protocol: loadbalancer.NONE, Port: 1},
+				"baz": {Protocol: loadbalancer.NONE, Port: 2},
 			},
 			want: false,
 		},
 		{
 			a: PortConfiguration{},
 			b: PortConfiguration{
-				"foo": {Protocol: loadbalancer.TCP, Port: 1},
+				"foo": {Protocol: loadbalancer.NONE, Port: 1},
 			},
 			want: false,
 		},
diff --git a/pkg/testutils/mockmaps/lbmap.go b/pkg/testutils/mockmaps/lbmap.go
index 84082f0d07..4eef66e1b5 100644
--- a/pkg/testutils/mockmaps/lbmap.go
+++ b/pkg/testutils/mockmaps/lbmap.go
@@ -19,7 +19,6 @@ import (
 	"net"
 
 	"github.com/cilium/cilium/pkg/cidr"
-	"github.com/cilium/cilium/pkg/loadbalancer"
 	lb "github.com/cilium/cilium/pkg/loadbalancer"
 	"github.com/cilium/cilium/pkg/maps/lbmap"
 )
@@ -52,7 +51,7 @@ func (m *LBMockMap) UpsertService(p *lbmap.UpsertServiceParams) error {
 
 	svc, found := m.ServiceByID[p.ID]
 	if !found {
-		frontend := lb.NewL3n4AddrID(loadbalancer.NewL4Type(p.Protocol), p.IP, p.Port, p.Scope, lb.ID(p.ID))
+		frontend := lb.NewL3n4AddrID(lb.NONE, p.IP, p.Port, p.Scope, lb.ID(p.ID))
 		svc = &lb.SVC{Frontend: *frontend}
 	} else {
 		if p.PrevBackendCount != len(svc.Backends) {
@@ -83,12 +82,12 @@ func (m *LBMockMap) DeleteService(addr lb.L3n4AddrID, backendCount int, maglev b
 	return nil
 }
 
-func (m *LBMockMap) AddBackend(id uint16, ip net.IP, protocol lb.L4Type, port uint16, ipv6 bool) error {
+func (m *LBMockMap) AddBackend(id uint16, ip net.IP, port uint16, ipv6 bool) error {
 	if _, found := m.BackendByID[id]; found {
 		return fmt.Errorf("Backend %d already exists", id)
 	}
 
-	m.BackendByID[id] = lb.NewBackend(lb.BackendID(id), protocol, ip, port)
+	m.BackendByID[id] = lb.NewBackend(lb.BackendID(id), lb.NONE, ip, port)
 
 	return nil
 }
diff --git a/test/helpers/cilium.go b/test/helpers/cilium.go
index 307fa748b2..a0aa05c114 100644
--- a/test/helpers/cilium.go
+++ b/test/helpers/cilium.go
@@ -799,12 +799,9 @@ func (s *SSHMeta) ServiceIsSynced(id int) (bool, error) {
 		return false, err
 	}
 
-	frontendAddr := serviceAddressKey(
+	frontendAddr := net.JoinHostPort(
 		svc.Status.Realized.FrontendAddress.IP,
-		fmt.Sprintf("%d", svc.Status.Realized.FrontendAddress.Port),
-		svc.Status.Realized.FrontendAddress.Protocol,
-		"",
-	)
+		fmt.Sprintf("%d", svc.Status.Realized.FrontendAddress.Port))
 	lb, ok := bpfLB[frontendAddr]
 	if ok == false {
 		return false, fmt.Errorf(
diff --git a/test/helpers/cons.go b/test/helpers/cons.go
index fd1663c436..d10911de56 100644
--- a/test/helpers/cons.go
+++ b/test/helpers/cons.go
@@ -22,7 +22,7 @@ import (
 
 	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
 	"github.com/cilium/cilium/pkg/versioncheck"
-	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"
+	"github.com/cilium/cilium/test/ginkgo-ext"
 )
 
 var (
diff --git a/test/helpers/kubectl.go b/test/helpers/kubectl.go
index 27ab84b440..b661c8ddf8 100644
--- a/test/helpers/kubectl.go
+++ b/test/helpers/kubectl.go
@@ -1727,7 +1727,7 @@ func (kub *Kubectl) validateServicePlumbingInCiliumPod(fullName, ciliumPod strin
 	for _, port := range serviceObj.Spec.Ports {
 		var foundPort *v1.ServicePort
 		for _, realizedService := range realizedServices {
-			if compareServicePortToFrontEnd(&port, realizedService.FrontendAddress) {
+			if port.Port == int32(realizedService.FrontendAddress.Port) {
 				foundPort = &port
 				break
 			}
@@ -1736,8 +1736,8 @@ func (kub *Kubectl) validateServicePlumbingInCiliumPod(fullName, ciliumPod strin
 			return fmt.Errorf("port %d of service %s (%s) not found in cilium pod %s",
 				port.Port, fullName, serviceObj.Spec.ClusterIP, ciliumPod)
 		}
-		lKey := serviceAddressKey(serviceObj.Spec.ClusterIP, fmt.Sprintf("%d", port.Port), string(port.Protocol), "")
-		if _, ok := lbMap[lKey]; !ok {
+
+		if _, ok := lbMap[net.JoinHostPort(serviceObj.Spec.ClusterIP, fmt.Sprintf("%d", port.Port))]; !ok {
 			return fmt.Errorf("port %d of service %s (%s) not found in cilium bpf lb list of pod %s",
 				port.Port, fullName, serviceObj.Spec.ClusterIP, ciliumPod)
 		}
@@ -1749,11 +1749,10 @@ func (kub *Kubectl) validateServicePlumbingInCiliumPod(fullName, ciliumPod strin
 				foundBackend, foundBackendLB := false, false
 				for _, realizedService := range realizedServices {
 					frontEnd := realizedService.FrontendAddress
-					lbKey := serviceAddressKey(frontEnd.IP, fmt.Sprintf("%d", frontEnd.Port), string(frontEnd.Protocol), "")
-					lb := lbMap[lbKey]
+					lb := lbMap[net.JoinHostPort(frontEnd.IP, fmt.Sprintf("%d", frontEnd.Port))]
+
 					for _, backAddr := range realizedService.BackendAddresses {
-						if addr.IP == *backAddr.IP && uint16(port.Port) == backAddr.Port &&
-							compareProto(string(port.Protocol), backAddr.Protocol) {
+						if addr.IP == *backAddr.IP && uint16(port.Port) == backAddr.Port {
 							foundBackend = true
 							for _, backend := range lb {
 								if strings.Contains(backend, net.JoinHostPort(*backAddr.IP, fmt.Sprintf("%d", port.Port))) {
@@ -3851,7 +3850,7 @@ CILIUM_SERVICES:
 	for _, cSvc := range ciliumSvcs {
 		if cSvc.Status.Realized.FrontendAddress.IP == k8sService.Spec.ClusterIP {
 			for _, port := range k8sService.Spec.Ports {
-				if compareServicePortToFrontEnd(&port, cSvc.Status.Realized.FrontendAddress) {
+				if int32(cSvc.Status.Realized.FrontendAddress.Port) == port.Port {
 					ciliumService = &cSvc
 					break CILIUM_SERVICES
 				}
@@ -3872,11 +3871,7 @@ CILIUM_SERVICES:
 }
 
 // CiliumServiceAdd adds the given service on a 'pod' running Cilium
-func (kub *Kubectl) CiliumServiceAdd(pod string, id int64, protocol string, frontend string, backends []string, svcType, trafficPolicy string) error {
-	protocol = strings.ToLower(protocol)
-	if protocol == "" || protocol == "any" || protocol == "none" {
-		protocol = "tcp"
-	}
+func (kub *Kubectl) CiliumServiceAdd(pod string, id int64, frontend string, backends []string, svcType, trafficPolicy string) error {
 	var opts []string
 	switch strings.ToLower(svcType) {
 	case "nodeport":
@@ -3903,8 +3898,8 @@ func (kub *Kubectl) CiliumServiceAdd(pod string, id int64, protocol string, fron
 	backendsStr := strings.Join(backends, ",")
 	ctx, cancel := context.WithTimeout(context.Background(), ShortCommandTimeout)
 	defer cancel()
-	return kub.CiliumExecContext(ctx, pod, fmt.Sprintf("cilium service update --id %d --protocol %q --frontend %q --backends %q %s",
-		id, protocol, frontend, backendsStr, optsStr)).GetErr("cilium service update")
+	return kub.CiliumExecContext(ctx, pod, fmt.Sprintf("cilium service update --id %d --frontend %q --backends %q %s",
+		id, frontend, backendsStr, optsStr)).GetErr("cilium service update")
 }
 
 // CiliumServiceDel deletes the service with 'id' on a 'pod' running Cilium
@@ -4128,7 +4123,7 @@ func validateCiliumSvc(cSvc models.Service, k8sSvcs []v1.Service, k8sEps []v1.En
 
 	var k8sServicePort *v1.ServicePort
 	for _, k8sPort := range k8sService.Spec.Ports {
-		if compareServicePortToFrontEnd(&k8sPort, cSvc.Status.Realized.FrontendAddress) {
+		if k8sPort.Port == int32(cSvc.Status.Realized.FrontendAddress.Port) {
 			k8sServicePort = &k8sPort
 			k8sServicesFound[serviceKey(*k8sService)] = true
 			break
@@ -4157,16 +4152,14 @@ func validateCiliumSvc(cSvc models.Service, k8sSvcs []v1.Service, k8sEps []v1.En
 }
 
 func validateCiliumSvcLB(cSvc models.Service, lbMap map[string][]string) error {
-	var scope string
+	scope := ""
 	if cSvc.Status.Realized.FrontendAddress.Scope == models.FrontendAddressScopeInternal {
 		scope = "/i"
 	}
-	frontendAddress := serviceAddressKey(
+
+	frontendAddress := net.JoinHostPort(
 		cSvc.Status.Realized.FrontendAddress.IP,
-		strconv.Itoa(int(cSvc.Status.Realized.FrontendAddress.Port)),
-		cSvc.Status.Realized.FrontendAddress.Protocol,
-		scope,
-	)
+		strconv.Itoa(int(cSvc.Status.Realized.FrontendAddress.Port))) + scope
 	bpfBackends, ok := lbMap[frontendAddress]
 	if !ok {
 		return fmt.Errorf("%s bpf lb map entry not found", frontendAddress)
@@ -4203,9 +4196,7 @@ func getK8sEndpointAddresses(ep v1.Endpoints) []*models.BackendAddress {
 }
 
 func addrsEqual(addr1, addr2 *models.BackendAddress) bool {
-	return *addr1.IP == *addr2.IP &&
-		addr1.Port == addr2.Port &&
-		compareProto(addr1.Protocol, addr2.Protocol)
+	return *addr1.IP == *addr2.IP && addr1.Port == addr2.Port
 }
 
 // GenerateNamespaceForTest generates a namespace based off of the current test
@@ -4338,36 +4329,3 @@ func (kub *Kubectl) CleanupCiliumComponents() {
 	}
 	wg.Wait()
 }
-
-func compareProto(proto1, proto2 string) bool {
-	proto1 = strings.ToLower(proto1)
-	if proto1 == "" || proto1 == "none" || proto1 == "any" {
-		return true
-	}
-	proto2 = strings.ToLower(proto2)
-	if proto2 == "" || proto2 == "none" || proto2 == "any" {
-		return true
-	}
-	return proto1 == proto2
-}
-
-func compareServicePortToFrontEnd(sP *v1.ServicePort, fA *models.FrontendAddress) bool {
-	return sP.Port == int32(fA.Port) && compareProto(string(sP.Protocol), fA.Protocol)
-}
-
-func compareBackendToFrontEnd(bA *models.BackendAddress, fA *models.FrontendAddress) bool {
-	return bA.Port == fA.Port && compareProto(bA.Protocol, fA.Protocol)
-}
-
-func serviceAddressKey(ip, port, proto, scope string) string {
-	newOutputStyle := HasNewServiceOutput(GetRunningCiliumVersion())
-	k := net.JoinHostPort(ip, port)
-	if newOutputStyle {
-		p := strings.ToLower(proto)
-		if p == "" || p == "none" || p == "any" {
-			proto = "NONE"
-		}
-		return fmt.Sprintf("%s/%s%s", k, proto, scope)
-	}
-	return fmt.Sprintf("%s%s", k, scope)
-}
diff --git a/test/helpers/utils.go b/test/helpers/utils.go
index 7d7ec4aee3..eb5c24337f 100644
--- a/test/helpers/utils.go
+++ b/test/helpers/utils.go
@@ -39,11 +39,8 @@ import (
 	"golang.org/x/sys/unix"
 )
 
-var (
-	// ensure that our random numbers are seeded differently on each run
-	randGen              = rand.NewSafeRand(time.Now().UnixNano())
-	runningCiliumVersion string
-)
+// ensure that our random numbers are seeded differently on each run
+var randGen = rand.NewSafeRand(time.Now().UnixNano())
 
 // IsRunningOnJenkins detects if the currently running Ginkgo application is
 // most likely running in a Jenkins environment. Returns true if certain
@@ -63,35 +60,6 @@ func IsRunningOnJenkins() bool {
 	return result
 }
 
-// SetCiliumVersion sets the currently running cilium version
-// and returns a function that unsets it.
-func SetRunningCiliumVersion(v string) func() {
-	runningCiliumVersion = v
-	return func() {
-		runningCiliumVersion = ""
-	}
-}
-
-// GetRunningCiliumVersion gets the currently running cilium version.
-func GetRunningCiliumVersion() string {
-	return runningCiliumVersion
-}
-
-// HasNewServiceOutput checks to see if the current running cilium
-// version uses the old style service output (e.g. "0.0.0.0:53") vs
-// the new style (e.g. "0.0.0.0:53/TCP").
-func HasNewServiceOutput(ver string) bool {
-	cst, err := versioncheck.Version(ver)
-	// If the version is not parseable it is probably
-	// someone's custom build  or not set.
-	// Either way, it is probably using the new output
-	// format.
-	if err != nil {
-		return true
-	}
-	return versioncheck.MustCompile(">=1.9.0")(cst)
-}
-
 // Sleep sleeps for the specified duration in seconds
 func Sleep(delay time.Duration) {
 	time.Sleep(delay * time.Second)
@@ -250,7 +218,6 @@ func GetAppPods(apps []string, namespace string, kubectl *Kubectl, appFmt string
 		res, err := kubectl.GetPodNames(namespace, fmt.Sprintf("%s=%s", appFmt, v))
 		Expect(err).Should(BeNil())
 		Expect(res).Should(Not(BeNil()))
-		Expect(len(res)).To(BeNumerically(">", 0))
 		appPods[v] = res[0]
 		log.Infof("GetAppPods: pod=%q assigned to %q", res[0], v)
 	}
diff --git a/test/k8sT/Services.go b/test/k8sT/Services.go
index ddb6584248..98ad51005a 100644
--- a/test/k8sT/Services.go
+++ b/test/k8sT/Services.go
@@ -120,20 +120,20 @@ var _ = Describe("K8sServicesTest", func() {
 		return backends
 	}
 
-	ciliumAddService := func(id int64, protocol string, frontend string, backends []string, svcType, trafficPolicy string) {
+	ciliumAddService := func(id int64, frontend string, backends []string, svcType, trafficPolicy string) {
 		ciliumPods, err := kubectl.GetCiliumPods()
 		ExpectWithOffset(1, err).To(BeNil(), "Cannot get cilium pods")
 		for _, pod := range ciliumPods {
-			err := kubectl.CiliumServiceAdd(pod, id, protocol, frontend, backends, svcType, trafficPolicy)
+			err := kubectl.CiliumServiceAdd(pod, id, frontend, backends, svcType, trafficPolicy)
 			ExpectWithOffset(1, err).To(BeNil(), "Failed to add cilium service")
 		}
 	}
 
-	ciliumAddServiceOnNode := func(node string, id int64, protocol string, frontend string, backends []string, svcType, trafficPolicy string) {
+	ciliumAddServiceOnNode := func(node string, id int64, frontend string, backends []string, svcType, trafficPolicy string) {
 		ciliumPod, err := kubectl.GetCiliumPodOnNode(node)
 		ExpectWithOffset(1, err).To(BeNil(), fmt.Sprintf("Cannot get cilium pod on node %s", node))
 
-		err = kubectl.CiliumServiceAdd(ciliumPod, id, protocol, frontend, backends, svcType, trafficPolicy)
+		err = kubectl.CiliumServiceAdd(ciliumPod, id, frontend, backends, svcType, trafficPolicy)
 		ExpectWithOffset(1, err).To(BeNil(), fmt.Sprintf("Failed to add cilium service on node %s", node))
 	}
 
@@ -324,14 +324,14 @@ var _ = Describe("K8sServicesTest", func() {
 			BeforeAll(func() {
 				// Installs the IPv6 equivalent of app1-service (demo.yaml)
 				httpBackends := ciliumIPv6Backends("-l k8s:id=app1,k8s:io.kubernetes.pod.namespace=default", "80")
-				ciliumAddService(10080, "tcp", net.JoinHostPort(demoClusterIPv6, "80"), httpBackends, "ClusterIP", "Cluster")
+				ciliumAddService(10080, net.JoinHostPort(demoClusterIPv6, "80"), httpBackends, "ClusterIP", "Cluster")
 				tftpBackends := ciliumIPv6Backends("-l k8s:id=app1,k8s:io.kubernetes.pod.namespace=default", "69")
-				ciliumAddService(10069, "udp", net.JoinHostPort(demoClusterIPv6, "69"), tftpBackends, "ClusterIP", "Cluster")
+				ciliumAddService(10069, net.JoinHostPort(demoClusterIPv6, "69"), tftpBackends, "ClusterIP", "Cluster")
 				// Installs the IPv6 equivalent of echo (echo-svc.yaml)
 				httpBackends = ciliumIPv6Backends("-l k8s:name=echo,k8s:io.kubernetes.pod.namespace=default", "80")
-				ciliumAddService(20080, "tcp", net.JoinHostPort(echoClusterIPv6, "80"), httpBackends, "ClusterIP", "Cluster")
+				ciliumAddService(20080, net.JoinHostPort(echoClusterIPv6, "80"), httpBackends, "ClusterIP", "Cluster")
 				tftpBackends = ciliumIPv6Backends("-l k8s:name=echo,k8s:io.kubernetes.pod.namespace=default", "69")
-				ciliumAddService(20069, "udp", net.JoinHostPort(echoClusterIPv6, "69"), tftpBackends, "ClusterIP", "Cluster")
+				ciliumAddService(20069, net.JoinHostPort(echoClusterIPv6, "69"), tftpBackends, "ClusterIP", "Cluster")
 			})
 
 			AfterAll(func() {
@@ -347,7 +347,7 @@ var _ = Describe("K8sServicesTest", func() {
 				status.ExpectSuccess("cannot curl to service IP from host")
 
 				status = kubectl.ExecInHostNetNS(context.TODO(), k8s1NodeName,
-					helpers.CurlFail(`"tftp://[%s]/hell"`, demoClusterIPv6))
+					helpers.CurlFail(`"tftp://[%s]/hello"`, demoClusterIPv6))
 				status.ExpectSuccess("cannot curl to service IP from host")
 			})
 
@@ -610,9 +610,9 @@ var _ = Describe("K8sServicesTest", func() {
 			BeforeAll(func() {
 				// Install rules for testds-service (demo_ds.yaml)
 				httpBackends := ciliumIPv6Backends("-l k8s:zgroup=testDS,k8s:io.kubernetes.pod.namespace=default", "80")
-				ciliumAddService(31080, "tcp", net.JoinHostPort(testDSIPv6, "80"), httpBackends, "ClusterIP", "Cluster")
+				ciliumAddService(31080, net.JoinHostPort(testDSIPv6, "80"), httpBackends, "ClusterIP", "Cluster")
 				tftpBackends := ciliumIPv6Backends("-l k8s:zgroup=testDS,k8s:io.kubernetes.pod.namespace=default", "69")
-				ciliumAddService(31069, "udp", net.JoinHostPort(testDSIPv6, "69"), tftpBackends, "ClusterIP", "Cluster")
+				ciliumAddService(31069, net.JoinHostPort(testDSIPv6, "69"), tftpBackends, "ClusterIP", "Cluster")
 			})
 
 			AfterAll(func() {
@@ -1515,21 +1515,21 @@ var _ = Describe("K8sServicesTest", func() {
 
 				// Install rules for testds-service NodePort Service(demo_ds.yaml)
 				httpBackends := ciliumIPv6Backends("-l k8s:zgroup=testDS,k8s:io.kubernetes.pod.namespace=default", "80")
-				ciliumAddService(31080, string(data.Spec.Ports[0].Protocol), net.JoinHostPort(testDSIPv6, fmt.Sprintf("%d", data.Spec.Ports[0].NodePort)), httpBackends, "NodePort", "Cluster")
-				ciliumAddService(31081, string(data.Spec.Ports[0].Protocol), net.JoinHostPort("::", fmt.Sprintf("%d", data.Spec.Ports[0].NodePort)), httpBackends, "NodePort", "Cluster")
+				ciliumAddService(31080, net.JoinHostPort(testDSIPv6, fmt.Sprintf("%d", data.Spec.Ports[0].NodePort)), httpBackends, "NodePort", "Cluster")
+				ciliumAddService(31081, net.JoinHostPort("::", fmt.Sprintf("%d", data.Spec.Ports[0].NodePort)), httpBackends, "NodePort", "Cluster")
 				// Add service corresponding to IPv6 address of the nodes so that they become
 				// reachable from outside the cluster.
-				ciliumAddServiceOnNode(helpers.K8s1, 31082, string(data.Spec.Ports[0].Protocol), net.JoinHostPort(k8s1IPv6, fmt.Sprintf("%d", data.Spec.Ports[0].NodePort)),
+				ciliumAddServiceOnNode(helpers.K8s1, 31082, net.JoinHostPort(k8s1IPv6, fmt.Sprintf("%d", data.Spec.Ports[0].NodePort)),
 					httpBackends, "NodePort", "Cluster")
-				ciliumAddServiceOnNode(helpers.K8s2, 31082, string(data.Spec.Ports[0].Protocol), net.JoinHostPort(k8s2IPv6, fmt.Sprintf("%d", data.Spec.Ports[0].NodePort)),
+				ciliumAddServiceOnNode(helpers.K8s2, 31082, net.JoinHostPort(k8s2IPv6, fmt.Sprintf("%d", data.Spec.Ports[0].NodePort)),
 					httpBackends, "NodePort", "Cluster")
 
 				tftpBackends := ciliumIPv6Backends("-l k8s:zgroup=testDS,k8s:io.kubernetes.pod.namespace=default", "69")
-				ciliumAddService(31069, string(data.Spec.Ports[1].Protocol), net.JoinHostPort(testDSIPv6, fmt.Sprintf("%d", data.Spec.Ports[1].NodePort)), tftpBackends, "NodePort", "Cluster")
-				ciliumAddService(31070, string(data.Spec.Ports[1].Protocol), net.JoinHostPort("::", fmt.Sprintf("%d", data.Spec.Ports[1].NodePort)), tftpBackends, "NodePort", "Cluster")
-				ciliumAddServiceOnNode(helpers.K8s1, 31071, string(data.Spec.Ports[1].Protocol), net.JoinHostPort(k8s1IPv6, fmt.Sprintf("%d", data.Spec.Ports[1].NodePort)),
+				ciliumAddService(31069, net.JoinHostPort(testDSIPv6, fmt.Sprintf("%d", data.Spec.Ports[1].NodePort)), tftpBackends, "NodePort", "Cluster")
+				ciliumAddService(31070, net.JoinHostPort("::", fmt.Sprintf("%d", data.Spec.Ports[1].NodePort)), tftpBackends, "NodePort", "Cluster")
+				ciliumAddServiceOnNode(helpers.K8s1, 31071, net.JoinHostPort(k8s1IPv6, fmt.Sprintf("%d", data.Spec.Ports[1].NodePort)),
 					tftpBackends, "NodePort", "Cluster")
-				ciliumAddServiceOnNode(helpers.K8s2, 31071, string(data.Spec.Ports[1].Protocol), net.JoinHostPort(k8s2IPv6, fmt.Sprintf("%d", data.Spec.Ports[1].NodePort)),
+				ciliumAddServiceOnNode(helpers.K8s2, 31071, net.JoinHostPort(k8s2IPv6, fmt.Sprintf("%d", data.Spec.Ports[1].NodePort)),
 					tftpBackends, "NodePort", "Cluster")
 			})
 
diff --git a/test/k8sT/Updates.go b/test/k8sT/Updates.go
index edeaa2bc06..a588a28d05 100644
--- a/test/k8sT/Updates.go
+++ b/test/k8sT/Updates.go
@@ -143,8 +143,6 @@ func InstallAndValidateCiliumUpgrades(kubectl *helpers.Kubectl, oldHelmChartVers
 	var (
 		privateIface string // only used when running w/o kube-proxy
 		err          error
-		apps         = []string{helpers.App1, helpers.App2, helpers.App3}
-		app1Service  = "app1-service"
 	)
 
 	canRun, err := helpers.CanRunK8sVersion(oldImageVersion, helpers.GetCurrentK8SEnv())
@@ -163,6 +161,9 @@ func InstallAndValidateCiliumUpgrades(kubectl *helpers.Kubectl, oldHelmChartVers
 		ExpectWithOffset(1, err).To(BeNil(), "Unable to determine private iface")
 	}
 
+	apps := []string{helpers.App1, helpers.App2, helpers.App3}
+	app1Service := "app1-service"
+
 	cleanupCiliumState := func(helmPath, chartVersion, imageName, imageTag, registry string) {
 		removeCilium(kubectl)
 
@@ -290,8 +291,6 @@ func InstallAndValidateCiliumUpgrades(kubectl *helpers.Kubectl, oldHelmChartVers
 				helpers.CiliumNamespace,
 				opts)
 		}, time.Second*30, time.Second*1).Should(helpers.CMDSuccess(), fmt.Sprintf("Cilium %q was not able to be deployed", oldHelmChartVersion))
-		unsetFn := helpers.SetRunningCiliumVersion(oldImageVersion)
-		defer unsetFn()
 
 		// Cilium is only ready if kvstore is ready, the kvstore is ready if
 		// kube-dns is running.
@@ -371,7 +370,7 @@ func InstallAndValidateCiliumUpgrades(kubectl *helpers.Kubectl, oldHelmChartVers
 			if lastCount == -1 {
 				lastCount = currentCount
 			}
-			ExpectWithOffset(1, lastCount).Should(BeIdenticalTo(currentCount),
+			Expect(lastCount).Should(BeIdenticalTo(currentCount),
 				"migrate-svc restart count values do not match")
 		}
 
@@ -381,23 +380,23 @@ func InstallAndValidateCiliumUpgrades(kubectl *helpers.Kubectl, oldHelmChartVers
 		ExpectWithOffset(1, res).To(helpers.CMDSuccess(), "cannot apply dempo application")
 
 		err = kubectl.WaitforPods(helpers.DefaultNamespace, "-l zgroup=testapp", timeout)
-		ExpectWithOffset(1, err).Should(BeNil(), "Test pods are not ready after timeout")
+		Expect(err).Should(BeNil(), "Test pods are not ready after timeout")
 
 		_, err = kubectl.CiliumPolicyAction(
 			helpers.DefaultNamespace, l7Policy, helpers.KubectlApply, timeout)
-		ExpectWithOffset(1, err).Should(BeNil(), "cannot import l7 policy: %v", l7Policy)
+		Expect(err).Should(BeNil(), "cannot import l7 policy: %v", l7Policy)
 
 		By("Creating service and clients for migration")
 
 		res = kubectl.ApplyDefault(migrateSVCServer)
 		ExpectWithOffset(1, res).To(helpers.CMDSuccess(), "cannot apply migrate-svc-server")
 		err = kubectl.WaitforPods(helpers.DefaultNamespace, "-l app=migrate-svc-server", timeout)
-		ExpectWithOffset(1, err).Should(BeNil(), "migrate-svc-server pods are not ready after timeout")
+		Expect(err).Should(BeNil(), "migrate-svc-server pods are not ready after timeout")
 
 		res = kubectl.ApplyDefault(migrateSVCClient)
 		ExpectWithOffset(1, res).To(helpers.CMDSuccess(), "cannot apply migrate-svc-client")
 		err = kubectl.WaitforPods(helpers.DefaultNamespace, "-l app=migrate-svc-client", timeout)
-		ExpectWithOffset(1, err).Should(BeNil(), "migrate-svc-client pods are not ready after timeout")
+		Expect(err).Should(BeNil(), "migrate-svc-client pods are not ready after timeout")
 
 		validateEndpointsConnection()
 		checkNoInteruptsInSVCFlows()
@@ -502,7 +501,6 @@ func InstallAndValidateCiliumUpgrades(kubectl *helpers.Kubectl, oldHelmChartVers
 				helpers.CiliumNamespace,
 				opts)
 		}, time.Second*30, time.Second*1).Should(helpers.CMDSuccess(), fmt.Sprintf("Cilium %q was not able to be deployed", newHelmChartVersion))
-		helpers.SetRunningCiliumVersion(newImageVersion)
 
 		By("Validating pods have the right image version upgraded")
 		err = helpers.WithTimeout(
@@ -537,7 +535,6 @@ func InstallAndValidateCiliumUpgrades(kubectl *helpers.Kubectl, oldHelmChartVers
 			"Cilium Pods are not updating correctly",
 			&helpers.TimeoutConfig{Timeout: timeout})
 		ExpectWithOffset(1, err).To(BeNil(), "Pods are not updating")
-		helpers.SetRunningCiliumVersion(oldImageVersion)
 
 		err = kubectl.WaitforPods(
 			helpers.CiliumNamespace, "-l k8s-app=cilium", timeout)