Repository: istio/istio (go)

Issue:
prometheus scraping config doesn't support ipv6
```
  - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
    separator: ;
    regex: ([^:]+)(?::\d+)?;(\d+)
    target_label: __address__
    replacement: $1:$2
    action: replace
```

Regex is invalid for ipv6 address, breaking ipv6 scraping

Pull request:
tests: upload WASM plugins to kind-registry to avoid DNS issues in IPv6 clusters
**Please provide a description of this PR:**

Fixes #35915.

This [comment](https://github.com/istio/istio/pull/47978#issuecomment-1823155014) explains why do we have to upload WASM plugins to kind-registry before running tests. Additionally, I had to patch CoreDNS config map, because `kind-registry` is not resolvable in IPv6-only cluster.

First https://github.com/istio/istio/pull/51779 must be merged.

Patch:
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 90f5c9e333..1575937ac3 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,6 +1,6 @@
 {
   "name": "istio build-tools",
-  "image": "gcr.io/istio-testing/build-tools:master-b9a4a433d98c4160b77c77a93be94f2aa07c5c4d",
+  "image": "gcr.io/istio-testing/build-tools:master-971914c666d927bef34865d2437b78860d575065",
   "privileged": true,
   "remoteEnv": {
     "USE_GKE_GCLOUD_AUTH_PLUGIN": "True",
diff --git a/cni/pkg/nodeagent/cni-watcher.go b/cni/pkg/nodeagent/cni-watcher.go
index 7a606afeeb..274ae17975 100644
--- a/cni/pkg/nodeagent/cni-watcher.go
+++ b/cni/pkg/nodeagent/cni-watcher.go
@@ -126,19 +126,19 @@ func (s *CniPluginServer) handleAddEvent(w http.ResponseWriter, req *http.Reques
 	defer req.Body.Close()
 	data, err := io.ReadAll(req.Body)
 	if err != nil {
-		log.Errorf("Failed to read event report from cni plugin: %v", err)
+		log.Errorf("failed to read event report from cni plugin: %v", err)
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
 	msg, err := processAddEvent(data)
 	if err != nil {
-		log.Errorf("Failed to process CNI event payload: %v", err)
+		log.Errorf("failed to process CNI event payload: %v", err)
 		http.Error(w, err.Error(), http.StatusBadRequest)
 		return
 	}
 
 	if err := s.ReconcileCNIAddEvent(req.Context(), msg); err != nil {
-		log.Errorf("Failed to handle add event: %v", err)
+		log.WithLabels("ns", msg.PodNamespace, "name", msg.PodName).Errorf("failed to handle add event: %v", err)
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
diff --git a/cni/pkg/plugin/cnieventclient.go b/cni/pkg/plugin/cnieventclient.go
index baf4af3767..b62a39a008 100644
--- a/cni/pkg/plugin/cnieventclient.go
+++ b/cni/pkg/plugin/cnieventclient.go
@@ -19,6 +19,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"io"
 	"net"
 	"net/http"
 	"time"
@@ -69,15 +70,18 @@ func PushCNIEvent(cniClient CNIEventClient, event *skel.CmdArgs, prevResIps []*c
 	if err != nil {
 		return err
 	}
-	var response *http.Response
-	response, err = cniClient.client.Post(cniClient.url, "application/json", bytes.NewBuffer(eventData))
+	response, err := cniClient.client.Post(cniClient.url, "application/json", bytes.NewBuffer(eventData))
 	if err != nil {
-		return err
+		return fmt.Errorf("failed to send event request: %v", err)
 	}
 	defer response.Body.Close()
 
 	if response.StatusCode != http.StatusOK {
-		return fmt.Errorf("unable to push CNI event, error was %d", response.StatusCode)
+		body, err := io.ReadAll(io.LimitReader(response.Body, 1024*1024))
+		if err != nil {
+			return fmt.Errorf("unable to push CNI event and failed to read body (status code %d): %v", response.StatusCode, err)
+		}
+		return fmt.Errorf("unable to push CNI event (status code %d): %v", response.StatusCode, string(body))
 	}
 
 	return nil
diff --git a/common/.commonfiles.sha b/common/.commonfiles.sha
index 567df701d3..7ac12405a5 100644
--- a/common/.commonfiles.sha
+++ b/common/.commonfiles.sha
@@ -1,1 +1,1 @@
-d668a644804baba70c5d6bd4b7015b30b933c6c2
+827fa4773ee564dd4a22895ab14f22bf4394ae69
diff --git a/common/scripts/setup_env.sh b/common/scripts/setup_env.sh
index b5839c08ca..3b490d81fe 100755
--- a/common/scripts/setup_env.sh
+++ b/common/scripts/setup_env.sh
@@ -75,7 +75,7 @@ fi
 TOOLS_REGISTRY_PROVIDER=${TOOLS_REGISTRY_PROVIDER:-gcr.io}
 PROJECT_ID=${PROJECT_ID:-istio-testing}
 if [[ "${IMAGE_VERSION:-}" == "" ]]; then
-  IMAGE_VERSION=master-b9a4a433d98c4160b77c77a93be94f2aa07c5c4d
+  IMAGE_VERSION=master-971914c666d927bef34865d2437b78860d575065
 fi
 if [[ "${IMAGE_NAME:-}" == "" ]]; then
   IMAGE_NAME=build-tools
diff --git a/istio.deps b/istio.deps
index fad57ef8ba..58efdccd09 100644
--- a/istio.deps
+++ b/istio.deps
@@ -4,13 +4,13 @@
     "name": "PROXY_REPO_SHA",
     "repoName": "proxy",
     "file": "",
-    "lastStableSHA": "5518cd30b6c36aeaee22610f5d3e057c1b74ccb6"
+    "lastStableSHA": "1fd1ca76e8e66e7d3cc68ae7eac6bed681d6d8da"
   },
   {
     "_comment": "",
     "name": "ZTUNNEL_REPO_SHA",
     "repoName": "ztunnel",
     "file": "",
-    "lastStableSHA": "7d5724ab9c872d7c107498b798b085795794111f"
+    "lastStableSHA": "d4ab2ea016bba789d7c6e9d4b817598aa783a71a"
   }
 ]
diff --git a/istioctl/pkg/proxyconfig/proxyconfig.go b/istioctl/pkg/proxyconfig/proxyconfig.go
index 32b625217d..e7f0514699 100644
--- a/istioctl/pkg/proxyconfig/proxyconfig.go
+++ b/istioctl/pkg/proxyconfig/proxyconfig.go
@@ -483,7 +483,7 @@ func allConfigCmd(ctx cli.Context) *cobra.Command {
 	allConfigCmd.PersistentFlags().StringVar(&listenerType, "type", "", "Filter listeners by type field")
 
 	// route
-	allConfigCmd.PersistentFlags().StringVar(&routeName, "name", "", "Filter listeners by route name field")
+	allConfigCmd.PersistentFlags().StringVar(&routeName, "name", "", "Filter routes by route name field")
 
 	return allConfigCmd
 }
diff --git a/istioctl/pkg/writer/ztunnel/configdump/api.go b/istioctl/pkg/writer/ztunnel/configdump/api.go
index 057bcd7282..2b310c384e 100644
--- a/istioctl/pkg/writer/ztunnel/configdump/api.go
+++ b/istioctl/pkg/writer/ztunnel/configdump/api.go
@@ -21,6 +21,7 @@ type Locality struct {
 }
 
 type ZtunnelWorkload struct {
+	UID                   string            `json:"uid"`
 	WorkloadIPs           []string          `json:"workloadIps"`
 	Waypoint              *GatewayAddress   `json:"waypoint,omitempty"`
 	NetworkGateway        *GatewayAddress   `json:"networkGateway,omitempty"`
@@ -57,14 +58,22 @@ type LoadBalancer struct {
 	RoutingPreferences []string `json:"routingPreferences"`
 }
 
+type ZtunnelEndpoint struct {
+	WorkloadUID string            `json:"workloadUid"`
+	Service     string            `json:"service"`
+	Address     string            `json:"address,omitempty"`
+	Port        map[uint16]uint16 `json:"port"`
+}
+
 type ZtunnelService struct {
-	Name         string          `json:"name"`
-	Namespace    string          `json:"namespace"`
-	Hostname     string          `json:"hostname"`
-	Addresses    []string        `json:"vips"`
-	Ports        map[string]int  `json:"ports"`
-	LoadBalancer *LoadBalancer   `json:"loadBalancer"`
-	Waypoint     *GatewayAddress `json:"waypoint"`
+	Name         string                      `json:"name"`
+	Namespace    string                      `json:"namespace"`
+	Hostname     string                      `json:"hostname"`
+	Addresses    []string                    `json:"vips"`
+	Ports        map[string]int              `json:"ports"`
+	LoadBalancer *LoadBalancer               `json:"loadBalancer"`
+	Waypoint     *GatewayAddress             `json:"waypoint"`
+	Endpoints    map[string]*ZtunnelEndpoint `json:"endpoints"`
 }
 
 type PolicyMatch struct {
diff --git a/istioctl/pkg/writer/ztunnel/configdump/services.go b/istioctl/pkg/writer/ztunnel/configdump/services.go
index c3232b1c55..74d7589159 100644
--- a/istioctl/pkg/writer/ztunnel/configdump/services.go
+++ b/istioctl/pkg/writer/ztunnel/configdump/services.go
@@ -46,6 +46,9 @@ func (c *ConfigWriter) PrintServiceSummary(filter ServiceFilter) error {
 	w := c.tabwriter()
 	zDump := c.ztunnelDump
 
+	workloadsByUID := slices.GroupUnique(zDump.Workloads, func(t *ZtunnelWorkload) string {
+		return t.UID
+	})
 	svcs := slices.Filter(zDump.Services, filter.Verify)
 	slices.SortFunc(svcs, func(a, b *ZtunnelService) int {
 		if r := cmp.Compare(a.Namespace, b.Namespace); r != 0 {
@@ -56,16 +59,30 @@ func (c *ConfigWriter) PrintServiceSummary(filter ServiceFilter) error {
 		}
 		return cmp.Compare(a.Hostname, b.Hostname)
 	})
-	fmt.Fprintln(w, "NAMESPACE\tSERVICE NAME\tSERVICE VIP\tWAYPOINT")
+	fmt.Fprintln(w, "NAMESPACE\tSERVICE NAME\tSERVICE VIP\tWAYPOINT\tENDPOINTS")
 
 	for _, svc := range svcs {
 		var ip string
 		if len(svc.Addresses) > 0 {
 			_, ip, _ = strings.Cut(svc.Addresses[0], "/")
 		}
+		allEndpoints := len(svc.Endpoints)
+		healthyEndpoints := 0
+		for _, ep := range svc.Endpoints {
+			w, f := workloadsByUID[ep.WorkloadUID]
+			if !f {
+				continue
+			}
+			if w.Status != "Healthy" {
+				continue
+			}
+			healthyEndpoints++
+		}
+		endpoints := fmt.Sprintf("%d/%d", healthyEndpoints, allEndpoints)
+
 		waypoint := serviceWaypointName(svc, zDump.Services)
-		fmt.Fprintf(w, "%v\t%v\t%v\t%v\n",
-			svc.Namespace, svc.Name, ip, waypoint)
+		fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\n",
+			svc.Namespace, svc.Name, ip, waypoint, endpoints)
 	}
 	return w.Flush()
 }
diff --git a/pilot/pkg/autoregistration/controller.go b/pilot/pkg/autoregistration/controller.go
index 1a70410319..35747afbd8 100644
--- a/pilot/pkg/autoregistration/controller.go
+++ b/pilot/pkg/autoregistration/controller.go
@@ -702,8 +702,12 @@ func workloadEntryFromGroup(name string, proxy *model.Proxy, groupCfg *config.Co
 	if proxy.Metadata.Network != "" {
 		entry.Network = string(proxy.Metadata.Network)
 	}
-	if proxy.Locality != nil {
-		entry.Locality = util.LocalityToString(proxy.Locality)
+	// proxy.Locality can be unset when auto registration takes place, because
+	// its state is not fully initialized. Therefore, we check the bootstrap
+	// node.
+	if proxy.XdsNode != nil && proxy.XdsNode.Locality != nil {
+		entry.Locality = util.LocalityToString(proxy.XdsNode.Locality)
+		log.Infof("Setting Locality: %s for WLE: %s via XdsNode", entry.Locality, name)
 	}
 	if proxy.Metadata.ProxyConfig != nil && proxy.Metadata.ProxyConfig.ReadinessProbe != nil {
 		annotations[status.WorkloadEntryHealthCheckAnnotation] = "true"
diff --git a/pilot/pkg/xds/debug.go b/pilot/pkg/xds/debug.go
index 725a02c291..89cc8c1c7d 100644
--- a/pilot/pkg/xds/debug.go
+++ b/pilot/pkg/xds/debug.go
@@ -48,8 +48,10 @@ import (
 	"istio.io/istio/pkg/config/xds"
 	istiolog "istio.io/istio/pkg/log"
 	"istio.io/istio/pkg/security"
+	"istio.io/istio/pkg/slices"
 	"istio.io/istio/pkg/util/protomarshal"
 	"istio.io/istio/pkg/util/sets"
+	"istio.io/istio/pkg/workloadapi"
 )
 
 var indexTmpl = template.Must(template.New("index").Parse(`<html>
@@ -203,6 +205,7 @@ func (s *DiscoveryServer) AddDebugHandlers(mux, internalMux *http.ServeMux, enab
 	s.addDebugHandler(mux, internalMux, "/debug/sidecarz", "Debug sidecar scope for a proxy", s.sidecarz)
 	s.addDebugHandler(mux, internalMux, "/debug/resourcesz", "Debug support for watched resources", s.resourcez)
 	s.addDebugHandler(mux, internalMux, "/debug/instancesz", "Debug support for service instances", s.instancesz)
+	s.addDebugHandler(mux, internalMux, "/debug/ambientz", "Debug support for ambient", s.ambientz)
 
 	s.addDebugHandler(mux, internalMux, "/debug/authorizationz", "Internal authorization policies", s.authorizationz)
 	s.addDebugHandler(mux, internalMux, "/debug/telemetryz", "Debug Telemetry configuration", s.telemetryz)
@@ -1091,6 +1094,54 @@ func (s *DiscoveryServer) instancesz(w http.ResponseWriter, req *http.Request) {
 	writeJSON(w, instances, req)
 }
 
+func (s *DiscoveryServer) ambientz(w http.ResponseWriter, req *http.Request) {
+	addresses, _ := s.Env.ServiceDiscovery.AddressInformation(nil)
+	res := struct {
+		Workloads []jsonMarshalProto `json:"workloads"`
+		Services  []jsonMarshalProto `json:"services"`
+	}{}
+	// WDS stores IPs as raw byte form. We want to view them as strings, so convert.
+	// This doesn't quite work ideally, since json marshal will write as base64, but its better than nothing
+	rewriteAddress := func(b []byte) []byte {
+		ip, ok := netip.AddrFromSlice(b)
+		if !ok {
+			return b
+		}
+		return []byte(ip.String())
+	}
+	rewriteNetworkAddress := func(b *workloadapi.NetworkAddress) *workloadapi.NetworkAddress {
+		b.Address = rewriteAddress(b.Address)
+		return b
+	}
+	rewriteGatewayAddress := func(b *workloadapi.GatewayAddress) *workloadapi.GatewayAddress {
+		if b == nil {
+			return nil
+		}
+		switch t := b.Destination.(type) {
+		case *workloadapi.GatewayAddress_Address:
+			t.Address = rewriteNetworkAddress(t.Address)
+		}
+		return b
+	}
+	for _, addr := range addresses {
+		switch addr := addr.Type.(type) {
+		case *workloadapi.Address_Workload:
+			w := addr.Workload
+			w.Addresses = slices.Map(w.Addresses, rewriteAddress)
+			w.Waypoint = rewriteGatewayAddress(w.Waypoint)
+			w.NetworkGateway = rewriteGatewayAddress(w.NetworkGateway)
+			res.Workloads = append(res.Workloads, jsonMarshalProto{w})
+		case *workloadapi.Address_Service:
+			s := addr.Service
+			s.Addresses = slices.Map(s.Addresses, rewriteNetworkAddress)
+			s.Waypoint = rewriteGatewayAddress(s.Waypoint)
+			res.Services = append(res.Services, jsonMarshalProto{s})
+		}
+	}
+
+	writeJSON(w, res, req)
+}
+
 func (s *DiscoveryServer) networkz(w http.ResponseWriter, req *http.Request) {
 	if s.Env == nil || s.Env.NetworkManager == nil {
 		return
diff --git a/prow/integ-suite-kind.sh b/prow/integ-suite-kind.sh
index 8c1179c6c1..8210e5c8c6 100755
--- a/prow/integ-suite-kind.sh
+++ b/prow/integ-suite-kind.sh
@@ -171,6 +171,27 @@ fi
 if [[ -z "${SKIP_BUILD:-}" ]]; then
   trace "setup kind registry" setup_kind_registry
   trace "build images" build_images "${PARAMS[*]}"
+
+  # upload WASM plugins to kind-registry
+  crane copy gcr.io/istio-testing/wasm/attributegen:359dcd3a19f109c50e97517fe6b1e2676e870c4d localhost:5000/istio-testing/wasm/attributegen:0.0.1
+  crane copy gcr.io/istio-testing/wasm/header-injector:0.0.1 localhost:5000/istio-testing/wasm/header-injector:0.0.1
+  crane copy gcr.io/istio-testing/wasm/header-injector:0.0.2 localhost:5000/istio-testing/wasm/header-injector:0.0.2
+
+  # Make "kind-registry" resolvable in IPv6 cluster
+  if [[ "$IP_FAMILY" == "ipv6" ]]; then
+    kind_registry_ip=$(docker inspect -f '{{range $k, $v := .NetworkSettings.Networks}}{{if eq $k "kind"}}{{.GlobalIPv6Address}}{{end}}{{end}}' kind-registry)
+    coredns_config=$(kubectl get -oyaml -n=kube-system configmap/coredns)
+    echo "Current CoreDNS config:"
+    echo "${coredns_config}"
+    patched_coredns_config=$(kubectl get -oyaml -n=kube-system configmap/coredns | sed -e '/^ *ready/i\
+        hosts {\
+            '"$kind_registry_ip"' kind-registry.lan\
+            fallthrough\
+        }')
+    echo "Patched CoreDNS config:"
+    echo "${patched_coredns_config}"
+    printf '%s' "${patched_coredns_config}" | kubectl apply -f -
+  fi
 fi
 
 # Run the test target if provided.
diff --git a/releasenotes/notes/51800.yaml b/releasenotes/notes/51800.yaml
new file mode 100644
index 0000000000..7511bc64e9
--- /dev/null
+++ b/releasenotes/notes/51800.yaml
@@ -0,0 +1,10 @@
+apiVersion: release-notes/v2
+kind: bug-fix
+area: traffic-management
+
+issue:
+  - 51800
+
+releaseNotes:
+  - |
+    **Fixed** VirtualMachine WorkloadEntry locality label missing during autoregistration.


Test patch:
diff --git a/cni/pkg/plugin/cnieventclient_test.go b/cni/pkg/plugin/cnieventclient_test.go
index 5def73246c..1cb741c56a 100644
--- a/cni/pkg/plugin/cnieventclient_test.go
+++ b/cni/pkg/plugin/cnieventclient_test.go
@@ -89,7 +89,7 @@ func TestPushCNIAddEventNotOK(t *testing.T) {
 	err := PushCNIEvent(cniC, fakeCmdArgs, []*cniv1.IPConfig{&fakePrevResultIPConfig}, "testpod", "testns")
 
 	assert.Error(t, err)
-	assert.Equal(t, strings.Contains(err.Error(), fmt.Sprintf("unable to push CNI event, error was %d", http.StatusInternalServerError)), true)
+	assert.Equal(t, strings.Contains(err.Error(), fmt.Sprintf("unable to push CNI event (status code %d)", http.StatusInternalServerError)), true)
 }
 
 func TestPushCNIAddEventGoodPayload(t *testing.T) {
diff --git a/pilot/pkg/autoregistration/controller_test.go b/pilot/pkg/autoregistration/controller_test.go
index 63bce1447b..db8da54a60 100644
--- a/pilot/pkg/autoregistration/controller_test.go
+++ b/pilot/pkg/autoregistration/controller_test.go
@@ -209,15 +209,15 @@ func TestAutoregistrationLifecycle(t *testing.T) {
 
 	var p1conn1, p1conn2 *fakeConn
 	p := fakeProxy("1.2.3.4", wgA, "nw1", "sa-a")
-	p.Locality = n.Locality
+	p.XdsNode = n
 
 	var p2conn1 *fakeConn
 	p2 := fakeProxy("1.2.3.4", wgA, "nw2", "sa-a")
-	p2.Locality = n.Locality
+	p2.XdsNode = n
 
 	var p3conn1 *fakeConn
 	p3 := fakeProxy("1.2.3.5", wgA, "nw1", "sa-a")
-	p3.Locality = n.Locality
+	p3.XdsNode = n
 
 	t.Run("initial registration", func(t *testing.T) {
 		// simply make sure the entry exists after connecting
@@ -432,7 +432,6 @@ func TestWorkloadEntryFromGroup(t *testing.T) {
 	proxy := fakeProxy("10.0.0.1", group, "nw1", "sa")
 	proxy.Labels[model.LocalityLabel] = "rgn2/zone2/subzone2"
 	proxy.XdsNode = fakeNode("rgn2", "zone2", "subzone2")
-	proxy.Locality = proxy.XdsNode.Locality
 
 	wantLabels := map[string]string{
 		"app":   "a",   // from WorkloadEntry template
diff --git a/pkg/test/framework/components/echo/calloptions.go b/pkg/test/framework/components/echo/calloptions.go
index ceb92ba029..098e865c8b 100644
--- a/pkg/test/framework/components/echo/calloptions.go
+++ b/pkg/test/framework/components/echo/calloptions.go
@@ -18,6 +18,7 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
+	"net/netip"
 	"time"
 
 	wrappers "google.golang.org/protobuf/types/known/wrapperspb"
@@ -199,11 +200,13 @@ func (o CallOptions) GetHost() string {
 		return o.To.Config().DefaultHostHeader
 	}
 
-	// Next, if the Address was manually specified use it as the Host.
+	// Next, if the Address was manually specified use it as the Host. If it is an IP, leave it so Go can handle the logic to decide
+	// how to format it (whether to add brackets, port, etc)
 	if len(o.Address) > 0 {
-		return o.Address
+		if _, err := netip.ParseAddr(o.Address); err != nil {
+			return o.Address
+		}
 	}
-
 	// Finally, use the target's FQDN.
 	if o.To != nil {
 		return o.To.Config().ClusterLocalFQDN()
diff --git a/pkg/test/framework/components/registryredirector/registry_redirector_server.yaml b/pkg/test/framework/components/registryredirector/registry_redirector_server.yaml
index 9503180471..11b8fe1bad 100644
--- a/pkg/test/framework/components/registryredirector/registry_redirector_server.yaml
+++ b/pkg/test/framework/components/registryredirector/registry_redirector_server.yaml
@@ -40,7 +40,7 @@ spec:
         app: registry-redirector
     spec:
       containers:
-      - image: {{ or .Image "gcr.io/istio-testing/fake-registry:1.3"}}
+      - image: {{ or .Image "gcr.io/istio-testing/fake-registry:1.4"}}
         name: registry-redirector
         {{- if .TargetRegistry }}
         args: ["--registry", {{ .TargetRegistry }}, "--scheme", {{ or .Scheme "https" }}]
diff --git a/tests/integration/ambient/baseline_test.go b/tests/integration/ambient/baseline_test.go
index a96223ed88..4959513595 100644
--- a/tests/integration/ambient/baseline_test.go
+++ b/tests/integration/ambient/baseline_test.go
@@ -21,6 +21,7 @@ import (
 	"context"
 	"fmt"
 	"net/http"
+	"net/netip"
 	"path/filepath"
 	"strconv"
 	"strings"
@@ -54,6 +55,7 @@ import (
 	"istio.io/istio/pkg/test/framework/components/istioctl"
 	"istio.io/istio/pkg/test/framework/components/namespace"
 	"istio.io/istio/pkg/test/framework/components/prometheus"
+	"istio.io/istio/pkg/test/framework/label"
 	"istio.io/istio/pkg/test/framework/resource/config/apply"
 	"istio.io/istio/pkg/test/framework/resource/config/cleanup"
 	kubetest "istio.io/istio/pkg/test/kube"
@@ -1284,86 +1286,88 @@ spec:
 }
 
 func TestL7JWT(t *testing.T) {
-	framework.NewTest(t).Run(func(t framework.TestContext) {
-		applyDrainingWorkaround(t)
-		runTestToServiceWaypoint(t, func(t framework.TestContext, src echo.Instance, dst echo.Instance, opt echo.CallOptions) {
-			if opt.Scheme != scheme.HTTP {
-				return
-			}
-			// Ensure we don't get stuck on old connections with old RBAC rules. This causes 45s test times
-			// due to draining.
-			opt.NewConnectionPerRequest = true
+	framework.NewTest(t).
+		Label(label.IPv4). // https://github.com/istio/istio/issues/35835
+		Run(func(t framework.TestContext) {
+			applyDrainingWorkaround(t)
+			runTestToServiceWaypoint(t, func(t framework.TestContext, src echo.Instance, dst echo.Instance, opt echo.CallOptions) {
+				if opt.Scheme != scheme.HTTP {
+					return
+				}
+				// Ensure we don't get stuck on old connections with old RBAC rules. This causes 45s test times
+				// due to draining.
+				opt.NewConnectionPerRequest = true
 
-			switch {
-			case dst.Config().HasWorkloadAddressedWaypointProxy() && !dst.Config().HasServiceAddressedWaypointProxy():
-				// send traffic to the workload instead of the service so it will redirect to the WL waypoint
-				opt.Address = dst.MustWorkloads().Addresses()[0]
-				opt.Port = echo.Port{ServicePort: ports.All().MustForName(opt.Port.Name).WorkloadPort}
-				if src == dst {
-					t.Skip("self call is not captured, L7 features will not work")
+				switch {
+				case dst.Config().HasWorkloadAddressedWaypointProxy() && !dst.Config().HasServiceAddressedWaypointProxy():
+					// send traffic to the workload instead of the service so it will redirect to the WL waypoint
+					opt.Address = dst.MustWorkloads().Addresses()[0]
+					opt.Port = echo.Port{ServicePort: ports.All().MustForName(opt.Port.Name).WorkloadPort}
+					if src == dst {
+						t.Skip("self call is not captured, L7 features will not work")
+					}
 				}
-			}
 
-			t.ConfigIstio().New().EvalFile(apps.Namespace.Name(), map[string]any{
-				param.Namespace.String(): apps.Namespace.Name(),
-				"Services":               apps.ServiceAddressedWaypoint,
-				"To":                     dst,
-			}, "testdata/requestauthn/waypoint-jwt.yaml.tmpl").ApplyOrFail(t)
+				t.ConfigIstio().New().EvalFile(apps.Namespace.Name(), map[string]any{
+					param.Namespace.String(): apps.Namespace.Name(),
+					"Services":               apps.ServiceAddressedWaypoint,
+					"To":                     dst,
+				}, "testdata/requestauthn/waypoint-jwt.yaml.tmpl").ApplyOrFail(t)
 
-			t.NewSubTest("deny without token").Run(func(t framework.TestContext) {
-				opt := opt.DeepCopy()
-				opt.HTTP.Path = "/"
-				opt.Check = check.Status(http.StatusForbidden)
-				src.CallOrFail(t, opt)
-			})
+				t.NewSubTest("deny without token").Run(func(t framework.TestContext) {
+					opt := opt.DeepCopy()
+					opt.HTTP.Path = "/"
+					opt.Check = check.Status(http.StatusForbidden)
+					src.CallOrFail(t, opt)
+				})
 
-			t.NewSubTest("allow with sub-1 token").Run(func(t framework.TestContext) {
-				opt := opt.DeepCopy()
-				opt.HTTP.Path = "/"
-				opt.HTTP.Headers = headers.New().
-					WithAuthz(jwt.TokenIssuer1).
-					Build()
-				opt.Check = check.OK()
-			})
+				t.NewSubTest("allow with sub-1 token").Run(func(t framework.TestContext) {
+					opt := opt.DeepCopy()
+					opt.HTTP.Path = "/"
+					opt.HTTP.Headers = headers.New().
+						WithAuthz(jwt.TokenIssuer1).
+						Build()
+					opt.Check = check.OK()
+				})
 
-			t.NewSubTest("deny with sub-3 token due to ignored RequestAuthentication").Run(func(t framework.TestContext) {
-				opt := opt.DeepCopy()
-				opt.HTTP.Path = "/"
-				opt.HTTP.Headers = headers.New().
-					WithAuthz(jwt.TokenIssuer3).
-					Build()
-				opt.Check = check.Status(http.StatusUnauthorized)
-				src.CallOrFail(t, opt)
-			})
+				t.NewSubTest("deny with sub-3 token due to ignored RequestAuthentication").Run(func(t framework.TestContext) {
+					opt := opt.DeepCopy()
+					opt.HTTP.Path = "/"
+					opt.HTTP.Headers = headers.New().
+						WithAuthz(jwt.TokenIssuer3).
+						Build()
+					opt.Check = check.Status(http.StatusUnauthorized)
+					src.CallOrFail(t, opt)
+				})
 
-			t.NewSubTest("deny with sub-2 token").Run(func(t framework.TestContext) {
-				opt := opt.DeepCopy()
-				opt.HTTP.Path = "/"
-				opt.HTTP.Headers = headers.New().
-					WithAuthz(jwt.TokenIssuer2).
-					Build()
-				opt.Check = check.Status(http.StatusForbidden)
-				src.CallOrFail(t, opt)
-			})
+				t.NewSubTest("deny with sub-2 token").Run(func(t framework.TestContext) {
+					opt := opt.DeepCopy()
+					opt.HTTP.Path = "/"
+					opt.HTTP.Headers = headers.New().
+						WithAuthz(jwt.TokenIssuer2).
+						Build()
+					opt.Check = check.Status(http.StatusForbidden)
+					src.CallOrFail(t, opt)
+				})
 
-			t.NewSubTest("deny with expired token").Run(func(t framework.TestContext) {
-				opt := opt.DeepCopy()
-				opt.HTTP.Path = "/"
-				opt.HTTP.Headers = headers.New().
-					WithAuthz(jwt.TokenExpired).
-					Build()
-				opt.Check = check.Status(http.StatusUnauthorized)
-				src.CallOrFail(t, opt)
-			})
+				t.NewSubTest("deny with expired token").Run(func(t framework.TestContext) {
+					opt := opt.DeepCopy()
+					opt.HTTP.Path = "/"
+					opt.HTTP.Headers = headers.New().
+						WithAuthz(jwt.TokenExpired).
+						Build()
+					opt.Check = check.Status(http.StatusUnauthorized)
+					src.CallOrFail(t, opt)
+				})
 
-			t.NewSubTest("allow healthz").Run(func(t framework.TestContext) {
-				opt := opt.DeepCopy()
-				opt.HTTP.Path = "/healthz"
-				opt.Check = check.OK()
-				src.CallOrFail(t, opt)
+				t.NewSubTest("allow healthz").Run(func(t framework.TestContext) {
+					opt := opt.DeepCopy()
+					opt.HTTP.Path = "/healthz"
+					opt.Check = check.OK()
+					src.CallOrFail(t, opt)
+				})
 			})
 		})
-	})
 }
 
 func applyDrainingWorkaround(t framework.TestContext) {
@@ -1703,17 +1707,39 @@ spec:
         host: "{{.Destination}}"
 `).ApplyOrFail(t)
 
+			// TODO(https://github.com/istio/istio/issues/51747) use a single SE instead of one for v4 and one for v6
 			cfg := config.YAML(`
 {{ $to := .To }}
 apiVersion: networking.istio.io/v1beta1
 kind: ServiceEntry
 metadata:
-  name: test-se
+  name: test-se-v4
 spec:
   hosts:
-  - serviceentry.istio.io # not used
+  - dummy-v4.example.com
   addresses:
-  - 111.111.222.222
+  - 240.240.240.255
+  ports:
+  - number: 80
+    name: http
+    protocol: HTTP
+  resolution: {{.Resolution}}
+  location: {{.Location}}
+  endpoints:
+  # we send directly to a Pod IP here. This is essentially headless
+  - address: {{.IngressIp}} # TODO won't work with DNS resolution tests
+    ports:
+      http: {{.IngressHttpPort}}
+---
+apiVersion: networking.istio.io/v1beta1
+kind: ServiceEntry
+metadata:
+  name: test-se-v6
+spec:
+  hosts:
+  - dummy-v6.example.com
+  addresses:
+  - 2001:2::f0f0:255
   ports:
   - number: 80
     name: http
@@ -1724,14 +1750,17 @@ spec:
   # we send directly to a Pod IP here. This is essentially headless
   - address: {{.IngressIp}} # TODO won't work with DNS resolution tests
     ports:
-      http: {{.IngressHttpPort}}`).
+      http: {{.IngressHttpPort}}
+---
+`).
 				WithParams(param.Params{}.SetWellKnown(param.Namespace, apps.Namespace))
 
+			v4, v6 := getSupportedIPFamilies(t)
 			ips, ports := istio.DefaultIngressOrFail(t, t).HTTPAddresses()
 			for _, tc := range testCases {
 				tc := tc
 				for i, ip := range ips {
-					t.NewSubTestf("%s %s %s", tc.location, tc.resolution, ip).Run(func(t framework.TestContext) {
+					t.NewSubTestf("%s %s %d", tc.location, tc.resolution, i).Run(func(t framework.TestContext) {
 						echotest.
 							New(t, apps.All).
 							// TODO eventually we can do this for uncaptured -> l7
@@ -1747,12 +1776,22 @@ spec:
 							})).
 							Run(func(t framework.TestContext, from echo.Instance, to echo.Target) {
 								// TODO validate L7 processing/some headers indicating we reach the svc we wanted
-								from.CallOrFail(t, echo.CallOptions{
-									Address: "111.111.222.222",
-									Port:    to.PortForName("http"),
-									// If request is sent before service is processed it will hit 10s timeout, so fail faster
-									Timeout: time.Millisecond * 500,
-								})
+								if v4 {
+									from.CallOrFail(t, echo.CallOptions{
+										Address: "240.240.240.255",
+										Port:    to.PortForName("http"),
+										// If request is sent before service is processed it will hit 10s timeout, so fail faster
+										Timeout: time.Millisecond * 500,
+									})
+								}
+								if v6 {
+									from.CallOrFail(t, echo.CallOptions{
+										Address: "2001:2::f0f0:255",
+										Port:    to.PortForName("http"),
+										// If request is sent before service is processed it will hit 10s timeout, so fail faster
+										Timeout: time.Millisecond * 500,
+									})
+								}
 							})
 					})
 				}
@@ -1760,6 +1799,23 @@ spec:
 		})
 }
 
+func getSupportedIPFamilies(t framework.TestContext) (v4 bool, v6 bool) {
+	addrs := apps.Captured.WorkloadsOrFail(t).Addresses()
+	for _, a := range addrs {
+		ip, err := netip.ParseAddr(a)
+		assert.NoError(t, err)
+		if ip.Is4() {
+			v4 = true
+		} else if ip.Is6() {
+			v6 = true
+		}
+	}
+	if !v4 && !v6 {
+		t.Fatalf("pod is neither v4 nor v6? %v", addrs)
+	}
+	return
+}
+
 func TestServiceEntrySelectsWorkloadEntry(t *testing.T) {
 	framework.NewTest(t).
 		Run(func(t framework.TestContext) {
diff --git a/tests/integration/ambient/main_test.go b/tests/integration/ambient/main_test.go
index 093a3ca058..a7f36de106 100644
--- a/tests/integration/ambient/main_test.go
+++ b/tests/integration/ambient/main_test.go
@@ -35,7 +35,6 @@ import (
 	"istio.io/istio/pkg/test/framework/components/istio"
 	"istio.io/istio/pkg/test/framework/components/namespace"
 	"istio.io/istio/pkg/test/framework/components/prometheus"
-	"istio.io/istio/pkg/test/framework/label"
 	"istio.io/istio/pkg/test/framework/resource"
 	"istio.io/istio/pkg/test/framework/resource/config/apply"
 	"istio.io/istio/pkg/test/scopes"
@@ -93,7 +92,6 @@ func TestMain(m *testing.M) {
 	framework.
 		NewSuite(m).
 		RequireMinVersion(24).
-		Label(label.IPv4). // https://github.com/istio/istio/issues/41008
 		Setup(func(t resource.Context) error {
 			t.Settings().Ambient = true
 			return nil
diff --git a/tests/integration/telemetry/api/customize_metrics_test.go b/tests/integration/telemetry/api/customize_metrics_test.go
index 7a5ec72ffe..3fef87dd8c 100644
--- a/tests/integration/telemetry/api/customize_metrics_test.go
+++ b/tests/integration/telemetry/api/customize_metrics_test.go
@@ -27,7 +27,6 @@ import (
 	"istio.io/istio/pkg/test/framework"
 	"istio.io/istio/pkg/test/framework/components/echo"
 	"istio.io/istio/pkg/test/framework/components/prometheus"
-	"istio.io/istio/pkg/test/framework/label"
 	"istio.io/istio/pkg/test/util/retry"
 	util "istio.io/istio/tests/integration/telemetry"
 )
@@ -40,7 +39,6 @@ const (
 
 func TestCustomizeMetrics(t *testing.T) {
 	framework.NewTest(t).
-		Label(label.IPv4). // https://github.com/istio/istio/issues/35835
 		Run(func(t framework.TestContext) {
 			setupWasmExtension(t)
 			t.ConfigIstio().YAML(apps.Namespace.Name(), `
@@ -113,8 +111,7 @@ spec:
 }
 
 func setupWasmExtension(t framework.TestContext) {
-	proxySHA := "359dcd3a19f109c50e97517fe6b1e2676e870c4d"
-	attrGenImageURL := fmt.Sprintf("oci://%v/istio-testing/wasm/attributegen:%v", registry.Address(), proxySHA)
+	attrGenImageURL := fmt.Sprintf("oci://%v/istio-testing/wasm/attributegen:0.0.1", registry.Address())
 	args := map[string]any{
 		"AttributeGenURL": attrGenImageURL,
 	}
diff --git a/tests/integration/telemetry/api/registry_setup_test.go b/tests/integration/telemetry/api/registry_setup_test.go
index b8d8f4a313..ab4eee0979 100644
--- a/tests/integration/telemetry/api/registry_setup_test.go
+++ b/tests/integration/telemetry/api/registry_setup_test.go
@@ -35,7 +35,9 @@ const (
 
 func testRegistrySetup(ctx resource.Context) (err error) {
 	registry, err = registryredirector.New(ctx, registryredirector.Config{
-		Cluster: ctx.AllClusters().Default(),
+		Cluster:        ctx.AllClusters().Default(),
+		TargetRegistry: "kind-registry:5000",
+		Scheme:         "http",
 	})
 	if err != nil {
 		return
diff --git a/tests/integration/telemetry/api/setup_test.go b/tests/integration/telemetry/api/setup_test.go
index 3d277f2078..2e7bd1ae12 100644
--- a/tests/integration/telemetry/api/setup_test.go
+++ b/tests/integration/telemetry/api/setup_test.go
@@ -52,8 +52,6 @@ var (
 func TestMain(m *testing.M) {
 	framework.NewSuite(m).
 		Label(label.CustomSetup).
-		// TODO: Remove this restriction once we validate our prometheus helm chart version is high enough
-		Label(label.IPv4). // https://github.com/istio/istio/issues/35915
 		Setup(istio.Setup(&ist, setupConfig)).
 		Setup(func(ctx resource.Context) error {
 			i, err := istio.Get(ctx)
diff --git a/tests/integration/telemetry/api/stats_test.go b/tests/integration/telemetry/api/stats_test.go
index dbcc4404f2..88f2c8d5d0 100644
--- a/tests/integration/telemetry/api/stats_test.go
+++ b/tests/integration/telemetry/api/stats_test.go
@@ -38,7 +38,6 @@ import (
 	"istio.io/istio/pkg/test/framework/components/echo/common/ports"
 	"istio.io/istio/pkg/test/framework/components/echo/match"
 	"istio.io/istio/pkg/test/framework/components/prometheus"
-	"istio.io/istio/pkg/test/framework/label"
 	"istio.io/istio/pkg/test/framework/resource/config/apply"
 	"istio.io/istio/pkg/test/util/retry"
 	util "istio.io/istio/tests/integration/telemetry"
@@ -470,7 +469,6 @@ func ValidateBucket(cluster cluster.Cluster, prom prometheus.Instance, sourceApp
 // Kiali depends on these metrics
 func TestGRPCCountMetrics(t *testing.T) {
 	framework.NewTest(t).
-		Label(label.IPv4). // https://github.com/istio/istio/issues/35835
 		Run(func(t framework.TestContext) {
 			// Metrics to be queried and tested
 			metrics := []string{"istio_request_messages_total", "istio_response_messages_total"}