Repository: cilium/cilium
Pull request #13484 (2020-10-09 12:35:34)

hubble/relay: print better error message on peer conn dial failure
To connect to peers via gRPC, Hubble Relay uses `grpc.DialContext()`
with a dial timeout. When a connection attempt to a peer fails, the
following error message is returned: `context deadline exceeded`.
This provides no value when trying to debug the issue. Is the peer
unreachable? Is this a TLS configuration issue? etc.

By using the dial option `grpc.FailOnNonTempDialError(true)`, we get
better error messages on non-temporary dial errors, such as:

    connection error: desc = "transport: error while dialing: dial tcp 172.18.0.4:4244: connect: connection refused"

Using the option `grpc.WithReturnConnectionError()`, we would get a
message that contains both the last connection error that occurred and
the `context.DeadlineExceeded` error. This greatly improves the
situation for errors that are deemed transient. Example:

    context deadline exceeded: connection error: desc = "transport: authentication handshake failed: x509: certificate is valid for *.hubble-relay.cilium.io, not localhost"

However, this is only available starting with grpc-go v1.30.0 and due to
the issue described [here](https://github.com/cilium/cilium/pull/13405#issuecomment-704766707), we are currently stuck with v1.29.1. Thus,
this commit adds the option but comments it with a TODO note so that it
can be caught up and uncommented when upgrading grpc-go becomes
possible.

```release-note
Hubble Relay: improve error log message on peer connection failure.
```


Rolinh: test-me-please
Rolinh: test-gke
Rolinh: test-gke
Rolinh: retest-4.9

Patch:
diff --git a/Documentation/cmdref/cilium_service_update.md b/Documentation/cmdref/cilium_service_update.md
index e76c3832c6..849df74b6f 100644
--- a/Documentation/cmdref/cilium_service_update.md
+++ b/Documentation/cmdref/cilium_service_update.md
@@ -26,6 +26,7 @@ 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 057a3ba82a..81ac219e98 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      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       
+    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
 
 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 4675a9767f..d305c02d8b 100644
--- a/api/v1/models/backend_address.go
+++ b/api/v1/models/backend_address.go
@@ -29,6 +29,9 @@ 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 f9c552725d..c045df68b8 100644
--- a/api/v1/openapi.yaml
+++ b/api/v1/openapi.yaml
@@ -2250,6 +2250,9 @@ 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 375d4011fb..1d8b94e1a1 100644
--- a/api/v1/server/embedded_spec.go
+++ b/api/v1/server/embedded_spec.go
@@ -1429,6 +1429,10 @@ func init() {
           "description": "Layer 4 port number",
           "type": "integer",
           "format": "uint16"
+        },
+        "protocol": {
+          "description": "Layer 4 protocol (TCP, UDP, etc)",
+          "type": "string"
         }
       }
     },
@@ -5147,6 +5151,10 @@ 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 afa690534a..2a746c53f3 100644
--- a/bpf/bpf_sock.c
+++ b/bpf/bpf_sock.c
@@ -272,15 +272,17 @@ static __always_inline int __sock4_xlate_fwd(struct bpf_sock_addr *ctx,
 	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(ctx->protocol))
+	if (!udp_only && !sock_proto_enabled(proto))
 		return -ENOTSUP;
 
 	/* In case a direct match fails, we try to look-up surrogate
@@ -372,12 +374,14 @@ 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(ctx->protocol) ||
+	if (!sock_proto_enabled(proto) ||
 	    !ctx_in_hostns(ctx_full, NULL))
 		return 0;
 
@@ -428,6 +432,7 @@ 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);
@@ -672,11 +677,13 @@ 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(ctx->protocol) ||
+	if (!sock_proto_enabled(proto) ||
 	    !ctx_in_hostns(ctx, NULL))
 		return 0;
 
@@ -715,14 +722,16 @@ 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(ctx->protocol))
+	if (!udp_only && !sock_proto_enabled(proto))
 		return -ENOTSUP;
 
 	ctx_get_v6_address(ctx, &key.address);
@@ -839,6 +848,7 @@ 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/lib/common.h b/bpf/lib/common.h
index ae2edcae89..60a430ed4b 100644
--- a/bpf/lib/common.h
+++ b/bpf/lib/common.h
@@ -632,7 +632,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, currently not used (set to 0) */
+	__u8 proto;		/* L4 protocol */
 	__u8 scope;		/* LB_LOOKUP_SCOPE_* for externalTrafficPolicy=Local */
 	__u8 pad[2];
 };
@@ -680,7 +680,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, currently not used (set to 0) */
+	__u8 proto;		/* L4 protocol */
 	__u8 scope;		/* LB_LOOKUP_SCOPE_* for externalTrafficPolicy=Local */
 	__u8 pad[2];
 };
diff --git a/bpf/lib/lb.h b/bpf/lib/lb.h
index 7b0d0db61b..dd499105cd 100644
--- a/bpf/lib/lb.h
+++ b/bpf/lib/lb.h
@@ -500,8 +500,7 @@ static __always_inline int lb6_extract_key(struct __ctx_buff *ctx __maybe_unused
 					   int dir)
 {
 	union v6addr *addr;
-	/* FIXME(brb): set after adding support for different L4 protocols in LB */
-	key->proto = 0;
+	key->proto = tuple->nexthdr;
 	addr = (dir == CT_INGRESS) ? &tuple->saddr : &tuple->daddr;
 	ipv6_addr_copy(&key->address, addr);
 	csum_l4_offset_and_flags(tuple->nexthdr, csum_off);
@@ -1020,8 +1019,7 @@ static __always_inline int lb4_extract_key(struct __ctx_buff *ctx __maybe_unused
 					   struct csum_offset *csum_off,
 					   int dir)
 {
-	/* FIXME: set after adding support for different L4 protocols in LB */
-	key->proto = 0;
+	key->proto = ip4->protocol;
 	key->address = (dir == CT_INGRESS) ? ip4->saddr : ip4->daddr;
 	csum_l4_offset_and_flags(ip4->protocol, csum_off);
 
diff --git a/bpf/sockops/bpf_redir.c b/bpf/sockops/bpf_redir.c
index 77855a71ae..f55fdab14a 100644
--- a/bpf/sockops/bpf_redir.c
+++ b/bpf/sockops/bpf_redir.c
@@ -28,6 +28,10 @@ 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 a932a3194b..13545c4e80 100644
--- a/bpf/sockops/bpf_sockops.c
+++ b/bpf/sockops/bpf_sockops.c
@@ -29,6 +29,11 @@ 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
@@ -45,6 +50,7 @@ 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 13c4a44025..f45ad0ffe0 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 pad7;
+	__u8 protocol;
 	__u16 pad8;
 	__u32 sport;
 	__u32 dport;
diff --git a/cilium/cmd/service_update.go b/cilium/cmd/service_update.go
index bf9eb225bd..76fe88285c 100644
--- a/cilium/cmd/service_update.go
+++ b/cilium/cmd/service_update.go
@@ -37,6 +37,7 @@ var (
 	localRedirect      bool
 	idU                uint64
 	frontend           string
+	protocol           string
 	backends           []string
 )
 
@@ -60,11 +61,36 @@ 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 parseFrontendAddress(address string) (*models.FrontendAddress, net.IP) {
-	frontend, err := net.ResolveTCPAddr("tcp", address)
+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)
 	if err != nil {
 		Fatalf("Unable to parse frontend address: %s\n", err)
 	}
@@ -74,13 +100,12 @@ func parseFrontendAddress(address string) (*models.FrontendAddress, net.IP) {
 		scope = models.FrontendAddressScopeInternal
 	}
 
-	// FIXME support more than TCP
 	return &models.FrontendAddress{
-		IP:       frontend.IP.String(),
-		Port:     uint16(frontend.Port),
-		Protocol: models.FrontendAddressProtocolTCP,
+		IP:       ip.String(),
+		Port:     uint16(port),
+		Protocol: proto,
 		Scope:    scope,
-	}, frontend.IP
+	}, ip
 }
 
 func boolToInt(set bool) int {
@@ -92,7 +117,7 @@ func boolToInt(set bool) int {
 
 func updateService(cmd *cobra.Command, args []string) {
 	id := int64(idU)
-	fa, faIP := parseFrontendAddress(frontend)
+	fa, faIP := parseFrontendAddress(protocol, frontend)
 
 	var spec *models.ServiceSpec
 	svc, err := client.GetServiceID(id)
@@ -150,20 +175,19 @@ func updateService(cmd *cobra.Command, args []string) {
 
 	spec.BackendAddresses = nil
 	for _, backend := range backends {
-		beAddr, err := net.ResolveTCPAddr("tcp", backend)
+		ip, port, proto, err := parseAddress(protocol, backend)
 		if err != nil {
-			Fatalf("Cannot parse backend address \"%s\": %s", backend, err)
+			Fatalf("Cannot parse backend address %q: %s", backend, err)
 		}
-
 		// Backend ID will be set by the daemon
-		be := loadbalancer.NewBackend(0, loadbalancer.TCP, beAddr.IP, uint16(beAddr.Port))
+		be := loadbalancer.NewBackend(0, loadbalancer.L4Type(strings.ToUpper(proto)), ip, uint16(port))
 
 		if be.IsIPv6() && faIP.To4() != nil {
 			Fatalf("Address mismatch between frontend and backend %s", backend)
 		}
 
-		if fa.Port == 0 && beAddr.Port != 0 {
-			Fatalf("L4 backend found (%v) with L3 frontend", beAddr)
+		if fa.Port == 0 && port != 0 {
+			Fatalf("L4 backend found (%s:%d) with L3 frontend", ip, port)
 		}
 
 		ba := be.GetBackendModel()
diff --git a/jenkinsfiles/kubernetes-upstream.Jenkinsfile b/jenkinsfiles/kubernetes-upstream.Jenkinsfile
index d1b47ed180..5d1baeabac 100644
--- a/jenkinsfiles/kubernetes-upstream.Jenkinsfile
+++ b/jenkinsfiles/kubernetes-upstream.Jenkinsfile
@@ -36,7 +36,8 @@ pipeline {
         TESTDIR="${WORKSPACE}/${PROJ_PATH}/test"
         VM_MEMORY = "5120"
         K8S_VERSION="1.19"
-        SERVER_BOX = "cilium/ubuntu"
+        KERNEL="419"
+        SERVER_BOX = "cilium/ubuntu-4-19"
         CNI_INTEGRATION=setIfLabel("integration/cni-flannel", "FLANNEL", "")
         RACE="""${sh(
                 returnStdout: true,
@@ -104,7 +105,7 @@ pipeline {
 
         stage('BDD-tests'){
             options {
-                timeout(time: 90, unit: 'MINUTES')
+                timeout(time: 150, unit: 'MINUTES')
             }
 
             steps {
diff --git a/pkg/hubble/relay/pool/option.go b/pkg/hubble/relay/pool/option.go
index 42037dfd97..27abf0ccdd 100644
--- a/pkg/hubble/relay/pool/option.go
+++ b/pkg/hubble/relay/pool/option.go
@@ -39,6 +39,11 @@ var defaultOptions = options{
 		Options: []grpc.DialOption{
 			grpc.WithInsecure(),
 			grpc.WithBlock(),
+			grpc.FailOnNonTempDialError(true),
+			// TODO: uncomment the line below once grpc-go is >= v1.30.0
+			// currently blocked on v1.29.1, see the following PR for details
+			// https://github.com/cilium/cilium/pull/13405
+			// grpc.WithReturnConnectionError(),
 		},
 	},
 	backoff: &backoff.Exponential{
diff --git a/pkg/hubble/relay/server/server.go b/pkg/hubble/relay/server/server.go
index 9276e355bd..557601eeb7 100644
--- a/pkg/hubble/relay/server/server.go
+++ b/pkg/hubble/relay/server/server.go
@@ -75,8 +75,15 @@ func New(options ...Option) (*Server, error) {
 		),
 		pool.WithClientConnBuilder(pool.GRPCClientConnBuilder{
 			DialTimeout: opts.dialTimeout,
-			Options:     []grpc.DialOption{grpc.WithBlock()},
-			TLSConfig:   opts.clientTLSConfig,
+			Options: []grpc.DialOption{
+				grpc.WithBlock(),
+				grpc.FailOnNonTempDialError(true),
+				// TODO: uncomment the line below once grpc-go is >= v1.30.0
+				// currently blocked on v1.29.1, see the following PR for details
+				// https://github.com/cilium/cilium/pull/13405
+				// grpc.WithReturnConnectionError(),
+			},
+			TLSConfig: opts.clientTLSConfig,
 		}),
 		pool.WithRetryTimeout(opts.retryTimeout),
 		pool.WithLogger(opts.log),
diff --git a/pkg/ipam/allocator/podcidr/podcidr.go b/pkg/ipam/allocator/podcidr/podcidr.go
index 207aaeb25a..7b45c1942e 100644
--- a/pkg/ipam/allocator/podcidr/podcidr.go
+++ b/pkg/ipam/allocator/podcidr/podcidr.go
@@ -836,7 +836,7 @@ func (n *NodesPodCIDRManager) allocateNext(nodeName string) (*nodeCIDRs, bool, e
 			return nil, false, err
 		}
 		revertStack.Push(revertFunc)
-		log.Debug("v4CIDR", v4CIDR)
+		log.WithField("CIDR", v4CIDR).Debug("v4 allocated CIDR")
 		nCIDRs.v4PodCIDRs = []*net.IPNet{v4CIDR}
 	}
 	if len(n.v6CIDRAllocators) != 0 {
@@ -845,7 +845,7 @@ func (n *NodesPodCIDRManager) allocateNext(nodeName string) (*nodeCIDRs, bool, e
 			return nil, false, err
 		}
 		revertStack.Push(revertFunc)
-		log.Debug("v6CIDR", v6CIDR)
+		log.WithField("CIDR", v6CIDR).Debug("v6 allocated CIDR")
 		nCIDRs.v6PodCIDRs = []*net.IPNet{v6CIDR}
 	}
 
diff --git a/pkg/k8s/apis/cilium.io/v2/clrp_types.go b/pkg/k8s/apis/cilium.io/v2/clrp_types.go
index fc49c240c1..7a3aff6d70 100644
--- a/pkg/k8s/apis/cilium.io/v2/clrp_types.go
+++ b/pkg/k8s/apis/cilium.io/v2/clrp_types.go
@@ -234,11 +234,6 @@ func (pInfo *PortInfo) SanitizePortInfo(checkNamedPort bool) (uint16, string, lb
 	}
 	pName = strings.ToLower(pInfo.Name) // Normalize for case insensitive comparison
 
-	// Sanitize protocol
-	var err error
-	protocol, err = lb.NewL4Type(string(pInfo.Protocol))
-	if err != nil {
-		return pInt, pName, protocol, err
-	}
+	protocol = lb.NewL4Type(string(pInfo.Protocol))
 	return pInt, pName, protocol, nil
 }
diff --git a/pkg/k8s/service.go b/pkg/k8s/service.go
index 09a6b3db4a..35e7030abf 100644
--- a/pkg/k8s/service.go
+++ b/pkg/k8s/service.go
@@ -442,18 +442,6 @@ 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 7731451ef0..e47c725896 100644
--- a/pkg/k8s/service_cache.go
+++ b/pkg/k8s/service_cache.go
@@ -341,28 +341,6 @@ 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.
@@ -379,12 +357,12 @@ func (s *ServiceCache) UniqueServiceFrontends() FrontendList {
 				L4Addr: *p,
 				Scope:  loadbalancer.ScopeExternal,
 			}
-			uniqueFrontends[address.StringWithProtocol()] = struct{}{}
+			uniqueFrontends[address.String()] = struct{}{}
 		}
 		for _, nodePortFEs := range svc.NodePorts {
 			for _, fe := range nodePortFEs {
 				if fe.Scope == loadbalancer.ScopeExternal {
-					uniqueFrontends[fe.StringWithProtocol()] = struct{}{}
+					uniqueFrontends[fe.String()] = struct{}{}
 				}
 			}
 		}
diff --git a/pkg/k8s/watchers/pod.go b/pkg/k8s/watchers/pod.go
index 2f1c4683cb..af79d1fb08 100644
--- a/pkg/k8s/watchers/pod.go
+++ b/pkg/k8s/watchers/pod.go
@@ -483,13 +483,9 @@ 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 fe986515c4..7be8255f06 100644
--- a/pkg/k8s/watchers/watcher.go
+++ b/pkg/k8s/watchers/watcher.go
@@ -605,16 +605,8 @@ 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)
 
@@ -728,14 +720,8 @@ 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 {
@@ -845,6 +831,7 @@ 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 508c392aff..4dd2b623ff 100644
--- a/pkg/loadbalancer/loadbalancer.go
+++ b/pkg/loadbalancer/loadbalancer.go
@@ -160,6 +160,7 @@ func (s ServiceFlags) UInt16() uint16 {
 }
 
 const (
+	// NONE type.
 	NONE = L4Type("NONE")
 	// TCP type.
 	TCP = L4Type("TCP")
@@ -265,14 +266,25 @@ func (s *SVC) GetModel() *models.Service {
 	}
 }
 
-func NewL4Type(name string) (L4Type, error) {
+func NewL4Type(name string) L4Type {
 	switch strings.ToLower(name) {
 	case "tcp":
-		return TCP, nil
+		return TCP
 	case "udp":
-		return UDP, nil
+		return UDP
 	default:
-		return "", fmt.Errorf("unknown L4 protocol")
+		return NONE
+	}
+}
+
+func NewL4TypeFromNumber(proto uint8) L4Type {
+	switch proto {
+	case 6:
+		return TCP
+	case 17:
+		return UDP
+	default:
+		return NONE
 	}
 }
 
@@ -307,6 +319,11 @@ 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}.
@@ -336,16 +353,9 @@ func NewL3n4AddrFromModel(base *models.FrontendAddress) (*L3n4Addr, error) {
 		return nil, fmt.Errorf("missing IP address")
 	}
 
-	proto := NONE
-	if base.Protocol != "" {
-		p, err := NewL4Type(base.Protocol)
-		if err != nil {
-			return nil, err
-		}
-		proto = p
-	}
+	p := NewL4Type(base.Protocol)
 
-	l4addr := NewL4Addr(proto, base.Port)
+	l4addr := NewL4Addr(p, base.Port)
 	ip := net.ParseIP(base.IP)
 	if ip == nil {
 		return nil, fmt.Errorf("invalid IP address \"%s\"", base.IP)
@@ -378,8 +388,8 @@ func NewBackendFromBackendModel(base *models.BackendAddress) (*Backend, error) {
 		return nil, fmt.Errorf("missing IP address")
 	}
 
-	// FIXME: Should this be NONE ?
-	l4addr := NewL4Addr(NONE, base.Port)
+	p := NewL4Type(base.Protocol)
+	l4addr := NewL4Addr(p, base.Port)
 	ip := net.ParseIP(*base.IP)
 	if ip == nil {
 		return nil, fmt.Errorf("invalid IP address \"%s\"", *base.IP)
@@ -393,8 +403,8 @@ func NewL3n4AddrFromBackendModel(base *models.BackendAddress) (*L3n4Addr, error)
 		return nil, fmt.Errorf("missing IP address")
 	}
 
-	// FIXME: Should this be NONE ?
-	l4addr := NewL4Addr(NONE, base.Port)
+	p := NewL4Type(base.Protocol)
+	l4addr := NewL4Addr(p, base.Port)
 	ip := net.ParseIP(*base.IP)
 	if ip == nil {
 		return nil, fmt.Errorf("invalid IP address \"%s\"", *base.IP)
@@ -412,9 +422,10 @@ func (a *L3n4Addr) GetModel() *models.FrontendAddress {
 		scope = models.FrontendAddressScopeInternal
 	}
 	return &models.FrontendAddress{
-		IP:    a.IP.String(),
-		Port:  a.Port,
-		Scope: scope,
+		IP:       a.IP.String(),
+		Port:     a.Port,
+		Scope:    scope,
+		Protocol: a.Protocol,
 	}
 }
 
@@ -428,25 +439,13 @@ 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[/Scope]" format for IPv4 and
-// "[IPv6]:Port[/Scope]" format for IPv6.
+// String returns the L3n4Addr in the "IPv4:Port/Protocol[/Scope]" format for IPv4 and
+// "[IPv6]:Port/Protocol[/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"
@@ -477,14 +476,7 @@ func (a *L3n4Addr) DeepCopy() *L3n4Addr {
 
 // Hash calculates L3n4Addr's internal SHA256Sum.
 func (a L3n4Addr) Hash() 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))
+	str := []byte(fmt.Sprintf("%s", a.String()))
 	return fmt.Sprintf("%x", sha512.Sum512_256(str))
 }
 
diff --git a/pkg/maps/lbmap/ipv4.go b/pkg/maps/lbmap/ipv4.go
index bc2751e1bd..523f20f908 100644
--- a/pkg/maps/lbmap/ipv4.go
+++ b/pkg/maps/lbmap/ipv4.go
@@ -171,11 +171,28 @@ func NewService4Key(ip net.IP, port uint16, proto u8proto.U8proto, scope uint8,
 }
 
 func (k *Service4Key) String() string {
-	addr := net.JoinHostPort(k.Address.String(), fmt.Sprintf("%d", k.Port))
-	if k.Scope == loadbalancer.ScopeInternal {
-		addr += "/i"
+	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"
 	}
-	return addr
+
+	return fmt.Sprintf("%s/%s%s", a, p, i)
 }
 
 func (k *Service4Key) GetKeyPtr() unsafe.Pointer { return unsafe.Pointer(k) }
@@ -189,6 +206,7 @@ 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 {
@@ -313,6 +331,7 @@ 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 25b3a96cbb..1d20e2dba8 100644
--- a/pkg/maps/lbmap/ipv6.go
+++ b/pkg/maps/lbmap/ipv6.go
@@ -164,11 +164,12 @@ func NewService6Key(ip net.IP, port uint16, proto u8proto.U8proto, scope uint8,
 }
 
 func (k *Service6Key) String() string {
-	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)
-	}
+	return serviceKey(
+		k.Address.String(),
+		k.Port,
+		k.GetProtocol(),
+		k.Scope == loadbalancer.ScopeInternal,
+	)
 }
 
 func (k *Service6Key) GetKeyPtr() unsafe.Pointer { return unsafe.Pointer(k) }
@@ -182,6 +183,7 @@ 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 {
@@ -297,6 +299,7 @@ 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 04059f3e51..b04c77d6a7 100644
--- a/pkg/maps/lbmap/lbmap.go
+++ b/pkg/maps/lbmap/lbmap.go
@@ -64,6 +64,7 @@ type UpsertServiceParams struct {
 	ID                        uint16
 	IP                        net.IP
 	Port                      uint16
+	Protocol                  string
 	Backends                  map[string]uint16
 	PrevBackendCount          int
 	IPv6                      bool
@@ -90,10 +91,15 @@ 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, u8proto.ANY, p.Scope, 0)
+		svcKey = NewService6Key(p.IP, p.Port, proto, p.Scope, 0)
 	} else {
-		svcKey = NewService4Key(p.IP, p.Port, u8proto.ANY, p.Scope, 0)
+		svcKey = NewService4Key(p.IP, p.Port, proto, p.Scope, 0)
 	}
 
 	slot := 1
@@ -176,12 +182,16 @@ 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, u8proto.ANY, svc.Scope, 0)
+		svcKey = NewService6Key(svc.IP, svc.Port, proto, svc.Scope, 0)
 		revNATKey = NewRevNat6Key(uint16(svc.ID))
 	} else {
-		svcKey = NewService4Key(svc.IP, svc.Port, u8proto.ANY, svc.Scope, 0)
+		svcKey = NewService4Key(svc.IP, svc.Port, proto, svc.Scope, 0)
 		revNATKey = NewRevNat4Key(uint16(svc.ID))
 	}
 
@@ -206,7 +216,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, port uint16, ipv6 bool) error {
+func (*LBBPFMap) AddBackend(id uint16, ip net.IP, protocol loadbalancer.L4Type, port uint16, ipv6 bool) error {
 	var (
 		backend Backend
 		err     error
@@ -216,10 +226,14 @@ func (*LBBPFMap) AddBackend(id uint16, ip net.IP, port uint16, ipv6 bool) error
 		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, u8proto.ANY)
+		backend, err = NewBackend6(loadbalancer.BackendID(id), ip, port, p)
 	} else {
-		backend, err = NewBackend4(loadbalancer.BackendID(id), ip, port, u8proto.ANY)
+		backend, err = NewBackend4(loadbalancer.BackendID(id), ip, port, p)
 	}
 	if err != nil {
 		return fmt.Errorf("Unable to create backend (%d, %s, %d, %t): %s",
@@ -473,7 +487,7 @@ func (*LBBPFMap) DumpBackendMaps() ([]*loadbalancer.Backend, error) {
 	for backendID, backendVal := range backendValueMap {
 		ip := backendVal.GetAddress()
 		port := backendVal.GetPort()
-		proto := loadbalancer.NONE
+		proto := loadbalancer.NewL4TypeFromNumber(backendVal.GetProtocol())
 		lbBackend := loadbalancer.NewBackend(backendID, proto, ip, port)
 		lbBackends = append(lbBackends, lbBackend)
 	}
diff --git a/pkg/maps/lbmap/lbmap_mock.go b/pkg/maps/lbmap/lbmap_mock.go
index 969b867c0a..c3aa4022f5 100644
--- a/pkg/maps/lbmap/lbmap_mock.go
+++ b/pkg/maps/lbmap/lbmap_mock.go
@@ -19,6 +19,7 @@ import (
 	"net"
 
 	"github.com/cilium/cilium/pkg/cidr"
+	"github.com/cilium/cilium/pkg/loadbalancer"
 	lb "github.com/cilium/cilium/pkg/loadbalancer"
 )
 
@@ -50,7 +51,7 @@ func (m *LBMockMap) UpsertService(p *UpsertServiceParams) error {
 
 	svc, found := m.ServiceByID[p.ID]
 	if !found {
-		frontend := lb.NewL3n4AddrID(lb.NONE, p.IP, p.Port, p.Scope, lb.ID(p.ID))
+		frontend := lb.NewL3n4AddrID(loadbalancer.NewL4Type(p.Protocol), p.IP, p.Port, p.Scope, lb.ID(p.ID))
 		svc = &lb.SVC{Frontend: *frontend}
 	} else {
 		if p.PrevBackendCount != len(svc.Backends) {
@@ -81,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, port uint16, ipv6 bool) error {
+func (m *LBMockMap) AddBackend(id uint16, ip net.IP, protocol lb.L4Type, 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), lb.NONE, ip, port)
+	m.BackendByID[id] = lb.NewBackend(lb.BackendID(id), protocol, ip, port)
 
 	return nil
 }
diff --git a/pkg/maps/lbmap/types.go b/pkg/maps/lbmap/types.go
index f8eefee7ac..3260ec1d33 100644
--- a/pkg/maps/lbmap/types.go
+++ b/pkg/maps/lbmap/types.go
@@ -53,6 +53,9 @@ type ServiceKey interface {
 	// Get frontend port
 	GetPort() uint16
 
+	// Get protocol
+	GetProtocol() uint8
+
 	// Returns a RevNatValue matching a ServiceKey
 	RevNatValue() RevNatValue
 
@@ -125,6 +128,9 @@ type BackendValue interface {
 	// Get backend port
 	GetPort() uint16
 
+	// Get backend protocol
+	GetProtocol() uint8
+
 	// Convert fields to network byte order.
 	ToNetwork() BackendValue
 }
@@ -168,7 +174,8 @@ 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 {
-	feL3n4Addr := loadbalancer.NewL3n4Addr(loadbalancer.NONE, svcKey.GetAddress(), svcKey.GetPort(), svcKey.GetScope())
+	p := loadbalancer.NewL4TypeFromNumber(svcKey.GetProtocol())
+	feL3n4Addr := loadbalancer.NewL3n4Addr(p, svcKey.GetAddress(), svcKey.GetPort(), svcKey.GetScope())
 	feL3n4AddrID := &loadbalancer.L3n4AddrID{
 		L3n4Addr: *feL3n4Addr,
 		ID:       loadbalancer.ID(svcValue.GetRevNat()),
@@ -179,7 +186,7 @@ func svcFrontend(svcKey ServiceKey, svcValue ServiceValue) *loadbalancer.L3n4Add
 func svcBackend(backendID loadbalancer.BackendID, backend BackendValue) *loadbalancer.Backend {
 	beIP := backend.GetAddress()
 	bePort := backend.GetPort()
-	beProto := loadbalancer.NONE
-	beBackend := loadbalancer.NewBackend(backendID, beProto, beIP, bePort)
+	p := loadbalancer.NewL4TypeFromNumber(backend.GetProtocol())
+	beBackend := loadbalancer.NewBackend(backendID, p, beIP, bePort)
 	return beBackend
 }
diff --git a/pkg/maps/sockmap/sockmap.go b/pkg/maps/sockmap/sockmap.go
index f6731d7fff..9fa63e017b 100644
--- a/pkg/maps/sockmap/sockmap.go
+++ b/pkg/maps/sockmap/sockmap.go
@@ -23,19 +23,20 @@ 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"`
-	Pad7   uint8      `align:"pad7"`
-	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"`
+	Protocol uint8      `align:"protocol"`
+	Pad8     uint16     `align:"pad8"`
+	SPort    uint32     `align:"sport"`
+	DPort    uint32     `align:"dport"`
 }
 
 // SockmapValue is the fd of a socket
@@ -47,7 +48,11 @@ type SockmapValue struct {
 
 // String pretty print the 5-tuple as sip:sport->dip:dport
 func (v SockmapKey) String() string {
-	return fmt.Sprintf("%s:%d->%s:%d", v.SIP.String(), v.SPort, v.DIP.String(), v.DPort)
+	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)
 }
 
 // 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 54a61ba5bb..1a508e8fbd 100644
--- a/pkg/option/config.go
+++ b/pkg/option/config.go
@@ -1503,7 +1503,7 @@ type DaemonConfig struct {
 	// runs in the same container as Cilium.
 	EnvoyLogPath string
 
-	// EnableSockOps specifies whether to enable sockops (socket lookup).
+	// SockopsEnable specifies whether to enable sockops (socket lookup).
 	SockopsEnable bool
 
 	// PrependIptablesChains is the name of the option to enable prepending
diff --git a/pkg/redirectpolicy/manager.go b/pkg/redirectpolicy/manager.go
index 6b1115cd2e..e47f433b3d 100644
--- a/pkg/redirectpolicy/manager.go
+++ b/pkg/redirectpolicy/manager.go
@@ -606,11 +606,11 @@ func (rpm *Manager) processConfigWithSinglePort(config *LRPConfig, pods ...*podM
 					},
 				}, pod.id,
 			}
-			if feM.feAddr.IP.To4() != nil {
+			if feM.feAddr.IP.To4() != nil && be.IP.To4() != nil {
 				if option.Config.EnableIPv4 {
 					bes4 = append(bes4, be)
 				}
-			} else {
+			} else if feM.feAddr.IP.To4() == nil && be.IP.To4() == nil {
 				if option.Config.EnableIPv6 {
 					bes6 = append(bes6, be)
 				}
@@ -667,11 +667,11 @@ func (rpm *Manager) processConfigWithNamedPorts(config *LRPConfig, pods ...*podM
 						},
 						pod.id,
 					}
-					if feM.feAddr.IP.To4() != nil {
+					if feM.feAddr.IP.To4() != nil && be.IP.To4() != nil {
 						if option.Config.EnableIPv4 {
 							bes4 = append(bes4, be)
 						}
-					} else {
+					} else if feM.feAddr.IP.To4() == nil && be.IP.To4() == nil {
 						if option.Config.EnableIPv6 {
 							bes6 = append(bes6, be)
 						}
diff --git a/pkg/service/service.go b/pkg/service/service.go
index 3a5bd8cbfd..3f914b282d 100644
--- a/pkg/service/service.go
+++ b/pkg/service/service.go
@@ -44,7 +44,7 @@ var (
 type LBMap interface {
 	UpsertService(*lbmap.UpsertServiceParams) error
 	DeleteService(lb.L3n4AddrID, int, bool) error
-	AddBackend(uint16, net.IP, uint16, bool) error
+	AddBackend(uint16, net.IP, lb.L4Type, uint16, bool) error
 	DeleteBackendByID(uint16, bool) error
 	AddAffinityMatch(uint16, uint16) error
 	DeleteAffinityMatch(uint16, uint16) error
@@ -684,6 +684,7 @@ 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
 		}
@@ -699,6 +700,7 @@ 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 b396d2c043..91f26a07eb 100644
--- a/pkg/u8proto/u8proto.go
+++ b/pkg/u8proto/u8proto.go
@@ -43,6 +43,7 @@ var protoNames = map[U8proto]string{
 var ProtoIDs = map[string]U8proto{
 	"all":    0,
 	"any":    0,
+	"none":   0,
 	"icmp":   1,
 	"tcp":    6,
 	"udp":    17,
@@ -62,5 +63,13 @@ func ParseProtocol(proto string) (U8proto, error) {
 	if u, ok := ProtoIDs[strings.ToLower(proto)]; ok {
 		return u, nil
 	}
-	return 0, fmt.Errorf("unknown protocol '%s'", proto)
+	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
 }


Test patch:
diff --git a/pkg/k8s/endpoints_test.go b/pkg/k8s/endpoints_test.go
index 7bc606ef5a..0e3ebdca18 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.NONE,
+									Protocol: loadbalancer.TCP,
 									Port:     1,
 								},
 							},
@@ -67,7 +67,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.NONE,
+									Protocol: loadbalancer.TCP,
 									Port:     1,
 								},
 							},
@@ -86,7 +86,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.NONE,
+									Protocol: loadbalancer.TCP,
 									Port:     1,
 								},
 							},
@@ -100,7 +100,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.2": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.NONE,
+									Protocol: loadbalancer.TCP,
 									Port:     1,
 								},
 							},
@@ -118,7 +118,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.NONE,
+									Protocol: loadbalancer.TCP,
 									Port:     1,
 								},
 							},
@@ -132,7 +132,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foz": {
-									Protocol: loadbalancer.NONE,
+									Protocol: loadbalancer.TCP,
 									Port:     1,
 								},
 							},
@@ -150,7 +150,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.NONE,
+									Protocol: loadbalancer.TCP,
 									Port:     1,
 								},
 							},
@@ -158,6 +158,70 @@ 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{
@@ -182,7 +246,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.NONE,
+									Protocol: loadbalancer.TCP,
 									Port:     1,
 								},
 							},
@@ -196,11 +260,11 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.NONE,
+									Protocol: loadbalancer.TCP,
 									Port:     1,
 								},
 								"baz": {
-									Protocol: loadbalancer.NONE,
+									Protocol: loadbalancer.TCP,
 									Port:     2,
 								},
 							},
@@ -219,7 +283,7 @@ func TestEndpoints_DeepEqual(t *testing.T) {
 						"172.20.0.1": {
 							Ports: map[string]*loadbalancer.L4Addr{
 								"foo": {
-									Protocol: loadbalancer.NONE,
+									Protocol: loadbalancer.TCP,
 									Port:     1,
 								},
 							},
diff --git a/pkg/k8s/service_cache_test.go b/pkg/k8s/service_cache_test.go
index 332248dbdd..531fc96ce9 100644
--- a/pkg/k8s/service_cache_test.go
+++ b/pkg/k8s/service_cache_test.go
@@ -95,33 +95,6 @@ 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 6484c5aed9..0c59d9078c 100644
--- a/pkg/k8s/service_test.go
+++ b/pkg/k8s/service_test.go
@@ -180,41 +180,6 @@ 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
@@ -232,7 +197,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.NONE,
+						Protocol: loadbalancer.TCP,
 						Port:     1,
 					},
 				},
@@ -241,7 +206,7 @@ func TestService_Equals(t *testing.T) {
 						"0.0.0.0:31000": {
 							L3n4Addr: loadbalancer.L3n4Addr{
 								L4Addr: loadbalancer.L4Addr{
-									Protocol: loadbalancer.NONE,
+									Protocol: loadbalancer.TCP,
 									Port:     31000,
 								},
 								IP: net.IPv4(0, 0, 0, 0),
@@ -264,7 +229,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.NONE,
+							Protocol: loadbalancer.TCP,
 							Port:     1,
 						},
 					},
@@ -273,7 +238,7 @@ func TestService_Equals(t *testing.T) {
 							"0.0.0.0:31000": {
 								L3n4Addr: loadbalancer.L3n4Addr{
 									L4Addr: loadbalancer.L4Addr{
-										Protocol: loadbalancer.NONE,
+										Protocol: loadbalancer.TCP,
 										Port:     31000,
 									},
 									IP: net.IPv4(0, 0, 0, 0),
@@ -300,7 +265,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.NONE,
+						Protocol: loadbalancer.TCP,
 						Port:     1,
 					},
 				},
@@ -315,7 +280,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.NONE,
+							Protocol: loadbalancer.TCP,
 							Port:     1,
 						},
 					},
@@ -336,7 +301,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.NONE,
+						Protocol: loadbalancer.TCP,
 						Port:     1,
 					},
 				},
@@ -349,7 +314,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.NONE,
+							Protocol: loadbalancer.TCP,
 							Port:     1,
 						},
 					},
@@ -368,7 +333,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foz"): {
-						Protocol: loadbalancer.NONE,
+						Protocol: loadbalancer.TCP,
 						Port:     1,
 					},
 				},
@@ -381,7 +346,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.NONE,
+							Protocol: loadbalancer.TCP,
 							Port:     1,
 						},
 					},
@@ -398,13 +363,73 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.NONE,
+						Protocol: loadbalancer.TCP,
 						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"),
@@ -428,7 +453,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.NONE,
+						Protocol: loadbalancer.TCP,
 						Port:     1,
 					},
 				},
@@ -441,11 +466,11 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.NONE,
+							Protocol: loadbalancer.TCP,
 							Port:     1,
 						},
 						loadbalancer.FEPortName("baz"): {
-							Protocol: loadbalancer.NONE,
+							Protocol: loadbalancer.TCP,
 							Port:     2,
 						},
 					},
@@ -469,7 +494,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.NONE,
+							Protocol: loadbalancer.TCP,
 							Port:     1,
 						},
 					},
@@ -486,7 +511,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: true,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.NONE,
+						Protocol: loadbalancer.TCP,
 						Port:     1,
 					},
 				},
@@ -495,7 +520,7 @@ func TestService_Equals(t *testing.T) {
 						"1.1.1.1:31000": {
 							L3n4Addr: loadbalancer.L3n4Addr{
 								L4Addr: loadbalancer.L4Addr{
-									Protocol: loadbalancer.NONE,
+									Protocol: loadbalancer.TCP,
 									Port:     31000,
 								},
 								IP: net.IPv4(1, 1, 1, 1),
@@ -518,7 +543,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: true,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.NONE,
+							Protocol: loadbalancer.TCP,
 							Port:     1,
 						},
 					},
@@ -527,7 +552,7 @@ func TestService_Equals(t *testing.T) {
 							"0.0.0.0:31000": {
 								L3n4Addr: loadbalancer.L3n4Addr{
 									L4Addr: loadbalancer.L4Addr{
-										Protocol: loadbalancer.NONE,
+										Protocol: loadbalancer.TCP,
 										Port:     31000,
 									},
 									IP: net.IPv4(0, 0, 0, 0),
@@ -554,7 +579,7 @@ func TestService_Equals(t *testing.T) {
 				IsHeadless: false,
 				Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 					loadbalancer.FEPortName("foo"): {
-						Protocol: loadbalancer.NONE,
+						Protocol: loadbalancer.TCP,
 						Port:     1,
 					},
 				},
@@ -572,7 +597,7 @@ func TestService_Equals(t *testing.T) {
 					IsHeadless: false,
 					Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
 						loadbalancer.FEPortName("foo"): {
-							Protocol: loadbalancer.NONE,
+							Protocol: loadbalancer.TCP,
 							Port:     1,
 						},
 					},
diff --git a/pkg/loadbalancer/loadbalancer_test.go b/pkg/loadbalancer/loadbalancer_test.go
index 64a5685e79..336e316bbf 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: NONE,
+				Protocol: TCP,
 				Port:     1,
 			},
 			args: args{
 				o: &L4Addr{
-					Protocol: NONE,
+					Protocol: TCP,
 					Port:     1,
 				},
 			},
@@ -59,12 +59,12 @@ func TestL4Addr_Equals(t *testing.T) {
 		{
 			name: "both different",
 			fields: &L4Addr{
-				Protocol: NONE,
+				Protocol: TCP,
 				Port:     0,
 			},
 			args: args{
 				o: &L4Addr{
-					Protocol: NONE,
+					Protocol: TCP,
 					Port:     1,
 				},
 			},
@@ -78,12 +78,40 @@ func TestL4Addr_Equals(t *testing.T) {
 		{
 			name: "other nil",
 			fields: &L4Addr{
-				Protocol: NONE,
+				Protocol: TCP,
 				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) {
@@ -110,7 +138,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 			fields: &L3n4AddrID{
 				L3n4Addr: L3n4Addr{
 					L4Addr: L4Addr{
-						Protocol: NONE,
+						Protocol: TCP,
 						Port:     1,
 					},
 					IP: net.IPv4(1, 1, 1, 1),
@@ -121,7 +149,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 				o: &L3n4AddrID{
 					L3n4Addr: L3n4Addr{
 						L4Addr: L4Addr{
-							Protocol: NONE,
+							Protocol: TCP,
 							Port:     1,
 						},
 						IP: net.IPv4(1, 1, 1, 1),
@@ -136,7 +164,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 			fields: &L3n4AddrID{
 				L3n4Addr: L3n4Addr{
 					L4Addr: L4Addr{
-						Protocol: NONE,
+						Protocol: TCP,
 						Port:     1,
 					},
 					IP: net.IPv4(1, 1, 1, 1),
@@ -147,7 +175,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 				o: &L3n4AddrID{
 					L3n4Addr: L3n4Addr{
 						L4Addr: L4Addr{
-							Protocol: NONE,
+							Protocol: TCP,
 							Port:     1,
 						},
 						IP: net.IPv4(1, 1, 1, 1),
@@ -162,7 +190,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 			fields: &L3n4AddrID{
 				L3n4Addr: L3n4Addr{
 					L4Addr: L4Addr{
-						Protocol: NONE,
+						Protocol: TCP,
 						Port:     1,
 					},
 					IP: net.IPv4(2, 2, 2, 2),
@@ -173,7 +201,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 				o: &L3n4AddrID{
 					L3n4Addr: L3n4Addr{
 						L4Addr: L4Addr{
-							Protocol: NONE,
+							Protocol: TCP,
 							Port:     1,
 						},
 						IP: net.IPv4(1, 1, 1, 1),
@@ -188,7 +216,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 			fields: &L3n4AddrID{
 				L3n4Addr: L3n4Addr{
 					L4Addr: L4Addr{
-						Protocol: NONE,
+						Protocol: TCP,
 						Port:     2,
 					},
 					IP: net.IPv4(1, 1, 1, 1),
@@ -199,7 +227,7 @@ func TestL3n4AddrID_Equals(t *testing.T) {
 				o: &L3n4AddrID{
 					L3n4Addr: L3n4Addr{
 						L4Addr: L4Addr{
-							Protocol: NONE,
+							Protocol: TCP,
 							Port:     1,
 						},
 						IP: net.IPv4(1, 1, 1, 1),
@@ -209,6 +237,32 @@ 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 d5091772a0..662caa345a 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 cb59c39c4a..7ae6950f9d 100644
--- a/pkg/redirectpolicy/manager_test.go
+++ b/pkg/redirectpolicy/manager_test.go
@@ -58,6 +58,9 @@ type fakePodStore struct {
 }
 
 func (ps *fakePodStore) List() []interface{} {
+	if ps.OnList != nil {
+		return ps.OnList()
+	}
 	pods := make([]interface{}, 2, 2)
 	pods = append(pods, pod1, pod2)
 	return pods
@@ -96,20 +99,25 @@ 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.IP("1.1.1.1"),
+		net.ParseIP("1.1.1.1"),
 		80,
 		lb.ScopeExternal)
 	fe2 = lb.NewL3n4Addr(
 		proto2,
-		net.IP("2.2.2.2"),
+		net.ParseIP("2.2.2.2"),
 		81,
 		lb.ScopeExternal)
+	fe3v6 = lb.NewL3n4Addr(
+		proto1,
+		net.ParseIP("fd00::2"),
+		80,
+		lb.ScopeExternal)
 	portName1 = "test1"
 	portName2 = "test2"
 	beP1      = bePortInfo{
@@ -218,7 +226,8 @@ func (m *ManagerSuite) SetUpTest(c *C) {
 			Name:      "test-foo",
 			Namespace: "ns1",
 		},
-		lrpType: lrpConfigTypeAddr,
+		lrpType:      lrpConfigTypeAddr,
+		frontendType: addrFrontendSinglePort,
 		frontendMappings: []*feMapping{{
 			feAddr:      fe1,
 			podBackends: nil,
@@ -231,6 +240,7 @@ func (m *ManagerSuite) SetUpTest(c *C) {
 				},
 			},
 		},
+		backendPorts: []bePortInfo{beP1},
 	}
 	configSvcType = LRPConfig{
 		id: k8s.ServiceID{
@@ -284,8 +294,6 @@ func (m *ManagerSuite) TestManager_AddRedirectPolicy_SvcMatcherDuplicateConfig(c
 func (m *ManagerSuite) TestManager_AddrMatcherConfigSinglePort(c *C) {
 	// Add an addressMatcher type LRP with single port. The policy config
 	// frontend should have 2 pod backends with each of the podIPs.
-	configAddrType.frontendType = addrFrontendSinglePort
-	configAddrType.backendPorts = append(configAddrType.backendPorts, beP1)
 	podIPs, _ := utils.ValidIPs(pod1.Status)
 	expectedbes := make([]backend, len(podIPs))
 	for i := range podIPs {
@@ -314,7 +322,7 @@ func (m *ManagerSuite) TestManager_AddrMatcherConfigSinglePort(c *C) {
 	c.Assert(m.rpm.policyPods[pod1ID][0], Equals, configAddrType.id)
 
 	// Add a new backend pod, this will add 2 more pod backends with each of the podIPs.
-	pod3 := pod2
+	pod3 := pod2.DeepCopy()
 	pod3.Labels["test"] = "foo"
 	pod3ID := pod2ID
 	podIPs, _ = utils.ValidIPs(pod3.Status)
@@ -374,7 +382,13 @@ func (m *ManagerSuite) TestManager_AddrMatcherConfigMultiplePorts(c *C) {
 		beP1.name: &configAddrType.backendPorts[0],
 		beP2.name: &configAddrType.backendPorts[1]}
 	podIPs, _ := utils.ValidIPs(pod1.Status)
-	expectedbes := make([]backend, len(podIPs))
+	expectedbes := make([]backend, 0, len(podIPs))
+	for i := range podIPs {
+		expectedbes = append(expectedbes, backend{
+			L3n4Addr: lb.L3n4Addr{IP: net.ParseIP(podIPs[i]), L4Addr: beP1.l4Addr},
+			podID:    pod1ID,
+		})
+	}
 
 	added, err := m.rpm.AddRedirectPolicy(configAddrType, &fakePodStore{})
 
@@ -429,4 +443,59 @@ func (m *ManagerSuite) TestManager_AddrMatcherConfigMultiplePorts(c *C) {
 	c.Assert(len(m.rpm.policyConfigs), Equals, 0)
 }
 
+// Tests if frontend ipv4 and ipv6 addresses are mapped to the ipv4 and ipv6
+// backends, respectively.
+func (m *ManagerSuite) TestManager_AddrMatcherConfigDualStack(c *C) {
+	// Only ipv4 backend(s) for ipv4 frontend
+	pod3 := pod1.DeepCopy()
+	pod3ID := pod1ID
+	podIPs, _ := utils.ValidIPs(pod3.Status)
+	expectedbes4 := make([]backend, 0, len(podIPs))
+	for i := range podIPs {
+		expectedbes4 = append(expectedbes4, backend{
+			L3n4Addr: lb.L3n4Addr{IP: net.ParseIP(podIPs[i]), L4Addr: beP1.l4Addr},
+			podID:    pod3ID,
+		})
+	}
+	pod3v6 := slimcorev1.PodIP{IP: "fd00::40"}
+	expectedbes6 := []backend{{
+		L3n4Addr: lb.L3n4Addr{IP: net.ParseIP(pod3v6.IP), L4Addr: beP1.l4Addr},
+		podID:    pod3ID,
+	}}
+	pod3.Status.PodIPs = append(pod3.Status.PodIPs, pod3v6)
+	ps := &fakePodStore{OnList: func() []interface{} {
+		return []interface{}{pod3}
+	}}
+
+	added, err := m.rpm.AddRedirectPolicy(configAddrType, ps)
+
+	c.Assert(added, Equals, true)
+	c.Assert(err, IsNil)
+	c.Assert(len(configAddrType.frontendMappings[0].podBackends), Equals,
+		len(expectedbes4))
+	for i := range configAddrType.frontendMappings[0].podBackends {
+		c.Assert(configAddrType.frontendMappings[0].podBackends[i], checker.Equals,
+			expectedbes4[i])
+	}
+
+	// Only ipv6 backend(s) for ipv6 frontend
+	feM := []*feMapping{{
+		feAddr:      fe3v6,
+		podBackends: nil,
+	}}
+	configAddrType.id.Name = "test-bar"
+	configAddrType.frontendMappings = feM
+
+	added, err = m.rpm.AddRedirectPolicy(configAddrType, ps)
+
+	c.Assert(added, Equals, true)
+	c.Assert(err, IsNil)
+	c.Assert(len(configAddrType.frontendMappings[0].podBackends), Equals,
+		len(expectedbes6))
+	for i := range configAddrType.frontendMappings[0].podBackends {
+		c.Assert(configAddrType.frontendMappings[0].podBackends[i], checker.Equals,
+			expectedbes6[i])
+	}
+}
+
 //TODO Tests for svcMatcher
diff --git a/pkg/service/id_test.go b/pkg/service/id_test.go
index f6bc26d2d2..65cda1fca1 100644
--- a/pkg/service/id_test.go
+++ b/pkg/service/id_test.go
@@ -87,11 +87,10 @@ func (s *IDAllocTestSuite) TestServices(c *C) {
 	c.Assert(err, Equals, nil)
 	c.Assert(l3n4AddrID.ID, Equals, loadbalancer.ID(ffsIDu16+1))
 
-	// l3n4Addr3 should have the same ID as l3n4Addr2 since we are omitting the
-	// protocol type.
+	// different protocols should change the id
 	l3n4AddrID, err = AcquireID(l3n4Addr3, 0)
 	c.Assert(err, Equals, nil)
-	c.Assert(l3n4AddrID.ID, Equals, loadbalancer.ID(ffsIDu16+1))
+	c.Assert(l3n4AddrID.ID, Equals, loadbalancer.ID(ffsIDu16+2))
 
 	gotL3n4AddrID, err := GetID(FirstFreeServiceID)
 	c.Assert(err, Equals, nil)
diff --git a/pkg/service/service_test.go b/pkg/service/service_test.go
index 271e20b2fc..0b5fea4647 100644
--- a/pkg/service/service_test.go
+++ b/pkg/service/service_test.go
@@ -21,6 +21,7 @@ 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"
 	"github.com/cilium/cilium/pkg/maps/lbmap"
 	nodeTypes "github.com/cilium/cilium/pkg/node/types"
@@ -68,6 +69,7 @@ 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),
@@ -76,6 +78,10 @@ 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) {
@@ -170,9 +176,50 @@ 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 fdfb0ee470..bff6bb8db0 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.NONE, Port: 1},
+				"foo": {Protocol: loadbalancer.TCP, Port: 1},
 			},
 			b: PortConfiguration{
-				"foo": {Protocol: loadbalancer.NONE, Port: 1},
+				"foo": {Protocol: loadbalancer.TCP, Port: 1},
 			},
 			want: true,
 		},
 		{
 			a: PortConfiguration{
-				"foo": {Protocol: loadbalancer.NONE, Port: 1},
+				"foo": {Protocol: loadbalancer.TCP, Port: 1},
 			},
 			b: PortConfiguration{
-				"foz": {Protocol: loadbalancer.NONE, Port: 1},
+				"foz": {Protocol: loadbalancer.TCP, Port: 1},
 			},
 			want: false,
 		},
 		{
 			a: PortConfiguration{
-				"foo": {Protocol: loadbalancer.NONE, Port: 1},
+				"foo": {Protocol: loadbalancer.TCP, Port: 1},
 			},
 			b: PortConfiguration{
-				"foo": {Protocol: loadbalancer.NONE, Port: 2},
+				"foo": {Protocol: loadbalancer.TCP, Port: 2},
 			},
 			want: false,
 		},
 		{
 			a: PortConfiguration{
-				"foo": {Protocol: loadbalancer.NONE, Port: 1},
+				"foo": {Protocol: loadbalancer.TCP, Port: 1},
 			},
 			b: PortConfiguration{
-				"foo": {Protocol: loadbalancer.NONE, Port: 1},
-				"baz": {Protocol: loadbalancer.NONE, Port: 2},
+				"foo": {Protocol: loadbalancer.TCP, Port: 1},
+				"baz": {Protocol: loadbalancer.TCP, Port: 2},
 			},
 			want: false,
 		},
 		{
 			a: PortConfiguration{},
 			b: PortConfiguration{
-				"foo": {Protocol: loadbalancer.NONE, Port: 1},
+				"foo": {Protocol: loadbalancer.TCP, Port: 1},
 			},
 			want: false,
 		},
diff --git a/test/helpers/cilium.go b/test/helpers/cilium.go
index a0aa05c114..307fa748b2 100644
--- a/test/helpers/cilium.go
+++ b/test/helpers/cilium.go
@@ -799,9 +799,12 @@ func (s *SSHMeta) ServiceIsSynced(id int) (bool, error) {
 		return false, err
 	}
 
-	frontendAddr := net.JoinHostPort(
+	frontendAddr := serviceAddressKey(
 		svc.Status.Realized.FrontendAddress.IP,
-		fmt.Sprintf("%d", svc.Status.Realized.FrontendAddress.Port))
+		fmt.Sprintf("%d", svc.Status.Realized.FrontendAddress.Port),
+		svc.Status.Realized.FrontendAddress.Protocol,
+		"",
+	)
 	lb, ok := bpfLB[frontendAddr]
 	if ok == false {
 		return false, fmt.Errorf(
diff --git a/test/helpers/cons.go b/test/helpers/cons.go
index d10911de56..fd1663c436 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"
-	"github.com/cilium/cilium/test/ginkgo-ext"
+	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"
 )
 
 var (
diff --git a/test/helpers/kubectl.go b/test/helpers/kubectl.go
index a6f4d87894..e8ee77a958 100644
--- a/test/helpers/kubectl.go
+++ b/test/helpers/kubectl.go
@@ -1721,7 +1721,7 @@ func (kub *Kubectl) validateServicePlumbingInCiliumPod(fullName, ciliumPod strin
 	for _, port := range serviceObj.Spec.Ports {
 		var foundPort *v1.ServicePort
 		for _, realizedService := range realizedServices {
-			if port.Port == int32(realizedService.FrontendAddress.Port) {
+			if compareServicePortToFrontEnd(&port, realizedService.FrontendAddress) {
 				foundPort = &port
 				break
 			}
@@ -1730,8 +1730,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)
 		}
-
-		if _, ok := lbMap[net.JoinHostPort(serviceObj.Spec.ClusterIP, fmt.Sprintf("%d", port.Port))]; !ok {
+		lKey := serviceAddressKey(serviceObj.Spec.ClusterIP, fmt.Sprintf("%d", port.Port), string(port.Protocol), "")
+		if _, ok := lbMap[lKey]; !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)
 		}
@@ -1743,10 +1743,11 @@ func (kub *Kubectl) validateServicePlumbingInCiliumPod(fullName, ciliumPod strin
 				foundBackend, foundBackendLB := false, false
 				for _, realizedService := range realizedServices {
 					frontEnd := realizedService.FrontendAddress
-					lb := lbMap[net.JoinHostPort(frontEnd.IP, fmt.Sprintf("%d", frontEnd.Port))]
-
+					lbKey := serviceAddressKey(frontEnd.IP, fmt.Sprintf("%d", frontEnd.Port), string(frontEnd.Protocol), "")
+					lb := lbMap[lbKey]
 					for _, backAddr := range realizedService.BackendAddresses {
-						if addr.IP == *backAddr.IP && uint16(port.Port) == backAddr.Port {
+						if addr.IP == *backAddr.IP && uint16(port.Port) == backAddr.Port &&
+							compareProto(string(port.Protocol), backAddr.Protocol) {
 							foundBackend = true
 							for _, backend := range lb {
 								if strings.Contains(backend, net.JoinHostPort(*backAddr.IP, fmt.Sprintf("%d", port.Port))) {
@@ -3753,7 +3754,7 @@ CILIUM_SERVICES:
 	for _, cSvc := range ciliumSvcs {
 		if cSvc.Status.Realized.FrontendAddress.IP == k8sService.Spec.ClusterIP {
 			for _, port := range k8sService.Spec.Ports {
-				if int32(cSvc.Status.Realized.FrontendAddress.Port) == port.Port {
+				if compareServicePortToFrontEnd(&port, cSvc.Status.Realized.FrontendAddress) {
 					ciliumService = &cSvc
 					break CILIUM_SERVICES
 				}
@@ -3774,7 +3775,11 @@ CILIUM_SERVICES:
 }
 
 // CiliumServiceAdd adds the given service on a 'pod' running Cilium
-func (kub *Kubectl) CiliumServiceAdd(pod string, id int64, frontend string, backends []string, svcType, trafficPolicy string) error {
+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"
+	}
 	var opts []string
 	switch strings.ToLower(svcType) {
 	case "nodeport":
@@ -3801,8 +3806,8 @@ func (kub *Kubectl) CiliumServiceAdd(pod string, id int64, frontend string, back
 	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 --frontend %q --backends %q %s",
-		id, frontend, backendsStr, optsStr)).GetErr("cilium service update")
+	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")
 }
 
 // CiliumServiceDel deletes the service with 'id' on a 'pod' running Cilium
@@ -4026,7 +4031,7 @@ func validateCiliumSvc(cSvc models.Service, k8sSvcs []v1.Service, k8sEps []v1.En
 
 	var k8sServicePort *v1.ServicePort
 	for _, k8sPort := range k8sService.Spec.Ports {
-		if k8sPort.Port == int32(cSvc.Status.Realized.FrontendAddress.Port) {
+		if compareServicePortToFrontEnd(&k8sPort, cSvc.Status.Realized.FrontendAddress) {
 			k8sServicePort = &k8sPort
 			k8sServicesFound[serviceKey(*k8sService)] = true
 			break
@@ -4055,14 +4060,16 @@ func validateCiliumSvc(cSvc models.Service, k8sSvcs []v1.Service, k8sEps []v1.En
 }
 
 func validateCiliumSvcLB(cSvc models.Service, lbMap map[string][]string) error {
-	scope := ""
+	var scope string
 	if cSvc.Status.Realized.FrontendAddress.Scope == models.FrontendAddressScopeInternal {
 		scope = "/i"
 	}
-
-	frontendAddress := net.JoinHostPort(
+	frontendAddress := serviceAddressKey(
 		cSvc.Status.Realized.FrontendAddress.IP,
-		strconv.Itoa(int(cSvc.Status.Realized.FrontendAddress.Port))) + scope
+		strconv.Itoa(int(cSvc.Status.Realized.FrontendAddress.Port)),
+		cSvc.Status.Realized.FrontendAddress.Protocol,
+		scope,
+	)
 	bpfBackends, ok := lbMap[frontendAddress]
 	if !ok {
 		return fmt.Errorf("%s bpf lb map entry not found", frontendAddress)
@@ -4099,7 +4106,9 @@ func getK8sEndpointAddresses(ep v1.Endpoints) []*models.BackendAddress {
 }
 
 func addrsEqual(addr1, addr2 *models.BackendAddress) bool {
-	return *addr1.IP == *addr2.IP && addr1.Port == addr2.Port
+	return *addr1.IP == *addr2.IP &&
+		addr1.Port == addr2.Port &&
+		compareProto(addr1.Protocol, addr2.Protocol)
 }
 
 // GenerateNamespaceForTest generates a namespace based off of the current test
@@ -4230,3 +4239,36 @@ 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 891daa9b39..9872a3ee29 100644
--- a/test/helpers/utils.go
+++ b/test/helpers/utils.go
@@ -39,8 +39,11 @@ import (
 	"golang.org/x/sys/unix"
 )
 
-// ensure that our random numbers are seeded differently on each run
-var randGen = rand.NewSafeRand(time.Now().UnixNano())
+var (
+	// ensure that our random numbers are seeded differently on each run
+	randGen              = rand.NewSafeRand(time.Now().UnixNano())
+	runningCiliumVersion string
+)
 
 // IsRunningOnJenkins detects if the currently running Ginkgo application is
 // most likely running in a Jenkins environment. Returns true if certain
@@ -60,6 +63,35 @@ 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)
@@ -218,6 +250,7 @@ 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/DatapathConfiguration.go b/test/k8sT/DatapathConfiguration.go
index ea02487ce0..41a9734009 100644
--- a/test/k8sT/DatapathConfiguration.go
+++ b/test/k8sT/DatapathConfiguration.go
@@ -254,11 +254,10 @@ var _ = Describe("K8sDatapathConfig", func() {
 			Expect(testPodConnectivityAcrossNodes(kubectl)).Should(BeTrue(), "Connectivity test between nodes failed")
 		})
 
-		It("Check vxlan connectivity with per endpoint routes", func() {
-			Skip("Encapsulation mode is not supported with per-endpoint routes")
-
+		It("Check vxlan connectivity with per-endpoint routes", func() {
 			deploymentManager.DeployCilium(map[string]string{
-				"global.autoDirectNodeRoutes": "true",
+				"global.tunnel":                 "vxlan",
+				"global.endpointRoutes.enabled": "true",
 			}, DeployCiliumOptionsAndDNS)
 			Expect(testPodConnectivityAcrossNodes(kubectl)).Should(BeTrue(), "Connectivity test between nodes failed")
 
diff --git a/test/k8sT/Services.go b/test/k8sT/Services.go
index 6101b0028f..934acb661a 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, frontend string, backends []string, svcType, trafficPolicy string) {
+	ciliumAddService := func(id int64, protocol string, 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, frontend, backends, svcType, trafficPolicy)
+			err := kubectl.CiliumServiceAdd(pod, id, protocol, frontend, backends, svcType, trafficPolicy)
 			ExpectWithOffset(1, err).To(BeNil(), "Failed to add cilium service")
 		}
 	}
 
-	ciliumAddServiceOnNode := func(node string, id int64, frontend string, backends []string, svcType, trafficPolicy string) {
+	ciliumAddServiceOnNode := func(node string, id int64, protocol string, 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, frontend, backends, svcType, trafficPolicy)
+		err = kubectl.CiliumServiceAdd(ciliumPod, id, protocol, 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, net.JoinHostPort(demoClusterIPv6, "80"), httpBackends, "ClusterIP", "Cluster")
+				ciliumAddService(10080, "tcp", net.JoinHostPort(demoClusterIPv6, "80"), httpBackends, "ClusterIP", "Cluster")
 				tftpBackends := ciliumIPv6Backends("-l k8s:id=app1,k8s:io.kubernetes.pod.namespace=default", "69")
-				ciliumAddService(10069, net.JoinHostPort(demoClusterIPv6, "69"), tftpBackends, "ClusterIP", "Cluster")
+				ciliumAddService(10069, "udp", 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, net.JoinHostPort(echoClusterIPv6, "80"), httpBackends, "ClusterIP", "Cluster")
+				ciliumAddService(20080, "tcp", net.JoinHostPort(echoClusterIPv6, "80"), httpBackends, "ClusterIP", "Cluster")
 				tftpBackends = ciliumIPv6Backends("-l k8s:name=echo,k8s:io.kubernetes.pod.namespace=default", "69")
-				ciliumAddService(20069, net.JoinHostPort(echoClusterIPv6, "69"), tftpBackends, "ClusterIP", "Cluster")
+				ciliumAddService(20069, "udp", 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]/hello"`, demoClusterIPv6))
+					helpers.CurlFail(`"tftp://[%s]/hell"`, demoClusterIPv6))
 				status.ExpectSuccess("cannot curl to service IP from host")
 			})
 
@@ -460,9 +460,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, net.JoinHostPort(testDSIPv6, "80"), httpBackends, "ClusterIP", "Cluster")
+				ciliumAddService(31080, "tcp", net.JoinHostPort(testDSIPv6, "80"), httpBackends, "ClusterIP", "Cluster")
 				tftpBackends := ciliumIPv6Backends("-l k8s:zgroup=testDS,k8s:io.kubernetes.pod.namespace=default", "69")
-				ciliumAddService(31069, net.JoinHostPort(testDSIPv6, "69"), tftpBackends, "ClusterIP", "Cluster")
+				ciliumAddService(31069, "udp", net.JoinHostPort(testDSIPv6, "69"), tftpBackends, "ClusterIP", "Cluster")
 			})
 
 			AfterAll(func() {
@@ -1365,21 +1365,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, 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")
+				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")
 				// Add service corresponding to IPv6 address of the nodes so that they become
 				// reachable from outside the cluster.
-				ciliumAddServiceOnNode(helpers.K8s1, 31082, net.JoinHostPort(k8s1IPv6, fmt.Sprintf("%d", data.Spec.Ports[0].NodePort)),
+				ciliumAddServiceOnNode(helpers.K8s1, 31082, string(data.Spec.Ports[0].Protocol), net.JoinHostPort(k8s1IPv6, fmt.Sprintf("%d", data.Spec.Ports[0].NodePort)),
 					httpBackends, "NodePort", "Cluster")
-				ciliumAddServiceOnNode(helpers.K8s2, 31082, net.JoinHostPort(k8s2IPv6, fmt.Sprintf("%d", data.Spec.Ports[0].NodePort)),
+				ciliumAddServiceOnNode(helpers.K8s2, 31082, string(data.Spec.Ports[0].Protocol), 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, 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)),
+				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)),
 					tftpBackends, "NodePort", "Cluster")
-				ciliumAddServiceOnNode(helpers.K8s2, 31071, net.JoinHostPort(k8s2IPv6, fmt.Sprintf("%d", data.Spec.Ports[1].NodePort)),
+				ciliumAddServiceOnNode(helpers.K8s2, 31071, string(data.Spec.Ports[1].Protocol), 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 efbd56699a..7602fcf936 100644
--- a/test/k8sT/Updates.go
+++ b/test/k8sT/Updates.go
@@ -142,6 +142,8 @@ 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())
@@ -160,9 +162,6 @@ 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,6 +289,8 @@ 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.
@@ -369,7 +370,7 @@ func InstallAndValidateCiliumUpgrades(kubectl *helpers.Kubectl, oldHelmChartVers
 			if lastCount == -1 {
 				lastCount = currentCount
 			}
-			Expect(lastCount).Should(BeIdenticalTo(currentCount),
+			ExpectWithOffset(1, lastCount).Should(BeIdenticalTo(currentCount),
 				"migrate-svc restart count values do not match")
 		}
 
@@ -379,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)
-		Expect(err).Should(BeNil(), "Test pods are not ready after timeout")
+		ExpectWithOffset(1, err).Should(BeNil(), "Test pods are not ready after timeout")
 
 		_, err = kubectl.CiliumPolicyAction(
 			helpers.DefaultNamespace, l7Policy, helpers.KubectlApply, timeout)
-		Expect(err).Should(BeNil(), "cannot import l7 policy: %v", l7Policy)
+		ExpectWithOffset(1, 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)
-		Expect(err).Should(BeNil(), "migrate-svc-server pods are not ready after timeout")
+		ExpectWithOffset(1, 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)
-		Expect(err).Should(BeNil(), "migrate-svc-client pods are not ready after timeout")
+		ExpectWithOffset(1, err).Should(BeNil(), "migrate-svc-client pods are not ready after timeout")
 
 		validateEndpointsConnection()
 		checkNoInteruptsInSVCFlows()
@@ -498,6 +499,7 @@ 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(
@@ -532,6 +534,7 @@ 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)
diff --git a/test/kubernetes-test.sh b/test/kubernetes-test.sh
index a4a499d0bd..7ab96e18b4 100755
--- a/test/kubernetes-test.sh
+++ b/test/kubernetes-test.sh
@@ -73,6 +73,10 @@ export KUBE_MASTER=192.168.36.11
 export KUBE_MASTER_IP=192.168.36.11
 export KUBE_MASTER_URL="https://192.168.36.11:6443"
 
+echo "Running upstream services conformance tests"
+${HOME}/go/bin/kubetest --provider=local --test \
+  --test_args="--ginkgo.focus=Services.*\[Conformance\].* --e2e-verify-service-account=false --host ${KUBE_MASTER_URL}"
+
 # We currently skip the following tests:
 # should not allow access by TCP when a policy specifies only SCTP
 #  - Cilium does not support SCTP yet
@@ -81,4 +85,6 @@ export KUBE_MASTER_URL="https://192.168.36.11:6443"
 #  - TL;DR Cilium does not allow to specify pod CIDRs as part of the policy
 #    because it conflicts with the pod's security identity.
 #  - More info at https://github.com/cilium/cilium/issues/9209
-${HOME}/go/bin/kubetest --provider=local --test --test_args="--ginkgo.focus=NetworkPolicy.* --e2e-verify-service-account=false --host ${KUBE_MASTER_URL} --ginkgo.skip=(should.ensure.an.IP.overlapping.both.IPBlock.CIDR.and.IPBlock.Except.is.allowed)|(should.allow.egress.access.to.server.in.CIDR.block)|(should.not.allow.access.by.TCP.when.a.policy.specifies.only.SCTP)"
+echo "Running upstream NetworkPolicy tests"
+${HOME}/go/bin/kubetest --provider=local --test \
+  --test_args="--ginkgo.focus=NetworkPolicy.* --e2e-verify-service-account=false --host ${KUBE_MASTER_URL} --ginkgo.skip=(should.ensure.an.IP.overlapping.both.IPBlock.CIDR.and.IPBlock.Except.is.allowed)|(should.allow.egress.access.to.server.in.CIDR.block)|(should.not.allow.access.by.TCP.when.a.policy.specifies.only.SCTP)"
diff --git a/test/provision/manifest/1.19/coredns_deployment.yaml b/test/provision/manifest/1.19/coredns_deployment.yaml
index 39fffbb5bb..80011421a1 100644
--- a/test/provision/manifest/1.19/coredns_deployment.yaml
+++ b/test/provision/manifest/1.19/coredns_deployment.yaml
@@ -77,13 +77,14 @@ data:
         ready
         kubernetes cluster.local in-addr.arpa ip6.arpa {
             pods insecure
-            ttl 0
             fallthrough in-addr.arpa ip6.arpa
+            ttl 30
         }
+        prometheus :9153
         forward . /etc/resolv.conf {
-            max_fails 0
+            max_concurrent 1000
         }
-        prometheus :9153
+        cache 30
         loop
         reload
         loadbalance