m/node: move clusternet under network

Move the clusternet network side under the core/network umbrella and
rename it to overlay. Also split out types.go into the ipam package
to get the overlay package out of a lot of dependents which only import
it for the Prefixes type which should be part of the ipam package.

This is a clean move with no functional changes intended yet, these
will be stacked on top.

Change-Id: I6a6a6964af9d608f9ec3bf75b386c010cfff1df4
Reviewed-on: https://review.monogon.dev/c/monogon/+/4500
Tested-by: Jenkins CI
Reviewed-by: Jan Schär <jan@monogon.tech>
diff --git a/metropolis/node/core/BUILD.bazel b/metropolis/node/core/BUILD.bazel
index 3d92543..db96103 100644
--- a/metropolis/node/core/BUILD.bazel
+++ b/metropolis/node/core/BUILD.bazel
@@ -24,13 +24,13 @@
         "//go/logging",
         "//metropolis/node",
         "//metropolis/node/core/cluster",
-        "//metropolis/node/core/clusternet",
         "//metropolis/node/core/devmgr",
         "//metropolis/node/core/localstorage",
         "//metropolis/node/core/localstorage/declarative",
         "//metropolis/node/core/metrics",
         "//metropolis/node/core/mgmt",
         "//metropolis/node/core/network",
+        "//metropolis/node/core/network/ipam",
         "//metropolis/node/core/productinfo",
         "//metropolis/node/core/roleserve",
         "//metropolis/node/core/rpc/resolver",
diff --git a/metropolis/node/core/main.go b/metropolis/node/core/main.go
index df9dd78..5c4ec33 100644
--- a/metropolis/node/core/main.go
+++ b/metropolis/node/core/main.go
@@ -12,12 +12,12 @@
 
 	"source.monogon.dev/go/logging"
 	"source.monogon.dev/metropolis/node/core/cluster"
-	"source.monogon.dev/metropolis/node/core/clusternet"
 	"source.monogon.dev/metropolis/node/core/devmgr"
 	"source.monogon.dev/metropolis/node/core/localstorage"
 	"source.monogon.dev/metropolis/node/core/localstorage/declarative"
 	"source.monogon.dev/metropolis/node/core/metrics"
 	"source.monogon.dev/metropolis/node/core/network"
+	"source.monogon.dev/metropolis/node/core/network/ipam"
 	"source.monogon.dev/metropolis/node/core/productinfo"
 	"source.monogon.dev/metropolis/node/core/roleserve"
 	"source.monogon.dev/metropolis/node/core/rpc/resolver"
@@ -121,7 +121,7 @@
 	}
 
 	metrics.CoreRegistry.MustRegister(dns.MetricsRegistry)
-	var podNetwork memory.Value[*clusternet.Prefixes]
+	var podNetwork memory.Value[*ipam.Prefixes]
 	networkSvc := network.New(nil, []string{"hosts", "kubernetes"}, &podNetwork)
 	networkSvc.DHCPVendorClassID = "dev.monogon.metropolis.node.v1"
 	timeSvc := timesvc.New()
diff --git a/metropolis/node/core/network/BUILD.bazel b/metropolis/node/core/network/BUILD.bazel
index b1a5e41..bec2f19 100644
--- a/metropolis/node/core/network/BUILD.bazel
+++ b/metropolis/node/core/network/BUILD.bazel
@@ -16,9 +16,9 @@
         "//go/algorithm/toposort",
         "//go/logging",
         "//metropolis/node",
-        "//metropolis/node/core/clusternet",
         "//metropolis/node/core/network/dhcp4c",
         "//metropolis/node/core/network/dhcp4c/callback",
+        "//metropolis/node/core/network/ipam",
         "//metropolis/node/core/network/workloads",
         "//metropolis/node/core/productinfo",
         "//osbase/event",
diff --git a/metropolis/node/core/network/ipam/BUILD.bazel b/metropolis/node/core/network/ipam/BUILD.bazel
new file mode 100644
index 0000000..4f761d0
--- /dev/null
+++ b/metropolis/node/core/network/ipam/BUILD.bazel
@@ -0,0 +1,9 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "ipam",
+    srcs = ["ipam.go"],
+    importpath = "source.monogon.dev/metropolis/node/core/network/ipam",
+    visibility = ["//visibility:public"],
+    deps = ["//metropolis/proto/common"],
+)
diff --git a/metropolis/node/core/clusternet/types.go b/metropolis/node/core/network/ipam/ipam.go
similarity index 86%
rename from metropolis/node/core/clusternet/types.go
rename to metropolis/node/core/network/ipam/ipam.go
index d1f2b06..74555ee 100644
--- a/metropolis/node/core/clusternet/types.go
+++ b/metropolis/node/core/network/ipam/ipam.go
@@ -1,7 +1,9 @@
 // Copyright The Monogon Project Authors.
 // SPDX-License-Identifier: Apache-2.0
 
-package clusternet
+// Package ipam is just a stub right now which contains a type for
+// describing prefix lists.
+package ipam
 
 import (
 	"net/netip"
@@ -15,7 +17,7 @@
 // Cluster Networking mesh.
 type Prefixes []netip.Prefix
 
-func (p *Prefixes) proto() (res []*cpb.NodeClusterNetworking_Prefix) {
+func (p *Prefixes) Proto() (res []*cpb.NodeClusterNetworking_Prefix) {
 	for _, prefix := range *p {
 		res = append(res, &cpb.NodeClusterNetworking_Prefix{
 			Cidr: prefix.String(),
diff --git a/metropolis/node/core/network/main.go b/metropolis/node/core/network/main.go
index 623e62f..276e59e 100644
--- a/metropolis/node/core/network/main.go
+++ b/metropolis/node/core/network/main.go
@@ -17,9 +17,9 @@
 	"github.com/vishvananda/netlink"
 
 	"source.monogon.dev/metropolis/node"
-	"source.monogon.dev/metropolis/node/core/clusternet"
 	"source.monogon.dev/metropolis/node/core/network/dhcp4c"
 	dhcpcb "source.monogon.dev/metropolis/node/core/network/dhcp4c/callback"
+	"source.monogon.dev/metropolis/node/core/network/ipam"
 	"source.monogon.dev/metropolis/node/core/network/workloads"
 	"source.monogon.dev/osbase/event"
 	"source.monogon.dev/osbase/event/memory"
@@ -72,7 +72,7 @@
 // If dnsHandlerNames is non-nil, DNS handlers with these names must be set
 // on the DNS service with s.DNS.SetHandler. When serving DNS queries, they
 // will be tried in the order they appear here before forwarding.
-func New(staticConfig *netpb.Net, dnsHandlerNames []string, ipamPrefixSrc event.Value[*clusternet.Prefixes]) *Service {
+func New(staticConfig *netpb.Net, dnsHandlerNames []string, ipamPrefixSrc event.Value[*ipam.Prefixes]) *Service {
 	dnsSvc := dns.New(slices.Concat(dnsHandlerNames, []string{"forward"}))
 	dnsForward := forward.New()
 	dnsSvc.SetHandler("forward", dnsForward)
@@ -271,7 +271,7 @@
 		Type:     nftables.ChainTypeNAT,
 	})
 	// SNAT/Masquerade all traffic coming from pod interface (identified by
-	// group) not going to another pod, either local or over clusternet.
+	// group) not going to another pod, either local or over the overlay.
 	// Will need to be changed when we support L3 attachments (BGP, ...).
 	s.nftConn.AddRule(&nftables.Rule{
 		Table: s.natTable,
@@ -297,11 +297,11 @@
 				Register: 8,
 				Data:     binaryutil.NativeEndian.PutUint32(node.LinkGroupK8sPod),
 			},
-			// Check if outgoing interface isn't clusternet
+			// Check if outgoing interface is not part of the overlay
 			&expr.Cmp{
 				Op:       expr.CmpOpNeq,
 				Register: 8,
-				Data:     binaryutil.NativeEndian.PutUint32(node.LinkGroupClusternet),
+				Data:     binaryutil.NativeEndian.PutUint32(node.LinkGroupOverlay),
 			},
 			&expr.Masq{},
 		},
diff --git a/metropolis/node/core/clusternet/BUILD.bazel b/metropolis/node/core/network/overlay/BUILD.bazel
similarity index 79%
rename from metropolis/node/core/clusternet/BUILD.bazel
rename to metropolis/node/core/network/overlay/BUILD.bazel
index 20a3fd0..4054127 100644
--- a/metropolis/node/core/clusternet/BUILD.bazel
+++ b/metropolis/node/core/network/overlay/BUILD.bazel
@@ -2,19 +2,19 @@
 load("//osbase/test/ktest:ktest.bzl", "k_test")
 
 go_library(
-    name = "clusternet",
+    name = "overlay",
     srcs = [
-        "clusternet.go",
-        "types.go",
+        "overlay.go",
         "wireguard.go",
     ],
-    importpath = "source.monogon.dev/metropolis/node/core/clusternet",
+    importpath = "source.monogon.dev/metropolis/node/core/network/overlay",
     visibility = ["//visibility:public"],
     deps = [
         "//metropolis/node",
         "//metropolis/node/core/curator/proto/api",
         "//metropolis/node/core/curator/watcher",
         "//metropolis/node/core/localstorage",
+        "//metropolis/node/core/network/ipam",
         "//metropolis/proto/common",
         "//osbase/event",
         "//osbase/supervisor",
@@ -26,14 +26,15 @@
 )
 
 go_test(
-    name = "clusternet_test",
-    srcs = ["clusternet_test.go"],
-    embed = [":clusternet"],
+    name = "overlay_test",
+    srcs = ["overlay_test.go"],
+    embed = [":overlay"],
     deps = [
         "//metropolis/node",
         "//metropolis/node/core/curator/proto/api",
         "//metropolis/node/core/localstorage",
         "//metropolis/node/core/localstorage/declarative",
+        "//metropolis/node/core/network/ipam",
         "//metropolis/proto/common",
         "//metropolis/test/util",
         "//osbase/event/memory",
@@ -46,5 +47,5 @@
 k_test(
     name = "ktest",
     cmdline = "ramdisk_size=128",
-    tester = ":clusternet_test",
+    tester = ":overlay_test",
 )
diff --git a/metropolis/node/core/clusternet/clusternet.go b/metropolis/node/core/network/overlay/overlay.go
similarity index 93%
rename from metropolis/node/core/clusternet/clusternet.go
rename to metropolis/node/core/network/overlay/overlay.go
index 6ea53f2..c7979cc 100644
--- a/metropolis/node/core/clusternet/clusternet.go
+++ b/metropolis/node/core/network/overlay/overlay.go
@@ -1,7 +1,7 @@
 // Copyright The Monogon Project Authors.
 // SPDX-License-Identifier: Apache-2.0
 
-// Package clusternet implements a Cluster Networking mesh service running on all
+// Package overlay implements a Cluster Networking mesh service running on all
 // Metropolis nodes.
 //
 // The mesh is based on wireguard and a centralized configuration store in the
@@ -22,7 +22,7 @@
 // Second, we have two hardcoded/purpose-specific sources of prefixes:
 //  1. Pod networking node prefixes from the kubelet
 //  2. The host's external IP address (as a /32) from the network service.
-package clusternet
+package overlay
 
 import (
 	"context"
@@ -37,6 +37,7 @@
 	"source.monogon.dev/metropolis/node"
 	"source.monogon.dev/metropolis/node/core/curator/watcher"
 	"source.monogon.dev/metropolis/node/core/localstorage"
+	"source.monogon.dev/metropolis/node/core/network/ipam"
 	"source.monogon.dev/osbase/event"
 	"source.monogon.dev/osbase/supervisor"
 
@@ -59,7 +60,7 @@
 	// LocalKubernetesPodNetwork is an event.Value watched for prefixes that should
 	// be announced into the mesh. This is to be Set by the Kubernetes service once
 	// it knows about the local node's IPAM address assignment.
-	LocalKubernetesPodNetwork event.Value[*Prefixes]
+	LocalKubernetesPodNetwork event.Value[*ipam.Prefixes]
 	// Network service used to get the local node's IP address to submit it as a /32.
 	Network event.Value[*node.NetStatus]
 
@@ -83,7 +84,7 @@
 
 	supervisor.Logger(ctx).Infof("Wireguard setup complete, starting updaters...")
 
-	kubeC := make(chan *Prefixes)
+	kubeC := make(chan *ipam.Prefixes)
 	netC := make(chan *node.NetStatus)
 	if err := supervisor.RunGroup(ctx, map[string]supervisor.Runnable{
 		"source-kubernetes": event.Pipe(s.LocalKubernetesPodNetwork, kubeC),
@@ -105,11 +106,11 @@
 
 // push is the sub-runnable responsible for letting the Curator know about what
 // prefixes that are originated by this node.
-func (s *Service) push(ctx context.Context, kubeC chan *Prefixes, netC chan *node.NetStatus) error {
+func (s *Service) push(ctx context.Context, kubeC chan *ipam.Prefixes, netC chan *node.NetStatus) error {
 	supervisor.Signal(ctx, supervisor.SignalHealthy)
 
-	var kubePrefixes *Prefixes
-	var prevKubePrefixes *Prefixes
+	var kubePrefixes *ipam.Prefixes
+	var prevKubePrefixes *ipam.Prefixes
 
 	var localAddr net.IP
 	var prevLocalAddr net.IP
@@ -144,7 +145,7 @@
 		}
 
 		// Prepare prefixes to submit to cluster.
-		var prefixes Prefixes
+		var prefixes ipam.Prefixes
 
 		// Do we have a local node address? Add it to the prefixes.
 		if len(localAddr) > 0 {
@@ -164,7 +165,7 @@
 			_, err := s.Curator.UpdateNodeClusterNetworking(ctx, &apb.UpdateNodeClusterNetworkingRequest{
 				Clusternet: &cpb.NodeClusterNetworking{
 					WireguardPubkey: s.wg.key().PublicKey().String(),
-					Prefixes:        prefixes.proto(),
+					Prefixes:        prefixes.Proto(),
 				},
 			})
 			if err != nil {
@@ -186,7 +187,7 @@
 // node host network namespace (i.e. the one container P2P interfaces point to)
 // on its loopback interface to make it eligible to be used as a source IP
 // address for communication into the clusternet overlay.
-func configureKubeNetwork(oldPrefixes *Prefixes, newPrefixes *Prefixes) error {
+func configureKubeNetwork(oldPrefixes *ipam.Prefixes, newPrefixes *ipam.Prefixes) error {
 	// diff maps prefixes to be removed to false
 	// and prefixes to be added to true.
 	diff := make(map[netip.Prefix]bool)
diff --git a/metropolis/node/core/clusternet/clusternet_test.go b/metropolis/node/core/network/overlay/overlay_test.go
similarity index 98%
rename from metropolis/node/core/clusternet/clusternet_test.go
rename to metropolis/node/core/network/overlay/overlay_test.go
index 741bc24..44bdd8b 100644
--- a/metropolis/node/core/clusternet/clusternet_test.go
+++ b/metropolis/node/core/network/overlay/overlay_test.go
@@ -1,7 +1,7 @@
 // Copyright The Monogon Project Authors.
 // SPDX-License-Identifier: Apache-2.0
 
-package clusternet
+package overlay
 
 import (
 	"fmt"
@@ -19,6 +19,7 @@
 	common "source.monogon.dev/metropolis/node"
 	"source.monogon.dev/metropolis/node/core/localstorage"
 	"source.monogon.dev/metropolis/node/core/localstorage/declarative"
+	"source.monogon.dev/metropolis/node/core/network/ipam"
 	"source.monogon.dev/metropolis/test/util"
 	"source.monogon.dev/osbase/event/memory"
 	"source.monogon.dev/osbase/supervisor"
@@ -95,7 +96,7 @@
 
 	var nval memory.Value[*common.NetStatus]
 
-	var podNetwork memory.Value[*Prefixes]
+	var podNetwork memory.Value[*ipam.Prefixes]
 	wg := &fakeWireguard{}
 	svc := Service{
 		Curator: curator,
diff --git a/metropolis/node/core/clusternet/wireguard.go b/metropolis/node/core/network/overlay/wireguard.go
similarity index 98%
rename from metropolis/node/core/clusternet/wireguard.go
rename to metropolis/node/core/network/overlay/wireguard.go
index 096e74d..5054553 100644
--- a/metropolis/node/core/clusternet/wireguard.go
+++ b/metropolis/node/core/network/overlay/wireguard.go
@@ -1,7 +1,7 @@
 // Copyright The Monogon Project Authors.
 // SPDX-License-Identifier: Apache-2.0
 
-package clusternet
+package overlay
 
 import (
 	"fmt"
@@ -89,7 +89,7 @@
 		}
 	}
 
-	wgInterface := &netlink.Wireguard{LinkAttrs: netlink.LinkAttrs{Name: clusterNetDeviceName, Flags: net.FlagUp, Group: common.LinkGroupClusternet}}
+	wgInterface := &netlink.Wireguard{LinkAttrs: netlink.LinkAttrs{Name: clusterNetDeviceName, Flags: net.FlagUp, Group: common.LinkGroupOverlay}}
 	if err := netlink.LinkAdd(wgInterface); err != nil {
 		return fmt.Errorf("when adding network interface: %w", err)
 	}
@@ -111,7 +111,7 @@
 	if err := netlink.RouteAdd(&netlink.Route{
 		Dst:       clusterNet,
 		LinkIndex: wgInterface.Index,
-		Protocol:  netlink.RouteProtocol(common.ProtocolClusternet),
+		Protocol:  netlink.RouteProtocol(common.ProtocolOverlay),
 	}); err != nil && !os.IsExist(err) {
 		return fmt.Errorf("when creating cluster route: %w", err)
 	}
diff --git a/metropolis/node/core/network/workloads/BUILD.bazel b/metropolis/node/core/network/workloads/BUILD.bazel
index 357b892..e031fca 100644
--- a/metropolis/node/core/network/workloads/BUILD.bazel
+++ b/metropolis/node/core/network/workloads/BUILD.bazel
@@ -7,7 +7,7 @@
     visibility = ["//visibility:public"],
     deps = [
         "//metropolis/node",
-        "//metropolis/node/core/clusternet",
+        "//metropolis/node/core/network/ipam",
         "//metropolis/node/core/network/workloads/spec",
         "//osbase/event",
         "//osbase/supervisor",
diff --git a/metropolis/node/core/network/workloads/workloads.go b/metropolis/node/core/network/workloads/workloads.go
index 2b4f5d3..dfe3eca 100644
--- a/metropolis/node/core/network/workloads/workloads.go
+++ b/metropolis/node/core/network/workloads/workloads.go
@@ -20,7 +20,7 @@
 	"google.golang.org/grpc/status"
 
 	"source.monogon.dev/metropolis/node"
-	"source.monogon.dev/metropolis/node/core/clusternet"
+	"source.monogon.dev/metropolis/node/core/network/ipam"
 	wlapi "source.monogon.dev/metropolis/node/core/network/workloads/spec"
 	"source.monogon.dev/osbase/event"
 	"source.monogon.dev/osbase/supervisor"
@@ -43,10 +43,10 @@
 	// intfUsed is the set of allocated short interface names.
 	intfUsed map[string]struct{}
 
-	k8sNodePrefix event.Value[*clusternet.Prefixes]
+	k8sNodePrefix event.Value[*ipam.Prefixes]
 }
 
-func New(k8sNodePrefix event.Value[*clusternet.Prefixes]) *Service {
+func New(k8sNodePrefix event.Value[*ipam.Prefixes]) *Service {
 	return &Service{
 		workloadNets:   []netip.Prefix{},
 		attachments:    make(map[netip.Addr]string),
diff --git a/metropolis/node/core/roleserve/BUILD.bazel b/metropolis/node/core/roleserve/BUILD.bazel
index 72a4472..c753683 100644
--- a/metropolis/node/core/roleserve/BUILD.bazel
+++ b/metropolis/node/core/roleserve/BUILD.bazel
@@ -19,7 +19,6 @@
     visibility = ["//visibility:public"],
     deps = [
         "//metropolis/node",
-        "//metropolis/node/core/clusternet",
         "//metropolis/node/core/consensus",
         "//metropolis/node/core/curator",
         "//metropolis/node/core/curator/proto/api",
@@ -30,6 +29,8 @@
         "//metropolis/node/core/mgmt",
         "//metropolis/node/core/network",
         "//metropolis/node/core/network/hostsfile",
+        "//metropolis/node/core/network/ipam",
+        "//metropolis/node/core/network/overlay",
         "//metropolis/node/core/productinfo",
         "//metropolis/node/core/rpc",
         "//metropolis/node/core/rpc/resolver",
diff --git a/metropolis/node/core/roleserve/roleserve.go b/metropolis/node/core/roleserve/roleserve.go
index 4c1f610..ef84643 100644
--- a/metropolis/node/core/roleserve/roleserve.go
+++ b/metropolis/node/core/roleserve/roleserve.go
@@ -43,11 +43,11 @@
 	"crypto/ed25519"
 
 	common "source.monogon.dev/metropolis/node"
-	"source.monogon.dev/metropolis/node/core/clusternet"
 	"source.monogon.dev/metropolis/node/core/curator"
 	"source.monogon.dev/metropolis/node/core/identity"
 	"source.monogon.dev/metropolis/node/core/localstorage"
 	"source.monogon.dev/metropolis/node/core/network"
+	"source.monogon.dev/metropolis/node/core/network/ipam"
 	"source.monogon.dev/metropolis/node/core/rpc/resolver"
 	"source.monogon.dev/metropolis/node/core/update"
 	cpb "source.monogon.dev/metropolis/proto/common"
@@ -66,7 +66,7 @@
 	// Network is a handle to the network service, used by workloads.
 	Network *network.Service
 
-	PodNetwork *memory.Value[*clusternet.Prefixes]
+	PodNetwork *memory.Value[*ipam.Prefixes]
 
 	// resolver is the main, long-lived, authenticated cluster resolver that is used
 	// for all subsequent gRPC calls by the subordinates of the roleserver. It is
diff --git a/metropolis/node/core/roleserve/worker_clusternet.go b/metropolis/node/core/roleserve/worker_clusternet.go
index 3442030..8a3bb8f 100644
--- a/metropolis/node/core/roleserve/worker_clusternet.go
+++ b/metropolis/node/core/roleserve/worker_clusternet.go
@@ -7,9 +7,10 @@
 	"context"
 	"net"
 
-	"source.monogon.dev/metropolis/node/core/clusternet"
 	"source.monogon.dev/metropolis/node/core/localstorage"
 	"source.monogon.dev/metropolis/node/core/network"
+	"source.monogon.dev/metropolis/node/core/network/ipam"
+	"source.monogon.dev/metropolis/node/core/network/overlay"
 	"source.monogon.dev/osbase/event/memory"
 	"source.monogon.dev/osbase/supervisor"
 
@@ -22,7 +23,7 @@
 	// curatorConnection will be read
 	curatorConnection *memory.Value[*CuratorConnection]
 	// podNetwork will be read.
-	podNetwork *memory.Value[*clusternet.Prefixes]
+	podNetwork *memory.Value[*ipam.Prefixes]
 	network    *network.Service
 }
 
@@ -37,7 +38,7 @@
 	supervisor.Logger(ctx).Infof("Got curator connection, starting...")
 	cur := ipb.NewCuratorClient(cc.conn)
 
-	svc := clusternet.Service{
+	svc := overlay.Service{
 		Curator: cur,
 		ClusterNet: net.IPNet{
 			IP:   []byte{10, 192, 0, 0},
diff --git a/metropolis/node/core/roleserve/worker_kubernetes.go b/metropolis/node/core/roleserve/worker_kubernetes.go
index 2bbbba8..9cb2304 100644
--- a/metropolis/node/core/roleserve/worker_kubernetes.go
+++ b/metropolis/node/core/roleserve/worker_kubernetes.go
@@ -8,10 +8,10 @@
 	"fmt"
 	"net"
 
-	"source.monogon.dev/metropolis/node/core/clusternet"
 	"source.monogon.dev/metropolis/node/core/identity"
 	"source.monogon.dev/metropolis/node/core/localstorage"
 	"source.monogon.dev/metropolis/node/core/network"
+	"source.monogon.dev/metropolis/node/core/network/ipam"
 	"source.monogon.dev/metropolis/node/kubernetes"
 	"source.monogon.dev/metropolis/node/kubernetes/containerd"
 	kpki "source.monogon.dev/metropolis/node/kubernetes/pki"
@@ -40,7 +40,7 @@
 	localControlPlane *memory.Value[*localControlPlane]
 	curatorConnection *memory.Value[*CuratorConnection]
 	kubernetesStatus  *memory.Value[*KubernetesStatus]
-	podNetwork        *memory.Value[*clusternet.Prefixes]
+	podNetwork        *memory.Value[*ipam.Prefixes]
 }
 
 // kubernetesStartup is used internally to provide a reduced (as in MapReduce
diff --git a/metropolis/node/kubernetes/BUILD.bazel b/metropolis/node/kubernetes/BUILD.bazel
index 530723f..3245964 100644
--- a/metropolis/node/kubernetes/BUILD.bazel
+++ b/metropolis/node/kubernetes/BUILD.bazel
@@ -21,7 +21,6 @@
         "//go/logging",
         "//go/net/tinylb",
         "//metropolis/node",
-        "//metropolis/node/core/clusternet",
         "//metropolis/node/core/consensus",
         "//metropolis/node/core/curator/proto/api",
         "//metropolis/node/core/curator/watcher",
@@ -29,6 +28,7 @@
         "//metropolis/node/core/localstorage",
         "//metropolis/node/core/metrics",
         "//metropolis/node/core/network",
+        "//metropolis/node/core/network/ipam",
         "//metropolis/node/kubernetes/authproxy",
         "//metropolis/node/kubernetes/clusternet",
         "//metropolis/node/kubernetes/metricsprovider",
diff --git a/metropolis/node/kubernetes/clusternet/BUILD.bazel b/metropolis/node/kubernetes/clusternet/BUILD.bazel
index 79bedbe..839d946 100644
--- a/metropolis/node/kubernetes/clusternet/BUILD.bazel
+++ b/metropolis/node/kubernetes/clusternet/BUILD.bazel
@@ -7,7 +7,7 @@
     visibility = ["//metropolis/node/kubernetes:__subpackages__"],
     deps = [
         "//go/logging",
-        "//metropolis/node/core/clusternet",
+        "//metropolis/node/core/network/ipam",
         "//osbase/event",
         "//osbase/supervisor",
         "@io_k8s_api//core/v1:core",
diff --git a/metropolis/node/kubernetes/clusternet/clusternet.go b/metropolis/node/kubernetes/clusternet/clusternet.go
index 9cf877b..dd5e5df 100644
--- a/metropolis/node/kubernetes/clusternet/clusternet.go
+++ b/metropolis/node/kubernetes/clusternet/clusternet.go
@@ -29,7 +29,7 @@
 	"k8s.io/client-go/tools/cache"
 
 	"source.monogon.dev/go/logging"
-	oclusternet "source.monogon.dev/metropolis/node/core/clusternet"
+	"source.monogon.dev/metropolis/node/core/network/ipam"
 	"source.monogon.dev/osbase/event"
 	"source.monogon.dev/osbase/supervisor"
 )
@@ -37,7 +37,7 @@
 type Service struct {
 	NodeName   string
 	Kubernetes kubernetes.Interface
-	Prefixes   event.Value[*oclusternet.Prefixes]
+	Prefixes   event.Value[*ipam.Prefixes]
 
 	logger logging.Leveled
 }
@@ -50,7 +50,7 @@
 		return nil
 	}
 
-	var prefixes oclusternet.Prefixes
+	var prefixes ipam.Prefixes
 	for _, podNetStr := range newNode.Spec.PodCIDRs {
 		prefix, err := netip.ParsePrefix(podNetStr)
 		if err != nil {
diff --git a/metropolis/node/kubernetes/service_worker.go b/metropolis/node/kubernetes/service_worker.go
index 8ca8556..5e28788 100644
--- a/metropolis/node/kubernetes/service_worker.go
+++ b/metropolis/node/kubernetes/service_worker.go
@@ -17,10 +17,10 @@
 
 	"source.monogon.dev/go/net/tinylb"
 	"source.monogon.dev/metropolis/node"
-	oclusternet "source.monogon.dev/metropolis/node/core/clusternet"
 	"source.monogon.dev/metropolis/node/core/localstorage"
 	"source.monogon.dev/metropolis/node/core/metrics"
 	"source.monogon.dev/metropolis/node/core/network"
+	"source.monogon.dev/metropolis/node/core/network/ipam"
 	"source.monogon.dev/metropolis/node/kubernetes/clusternet"
 	"source.monogon.dev/metropolis/node/kubernetes/metricsprovider"
 	"source.monogon.dev/metropolis/node/kubernetes/networkpolicy"
@@ -44,7 +44,7 @@
 	Network       *network.Service
 	NodeID        string
 	CuratorClient ipb.CuratorClient
-	PodNetwork    event.Value[*oclusternet.Prefixes]
+	PodNetwork    event.Value[*ipam.Prefixes]
 }
 
 type Worker struct {
diff --git a/metropolis/node/net_protocols.go b/metropolis/node/net_protocols.go
index f3e69b8..c4c3f64 100644
--- a/metropolis/node/net_protocols.go
+++ b/metropolis/node/net_protocols.go
@@ -6,9 +6,9 @@
 // These are netlink protocol numbers used internally for various netlink
 // resource (e.g. route) owners/manager.
 const (
-	// ProtocolClusternet is used by //metropolis/node/core/clusternet when
-	// creating/removing routes pointing to the clusternet interface.
-	ProtocolClusternet int = 129
+	// ProtocolOverlay is used by //metropolis/node/core/network/overlay
+	// when creating/removing routes pointing to the overlay interface.
+	ProtocolOverlay int = 129
 )
 
 // Netlink link groups used for interface classification and traffic matching.
@@ -16,7 +16,7 @@
 	// LinkGroupK8sPod is set on all host side PtP interfaces going to K8s
 	// pods.
 	LinkGroupK8sPod uint32 = 8
-	// LinkGroupClusternet is set on all interfaces not needing SNAT from the
-	// K8s internal IPs.
-	LinkGroupClusternet uint32 = 9
+	// LinkGroupOverlay is set on all interfaces which are part of the overlay
+	// network and thus exempt from SNATing of workload traffic.
+	LinkGroupOverlay uint32 = 9
 )